Version 7.6.3.2-android, tag libreoffice-7.6.3.2-android
[LibreOffice.git] / compilerplugins / clang / virtualdead.cxx
blobe3cdf2b45018dd796592f9386be30993c3bef089
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 "plugin.hxx"
11 #include "check.hxx"
12 #include "compat.hxx"
14 #include <cassert>
15 #include <string>
16 #include <iostream>
17 #include <set>
18 #include <fstream>
20 /**
21 Look for virtual methods where all of the overrides either
22 (a) do nothing
23 (b) all return the same value
25 The process goes something like this:
26 $ make check
27 $ make FORCE_COMPILE=all COMPILER_PLUGIN_TOOL='VirtualDead' check
28 $ ./compilerplugins/clang/VirtualDead.py
29 $ for dir in *; do make FORCE_COMPILE=all UPDATE_FILES=$dir COMPILER_PLUGIN_TOOL='removevirtuals' $dir; done
31 Note that the actual process may involve a fair amount of undoing, hand editing, and general messing around
32 to get it to work :-)
35 namespace
37 struct MyFuncInfo
39 std::string name;
40 std::string sourceLocation;
41 std::string returnValue;
43 bool operator<(const MyFuncInfo& lhs, const MyFuncInfo& rhs)
45 return std::tie(lhs.name, lhs.returnValue) < std::tie(rhs.name, rhs.returnValue);
47 struct MyParamInfo
49 std::string funcName;
50 std::string paramBitField;
52 bool operator<(const MyParamInfo& lhs, const MyParamInfo& rhs)
54 return std::tie(lhs.funcName, lhs.paramBitField) < std::tie(rhs.funcName, rhs.paramBitField);
57 // try to limit the voluminous output a little
58 static std::set<MyFuncInfo> definitionSet;
59 static std::set<MyParamInfo> paramUsedSet;
61 class VirtualDead : public RecursiveASTVisitor<VirtualDead>, public loplugin::Plugin
63 public:
64 explicit VirtualDead(loplugin::InstantiationData const& data)
65 : Plugin(data)
69 virtual void run() override
71 handler.enableTreeWideAnalysisMode();
73 TraverseDecl(compiler.getASTContext().getTranslationUnitDecl());
75 // dump all our output in one write call - this is to try and limit IO "crosstalk" between multiple processes
76 // writing to the same logfile
77 std::string output;
78 for (const MyFuncInfo& s : definitionSet)
79 output += "virtual:\t" + s.name + "\t" + s.sourceLocation + "\t" + s.returnValue + "\n";
80 for (const MyParamInfo& s : paramUsedSet)
81 output += "param:\t" + s.funcName + "\t" + s.paramBitField + "\n";
82 std::ofstream myfile;
83 myfile.open(WORKDIR "/loplugin.virtualdead.log", std::ios::app | std::ios::out);
84 myfile << output;
85 myfile.close();
87 bool shouldVisitTemplateInstantiations() const { return true; }
88 bool shouldVisitImplicitCode() const { return true; }
90 bool VisitCXXMethodDecl(const CXXMethodDecl* decl);
92 private:
93 std::string getCallValue(const Expr* arg);
94 std::string toString(SourceLocation loc);
95 void markSuperclassMethods(const CXXMethodDecl* methodDecl, const std::string& returnValue,
96 const std::string& paramBitField);
99 std::string niceName(const CXXMethodDecl* cxxMethodDecl)
101 while (cxxMethodDecl->getTemplateInstantiationPattern())
102 cxxMethodDecl = dyn_cast<CXXMethodDecl>(cxxMethodDecl->getTemplateInstantiationPattern());
103 while (cxxMethodDecl->getInstantiatedFromMemberFunction())
104 cxxMethodDecl = dyn_cast<CXXMethodDecl>(cxxMethodDecl->getInstantiatedFromMemberFunction());
105 std::string s = cxxMethodDecl->getReturnType().getCanonicalType().getAsString() + " "
106 + cxxMethodDecl->getQualifiedNameAsString() + "(";
107 for (const ParmVarDecl* pParmVarDecl : cxxMethodDecl->parameters())
109 s += pParmVarDecl->getType().getCanonicalType().getAsString();
110 s += ",";
112 s += ")";
113 if (cxxMethodDecl->isConst())
115 s += "const";
117 return s;
120 bool VirtualDead::VisitCXXMethodDecl(const CXXMethodDecl* methodDecl)
122 if (!methodDecl->isVirtual() || methodDecl->isDeleted())
123 return true;
124 if (isa<CXXDestructorDecl>(methodDecl))
125 return true;
126 // ignore stuff that forms part of the stable URE interface
127 if (isInUnoIncludeFile(methodDecl->getCanonicalDecl()))
128 return true;
130 if (!methodDecl->isThisDeclarationADefinition())
131 return true;
133 std::string returnValue;
135 auto body = methodDecl->getBody();
136 if (body)
138 auto compoundStmt = dyn_cast<CompoundStmt>(body);
139 if (!compoundStmt)
140 returnValue = "empty";
141 else if (compoundStmt->size() == 0)
142 returnValue = "empty";
143 else
145 if (auto returnStmt = dyn_cast<ReturnStmt>(*compoundStmt->body_begin()))
147 if (!returnStmt->getRetValue())
148 returnValue = "empty";
149 else
150 returnValue = getCallValue(returnStmt->getRetValue());
152 else
153 returnValue = "unknown-stmt";
156 else
157 returnValue = "empty";
159 std::string paramBitfield;
160 for (auto it = methodDecl->param_begin(); it != methodDecl->param_end(); ++it)
162 auto param = *it;
163 paramBitfield += param->getName().empty() ? "0" : "1";
166 markSuperclassMethods(methodDecl, returnValue, paramBitfield);
168 return true;
171 void VirtualDead::markSuperclassMethods(const CXXMethodDecl* methodDecl,
172 const std::string& returnValue,
173 std::string const& paramBitField)
175 if (methodDecl->size_overridden_methods() == 0)
177 std::string aNiceName = niceName(methodDecl);
178 definitionSet.insert(
179 { aNiceName, toString(methodDecl->getCanonicalDecl()->getLocation()), returnValue });
180 paramUsedSet.insert({ aNiceName, paramBitField });
181 return;
184 for (auto iter = methodDecl->begin_overridden_methods();
185 iter != methodDecl->end_overridden_methods(); ++iter)
187 const CXXMethodDecl* overriddenMethod = *iter;
188 markSuperclassMethods(overriddenMethod, returnValue, paramBitField);
192 std::string VirtualDead::getCallValue(const Expr* arg)
194 arg = arg->IgnoreParenCasts();
195 if (isa<CXXDefaultArgExpr>(arg))
197 arg = dyn_cast<CXXDefaultArgExpr>(arg)->getExpr();
199 arg = arg->IgnoreParenCasts();
200 // ignore this, it seems to trigger an infinite recursion
201 if (isa<UnaryExprOrTypeTraitExpr>(arg))
202 return "unknown1";
203 if (arg->isValueDependent())
204 return "unknown2";
205 APSInt x1;
206 if (compat::EvaluateAsInt(arg, x1, compiler.getASTContext()))
208 return compat::toString(x1, 10);
210 if (isa<CXXNullPtrLiteralExpr>(arg))
212 return "0";
214 if (isa<MaterializeTemporaryExpr>(arg))
216 const CXXBindTemporaryExpr* strippedArg
217 = dyn_cast_or_null<CXXBindTemporaryExpr>(arg->IgnoreParenCasts());
218 if (strippedArg)
220 auto temp = dyn_cast<CXXTemporaryObjectExpr>(strippedArg->getSubExpr());
221 if (temp->getNumArgs() == 0)
223 if (loplugin::TypeCheck(temp->getType())
224 .Class("OUString")
225 .Namespace("rtl")
226 .GlobalNamespace())
228 return "\"\"";
230 if (loplugin::TypeCheck(temp->getType())
231 .Class("OString")
232 .Namespace("rtl")
233 .GlobalNamespace())
235 return "\"\"";
237 return "defaultConstruct";
242 // Get the expression contents.
243 // This helps us find params which are always initialised with something like "OUString()".
244 SourceManager& SM = compiler.getSourceManager();
245 SourceLocation startLoc = arg->getBeginLoc();
246 SourceLocation endLoc = arg->getEndLoc();
247 const char* p1 = SM.getCharacterData(startLoc);
248 const char* p2 = SM.getCharacterData(endLoc);
249 if (!p1 || !p2 || (p2 - p1) < 0 || (p2 - p1) > 40)
251 return "unknown3";
253 unsigned n = Lexer::MeasureTokenLength(endLoc, SM, compiler.getLangOpts());
254 std::string s(p1, p2 - p1 + n);
255 // strip linefeed and tab characters so they don't interfere with the parsing of the log file
256 std::replace(s.begin(), s.end(), '\r', ' ');
257 std::replace(s.begin(), s.end(), '\n', ' ');
258 std::replace(s.begin(), s.end(), '\t', ' ');
260 // now normalize the value. For some params, like OUString, we can pass it as OUString() or "" and they are the same thing
261 if (s == "OUString()")
262 s = "\"\"";
263 else if (s == "OString()")
264 s = "\"\"";
266 if (s[0] == '"' || s[0] == '\'')
267 return s;
268 return "unknown4";
271 std::string VirtualDead::toString(SourceLocation loc)
273 SourceLocation expansionLoc = compiler.getSourceManager().getExpansionLoc(loc);
274 StringRef name = getFilenameOfLocation(expansionLoc);
275 std::string sourceLocation
276 = std::string(name.substr(strlen(SRCDIR) + 1)) + ":"
277 + std::to_string(compiler.getSourceManager().getSpellingLineNumber(expansionLoc));
278 loplugin::normalizeDotDotInFilePath(sourceLocation);
279 return sourceLocation;
282 loplugin::Plugin::Registration<VirtualDead> X("virtualdead", false);
285 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */