Version 7.6.3.2-android, tag libreoffice-7.6.3.2-android
[LibreOffice.git] / compilerplugins / clang / singlevalfields.cxx
blob8964d4fa3fde3d46fda848440b90d8978fdb1f14
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>
16 #include "config_clang.h"
18 #include "compat.hxx"
19 #include "plugin.hxx"
21 #include "clang/AST/ParentMapContext.h"
23 /**
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:
32 $ make check
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
37 to get it to work :-)
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
44 namespace {
46 struct MyFieldInfo
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
62 std::string value;
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)
79 std::string out;
80 for (size_t i=0; i<s.length(); ++i)
81 if (int(s[i]) >= 32)
82 out += s[i];
83 else
84 out += "\\" + std::to_string((int)s[i]);
85 return out;
88 class SingleValFields:
89 public RecursiveASTVisitor<SingleValFields>, public loplugin::Plugin
91 public:
92 explicit SingleValFields(loplugin::InstantiationData const & data):
93 Plugin(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
105 std::string output;
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);
112 myfile << output;
113 myfile.close();
115 else
117 for (const MyFieldAssignmentInfo & s : assignedSet)
118 if (s.fieldDecl && compiler.getSourceManager().isInMainFile(s.fieldDecl->getBeginLoc()))
119 report(
120 DiagnosticsEngine::Warning,
121 "assign %0",
122 s.fieldDecl->getBeginLoc())
123 << s.value;
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* );
137 private:
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;
150 if (fieldDecl)
151 aInfo.parentClass = fieldDecl->getParent()->getQualifiedNameAsString();
152 else
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?
166 else
168 std::cout << "what is this? " << varDecl->getDeclContext()->getDeclKindName() << std::endl;
169 exit(1);
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())) )
186 return true;
188 MyFieldInfo aInfo;
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);
200 return true;
203 bool SingleValFields::VisitVarDecl( const VarDecl* varDecl )
205 if (isa<ParmVarDecl>(varDecl))
206 return true;
207 if (varDecl->getType().isConstQualified())
208 return true;
209 if (!(varDecl->isStaticLocal() || varDecl->isStaticDataMember() || varDecl->hasGlobalStorage()))
210 return true;
212 auto canonicalDecl = varDecl->getCanonicalDecl();
213 if (!canonicalDecl->getLocation().isValid())
214 return true;
216 if( isInUnoIncludeFile( compiler.getSourceManager().getSpellingLoc(canonicalDecl->getLocation())) )
217 return true;
219 MyFieldInfo aInfo;
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);
231 return true;
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())
238 return true;
240 for(auto it = decl->init_begin(); it != decl->init_end(); ++it)
242 const CXXCtorInitializer* init = *it;
243 const FieldDecl* fieldDecl = init->getMember();
244 if( !fieldDecl )
245 continue;
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);
257 return true;
260 bool SingleValFields::VisitMemberExpr( const MemberExpr* memberExpr )
262 const ValueDecl* decl = memberExpr->getMemberDecl();
263 const FieldDecl* fieldDecl = dyn_cast<FieldDecl>(decl);
264 if (!fieldDecl)
265 return true;
266 walkPotentialAssign(fieldDecl, memberExpr);
267 return true;
270 bool SingleValFields::VisitDeclRefExpr( const DeclRefExpr* declRefExpr )
272 const VarDecl* varDecl = dyn_cast_or_null<VarDecl>(declRefExpr->getDecl());
273 if (!varDecl)
274 return true;
275 if (isa<ParmVarDecl>(varDecl))
276 return true;
277 if (varDecl->getType().isConstQualified())
278 return true;
279 if (!(varDecl->isStaticLocal() || varDecl->isStaticDataMember() || varDecl->hasGlobalStorage()))
280 return true;
281 walkPotentialAssign(varDecl, declRefExpr);
282 return true;
285 void SingleValFields::walkPotentialAssign( const DeclaratorDecl* fieldOrVarDecl, const Stmt* memberExpr )
287 const FunctionDecl* parentFunction = getParentFunctionDecl(memberExpr);
288 if (parentFunction)
290 auto methodDecl = dyn_cast<CXXMethodDecl>(parentFunction);
291 if (methodDecl && (methodDecl->isCopyAssignmentOperator() || methodDecl->isMoveAssignmentOperator()))
292 return;
293 if (methodDecl && methodDecl->getIdentifier()
294 && (methodDecl->getName().startswith("Clone") || methodDecl->getName().startswith("clone")))
295 return;
296 auto cxxConstructorDecl = dyn_cast<CXXConstructorDecl>(parentFunction);
297 if (cxxConstructorDecl && cxxConstructorDecl->isCopyOrMoveConstructor())
298 return;
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;
305 bool bDump = 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>();
325 if (varDecl) {
326 QualType qt = varDecl->getType().getDesugaredType(compiler.getASTContext());
327 if (!qt.isConstQualified() && qt->isReferenceType()) {
328 bPotentiallyAssignedTo = true;
329 break;
334 if (!parent) {
335 return;
337 if (isa<CastExpr>(parent) || isa<MemberExpr>(parent) || isa<ParenExpr>(parent) || isa<ParenListExpr>(parent)
338 || isa<ExprWithCleanups>(parent))
340 child = 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) {
348 assignValue = "?";
349 bPotentiallyAssignedTo = true;
350 break;
352 // cannot be assigned to anymore
353 break;
355 else if (auto callExpr = dyn_cast<CallExpr>(parent))
357 checkCallExpr(child, callExpr, assignValue, bPotentiallyAssignedTo);
358 break;
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
366 break;
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;
373 break;
376 break;
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;
396 break;
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)
421 break;
423 else if ( isa<ArrayInitLoopExpr>(parent) || isa<AtomicExpr>(parent) || isa<GCCAsmStmt>(parent) || isa<VAArgExpr>(parent))
425 bPotentiallyAssignedTo = true;
426 break;
428 else {
429 bPotentiallyAssignedTo = true;
430 bDump = true;
431 break;
434 if (bDump)
436 report(
437 DiagnosticsEngine::Warning,
438 "oh dear, what can the matter be?",
439 memberExpr->getBeginLoc())
440 << memberExpr->getSourceRange();
441 parent->dump();
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) {
455 return;
457 const FunctionDecl* functionDecl;
458 if (auto memberCallExpr = dyn_cast<CXXMemberCallExpr>(callExpr)) {
459 functionDecl = memberCallExpr->getMethodDecl();
460 } else {
461 functionDecl = callExpr->getDirectCallee();
463 if (functionDecl) {
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;
471 return;
476 for (unsigned i = 0; i < callExpr->getNumArgs(); ++i) {
477 if (i >= functionDecl->getNumParams()) // can happen in template code
478 break;
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()) {
483 assignValue = "?";
484 bPotentiallyAssignedTo = true;
486 break;
489 return;
491 // check for function pointers
492 const FieldDecl* calleeFieldDecl = dyn_cast_or_null<FieldDecl>(callExpr->getCalleeDecl());
493 if (!calleeFieldDecl) {
494 return;
496 QualType qt = calleeFieldDecl->getType().getDesugaredType(compiler.getASTContext());
497 if (!qt->isPointerType()) {
498 return;
500 qt = qt->getPointeeType().getDesugaredType(compiler.getASTContext());
501 const FunctionProtoType* proto = qt->getAs<FunctionProtoType>();
502 if (!proto) {
503 return;
505 for (unsigned i = 0; i < callExpr->getNumArgs(); ++i) {
506 if (i >= proto->getNumParams()) // can happen in template code
507 break;
508 if (callExpr->getArg(i) == child) {
509 QualType qt = proto->getParamType(i).getDesugaredType(compiler.getASTContext());
510 if (!qt.isConstQualified() && qt->isReferenceType()) {
511 assignValue = "?";
512 bPotentiallyAssignedTo = true;
514 break;
520 std::string SingleValFields::getExprValue(const Expr* arg)
522 if (!arg)
523 return "?";
524 arg = arg->IgnoreParenCasts();
525 arg = arg->IgnoreImplicit();
526 // ignore this, it seems to trigger an infinite recursion
527 if (isa<UnaryExprOrTypeTraitExpr>(arg))
528 return "?";
529 if (arg->isValueDependent())
530 return "?";
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();
536 return "?";
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));
543 return "?";
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();
553 return "?";
556 if (arg->getType()->isFloatingType())
558 APFloat x1(0.0f);
559 if (arg->EvaluateAsFloat(x1, compiler.getASTContext()))
561 std::string s;
562 llvm::raw_string_ostream os(s);
563 x1.print(os);
564 return os.str();
567 APSInt x1;
568 if (compat::EvaluateAsInt(arg, x1, compiler.getASTContext()))
569 return compat::toString(x1, 10);
570 if (isa<CXXNullPtrLiteralExpr>(arg))
571 return "0";
572 return "?";
575 loplugin::Plugin::Registration< SingleValFields > X("singlevalfields", false);
579 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */