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/.
14 #include "config_clang.h"
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 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 loplugin::FilteringPlugin
<VirtualDown
>
48 explicit VirtualDown(loplugin::InstantiationData
const& data
)
49 : FilteringPlugin(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 bool TraverseCXXDeductionGuideDecl(CXXDeductionGuideDecl
*);
80 std::string
toString(SourceLocation loc
);
81 std::string
niceName(const CXXMethodDecl
* functionDecl
);
82 FunctionDecl
const* currentFunctionDecl
= nullptr;
85 bool VirtualDown::VisitCXXMethodDecl(const CXXMethodDecl
* methodDecl
)
87 if (ignoreLocation(methodDecl
))
91 if (!methodDecl
->isThisDeclarationADefinition() || !methodDecl
->isVirtual()
92 || methodDecl
->isDeleted())
96 methodDecl
= methodDecl
->getCanonicalDecl();
97 // ignore stuff that forms part of the stable URE interface
98 if (isInUnoIncludeFile(methodDecl
))
103 std::string aNiceName
= niceName(methodDecl
);
105 if (isa
<CXXDestructorDecl
>(methodDecl
))
107 if (isa
<CXXConstructorDecl
>(methodDecl
))
110 if (methodDecl
->size_overridden_methods() == 0)
111 definitionSet
.insert({ aNiceName
, toString(methodDecl
->getLocation()) });
116 bool VirtualDown::VisitCXXMemberCallExpr(CXXMemberCallExpr
const* expr
)
118 // Note that I don't ignore ANYTHING here, because I want to get calls to my code that result
119 // from template instantiation deep inside the STL and other external code
121 FunctionDecl
const* calleeFunctionDecl
= expr
->getDirectCallee();
122 if (calleeFunctionDecl
== nullptr)
124 Expr
const* callee
= expr
->getCallee()->IgnoreParenImpCasts();
125 DeclRefExpr
const* dr
= dyn_cast
<DeclRefExpr
>(callee
);
128 calleeFunctionDecl
= dyn_cast
<FunctionDecl
>(dr
->getDecl());
129 if (calleeFunctionDecl
)
137 // ignore recursive calls
138 if (currentFunctionDecl
== calleeFunctionDecl
)
141 auto cxxMethodDecl
= dyn_cast
<CXXMethodDecl
>(calleeFunctionDecl
);
145 while (cxxMethodDecl
->getTemplateInstantiationPattern())
146 cxxMethodDecl
= dyn_cast
<CXXMethodDecl
>(cxxMethodDecl
->getTemplateInstantiationPattern());
147 while (cxxMethodDecl
->getInstantiatedFromMemberFunction())
148 cxxMethodDecl
= dyn_cast
<CXXMethodDecl
>(cxxMethodDecl
->getInstantiatedFromMemberFunction());
149 if (cxxMethodDecl
->getLocation().isValid() && !ignoreLocation(cxxMethodDecl
))
150 callSet
.insert(niceName(cxxMethodDecl
));
155 bool VirtualDown::TraverseFunctionDecl(FunctionDecl
* f
)
157 auto copy
= currentFunctionDecl
;
158 currentFunctionDecl
= f
;
159 bool ret
= RecursiveASTVisitor::TraverseFunctionDecl(f
);
160 currentFunctionDecl
= copy
;
163 bool VirtualDown::TraverseCXXMethodDecl(CXXMethodDecl
* f
)
165 auto copy
= currentFunctionDecl
;
166 currentFunctionDecl
= f
;
167 bool ret
= RecursiveASTVisitor::TraverseCXXMethodDecl(f
);
168 currentFunctionDecl
= copy
;
171 bool VirtualDown::TraverseCXXConversionDecl(CXXConversionDecl
* f
)
173 auto copy
= currentFunctionDecl
;
174 currentFunctionDecl
= f
;
175 bool ret
= RecursiveASTVisitor::TraverseCXXConversionDecl(f
);
176 currentFunctionDecl
= copy
;
179 bool VirtualDown::TraverseCXXDeductionGuideDecl(CXXDeductionGuideDecl
* f
)
181 auto copy
= currentFunctionDecl
;
182 currentFunctionDecl
= f
;
183 bool ret
= RecursiveASTVisitor::TraverseCXXDeductionGuideDecl(f
);
184 currentFunctionDecl
= copy
;
188 std::string
VirtualDown::niceName(const CXXMethodDecl
* cxxMethodDecl
)
190 std::string s
= cxxMethodDecl
->getReturnType().getCanonicalType().getAsString() + " "
191 + cxxMethodDecl
->getQualifiedNameAsString() + "(";
192 for (const ParmVarDecl
* pParmVarDecl
: cxxMethodDecl
->parameters())
194 s
+= pParmVarDecl
->getType().getCanonicalType().getAsString();
198 if (cxxMethodDecl
->isConst())
205 std::string
VirtualDown::toString(SourceLocation loc
)
207 SourceLocation expansionLoc
= compiler
.getSourceManager().getExpansionLoc(loc
);
208 StringRef name
= getFilenameOfLocation(expansionLoc
);
209 std::string sourceLocation
210 = std::string(name
.substr(strlen(SRCDIR
) + 1)) + ":"
211 + std::to_string(compiler
.getSourceManager().getSpellingLineNumber(expansionLoc
));
212 loplugin::normalizeDotDotInFilePath(sourceLocation
);
213 return sourceLocation
;
216 loplugin::Plugin::Registration
<VirtualDown
> X("virtualdown", false);
219 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */