LanguageTool: don't crash if REST protocol isn't set
[LibreOffice.git] / compilerplugins / clang / singlevalfields.cxx
blob3f5b00a7c66b8771495e6c510d4db178bc705e17
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 #if CLANG_VERSION >= 110000
22 #include "clang/AST/ParentMapContext.h"
23 #endif
25 /**
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:
34 $ make check
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
39 to get it to work :-)
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
46 namespace {
48 struct MyFieldInfo
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
64 std::string value;
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)
81 std::string out;
82 for (size_t i=0; i<s.length(); ++i)
83 if (int(s[i]) >= 32)
84 out += s[i];
85 else
86 out += "\\" + std::to_string((int)s[i]);
87 return out;
90 class SingleValFields:
91 public RecursiveASTVisitor<SingleValFields>, public loplugin::Plugin
93 public:
94 explicit SingleValFields(loplugin::InstantiationData const & data):
95 Plugin(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
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(compat::getBeginLoc(s.fieldDecl)))
119 report(
120 DiagnosticsEngine::Warning,
121 "assign %0",
122 compat::getBeginLoc(s.fieldDecl))
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( ignoreLocation( canonicalDecl )
186 || isInUnoIncludeFile( compiler.getSourceManager().getSpellingLoc(canonicalDecl->getLocation())) )
187 return true;
189 MyFieldInfo aInfo;
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);
201 return true;
204 bool SingleValFields::VisitVarDecl( const VarDecl* varDecl )
206 if (isa<ParmVarDecl>(varDecl))
207 return true;
208 if (varDecl->getType().isConstQualified())
209 return true;
210 if (!(varDecl->isStaticLocal() || varDecl->isStaticDataMember() || varDecl->hasGlobalStorage()))
211 return true;
213 auto canonicalDecl = varDecl->getCanonicalDecl();
214 if (!canonicalDecl->getLocation().isValid())
215 return true;
217 if( ignoreLocation( canonicalDecl )
218 || isInUnoIncludeFile( compiler.getSourceManager().getSpellingLoc(canonicalDecl->getLocation())) )
219 return true;
221 MyFieldInfo aInfo;
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);
233 return true;
236 bool SingleValFields::VisitCXXConstructorDecl( const CXXConstructorDecl* decl )
238 if( ignoreLocation( decl ) )
239 return true;
241 // doesn't count as a write to fields because it's self->self
242 if (decl->isCopyOrMoveConstructor())
243 return true;
245 for(auto it = decl->init_begin(); it != decl->init_end(); ++it)
247 const CXXCtorInitializer* init = *it;
248 const FieldDecl* fieldDecl = init->getMember();
249 if( !fieldDecl )
250 continue;
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);
262 return true;
265 bool SingleValFields::VisitMemberExpr( const MemberExpr* memberExpr )
267 const ValueDecl* decl = memberExpr->getMemberDecl();
268 const FieldDecl* fieldDecl = dyn_cast<FieldDecl>(decl);
269 if (!fieldDecl)
270 return true;
271 if (ignoreLocation(memberExpr))
272 return true;
273 walkPotentialAssign(fieldDecl, memberExpr);
274 return true;
277 bool SingleValFields::VisitDeclRefExpr( const DeclRefExpr* declRefExpr )
279 const VarDecl* varDecl = dyn_cast_or_null<VarDecl>(declRefExpr->getDecl());
280 if (!varDecl)
281 return true;
282 if (isa<ParmVarDecl>(varDecl))
283 return true;
284 if (varDecl->getType().isConstQualified())
285 return true;
286 if (!(varDecl->isStaticLocal() || varDecl->isStaticDataMember() || varDecl->hasGlobalStorage()))
287 return true;
288 if (ignoreLocation(declRefExpr))
289 return true;
290 walkPotentialAssign(varDecl, declRefExpr);
291 return true;
294 void SingleValFields::walkPotentialAssign( const DeclaratorDecl* fieldOrVarDecl, const Stmt* memberExpr )
296 const FunctionDecl* parentFunction = getParentFunctionDecl(memberExpr);
297 if (parentFunction)
299 auto methodDecl = dyn_cast<CXXMethodDecl>(parentFunction);
300 if (methodDecl && (methodDecl->isCopyAssignmentOperator() || methodDecl->isMoveAssignmentOperator()))
301 return;
302 if (methodDecl && methodDecl->getIdentifier()
303 && (methodDecl->getName().startswith("Clone") || methodDecl->getName().startswith("clone")))
304 return;
305 auto cxxConstructorDecl = dyn_cast<CXXConstructorDecl>(parentFunction);
306 if (cxxConstructorDecl && cxxConstructorDecl->isCopyOrMoveConstructor())
307 return;
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;
314 bool bDump = 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>();
334 if (varDecl) {
335 QualType qt = varDecl->getType().getDesugaredType(compiler.getASTContext());
336 if (!qt.isConstQualified() && qt->isReferenceType()) {
337 bPotentiallyAssignedTo = true;
338 break;
343 if (!parent) {
344 return;
346 if (isa<CastExpr>(parent) || isa<MemberExpr>(parent) || isa<ParenExpr>(parent) || isa<ParenListExpr>(parent)
347 || isa<ExprWithCleanups>(parent))
349 child = 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) {
357 assignValue = "?";
358 bPotentiallyAssignedTo = true;
359 break;
361 // cannot be assigned to anymore
362 break;
364 else if (auto callExpr = dyn_cast<CallExpr>(parent))
366 checkCallExpr(child, callExpr, assignValue, bPotentiallyAssignedTo);
367 break;
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
375 break;
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;
382 break;
385 break;
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;
405 break;
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)
430 break;
432 else if ( isa<ArrayInitLoopExpr>(parent) || isa<GCCAsmStmt>(parent) || isa<VAArgExpr>(parent))
434 bPotentiallyAssignedTo = true;
435 break;
437 else {
438 bPotentiallyAssignedTo = true;
439 bDump = true;
440 break;
443 if (bDump)
445 report(
446 DiagnosticsEngine::Warning,
447 "oh dear, what can the matter be?",
448 compat::getBeginLoc(memberExpr))
449 << memberExpr->getSourceRange();
450 parent->dump();
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) {
464 return;
466 const FunctionDecl* functionDecl;
467 if (auto memberCallExpr = dyn_cast<CXXMemberCallExpr>(callExpr)) {
468 functionDecl = memberCallExpr->getMethodDecl();
469 } else {
470 functionDecl = callExpr->getDirectCallee();
472 if (functionDecl) {
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;
480 return;
485 for (unsigned i = 0; i < callExpr->getNumArgs(); ++i) {
486 if (i >= functionDecl->getNumParams()) // can happen in template code
487 break;
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()) {
492 assignValue = "?";
493 bPotentiallyAssignedTo = true;
495 break;
498 return;
500 // check for function pointers
501 const FieldDecl* calleeFieldDecl = dyn_cast_or_null<FieldDecl>(callExpr->getCalleeDecl());
502 if (!calleeFieldDecl) {
503 return;
505 QualType qt = calleeFieldDecl->getType().getDesugaredType(compiler.getASTContext());
506 if (!qt->isPointerType()) {
507 return;
509 qt = qt->getPointeeType().getDesugaredType(compiler.getASTContext());
510 const FunctionProtoType* proto = qt->getAs<FunctionProtoType>();
511 if (!proto) {
512 return;
514 for (unsigned i = 0; i < callExpr->getNumArgs(); ++i) {
515 if (i >= proto->getNumParams()) // can happen in template code
516 break;
517 if (callExpr->getArg(i) == child) {
518 QualType qt = proto->getParamType(i).getDesugaredType(compiler.getASTContext());
519 if (!qt.isConstQualified() && qt->isReferenceType()) {
520 assignValue = "?";
521 bPotentiallyAssignedTo = true;
523 break;
529 std::string SingleValFields::getExprValue(const Expr* arg)
531 if (!arg)
532 return "?";
533 arg = arg->IgnoreParenCasts();
534 arg = arg->IgnoreImplicit();
535 // ignore this, it seems to trigger an infinite recursion
536 if (isa<UnaryExprOrTypeTraitExpr>(arg))
537 return "?";
538 if (arg->isValueDependent())
539 return "?";
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();
545 return "?";
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));
552 return "?";
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();
562 return "?";
565 if (arg->getType()->isFloatingType())
567 APFloat x1(0.0f);
568 if (arg->EvaluateAsFloat(x1, compiler.getASTContext()))
570 std::string s;
571 llvm::raw_string_ostream os(s);
572 x1.print(os);
573 return os.str();
576 APSInt x1;
577 if (compat::EvaluateAsInt(arg, x1, compiler.getASTContext()))
578 return compat::toString(x1, 10);
579 if (isa<CXXNullPtrLiteralExpr>(arg))
580 return "0";
581 return "?";
584 loplugin::Plugin::Registration< SingleValFields > X("singlevalfields", false);
588 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */