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/.
20 Look for virtual methods where all of the overrides either
22 (b) all return the same value
24 The process goes something like this:
26 $ make FORCE_COMPILE_ALL=1 COMPILER_PLUGIN_TOOL='VirtualDead' check
27 $ ./compilerplugins/clang/VirtualDead.py
28 $ for dir in *; do make FORCE_COMPILE_ALL=1 UPDATE_FILES=$dir COMPILER_PLUGIN_TOOL='removevirtuals' $dir; done
30 Note that the actual process may involve a fair amount of undoing, hand editing, and general messing around
39 std::string sourceLocation
;
40 std::string returnValue
;
42 bool operator<(const MyFuncInfo
& lhs
, const MyFuncInfo
& rhs
)
44 return std::tie(lhs
.name
, lhs
.returnValue
) < std::tie(rhs
.name
, rhs
.returnValue
);
49 std::string paramBitField
;
51 bool operator<(const MyParamInfo
& lhs
, const MyParamInfo
& rhs
)
53 return std::tie(lhs
.funcName
, lhs
.paramBitField
) < std::tie(rhs
.funcName
, rhs
.paramBitField
);
56 // try to limit the voluminous output a little
57 static std::set
<MyFuncInfo
> definitionSet
;
58 static std::set
<MyParamInfo
> paramUsedSet
;
60 class VirtualDead
: public RecursiveASTVisitor
<VirtualDead
>, public loplugin::Plugin
63 explicit VirtualDead(loplugin::InstantiationData
const& data
)
68 virtual void run() override
70 TraverseDecl(compiler
.getASTContext().getTranslationUnitDecl());
72 // dump all our output in one write call - this is to try and limit IO "crosstalk" between multiple processes
73 // writing to the same logfile
75 for (const MyFuncInfo
& s
: definitionSet
)
76 output
+= "virtual:\t" + s
.name
+ "\t" + s
.sourceLocation
+ "\t" + s
.returnValue
+ "\n";
77 for (const MyParamInfo
& s
: paramUsedSet
)
78 output
+= "param:\t" + s
.funcName
+ "\t" + s
.paramBitField
+ "\n";
80 myfile
.open(WORKDIR
"/loplugin.virtualdead.log", std::ios::app
| std::ios::out
);
84 bool shouldVisitTemplateInstantiations() const { return true; }
85 bool shouldVisitImplicitCode() const { return true; }
87 bool VisitCXXMethodDecl(const CXXMethodDecl
* decl
);
90 std::string
getCallValue(const Expr
* arg
);
91 std::string
toString(SourceLocation loc
);
92 void markSuperclassMethods(const CXXMethodDecl
* methodDecl
, const std::string
& returnValue
,
93 const std::string
& paramBitField
);
96 std::string
niceName(const CXXMethodDecl
* cxxMethodDecl
)
98 while (cxxMethodDecl
->getTemplateInstantiationPattern())
99 cxxMethodDecl
= dyn_cast
<CXXMethodDecl
>(cxxMethodDecl
->getTemplateInstantiationPattern());
100 while (cxxMethodDecl
->getInstantiatedFromMemberFunction())
101 cxxMethodDecl
= dyn_cast
<CXXMethodDecl
>(cxxMethodDecl
->getInstantiatedFromMemberFunction());
102 std::string s
= cxxMethodDecl
->getReturnType().getCanonicalType().getAsString() + " "
103 + cxxMethodDecl
->getQualifiedNameAsString() + "(";
104 for (const ParmVarDecl
* pParmVarDecl
: cxxMethodDecl
->parameters())
106 s
+= pParmVarDecl
->getType().getCanonicalType().getAsString();
110 if (cxxMethodDecl
->isConst())
117 bool VirtualDead::VisitCXXMethodDecl(const CXXMethodDecl
* methodDecl
)
119 if (ignoreLocation(methodDecl
))
121 if (!methodDecl
->isVirtual() || methodDecl
->isDeleted())
123 if (isa
<CXXDestructorDecl
>(methodDecl
))
125 // ignore stuff that forms part of the stable URE interface
126 if (isInUnoIncludeFile(methodDecl
->getCanonicalDecl()))
129 if (!methodDecl
->isThisDeclarationADefinition())
132 std::string returnValue
;
134 auto body
= methodDecl
->getBody();
137 auto compoundStmt
= dyn_cast
<CompoundStmt
>(body
);
139 returnValue
= "empty";
140 else if (compoundStmt
->size() == 0)
141 returnValue
= "empty";
144 if (auto returnStmt
= dyn_cast
<ReturnStmt
>(*compoundStmt
->body_begin()))
146 if (!returnStmt
->getRetValue())
147 returnValue
= "empty";
149 returnValue
= getCallValue(returnStmt
->getRetValue());
152 returnValue
= "unknown-stmt";
156 returnValue
= "empty";
158 std::string paramBitfield
;
159 for (auto it
= methodDecl
->param_begin(); it
!= methodDecl
->param_end(); ++it
)
162 paramBitfield
+= param
->getName().empty() ? "0" : "1";
165 markSuperclassMethods(methodDecl
, returnValue
, paramBitfield
);
170 void VirtualDead::markSuperclassMethods(const CXXMethodDecl
* methodDecl
,
171 const std::string
& returnValue
,
172 std::string
const& paramBitField
)
174 if (methodDecl
->size_overridden_methods() == 0)
176 std::string aNiceName
= niceName(methodDecl
);
177 definitionSet
.insert(
178 { aNiceName
, toString(methodDecl
->getCanonicalDecl()->getLocation()), returnValue
});
179 paramUsedSet
.insert({ aNiceName
, paramBitField
});
183 for (auto iter
= methodDecl
->begin_overridden_methods();
184 iter
!= methodDecl
->end_overridden_methods(); ++iter
)
186 const CXXMethodDecl
* overriddenMethod
= *iter
;
187 markSuperclassMethods(overriddenMethod
, returnValue
, paramBitField
);
191 std::string
VirtualDead::getCallValue(const Expr
* arg
)
193 arg
= arg
->IgnoreParenCasts();
194 if (isa
<CXXDefaultArgExpr
>(arg
))
196 arg
= dyn_cast
<CXXDefaultArgExpr
>(arg
)->getExpr();
198 arg
= arg
->IgnoreParenCasts();
199 // ignore this, it seems to trigger an infinite recursion
200 if (isa
<UnaryExprOrTypeTraitExpr
>(arg
))
202 if (arg
->isValueDependent())
205 if (compat::EvaluateAsInt(arg
, x1
, compiler
.getASTContext()))
207 return x1
.toString(10);
209 if (isa
<CXXNullPtrLiteralExpr
>(arg
))
213 if (isa
<MaterializeTemporaryExpr
>(arg
))
215 const CXXBindTemporaryExpr
* strippedArg
216 = dyn_cast_or_null
<CXXBindTemporaryExpr
>(arg
->IgnoreParenCasts());
219 auto temp
= dyn_cast
<CXXTemporaryObjectExpr
>(strippedArg
->getSubExpr());
220 if (temp
->getNumArgs() == 0)
222 if (loplugin::TypeCheck(temp
->getType())
229 if (loplugin::TypeCheck(temp
->getType())
236 return "defaultConstruct";
241 // Get the expression contents.
242 // This helps us find params which are always initialised with something like "OUString()".
243 SourceManager
& SM
= compiler
.getSourceManager();
244 SourceLocation startLoc
= compat::getBeginLoc(arg
);
245 SourceLocation endLoc
= compat::getEndLoc(arg
);
246 const char* p1
= SM
.getCharacterData(startLoc
);
247 const char* p2
= SM
.getCharacterData(endLoc
);
248 if (!p1
|| !p2
|| (p2
- p1
) < 0 || (p2
- p1
) > 40)
252 unsigned n
= Lexer::MeasureTokenLength(endLoc
, SM
, compiler
.getLangOpts());
253 std::string
s(p1
, p2
- p1
+ n
);
254 // strip linefeed and tab characters so they don't interfere with the parsing of the log file
255 std::replace(s
.begin(), s
.end(), '\r', ' ');
256 std::replace(s
.begin(), s
.end(), '\n', ' ');
257 std::replace(s
.begin(), s
.end(), '\t', ' ');
259 // now normalize the value. For some params, like OUString, we can pass it as OUString() or "" and they are the same thing
260 if (s
== "OUString()")
262 else if (s
== "OString()")
264 else if (s
== "aEmptyOUStr") //sw
266 else if (s
== "EMPTY_OUSTRING") //sc
268 else if (s
== "GetEmptyOUString()") //sc
271 if (s
[0] == '"' || s
[0] == '\'')
276 std::string
VirtualDead::toString(SourceLocation loc
)
278 SourceLocation expansionLoc
= compiler
.getSourceManager().getExpansionLoc(loc
);
279 StringRef name
= getFilenameOfLocation(expansionLoc
);
280 std::string sourceLocation
281 = std::string(name
.substr(strlen(SRCDIR
) + 1)) + ":"
282 + std::to_string(compiler
.getSourceManager().getSpellingLineNumber(expansionLoc
));
283 loplugin::normalizeDotDotInFilePath(sourceLocation
);
284 return sourceLocation
;
287 loplugin::Plugin::Registration
<VirtualDead
> X("virtualdead", false);
290 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */