Version 6.1.0.2, tag libreoffice-6.1.0.2
[LibreOffice.git] / compilerplugins / clang / virtualdown.cxx
blob8c7ef41494d9ae89914f892fe546a9bfa6531ec6
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 "compat.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=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+
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 RecursiveASTVisitor<VirtualDown>, public loplugin::Plugin
47 public:
48 explicit VirtualDown(loplugin::InstantiationData const& data)
49 : Plugin(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 #if CLANG_VERSION >= 50000
78 bool TraverseCXXDeductionGuideDecl(CXXDeductionGuideDecl*);
79 #endif
80 private:
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))
90 return true;
92 if (!methodDecl->isThisDeclarationADefinition() || !methodDecl->isVirtual()
93 || methodDecl->isDeleted())
95 return true;
97 methodDecl = methodDecl->getCanonicalDecl();
98 // ignore stuff that forms part of the stable URE interface
99 if (isInUnoIncludeFile(methodDecl))
101 return true;
104 std::string aNiceName = niceName(methodDecl);
106 if (isa<CXXDestructorDecl>(methodDecl))
107 return true;
108 if (isa<CXXConstructorDecl>(methodDecl))
109 return true;
111 if (methodDecl->size_overridden_methods() == 0)
112 definitionSet.insert({ aNiceName, toString(methodDecl->getLocation()) });
114 return true;
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);
127 if (dr)
129 calleeFunctionDecl = dyn_cast<FunctionDecl>(dr->getDecl());
130 if (calleeFunctionDecl)
131 goto gotfunc;
133 return true;
136 gotfunc:
138 // ignore recursive calls
139 if (currentFunctionDecl == calleeFunctionDecl)
140 return true;
142 auto cxxMethodDecl = dyn_cast<CXXMethodDecl>(calleeFunctionDecl);
143 if (!cxxMethodDecl)
144 return true;
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));
153 return true;
156 bool VirtualDown::TraverseFunctionDecl(FunctionDecl* f)
158 auto copy = currentFunctionDecl;
159 currentFunctionDecl = f;
160 bool ret = RecursiveASTVisitor::TraverseFunctionDecl(f);
161 currentFunctionDecl = copy;
162 return ret;
164 bool VirtualDown::TraverseCXXMethodDecl(CXXMethodDecl* f)
166 auto copy = currentFunctionDecl;
167 currentFunctionDecl = f;
168 bool ret = RecursiveASTVisitor::TraverseCXXMethodDecl(f);
169 currentFunctionDecl = copy;
170 return ret;
172 bool VirtualDown::TraverseCXXConversionDecl(CXXConversionDecl* f)
174 auto copy = currentFunctionDecl;
175 currentFunctionDecl = f;
176 bool ret = RecursiveASTVisitor::TraverseCXXConversionDecl(f);
177 currentFunctionDecl = copy;
178 return ret;
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;
187 return ret;
189 #endif
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();
198 s += ",";
200 s += ")";
201 if (cxxMethodDecl->isConst())
203 s += "const";
205 return s;
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: */