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/.
16 #include "config_clang.h"
20 #if CLANG_VERSION >= 110000
21 #include "clang/AST/ParentMapContext.h"
25 Look for fields that are only ever assigned a single constant value.
27 We dmp a list of values assigned to fields, and a list of field definitions.
28 Then we will post-process the 2 lists and find the set of interesting fields.
30 Be warned that it produces around 5G of log file.
32 The process goes something like this:
34 $ make FORCE_COMPILE_ALL=1 COMPILER_PLUGIN_TOOL='singlevalfields' check
35 $ ./compilerplugins/clang/singlevalfields.py
37 Note that the actual process may involve a fair amount of undoing, hand editing, and general messing around
40 @TODO we don't spot fields that have been zero-initialised via calloc or rtl_allocateZeroMemory or memset
41 @TODO calls to lambdas where a reference to the field is taken
49 FieldDecl
const * fieldDecl
;
50 std::string parentClass
;
51 std::string fieldName
;
52 std::string fieldType
;
53 std::string sourceLocation
;
55 bool operator < (const MyFieldInfo
&lhs
, const MyFieldInfo
&rhs
)
57 return std::tie(lhs
.parentClass
, lhs
.fieldName
)
58 < std::tie(rhs
.parentClass
, rhs
.fieldName
);
61 struct MyFieldAssignmentInfo
: public MyFieldInfo
66 bool operator < (const MyFieldAssignmentInfo
&lhs
, const MyFieldAssignmentInfo
&rhs
)
68 return std::tie(lhs
.parentClass
, lhs
.fieldName
, lhs
.value
)
69 < std::tie(rhs
.parentClass
, rhs
.fieldName
, rhs
.value
);
73 // try to limit the voluminous output a little
74 static std::set
<MyFieldAssignmentInfo
> assignedSet
;
75 static std::set
<MyFieldInfo
> definitionSet
;
77 /** escape the value string to make it easier to parse the output file in python */
78 std::string
escape(std::string s
)
81 for (size_t i
=0; i
<s
.length(); ++i
)
85 out
+= "\\" + std::to_string((int)s
[i
]);
89 class SingleValFields
:
90 public RecursiveASTVisitor
<SingleValFields
>, public loplugin::Plugin
93 explicit SingleValFields(loplugin::InstantiationData
const & data
):
96 virtual void run() override
98 TraverseDecl(compiler
.getASTContext().getTranslationUnitDecl());
100 if (!isUnitTestMode())
102 // dump all our output in one write call - this is to try and limit IO "crosstalk" between multiple processes
103 // writing to the same logfile
105 for (const MyFieldAssignmentInfo
& s
: assignedSet
)
106 output
+= "asgn:\t" + s
.parentClass
+ "\t" + s
.fieldName
+ "\t" + escape(s
.value
) + "\n";
107 for (const MyFieldInfo
& s
: definitionSet
)
108 output
+= "defn:\t" + s
.parentClass
+ "\t" + s
.fieldName
+ "\t" + s
.fieldType
+ "\t" + s
.sourceLocation
+ "\n";
109 std::ofstream myfile
;
110 myfile
.open( WORKDIR
"/loplugin.singlevalfields.log", std::ios::app
| std::ios::out
);
116 for (const MyFieldAssignmentInfo
& s
: assignedSet
)
117 if (s
.fieldDecl
&& compiler
.getSourceManager().isInMainFile(compat::getBeginLoc(s
.fieldDecl
)))
119 DiagnosticsEngine::Warning
,
121 compat::getBeginLoc(s
.fieldDecl
))
126 bool shouldVisitTemplateInstantiations () const { return true; }
127 // to catch compiler-generated constructors
128 bool shouldVisitImplicitCode() const { return true; }
130 bool VisitFieldDecl( const FieldDecl
* );
131 bool VisitVarDecl( const VarDecl
* );
132 bool VisitMemberExpr( const MemberExpr
* );
133 bool VisitDeclRefExpr( const DeclRefExpr
* );
134 bool VisitCXXConstructorDecl( const CXXConstructorDecl
* );
135 // bool VisitUnaryExprOrTypeTraitExpr( const UnaryExprOrTypeTraitExpr* );
137 void niceName(const DeclaratorDecl
*, MyFieldInfo
&);
138 void walkPotentialAssign( const DeclaratorDecl
* fieldOrVarDecl
, const Stmt
* stmt
);
139 std::string
getExprValue(const Expr
*);
140 const FunctionDecl
* get_top_FunctionDecl_from_Stmt(const Stmt
&);
141 void checkCallExpr(const Stmt
* child
, const CallExpr
* callExpr
, std::string
& assignValue
, bool& bPotentiallyAssignedTo
);
144 void SingleValFields::niceName(const DeclaratorDecl
* fieldOrVarDecl
, MyFieldInfo
& aInfo
)
146 const VarDecl
* varDecl
= dyn_cast
<VarDecl
>(fieldOrVarDecl
);
147 const FieldDecl
* fieldDecl
= dyn_cast
<FieldDecl
>(fieldOrVarDecl
);
148 aInfo
.fieldDecl
= fieldDecl
;
150 aInfo
.parentClass
= fieldDecl
->getParent()->getQualifiedNameAsString();
153 if (auto parentRecordDecl
= dyn_cast
<CXXRecordDecl
>(varDecl
->getDeclContext()))
154 aInfo
.parentClass
= parentRecordDecl
->getQualifiedNameAsString();
155 else if (auto parentMethodDecl
= dyn_cast
<CXXMethodDecl
>(varDecl
->getDeclContext()))
156 aInfo
.parentClass
= parentMethodDecl
->getQualifiedNameAsString();
157 else if (auto parentFunctionDecl
= dyn_cast
<FunctionDecl
>(varDecl
->getDeclContext()))
158 aInfo
.parentClass
= parentFunctionDecl
->getQualifiedNameAsString();
159 else if (isa
<TranslationUnitDecl
>(varDecl
->getDeclContext()))
160 aInfo
.parentClass
= handler
.getMainFileName().str();
161 else if (auto parentNamespaceDecl
= dyn_cast
<NamespaceDecl
>(varDecl
->getDeclContext()))
162 aInfo
.parentClass
= parentNamespaceDecl
->getQualifiedNameAsString();
163 else if (isa
<LinkageSpecDecl
>(varDecl
->getDeclContext()))
164 aInfo
.parentClass
= "extern"; // what to do here?
167 std::cout
<< "what is this? " << varDecl
->getDeclContext()->getDeclKindName() << std::endl
;
171 aInfo
.fieldName
= fieldOrVarDecl
->getNameAsString();
172 aInfo
.fieldType
= fieldOrVarDecl
->getType().getAsString();
174 SourceLocation expansionLoc
= compiler
.getSourceManager().getExpansionLoc( fieldOrVarDecl
->getLocation() );
175 StringRef name
= getFilenameOfLocation(expansionLoc
);
176 aInfo
.sourceLocation
= std::string(name
.substr(strlen(SRCDIR
)+1)) + ":" + std::to_string(compiler
.getSourceManager().getSpellingLineNumber(expansionLoc
));
177 loplugin::normalizeDotDotInFilePath(aInfo
.sourceLocation
);
180 bool SingleValFields::VisitFieldDecl( const FieldDecl
* fieldDecl
)
182 auto canonicalDecl
= fieldDecl
->getCanonicalDecl();
184 if( ignoreLocation( canonicalDecl
)
185 || isInUnoIncludeFile( compiler
.getSourceManager().getSpellingLoc(canonicalDecl
->getLocation())) )
189 niceName(canonicalDecl
, aInfo
);
190 definitionSet
.insert(aInfo
);
192 if (fieldDecl
->getInClassInitializer())
194 MyFieldAssignmentInfo aInfo
;
195 niceName(canonicalDecl
, aInfo
);
196 aInfo
.value
= getExprValue(fieldDecl
->getInClassInitializer());
197 assignedSet
.insert(aInfo
);
203 bool SingleValFields::VisitVarDecl( const VarDecl
* varDecl
)
205 if (isa
<ParmVarDecl
>(varDecl
))
207 if (varDecl
->getType().isConstQualified())
209 if (!(varDecl
->isStaticLocal() || varDecl
->isStaticDataMember() || varDecl
->hasGlobalStorage()))
212 auto canonicalDecl
= varDecl
->getCanonicalDecl();
213 if (!canonicalDecl
->getLocation().isValid())
216 if( ignoreLocation( canonicalDecl
)
217 || isInUnoIncludeFile( compiler
.getSourceManager().getSpellingLoc(canonicalDecl
->getLocation())) )
221 niceName(canonicalDecl
, aInfo
);
222 definitionSet
.insert(aInfo
);
224 if (varDecl
->getInit())
226 MyFieldAssignmentInfo aInfo
;
227 niceName(canonicalDecl
, aInfo
);
228 aInfo
.value
= getExprValue(varDecl
->getInit());
229 assignedSet
.insert(aInfo
);
235 bool SingleValFields::VisitCXXConstructorDecl( const CXXConstructorDecl
* decl
)
237 if( ignoreLocation( decl
) )
240 // doesn't count as a write to fields because it's self->self
241 if (decl
->isCopyOrMoveConstructor())
244 for(auto it
= decl
->init_begin(); it
!= decl
->init_end(); ++it
)
246 const CXXCtorInitializer
* init
= *it
;
247 const FieldDecl
* fieldDecl
= init
->getMember();
250 MyFieldAssignmentInfo aInfo
;
251 niceName(fieldDecl
, aInfo
);
252 const Expr
* expr
= init
->getInit();
253 // unwrap any single-arg constructors, this helps to find smart pointers
254 // that are only assigned nullptr
255 if (auto cxxConstructExpr
= dyn_cast
<CXXConstructExpr
>(expr
))
256 if (cxxConstructExpr
->getNumArgs() == 1)
257 expr
= cxxConstructExpr
->getArg(0);
258 aInfo
.value
= getExprValue(expr
);
259 assignedSet
.insert(aInfo
);
264 bool SingleValFields::VisitMemberExpr( const MemberExpr
* memberExpr
)
266 const ValueDecl
* decl
= memberExpr
->getMemberDecl();
267 const FieldDecl
* fieldDecl
= dyn_cast
<FieldDecl
>(decl
);
270 if (ignoreLocation(memberExpr
))
272 walkPotentialAssign(fieldDecl
, memberExpr
);
276 bool SingleValFields::VisitDeclRefExpr( const DeclRefExpr
* declRefExpr
)
278 const VarDecl
* varDecl
= dyn_cast_or_null
<VarDecl
>(declRefExpr
->getDecl());
281 if (isa
<ParmVarDecl
>(varDecl
))
283 if (varDecl
->getType().isConstQualified())
285 if (!(varDecl
->isStaticLocal() || varDecl
->isStaticDataMember() || varDecl
->hasGlobalStorage()))
287 if (ignoreLocation(declRefExpr
))
289 walkPotentialAssign(varDecl
, declRefExpr
);
293 void SingleValFields::walkPotentialAssign( const DeclaratorDecl
* fieldOrVarDecl
, const Stmt
* memberExpr
)
295 const FunctionDecl
* parentFunction
= getParentFunctionDecl(memberExpr
);
298 auto methodDecl
= dyn_cast
<CXXMethodDecl
>(parentFunction
);
299 if (methodDecl
&& (methodDecl
->isCopyAssignmentOperator() || methodDecl
->isMoveAssignmentOperator()))
301 if (methodDecl
&& methodDecl
->getIdentifier()
302 && (methodDecl
->getName().startswith("Clone") || methodDecl
->getName().startswith("clone")))
304 auto cxxConstructorDecl
= dyn_cast
<CXXConstructorDecl
>(parentFunction
);
305 if (cxxConstructorDecl
&& cxxConstructorDecl
->isCopyOrMoveConstructor())
309 // walk up the tree until we find something interesting
310 const Stmt
* child
= memberExpr
;
311 const Stmt
* parent
= getParentStmt(memberExpr
);
312 bool bPotentiallyAssignedTo
= false;
314 std::string assignValue
= "?";
316 // check for field being returned by non-const ref eg. Foo& getFoo() { return f; }
317 if (parentFunction
&& parent
&& isa
<ReturnStmt
>(parent
)) {
318 const Stmt
* parent2
= getParentStmt(parent
);
319 if (parent2
&& isa
<CompoundStmt
>(parent2
)) {
320 QualType qt
= parentFunction
->getReturnType().getDesugaredType(compiler
.getASTContext());
321 if (!qt
.isConstQualified() && qt
->isReferenceType()) {
322 bPotentiallyAssignedTo
= true;
327 while (!bPotentiallyAssignedTo
) {
328 // check for field being accessed by a reference variable e.g. Foo& f = m.foo;
329 auto parentsList
= compiler
.getASTContext().getParents(*child
);
330 auto it
= parentsList
.begin();
331 if (it
!= parentsList
.end()) {
332 const VarDecl
*varDecl
= it
->get
<VarDecl
>();
334 QualType qt
= varDecl
->getType().getDesugaredType(compiler
.getASTContext());
335 if (!qt
.isConstQualified() && qt
->isReferenceType()) {
336 bPotentiallyAssignedTo
= true;
345 if (isa
<CastExpr
>(parent
) || isa
<MemberExpr
>(parent
) || isa
<ParenExpr
>(parent
) || isa
<ParenListExpr
>(parent
)
346 || isa
<ExprWithCleanups
>(parent
))
349 parent
= getParentStmt(parent
);
351 else if (isa
<UnaryOperator
>(parent
))
353 const UnaryOperator
* unaryOperator
= dyn_cast
<UnaryOperator
>(parent
);
354 int x
= unaryOperator
->getOpcode();
355 if (x
== UO_AddrOf
|| x
== UO_PostInc
|| x
== UO_PostDec
|| x
== UO_PreInc
|| x
== UO_PreDec
) {
357 bPotentiallyAssignedTo
= true;
360 // cannot be assigned to anymore
363 else if (auto callExpr
= dyn_cast
<CallExpr
>(parent
))
365 checkCallExpr(child
, callExpr
, assignValue
, bPotentiallyAssignedTo
);
368 else if (isa
<CXXConstructExpr
>(parent
))
370 const CXXConstructExpr
* consExpr
= dyn_cast
<CXXConstructExpr
>(parent
);
371 const CXXConstructorDecl
* consDecl
= consExpr
->getConstructor();
372 for (unsigned i
= 0; i
< consExpr
->getNumArgs(); ++i
) {
373 if (i
>= consDecl
->getNumParams()) // can happen in template code
375 if (consExpr
->getArg(i
) == child
) {
376 const ParmVarDecl
* parmVarDecl
= consDecl
->getParamDecl(i
);
377 QualType qt
= parmVarDecl
->getType().getDesugaredType(compiler
.getASTContext());
378 if (!qt
.isConstQualified() && qt
->isReferenceType()) {
379 bPotentiallyAssignedTo
= true;
386 else if (isa
<BinaryOperator
>(parent
))
388 const BinaryOperator
* binaryOp
= dyn_cast
<BinaryOperator
>(parent
);
389 auto op
= binaryOp
->getOpcode();
390 if ( binaryOp
->getLHS() != child
) {
391 // if the expr is on the RHS, do nothing
393 else if ( op
== BO_Assign
) {
394 assignValue
= getExprValue(binaryOp
->getRHS());
395 bPotentiallyAssignedTo
= true;
396 } else if ( op
== BO_MulAssign
|| op
== BO_DivAssign
397 || op
== BO_RemAssign
|| op
== BO_AddAssign
398 || op
== BO_SubAssign
|| op
== BO_ShlAssign
399 || op
== BO_ShrAssign
|| op
== BO_AndAssign
400 || op
== BO_XorAssign
|| op
== BO_OrAssign
)
402 bPotentiallyAssignedTo
= true;
406 else if ( isa
<CompoundStmt
>(parent
)
407 || isa
<SwitchStmt
>(parent
) || isa
<CaseStmt
>(parent
) || isa
<DefaultStmt
>(parent
)
408 || isa
<DoStmt
>(parent
) || isa
<WhileStmt
>(parent
)
409 || isa
<IfStmt
>(parent
)
410 || isa
<ForStmt
>(parent
)
411 || isa
<ReturnStmt
>(parent
)
412 || isa
<CXXNewExpr
>(parent
)
413 || isa
<CXXDeleteExpr
>(parent
)
414 || isa
<ConditionalOperator
>(parent
)
415 || isa
<CXXTypeidExpr
>(parent
)
416 || isa
<ArraySubscriptExpr
>(parent
)
417 || isa
<CXXDependentScopeMemberExpr
>(parent
)
418 || isa
<DeclStmt
>(parent
)
419 || isa
<UnaryExprOrTypeTraitExpr
>(parent
)
420 || isa
<UnresolvedMemberExpr
>(parent
)
421 || isa
<MaterializeTemporaryExpr
>(parent
) //???
422 || isa
<InitListExpr
>(parent
)
423 || isa
<CXXUnresolvedConstructExpr
>(parent
)
424 || isa
<LambdaExpr
>(parent
)
425 || isa
<PackExpansionExpr
>(parent
)
426 || isa
<CXXPseudoDestructorExpr
>(parent
)
431 else if ( isa
<ArrayInitLoopExpr
>(parent
) || isa
<GCCAsmStmt
>(parent
) || isa
<VAArgExpr
>(parent
))
433 bPotentiallyAssignedTo
= true;
437 bPotentiallyAssignedTo
= true;
445 DiagnosticsEngine::Warning
,
446 "oh dear, what can the matter be?",
447 compat::getBeginLoc(memberExpr
))
448 << memberExpr
->getSourceRange();
451 if (bPotentiallyAssignedTo
)
453 MyFieldAssignmentInfo aInfo
;
454 niceName(fieldOrVarDecl
, aInfo
);
455 aInfo
.value
= assignValue
;
456 assignedSet
.insert(aInfo
);
460 void SingleValFields::checkCallExpr(const Stmt
* child
, const CallExpr
* callExpr
, std::string
& assignValue
, bool& bPotentiallyAssignedTo
)
462 if (callExpr
->getCallee() == child
) {
465 const FunctionDecl
* functionDecl
;
466 if (auto memberCallExpr
= dyn_cast
<CXXMemberCallExpr
>(callExpr
)) {
467 functionDecl
= memberCallExpr
->getMethodDecl();
469 functionDecl
= callExpr
->getDirectCallee();
472 if (auto operatorCallExpr
= dyn_cast
<CXXOperatorCallExpr
>(callExpr
)) {
473 if (operatorCallExpr
->getArg(0) == child
) {
474 const CXXMethodDecl
* calleeMethodDecl
= dyn_cast_or_null
<CXXMethodDecl
>(operatorCallExpr
->getDirectCallee());
475 if (calleeMethodDecl
) {
476 if (operatorCallExpr
->getOperator() == OO_Equal
) {
477 assignValue
= getExprValue(operatorCallExpr
->getArg(1));
478 bPotentiallyAssignedTo
= true;
484 for (unsigned i
= 0; i
< callExpr
->getNumArgs(); ++i
) {
485 if (i
>= functionDecl
->getNumParams()) // can happen in template code
487 if (callExpr
->getArg(i
) == child
) {
488 const ParmVarDecl
* parmVarDecl
= functionDecl
->getParamDecl(i
);
489 QualType qt
= parmVarDecl
->getType().getDesugaredType(compiler
.getASTContext());
490 if (!qt
.isConstQualified() && qt
->isReferenceType()) {
492 bPotentiallyAssignedTo
= true;
499 // check for function pointers
500 const FieldDecl
* calleeFieldDecl
= dyn_cast_or_null
<FieldDecl
>(callExpr
->getCalleeDecl());
501 if (!calleeFieldDecl
) {
504 QualType qt
= calleeFieldDecl
->getType().getDesugaredType(compiler
.getASTContext());
505 if (!qt
->isPointerType()) {
508 qt
= qt
->getPointeeType().getDesugaredType(compiler
.getASTContext());
509 const FunctionProtoType
* proto
= qt
->getAs
<FunctionProtoType
>();
513 for (unsigned i
= 0; i
< callExpr
->getNumArgs(); ++i
) {
514 if (i
>= proto
->getNumParams()) // can happen in template code
516 if (callExpr
->getArg(i
) == child
) {
517 QualType qt
= proto
->getParamType(i
).getDesugaredType(compiler
.getASTContext());
518 if (!qt
.isConstQualified() && qt
->isReferenceType()) {
520 bPotentiallyAssignedTo
= true;
528 std::string
SingleValFields::getExprValue(const Expr
* arg
)
532 arg
= arg
->IgnoreParenCasts();
533 arg
= arg
->IgnoreImplicit();
534 // ignore this, it seems to trigger an infinite recursion
535 if (isa
<UnaryExprOrTypeTraitExpr
>(arg
))
537 if (arg
->isValueDependent())
539 // for stuff like: OUString foo = "xxx";
540 if (auto stringLiteral
= dyn_cast
<clang::StringLiteral
>(arg
))
542 if (stringLiteral
->getCharByteWidth() == 1)
543 return stringLiteral
->getString().str();
546 // ParenListExpr containing a CXXNullPtrLiteralExpr and has a NULL type pointer
547 if (auto parenListExpr
= dyn_cast
<ParenListExpr
>(arg
))
549 if (parenListExpr
->getNumExprs() == 1)
550 return getExprValue(parenListExpr
->getExpr(0));
553 if (auto constructExpr
= dyn_cast
<CXXConstructExpr
>(arg
))
555 if (constructExpr
->getNumArgs() >= 1
556 && isa
<clang::StringLiteral
>(constructExpr
->getArg(0)))
558 auto stringLiteral
= dyn_cast
<clang::StringLiteral
>(constructExpr
->getArg(0));
559 if (stringLiteral
->getCharByteWidth() == 1)
560 return stringLiteral
->getString().str();
564 if (arg
->getType()->isFloatingType())
567 if (arg
->EvaluateAsFloat(x1
, compiler
.getASTContext()))
570 llvm::raw_string_ostream
os(s
);
576 if (compat::EvaluateAsInt(arg
, x1
, compiler
.getASTContext()))
577 return x1
.toString(10);
578 if (isa
<CXXNullPtrLiteralExpr
>(arg
))
583 loplugin::Plugin::Registration
< SingleValFields
> X("singlevalfields", false);
587 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */