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 else if (functionDecl
->getClassScopeSpecializationPattern())
118 functionDecl
= functionDecl
->getClassScopeSpecializationPattern();
119 else if (functionDecl
->getTemplateInstantiationPattern())
120 functionDecl
= functionDecl
->getTemplateInstantiationPattern();
123 switch (functionDecl
->getAccess())
125 case AS_public
: aInfo
.access
= "public"; break;
126 case AS_private
: aInfo
.access
= "private"; break;
127 case AS_protected
: aInfo
.access
= "protected"; break;
128 default: aInfo
.access
= "unknown"; break;
130 if (!isa
<CXXConstructorDecl
>(functionDecl
)) {
131 aInfo
.returnType
= functionDecl
->getReturnType().getCanonicalType().getAsString();
133 aInfo
.returnType
= "";
136 if (isa
<CXXMethodDecl
>(functionDecl
)) {
137 const CXXRecordDecl
* recordDecl
= dyn_cast
<CXXMethodDecl
>(functionDecl
)->getParent();
138 aInfo
.nameAndParams
+= recordDecl
->getQualifiedNameAsString();
139 aInfo
.nameAndParams
+= "::";
141 aInfo
.nameAndParams
+= functionDecl
->getNameAsString() + "(";
143 for (const ParmVarDecl
*pParmVarDecl
: compat::parameters(*functionDecl
)) {
147 aInfo
.nameAndParams
+= ",";
148 aInfo
.nameAndParams
+= pParmVarDecl
->getType().getCanonicalType().getAsString();
150 aInfo
.nameAndParams
+= ")";
151 if (isa
<CXXMethodDecl
>(functionDecl
) && dyn_cast
<CXXMethodDecl
>(functionDecl
)->isConst()) {
152 aInfo
.nameAndParams
+= " const";
155 aInfo
.sourceLocation
= toString( functionDecl
->getLocation() );
160 std::string
ExpandableMethods::toString(SourceLocation loc
)
162 SourceLocation expansionLoc
= compiler
.getSourceManager().getExpansionLoc( loc
);
163 StringRef name
= compiler
.getSourceManager().getFilename(expansionLoc
);
164 std::string sourceLocation
= std::string(name
.substr(strlen(SRCDIR
)+1)) + ":" + std::to_string(compiler
.getSourceManager().getSpellingLineNumber(expansionLoc
));
165 loplugin::normalizeDotDotInFilePath(sourceLocation
);
166 return sourceLocation
;
169 bool ExpandableMethods::VisitFunctionDecl( const FunctionDecl
* functionDecl
)
171 const FunctionDecl
* canonicalFunctionDecl
= functionDecl
->getCanonicalDecl();
172 if (!isCalleeFunctionInteresting(canonicalFunctionDecl
)) {
175 definitionSet
.insert(niceName(canonicalFunctionDecl
));
177 if (functionDecl
->doesThisDeclarationHaveABody()) {
178 bool bLargeFunction
= false;
179 if (const CompoundStmt
* compoundStmt
= dyn_cast
<CompoundStmt
>(functionDecl
->getBody())) {
180 if (compoundStmt
->size() > 1) {
181 bLargeFunction
= true;
183 if (!bLargeFunction
) {
184 auto s1
= compiler
.getSourceManager().getCharacterData(compoundStmt
->getLBracLoc());
185 auto s2
= compiler
.getSourceManager().getCharacterData(compoundStmt
->getRBracLoc());
186 bLargeFunction
= (s2
- s1
) > 40;
187 // any function that uses a parameter more than once
188 if (!bLargeFunction
) {
189 StringRef
bodyText(s1
, s2
-s1
);
190 for (const ParmVarDecl
* param
: compat::parameters(*functionDecl
)) {
191 StringRef name
= param
->getName();
194 size_t idx
= bodyText
.find(name
);
195 if (idx
!= StringRef::npos
&& bodyText
.find(name
, idx
+1) != StringRef::npos
) {
196 bLargeFunction
= true;
203 if (bLargeFunction
) {
204 largeFunctionSet
.insert(niceName(canonicalFunctionDecl
));
210 bool ExpandableMethods::TraverseFunctionDecl( FunctionDecl
* p
)
212 maTraversingFunctions
.push_back(p
);
213 bool ret
= RecursiveASTVisitor::TraverseFunctionDecl(p
);
214 maTraversingFunctions
.pop_back();
217 bool ExpandableMethods::TraverseCXXMethodDecl( CXXMethodDecl
* p
)
219 maTraversingFunctions
.push_back(p
);
220 bool ret
= RecursiveASTVisitor::TraverseCXXMethodDecl(p
);
221 maTraversingFunctions
.pop_back();
224 bool ExpandableMethods::TraverseCXXConstructorDecl( CXXConstructorDecl
* p
)
226 maTraversingFunctions
.push_back(p
);
227 bool ret
= RecursiveASTVisitor::TraverseCXXConstructorDecl(p
);
228 maTraversingFunctions
.pop_back();
231 bool ExpandableMethods::TraverseCXXConversionDecl( CXXConversionDecl
* p
)
233 maTraversingFunctions
.push_back(p
);
234 bool ret
= RecursiveASTVisitor::TraverseCXXConversionDecl(p
);
235 maTraversingFunctions
.pop_back();
238 bool ExpandableMethods::TraverseCXXDestructorDecl( CXXDestructorDecl
* p
)
240 maTraversingFunctions
.push_back(p
);
241 bool ret
= RecursiveASTVisitor::TraverseCXXDestructorDecl(p
);
242 maTraversingFunctions
.pop_back();
246 bool ExpandableMethods::VisitMemberExpr( const MemberExpr
* memberExpr
)
248 const FunctionDecl
* functionDecl
= dyn_cast
<FunctionDecl
>(memberExpr
->getMemberDecl());
250 functionTouchedFromExpr(functionDecl
, memberExpr
);
255 bool ExpandableMethods::VisitDeclRefExpr( const DeclRefExpr
* declRefExpr
)
257 const FunctionDecl
* functionDecl
= dyn_cast
<FunctionDecl
>(declRefExpr
->getDecl());
259 functionTouchedFromExpr(functionDecl
, declRefExpr
);
264 void ExpandableMethods::functionTouchedFromExpr( const FunctionDecl
* calleeFunctionDecl
, const Expr
* expr
)
266 const FunctionDecl
* canonicalFunctionDecl
= calleeFunctionDecl
->getCanonicalDecl();
267 if (!isCalleeFunctionInteresting(canonicalFunctionDecl
)) {
271 calledFromSet
.emplace(toString(expr
->getLocStart()), niceName(canonicalFunctionDecl
));
273 if (const UnaryOperator
* unaryOp
= dyn_cast_or_null
<UnaryOperator
>(getParentStmt(expr
))) {
274 if (unaryOp
->getOpcode() == UO_AddrOf
) {
275 addressOfSet
.insert(niceName(canonicalFunctionDecl
));
279 const CXXMethodDecl
* calleeMethodDecl
= dyn_cast
<CXXMethodDecl
>(calleeFunctionDecl
);
280 if (maTraversingFunctions
.empty())
282 calledFromOutsideSet
.insert(niceName(canonicalFunctionDecl
));
286 const CXXMethodDecl
* callsiteParentMethodDecl
= dyn_cast
<CXXMethodDecl
>(maTraversingFunctions
.back());
287 if (!callsiteParentMethodDecl
288 || calleeMethodDecl
->getParent() != callsiteParentMethodDecl
->getParent())
290 calledFromOutsideSet
.insert(niceName(canonicalFunctionDecl
));
295 bool ExpandableMethods::isCalleeFunctionInteresting(const FunctionDecl
* functionDecl
)
297 // ignore stuff that forms part of the stable URE interface
298 if (isInUnoIncludeFile(functionDecl
)) {
301 if (isa
<CXXDestructorDecl
>(functionDecl
)) {
304 if (functionDecl
->isDeleted() || functionDecl
->isDefaulted()) {
307 if (isa
<CXXConstructorDecl
>(functionDecl
)
308 && dyn_cast
<CXXConstructorDecl
>(functionDecl
)->isCopyOrMoveConstructor())
312 if (!functionDecl
->getLocation().isValid() || ignoreLocation(functionDecl
)) {
315 const CXXMethodDecl
* methodDecl
= dyn_cast
<CXXMethodDecl
>(functionDecl
);
316 if (!methodDecl
|| methodDecl
->isVirtual()) {
322 loplugin::Plugin::Registration
< ExpandableMethods
> X("expandablemethods", false);
326 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */