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/.
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:
31 $ make FORCE_COMPILE_ALL=1 COMPILER_PLUGIN_TOOL='inlinefields' check
32 $ ./compilerplugins/clang/inlinefields.py
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
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
;
65 public RecursiveASTVisitor
<InlineFields
>, public loplugin::Plugin
68 explicit InlineFields(loplugin::InstantiationData
const & 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
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";
87 myfile
.open( WORKDIR
"/loplugin.inlinefields.log", std::ios::app
| std::ios::out
);
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
* );
100 MyFieldInfo
niceName(const FieldDecl
*);
101 void checkTouched(const FieldDecl
* fieldDecl
, const Expr
* memberExpr
);
104 MyFieldInfo
InlineFields::niceName(const FieldDecl
* fieldDecl
)
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();
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
);
129 bool InlineFields::VisitFieldDecl( const FieldDecl
* fieldDecl
)
131 fieldDecl
= fieldDecl
->getCanonicalDecl();
132 if (ignoreLocation( fieldDecl
)) {
135 // ignore stuff that forms part of the stable URE interface
136 if (isInUnoIncludeFile(compiler
.getSourceManager().getSpellingLoc(fieldDecl
->getLocation()))) {
139 QualType type
= fieldDecl
->getType();
140 if (!type
->isPointerType())
142 definitionSet
.insert(niceName(fieldDecl
));
146 bool InlineFields::VisitCXXConstructorDecl( const CXXConstructorDecl
* decl
)
148 if( ignoreLocation( decl
) )
150 if (decl
->isCopyOrMoveConstructor())
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() )
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
))
167 excludedSet
.insert(niceName(fieldDecl
));
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
) )
181 auto memberExpr
= dyn_cast
<MemberExpr
>(binaryOp
->getLHS());
184 auto fieldDecl
= dyn_cast
<FieldDecl
>(memberExpr
->getMemberDecl());
185 if (!fieldDecl
|| !fieldDecl
->getType()->isPointerType()) {
188 const FunctionDecl
* parentFunction
= getParentFunctionDecl(binaryOp
);
189 if (!parentFunction
) {
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
));
198 excludedSet
.insert(niceName(fieldDecl
));
199 std::cout
<< "assign in constructor:" << std::endl
;
200 binaryOp
->getRHS()->dump();
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
;
214 excludedSet
.insert(niceName(fieldDecl
));
218 bool InlineFields::VisitCXXDeleteExpr(const CXXDeleteExpr
* deleteExpr
)
220 if( ignoreLocation( deleteExpr
) )
222 auto memberExpr
= dyn_cast
<MemberExpr
>(deleteExpr
->getArgument()->IgnoreParenImpCasts());
225 auto fieldDecl
= dyn_cast
<FieldDecl
>(memberExpr
->getMemberDecl());
226 if (!fieldDecl
|| !fieldDecl
->getType()->isPointerType()) {
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
) {
235 auto destructorDecl
= dyn_cast
<CXXDestructorDecl
>(parentFunction
);
236 if (destructorDecl
&& isSameParent(destructorDecl
, fieldDecl
)) {
237 deletedInDestructorSet
.insert(niceName(fieldDecl
));
240 excludedSet
.insert(niceName(fieldDecl
));
244 loplugin::Plugin::Registration
< InlineFields
> X("inlinefields", false);
248 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */