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/.
15 #include "clang/AST/Attr.h"
21 Count call sites that are actually using the defaulted values on params on methods that declare such.
23 The process goes something like this:
25 $ make FORCE_COMPILE_ALL=1 COMPILER_PLUGIN_TOOL='countusersofdefaultparams' check
26 $ ./compilerplugins/clang/countusersofdefaultparams.py
34 std::string returnType
;
35 std::string nameAndParams
;
36 std::string sourceLocation
;
38 bool operator < (const MyFuncInfo
&lhs
, const MyFuncInfo
&rhs
)
40 return std::tie(lhs
.returnType
, lhs
.nameAndParams
)
41 < std::tie(rhs
.returnType
, rhs
.nameAndParams
);
43 struct MyCallInfo
: public MyFuncInfo
45 std::string sourceLocationOfCall
;
47 bool operator < (const MyCallInfo
&lhs
, const MyCallInfo
&rhs
)
49 return std::tie(lhs
.returnType
, lhs
.nameAndParams
, lhs
.sourceLocationOfCall
)
50 < std::tie(rhs
.returnType
, rhs
.nameAndParams
, rhs
.sourceLocationOfCall
);
54 // try to limit the voluminous output a little
55 static std::set
<MyCallInfo
> callSet
;
56 static std::set
<MyFuncInfo
> definitionSet
;
58 class CountUsersOfDefaultParams
:
59 public RecursiveASTVisitor
<CountUsersOfDefaultParams
>, public loplugin::Plugin
62 explicit CountUsersOfDefaultParams(loplugin::InstantiationData
const & data
): Plugin(data
) {}
64 virtual void run() override
66 TraverseDecl(compiler
.getASTContext().getTranslationUnitDecl());
68 // dump all our output in one write call - this is to try and limit IO "crosstalk" between multiple processes
69 // writing to the same logfile
72 for (const MyFuncInfo
& s
: definitionSet
)
73 output
+= "defn:\t" + s
.access
+ "\t" + s
.returnType
+ "\t" + s
.nameAndParams
+ "\t" + s
.sourceLocation
+ "\n";
74 for (const MyCallInfo
& s
: callSet
)
75 output
+= "call:\t" + s
.returnType
+ "\t" + s
.nameAndParams
+ "\t" + s
.sourceLocationOfCall
+ "\n";
77 myfile
.open( WORKDIR
"/loplugin.countusersofdefaultparams.log", std::ios::app
| std::ios::out
);
82 bool shouldVisitTemplateInstantiations () const { return true; }
84 bool VisitCallExpr( const CallExpr
* );
85 bool VisitFunctionDecl( const FunctionDecl
* );
86 bool VisitCXXConstructExpr( const CXXConstructExpr
* );
88 void niceName(const FunctionDecl
* functionDecl
, MyFuncInfo
&);
89 std::string
locationToString(const SourceLocation
&);
92 void CountUsersOfDefaultParams::niceName(const FunctionDecl
* functionDecl
, MyFuncInfo
& aInfo
)
94 if (functionDecl
->getInstantiatedFromMemberFunction())
95 functionDecl
= functionDecl
->getInstantiatedFromMemberFunction();
96 #if CLANG_VERSION < 90000
97 else if (functionDecl
->getClassScopeSpecializationPattern())
98 functionDecl
= functionDecl
->getClassScopeSpecializationPattern();
100 else if (functionDecl
->getTemplateInstantiationPattern())
101 functionDecl
= functionDecl
->getTemplateInstantiationPattern();
103 switch (functionDecl
->getAccess())
105 case AS_public
: aInfo
.access
= "public"; break;
106 case AS_private
: aInfo
.access
= "private"; break;
107 case AS_protected
: aInfo
.access
= "protected"; break;
108 default: aInfo
.access
= "unknown"; break;
110 aInfo
.returnType
= functionDecl
->getReturnType().getCanonicalType().getAsString();
112 if (isa
<CXXMethodDecl
>(functionDecl
)) {
113 const CXXRecordDecl
* recordDecl
= dyn_cast
<CXXMethodDecl
>(functionDecl
)->getParent();
114 aInfo
.nameAndParams
+= recordDecl
->getQualifiedNameAsString();
115 aInfo
.nameAndParams
+= "::";
117 aInfo
.nameAndParams
+= functionDecl
->getNameAsString() + "(";
119 for (const ParmVarDecl
*pParmVarDecl
: functionDecl
->parameters()) {
123 aInfo
.nameAndParams
+= ",";
124 aInfo
.nameAndParams
+= pParmVarDecl
->getType().getCanonicalType().getAsString();
126 aInfo
.nameAndParams
+= ")";
127 if (isa
<CXXMethodDecl
>(functionDecl
) && dyn_cast
<CXXMethodDecl
>(functionDecl
)->isConst()) {
128 aInfo
.nameAndParams
+= " const";
131 aInfo
.sourceLocation
= locationToString(functionDecl
->getLocation());
132 loplugin::normalizeDotDotInFilePath(aInfo
.sourceLocation
);
135 bool CountUsersOfDefaultParams::VisitCallExpr(const CallExpr
* callExpr
) {
136 if (ignoreLocation(callExpr
)) {
139 const FunctionDecl
* functionDecl
;
140 if (isa
<CXXMemberCallExpr
>(callExpr
)) {
141 functionDecl
= dyn_cast
<CXXMemberCallExpr
>(callExpr
)->getMethodDecl();
144 functionDecl
= callExpr
->getDirectCallee();
146 if (functionDecl
== nullptr) {
149 functionDecl
= functionDecl
->getCanonicalDecl();
150 // work our way up to the root method
151 while (isa
<CXXMethodDecl
>(functionDecl
)) {
152 const CXXMethodDecl
* methodDecl
= dyn_cast
<CXXMethodDecl
>(functionDecl
);
153 if (methodDecl
->size_overridden_methods()==0)
155 functionDecl
= *methodDecl
->begin_overridden_methods();
157 // work our way back to the root definition for template methods
158 if (functionDecl
->getInstantiatedFromMemberFunction())
159 functionDecl
= functionDecl
->getInstantiatedFromMemberFunction();
160 #if CLANG_VERSION < 90000
161 else if (functionDecl
->getClassScopeSpecializationPattern())
162 functionDecl
= functionDecl
->getClassScopeSpecializationPattern();
164 else if (functionDecl
->getTemplateInstantiationPattern())
165 functionDecl
= functionDecl
->getTemplateInstantiationPattern();
166 int n
= functionDecl
->getNumParams() - 1;
167 if (n
< 0 || !functionDecl
->getParamDecl(n
)->hasDefaultArg()) {
170 while (n
> 0 && functionDecl
->getParamDecl(n
-1)->hasDefaultArg()) {
173 // look for callsites that are actually using the defaulted values
174 if ( n
< (int)callExpr
->getNumArgs() && callExpr
->getArg(n
)->isDefaultArgument()) {
176 niceName(functionDecl
, callInfo
);
177 callInfo
.sourceLocationOfCall
= locationToString(compat::getBeginLoc(callExpr
));
178 callSet
.insert(callInfo
);
183 bool CountUsersOfDefaultParams::VisitCXXConstructExpr(const CXXConstructExpr
* constructExpr
) {
184 if (ignoreLocation(constructExpr
)) {
187 const CXXConstructorDecl
* constructorDecl
= constructExpr
->getConstructor()->getCanonicalDecl();
188 // work our way back to the root definition for template methods
189 if (constructorDecl
->getInstantiatedFromMemberFunction())
190 constructorDecl
= dyn_cast
<CXXConstructorDecl
>(constructorDecl
->getInstantiatedFromMemberFunction());
191 #if CLANG_VERSION < 90000
192 else if (constructorDecl
->getClassScopeSpecializationPattern())
193 constructorDecl
= dyn_cast
<CXXConstructorDecl
>(constructorDecl
->getClassScopeSpecializationPattern());
195 else if (constructorDecl
->getTemplateInstantiationPattern())
196 constructorDecl
= dyn_cast
<CXXConstructorDecl
>(constructorDecl
->getTemplateInstantiationPattern());
197 int n
= constructorDecl
->getNumParams() - 1;
198 if (n
< 0 || !constructorDecl
->getParamDecl(n
)->hasDefaultArg()) {
201 while (n
> 0 && constructorDecl
->getParamDecl(n
-1)->hasDefaultArg()) {
204 // look for callsites that are actually using the defaulted values
205 if ( n
< (int)constructExpr
->getNumArgs() && constructExpr
->getArg(n
)->isDefaultArgument()) {
207 niceName(constructorDecl
, callInfo
);
208 callInfo
.sourceLocationOfCall
= locationToString(compat::getBeginLoc(constructExpr
));
209 callSet
.insert(callInfo
);
214 std::string
CountUsersOfDefaultParams::locationToString(const SourceLocation
& sourceLoc
)
216 SourceLocation expansionLoc
= compiler
.getSourceManager().getExpansionLoc( sourceLoc
);
217 StringRef name
= getFilenameOfLocation(expansionLoc
);
218 return std::string(name
.substr(strlen(SRCDIR
)+1)) + ":" + std::to_string(compiler
.getSourceManager().getSpellingLineNumber(expansionLoc
));
221 bool CountUsersOfDefaultParams::VisitFunctionDecl( const FunctionDecl
* functionDecl
)
223 functionDecl
= functionDecl
->getCanonicalDecl();
224 if( !functionDecl
|| !functionDecl
->getLocation().isValid() || ignoreLocation( functionDecl
)) {
227 const CXXMethodDecl
* methodDecl
= dyn_cast_or_null
<CXXMethodDecl
>(functionDecl
);
229 // ignore method overrides, since the call will show up as being directed to the root method
230 if (methodDecl
&& (methodDecl
->size_overridden_methods() != 0 || methodDecl
->hasAttr
<OverrideAttr
>())) {
233 // ignore stuff that forms part of the stable URE interface
234 if (isInUnoIncludeFile(functionDecl
)) {
237 if (isa
<CXXDestructorDecl
>(functionDecl
)) {
240 if (functionDecl
->isDeleted()) {
243 auto n
= functionDecl
->getNumParams();
244 if (n
== 0 || !functionDecl
->getParamDecl(n
- 1)->hasDefaultArg()) {
248 if( functionDecl
->getLocation().isValid() )
251 niceName(functionDecl
, funcInfo
);
252 definitionSet
.insert(funcInfo
);
257 loplugin::Plugin::Registration
< CountUsersOfDefaultParams
> X("countusersofdefaultparams", false);
261 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */