Remove exec bits from docx
[LibreOffice.git] / compilerplugins / clang / expandablemethods.cxx
blob825c09705dd5d2655e49efd9955623c8a0702894
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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/.
8 */
10 #include <cassert>
11 #include <string>
12 #include <iostream>
13 #include <fstream>
14 #include <set>
15 #include <unordered_map>
18 #include "clang/AST/Attr.h"
20 #include "config_clang.h"
22 #include "plugin.hxx"
24 /**
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
31 multiple constructors
34 namespace {
36 struct MyFuncInfo
38 std::string access;
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
62 public:
63 explicit ExpandableMethods(loplugin::InstantiationData const & data):
64 Plugin(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
75 std::string output;
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";
87 std::ofstream myfile;
88 myfile.open( WORKDIR "/loplugin.expandablemethods.log", std::ios::app | std::ios::out);
89 myfile << output;
90 myfile.close();
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* );
106 private:
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();
123 MyFuncInfo aInfo;
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();
133 } else {
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() + "(";
143 bool bFirst = true;
144 for (const ParmVarDecl *pParmVarDecl : functionDecl->parameters()) {
145 if (bFirst)
146 bFirst = false;
147 else
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() );
158 return aInfo;
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)) {
174 return true;
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();
193 if (name.empty())
194 continue;
195 size_t idx = bodyText.find(name);
196 if (idx != StringRef::npos && bodyText.find(name, idx+1) != StringRef::npos) {
197 bLargeFunction = true;
198 break;
204 if (bLargeFunction) {
205 largeFunctionSet.insert(niceName(canonicalFunctionDecl));
208 return true;
211 bool ExpandableMethods::TraverseFunctionDecl( FunctionDecl* p )
213 maTraversingFunctions.push_back(p);
214 bool ret = RecursiveASTVisitor::TraverseFunctionDecl(p);
215 maTraversingFunctions.pop_back();
216 return ret;
218 bool ExpandableMethods::TraverseCXXMethodDecl( CXXMethodDecl* p )
220 maTraversingFunctions.push_back(p);
221 bool ret = RecursiveASTVisitor::TraverseCXXMethodDecl(p);
222 maTraversingFunctions.pop_back();
223 return ret;
225 bool ExpandableMethods::TraverseCXXConstructorDecl( CXXConstructorDecl* p )
227 maTraversingFunctions.push_back(p);
228 bool ret = RecursiveASTVisitor::TraverseCXXConstructorDecl(p);
229 maTraversingFunctions.pop_back();
230 return ret;
232 bool ExpandableMethods::TraverseCXXConversionDecl( CXXConversionDecl* p )
234 maTraversingFunctions.push_back(p);
235 bool ret = RecursiveASTVisitor::TraverseCXXConversionDecl(p);
236 maTraversingFunctions.pop_back();
237 return ret;
239 bool ExpandableMethods::TraverseCXXDestructorDecl( CXXDestructorDecl* p )
241 maTraversingFunctions.push_back(p);
242 bool ret = RecursiveASTVisitor::TraverseCXXDestructorDecl(p);
243 maTraversingFunctions.pop_back();
244 return ret;
247 bool ExpandableMethods::VisitMemberExpr( const MemberExpr* memberExpr )
249 const FunctionDecl* functionDecl = dyn_cast<FunctionDecl>(memberExpr->getMemberDecl());
250 if (functionDecl) {
251 functionTouchedFromExpr(functionDecl, memberExpr);
253 return true;
256 bool ExpandableMethods::VisitDeclRefExpr( const DeclRefExpr* declRefExpr )
258 const FunctionDecl* functionDecl = dyn_cast<FunctionDecl>(declRefExpr->getDecl());
259 if (functionDecl) {
260 functionTouchedFromExpr(functionDecl, declRefExpr);
262 return true;
265 void ExpandableMethods::functionTouchedFromExpr( const FunctionDecl* calleeFunctionDecl, const Expr* expr )
267 const FunctionDecl* canonicalFunctionDecl = calleeFunctionDecl->getCanonicalDecl();
268 if (!isCalleeFunctionInteresting(canonicalFunctionDecl)) {
269 return;
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));
285 else
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)) {
300 return false;
302 if (isa<CXXDestructorDecl>(functionDecl)) {
303 return false;
305 if (functionDecl->isDeleted() || functionDecl->isDefaulted()) {
306 return false;
308 if (isa<CXXConstructorDecl>(functionDecl)
309 && dyn_cast<CXXConstructorDecl>(functionDecl)->isCopyOrMoveConstructor())
311 return false;
313 if (!functionDecl->getLocation().isValid() || ignoreLocation(functionDecl)) {
314 return false;
316 const CXXMethodDecl* methodDecl = dyn_cast<CXXMethodDecl>(functionDecl);
317 if (!methodDecl || methodDecl->isVirtual()) {
318 return false;
320 return true;
323 loplugin::Plugin::Registration< ExpandableMethods > X("expandablemethods", false);
327 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */