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/.
19 Look for virtual methods where we never call the defining virtual method, and only call the overriding virtual
20 methods, which indicates a places where the virtual-ness is unwarranted, normally a result of premature abstraction.
22 The process goes something like this:
24 $ make FORCE_COMPILE_ALL=1 COMPILER_PLUGIN_TOOL='VirtualDown' check
25 $ ./compilerplugins/clang/VirtualDown.py
27 @TODO for some reason, we get false+ for operator== methods
28 @TODO some templates confuse it and we get false+
37 std::string sourceLocation
;
39 bool operator<(const MyFuncInfo
& lhs
, const MyFuncInfo
& rhs
) { return lhs
.name
< rhs
.name
; }
41 // try to limit the voluminous output a little
42 static std::set
<MyFuncInfo
> definitionSet
;
43 static std::set
<std::string
> callSet
;
45 class VirtualDown
: public RecursiveASTVisitor
<VirtualDown
>, public loplugin::Plugin
48 explicit VirtualDown(loplugin::InstantiationData
const& data
)
53 virtual void run() override
55 TraverseDecl(compiler
.getASTContext().getTranslationUnitDecl());
57 // dump all our output in one write call - this is to try and limit IO "crosstalk" between multiple processes
58 // writing to the same logfile
60 for (const MyFuncInfo
& s
: definitionSet
)
61 output
+= "definition:\t" + s
.name
+ "\t" + s
.sourceLocation
+ "\n";
62 for (const std::string
& s
: callSet
)
63 output
+= "call:\t" + s
+ "\n";
65 myfile
.open(WORKDIR
"/loplugin.virtualdown.log", std::ios::app
| std::ios::out
);
69 bool shouldVisitTemplateInstantiations() const { return true; }
70 bool shouldVisitImplicitCode() const { return true; }
72 bool VisitCXXMethodDecl(CXXMethodDecl
const*);
73 bool VisitCXXMemberCallExpr(CXXMemberCallExpr
const*);
74 bool TraverseFunctionDecl(FunctionDecl
*);
75 bool TraverseCXXMethodDecl(CXXMethodDecl
*);
76 bool TraverseCXXConversionDecl(CXXConversionDecl
*);
77 #if CLANG_VERSION >= 50000
78 bool TraverseCXXDeductionGuideDecl(CXXDeductionGuideDecl
*);
81 std::string
toString(SourceLocation loc
);
82 std::string
niceName(const CXXMethodDecl
* functionDecl
);
83 FunctionDecl
const* currentFunctionDecl
= nullptr;
86 bool VirtualDown::VisitCXXMethodDecl(const CXXMethodDecl
* methodDecl
)
88 if (ignoreLocation(methodDecl
))
92 if (!methodDecl
->isThisDeclarationADefinition() || !methodDecl
->isVirtual()
93 || methodDecl
->isDeleted())
97 methodDecl
= methodDecl
->getCanonicalDecl();
98 // ignore stuff that forms part of the stable URE interface
99 if (isInUnoIncludeFile(methodDecl
))
104 std::string aNiceName
= niceName(methodDecl
);
106 if (isa
<CXXDestructorDecl
>(methodDecl
))
108 if (isa
<CXXConstructorDecl
>(methodDecl
))
111 if (methodDecl
->size_overridden_methods() == 0)
112 definitionSet
.insert({ aNiceName
, toString(methodDecl
->getLocation()) });
117 bool VirtualDown::VisitCXXMemberCallExpr(CXXMemberCallExpr
const* expr
)
119 // Note that I don't ignore ANYTHING here, because I want to get calls to my code that result
120 // from template instantiation deep inside the STL and other external code
122 FunctionDecl
const* calleeFunctionDecl
= expr
->getDirectCallee();
123 if (calleeFunctionDecl
== nullptr)
125 Expr
const* callee
= expr
->getCallee()->IgnoreParenImpCasts();
126 DeclRefExpr
const* dr
= dyn_cast
<DeclRefExpr
>(callee
);
129 calleeFunctionDecl
= dyn_cast
<FunctionDecl
>(dr
->getDecl());
130 if (calleeFunctionDecl
)
138 // ignore recursive calls
139 if (currentFunctionDecl
== calleeFunctionDecl
)
142 auto cxxMethodDecl
= dyn_cast
<CXXMethodDecl
>(calleeFunctionDecl
);
146 while (cxxMethodDecl
->getTemplateInstantiationPattern())
147 cxxMethodDecl
= dyn_cast
<CXXMethodDecl
>(cxxMethodDecl
->getTemplateInstantiationPattern());
148 while (cxxMethodDecl
->getInstantiatedFromMemberFunction())
149 cxxMethodDecl
= dyn_cast
<CXXMethodDecl
>(cxxMethodDecl
->getInstantiatedFromMemberFunction());
150 if (cxxMethodDecl
->getLocation().isValid() && !ignoreLocation(cxxMethodDecl
))
151 callSet
.insert(niceName(cxxMethodDecl
));
156 bool VirtualDown::TraverseFunctionDecl(FunctionDecl
* f
)
158 auto copy
= currentFunctionDecl
;
159 currentFunctionDecl
= f
;
160 bool ret
= RecursiveASTVisitor::TraverseFunctionDecl(f
);
161 currentFunctionDecl
= copy
;
164 bool VirtualDown::TraverseCXXMethodDecl(CXXMethodDecl
* f
)
166 auto copy
= currentFunctionDecl
;
167 currentFunctionDecl
= f
;
168 bool ret
= RecursiveASTVisitor::TraverseCXXMethodDecl(f
);
169 currentFunctionDecl
= copy
;
172 bool VirtualDown::TraverseCXXConversionDecl(CXXConversionDecl
* f
)
174 auto copy
= currentFunctionDecl
;
175 currentFunctionDecl
= f
;
176 bool ret
= RecursiveASTVisitor::TraverseCXXConversionDecl(f
);
177 currentFunctionDecl
= copy
;
180 #if CLANG_VERSION >= 50000
181 bool VirtualDown::TraverseCXXDeductionGuideDecl(CXXDeductionGuideDecl
* f
)
183 auto copy
= currentFunctionDecl
;
184 currentFunctionDecl
= f
;
185 bool ret
= RecursiveASTVisitor::TraverseCXXDeductionGuideDecl(f
);
186 currentFunctionDecl
= copy
;
191 std::string
VirtualDown::niceName(const CXXMethodDecl
* cxxMethodDecl
)
193 std::string s
= cxxMethodDecl
->getReturnType().getCanonicalType().getAsString() + " "
194 + cxxMethodDecl
->getQualifiedNameAsString() + "(";
195 for (const ParmVarDecl
* pParmVarDecl
: compat::parameters(*cxxMethodDecl
))
197 s
+= pParmVarDecl
->getType().getCanonicalType().getAsString();
201 if (cxxMethodDecl
->isConst())
208 std::string
VirtualDown::toString(SourceLocation loc
)
210 SourceLocation expansionLoc
= compiler
.getSourceManager().getExpansionLoc(loc
);
211 StringRef name
= compiler
.getSourceManager().getFilename(expansionLoc
);
212 std::string sourceLocation
213 = std::string(name
.substr(strlen(SRCDIR
) + 1)) + ":"
214 + std::to_string(compiler
.getSourceManager().getSpellingLineNumber(expansionLoc
));
215 loplugin::normalizeDotDotInFilePath(sourceLocation
);
216 return sourceLocation
;
219 loplugin::Plugin::Registration
<VirtualDown
> X("virtualdown", false);
222 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */