Version 6.1.0.2, tag libreoffice-6.1.0.2
[LibreOffice.git] / compilerplugins / clang / inlinefields.cxx
blob0c1d28e20e0cf98f1b6afc5c0c9cd076fe014f01
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 "plugin.hxx"
16 #include "compat.hxx"
18 /**
19 if a field is
20 - a pointer
21 - only assigned to in the constructor via a new expression
22 - unconditionally deleted in the destructor
23 then it can probably just be allocated inline in the parent object
25 TODO check for cases where the pointer is passed by non-const reference
27 Be warned that it produces around 5G of log file.
29 The process goes something like this:
30 $ make check
31 $ make FORCE_COMPILE_ALL=1 COMPILER_PLUGIN_TOOL='inlinefields' check
32 $ ./compilerplugins/clang/inlinefields.py
34 and then
35 $ for dir in *; do make FORCE_COMPILE_ALL=1 UPDATE_FILES=$dir COMPILER_PLUGIN_TOOL='inlinefieldsremove' $dir; done
36 to auto-remove the method declarations
38 Note that the actual process may involve a fair amount of undoing, hand editing, and general messing around
39 to get it to work :-)
43 namespace {
45 struct MyFieldInfo
47 std::string parentClass;
48 std::string fieldName;
49 std::string sourceLocation;
51 bool operator < (const MyFieldInfo &lhs, const MyFieldInfo &rhs)
53 return std::tie(lhs.parentClass, lhs.fieldName)
54 < std::tie(rhs.parentClass, rhs.fieldName);
58 // try to limit the voluminous output a little
59 static std::set<MyFieldInfo> excludedSet;
60 static std::set<MyFieldInfo> definitionSet;
61 static std::set<MyFieldInfo> deletedInDestructorSet;
62 static std::set<MyFieldInfo> newedInConstructorSet;
64 class InlineFields:
65 public RecursiveASTVisitor<InlineFields>, public loplugin::Plugin
67 public:
68 explicit InlineFields(loplugin::InstantiationData const & data):
69 Plugin(data) {}
71 virtual void run() override
73 TraverseDecl(compiler.getASTContext().getTranslationUnitDecl());
75 // dump all our output in one write call - this is to try and limit IO "crosstalk" between multiple processes
76 // writing to the same logfile
77 std::string output;
78 for (const MyFieldInfo & s : definitionSet)
79 output += "definition:\t" + s.parentClass + "\t" + s.fieldName + "\t" + s.sourceLocation + "\n";
80 for (const MyFieldInfo & s : excludedSet)
81 output += "excluded:\t" + s.parentClass + "\t" + s.fieldName + "\n";
82 for (const MyFieldInfo & s : deletedInDestructorSet)
83 output += "deletedInDestructor:\t" + s.parentClass + "\t" + s.fieldName + "\n";
84 for (const MyFieldInfo & s : newedInConstructorSet)
85 output += "newedInConstructor:\t" + s.parentClass + "\t" + s.fieldName + "\n";
86 std::ofstream myfile;
87 myfile.open( WORKDIR "/loplugin.inlinefields.log", std::ios::app | std::ios::out);
88 myfile << output;
89 myfile.close();
92 bool shouldVisitTemplateInstantiations () const { return true; }
93 bool shouldVisitImplicitCode() const { return true; }
95 bool VisitFieldDecl( const FieldDecl* );
96 bool VisitCXXConstructorDecl( const CXXConstructorDecl* );
97 bool VisitCXXDeleteExpr( const CXXDeleteExpr* );
98 bool VisitBinAssign( const BinaryOperator* );
99 private:
100 MyFieldInfo niceName(const FieldDecl*);
101 void checkTouched(const FieldDecl* fieldDecl, const Expr* memberExpr);
104 MyFieldInfo InlineFields::niceName(const FieldDecl* fieldDecl)
106 MyFieldInfo aInfo;
108 const RecordDecl* recordDecl = fieldDecl->getParent();
110 if (const CXXRecordDecl* cxxRecordDecl = dyn_cast<CXXRecordDecl>(recordDecl))
112 if (cxxRecordDecl->getTemplateInstantiationPattern())
113 cxxRecordDecl = cxxRecordDecl->getTemplateInstantiationPattern();
114 aInfo.parentClass = cxxRecordDecl->getQualifiedNameAsString();
116 else
117 aInfo.parentClass = recordDecl->getQualifiedNameAsString();
119 aInfo.fieldName = fieldDecl->getNameAsString();
121 SourceLocation expansionLoc = compiler.getSourceManager().getExpansionLoc( fieldDecl->getLocation() );
122 StringRef name = compiler.getSourceManager().getFilename(expansionLoc);
123 aInfo.sourceLocation = std::string(name.substr(strlen(SRCDIR)+1)) + ":" + std::to_string(compiler.getSourceManager().getSpellingLineNumber(expansionLoc));
124 loplugin::normalizeDotDotInFilePath(aInfo.sourceLocation);
126 return aInfo;
129 bool InlineFields::VisitFieldDecl( const FieldDecl* fieldDecl )
131 fieldDecl = fieldDecl->getCanonicalDecl();
132 if (ignoreLocation( fieldDecl )) {
133 return true;
135 // ignore stuff that forms part of the stable URE interface
136 if (isInUnoIncludeFile(compiler.getSourceManager().getSpellingLoc(fieldDecl->getLocation()))) {
137 return true;
139 QualType type = fieldDecl->getType();
140 if (!type->isPointerType())
141 return true;
142 definitionSet.insert(niceName(fieldDecl));
143 return true;
146 bool InlineFields::VisitCXXConstructorDecl( const CXXConstructorDecl* decl )
148 if( ignoreLocation( decl ) )
149 return true;
150 if (decl->isCopyOrMoveConstructor())
151 return true;
152 for(auto it = decl->init_begin(); it != decl->init_end(); ++it)
154 const CXXCtorInitializer* init = *it;
155 const FieldDecl* fieldDecl = init->getMember();
156 if( !fieldDecl || !fieldDecl->getType()->isPointerType() )
157 continue;
158 auto e = init->getInit();
159 if (auto parentListExpr = dyn_cast<ParenListExpr>(e))
160 e = parentListExpr->getExpr(0);
161 e = e->IgnoreParenImpCasts();
162 if( isa<CXXNewExpr>(e) )
163 newedInConstructorSet.insert(niceName(fieldDecl));
164 else if( isa<CXXNullPtrLiteralExpr>(e) || isa<GNUNullExpr>(e))
165 ; // ignore
166 else
167 excludedSet.insert(niceName(fieldDecl));
169 return true;
172 static bool isSameParent(const CXXMethodDecl* cxxMethodDecl, const FieldDecl* fieldDecl)
174 return cxxMethodDecl->getParent() == dyn_cast<CXXRecordDecl>(fieldDecl->getParent());
177 bool InlineFields::VisitBinAssign(const BinaryOperator * binaryOp)
179 if( ignoreLocation( binaryOp ) )
180 return true;
181 auto memberExpr = dyn_cast<MemberExpr>(binaryOp->getLHS());
182 if (!memberExpr)
183 return true;
184 auto fieldDecl = dyn_cast<FieldDecl>(memberExpr->getMemberDecl());
185 if (!fieldDecl || !fieldDecl->getType()->isPointerType()) {
186 return true;
188 const FunctionDecl* parentFunction = getParentFunctionDecl(binaryOp);
189 if (!parentFunction) {
190 return true;
192 // if the field is being assigned from outside its own constructor or destructor, exclude
193 auto constructorDecl = dyn_cast<CXXConstructorDecl>(parentFunction);
194 if (constructorDecl && isSameParent(constructorDecl, fieldDecl)) {
195 if( isa<CXXNewExpr>(binaryOp->getRHS()) )
196 newedInConstructorSet.insert(niceName(fieldDecl));
197 else {
198 excludedSet.insert(niceName(fieldDecl));
199 std::cout << "assign in constructor:" << std::endl;
200 binaryOp->getRHS()->dump();
202 return true;
204 auto destructorDecl = dyn_cast<CXXDestructorDecl>(parentFunction);
205 if (destructorDecl && isSameParent(destructorDecl, fieldDecl)) {
206 auto e = binaryOp->getRHS()->IgnoreParenImpCasts();
207 if( !isa<CXXNullPtrLiteralExpr>(e) && !isa<GNUNullExpr>(e)) {
208 excludedSet.insert(niceName(fieldDecl));
209 std::cout << "assign in destructor:" << std::endl;
210 e->dump();
212 return true;
214 excludedSet.insert(niceName(fieldDecl));
215 return true;
218 bool InlineFields::VisitCXXDeleteExpr(const CXXDeleteExpr * deleteExpr)
220 if( ignoreLocation( deleteExpr ) )
221 return true;
222 auto memberExpr = dyn_cast<MemberExpr>(deleteExpr->getArgument()->IgnoreParenImpCasts());
223 if (!memberExpr)
224 return true;
225 auto fieldDecl = dyn_cast<FieldDecl>(memberExpr->getMemberDecl());
226 if (!fieldDecl || !fieldDecl->getType()->isPointerType()) {
227 return true;
229 // TODO for some reason, this part is not working properly, it doesn't find the parent
230 // function for delete statements properly
231 const FunctionDecl* parentFunction = getParentFunctionDecl(deleteExpr);
232 if (!parentFunction) {
233 return true;
235 auto destructorDecl = dyn_cast<CXXDestructorDecl>(parentFunction);
236 if (destructorDecl && isSameParent(destructorDecl, fieldDecl)) {
237 deletedInDestructorSet.insert(niceName(fieldDecl));
238 return true;
240 excludedSet.insert(niceName(fieldDecl));
241 return true;
244 loplugin::Plugin::Registration< InlineFields > X("inlinefields", false);
248 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */