update credits
[LibreOffice.git] / compilerplugins / clang / virtualdown.cxx
blob946d8bc953bc23123ec3469c943ba85887d29f57
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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/.
8 */
10 #include <cassert>
11 #include <string>
12 #include <iostream>
13 #include <set>
14 #include "config_clang.h"
15 #include "plugin.hxx"
16 #include <fstream>
18 /**
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:
23 $ make check
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+
32 namespace
34 struct MyFuncInfo
36 std::string name;
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>
47 public:
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
59 std::string output;
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";
64 std::ofstream myfile;
65 myfile.open(WORKDIR "/loplugin.virtualdown.log", std::ios::app | std::ios::out);
66 myfile << output;
67 myfile.close();
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*);
79 private:
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))
89 return true;
91 if (!methodDecl->isThisDeclarationADefinition() || !methodDecl->isVirtual()
92 || methodDecl->isDeleted())
94 return true;
96 methodDecl = methodDecl->getCanonicalDecl();
97 // ignore stuff that forms part of the stable URE interface
98 if (isInUnoIncludeFile(methodDecl))
100 return true;
103 std::string aNiceName = niceName(methodDecl);
105 if (isa<CXXDestructorDecl>(methodDecl))
106 return true;
107 if (isa<CXXConstructorDecl>(methodDecl))
108 return true;
110 if (methodDecl->size_overridden_methods() == 0)
111 definitionSet.insert({ aNiceName, toString(methodDecl->getLocation()) });
113 return true;
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);
126 if (dr)
128 calleeFunctionDecl = dyn_cast<FunctionDecl>(dr->getDecl());
129 if (calleeFunctionDecl)
130 goto gotfunc;
132 return true;
135 gotfunc:
137 // ignore recursive calls
138 if (currentFunctionDecl == calleeFunctionDecl)
139 return true;
141 auto cxxMethodDecl = dyn_cast<CXXMethodDecl>(calleeFunctionDecl);
142 if (!cxxMethodDecl)
143 return true;
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));
152 return true;
155 bool VirtualDown::TraverseFunctionDecl(FunctionDecl* f)
157 auto copy = currentFunctionDecl;
158 currentFunctionDecl = f;
159 bool ret = RecursiveASTVisitor::TraverseFunctionDecl(f);
160 currentFunctionDecl = copy;
161 return ret;
163 bool VirtualDown::TraverseCXXMethodDecl(CXXMethodDecl* f)
165 auto copy = currentFunctionDecl;
166 currentFunctionDecl = f;
167 bool ret = RecursiveASTVisitor::TraverseCXXMethodDecl(f);
168 currentFunctionDecl = copy;
169 return ret;
171 bool VirtualDown::TraverseCXXConversionDecl(CXXConversionDecl* f)
173 auto copy = currentFunctionDecl;
174 currentFunctionDecl = f;
175 bool ret = RecursiveASTVisitor::TraverseCXXConversionDecl(f);
176 currentFunctionDecl = copy;
177 return ret;
179 bool VirtualDown::TraverseCXXDeductionGuideDecl(CXXDeductionGuideDecl* f)
181 auto copy = currentFunctionDecl;
182 currentFunctionDecl = f;
183 bool ret = RecursiveASTVisitor::TraverseCXXDeductionGuideDecl(f);
184 currentFunctionDecl = copy;
185 return ret;
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();
195 s += ",";
197 s += ")";
198 if (cxxMethodDecl->isConst())
200 s += "const";
202 return s;
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: */