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/.
18 Look for virtual methods where we never call the defining virtual method, and only call the overriding virtual
19 methods, which indicates a places where the virtual-ness is unwarranted, normally a result of premature abstraction.
21 The process goes something like this:
23 $ make FORCE_COMPILE_ALL=1 COMPILER_PLUGIN_TOOL='VirtualDown' check
24 $ ./compilerplugins/clang/VirtualDown.py
26 @TODO for some reason, we get false+ for operator== methods
27 @TODO some templates confuse it and we get false+
36 std::string sourceLocation
;
38 bool operator<(const MyFuncInfo
& lhs
, const MyFuncInfo
& rhs
) { return lhs
.name
< rhs
.name
; }
40 // try to limit the voluminous output a little
41 static std::set
<MyFuncInfo
> definitionSet
;
42 static std::set
<std::string
> callSet
;
44 class VirtualDown
: public loplugin::FilteringPlugin
<VirtualDown
>
47 explicit VirtualDown(loplugin::InstantiationData
const& data
)
48 : FilteringPlugin(data
)
52 virtual void run() override
54 TraverseDecl(compiler
.getASTContext().getTranslationUnitDecl());
56 // dump all our output in one write call - this is to try and limit IO "crosstalk" between multiple processes
57 // writing to the same logfile
59 for (const MyFuncInfo
& s
: definitionSet
)
60 output
+= "definition:\t" + s
.name
+ "\t" + s
.sourceLocation
+ "\n";
61 for (const std::string
& s
: callSet
)
62 output
+= "call:\t" + s
+ "\n";
64 myfile
.open(WORKDIR
"/loplugin.virtualdown.log", std::ios::app
| std::ios::out
);
68 bool shouldVisitTemplateInstantiations() const { return true; }
69 bool shouldVisitImplicitCode() const { return true; }
71 bool VisitCXXMethodDecl(CXXMethodDecl
const*);
72 bool VisitCXXMemberCallExpr(CXXMemberCallExpr
const*);
73 bool TraverseFunctionDecl(FunctionDecl
*);
74 bool TraverseCXXMethodDecl(CXXMethodDecl
*);
75 bool TraverseCXXConversionDecl(CXXConversionDecl
*);
76 bool TraverseCXXDeductionGuideDecl(CXXDeductionGuideDecl
*);
79 std::string
toString(SourceLocation loc
);
80 std::string
niceName(const CXXMethodDecl
* functionDecl
);
81 FunctionDecl
const* currentFunctionDecl
= nullptr;
84 bool VirtualDown::VisitCXXMethodDecl(const CXXMethodDecl
* methodDecl
)
86 if (ignoreLocation(methodDecl
))
90 if (!methodDecl
->isThisDeclarationADefinition() || !methodDecl
->isVirtual()
91 || methodDecl
->isDeleted())
95 methodDecl
= methodDecl
->getCanonicalDecl();
96 // ignore stuff that forms part of the stable URE interface
97 if (isInUnoIncludeFile(methodDecl
))
102 std::string aNiceName
= niceName(methodDecl
);
104 if (isa
<CXXDestructorDecl
>(methodDecl
))
106 if (isa
<CXXConstructorDecl
>(methodDecl
))
109 if (methodDecl
->size_overridden_methods() == 0)
110 definitionSet
.insert({ aNiceName
, toString(methodDecl
->getLocation()) });
115 bool VirtualDown::VisitCXXMemberCallExpr(CXXMemberCallExpr
const* expr
)
117 // Note that I don't ignore ANYTHING here, because I want to get calls to my code that result
118 // from template instantiation deep inside the STL and other external code
120 FunctionDecl
const* calleeFunctionDecl
= expr
->getDirectCallee();
121 if (calleeFunctionDecl
== nullptr)
123 Expr
const* callee
= expr
->getCallee()->IgnoreParenImpCasts();
124 DeclRefExpr
const* dr
= dyn_cast
<DeclRefExpr
>(callee
);
127 calleeFunctionDecl
= dyn_cast
<FunctionDecl
>(dr
->getDecl());
128 if (calleeFunctionDecl
)
136 // ignore recursive calls
137 if (currentFunctionDecl
== calleeFunctionDecl
)
140 auto cxxMethodDecl
= dyn_cast
<CXXMethodDecl
>(calleeFunctionDecl
);
144 while (cxxMethodDecl
->getTemplateInstantiationPattern())
145 cxxMethodDecl
= dyn_cast
<CXXMethodDecl
>(cxxMethodDecl
->getTemplateInstantiationPattern());
146 while (cxxMethodDecl
->getInstantiatedFromMemberFunction())
147 cxxMethodDecl
= dyn_cast
<CXXMethodDecl
>(cxxMethodDecl
->getInstantiatedFromMemberFunction());
148 if (cxxMethodDecl
->getLocation().isValid() && !ignoreLocation(cxxMethodDecl
))
149 callSet
.insert(niceName(cxxMethodDecl
));
154 bool VirtualDown::TraverseFunctionDecl(FunctionDecl
* f
)
156 auto copy
= currentFunctionDecl
;
157 currentFunctionDecl
= f
;
158 bool ret
= RecursiveASTVisitor::TraverseFunctionDecl(f
);
159 currentFunctionDecl
= copy
;
162 bool VirtualDown::TraverseCXXMethodDecl(CXXMethodDecl
* f
)
164 auto copy
= currentFunctionDecl
;
165 currentFunctionDecl
= f
;
166 bool ret
= RecursiveASTVisitor::TraverseCXXMethodDecl(f
);
167 currentFunctionDecl
= copy
;
170 bool VirtualDown::TraverseCXXConversionDecl(CXXConversionDecl
* f
)
172 auto copy
= currentFunctionDecl
;
173 currentFunctionDecl
= f
;
174 bool ret
= RecursiveASTVisitor::TraverseCXXConversionDecl(f
);
175 currentFunctionDecl
= copy
;
178 bool VirtualDown::TraverseCXXDeductionGuideDecl(CXXDeductionGuideDecl
* f
)
180 auto copy
= currentFunctionDecl
;
181 currentFunctionDecl
= f
;
182 bool ret
= RecursiveASTVisitor::TraverseCXXDeductionGuideDecl(f
);
183 currentFunctionDecl
= copy
;
187 std::string
VirtualDown::niceName(const CXXMethodDecl
* cxxMethodDecl
)
189 std::string s
= cxxMethodDecl
->getReturnType().getCanonicalType().getAsString() + " "
190 + cxxMethodDecl
->getQualifiedNameAsString() + "(";
191 for (const ParmVarDecl
* pParmVarDecl
: cxxMethodDecl
->parameters())
193 s
+= pParmVarDecl
->getType().getCanonicalType().getAsString();
197 if (cxxMethodDecl
->isConst())
204 std::string
VirtualDown::toString(SourceLocation loc
)
206 SourceLocation expansionLoc
= compiler
.getSourceManager().getExpansionLoc(loc
);
207 StringRef name
= getFilenameOfLocation(expansionLoc
);
208 std::string sourceLocation
209 = std::string(name
.substr(strlen(SRCDIR
) + 1)) + ":"
210 + std::to_string(compiler
.getSourceManager().getSpellingLineNumber(expansionLoc
));
211 loplugin::normalizeDotDotInFilePath(sourceLocation
);
212 return sourceLocation
;
215 loplugin::Plugin::Registration
<VirtualDown
> X("virtualdown", false);
218 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */