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"
23 What we are looking for here are methods that are not reachable from any of the program
25 "Entry points" includes main, and various binary API
27 Mostly that means we end up finding cycles of methods i.e. methods that refer to each
28 other, but are not reachable.
30 It does so, by dumping various call/definition info to a log file.
31 Be warned that it produces around 20G of log file.
33 Then we will post-process the log file with a python script, which takes about
34 15min to run on a fast machine.
36 The process goes something like this:
38 $ make FORCE_COMPILE_ALL=1 COMPILER_PLUGIN_TOOL='methodcycles' check
39 $ ./compilerplugins/clang/methodcycles.py
41 Note that the actual process may involve a fair amount of undoing, hand editing, and general messing around
50 std::string returnType
;
51 std::string nameAndParams
;
52 std::string sourceLocation
;
54 bool operator<(const MyFuncInfo
& lhs
, const MyFuncInfo
& rhs
)
56 return std::tie(lhs
.returnType
, lhs
.nameAndParams
)
57 < std::tie(rhs
.returnType
, rhs
.nameAndParams
);
60 // try to limit the voluminous output a little
61 static std::multimap
<const FunctionDecl
*, const FunctionDecl
*> callMap
;
62 static std::set
<MyFuncInfo
> definitionSet
;
64 class MethodCycles
: public RecursiveASTVisitor
<MethodCycles
>, public loplugin::Plugin
67 explicit MethodCycles(loplugin::InstantiationData
const& data
)
72 virtual void run() override
74 TraverseDecl(compiler
.getASTContext().getTranslationUnitDecl());
76 // dump all our output in one write call - this is to try and limit IO "crosstalk" between multiple processes
77 // writing to the same logfile
80 for (const MyFuncInfo
& s
: definitionSet
)
81 output
+= "definition:\t" + s
.returnType
+ "\t" + s
.nameAndParams
+ "\t"
82 + s
.sourceLocation
+ "\n";
83 for (const std::pair
<const FunctionDecl
*, const FunctionDecl
*>& pair
: callMap
)
85 if (!isLocationMine(pair
.first
->getLocation())
86 || !isLocationMine(pair
.second
->getLocation()))
88 auto niceNameFrom
= niceName(pair
.first
);
89 auto niceNameTo
= niceName(pair
.second
);
90 output
+= "call:\t" + niceNameFrom
.returnType
+ "\t" + niceNameFrom
.nameAndParams
+ "\t"
91 + niceNameTo
.returnType
+ "\t" + niceNameTo
.nameAndParams
+ "\n";
94 myfile
.open(WORKDIR
"/loplugin.methodcycles.log", std::ios::app
| std::ios::out
);
99 bool shouldVisitTemplateInstantiations() const { return true; }
100 bool shouldVisitImplicitCode() const { return true; }
102 bool VisitCallExpr(CallExpr
*);
103 bool VisitFunctionDecl(const FunctionDecl
* decl
);
104 bool VisitDeclRefExpr(const DeclRefExpr
*);
105 bool VisitCXXConstructExpr(const CXXConstructExpr
*);
107 bool TraverseFunctionDecl(FunctionDecl
*);
108 bool TraverseCXXMethodDecl(CXXMethodDecl
*);
109 bool TraverseCXXConstructorDecl(CXXConstructorDecl
*);
110 bool TraverseCXXConversionDecl(CXXConversionDecl
*);
111 bool TraverseCXXDestructorDecl(CXXDestructorDecl
*);
112 bool TraverseCXXDeductionGuideDecl(CXXDeductionGuideDecl
*);
115 void logCallToRootMethods(const FunctionDecl
* functionDeclFrom
,
116 const FunctionDecl
* functionDeclTo
);
117 void findRoots(const FunctionDecl
* functionDecl
,
118 std::unordered_set
<const FunctionDecl
*>& roots
);
119 MyFuncInfo
niceName(const FunctionDecl
* functionDecl
);
120 bool isLocationMine(SourceLocation loc
);
121 std::string
toString(SourceLocation loc
);
122 FunctionDecl
const* currentFunctionDecl
= nullptr;
125 MyFuncInfo
MethodCycles::niceName(const FunctionDecl
* functionDecl
)
127 if (functionDecl
->getInstantiatedFromMemberFunction())
128 functionDecl
= functionDecl
->getInstantiatedFromMemberFunction();
129 #if CLANG_VERSION < 90000
130 else if (functionDecl
->getClassScopeSpecializationPattern())
131 functionDecl
= functionDecl
->getClassScopeSpecializationPattern();
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
= compiler
.getSourceManager().getFilename(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: */