bump product version to 6.4.0.3
[LibreOffice.git] / compilerplugins / clang / virtualdown.cxx
blobff5fc373a1dd461a84b2dd32f13f4c87353b81f4
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 "plugin.hxx"
15 #include <fstream>
17 /**
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:
22 $ make check
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+
31 namespace
33 struct MyFuncInfo
35 std::string name;
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>
46 public:
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
58 std::string output;
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";
63 std::ofstream myfile;
64 myfile.open(WORKDIR "/loplugin.virtualdown.log", std::ios::app | std::ios::out);
65 myfile << output;
66 myfile.close();
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*);
78 private:
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))
88 return true;
90 if (!methodDecl->isThisDeclarationADefinition() || !methodDecl->isVirtual()
91 || methodDecl->isDeleted())
93 return true;
95 methodDecl = methodDecl->getCanonicalDecl();
96 // ignore stuff that forms part of the stable URE interface
97 if (isInUnoIncludeFile(methodDecl))
99 return true;
102 std::string aNiceName = niceName(methodDecl);
104 if (isa<CXXDestructorDecl>(methodDecl))
105 return true;
106 if (isa<CXXConstructorDecl>(methodDecl))
107 return true;
109 if (methodDecl->size_overridden_methods() == 0)
110 definitionSet.insert({ aNiceName, toString(methodDecl->getLocation()) });
112 return true;
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);
125 if (dr)
127 calleeFunctionDecl = dyn_cast<FunctionDecl>(dr->getDecl());
128 if (calleeFunctionDecl)
129 goto gotfunc;
131 return true;
134 gotfunc:
136 // ignore recursive calls
137 if (currentFunctionDecl == calleeFunctionDecl)
138 return true;
140 auto cxxMethodDecl = dyn_cast<CXXMethodDecl>(calleeFunctionDecl);
141 if (!cxxMethodDecl)
142 return true;
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));
151 return true;
154 bool VirtualDown::TraverseFunctionDecl(FunctionDecl* f)
156 auto copy = currentFunctionDecl;
157 currentFunctionDecl = f;
158 bool ret = RecursiveASTVisitor::TraverseFunctionDecl(f);
159 currentFunctionDecl = copy;
160 return ret;
162 bool VirtualDown::TraverseCXXMethodDecl(CXXMethodDecl* f)
164 auto copy = currentFunctionDecl;
165 currentFunctionDecl = f;
166 bool ret = RecursiveASTVisitor::TraverseCXXMethodDecl(f);
167 currentFunctionDecl = copy;
168 return ret;
170 bool VirtualDown::TraverseCXXConversionDecl(CXXConversionDecl* f)
172 auto copy = currentFunctionDecl;
173 currentFunctionDecl = f;
174 bool ret = RecursiveASTVisitor::TraverseCXXConversionDecl(f);
175 currentFunctionDecl = copy;
176 return ret;
178 bool VirtualDown::TraverseCXXDeductionGuideDecl(CXXDeductionGuideDecl* f)
180 auto copy = currentFunctionDecl;
181 currentFunctionDecl = f;
182 bool ret = RecursiveASTVisitor::TraverseCXXDeductionGuideDecl(f);
183 currentFunctionDecl = copy;
184 return ret;
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();
194 s += ",";
196 s += ")";
197 if (cxxMethodDecl->isConst())
199 s += "const";
201 return s;
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: */