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=1 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
<ConstantParam
>
60 explicit ConstantParam(loplugin::InstantiationData
const & data
): loplugin::FunctionAddress
<ConstantParam
>(data
) {}
62 virtual void run() override
64 // ignore some files that make clang crash inside EvaluateAsInt
65 std::string
fn( compiler
.getSourceManager().getFileEntryForID(
66 compiler
.getSourceManager().getMainFileID())->getName() );
67 loplugin::normalizeDotDotInFilePath(fn
);
68 if (loplugin::isSamePathname(fn
, SRCDIR
"/basegfx/source/matrix/b2dhommatrix.cxx")
69 || loplugin::isSamePathname(fn
, SRCDIR
"/basegfx/source/matrix/b3dhommatrix.cxx"))
72 TraverseDecl(compiler
.getASTContext().getTranslationUnitDecl());
74 // this catches places that take the address of a method
75 for (auto functionDecl
: getFunctionsWithAddressTaken())
77 for (unsigned i
= 0; i
< functionDecl
->getNumParams(); ++i
)
78 addToCallSet(functionDecl
, i
, functionDecl
->getParamDecl(i
)->getName(), "unknown3");
81 // dump all our output in one write call - this is to try and limit IO "crosstalk" between multiple processes
82 // writing to the same logfile
85 for (const MyCallSiteInfo
& s
: callSet
)
86 output
+= s
.returnType
+ "\t" + s
.nameAndParams
+ "\t" + s
.sourceLocation
+ "\t"
87 + s
.paramName
+ "\t" + s
.paramType
+ "\t" + s
.callValue
+ "\n";
89 myfile
.open( WORKDIR
"/loplugin.constantparam.log", std::ios::app
| std::ios::out
);
94 bool shouldVisitTemplateInstantiations () const { return true; }
95 bool shouldVisitImplicitCode () const { return true; }
97 bool VisitCallExpr( const CallExpr
* );
98 bool VisitCXXConstructExpr( const CXXConstructExpr
* );
100 void addToCallSet(const FunctionDecl
* functionDecl
, int paramIndex
, llvm::StringRef paramName
, const std::string
& callValue
);
101 std::string
getCallValue(const Expr
* arg
);
104 void ConstantParam::addToCallSet(const FunctionDecl
* functionDecl
, int paramIndex
, llvm::StringRef paramName
, const std::string
& callValue
)
106 if (functionDecl
->getInstantiatedFromMemberFunction())
107 functionDecl
= functionDecl
->getInstantiatedFromMemberFunction();
108 else if (functionDecl
->getClassScopeSpecializationPattern())
109 functionDecl
= functionDecl
->getClassScopeSpecializationPattern();
110 else if (functionDecl
->getTemplateInstantiationPattern())
111 functionDecl
= functionDecl
->getTemplateInstantiationPattern();
113 if (!functionDecl
->getNameInfo().getLoc().isValid())
115 if (functionDecl
->isVariadic())
117 if (ignoreLocation(functionDecl
))
119 // ignore stuff that forms part of the stable URE interface
120 if (isInUnoIncludeFile(functionDecl
))
122 SourceLocation expansionLoc
= compiler
.getSourceManager().getExpansionLoc( functionDecl
->getLocation() );
123 StringRef filename
= compiler
.getSourceManager().getFilename(expansionLoc
);
124 if (!loplugin::hasPathnamePrefix(filename
, SRCDIR
"/"))
126 filename
= filename
.substr(strlen(SRCDIR
)+1);
129 MyCallSiteInfo aInfo
;
130 aInfo
.returnType
= functionDecl
->getReturnType().getCanonicalType().getAsString();
132 if (isa
<CXXMethodDecl
>(functionDecl
)) {
133 const CXXRecordDecl
* recordDecl
= dyn_cast
<CXXMethodDecl
>(functionDecl
)->getParent();
134 aInfo
.nameAndParams
+= recordDecl
->getQualifiedNameAsString();
135 aInfo
.nameAndParams
+= "::";
137 aInfo
.nameAndParams
+= functionDecl
->getNameAsString() + "(";
139 for (const ParmVarDecl
*pParmVarDecl
: compat::parameters(*functionDecl
)) {
143 aInfo
.nameAndParams
+= ",";
144 aInfo
.nameAndParams
+= pParmVarDecl
->getType().getCanonicalType().getAsString();
146 aInfo
.nameAndParams
+= ")";
147 if (isa
<CXXMethodDecl
>(functionDecl
) && dyn_cast
<CXXMethodDecl
>(functionDecl
)->isConst()) {
148 aInfo
.nameAndParams
+= " const";
150 aInfo
.paramName
= paramName
;
151 aInfo
.paramIndex
= paramIndex
;
152 if (paramIndex
< (int)functionDecl
->getNumParams())
153 aInfo
.paramType
= functionDecl
->getParamDecl(paramIndex
)->getType().getCanonicalType().getAsString();
154 aInfo
.callValue
= callValue
;
156 aInfo
.sourceLocation
= filename
.str() + ":" + std::to_string(compiler
.getSourceManager().getSpellingLineNumber(expansionLoc
));
157 loplugin::normalizeDotDotInFilePath(aInfo
.sourceLocation
);
159 callSet
.insert(aInfo
);
162 std::string
ConstantParam::getCallValue(const Expr
* arg
)
164 arg
= arg
->IgnoreParenCasts();
165 if (isa
<CXXDefaultArgExpr
>(arg
)) {
166 arg
= dyn_cast
<CXXDefaultArgExpr
>(arg
)->getExpr();
168 arg
= arg
->IgnoreParenCasts();
169 // ignore this, it seems to trigger an infinite recursion
170 if (isa
<UnaryExprOrTypeTraitExpr
>(arg
)) {
174 if (arg
->EvaluateAsInt(x1
, compiler
.getASTContext()))
176 return x1
.toString(10);
178 if (isa
<CXXNullPtrLiteralExpr
>(arg
)) {
181 if (isa
<MaterializeTemporaryExpr
>(arg
))
183 const CXXBindTemporaryExpr
* strippedArg
= dyn_cast_or_null
<CXXBindTemporaryExpr
>(arg
->IgnoreParenCasts());
186 auto temp
= dyn_cast
<CXXTemporaryObjectExpr
>(strippedArg
->getSubExpr());
187 if (temp
->getNumArgs() == 0)
189 if (loplugin::TypeCheck(temp
->getType()).Class("OUString").Namespace("rtl").GlobalNamespace()) {
192 if (loplugin::TypeCheck(temp
->getType()).Class("OString").Namespace("rtl").GlobalNamespace()) {
195 return "defaultConstruct";
200 // Get the expression contents.
201 // This helps us find params which are always initialised with something like "OUString()".
202 SourceManager
& SM
= compiler
.getSourceManager();
203 SourceLocation startLoc
= arg
->getLocStart();
204 SourceLocation endLoc
= arg
->getLocEnd();
205 const char *p1
= SM
.getCharacterData( startLoc
);
206 const char *p2
= SM
.getCharacterData( endLoc
);
207 if (!p1
|| !p2
|| (p2
- p1
) < 0 || (p2
- p1
) > 40) {
210 unsigned n
= Lexer::MeasureTokenLength( endLoc
, SM
, compiler
.getLangOpts());
211 std::string
s( p1
, p2
- p1
+ n
);
212 // strip linefeed and tab characters so they don't interfere with the parsing of the log file
213 std::replace( s
.begin(), s
.end(), '\r', ' ');
214 std::replace( s
.begin(), s
.end(), '\n', ' ');
215 std::replace( s
.begin(), s
.end(), '\t', ' ');
217 // now normalize the value. For some params, like OUString, we can pass it as OUString() or "" and they are the same thing
218 if (s
== "OUString()")
220 else if (s
== "OString()")
222 else if (s
== "aEmptyOUStr") //sw
224 else if (s
== "EMPTY_OUSTRING")//sc
226 else if (s
== "GetEmptyOUString()") //sc
231 bool ConstantParam::VisitCallExpr(const CallExpr
* callExpr
) {
232 if (ignoreLocation(callExpr
)) {
235 const FunctionDecl
* functionDecl
;
236 if (isa
<CXXMemberCallExpr
>(callExpr
)) {
237 functionDecl
= dyn_cast
<CXXMemberCallExpr
>(callExpr
)->getMethodDecl();
240 functionDecl
= callExpr
->getDirectCallee();
244 functionDecl
= functionDecl
->getCanonicalDecl();
245 // method overrides don't always specify the same default params (although they probably should)
246 // so we need to work our way up to the root method
247 while (isa
<CXXMethodDecl
>(functionDecl
)) {
248 const CXXMethodDecl
* methodDecl
= dyn_cast
<CXXMethodDecl
>(functionDecl
);
249 if (methodDecl
->size_overridden_methods()==0)
251 functionDecl
= *methodDecl
->begin_overridden_methods();
253 // work our way back to the root definition for template methods
254 if (functionDecl
->getInstantiatedFromMemberFunction())
255 functionDecl
= functionDecl
->getInstantiatedFromMemberFunction();
256 else if (functionDecl
->getClassScopeSpecializationPattern())
257 functionDecl
= functionDecl
->getClassScopeSpecializationPattern();
258 else if (functionDecl
->getTemplateInstantiationPattern())
259 functionDecl
= functionDecl
->getTemplateInstantiationPattern();
261 unsigned len
= std::max(callExpr
->getNumArgs(), functionDecl
->getNumParams());
262 for (unsigned i
= 0; i
< len
; ++i
) {
264 if (i
< callExpr
->getNumArgs())
265 valExpr
= callExpr
->getArg(i
);
266 else if (i
< functionDecl
->getNumParams() && functionDecl
->getParamDecl(i
)->hasDefaultArg())
267 valExpr
= functionDecl
->getParamDecl(i
)->getDefaultArg();
269 // can happen in template code
271 std::string callValue
= getCallValue(valExpr
);
272 std::string paramName
= i
< functionDecl
->getNumParams()
273 ? functionDecl
->getParamDecl(i
)->getName()
274 : llvm::StringRef("###" + std::to_string(i
));
275 addToCallSet(functionDecl
, i
, paramName
, callValue
);
280 bool ConstantParam::VisitCXXConstructExpr( const CXXConstructExpr
* constructExpr
)
282 const CXXConstructorDecl
* constructorDecl
= constructExpr
->getConstructor();
283 constructorDecl
= constructorDecl
->getCanonicalDecl();
285 unsigned len
= std::max(constructExpr
->getNumArgs(), constructorDecl
->getNumParams());
286 for (unsigned i
= 0; i
< len
; ++i
) {
288 if (i
< constructExpr
->getNumArgs())
289 valExpr
= constructExpr
->getArg(i
);
290 else if (i
< constructorDecl
->getNumParams() && constructorDecl
->getParamDecl(i
)->hasDefaultArg())
291 valExpr
= constructorDecl
->getParamDecl(i
)->getDefaultArg();
293 // can happen in template code
295 std::string callValue
= getCallValue(valExpr
);
296 std::string paramName
= i
< constructorDecl
->getNumParams()
297 ? constructorDecl
->getParamDecl(i
)->getName()
298 : llvm::StringRef("###" + std::to_string(i
));
299 addToCallSet(constructorDecl
, i
, paramName
, callValue
);
305 loplugin::Plugin::Registration
< ConstantParam
> X("constantparam", false);
309 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */