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(handler
.getMainFileName());
66 loplugin::normalizeDotDotInFilePath(fn
);
67 if (loplugin::isSamePathname(fn
, SRCDIR
"/basegfx/source/matrix/b2dhommatrix.cxx")
68 || loplugin::isSamePathname(fn
, SRCDIR
"/basegfx/source/matrix/b3dhommatrix.cxx"))
71 TraverseDecl(compiler
.getASTContext().getTranslationUnitDecl());
73 // this catches places that take the address of a method
74 for (auto functionDecl
: getFunctionsWithAddressTaken())
76 for (unsigned i
= 0; i
< functionDecl
->getNumParams(); ++i
)
77 addToCallSet(functionDecl
, i
, functionDecl
->getParamDecl(i
)->getName(), "unknown3");
80 // dump all our output in one write call - this is to try and limit IO "crosstalk" between multiple processes
81 // writing to the same logfile
84 for (const MyCallSiteInfo
& s
: callSet
)
85 output
+= s
.returnType
+ "\t" + s
.nameAndParams
+ "\t" + s
.sourceLocation
+ "\t"
86 + s
.paramName
+ "\t" + s
.paramType
+ "\t" + s
.callValue
+ "\n";
88 myfile
.open( WORKDIR
"/loplugin.constantparam.log", std::ios::app
| std::ios::out
);
93 bool shouldVisitTemplateInstantiations () const { return true; }
94 bool shouldVisitImplicitCode () const { return true; }
96 bool VisitCallExpr( const CallExpr
* );
97 bool VisitCXXConstructExpr( const CXXConstructExpr
* );
99 void addToCallSet(const FunctionDecl
* functionDecl
, int paramIndex
, llvm::StringRef paramName
, const std::string
& callValue
);
100 std::string
getCallValue(const Expr
* arg
);
103 void ConstantParam::addToCallSet(const FunctionDecl
* functionDecl
, int paramIndex
, llvm::StringRef paramName
, const std::string
& callValue
)
105 if (functionDecl
->getInstantiatedFromMemberFunction())
106 functionDecl
= functionDecl
->getInstantiatedFromMemberFunction();
107 #if CLANG_VERSION < 90000
108 else if (functionDecl
->getClassScopeSpecializationPattern())
109 functionDecl
= functionDecl
->getClassScopeSpecializationPattern();
111 else if (functionDecl
->getTemplateInstantiationPattern())
112 functionDecl
= functionDecl
->getTemplateInstantiationPattern();
114 if (!functionDecl
->getNameInfo().getLoc().isValid())
116 if (functionDecl
->isVariadic())
118 if (ignoreLocation(functionDecl
))
120 // ignore stuff that forms part of the stable URE interface
121 if (isInUnoIncludeFile(functionDecl
))
123 SourceLocation expansionLoc
= compiler
.getSourceManager().getExpansionLoc( functionDecl
->getLocation() );
124 StringRef filename
= compiler
.getSourceManager().getFilename(expansionLoc
);
125 if (!loplugin::hasPathnamePrefix(filename
, SRCDIR
"/"))
127 filename
= filename
.substr(strlen(SRCDIR
)+1);
130 MyCallSiteInfo aInfo
;
131 aInfo
.returnType
= functionDecl
->getReturnType().getCanonicalType().getAsString();
133 if (isa
<CXXMethodDecl
>(functionDecl
)) {
134 const CXXRecordDecl
* recordDecl
= dyn_cast
<CXXMethodDecl
>(functionDecl
)->getParent();
135 aInfo
.nameAndParams
+= recordDecl
->getQualifiedNameAsString();
136 aInfo
.nameAndParams
+= "::";
138 aInfo
.nameAndParams
+= functionDecl
->getNameAsString() + "(";
140 for (const ParmVarDecl
*pParmVarDecl
: functionDecl
->parameters()) {
144 aInfo
.nameAndParams
+= ",";
145 aInfo
.nameAndParams
+= pParmVarDecl
->getType().getCanonicalType().getAsString();
147 aInfo
.nameAndParams
+= ")";
148 if (isa
<CXXMethodDecl
>(functionDecl
) && dyn_cast
<CXXMethodDecl
>(functionDecl
)->isConst()) {
149 aInfo
.nameAndParams
+= " const";
151 aInfo
.paramName
= paramName
;
152 aInfo
.paramIndex
= paramIndex
;
153 if (paramIndex
< (int)functionDecl
->getNumParams())
154 aInfo
.paramType
= functionDecl
->getParamDecl(paramIndex
)->getType().getCanonicalType().getAsString();
155 aInfo
.callValue
= callValue
;
157 aInfo
.sourceLocation
= filename
.str() + ":" + std::to_string(compiler
.getSourceManager().getSpellingLineNumber(expansionLoc
));
158 loplugin::normalizeDotDotInFilePath(aInfo
.sourceLocation
);
160 callSet
.insert(aInfo
);
163 std::string
ConstantParam::getCallValue(const Expr
* arg
)
165 arg
= arg
->IgnoreParenCasts();
166 if (isa
<CXXDefaultArgExpr
>(arg
)) {
167 arg
= dyn_cast
<CXXDefaultArgExpr
>(arg
)->getExpr();
169 arg
= arg
->IgnoreParenCasts();
170 // ignore this, it seems to trigger an infinite recursion
171 if (isa
<UnaryExprOrTypeTraitExpr
>(arg
)) {
175 if (compat::EvaluateAsInt(arg
, x1
, compiler
.getASTContext()))
177 return x1
.toString(10);
179 if (isa
<CXXNullPtrLiteralExpr
>(arg
)) {
182 if (isa
<MaterializeTemporaryExpr
>(arg
))
184 const CXXBindTemporaryExpr
* strippedArg
= dyn_cast_or_null
<CXXBindTemporaryExpr
>(arg
->IgnoreParenCasts());
187 auto temp
= dyn_cast
<CXXTemporaryObjectExpr
>(strippedArg
->getSubExpr());
188 if (temp
->getNumArgs() == 0)
190 if (loplugin::TypeCheck(temp
->getType()).Class("OUString").Namespace("rtl").GlobalNamespace()) {
193 if (loplugin::TypeCheck(temp
->getType()).Class("OString").Namespace("rtl").GlobalNamespace()) {
196 return "defaultConstruct";
201 // Get the expression contents.
202 // This helps us find params which are always initialised with something like "OUString()".
203 SourceManager
& SM
= compiler
.getSourceManager();
204 SourceLocation startLoc
= compat::getBeginLoc(arg
);
205 SourceLocation endLoc
= compat::getEndLoc(arg
);
206 const char *p1
= SM
.getCharacterData( startLoc
);
207 const char *p2
= SM
.getCharacterData( endLoc
);
208 if (!p1
|| !p2
|| (p2
- p1
) < 0 || (p2
- p1
) > 40) {
211 unsigned n
= Lexer::MeasureTokenLength( endLoc
, SM
, compiler
.getLangOpts());
212 std::string
s( p1
, p2
- p1
+ n
);
213 // strip linefeed and tab characters so they don't interfere with the parsing of the log file
214 std::replace( s
.begin(), s
.end(), '\r', ' ');
215 std::replace( s
.begin(), s
.end(), '\n', ' ');
216 std::replace( s
.begin(), s
.end(), '\t', ' ');
218 // now normalize the value. For some params, like OUString, we can pass it as OUString() or "" and they are the same thing
219 if (s
== "OUString()")
221 else if (s
== "OString()")
223 else if (s
== "aEmptyOUStr") //sw
225 else if (s
== "EMPTY_OUSTRING")//sc
227 else if (s
== "GetEmptyOUString()") //sc
232 bool ConstantParam::VisitCallExpr(const CallExpr
* callExpr
) {
233 if (ignoreLocation(callExpr
)) {
236 const FunctionDecl
* functionDecl
;
237 if (isa
<CXXMemberCallExpr
>(callExpr
)) {
238 functionDecl
= dyn_cast
<CXXMemberCallExpr
>(callExpr
)->getMethodDecl();
241 functionDecl
= callExpr
->getDirectCallee();
245 functionDecl
= functionDecl
->getCanonicalDecl();
246 // method overrides don't always specify the same default params (although they probably should)
247 // so we need to work our way up to the root method
248 while (isa
<CXXMethodDecl
>(functionDecl
)) {
249 const CXXMethodDecl
* methodDecl
= dyn_cast
<CXXMethodDecl
>(functionDecl
);
250 if (methodDecl
->size_overridden_methods()==0)
252 functionDecl
= *methodDecl
->begin_overridden_methods();
254 // work our way back to the root definition for template methods
255 if (functionDecl
->getInstantiatedFromMemberFunction())
256 functionDecl
= functionDecl
->getInstantiatedFromMemberFunction();
257 #if CLANG_VERSION < 90000
258 else if (functionDecl
->getClassScopeSpecializationPattern())
259 functionDecl
= functionDecl
->getClassScopeSpecializationPattern();
261 else if (functionDecl
->getTemplateInstantiationPattern())
262 functionDecl
= functionDecl
->getTemplateInstantiationPattern();
264 unsigned len
= std::max(callExpr
->getNumArgs(), functionDecl
->getNumParams());
265 for (unsigned i
= 0; i
< len
; ++i
) {
267 if (i
< callExpr
->getNumArgs())
268 valExpr
= callExpr
->getArg(i
);
269 else if (i
< functionDecl
->getNumParams() && functionDecl
->getParamDecl(i
)->hasDefaultArg())
270 valExpr
= functionDecl
->getParamDecl(i
)->getDefaultArg();
272 // can happen in template code
274 std::string callValue
= getCallValue(valExpr
);
275 std::string paramName
= i
< functionDecl
->getNumParams()
276 ? functionDecl
->getParamDecl(i
)->getName()
277 : llvm::StringRef("###" + std::to_string(i
));
278 addToCallSet(functionDecl
, i
, paramName
, callValue
);
283 bool ConstantParam::VisitCXXConstructExpr( const CXXConstructExpr
* constructExpr
)
285 const CXXConstructorDecl
* constructorDecl
= constructExpr
->getConstructor();
286 constructorDecl
= constructorDecl
->getCanonicalDecl();
288 unsigned len
= std::max(constructExpr
->getNumArgs(), constructorDecl
->getNumParams());
289 for (unsigned i
= 0; i
< len
; ++i
) {
291 if (i
< constructExpr
->getNumArgs())
292 valExpr
= constructExpr
->getArg(i
);
293 else if (i
< constructorDecl
->getNumParams() && constructorDecl
->getParamDecl(i
)->hasDefaultArg())
294 valExpr
= constructorDecl
->getParamDecl(i
)->getDefaultArg();
296 // can happen in template code
298 std::string callValue
= getCallValue(valExpr
);
299 std::string paramName
= i
< constructorDecl
->getNumParams()
300 ? constructorDecl
->getParamDecl(i
)->getName()
301 : llvm::StringRef("###" + std::to_string(i
));
302 addToCallSet(constructorDecl
, i
, paramName
, callValue
);
308 loplugin::Plugin::Registration
< ConstantParam
> X("constantparam", false);
312 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */