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/.
18 #include "functionaddress.hxx"
21 Find params on methods where the param is only ever passed as a single constant value.
23 The process goes something like this:
25 $ make FORCE_COMPILE=all COMPILER_PLUGIN_TOOL='constantparam' check
26 $ ./compilerplugins/clang/constantparam.py
28 TODO look for OUString and OString params and check for call-params that are always either "" or default constructed
30 FIXME this plugin manages to trigger crashes inside clang, when calling EvaluateAsInt, so I end up disabling it for a handful of files
38 std::string returnType
;
39 std::string nameAndParams
;
40 std::string paramName
;
41 std::string paramType
;
42 int paramIndex
; // because in some declarations the names are empty
43 std::string callValue
;
44 std::string sourceLocation
;
46 bool operator < (const MyCallSiteInfo
&lhs
, const MyCallSiteInfo
&rhs
)
48 return std::tie(lhs
.sourceLocation
, lhs
.paramIndex
, lhs
.callValue
)
49 < std::tie(rhs
.sourceLocation
, rhs
.paramIndex
, rhs
.callValue
);
53 // try to limit the voluminous output a little
54 static std::set
<MyCallSiteInfo
> callSet
;
57 public loplugin::FunctionAddress
<loplugin::FilteringPlugin
<ConstantParam
>>
60 explicit ConstantParam(loplugin::InstantiationData
const & data
): FunctionAddress(data
) {}
62 virtual void run() override
64 handler
.enableTreeWideAnalysisMode();
66 // ignore some files that make clang crash inside EvaluateAsInt
67 std::string
fn(handler
.getMainFileName());
68 loplugin::normalizeDotDotInFilePath(fn
);
69 if (loplugin::isSamePathname(fn
, SRCDIR
"/basegfx/source/matrix/b2dhommatrix.cxx")
70 || loplugin::isSamePathname(fn
, SRCDIR
"/basegfx/source/matrix/b3dhommatrix.cxx"))
73 TraverseDecl(compiler
.getASTContext().getTranslationUnitDecl());
75 // this catches places that take the address of a method
76 for (auto functionDecl
: getFunctionsWithAddressTaken())
78 for (unsigned i
= 0; i
< functionDecl
->getNumParams(); ++i
)
79 addToCallSet(functionDecl
, i
, functionDecl
->getParamDecl(i
)->getName(), "unknown3");
82 // dump all our output in one write call - this is to try and limit IO "crosstalk" between multiple processes
83 // writing to the same logfile
86 for (const MyCallSiteInfo
& s
: callSet
)
87 output
+= s
.returnType
+ "\t" + s
.nameAndParams
+ "\t" + s
.sourceLocation
+ "\t"
88 + s
.paramName
+ "\t" + s
.paramType
+ "\t" + s
.callValue
+ "\n";
90 myfile
.open( WORKDIR
"/loplugin.constantparam.log", std::ios::app
| std::ios::out
);
95 bool shouldVisitTemplateInstantiations () const { return true; }
96 bool shouldVisitImplicitCode () const { return true; }
98 bool VisitCallExpr( const CallExpr
* );
99 bool VisitCXXConstructExpr( const CXXConstructExpr
* );
101 void addToCallSet(const FunctionDecl
* functionDecl
, int paramIndex
, llvm::StringRef paramName
, const std::string
& callValue
);
102 std::string
getCallValue(const Expr
* arg
);
105 void ConstantParam::addToCallSet(const FunctionDecl
* functionDecl
, int paramIndex
, llvm::StringRef paramName
, const std::string
& callValue
)
107 if (functionDecl
->getInstantiatedFromMemberFunction())
108 functionDecl
= functionDecl
->getInstantiatedFromMemberFunction();
109 else if (functionDecl
->getTemplateInstantiationPattern())
110 functionDecl
= functionDecl
->getTemplateInstantiationPattern();
112 if (!functionDecl
->getNameInfo().getLoc().isValid())
114 if (functionDecl
->isVariadic())
116 // ignore stuff that forms part of the stable URE interface
117 if (isInUnoIncludeFile(functionDecl
))
119 SourceLocation expansionLoc
= compiler
.getSourceManager().getExpansionLoc( functionDecl
->getLocation() );
120 StringRef filename
= getFilenameOfLocation(expansionLoc
);
121 if (!loplugin::hasPathnamePrefix(filename
, SRCDIR
"/"))
123 filename
= filename
.substr(strlen(SRCDIR
)+1);
125 MyCallSiteInfo aInfo
;
126 aInfo
.returnType
= functionDecl
->getReturnType().getCanonicalType().getAsString();
128 if (isa
<CXXMethodDecl
>(functionDecl
)) {
129 const CXXRecordDecl
* recordDecl
= dyn_cast
<CXXMethodDecl
>(functionDecl
)->getParent();
130 aInfo
.nameAndParams
+= recordDecl
->getQualifiedNameAsString();
131 aInfo
.nameAndParams
+= "::";
133 aInfo
.nameAndParams
+= functionDecl
->getNameAsString() + "(";
135 for (const ParmVarDecl
*pParmVarDecl
: functionDecl
->parameters()) {
139 aInfo
.nameAndParams
+= ",";
140 aInfo
.nameAndParams
+= pParmVarDecl
->getType().getCanonicalType().getAsString();
142 aInfo
.nameAndParams
+= ")";
143 if (isa
<CXXMethodDecl
>(functionDecl
) && dyn_cast
<CXXMethodDecl
>(functionDecl
)->isConst()) {
144 aInfo
.nameAndParams
+= " const";
146 aInfo
.paramName
= paramName
.str();
147 aInfo
.paramIndex
= paramIndex
;
148 if (paramIndex
< (int)functionDecl
->getNumParams())
149 aInfo
.paramType
= functionDecl
->getParamDecl(paramIndex
)->getType().getCanonicalType().getAsString();
151 aInfo
.callValue
= callValue
;
152 aInfo
.sourceLocation
= filename
.str() + ":" + std::to_string(compiler
.getSourceManager().getSpellingLineNumber(expansionLoc
));
153 loplugin::normalizeDotDotInFilePath(aInfo
.sourceLocation
);
155 callSet
.insert(aInfo
);
158 std::string
ConstantParam::getCallValue(const Expr
* arg
)
160 arg
= arg
->IgnoreParenCasts();
161 if (isa
<CXXDefaultArgExpr
>(arg
)) {
162 arg
= dyn_cast
<CXXDefaultArgExpr
>(arg
)->getExpr();
164 arg
= arg
->IgnoreParenCasts();
165 // ignore this, it seems to trigger an infinite recursion
166 if (isa
<UnaryExprOrTypeTraitExpr
>(arg
))
168 if (arg
->isValueDependent())
171 if (compat::EvaluateAsInt(arg
, x1
, compiler
.getASTContext()))
173 return compat::toString(x1
, 10);
175 if (isa
<CXXNullPtrLiteralExpr
>(arg
)) {
178 if (isa
<MaterializeTemporaryExpr
>(arg
))
180 const CXXBindTemporaryExpr
* strippedArg
= dyn_cast_or_null
<CXXBindTemporaryExpr
>(arg
->IgnoreParenCasts());
183 auto temp
= dyn_cast
<CXXTemporaryObjectExpr
>(strippedArg
->getSubExpr());
184 if (temp
->getNumArgs() == 0)
186 if (loplugin::TypeCheck(temp
->getType()).Class("OUString").Namespace("rtl").GlobalNamespace()) {
189 if (loplugin::TypeCheck(temp
->getType()).Class("OString").Namespace("rtl").GlobalNamespace()) {
192 return "defaultConstruct";
197 // Get the expression contents.
198 // This helps us find params which are always initialised with something like "OUString()".
199 SourceManager
& SM
= compiler
.getSourceManager();
200 SourceLocation startLoc
= arg
->getBeginLoc();
201 SourceLocation endLoc
= arg
->getEndLoc();
202 const char *p1
= SM
.getCharacterData( startLoc
);
203 const char *p2
= SM
.getCharacterData( endLoc
);
204 if (!p1
|| !p2
|| (p2
- p1
) < 0 || (p2
- p1
) > 40) {
207 unsigned n
= Lexer::MeasureTokenLength( endLoc
, SM
, compiler
.getLangOpts());
208 std::string
s( p1
, p2
- p1
+ n
);
209 // sanitize call value, makes using command line tools (and python) much less error prone
210 for (auto const & ch
: s
)
214 // now normalize the value. For some params, like OUString, we can pass it as OUString() or "" and they are the same thing
215 if (s
== "OUString()")
217 else if (s
== "OString()")
222 bool ConstantParam::VisitCallExpr(const CallExpr
* callExpr
) {
223 if (ignoreLocation(callExpr
)) {
226 const FunctionDecl
* functionDecl
;
227 if (isa
<CXXMemberCallExpr
>(callExpr
)) {
228 functionDecl
= dyn_cast
<CXXMemberCallExpr
>(callExpr
)->getMethodDecl();
231 functionDecl
= callExpr
->getDirectCallee();
235 functionDecl
= functionDecl
->getCanonicalDecl();
236 // method overrides don't always specify the same default params (although they probably should)
237 // so we need to work our way up to the root method
238 while (isa
<CXXMethodDecl
>(functionDecl
)) {
239 const CXXMethodDecl
* methodDecl
= dyn_cast
<CXXMethodDecl
>(functionDecl
);
240 if (methodDecl
->size_overridden_methods()==0)
242 functionDecl
= *methodDecl
->begin_overridden_methods();
244 // work our way back to the root definition for template methods
245 if (functionDecl
->getInstantiatedFromMemberFunction())
246 functionDecl
= functionDecl
->getInstantiatedFromMemberFunction();
247 else if (functionDecl
->getTemplateInstantiationPattern())
248 functionDecl
= functionDecl
->getTemplateInstantiationPattern();
250 unsigned len
= std::max(callExpr
->getNumArgs(), functionDecl
->getNumParams());
251 for (unsigned i
= 0; i
< len
; ++i
) {
253 if (i
< callExpr
->getNumArgs())
254 valExpr
= callExpr
->getArg(i
);
255 else if (i
< functionDecl
->getNumParams() && functionDecl
->getParamDecl(i
)->hasDefaultArg())
256 valExpr
= functionDecl
->getParamDecl(i
)->getDefaultArg();
258 // can happen in template code
260 std::string callValue
= getCallValue(valExpr
);
261 std::string paramName
= i
< functionDecl
->getNumParams()
262 ? functionDecl
->getParamDecl(i
)->getName().str()
263 : "###" + std::to_string(i
);
264 addToCallSet(functionDecl
, i
, paramName
, callValue
);
269 bool ConstantParam::VisitCXXConstructExpr( const CXXConstructExpr
* constructExpr
)
271 const CXXConstructorDecl
* constructorDecl
= constructExpr
->getConstructor();
272 constructorDecl
= constructorDecl
->getCanonicalDecl();
274 unsigned len
= std::max(constructExpr
->getNumArgs(), constructorDecl
->getNumParams());
275 for (unsigned i
= 0; i
< len
; ++i
) {
277 if (i
< constructExpr
->getNumArgs())
278 valExpr
= constructExpr
->getArg(i
);
279 else if (i
< constructorDecl
->getNumParams() && constructorDecl
->getParamDecl(i
)->hasDefaultArg())
280 valExpr
= constructorDecl
->getParamDecl(i
)->getDefaultArg();
282 // can happen in template code
284 std::string callValue
= getCallValue(valExpr
);
285 std::string paramName
= i
< constructorDecl
->getNumParams()
286 ? constructorDecl
->getParamDecl(i
)->getName().str()
287 : "###" + std::to_string(i
);
288 addToCallSet(constructorDecl
, i
, paramName
, callValue
);
294 loplugin::Plugin::Registration
< ConstantParam
> X("constantparam", false);
298 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */