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"
20 #include "config_clang.h"
25 Find methods that are only called from inside their own class, and are only called from one spot.
26 They are candidates to be removed and have their code inlined into the call site.
29 TODO if a method has only one call-site, and that call site is inside a constructor
30 then it's probably worth inlining, since it's probably an old method that was intended to be shared amongst
39 std::string returnType
;
40 std::string nameAndParams
;
41 std::string sourceLocation
;
44 bool operator < (const MyFuncInfo
&lhs
, const MyFuncInfo
&rhs
)
46 return std::tie(lhs
.returnType
, lhs
.nameAndParams
)
47 < std::tie(rhs
.returnType
, rhs
.nameAndParams
);
50 // try to limit the voluminous output a little
52 static std::set
<std::pair
<std::string
, MyFuncInfo
>> calledFromSet
;
53 static std::set
<MyFuncInfo
> definitionSet
;
54 static std::set
<MyFuncInfo
> calledFromOutsideSet
;
55 static std::set
<MyFuncInfo
> largeFunctionSet
;
56 static std::set
<MyFuncInfo
> addressOfSet
;
59 class ExpandableMethods
:
60 public RecursiveASTVisitor
<ExpandableMethods
>, public loplugin::Plugin
63 explicit ExpandableMethods(loplugin::InstantiationData
const & data
):
66 virtual void run() override
68 handler
.enableTreeWideAnalysisMode();
70 TraverseDecl(compiler
.getASTContext().getTranslationUnitDecl());
72 // dump all our output in one write call - this is to try and limit IO "crosstalk" between multiple processes
73 // writing to the same logfile
76 for (const MyFuncInfo
& s
: definitionSet
)
77 output
+= "definition:\t" + s
.access
+ "\t" + s
.returnType
+ "\t" + s
.nameAndParams
+ "\t" + s
.sourceLocation
+ "\n";
78 for (const MyFuncInfo
& s
: calledFromOutsideSet
)
79 output
+= "outside:\t" + s
.returnType
+ "\t" + s
.nameAndParams
+ "\n";
80 for (const std::pair
<std::string
,MyFuncInfo
> & s
: calledFromSet
)
81 output
+= "calledFrom:\t" + s
.first
82 + "\t" + s
.second
.returnType
+ "\t" + s
.second
.nameAndParams
+ "\n";
83 for (const MyFuncInfo
& s
: largeFunctionSet
)
84 output
+= "large:\t" + s
.returnType
+ "\t" + s
.nameAndParams
+ "\n";
85 for (const MyFuncInfo
& s
: addressOfSet
)
86 output
+= "addrof:\t" + s
.returnType
+ "\t" + s
.nameAndParams
+ "\n";
88 myfile
.open( WORKDIR
"/loplugin.expandablemethods.log", std::ios::app
| std::ios::out
);
93 bool shouldVisitTemplateInstantiations () const { return true; }
94 bool shouldVisitImplicitCode() const { return true; }
96 bool VisitFunctionDecl( const FunctionDecl
* );
97 bool VisitDeclRefExpr( const DeclRefExpr
* );
98 bool VisitMemberExpr( const MemberExpr
* );
99 // interception methods for FunctionDecl and all its subclasses
100 bool TraverseFunctionDecl( FunctionDecl
* );
101 bool TraverseCXXMethodDecl( CXXMethodDecl
* );
102 bool TraverseCXXConstructorDecl( CXXConstructorDecl
* );
103 bool TraverseCXXConversionDecl( CXXConversionDecl
* );
104 bool TraverseCXXDestructorDecl( CXXDestructorDecl
* );
107 MyFuncInfo
niceName(const FunctionDecl
* functionDecl
);
108 std::string
toString(SourceLocation loc
);
109 void functionTouchedFromExpr( const FunctionDecl
* calleeFunctionDecl
, const Expr
* expr
);
110 bool isCalleeFunctionInteresting( const FunctionDecl
* );
112 // 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
113 std::vector
<const FunctionDecl
*> maTraversingFunctions
;
116 MyFuncInfo
ExpandableMethods::niceName(const FunctionDecl
* functionDecl
)
118 if (functionDecl
->getInstantiatedFromMemberFunction())
119 functionDecl
= functionDecl
->getInstantiatedFromMemberFunction();
120 else if (functionDecl
->getTemplateInstantiationPattern())
121 functionDecl
= functionDecl
->getTemplateInstantiationPattern();
124 switch (functionDecl
->getAccess())
126 case AS_public
: aInfo
.access
= "public"; break;
127 case AS_private
: aInfo
.access
= "private"; break;
128 case AS_protected
: aInfo
.access
= "protected"; break;
129 default: aInfo
.access
= "unknown"; break;
131 if (!isa
<CXXConstructorDecl
>(functionDecl
)) {
132 aInfo
.returnType
= functionDecl
->getReturnType().getCanonicalType().getAsString();
134 aInfo
.returnType
= "";
137 if (isa
<CXXMethodDecl
>(functionDecl
)) {
138 const CXXRecordDecl
* recordDecl
= dyn_cast
<CXXMethodDecl
>(functionDecl
)->getParent();
139 aInfo
.nameAndParams
+= recordDecl
->getQualifiedNameAsString();
140 aInfo
.nameAndParams
+= "::";
142 aInfo
.nameAndParams
+= functionDecl
->getNameAsString() + "(";
144 for (const ParmVarDecl
*pParmVarDecl
: functionDecl
->parameters()) {
148 aInfo
.nameAndParams
+= ",";
149 aInfo
.nameAndParams
+= pParmVarDecl
->getType().getCanonicalType().getAsString();
151 aInfo
.nameAndParams
+= ")";
152 if (isa
<CXXMethodDecl
>(functionDecl
) && dyn_cast
<CXXMethodDecl
>(functionDecl
)->isConst()) {
153 aInfo
.nameAndParams
+= " const";
156 aInfo
.sourceLocation
= toString( functionDecl
->getLocation() );
161 std::string
ExpandableMethods::toString(SourceLocation loc
)
163 SourceLocation expansionLoc
= compiler
.getSourceManager().getExpansionLoc( loc
);
164 StringRef name
= getFilenameOfLocation(expansionLoc
);
165 std::string sourceLocation
= std::string(name
.substr(strlen(SRCDIR
)+1)) + ":" + std::to_string(compiler
.getSourceManager().getSpellingLineNumber(expansionLoc
));
166 loplugin::normalizeDotDotInFilePath(sourceLocation
);
167 return sourceLocation
;
170 bool ExpandableMethods::VisitFunctionDecl( const FunctionDecl
* functionDecl
)
172 const FunctionDecl
* canonicalFunctionDecl
= functionDecl
->getCanonicalDecl();
173 if (!isCalleeFunctionInteresting(canonicalFunctionDecl
)) {
176 definitionSet
.insert(niceName(canonicalFunctionDecl
));
178 if (functionDecl
->doesThisDeclarationHaveABody()) {
179 bool bLargeFunction
= false;
180 if (const CompoundStmt
* compoundStmt
= dyn_cast
<CompoundStmt
>(functionDecl
->getBody())) {
181 if (compoundStmt
->size() > 1) {
182 bLargeFunction
= true;
184 if (!bLargeFunction
) {
185 auto s1
= compiler
.getSourceManager().getCharacterData(compoundStmt
->getLBracLoc());
186 auto s2
= compiler
.getSourceManager().getCharacterData(compoundStmt
->getRBracLoc());
187 bLargeFunction
= (s2
- s1
) > 40;
188 // any function that uses a parameter more than once
189 if (!bLargeFunction
) {
190 StringRef
bodyText(s1
, s2
-s1
);
191 for (const ParmVarDecl
* param
: functionDecl
->parameters()) {
192 StringRef name
= param
->getName();
195 size_t idx
= bodyText
.find(name
);
196 if (idx
!= StringRef::npos
&& bodyText
.find(name
, idx
+1) != StringRef::npos
) {
197 bLargeFunction
= true;
204 if (bLargeFunction
) {
205 largeFunctionSet
.insert(niceName(canonicalFunctionDecl
));
211 bool ExpandableMethods::TraverseFunctionDecl( FunctionDecl
* p
)
213 maTraversingFunctions
.push_back(p
);
214 bool ret
= RecursiveASTVisitor::TraverseFunctionDecl(p
);
215 maTraversingFunctions
.pop_back();
218 bool ExpandableMethods::TraverseCXXMethodDecl( CXXMethodDecl
* p
)
220 maTraversingFunctions
.push_back(p
);
221 bool ret
= RecursiveASTVisitor::TraverseCXXMethodDecl(p
);
222 maTraversingFunctions
.pop_back();
225 bool ExpandableMethods::TraverseCXXConstructorDecl( CXXConstructorDecl
* p
)
227 maTraversingFunctions
.push_back(p
);
228 bool ret
= RecursiveASTVisitor::TraverseCXXConstructorDecl(p
);
229 maTraversingFunctions
.pop_back();
232 bool ExpandableMethods::TraverseCXXConversionDecl( CXXConversionDecl
* p
)
234 maTraversingFunctions
.push_back(p
);
235 bool ret
= RecursiveASTVisitor::TraverseCXXConversionDecl(p
);
236 maTraversingFunctions
.pop_back();
239 bool ExpandableMethods::TraverseCXXDestructorDecl( CXXDestructorDecl
* p
)
241 maTraversingFunctions
.push_back(p
);
242 bool ret
= RecursiveASTVisitor::TraverseCXXDestructorDecl(p
);
243 maTraversingFunctions
.pop_back();
247 bool ExpandableMethods::VisitMemberExpr( const MemberExpr
* memberExpr
)
249 const FunctionDecl
* functionDecl
= dyn_cast
<FunctionDecl
>(memberExpr
->getMemberDecl());
251 functionTouchedFromExpr(functionDecl
, memberExpr
);
256 bool ExpandableMethods::VisitDeclRefExpr( const DeclRefExpr
* declRefExpr
)
258 const FunctionDecl
* functionDecl
= dyn_cast
<FunctionDecl
>(declRefExpr
->getDecl());
260 functionTouchedFromExpr(functionDecl
, declRefExpr
);
265 void ExpandableMethods::functionTouchedFromExpr( const FunctionDecl
* calleeFunctionDecl
, const Expr
* expr
)
267 const FunctionDecl
* canonicalFunctionDecl
= calleeFunctionDecl
->getCanonicalDecl();
268 if (!isCalleeFunctionInteresting(canonicalFunctionDecl
)) {
272 calledFromSet
.emplace(toString(expr
->getBeginLoc()), niceName(canonicalFunctionDecl
));
274 if (const UnaryOperator
* unaryOp
= dyn_cast_or_null
<UnaryOperator
>(getParentStmt(expr
))) {
275 if (unaryOp
->getOpcode() == UO_AddrOf
) {
276 addressOfSet
.insert(niceName(canonicalFunctionDecl
));
280 const CXXMethodDecl
* calleeMethodDecl
= dyn_cast
<CXXMethodDecl
>(calleeFunctionDecl
);
281 if (maTraversingFunctions
.empty())
283 calledFromOutsideSet
.insert(niceName(canonicalFunctionDecl
));
287 const CXXMethodDecl
* callsiteParentMethodDecl
= dyn_cast
<CXXMethodDecl
>(maTraversingFunctions
.back());
288 if (!callsiteParentMethodDecl
289 || calleeMethodDecl
->getParent() != callsiteParentMethodDecl
->getParent())
291 calledFromOutsideSet
.insert(niceName(canonicalFunctionDecl
));
296 bool ExpandableMethods::isCalleeFunctionInteresting(const FunctionDecl
* functionDecl
)
298 // ignore stuff that forms part of the stable URE interface
299 if (isInUnoIncludeFile(functionDecl
)) {
302 if (isa
<CXXDestructorDecl
>(functionDecl
)) {
305 if (functionDecl
->isDeleted() || functionDecl
->isDefaulted()) {
308 if (isa
<CXXConstructorDecl
>(functionDecl
)
309 && dyn_cast
<CXXConstructorDecl
>(functionDecl
)->isCopyOrMoveConstructor())
313 if (!functionDecl
->getLocation().isValid() || ignoreLocation(functionDecl
)) {
316 const CXXMethodDecl
* methodDecl
= dyn_cast
<CXXMethodDecl
>(functionDecl
);
317 if (!methodDecl
|| methodDecl
->isVirtual()) {
323 loplugin::Plugin::Registration
< ExpandableMethods
> X("expandablemethods", false);
327 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */