11#include "clang/AST/ASTContext.h"
12#include "clang/ASTMatchers/ASTMatchFinder.h"
13#include "clang/Analysis/Analyses/ExprMutationAnalyzer.h"
14#include "clang/Analysis/CallGraph.h"
15#include "llvm/ADT/SCCIterator.h"
18using clang::ast_matchers::internal::Matcher;
26 return Node.hasAttr<NoReturnAttr>() || Node.hasAttr<CXX11NoReturnAttr>() ||
27 Node.hasAttr<C11NoReturnAttr>();
32 return Node.getNoReturnAttr();
37 Matcher<QualType> IsNoReturnFunType =
38 ignoringParens(functionType(typeHasNoReturnAttr()));
39 Matcher<Decl> IsNoReturnDecl =
40 anyOf(declHasNoReturnAttr(), functionDecl(hasType(IsNoReturnFunType)),
41 varDecl(hasType(blockPointerType(pointee(IsNoReturnFunType)))));
44 mapAnyOf(breakStmt, returnStmt, gotoStmt, cxxThrowExpr).with(Internal),
46 callee(mapAnyOf(functionDecl, varDecl)
47 .with(IsNoReturnDecl))),
48 objcMessageExpr(Internal, callee(IsNoReturnDecl))));
52static bool isChanged(
const Stmt *LoopStmt,
const ValueDecl *Var,
53 ASTContext *Context) {
54 if (
const auto *ForLoop = dyn_cast<ForStmt>(LoopStmt))
55 return (ForLoop->getInc() &&
56 ExprMutationAnalyzer(*ForLoop->getInc(), *Context)
58 (ForLoop->getBody() &&
59 ExprMutationAnalyzer(*ForLoop->getBody(), *Context)
61 (ForLoop->getCond() &&
62 ExprMutationAnalyzer(*ForLoop->getCond(), *Context).isMutated(Var));
64 return ExprMutationAnalyzer(*LoopStmt, *Context).isMutated(Var);
68 const ValueDecl *VD, ASTContext *Context) {
69 const VarDecl *Var =
nullptr;
70 if (
const auto *VarD = dyn_cast<VarDecl>(VD)) {
72 }
else if (
const auto *BD = dyn_cast<BindingDecl>(VD)) {
73 if (
const auto *DD = dyn_cast<DecompositionDecl>(BD->getDecomposedDecl()))
80 if (!Var->isLocalVarDeclOrParm() || Var->getType().isVolatileQualified())
83 if (!VD->getType().getTypePtr()->isIntegerType())
92 const Stmt *Cond, ASTContext *Context) {
93 if (
const auto *DRE = dyn_cast<DeclRefExpr>(Cond)) {
94 if (
const auto *VD = dyn_cast<ValueDecl>(DRE->getDecl()))
96 }
else if (isa<MemberExpr, CallExpr, ObjCIvarRefExpr, ObjCPropertyRefExpr,
97 ObjCMessageExpr>(Cond)) {
100 }
else if (
const auto *CE = dyn_cast<CastExpr>(Cond)) {
101 QualType T = CE->getType();
103 if (T.isVolatileQualified())
106 if (!T->isAnyPointerType() && !T->isReferenceType())
109 T = T->getPointeeType();
118 const Stmt *Cond, ASTContext *Context) {
122 for (
const Stmt *Child : Cond->children()) {
134 if (
const auto *DRE = dyn_cast<DeclRefExpr>(Cond)) {
135 if (
const auto *Var = dyn_cast<VarDecl>(DRE->getDecl()))
136 return std::string(Var->getName());
138 if (
const auto *BD = dyn_cast<BindingDecl>(DRE->getDecl())) {
139 return std::string(BD->getName());
144 for (
const Stmt *Child : Cond->children()) {
149 if (!Result.empty() && !NewNames.empty())
157 bool ExpectedValue) {
158 if (Cond.isValueDependent()) {
159 if (
const auto *BinOp = dyn_cast<BinaryOperator>(&Cond)) {
162 if (!ExpectedValue && BinOp->getOpcode() == BO_LAnd)
165 if (ExpectedValue && BinOp->getOpcode() == BO_LOr)
168 if (BinOp->getOpcode() == BO_Comma)
170 }
else if (
const auto *UnOp = dyn_cast<UnaryOperator>(&Cond)) {
171 if (UnOp->getOpcode() == UO_LNot)
173 }
else if (
const auto *Paren = dyn_cast<ParenExpr>(&Cond))
175 else if (
const auto *ImplCast = dyn_cast<ImplicitCastExpr>(&Cond))
180 if (Cond.EvaluateAsBooleanCondition(Result, Ctx))
181 return Result == ExpectedValue;
191 llvm::SmallPtrSet<const Decl *, 16> &Callees) {
192 if (
const auto *Call = dyn_cast<CallExpr>(StmtNode)) {
193 const Decl *Callee = Call->getDirectCallee();
197 Callees.insert(Callee->getCanonicalDecl());
199 if (
const auto *Call = dyn_cast<ObjCMessageExpr>(StmtNode)) {
200 const Decl *Callee = Call->getMethodDecl();
204 Callees.insert(Callee->getCanonicalDecl());
206 for (
const Stmt *Child : StmtNode->children())
214static bool overlap(ArrayRef<CallGraphNode *> SCC,
215 const llvm::SmallPtrSet<const Decl *, 16> &Callees,
217 bool ContainsFunc =
false, Overlap =
false;
219 for (
const CallGraphNode *GNode : SCC) {
220 const Decl *CanDecl = GNode->getDecl()->getCanonicalDecl();
222 ContainsFunc = ContainsFunc || (CanDecl == Func);
223 Overlap = Overlap || Callees.contains(CanDecl);
224 if (ContainsFunc && Overlap)
232 if (
const auto *DRE = dyn_cast<DeclRefExpr>(Cond)) {
233 if (
const auto *VD = dyn_cast<VarDecl>(DRE->getDecl()))
234 if (VD->isStaticLocal())
237 if (
const auto *BD = dyn_cast<BindingDecl>(DRE->getDecl()))
238 if (
const auto *DD = dyn_cast<DecompositionDecl>(BD->getDecomposedDecl()))
239 if (DD->isStaticLocal())
243 for (
const Stmt *Child : Cond->children())
261 const Stmt *LoopStmt,
263 const ASTContext *Ctx) {
267 llvm::SmallPtrSet<const Decl *, 16> CalleesInLoop;
274 if (CalleesInLoop.empty())
277 TranslationUnitDecl *TUDecl = Ctx->getTranslationUnitDecl();
280 CG.addToCallGraph(TUDecl);
283 for (llvm::scc_iterator<CallGraph *> SCCI = llvm::scc_begin(&CG),
284 SCCE = llvm::scc_end(&CG);
285 SCCI != SCCE; ++SCCI) {
286 if (!SCCI.hasCycle())
290 if (
overlap(*SCCI, CalleesInLoop, Func->getCanonicalDecl()))
297 const auto LoopCondition = allOf(
298 hasCondition(expr(forCallable(decl().bind(
"func"))).bind(
"condition")),
299 unless(hasBody(hasDescendant(
302 Finder->addMatcher(mapAnyOf(whileStmt, doStmt, forStmt)
309 const auto *Cond = Result.Nodes.getNodeAs<Expr>(
"condition");
310 const auto *LoopStmt = Result.Nodes.getNodeAs<Stmt>(
"loop-stmt");
311 const auto *Func = Result.Nodes.getNodeAs<Decl>(
"func");
316 bool ShouldHaveConditionVariables =
true;
317 if (
const auto *While = dyn_cast<WhileStmt>(LoopStmt)) {
318 if (
const VarDecl *LoopVarDecl = While->getConditionVariable()) {
319 if (
const Expr *Init = LoopVarDecl->getInit()) {
320 ShouldHaveConditionVariables =
false;
326 if (ExprMutationAnalyzer::isUnevaluated(LoopStmt, *Result.Context))
336 if (ShouldHaveConditionVariables && CondVarNames.empty())
339 if (CondVarNames.empty()) {
340 diag(LoopStmt->getBeginLoc(),
341 "this loop is infinite; it does not check any variables in the"
344 diag(LoopStmt->getBeginLoc(),
345 "this loop is infinite; none of its condition variables (%0)"
346 " are updated in the loop body")
bool hasPtrOrReferenceInFunc(const Decl *Func, const ValueDecl *Var)
Returns whether Var has a pointer or reference in Func.
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
void registerMatchers(ast_matchers::MatchFinder *Finder) override
static bool isChanged(const Stmt *LoopStmt, const ValueDecl *Var, ASTContext *Context)
Return whether Var was changed in LoopStmt.
static Matcher< Stmt > loopEndingStmt(Matcher< Stmt > Internal)
static bool isKnownToHaveValue(const Expr &Cond, const ASTContext &Ctx, bool ExpectedValue)
static bool hasStaticLocalVariable(const Stmt *Cond)
returns true iff Cond involves at least one static local variable.
static bool isVarThatIsPossiblyChanged(const Decl *Func, const Stmt *LoopStmt, const Stmt *Cond, ASTContext *Context)
Return whether Cond is a variable that is possibly changed in LoopStmt.
static bool hasRecursionOverStaticLoopCondVariables(const Expr *Cond, const Stmt *LoopStmt, const Decl *Func, const ASTContext *Ctx)
Tests if the loop condition Cond involves static local variables and the enclosing function Func is r...
static bool overlap(ArrayRef< CallGraphNode * > SCC, const llvm::SmallPtrSet< const Decl *, 16 > &Callees, const Decl *Func)
returns true iff SCC contains Func and its' function set overlaps with Callees
static std::string getCondVarNames(const Stmt *Cond)
Return the variable names in Cond.
static bool populateCallees(const Stmt *StmtNode, llvm::SmallPtrSet< const Decl *, 16 > &Callees)
populates the set Callees with all function (and objc method) declarations called in StmtNode if all ...
static bool isVarPossiblyChanged(const Decl *Func, const Stmt *LoopStmt, const ValueDecl *VD, ASTContext *Context)
static bool isAtLeastOneCondVarChanged(const Decl *Func, const Stmt *LoopStmt, const Stmt *Cond, ASTContext *Context)
Return whether at least one variable of Cond changed in LoopStmt.
AST_MATCHER(BinaryOperator, isRelationalOperator)
bool hasPtrOrReferenceInFunc(const Decl *Func, const ValueDecl *Var)
Returns whether Var has a pointer or reference in Func.