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 #if CLANG_VERSION >= 110000
22 #include "clang/AST/ParentMapContext.h"
26 Look for fields that are only ever assigned a single constant value.
28 We dmp a list of values assigned to fields, and a list of field definitions.
29 Then we will post-process the 2 lists and find the set of interesting fields.
31 Be warned that it produces around 5G of log file.
33 The process goes something like this:
35 $ make FORCE_COMPILE=all COMPILER_PLUGIN_TOOL='singlevalfields' check
36 $ ./compilerplugins/clang/singlevalfields.py
38 Note that the actual process may involve a fair amount of undoing, hand editing, and general messing around
41 @TODO we don't spot fields that have been zero-initialised via calloc or rtl_allocateZeroMemory or memset
42 @TODO calls to lambdas where a reference to the field is taken
50 FieldDecl
const * fieldDecl
;
51 std::string parentClass
;
52 std::string fieldName
;
53 std::string fieldType
;
54 std::string sourceLocation
;
56 bool operator < (const MyFieldInfo
&lhs
, const MyFieldInfo
&rhs
)
58 return std::tie(lhs
.parentClass
, lhs
.fieldName
)
59 < std::tie(rhs
.parentClass
, rhs
.fieldName
);
62 struct MyFieldAssignmentInfo
: public MyFieldInfo
67 bool operator < (const MyFieldAssignmentInfo
&lhs
, const MyFieldAssignmentInfo
&rhs
)
69 return std::tie(lhs
.parentClass
, lhs
.fieldName
, lhs
.value
)
70 < std::tie(rhs
.parentClass
, rhs
.fieldName
, rhs
.value
);
74 // try to limit the voluminous output a little
75 static std::set
<MyFieldAssignmentInfo
> assignedSet
;
76 static std::set
<MyFieldInfo
> definitionSet
;
78 /** escape the value string to make it easier to parse the output file in python */
79 std::string
escape(std::string s
)
82 for (size_t i
=0; i
<s
.length(); ++i
)
86 out
+= "\\" + std::to_string((int)s
[i
]);
90 class SingleValFields
:
91 public RecursiveASTVisitor
<SingleValFields
>, public loplugin::Plugin
94 explicit SingleValFields(loplugin::InstantiationData
const & data
):
97 virtual void run() override
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(compat::getBeginLoc(s
.fieldDecl
)))
120 DiagnosticsEngine::Warning
,
122 compat::getBeginLoc(s
.fieldDecl
))
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( ignoreLocation( canonicalDecl
)
186 || isInUnoIncludeFile( compiler
.getSourceManager().getSpellingLoc(canonicalDecl
->getLocation())) )
190 niceName(canonicalDecl
, aInfo
);
191 definitionSet
.insert(aInfo
);
193 if (fieldDecl
->getInClassInitializer())
195 MyFieldAssignmentInfo aInfo
;
196 niceName(canonicalDecl
, aInfo
);
197 aInfo
.value
= getExprValue(fieldDecl
->getInClassInitializer());
198 assignedSet
.insert(aInfo
);
204 bool SingleValFields::VisitVarDecl( const VarDecl
* varDecl
)
206 if (isa
<ParmVarDecl
>(varDecl
))
208 if (varDecl
->getType().isConstQualified())
210 if (!(varDecl
->isStaticLocal() || varDecl
->isStaticDataMember() || varDecl
->hasGlobalStorage()))
213 auto canonicalDecl
= varDecl
->getCanonicalDecl();
214 if (!canonicalDecl
->getLocation().isValid())
217 if( ignoreLocation( canonicalDecl
)
218 || isInUnoIncludeFile( compiler
.getSourceManager().getSpellingLoc(canonicalDecl
->getLocation())) )
222 niceName(canonicalDecl
, aInfo
);
223 definitionSet
.insert(aInfo
);
225 if (varDecl
->getInit())
227 MyFieldAssignmentInfo aInfo
;
228 niceName(canonicalDecl
, aInfo
);
229 aInfo
.value
= getExprValue(varDecl
->getInit());
230 assignedSet
.insert(aInfo
);
236 bool SingleValFields::VisitCXXConstructorDecl( const CXXConstructorDecl
* decl
)
238 if( ignoreLocation( decl
) )
241 // doesn't count as a write to fields because it's self->self
242 if (decl
->isCopyOrMoveConstructor())
245 for(auto it
= decl
->init_begin(); it
!= decl
->init_end(); ++it
)
247 const CXXCtorInitializer
* init
= *it
;
248 const FieldDecl
* fieldDecl
= init
->getMember();
251 MyFieldAssignmentInfo aInfo
;
252 niceName(fieldDecl
, aInfo
);
253 const Expr
* expr
= init
->getInit();
254 // unwrap any single-arg constructors, this helps to find smart pointers
255 // that are only assigned nullptr
256 if (auto cxxConstructExpr
= dyn_cast
<CXXConstructExpr
>(expr
))
257 if (cxxConstructExpr
->getNumArgs() == 1)
258 expr
= cxxConstructExpr
->getArg(0);
259 aInfo
.value
= getExprValue(expr
);
260 assignedSet
.insert(aInfo
);
265 bool SingleValFields::VisitMemberExpr( const MemberExpr
* memberExpr
)
267 const ValueDecl
* decl
= memberExpr
->getMemberDecl();
268 const FieldDecl
* fieldDecl
= dyn_cast
<FieldDecl
>(decl
);
271 if (ignoreLocation(memberExpr
))
273 walkPotentialAssign(fieldDecl
, memberExpr
);
277 bool SingleValFields::VisitDeclRefExpr( const DeclRefExpr
* declRefExpr
)
279 const VarDecl
* varDecl
= dyn_cast_or_null
<VarDecl
>(declRefExpr
->getDecl());
282 if (isa
<ParmVarDecl
>(varDecl
))
284 if (varDecl
->getType().isConstQualified())
286 if (!(varDecl
->isStaticLocal() || varDecl
->isStaticDataMember() || varDecl
->hasGlobalStorage()))
288 if (ignoreLocation(declRefExpr
))
290 walkPotentialAssign(varDecl
, declRefExpr
);
294 void SingleValFields::walkPotentialAssign( const DeclaratorDecl
* fieldOrVarDecl
, const Stmt
* memberExpr
)
296 const FunctionDecl
* parentFunction
= getParentFunctionDecl(memberExpr
);
299 auto methodDecl
= dyn_cast
<CXXMethodDecl
>(parentFunction
);
300 if (methodDecl
&& (methodDecl
->isCopyAssignmentOperator() || methodDecl
->isMoveAssignmentOperator()))
302 if (methodDecl
&& methodDecl
->getIdentifier()
303 && (methodDecl
->getName().startswith("Clone") || methodDecl
->getName().startswith("clone")))
305 auto cxxConstructorDecl
= dyn_cast
<CXXConstructorDecl
>(parentFunction
);
306 if (cxxConstructorDecl
&& cxxConstructorDecl
->isCopyOrMoveConstructor())
310 // walk up the tree until we find something interesting
311 const Stmt
* child
= memberExpr
;
312 const Stmt
* parent
= getParentStmt(memberExpr
);
313 bool bPotentiallyAssignedTo
= false;
315 std::string assignValue
= "?";
317 // check for field being returned by non-const ref eg. Foo& getFoo() { return f; }
318 if (parentFunction
&& parent
&& isa
<ReturnStmt
>(parent
)) {
319 const Stmt
* parent2
= getParentStmt(parent
);
320 if (parent2
&& isa
<CompoundStmt
>(parent2
)) {
321 QualType qt
= parentFunction
->getReturnType().getDesugaredType(compiler
.getASTContext());
322 if (!qt
.isConstQualified() && qt
->isReferenceType()) {
323 bPotentiallyAssignedTo
= true;
328 while (!bPotentiallyAssignedTo
) {
329 // check for field being accessed by a reference variable e.g. Foo& f = m.foo;
330 auto parentsList
= compiler
.getASTContext().getParents(*child
);
331 auto it
= parentsList
.begin();
332 if (it
!= parentsList
.end()) {
333 const VarDecl
*varDecl
= it
->get
<VarDecl
>();
335 QualType qt
= varDecl
->getType().getDesugaredType(compiler
.getASTContext());
336 if (!qt
.isConstQualified() && qt
->isReferenceType()) {
337 bPotentiallyAssignedTo
= true;
346 if (isa
<CastExpr
>(parent
) || isa
<MemberExpr
>(parent
) || isa
<ParenExpr
>(parent
) || isa
<ParenListExpr
>(parent
)
347 || isa
<ExprWithCleanups
>(parent
))
350 parent
= getParentStmt(parent
);
352 else if (isa
<UnaryOperator
>(parent
))
354 const UnaryOperator
* unaryOperator
= dyn_cast
<UnaryOperator
>(parent
);
355 int x
= unaryOperator
->getOpcode();
356 if (x
== UO_AddrOf
|| x
== UO_PostInc
|| x
== UO_PostDec
|| x
== UO_PreInc
|| x
== UO_PreDec
) {
358 bPotentiallyAssignedTo
= true;
361 // cannot be assigned to anymore
364 else if (auto callExpr
= dyn_cast
<CallExpr
>(parent
))
366 checkCallExpr(child
, callExpr
, assignValue
, bPotentiallyAssignedTo
);
369 else if (isa
<CXXConstructExpr
>(parent
))
371 const CXXConstructExpr
* consExpr
= dyn_cast
<CXXConstructExpr
>(parent
);
372 const CXXConstructorDecl
* consDecl
= consExpr
->getConstructor();
373 for (unsigned i
= 0; i
< consExpr
->getNumArgs(); ++i
) {
374 if (i
>= consDecl
->getNumParams()) // can happen in template code
376 if (consExpr
->getArg(i
) == child
) {
377 const ParmVarDecl
* parmVarDecl
= consDecl
->getParamDecl(i
);
378 QualType qt
= parmVarDecl
->getType().getDesugaredType(compiler
.getASTContext());
379 if (!qt
.isConstQualified() && qt
->isReferenceType()) {
380 bPotentiallyAssignedTo
= true;
387 else if (isa
<BinaryOperator
>(parent
))
389 const BinaryOperator
* binaryOp
= dyn_cast
<BinaryOperator
>(parent
);
390 auto op
= binaryOp
->getOpcode();
391 if ( binaryOp
->getLHS() != child
) {
392 // if the expr is on the RHS, do nothing
394 else if ( op
== BO_Assign
) {
395 assignValue
= getExprValue(binaryOp
->getRHS());
396 bPotentiallyAssignedTo
= true;
397 } else if ( op
== BO_MulAssign
|| op
== BO_DivAssign
398 || op
== BO_RemAssign
|| op
== BO_AddAssign
399 || op
== BO_SubAssign
|| op
== BO_ShlAssign
400 || op
== BO_ShrAssign
|| op
== BO_AndAssign
401 || op
== BO_XorAssign
|| op
== BO_OrAssign
)
403 bPotentiallyAssignedTo
= true;
407 else if ( isa
<CompoundStmt
>(parent
)
408 || isa
<SwitchStmt
>(parent
) || isa
<CaseStmt
>(parent
) || isa
<DefaultStmt
>(parent
)
409 || isa
<DoStmt
>(parent
) || isa
<WhileStmt
>(parent
)
410 || isa
<IfStmt
>(parent
)
411 || isa
<ForStmt
>(parent
)
412 || isa
<ReturnStmt
>(parent
)
413 || isa
<CXXNewExpr
>(parent
)
414 || isa
<CXXDeleteExpr
>(parent
)
415 || isa
<ConditionalOperator
>(parent
)
416 || isa
<CXXTypeidExpr
>(parent
)
417 || isa
<ArraySubscriptExpr
>(parent
)
418 || isa
<CXXDependentScopeMemberExpr
>(parent
)
419 || isa
<DeclStmt
>(parent
)
420 || isa
<UnaryExprOrTypeTraitExpr
>(parent
)
421 || isa
<UnresolvedMemberExpr
>(parent
)
422 || isa
<MaterializeTemporaryExpr
>(parent
) //???
423 || isa
<InitListExpr
>(parent
)
424 || isa
<CXXUnresolvedConstructExpr
>(parent
)
425 || isa
<LambdaExpr
>(parent
)
426 || isa
<PackExpansionExpr
>(parent
)
427 || isa
<CXXPseudoDestructorExpr
>(parent
)
432 else if ( isa
<ArrayInitLoopExpr
>(parent
) || isa
<GCCAsmStmt
>(parent
) || isa
<VAArgExpr
>(parent
))
434 bPotentiallyAssignedTo
= true;
438 bPotentiallyAssignedTo
= true;
446 DiagnosticsEngine::Warning
,
447 "oh dear, what can the matter be?",
448 compat::getBeginLoc(memberExpr
))
449 << memberExpr
->getSourceRange();
452 if (bPotentiallyAssignedTo
)
454 MyFieldAssignmentInfo aInfo
;
455 niceName(fieldOrVarDecl
, aInfo
);
456 aInfo
.value
= assignValue
;
457 assignedSet
.insert(aInfo
);
461 void SingleValFields::checkCallExpr(const Stmt
* child
, const CallExpr
* callExpr
, std::string
& assignValue
, bool& bPotentiallyAssignedTo
)
463 if (callExpr
->getCallee() == child
) {
466 const FunctionDecl
* functionDecl
;
467 if (auto memberCallExpr
= dyn_cast
<CXXMemberCallExpr
>(callExpr
)) {
468 functionDecl
= memberCallExpr
->getMethodDecl();
470 functionDecl
= callExpr
->getDirectCallee();
473 if (auto operatorCallExpr
= dyn_cast
<CXXOperatorCallExpr
>(callExpr
)) {
474 if (operatorCallExpr
->getArg(0) == child
) {
475 const CXXMethodDecl
* calleeMethodDecl
= dyn_cast_or_null
<CXXMethodDecl
>(operatorCallExpr
->getDirectCallee());
476 if (calleeMethodDecl
) {
477 if (operatorCallExpr
->getOperator() == OO_Equal
) {
478 assignValue
= getExprValue(operatorCallExpr
->getArg(1));
479 bPotentiallyAssignedTo
= true;
485 for (unsigned i
= 0; i
< callExpr
->getNumArgs(); ++i
) {
486 if (i
>= functionDecl
->getNumParams()) // can happen in template code
488 if (callExpr
->getArg(i
) == child
) {
489 const ParmVarDecl
* parmVarDecl
= functionDecl
->getParamDecl(i
);
490 QualType qt
= parmVarDecl
->getType().getDesugaredType(compiler
.getASTContext());
491 if (!qt
.isConstQualified() && qt
->isReferenceType()) {
493 bPotentiallyAssignedTo
= true;
500 // check for function pointers
501 const FieldDecl
* calleeFieldDecl
= dyn_cast_or_null
<FieldDecl
>(callExpr
->getCalleeDecl());
502 if (!calleeFieldDecl
) {
505 QualType qt
= calleeFieldDecl
->getType().getDesugaredType(compiler
.getASTContext());
506 if (!qt
->isPointerType()) {
509 qt
= qt
->getPointeeType().getDesugaredType(compiler
.getASTContext());
510 const FunctionProtoType
* proto
= qt
->getAs
<FunctionProtoType
>();
514 for (unsigned i
= 0; i
< callExpr
->getNumArgs(); ++i
) {
515 if (i
>= proto
->getNumParams()) // can happen in template code
517 if (callExpr
->getArg(i
) == child
) {
518 QualType qt
= proto
->getParamType(i
).getDesugaredType(compiler
.getASTContext());
519 if (!qt
.isConstQualified() && qt
->isReferenceType()) {
521 bPotentiallyAssignedTo
= true;
529 std::string
SingleValFields::getExprValue(const Expr
* arg
)
533 arg
= arg
->IgnoreParenCasts();
534 arg
= arg
->IgnoreImplicit();
535 // ignore this, it seems to trigger an infinite recursion
536 if (isa
<UnaryExprOrTypeTraitExpr
>(arg
))
538 if (arg
->isValueDependent())
540 // for stuff like: OUString foo = "xxx";
541 if (auto stringLiteral
= dyn_cast
<clang::StringLiteral
>(arg
))
543 if (stringLiteral
->getCharByteWidth() == 1)
544 return stringLiteral
->getString().str();
547 // ParenListExpr containing a CXXNullPtrLiteralExpr and has a NULL type pointer
548 if (auto parenListExpr
= dyn_cast
<ParenListExpr
>(arg
))
550 if (parenListExpr
->getNumExprs() == 1)
551 return getExprValue(parenListExpr
->getExpr(0));
554 if (auto constructExpr
= dyn_cast
<CXXConstructExpr
>(arg
))
556 if (constructExpr
->getNumArgs() >= 1
557 && isa
<clang::StringLiteral
>(constructExpr
->getArg(0)))
559 auto stringLiteral
= dyn_cast
<clang::StringLiteral
>(constructExpr
->getArg(0));
560 if (stringLiteral
->getCharByteWidth() == 1)
561 return stringLiteral
->getString().str();
565 if (arg
->getType()->isFloatingType())
568 if (arg
->EvaluateAsFloat(x1
, compiler
.getASTContext()))
571 llvm::raw_string_ostream
os(s
);
577 if (compat::EvaluateAsInt(arg
, x1
, compiler
.getASTContext()))
578 return compat::toString(x1
, 10);
579 if (isa
<CXXNullPtrLiteralExpr
>(arg
))
584 loplugin::Plugin::Registration
< SingleValFields
> X("singlevalfields", false);
588 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */