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 <unordered_map>
18 #include "clang/AST/Attr.h"
24 Find methods that are only called from inside their own class, and are only called from one spot.
25 They are candidates to be removed and have their code inlined into the call site.
28 TODO if a method has only one call-site, and that call site is inside a constructor
29 then it's probably worth inlining, since it's probably an old method that was intended to be shared amongst
38 std::string returnType
;
39 std::string nameAndParams
;
40 std::string sourceLocation
;
43 bool operator < (const MyFuncInfo
&lhs
, const MyFuncInfo
&rhs
)
45 return std::tie(lhs
.returnType
, lhs
.nameAndParams
)
46 < std::tie(rhs
.returnType
, rhs
.nameAndParams
);
49 // try to limit the voluminous output a little
51 static std::set
<std::pair
<std::string
, MyFuncInfo
>> calledFromSet
;
52 static std::set
<MyFuncInfo
> definitionSet
;
53 static std::set
<MyFuncInfo
> calledFromOutsideSet
;
54 static std::set
<MyFuncInfo
> largeFunctionSet
;
55 static std::set
<MyFuncInfo
> addressOfSet
;
58 class ExpandableMethods
:
59 public RecursiveASTVisitor
<ExpandableMethods
>, public loplugin::Plugin
62 explicit ExpandableMethods(loplugin::InstantiationData
const & data
):
65 virtual void run() override
67 TraverseDecl(compiler
.getASTContext().getTranslationUnitDecl());
69 // dump all our output in one write call - this is to try and limit IO "crosstalk" between multiple processes
70 // writing to the same logfile
73 for (const MyFuncInfo
& s
: definitionSet
)
74 output
+= "definition:\t" + s
.access
+ "\t" + s
.returnType
+ "\t" + s
.nameAndParams
+ "\t" + s
.sourceLocation
+ "\n";
75 for (const MyFuncInfo
& s
: calledFromOutsideSet
)
76 output
+= "outside:\t" + s
.returnType
+ "\t" + s
.nameAndParams
+ "\n";
77 for (const std::pair
<std::string
,MyFuncInfo
> & s
: calledFromSet
)
78 output
+= "calledFrom:\t" + s
.first
79 + "\t" + s
.second
.returnType
+ "\t" + s
.second
.nameAndParams
+ "\n";
80 for (const MyFuncInfo
& s
: largeFunctionSet
)
81 output
+= "large:\t" + s
.returnType
+ "\t" + s
.nameAndParams
+ "\n";
82 for (const MyFuncInfo
& s
: addressOfSet
)
83 output
+= "addrof:\t" + s
.returnType
+ "\t" + s
.nameAndParams
+ "\n";
85 myfile
.open( WORKDIR
"/loplugin.expandablemethods.log", std::ios::app
| std::ios::out
);
90 bool shouldVisitTemplateInstantiations () const { return true; }
91 bool shouldVisitImplicitCode() const { return true; }
93 bool VisitFunctionDecl( const FunctionDecl
* );
94 bool VisitDeclRefExpr( const DeclRefExpr
* );
95 bool VisitMemberExpr( const MemberExpr
* );
96 // interception methods for FunctionDecl and all its subclasses
97 bool TraverseFunctionDecl( FunctionDecl
* );
98 bool TraverseCXXMethodDecl( CXXMethodDecl
* );
99 bool TraverseCXXConstructorDecl( CXXConstructorDecl
* );
100 bool TraverseCXXConversionDecl( CXXConversionDecl
* );
101 bool TraverseCXXDestructorDecl( CXXDestructorDecl
* );
104 MyFuncInfo
niceName(const FunctionDecl
* functionDecl
);
105 std::string
toString(SourceLocation loc
);
106 void functionTouchedFromExpr( const FunctionDecl
* calleeFunctionDecl
, const Expr
* expr
);
107 bool isCalleeFunctionInteresting( const FunctionDecl
* );
109 // I use traverse and a member variable because I cannot find a reliable way of walking back up the AST tree using the parentStmt() stuff
110 std::vector
<const FunctionDecl
*> maTraversingFunctions
;
113 MyFuncInfo
ExpandableMethods::niceName(const FunctionDecl
* functionDecl
)
115 if (functionDecl
->getInstantiatedFromMemberFunction())
116 functionDecl
= functionDecl
->getInstantiatedFromMemberFunction();
117 #if CLANG_VERSION < 90000
118 else if (functionDecl
->getClassScopeSpecializationPattern())
119 functionDecl
= functionDecl
->getClassScopeSpecializationPattern();
121 else if (functionDecl
->getTemplateInstantiationPattern())
122 functionDecl
= functionDecl
->getTemplateInstantiationPattern();
125 switch (functionDecl
->getAccess())
127 case AS_public
: aInfo
.access
= "public"; break;
128 case AS_private
: aInfo
.access
= "private"; break;
129 case AS_protected
: aInfo
.access
= "protected"; break;
130 default: aInfo
.access
= "unknown"; break;
132 if (!isa
<CXXConstructorDecl
>(functionDecl
)) {
133 aInfo
.returnType
= functionDecl
->getReturnType().getCanonicalType().getAsString();
135 aInfo
.returnType
= "";
138 if (isa
<CXXMethodDecl
>(functionDecl
)) {
139 const CXXRecordDecl
* recordDecl
= dyn_cast
<CXXMethodDecl
>(functionDecl
)->getParent();
140 aInfo
.nameAndParams
+= recordDecl
->getQualifiedNameAsString();
141 aInfo
.nameAndParams
+= "::";
143 aInfo
.nameAndParams
+= functionDecl
->getNameAsString() + "(";
145 for (const ParmVarDecl
*pParmVarDecl
: functionDecl
->parameters()) {
149 aInfo
.nameAndParams
+= ",";
150 aInfo
.nameAndParams
+= pParmVarDecl
->getType().getCanonicalType().getAsString();
152 aInfo
.nameAndParams
+= ")";
153 if (isa
<CXXMethodDecl
>(functionDecl
) && dyn_cast
<CXXMethodDecl
>(functionDecl
)->isConst()) {
154 aInfo
.nameAndParams
+= " const";
157 aInfo
.sourceLocation
= toString( functionDecl
->getLocation() );
162 std::string
ExpandableMethods::toString(SourceLocation loc
)
164 SourceLocation expansionLoc
= compiler
.getSourceManager().getExpansionLoc( loc
);
165 StringRef name
= compiler
.getSourceManager().getFilename(expansionLoc
);
166 std::string sourceLocation
= std::string(name
.substr(strlen(SRCDIR
)+1)) + ":" + std::to_string(compiler
.getSourceManager().getSpellingLineNumber(expansionLoc
));
167 loplugin::normalizeDotDotInFilePath(sourceLocation
);
168 return sourceLocation
;
171 bool ExpandableMethods::VisitFunctionDecl( const FunctionDecl
* functionDecl
)
173 const FunctionDecl
* canonicalFunctionDecl
= functionDecl
->getCanonicalDecl();
174 if (!isCalleeFunctionInteresting(canonicalFunctionDecl
)) {
177 definitionSet
.insert(niceName(canonicalFunctionDecl
));
179 if (functionDecl
->doesThisDeclarationHaveABody()) {
180 bool bLargeFunction
= false;
181 if (const CompoundStmt
* compoundStmt
= dyn_cast
<CompoundStmt
>(functionDecl
->getBody())) {
182 if (compoundStmt
->size() > 1) {
183 bLargeFunction
= true;
185 if (!bLargeFunction
) {
186 auto s1
= compiler
.getSourceManager().getCharacterData(compoundStmt
->getLBracLoc());
187 auto s2
= compiler
.getSourceManager().getCharacterData(compoundStmt
->getRBracLoc());
188 bLargeFunction
= (s2
- s1
) > 40;
189 // any function that uses a parameter more than once
190 if (!bLargeFunction
) {
191 StringRef
bodyText(s1
, s2
-s1
);
192 for (const ParmVarDecl
* param
: functionDecl
->parameters()) {
193 StringRef name
= param
->getName();
196 size_t idx
= bodyText
.find(name
);
197 if (idx
!= StringRef::npos
&& bodyText
.find(name
, idx
+1) != StringRef::npos
) {
198 bLargeFunction
= true;
205 if (bLargeFunction
) {
206 largeFunctionSet
.insert(niceName(canonicalFunctionDecl
));
212 bool ExpandableMethods::TraverseFunctionDecl( FunctionDecl
* p
)
214 maTraversingFunctions
.push_back(p
);
215 bool ret
= RecursiveASTVisitor::TraverseFunctionDecl(p
);
216 maTraversingFunctions
.pop_back();
219 bool ExpandableMethods::TraverseCXXMethodDecl( CXXMethodDecl
* p
)
221 maTraversingFunctions
.push_back(p
);
222 bool ret
= RecursiveASTVisitor::TraverseCXXMethodDecl(p
);
223 maTraversingFunctions
.pop_back();
226 bool ExpandableMethods::TraverseCXXConstructorDecl( CXXConstructorDecl
* p
)
228 maTraversingFunctions
.push_back(p
);
229 bool ret
= RecursiveASTVisitor::TraverseCXXConstructorDecl(p
);
230 maTraversingFunctions
.pop_back();
233 bool ExpandableMethods::TraverseCXXConversionDecl( CXXConversionDecl
* p
)
235 maTraversingFunctions
.push_back(p
);
236 bool ret
= RecursiveASTVisitor::TraverseCXXConversionDecl(p
);
237 maTraversingFunctions
.pop_back();
240 bool ExpandableMethods::TraverseCXXDestructorDecl( CXXDestructorDecl
* p
)
242 maTraversingFunctions
.push_back(p
);
243 bool ret
= RecursiveASTVisitor::TraverseCXXDestructorDecl(p
);
244 maTraversingFunctions
.pop_back();
248 bool ExpandableMethods::VisitMemberExpr( const MemberExpr
* memberExpr
)
250 const FunctionDecl
* functionDecl
= dyn_cast
<FunctionDecl
>(memberExpr
->getMemberDecl());
252 functionTouchedFromExpr(functionDecl
, memberExpr
);
257 bool ExpandableMethods::VisitDeclRefExpr( const DeclRefExpr
* declRefExpr
)
259 const FunctionDecl
* functionDecl
= dyn_cast
<FunctionDecl
>(declRefExpr
->getDecl());
261 functionTouchedFromExpr(functionDecl
, declRefExpr
);
266 void ExpandableMethods::functionTouchedFromExpr( const FunctionDecl
* calleeFunctionDecl
, const Expr
* expr
)
268 const FunctionDecl
* canonicalFunctionDecl
= calleeFunctionDecl
->getCanonicalDecl();
269 if (!isCalleeFunctionInteresting(canonicalFunctionDecl
)) {
273 calledFromSet
.emplace(toString(compat::getBeginLoc(expr
)), niceName(canonicalFunctionDecl
));
275 if (const UnaryOperator
* unaryOp
= dyn_cast_or_null
<UnaryOperator
>(getParentStmt(expr
))) {
276 if (unaryOp
->getOpcode() == UO_AddrOf
) {
277 addressOfSet
.insert(niceName(canonicalFunctionDecl
));
281 const CXXMethodDecl
* calleeMethodDecl
= dyn_cast
<CXXMethodDecl
>(calleeFunctionDecl
);
282 if (maTraversingFunctions
.empty())
284 calledFromOutsideSet
.insert(niceName(canonicalFunctionDecl
));
288 const CXXMethodDecl
* callsiteParentMethodDecl
= dyn_cast
<CXXMethodDecl
>(maTraversingFunctions
.back());
289 if (!callsiteParentMethodDecl
290 || calleeMethodDecl
->getParent() != callsiteParentMethodDecl
->getParent())
292 calledFromOutsideSet
.insert(niceName(canonicalFunctionDecl
));
297 bool ExpandableMethods::isCalleeFunctionInteresting(const FunctionDecl
* functionDecl
)
299 // ignore stuff that forms part of the stable URE interface
300 if (isInUnoIncludeFile(functionDecl
)) {
303 if (isa
<CXXDestructorDecl
>(functionDecl
)) {
306 if (functionDecl
->isDeleted() || functionDecl
->isDefaulted()) {
309 if (isa
<CXXConstructorDecl
>(functionDecl
)
310 && dyn_cast
<CXXConstructorDecl
>(functionDecl
)->isCopyOrMoveConstructor())
314 if (!functionDecl
->getLocation().isValid() || ignoreLocation(functionDecl
)) {
317 const CXXMethodDecl
* methodDecl
= dyn_cast
<CXXMethodDecl
>(functionDecl
);
318 if (!methodDecl
|| methodDecl
->isVirtual()) {
324 loplugin::Plugin::Registration
< ExpandableMethods
> X("expandablemethods", false);
328 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */