Move setting of LD_LIBRARY_PATH closer to invocation of cppunittester
[LibreOffice.git] / compilerplugins / clang / store / constvars.cxx
blob2b06f54ea343f6b5c6a0d7f944bad241bc39c2d1
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 #if !defined _WIN32 //TODO, #include <sys/file.h>
12 #include <cassert>
13 #include <string>
14 #include <iostream>
15 #include <fstream>
16 #include <unordered_set>
17 #include <vector>
18 #include <algorithm>
19 #include <sys/file.h>
20 #include <unistd.h>
22 #include "plugin.hxx"
23 #include "check.hxx"
25 #include "clang/AST/ParentMapContext.h"
27 /**
28 Look for static vars that are only assigned to once, and never written to, they can be const.
31 namespace
33 /**
34 * Wrap the different kinds of callable and callee objects in the clang AST so I can define methods that handle everything.
36 class CallerWrapper
38 const CallExpr* m_callExpr;
39 const CXXConstructExpr* m_cxxConstructExpr;
41 public:
42 CallerWrapper(const CallExpr* callExpr)
43 : m_callExpr(callExpr)
44 , m_cxxConstructExpr(nullptr)
47 CallerWrapper(const CXXConstructExpr* cxxConstructExpr)
48 : m_callExpr(nullptr)
49 , m_cxxConstructExpr(cxxConstructExpr)
52 unsigned getNumArgs() const
54 return m_callExpr ? m_callExpr->getNumArgs() : m_cxxConstructExpr->getNumArgs();
56 const Expr* getArg(unsigned i) const
58 return m_callExpr ? m_callExpr->getArg(i) : m_cxxConstructExpr->getArg(i);
61 class CalleeWrapper
63 const FunctionDecl* m_calleeFunctionDecl = nullptr;
64 const CXXConstructorDecl* m_cxxConstructorDecl = nullptr;
65 const FunctionProtoType* m_functionPrototype = nullptr;
67 public:
68 explicit CalleeWrapper(const FunctionDecl* calleeFunctionDecl)
69 : m_calleeFunctionDecl(calleeFunctionDecl)
72 explicit CalleeWrapper(const CXXConstructExpr* cxxConstructExpr)
73 : m_cxxConstructorDecl(cxxConstructExpr->getConstructor())
76 explicit CalleeWrapper(const FunctionProtoType* functionPrototype)
77 : m_functionPrototype(functionPrototype)
80 unsigned getNumParams() const
82 if (m_calleeFunctionDecl)
83 return m_calleeFunctionDecl->getNumParams();
84 else if (m_cxxConstructorDecl)
85 return m_cxxConstructorDecl->getNumParams();
86 else if (m_functionPrototype->param_type_begin() == m_functionPrototype->param_type_end())
87 // FunctionProtoType will assert if we call getParamTypes() and it has no params
88 return 0;
89 else
90 return m_functionPrototype->getParamTypes().size();
92 const QualType getParamType(unsigned i) const
94 if (m_calleeFunctionDecl)
95 return m_calleeFunctionDecl->getParamDecl(i)->getType();
96 else if (m_cxxConstructorDecl)
97 return m_cxxConstructorDecl->getParamDecl(i)->getType();
98 else
99 return m_functionPrototype->getParamTypes()[i];
101 std::string getNameAsString() const
103 if (m_calleeFunctionDecl)
104 return m_calleeFunctionDecl->getNameAsString();
105 else if (m_cxxConstructorDecl)
106 return m_cxxConstructorDecl->getNameAsString();
107 else
108 return "";
110 CXXMethodDecl const* getAsCXXMethodDecl() const
112 if (m_calleeFunctionDecl)
113 return dyn_cast<CXXMethodDecl>(m_calleeFunctionDecl);
114 return nullptr;
118 class ConstVars : public RecursiveASTVisitor<ConstVars>, public loplugin::Plugin
120 public:
121 explicit ConstVars(loplugin::InstantiationData const& data)
122 : Plugin(data)
126 virtual void run() override;
128 bool shouldVisitTemplateInstantiations() const { return true; }
129 bool shouldVisitImplicitCode() const { return true; }
131 bool VisitVarDecl(const VarDecl*);
132 bool VisitCXXForRangeStmt(const CXXForRangeStmt*);
133 bool VisitDeclRefExpr(const DeclRefExpr*);
134 bool TraverseCXXConstructorDecl(CXXConstructorDecl*);
135 bool TraverseCXXMethodDecl(CXXMethodDecl*);
136 bool TraverseFunctionDecl(FunctionDecl*);
137 bool TraverseIfStmt(IfStmt*);
139 private:
140 void check(const VarDecl* varDecl, const Expr* memberExpr);
141 bool isSomeKindOfZero(const Expr* arg);
142 bool IsPassedByNonConst(const VarDecl* varDecl, const Stmt* child, CallerWrapper callExpr,
143 CalleeWrapper calleeFunctionDecl);
144 llvm::Optional<CalleeWrapper> getCallee(CallExpr const*);
146 RecordDecl* insideMoveOrCopyDeclParent = nullptr;
147 // For reasons I do not understand, parentFunctionDecl() is not reliable, so
148 // we store the parent function on the way down the AST.
149 FunctionDecl* insideFunctionDecl = nullptr;
150 std::vector<VarDecl const*> insideConditionalCheckOfVarSet;
152 std::set<VarDecl const*> cannotBeConstSet;
153 std::set<VarDecl const*> definitionSet;
156 void ConstVars::run()
158 // clang::Expr::isCXX11ConstantExpr only works for C++
159 if (!compiler.getLangOpts().CPlusPlus)
160 return;
162 TraverseDecl(compiler.getASTContext().getTranslationUnitDecl());
164 SourceManager& SM = compiler.getSourceManager();
165 for (VarDecl const* v : definitionSet)
167 if (cannotBeConstSet.find(v) != cannotBeConstSet.end())
168 continue;
169 llvm::StringRef sourceString(SM.getCharacterData(v->getSourceRange().getEnd()), 50);
170 // Implement a marker that disables this plugins warning at a specific site
171 if (sourceString.contains("loplugin:constvars:ignore"))
172 continue;
173 report(DiagnosticsEngine::Warning, "var can be const", v->getBeginLoc());
177 bool ConstVars::VisitVarDecl(const VarDecl* varDecl)
179 varDecl = varDecl->getCanonicalDecl();
180 if (varDecl->getLocation().isValid() && ignoreLocation(varDecl))
181 return true;
182 if (!varDecl->hasGlobalStorage())
183 return true;
184 if (isa<ParmVarDecl>(varDecl))
185 return true;
186 if (varDecl->getLinkageAndVisibility().getLinkage() == ExternalLinkage)
187 return true;
188 if (varDecl->getType().isConstQualified())
189 return true;
190 if (isa<ConstantArrayType>(varDecl->getType()))
191 return true;
192 if (loplugin::TypeCheck(varDecl->getType()).Pointer().Const())
193 return true;
194 // ignore stuff that forms part of the stable URE interface
195 if (isInUnoIncludeFile(compiler.getSourceManager().getSpellingLoc(varDecl->getLocation())))
196 return true;
198 if (!varDecl->getInit())
199 return true;
200 if (varDecl->getInit()->isInstantiationDependent())
201 return true;
202 if (!varDecl->getInit()->isCXX11ConstantExpr(compiler.getASTContext()))
203 return true;
205 definitionSet.insert(varDecl);
206 return true;
209 bool ConstVars::VisitCXXForRangeStmt(const CXXForRangeStmt* forStmt)
211 if (forStmt->getBeginLoc().isValid() && ignoreLocation(forStmt))
212 return true;
213 const VarDecl* varDecl = forStmt->getLoopVariable();
214 if (!varDecl)
215 return true;
216 // we don't handle structured assignment properly
217 if (isa<DecompositionDecl>(varDecl))
218 return true;
219 auto tc = loplugin::TypeCheck(varDecl->getType());
220 if (!tc.LvalueReference())
221 return true;
222 if (tc.LvalueReference().Const())
223 return true;
225 definitionSet.insert(varDecl);
226 return true;
229 bool ConstVars::TraverseCXXConstructorDecl(CXXConstructorDecl* cxxConstructorDecl)
231 auto copy = insideMoveOrCopyDeclParent;
232 if (!ignoreLocation(cxxConstructorDecl) && cxxConstructorDecl->isThisDeclarationADefinition())
234 if (cxxConstructorDecl->isCopyOrMoveConstructor())
235 insideMoveOrCopyDeclParent = cxxConstructorDecl->getParent();
237 bool ret = RecursiveASTVisitor::TraverseCXXConstructorDecl(cxxConstructorDecl);
238 insideMoveOrCopyDeclParent = copy;
239 return ret;
242 bool ConstVars::TraverseCXXMethodDecl(CXXMethodDecl* cxxMethodDecl)
244 auto copy1 = insideMoveOrCopyDeclParent;
245 auto copy2 = insideFunctionDecl;
246 if (!ignoreLocation(cxxMethodDecl) && cxxMethodDecl->isThisDeclarationADefinition())
248 if (cxxMethodDecl->isCopyAssignmentOperator() || cxxMethodDecl->isMoveAssignmentOperator())
249 insideMoveOrCopyDeclParent = cxxMethodDecl->getParent();
251 insideFunctionDecl = cxxMethodDecl;
252 bool ret = RecursiveASTVisitor::TraverseCXXMethodDecl(cxxMethodDecl);
253 insideMoveOrCopyDeclParent = copy1;
254 insideFunctionDecl = copy2;
255 return ret;
258 bool ConstVars::TraverseFunctionDecl(FunctionDecl* functionDecl)
260 auto copy2 = insideFunctionDecl;
261 insideFunctionDecl = functionDecl;
262 bool ret = RecursiveASTVisitor::TraverseFunctionDecl(functionDecl);
263 insideFunctionDecl = copy2;
264 return ret;
267 bool ConstVars::TraverseIfStmt(IfStmt* ifStmt)
269 VarDecl const* varDecl = nullptr;
270 if (Expr const* cond = ifStmt->getCond())
272 if (auto declRefExpr = dyn_cast<DeclRefExpr>(cond->IgnoreParenImpCasts()))
274 if ((varDecl = dyn_cast<VarDecl>(declRefExpr->getDecl())))
275 insideConditionalCheckOfVarSet.push_back(varDecl);
278 bool ret = RecursiveASTVisitor::TraverseIfStmt(ifStmt);
279 if (varDecl)
280 insideConditionalCheckOfVarSet.pop_back();
281 return ret;
284 bool ConstVars::VisitDeclRefExpr(const DeclRefExpr* declRefExpr)
286 const VarDecl* varDecl = dyn_cast<VarDecl>(declRefExpr->getDecl());
287 if (!varDecl)
288 return true;
289 varDecl = varDecl->getCanonicalDecl();
290 if (ignoreLocation(varDecl))
291 return true;
292 // ignore stuff that forms part of the stable URE interface
293 if (isInUnoIncludeFile(compiler.getSourceManager().getSpellingLoc(varDecl->getLocation())))
294 return true;
296 if (definitionSet.find(varDecl) != definitionSet.end())
297 check(varDecl, declRefExpr);
299 return true;
302 void ConstVars::check(const VarDecl* varDecl, const Expr* memberExpr)
304 auto parentsRange = compiler.getASTContext().getParents(*memberExpr);
305 const Stmt* child = memberExpr;
306 const Stmt* parent
307 = parentsRange.begin() == parentsRange.end() ? nullptr : parentsRange.begin()->get<Stmt>();
309 // walk up the tree until we find something interesting
311 bool bCannotBeConst = false;
312 bool bDump = false;
313 auto walkUp = [&]() {
314 child = parent;
315 auto parentsRange = compiler.getASTContext().getParents(*parent);
316 parent = parentsRange.begin() == parentsRange.end() ? nullptr
317 : parentsRange.begin()->get<Stmt>();
321 if (!parent)
323 // check if we have an expression like
324 // int& r = var;
325 auto parentsRange = compiler.getASTContext().getParents(*child);
326 if (parentsRange.begin() != parentsRange.end())
328 auto varDecl = dyn_cast_or_null<VarDecl>(parentsRange.begin()->get<Decl>());
329 if (varDecl)
331 if (varDecl->isImplicit())
333 // so we can walk up from inside a for-range stmt
334 parentsRange = compiler.getASTContext().getParents(*varDecl);
335 if (parentsRange.begin() != parentsRange.end())
336 parent = parentsRange.begin()->get<Stmt>();
338 else if (loplugin::TypeCheck(varDecl->getType()).LvalueReference().NonConst())
340 bCannotBeConst = true;
341 break;
346 if (!parent)
347 break;
348 if (isa<CXXReinterpretCastExpr>(parent))
350 // once we see one of these, there is not much useful we can know
351 bCannotBeConst = true;
352 break;
354 else if (isa<CastExpr>(parent) || isa<MemberExpr>(parent) || isa<ParenExpr>(parent)
355 || isa<ParenListExpr>(parent) || isa<ArrayInitLoopExpr>(parent)
356 || isa<ExprWithCleanups>(parent))
358 walkUp();
360 else if (auto unaryOperator = dyn_cast<UnaryOperator>(parent))
362 UnaryOperator::Opcode op = unaryOperator->getOpcode();
363 if (op == UO_AddrOf || op == UO_PostInc || op == UO_PostDec || op == UO_PreInc
364 || op == UO_PreDec)
366 bCannotBeConst = true;
368 walkUp();
370 else if (auto operatorCallExpr = dyn_cast<CXXOperatorCallExpr>(parent))
372 auto callee = getCallee(operatorCallExpr);
373 if (callee)
375 // if calling a non-const operator on the var
376 auto calleeMethodDecl = callee->getAsCXXMethodDecl();
377 if (calleeMethodDecl && operatorCallExpr->getArg(0) == child
378 && !calleeMethodDecl->isConst())
380 bCannotBeConst = true;
382 else if (IsPassedByNonConst(varDecl, child, operatorCallExpr, *callee))
384 bCannotBeConst = true;
387 else
388 bCannotBeConst = true; // conservative, could improve
389 walkUp();
391 else if (auto cxxMemberCallExpr = dyn_cast<CXXMemberCallExpr>(parent))
393 const CXXMethodDecl* calleeMethodDecl = cxxMemberCallExpr->getMethodDecl();
394 if (calleeMethodDecl)
396 // if calling a non-const method on the var
397 const Expr* tmp = dyn_cast<Expr>(child);
398 if (tmp->isBoundMemberFunction(compiler.getASTContext()))
400 tmp = dyn_cast<MemberExpr>(tmp)->getBase();
402 if (cxxMemberCallExpr->getImplicitObjectArgument() == tmp
403 && !calleeMethodDecl->isConst())
405 bCannotBeConst = true;
406 break;
408 if (IsPassedByNonConst(varDecl, child, cxxMemberCallExpr,
409 CalleeWrapper(calleeMethodDecl)))
410 bCannotBeConst = true;
412 else
413 bCannotBeConst = true; // can happen in templates
414 walkUp();
416 else if (auto cxxConstructExpr = dyn_cast<CXXConstructExpr>(parent))
418 if (IsPassedByNonConst(varDecl, child, cxxConstructExpr,
419 CalleeWrapper(cxxConstructExpr)))
420 bCannotBeConst = true;
421 walkUp();
423 else if (auto callExpr = dyn_cast<CallExpr>(parent))
425 auto callee = getCallee(callExpr);
426 if (callee)
428 if (IsPassedByNonConst(varDecl, child, callExpr, *callee))
429 bCannotBeConst = true;
431 else
432 bCannotBeConst = true; // conservative, could improve
433 walkUp();
435 else if (auto binaryOp = dyn_cast<BinaryOperator>(parent))
437 BinaryOperator::Opcode op = binaryOp->getOpcode();
438 const bool assignmentOp = op == BO_Assign || op == BO_MulAssign || op == BO_DivAssign
439 || op == BO_RemAssign || op == BO_AddAssign
440 || op == BO_SubAssign || op == BO_ShlAssign
441 || op == BO_ShrAssign || op == BO_AndAssign
442 || op == BO_XorAssign || op == BO_OrAssign;
443 if (assignmentOp)
445 if (binaryOp->getLHS() == child)
446 bCannotBeConst = true;
447 else if (loplugin::TypeCheck(binaryOp->getLHS()->getType())
448 .LvalueReference()
449 .NonConst())
450 // if the LHS is a non-const reference, we could write to the var later on
451 bCannotBeConst = true;
453 walkUp();
455 else if (isa<ReturnStmt>(parent))
457 if (insideFunctionDecl)
459 auto tc = loplugin::TypeCheck(insideFunctionDecl->getReturnType());
460 if (tc.LvalueReference().NonConst())
461 bCannotBeConst = true;
463 break;
465 else if (auto rangeStmt = dyn_cast<CXXForRangeStmt>(parent))
467 if (rangeStmt->getRangeStmt() == child)
469 auto tc = loplugin::TypeCheck(rangeStmt->getLoopVariable()->getType());
470 if (tc.LvalueReference().NonConst())
471 bCannotBeConst = true;
473 break;
475 else if (isa<SwitchStmt>(parent) || isa<WhileStmt>(parent) || isa<ForStmt>(parent)
476 || isa<IfStmt>(parent) || isa<DoStmt>(parent) || isa<DefaultStmt>(parent))
478 break;
480 else
482 walkUp();
484 } while (true);
486 if (bDump)
488 report(DiagnosticsEngine::Warning, "oh dear, what can the matter be? writtenTo=%0",
489 memberExpr->getBeginLoc())
490 << bCannotBeConst << memberExpr->getSourceRange();
491 if (parent)
493 report(DiagnosticsEngine::Note, "parent over here", parent->getBeginLoc())
494 << parent->getSourceRange();
495 parent->dump();
497 memberExpr->dump();
498 varDecl->getType()->dump();
501 if (bCannotBeConst)
502 cannotBeConstSet.insert(varDecl);
505 bool ConstVars::IsPassedByNonConst(const VarDecl* varDecl, const Stmt* child,
506 CallerWrapper callExpr, CalleeWrapper calleeFunctionDecl)
508 unsigned len = std::min(callExpr.getNumArgs(), calleeFunctionDecl.getNumParams());
509 // if it's an array, passing it by value to a method typically means the
510 // callee takes a pointer and can modify the array
511 if (varDecl->getType()->isConstantArrayType())
513 for (unsigned i = 0; i < len; ++i)
514 if (callExpr.getArg(i) == child)
515 if (loplugin::TypeCheck(calleeFunctionDecl.getParamType(i)).Pointer().NonConst())
516 return true;
518 else
520 for (unsigned i = 0; i < len; ++i)
521 if (callExpr.getArg(i) == child)
523 auto tc = loplugin::TypeCheck(calleeFunctionDecl.getParamType(i));
524 if (tc.LvalueReference().NonConst() || tc.Pointer().NonConst())
525 return true;
528 return false;
531 llvm::Optional<CalleeWrapper> ConstVars::getCallee(CallExpr const* callExpr)
533 FunctionDecl const* functionDecl = callExpr->getDirectCallee();
534 if (functionDecl)
535 return CalleeWrapper(functionDecl);
537 // Extract the functionprototype from a type
538 clang::Type const* calleeType = callExpr->getCallee()->getType().getTypePtr();
539 if (auto pointerType = calleeType->getUnqualifiedDesugaredType()->getAs<clang::PointerType>())
541 if (auto prototype = pointerType->getPointeeType()
542 ->getUnqualifiedDesugaredType()
543 ->getAs<FunctionProtoType>())
545 return CalleeWrapper(prototype);
549 return llvm::Optional<CalleeWrapper>();
552 /** off by default because it is very expensive, it walks up the AST a lot */
553 loplugin::Plugin::Registration<ConstVars> X("constvars", false);
556 #endif
558 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */