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"
21 #include "clang/AST/ParentMapContext.h"
24 Look for fields that are only ever assigned a single constant value.
26 We dmp a list of values assigned to fields, and a list of field definitions.
27 Then we will post-process the 2 lists and find the set of interesting fields.
29 Be warned that it produces around 5G of log file.
31 The process goes something like this:
33 $ make FORCE_COMPILE=all COMPILER_PLUGIN_TOOL='singlevalfields' check
34 $ ./compilerplugins/clang/singlevalfields.py
36 Note that the actual process may involve a fair amount of undoing, hand editing, and general messing around
39 @TODO we don't spot fields that have been zero-initialised via calloc or rtl_allocateZeroMemory or memset
40 @TODO calls to lambdas where a reference to the field is taken
48 FieldDecl
const * fieldDecl
;
49 std::string parentClass
;
50 std::string fieldName
;
51 std::string fieldType
;
52 std::string sourceLocation
;
54 bool operator < (const MyFieldInfo
&lhs
, const MyFieldInfo
&rhs
)
56 return std::tie(lhs
.parentClass
, lhs
.fieldName
)
57 < std::tie(rhs
.parentClass
, rhs
.fieldName
);
60 struct MyFieldAssignmentInfo
: public MyFieldInfo
65 bool operator < (const MyFieldAssignmentInfo
&lhs
, const MyFieldAssignmentInfo
&rhs
)
67 return std::tie(lhs
.parentClass
, lhs
.fieldName
, lhs
.value
)
68 < std::tie(rhs
.parentClass
, rhs
.fieldName
, rhs
.value
);
72 // try to limit the voluminous output a little
73 static std::set
<MyFieldAssignmentInfo
> assignedSet
;
74 static std::set
<MyFieldInfo
> definitionSet
;
76 /** escape the value string to make it easier to parse the output file in python */
77 std::string
escape(std::string s
)
80 for (size_t i
=0; i
<s
.length(); ++i
)
84 out
+= "\\" + std::to_string((int)s
[i
]);
88 class SingleValFields
:
89 public RecursiveASTVisitor
<SingleValFields
>, public loplugin::Plugin
92 explicit SingleValFields(loplugin::InstantiationData
const & data
):
95 virtual void run() override
97 handler
.enableTreeWideAnalysisMode();
99 TraverseDecl(compiler
.getASTContext().getTranslationUnitDecl());
101 if (!isUnitTestMode())
103 // dump all our output in one write call - this is to try and limit IO "crosstalk" between multiple processes
104 // writing to the same logfile
106 for (const MyFieldAssignmentInfo
& s
: assignedSet
)
107 output
+= "asgn:\t" + s
.parentClass
+ "\t" + s
.fieldName
+ "\t" + escape(s
.value
) + "\n";
108 for (const MyFieldInfo
& s
: definitionSet
)
109 output
+= "defn:\t" + s
.parentClass
+ "\t" + s
.fieldName
+ "\t" + s
.fieldType
+ "\t" + s
.sourceLocation
+ "\n";
110 std::ofstream myfile
;
111 myfile
.open( WORKDIR
"/loplugin.singlevalfields.log", std::ios::app
| std::ios::out
);
117 for (const MyFieldAssignmentInfo
& s
: assignedSet
)
118 if (s
.fieldDecl
&& compiler
.getSourceManager().isInMainFile(s
.fieldDecl
->getBeginLoc()))
120 DiagnosticsEngine::Warning
,
122 s
.fieldDecl
->getBeginLoc())
127 bool shouldVisitTemplateInstantiations () const { return true; }
128 // to catch compiler-generated constructors
129 bool shouldVisitImplicitCode() const { return true; }
131 bool VisitFieldDecl( const FieldDecl
* );
132 bool VisitVarDecl( const VarDecl
* );
133 bool VisitMemberExpr( const MemberExpr
* );
134 bool VisitDeclRefExpr( const DeclRefExpr
* );
135 bool VisitCXXConstructorDecl( const CXXConstructorDecl
* );
136 // bool VisitUnaryExprOrTypeTraitExpr( const UnaryExprOrTypeTraitExpr* );
138 void niceName(const DeclaratorDecl
*, MyFieldInfo
&);
139 void walkPotentialAssign( const DeclaratorDecl
* fieldOrVarDecl
, const Stmt
* stmt
);
140 std::string
getExprValue(const Expr
*);
141 const FunctionDecl
* get_top_FunctionDecl_from_Stmt(const Stmt
&);
142 void checkCallExpr(const Stmt
* child
, const CallExpr
* callExpr
, std::string
& assignValue
, bool& bPotentiallyAssignedTo
);
145 void SingleValFields::niceName(const DeclaratorDecl
* fieldOrVarDecl
, MyFieldInfo
& aInfo
)
147 const VarDecl
* varDecl
= dyn_cast
<VarDecl
>(fieldOrVarDecl
);
148 const FieldDecl
* fieldDecl
= dyn_cast
<FieldDecl
>(fieldOrVarDecl
);
149 aInfo
.fieldDecl
= fieldDecl
;
151 aInfo
.parentClass
= fieldDecl
->getParent()->getQualifiedNameAsString();
154 if (auto parentRecordDecl
= dyn_cast
<CXXRecordDecl
>(varDecl
->getDeclContext()))
155 aInfo
.parentClass
= parentRecordDecl
->getQualifiedNameAsString();
156 else if (auto parentMethodDecl
= dyn_cast
<CXXMethodDecl
>(varDecl
->getDeclContext()))
157 aInfo
.parentClass
= parentMethodDecl
->getQualifiedNameAsString();
158 else if (auto parentFunctionDecl
= dyn_cast
<FunctionDecl
>(varDecl
->getDeclContext()))
159 aInfo
.parentClass
= parentFunctionDecl
->getQualifiedNameAsString();
160 else if (isa
<TranslationUnitDecl
>(varDecl
->getDeclContext()))
161 aInfo
.parentClass
= handler
.getMainFileName().str();
162 else if (auto parentNamespaceDecl
= dyn_cast
<NamespaceDecl
>(varDecl
->getDeclContext()))
163 aInfo
.parentClass
= parentNamespaceDecl
->getQualifiedNameAsString();
164 else if (isa
<LinkageSpecDecl
>(varDecl
->getDeclContext()))
165 aInfo
.parentClass
= "extern"; // what to do here?
168 std::cout
<< "what is this? " << varDecl
->getDeclContext()->getDeclKindName() << std::endl
;
172 aInfo
.fieldName
= fieldOrVarDecl
->getNameAsString();
173 aInfo
.fieldType
= fieldOrVarDecl
->getType().getAsString();
175 SourceLocation expansionLoc
= compiler
.getSourceManager().getExpansionLoc( fieldOrVarDecl
->getLocation() );
176 StringRef name
= getFilenameOfLocation(expansionLoc
);
177 aInfo
.sourceLocation
= std::string(name
.substr(strlen(SRCDIR
)+1)) + ":" + std::to_string(compiler
.getSourceManager().getSpellingLineNumber(expansionLoc
));
178 loplugin::normalizeDotDotInFilePath(aInfo
.sourceLocation
);
181 bool SingleValFields::VisitFieldDecl( const FieldDecl
* fieldDecl
)
183 auto canonicalDecl
= fieldDecl
->getCanonicalDecl();
185 if( 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( isInUnoIncludeFile( compiler
.getSourceManager().getSpellingLoc(canonicalDecl
->getLocation())) )
220 niceName(canonicalDecl
, aInfo
);
221 definitionSet
.insert(aInfo
);
223 if (varDecl
->getInit())
225 MyFieldAssignmentInfo aInfo
;
226 niceName(canonicalDecl
, aInfo
);
227 aInfo
.value
= getExprValue(varDecl
->getInit());
228 assignedSet
.insert(aInfo
);
234 bool SingleValFields::VisitCXXConstructorDecl( const CXXConstructorDecl
* decl
)
236 // doesn't count as a write to fields because it's self->self
237 if (decl
->isCopyOrMoveConstructor())
240 for(auto it
= decl
->init_begin(); it
!= decl
->init_end(); ++it
)
242 const CXXCtorInitializer
* init
= *it
;
243 const FieldDecl
* fieldDecl
= init
->getMember();
246 MyFieldAssignmentInfo aInfo
;
247 niceName(fieldDecl
, aInfo
);
248 const Expr
* expr
= init
->getInit();
249 // unwrap any single-arg constructors, this helps to find smart pointers
250 // that are only assigned nullptr
251 if (auto cxxConstructExpr
= dyn_cast
<CXXConstructExpr
>(expr
))
252 if (cxxConstructExpr
->getNumArgs() == 1)
253 expr
= cxxConstructExpr
->getArg(0);
254 aInfo
.value
= getExprValue(expr
);
255 assignedSet
.insert(aInfo
);
260 bool SingleValFields::VisitMemberExpr( const MemberExpr
* memberExpr
)
262 const ValueDecl
* decl
= memberExpr
->getMemberDecl();
263 const FieldDecl
* fieldDecl
= dyn_cast
<FieldDecl
>(decl
);
266 walkPotentialAssign(fieldDecl
, memberExpr
);
270 bool SingleValFields::VisitDeclRefExpr( const DeclRefExpr
* declRefExpr
)
272 const VarDecl
* varDecl
= dyn_cast_or_null
<VarDecl
>(declRefExpr
->getDecl());
275 if (isa
<ParmVarDecl
>(varDecl
))
277 if (varDecl
->getType().isConstQualified())
279 if (!(varDecl
->isStaticLocal() || varDecl
->isStaticDataMember() || varDecl
->hasGlobalStorage()))
281 walkPotentialAssign(varDecl
, declRefExpr
);
285 void SingleValFields::walkPotentialAssign( const DeclaratorDecl
* fieldOrVarDecl
, const Stmt
* memberExpr
)
287 const FunctionDecl
* parentFunction
= getParentFunctionDecl(memberExpr
);
290 auto methodDecl
= dyn_cast
<CXXMethodDecl
>(parentFunction
);
291 if (methodDecl
&& (methodDecl
->isCopyAssignmentOperator() || methodDecl
->isMoveAssignmentOperator()))
293 if (methodDecl
&& methodDecl
->getIdentifier()
294 && (methodDecl
->getName().startswith("Clone") || methodDecl
->getName().startswith("clone")))
296 auto cxxConstructorDecl
= dyn_cast
<CXXConstructorDecl
>(parentFunction
);
297 if (cxxConstructorDecl
&& cxxConstructorDecl
->isCopyOrMoveConstructor())
301 // walk up the tree until we find something interesting
302 const Stmt
* child
= memberExpr
;
303 const Stmt
* parent
= getParentStmt(memberExpr
);
304 bool bPotentiallyAssignedTo
= false;
306 std::string assignValue
= "?";
308 // check for field being returned by non-const ref eg. Foo& getFoo() { return f; }
309 if (parentFunction
&& parent
&& isa
<ReturnStmt
>(parent
)) {
310 const Stmt
* parent2
= getParentStmt(parent
);
311 if (parent2
&& isa
<CompoundStmt
>(parent2
)) {
312 QualType qt
= parentFunction
->getReturnType().getDesugaredType(compiler
.getASTContext());
313 if (!qt
.isConstQualified() && qt
->isReferenceType()) {
314 bPotentiallyAssignedTo
= true;
319 while (!bPotentiallyAssignedTo
) {
320 // check for field being accessed by a reference variable e.g. Foo& f = m.foo;
321 auto parentsList
= compiler
.getASTContext().getParents(*child
);
322 auto it
= parentsList
.begin();
323 if (it
!= parentsList
.end()) {
324 const VarDecl
*varDecl
= it
->get
<VarDecl
>();
326 QualType qt
= varDecl
->getType().getDesugaredType(compiler
.getASTContext());
327 if (!qt
.isConstQualified() && qt
->isReferenceType()) {
328 bPotentiallyAssignedTo
= true;
337 if (isa
<CastExpr
>(parent
) || isa
<MemberExpr
>(parent
) || isa
<ParenExpr
>(parent
) || isa
<ParenListExpr
>(parent
)
338 || isa
<ExprWithCleanups
>(parent
))
341 parent
= getParentStmt(parent
);
343 else if (isa
<UnaryOperator
>(parent
))
345 const UnaryOperator
* unaryOperator
= dyn_cast
<UnaryOperator
>(parent
);
346 int x
= unaryOperator
->getOpcode();
347 if (x
== UO_AddrOf
|| x
== UO_PostInc
|| x
== UO_PostDec
|| x
== UO_PreInc
|| x
== UO_PreDec
) {
349 bPotentiallyAssignedTo
= true;
352 // cannot be assigned to anymore
355 else if (auto callExpr
= dyn_cast
<CallExpr
>(parent
))
357 checkCallExpr(child
, callExpr
, assignValue
, bPotentiallyAssignedTo
);
360 else if (isa
<CXXConstructExpr
>(parent
))
362 const CXXConstructExpr
* consExpr
= dyn_cast
<CXXConstructExpr
>(parent
);
363 const CXXConstructorDecl
* consDecl
= consExpr
->getConstructor();
364 for (unsigned i
= 0; i
< consExpr
->getNumArgs(); ++i
) {
365 if (i
>= consDecl
->getNumParams()) // can happen in template code
367 if (consExpr
->getArg(i
) == child
) {
368 const ParmVarDecl
* parmVarDecl
= consDecl
->getParamDecl(i
);
369 QualType qt
= parmVarDecl
->getType().getDesugaredType(compiler
.getASTContext());
370 if (!qt
.isConstQualified() && qt
->isReferenceType()) {
371 bPotentiallyAssignedTo
= true;
378 else if (isa
<BinaryOperator
>(parent
))
380 const BinaryOperator
* binaryOp
= dyn_cast
<BinaryOperator
>(parent
);
381 auto op
= binaryOp
->getOpcode();
382 if ( binaryOp
->getLHS() != child
) {
383 // if the expr is on the RHS, do nothing
385 else if ( op
== BO_Assign
) {
386 assignValue
= getExprValue(binaryOp
->getRHS());
387 bPotentiallyAssignedTo
= true;
388 } else if ( op
== BO_MulAssign
|| op
== BO_DivAssign
389 || op
== BO_RemAssign
|| op
== BO_AddAssign
390 || op
== BO_SubAssign
|| op
== BO_ShlAssign
391 || op
== BO_ShrAssign
|| op
== BO_AndAssign
392 || op
== BO_XorAssign
|| op
== BO_OrAssign
)
394 bPotentiallyAssignedTo
= true;
398 else if ( isa
<CompoundStmt
>(parent
)
399 || isa
<SwitchStmt
>(parent
) || isa
<CaseStmt
>(parent
) || isa
<DefaultStmt
>(parent
)
400 || isa
<DoStmt
>(parent
) || isa
<WhileStmt
>(parent
)
401 || isa
<IfStmt
>(parent
)
402 || isa
<ForStmt
>(parent
)
403 || isa
<ReturnStmt
>(parent
)
404 || isa
<CXXNewExpr
>(parent
)
405 || isa
<CXXDeleteExpr
>(parent
)
406 || isa
<ConditionalOperator
>(parent
)
407 || isa
<CXXTypeidExpr
>(parent
)
408 || isa
<ArraySubscriptExpr
>(parent
)
409 || isa
<CXXDependentScopeMemberExpr
>(parent
)
410 || isa
<DeclStmt
>(parent
)
411 || isa
<UnaryExprOrTypeTraitExpr
>(parent
)
412 || isa
<UnresolvedMemberExpr
>(parent
)
413 || isa
<MaterializeTemporaryExpr
>(parent
) //???
414 || isa
<InitListExpr
>(parent
)
415 || isa
<CXXUnresolvedConstructExpr
>(parent
)
416 || isa
<LambdaExpr
>(parent
)
417 || isa
<PackExpansionExpr
>(parent
)
418 || isa
<CXXPseudoDestructorExpr
>(parent
)
423 else if ( isa
<ArrayInitLoopExpr
>(parent
) || isa
<AtomicExpr
>(parent
) || isa
<GCCAsmStmt
>(parent
) || isa
<VAArgExpr
>(parent
))
425 bPotentiallyAssignedTo
= true;
429 bPotentiallyAssignedTo
= true;
437 DiagnosticsEngine::Warning
,
438 "oh dear, what can the matter be?",
439 memberExpr
->getBeginLoc())
440 << memberExpr
->getSourceRange();
443 if (bPotentiallyAssignedTo
)
445 MyFieldAssignmentInfo aInfo
;
446 niceName(fieldOrVarDecl
, aInfo
);
447 aInfo
.value
= assignValue
;
448 assignedSet
.insert(aInfo
);
452 void SingleValFields::checkCallExpr(const Stmt
* child
, const CallExpr
* callExpr
, std::string
& assignValue
, bool& bPotentiallyAssignedTo
)
454 if (callExpr
->getCallee() == child
) {
457 const FunctionDecl
* functionDecl
;
458 if (auto memberCallExpr
= dyn_cast
<CXXMemberCallExpr
>(callExpr
)) {
459 functionDecl
= memberCallExpr
->getMethodDecl();
461 functionDecl
= callExpr
->getDirectCallee();
464 if (auto operatorCallExpr
= dyn_cast
<CXXOperatorCallExpr
>(callExpr
)) {
465 if (operatorCallExpr
->getArg(0) == child
) {
466 const CXXMethodDecl
* calleeMethodDecl
= dyn_cast_or_null
<CXXMethodDecl
>(operatorCallExpr
->getDirectCallee());
467 if (calleeMethodDecl
) {
468 if (operatorCallExpr
->getOperator() == OO_Equal
) {
469 assignValue
= getExprValue(operatorCallExpr
->getArg(1));
470 bPotentiallyAssignedTo
= true;
476 for (unsigned i
= 0; i
< callExpr
->getNumArgs(); ++i
) {
477 if (i
>= functionDecl
->getNumParams()) // can happen in template code
479 if (callExpr
->getArg(i
) == child
) {
480 const ParmVarDecl
* parmVarDecl
= functionDecl
->getParamDecl(i
);
481 QualType qt
= parmVarDecl
->getType().getDesugaredType(compiler
.getASTContext());
482 if (!qt
.isConstQualified() && qt
->isReferenceType()) {
484 bPotentiallyAssignedTo
= true;
491 // check for function pointers
492 const FieldDecl
* calleeFieldDecl
= dyn_cast_or_null
<FieldDecl
>(callExpr
->getCalleeDecl());
493 if (!calleeFieldDecl
) {
496 QualType qt
= calleeFieldDecl
->getType().getDesugaredType(compiler
.getASTContext());
497 if (!qt
->isPointerType()) {
500 qt
= qt
->getPointeeType().getDesugaredType(compiler
.getASTContext());
501 const FunctionProtoType
* proto
= qt
->getAs
<FunctionProtoType
>();
505 for (unsigned i
= 0; i
< callExpr
->getNumArgs(); ++i
) {
506 if (i
>= proto
->getNumParams()) // can happen in template code
508 if (callExpr
->getArg(i
) == child
) {
509 QualType qt
= proto
->getParamType(i
).getDesugaredType(compiler
.getASTContext());
510 if (!qt
.isConstQualified() && qt
->isReferenceType()) {
512 bPotentiallyAssignedTo
= true;
520 std::string
SingleValFields::getExprValue(const Expr
* arg
)
524 arg
= arg
->IgnoreParenCasts();
525 arg
= arg
->IgnoreImplicit();
526 // ignore this, it seems to trigger an infinite recursion
527 if (isa
<UnaryExprOrTypeTraitExpr
>(arg
))
529 if (arg
->isValueDependent())
531 // for stuff like: OUString foo = "xxx";
532 if (auto stringLiteral
= dyn_cast
<clang::StringLiteral
>(arg
))
534 if (stringLiteral
->getCharByteWidth() == 1)
535 return stringLiteral
->getString().str();
538 // ParenListExpr containing a CXXNullPtrLiteralExpr and has a NULL type pointer
539 if (auto parenListExpr
= dyn_cast
<ParenListExpr
>(arg
))
541 if (parenListExpr
->getNumExprs() == 1)
542 return getExprValue(parenListExpr
->getExpr(0));
545 if (auto constructExpr
= dyn_cast
<CXXConstructExpr
>(arg
))
547 if (constructExpr
->getNumArgs() >= 1
548 && isa
<clang::StringLiteral
>(constructExpr
->getArg(0)))
550 auto stringLiteral
= dyn_cast
<clang::StringLiteral
>(constructExpr
->getArg(0));
551 if (stringLiteral
->getCharByteWidth() == 1)
552 return stringLiteral
->getString().str();
556 if (arg
->getType()->isFloatingType())
559 if (arg
->EvaluateAsFloat(x1
, compiler
.getASTContext()))
562 llvm::raw_string_ostream
os(s
);
568 if (compat::EvaluateAsInt(arg
, x1
, compiler
.getASTContext()))
569 return compat::toString(x1
, 10);
570 if (isa
<CXXNullPtrLiteralExpr
>(arg
))
575 loplugin::Plugin::Registration
< SingleValFields
> X("singlevalfields", false);
579 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */