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/.
21 Look for virtual methods where all of the overrides either
23 (b) all return the same value
25 The process goes something like this:
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
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
);
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
64 explicit VirtualDead(loplugin::InstantiationData
const& 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
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";
83 myfile
.open(WORKDIR
"/loplugin.virtualdead.log", std::ios::app
| std::ios::out
);
87 bool shouldVisitTemplateInstantiations() const { return true; }
88 bool shouldVisitImplicitCode() const { return true; }
90 bool VisitCXXMethodDecl(const CXXMethodDecl
* decl
);
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();
113 if (cxxMethodDecl
->isConst())
120 bool VirtualDead::VisitCXXMethodDecl(const CXXMethodDecl
* methodDecl
)
122 if (!methodDecl
->isVirtual() || methodDecl
->isDeleted())
124 if (isa
<CXXDestructorDecl
>(methodDecl
))
126 // ignore stuff that forms part of the stable URE interface
127 if (isInUnoIncludeFile(methodDecl
->getCanonicalDecl()))
130 if (!methodDecl
->isThisDeclarationADefinition())
133 std::string returnValue
;
135 auto body
= methodDecl
->getBody();
138 auto compoundStmt
= dyn_cast
<CompoundStmt
>(body
);
140 returnValue
= "empty";
141 else if (compoundStmt
->size() == 0)
142 returnValue
= "empty";
145 if (auto returnStmt
= dyn_cast
<ReturnStmt
>(*compoundStmt
->body_begin()))
147 if (!returnStmt
->getRetValue())
148 returnValue
= "empty";
150 returnValue
= getCallValue(returnStmt
->getRetValue());
153 returnValue
= "unknown-stmt";
157 returnValue
= "empty";
159 std::string paramBitfield
;
160 for (auto it
= methodDecl
->param_begin(); it
!= methodDecl
->param_end(); ++it
)
163 paramBitfield
+= param
->getName().empty() ? "0" : "1";
166 markSuperclassMethods(methodDecl
, returnValue
, paramBitfield
);
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
});
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
))
203 if (arg
->isValueDependent())
206 if (compat::EvaluateAsInt(arg
, x1
, compiler
.getASTContext()))
208 return compat::toString(x1
, 10);
210 if (isa
<CXXNullPtrLiteralExpr
>(arg
))
214 if (isa
<MaterializeTemporaryExpr
>(arg
))
216 const CXXBindTemporaryExpr
* strippedArg
217 = dyn_cast_or_null
<CXXBindTemporaryExpr
>(arg
->IgnoreParenCasts());
220 auto temp
= dyn_cast
<CXXTemporaryObjectExpr
>(strippedArg
->getSubExpr());
221 if (temp
->getNumArgs() == 0)
223 if (loplugin::TypeCheck(temp
->getType())
230 if (loplugin::TypeCheck(temp
->getType())
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)
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()")
263 else if (s
== "OString()")
266 if (s
[0] == '"' || s
[0] == '\'')
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: */