1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
15 #include <unordered_set>
18 #include "clang/AST/Attr.h"
20 #include "config_clang.h"
25 What we are looking for here are methods that are not reachable from any of the program
27 "Entry points" includes main, and various binary API
29 Mostly that means we end up finding cycles of methods i.e. methods that refer to each
30 other, but are not reachable.
32 It does so, by dumping various call/definition info to a log file.
33 Be warned that it produces around 20G of log file.
35 Then we will post-process the log file with a python script, which takes about
36 15min to run on a fast machine.
38 The process goes something like this:
40 $ make FORCE_COMPILE=all COMPILER_PLUGIN_TOOL='methodcycles' check
41 $ ./compilerplugins/clang/methodcycles.py
43 Note that the actual process may involve a fair amount of undoing, hand editing, and general messing around
52 std::string returnType
;
53 std::string nameAndParams
;
54 std::string sourceLocation
;
56 bool operator<(const MyFuncInfo
& lhs
, const MyFuncInfo
& rhs
)
58 return std::tie(lhs
.returnType
, lhs
.nameAndParams
)
59 < std::tie(rhs
.returnType
, rhs
.nameAndParams
);
62 // try to limit the voluminous output a little
63 static std::multimap
<const FunctionDecl
*, const FunctionDecl
*> callMap
;
64 static std::set
<MyFuncInfo
> definitionSet
;
66 class MethodCycles
: public RecursiveASTVisitor
<MethodCycles
>, public loplugin::Plugin
69 explicit MethodCycles(loplugin::InstantiationData
const& data
)
74 virtual void run() override
76 handler
.enableTreeWideAnalysisMode();
78 TraverseDecl(compiler
.getASTContext().getTranslationUnitDecl());
80 // dump all our output in one write call - this is to try and limit IO "crosstalk" between multiple processes
81 // writing to the same logfile
84 for (const MyFuncInfo
& s
: definitionSet
)
85 output
+= "definition:\t" + s
.returnType
+ "\t" + s
.nameAndParams
+ "\t"
86 + s
.sourceLocation
+ "\n";
87 for (const auto& pair
: callMap
)
89 if (!isLocationMine(pair
.first
->getLocation())
90 || !isLocationMine(pair
.second
->getLocation()))
92 auto niceNameFrom
= niceName(pair
.first
);
93 auto niceNameTo
= niceName(pair
.second
);
94 output
+= "call:\t" + niceNameFrom
.returnType
+ "\t" + niceNameFrom
.nameAndParams
+ "\t"
95 + niceNameTo
.returnType
+ "\t" + niceNameTo
.nameAndParams
+ "\n";
98 myfile
.open(WORKDIR
"/loplugin.methodcycles.log", std::ios::app
| std::ios::out
);
103 bool shouldVisitTemplateInstantiations() const { return true; }
104 bool shouldVisitImplicitCode() const { return true; }
106 bool VisitCallExpr(CallExpr
*);
107 bool VisitFunctionDecl(const FunctionDecl
* decl
);
108 bool VisitDeclRefExpr(const DeclRefExpr
*);
109 bool VisitCXXConstructExpr(const CXXConstructExpr
*);
111 bool TraverseFunctionDecl(FunctionDecl
*);
112 bool TraverseCXXMethodDecl(CXXMethodDecl
*);
113 bool TraverseCXXConstructorDecl(CXXConstructorDecl
*);
114 bool TraverseCXXConversionDecl(CXXConversionDecl
*);
115 bool TraverseCXXDestructorDecl(CXXDestructorDecl
*);
116 bool TraverseCXXDeductionGuideDecl(CXXDeductionGuideDecl
*);
119 void logCallToRootMethods(const FunctionDecl
* functionDeclFrom
,
120 const FunctionDecl
* functionDeclTo
);
121 void findRoots(const FunctionDecl
* functionDecl
,
122 std::unordered_set
<const FunctionDecl
*>& roots
);
123 MyFuncInfo
niceName(const FunctionDecl
* functionDecl
);
124 bool isLocationMine(SourceLocation loc
);
125 std::string
toString(SourceLocation loc
);
126 FunctionDecl
const* currentFunctionDecl
= nullptr;
129 MyFuncInfo
MethodCycles::niceName(const FunctionDecl
* functionDecl
)
131 if (functionDecl
->getInstantiatedFromMemberFunction())
132 functionDecl
= functionDecl
->getInstantiatedFromMemberFunction();
133 else if (functionDecl
->getTemplateInstantiationPattern())
134 functionDecl
= functionDecl
->getTemplateInstantiationPattern();
137 if (!isa
<CXXConstructorDecl
>(functionDecl
))
139 aInfo
.returnType
= functionDecl
->getReturnType().getCanonicalType().getAsString();
143 aInfo
.returnType
= "";
146 if (auto methodDecl
= dyn_cast
<CXXMethodDecl
>(functionDecl
))
148 const CXXRecordDecl
* recordDecl
= methodDecl
->getParent();
150 = recordDecl
->getQualifiedNameAsString() + "::" + functionDecl
->getNameAsString() + "(";
154 aInfo
.nameAndParams
= functionDecl
->getQualifiedNameAsString() + "(";
157 for (const ParmVarDecl
* pParmVarDecl
: functionDecl
->parameters())
162 aInfo
.nameAndParams
+= ",";
163 aInfo
.nameAndParams
+= pParmVarDecl
->getType().getCanonicalType().getAsString();
165 aInfo
.nameAndParams
+= ")";
166 if (isa
<CXXMethodDecl
>(functionDecl
) && dyn_cast
<CXXMethodDecl
>(functionDecl
)->isConst())
168 aInfo
.nameAndParams
+= " const";
171 aInfo
.sourceLocation
= toString(functionDecl
->getLocation());
176 std::string
MethodCycles::toString(SourceLocation loc
)
178 SourceLocation expansionLoc
= compiler
.getSourceManager().getExpansionLoc(loc
);
179 StringRef name
= getFilenameOfLocation(expansionLoc
);
180 std::string sourceLocation
181 = std::string(name
.substr(strlen(SRCDIR
) + 1)) + ":"
182 + std::to_string(compiler
.getSourceManager().getSpellingLineNumber(expansionLoc
));
183 loplugin::normalizeDotDotInFilePath(sourceLocation
);
184 return sourceLocation
;
187 bool MethodCycles::isLocationMine(SourceLocation loc
)
189 SourceLocation expansionLoc
= compiler
.getSourceManager().getExpansionLoc(loc
);
190 if (compiler
.getSourceManager().isInSystemHeader(expansionLoc
))
192 const char* bufferName
= compiler
.getSourceManager().getPresumedLoc(expansionLoc
).getFilename();
193 if (bufferName
== NULL
)
195 if (loplugin::hasPathnamePrefix(bufferName
, WORKDIR
"/")
196 || loplugin::hasPathnamePrefix(bufferName
, BUILDDIR
"/")
197 || loplugin::hasPathnamePrefix(bufferName
, SRCDIR
"/"))
202 void MethodCycles::logCallToRootMethods(const FunctionDecl
* functionDeclFrom
,
203 const FunctionDecl
* functionDeclTo
)
205 if (!functionDeclFrom
)
207 // template magic mostly, but also things called from initialisers
210 functionDeclFrom
= functionDeclFrom
->getCanonicalDecl();
211 functionDeclTo
= functionDeclTo
->getCanonicalDecl();
213 std::unordered_set
<const FunctionDecl
*> fromRoots
;
214 findRoots(functionDeclFrom
, fromRoots
);
215 std::unordered_set
<const FunctionDecl
*> toRoots
;
216 findRoots(functionDeclTo
, toRoots
);
218 for (auto const& from
: fromRoots
)
219 for (auto const& to
: toRoots
)
220 callMap
.insert({ from
, to
});
223 void MethodCycles::findRoots(const FunctionDecl
* functionDecl
,
224 std::unordered_set
<const FunctionDecl
*>& roots
)
226 bool bCalledSuperMethod
= false;
227 if (auto methodDecl
= dyn_cast
<CXXMethodDecl
>(functionDecl
))
229 // For virtual/overriding methods, we need to pretend we called from/to root method(s),
230 // so that they get marked as used.
231 for (auto it
= methodDecl
->begin_overridden_methods();
232 it
!= methodDecl
->end_overridden_methods(); ++it
)
234 findRoots(*it
, roots
);
235 bCalledSuperMethod
= true;
238 if (!bCalledSuperMethod
)
240 while (functionDecl
->getTemplateInstantiationPattern())
241 functionDecl
= functionDecl
->getTemplateInstantiationPattern();
242 if (functionDecl
->getLocation().isValid())
243 roots
.insert(functionDecl
);
247 bool MethodCycles::VisitCallExpr(CallExpr
* expr
)
249 // Note that I don't ignore ANYTHING here, because I want to get calls to my code that result
250 // from template instantiation deep inside the STL and other external code
252 FunctionDecl
* calleeFunctionDecl
= expr
->getDirectCallee();
253 if (calleeFunctionDecl
== nullptr)
255 Expr
* callee
= expr
->getCallee()->IgnoreParenImpCasts();
256 DeclRefExpr
* dr
= dyn_cast
<DeclRefExpr
>(callee
);
259 calleeFunctionDecl
= dyn_cast
<FunctionDecl
>(dr
->getDecl());
260 if (calleeFunctionDecl
)
268 if (currentFunctionDecl
!= calleeFunctionDecl
)
269 // ignore recursive calls
270 logCallToRootMethods(currentFunctionDecl
, calleeFunctionDecl
);
275 bool MethodCycles::VisitCXXConstructExpr(const CXXConstructExpr
* constructExpr
)
277 // Note that I don't ignore ANYTHING here, because I want to get calls to my code that result
278 // from template instantiation deep inside the STL and other external code
280 const CXXConstructorDecl
* constructorDecl
= constructExpr
->getConstructor();
281 constructorDecl
= constructorDecl
->getCanonicalDecl();
283 if (!constructorDecl
->getLocation().isValid())
288 logCallToRootMethods(currentFunctionDecl
, constructorDecl
);
293 bool MethodCycles::VisitFunctionDecl(const FunctionDecl
* functionDecl
)
295 const FunctionDecl
* canonicalFunctionDecl
= functionDecl
->getCanonicalDecl();
296 if (functionDecl
->isDeleted())
298 // don't care about compiler-generated functions
299 if (functionDecl
->isImplicit())
301 if (!canonicalFunctionDecl
->getLocation().isValid())
303 // ignore method overrides, since the call will show up as being directed to the root method
304 const CXXMethodDecl
* methodDecl
= dyn_cast
<CXXMethodDecl
>(functionDecl
);
306 && (methodDecl
->size_overridden_methods() != 0 || methodDecl
->hasAttr
<OverrideAttr
>()))
308 if (!isLocationMine(canonicalFunctionDecl
->getLocation()))
311 MyFuncInfo funcInfo
= niceName(canonicalFunctionDecl
);
312 definitionSet
.insert(funcInfo
);
316 bool MethodCycles::VisitDeclRefExpr(const DeclRefExpr
* declRefExpr
)
318 const FunctionDecl
* functionDecl
= dyn_cast
<FunctionDecl
>(declRefExpr
->getDecl());
323 logCallToRootMethods(currentFunctionDecl
, functionDecl
->getCanonicalDecl());
328 bool MethodCycles::TraverseFunctionDecl(FunctionDecl
* f
)
330 auto copy
= currentFunctionDecl
;
331 currentFunctionDecl
= f
;
332 bool ret
= RecursiveASTVisitor::TraverseFunctionDecl(f
);
333 currentFunctionDecl
= copy
;
336 bool MethodCycles::TraverseCXXMethodDecl(CXXMethodDecl
* f
)
338 auto copy
= currentFunctionDecl
;
339 currentFunctionDecl
= f
;
340 bool ret
= RecursiveASTVisitor::TraverseCXXMethodDecl(f
);
341 currentFunctionDecl
= copy
;
344 bool MethodCycles::TraverseCXXConversionDecl(CXXConversionDecl
* f
)
346 auto copy
= currentFunctionDecl
;
347 currentFunctionDecl
= f
;
348 bool ret
= RecursiveASTVisitor::TraverseCXXConversionDecl(f
);
349 currentFunctionDecl
= copy
;
352 bool MethodCycles::TraverseCXXDeductionGuideDecl(CXXDeductionGuideDecl
* f
)
354 auto copy
= currentFunctionDecl
;
355 currentFunctionDecl
= f
;
356 bool ret
= RecursiveASTVisitor::TraverseCXXDeductionGuideDecl(f
);
357 currentFunctionDecl
= copy
;
360 bool MethodCycles::TraverseCXXConstructorDecl(CXXConstructorDecl
* f
)
362 auto copy
= currentFunctionDecl
;
363 currentFunctionDecl
= f
;
364 bool ret
= RecursiveASTVisitor::TraverseCXXConstructorDecl(f
);
365 currentFunctionDecl
= copy
;
368 bool MethodCycles::TraverseCXXDestructorDecl(CXXDestructorDecl
* f
)
370 auto copy
= currentFunctionDecl
;
371 currentFunctionDecl
= f
;
372 bool ret
= RecursiveASTVisitor::TraverseCXXDestructorDecl(f
);
373 currentFunctionDecl
= copy
;
377 loplugin::Plugin::Registration
<MethodCycles
> X("methodcycles", false);
380 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */