diff --git a/clang-tools-extra/clang-tidy/bugprone/InfiniteLoopCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/InfiniteLoopCheck.cpp --- a/clang-tools-extra/clang-tidy/bugprone/InfiniteLoopCheck.cpp +++ b/clang-tools-extra/clang-tidy/bugprone/InfiniteLoopCheck.cpp @@ -177,6 +177,9 @@ } } + if (ExprMutationAnalyzer::isUnevaluated(LoopStmt, *LoopStmt, *Result.Context)) + return; + if (isAtLeastOneCondVarChanged(Func, LoopStmt, Cond, Result.Context)) return; diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone-infinite-loop.cpp b/clang-tools-extra/test/clang-tidy/checkers/bugprone-infinite-loop.cpp --- a/clang-tools-extra/test/clang-tidy/checkers/bugprone-infinite-loop.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone-infinite-loop.cpp @@ -650,3 +650,38 @@ do { } while (1, (false) && val4 == 1); } + +void test_typeof() { + __typeof__({ + for (int i = 0; i < 10; ++i) { + } + 0; + }) x; +} + +void test_typeof_infinite() { + __typeof__({ + for (int i = 0; i < 10;) { + } + 0; + }) x; +} + +void test_typeof_while_infinite() { + __typeof__({ + int i = 0; + while (i < 10) { + } + 0; + }) x; +} + +void test_typeof_dowhile_infinite() { + __typeof__({ + int i = 0; + do { + + } while (i < 10); + 0; + }) x; +} diff --git a/clang/include/clang/Analysis/Analyses/ExprMutationAnalyzer.h b/clang/include/clang/Analysis/Analyses/ExprMutationAnalyzer.h --- a/clang/include/clang/Analysis/Analyses/ExprMutationAnalyzer.h +++ b/clang/include/clang/Analysis/Analyses/ExprMutationAnalyzer.h @@ -38,6 +38,10 @@ } const Stmt *findPointeeMutation(const Expr *Exp); const Stmt *findPointeeMutation(const Decl *Dec); + static bool isUnevaluated(const Stmt *Smt, const Stmt &Stm, + ASTContext &Context); + static bool isUnevaluated(const Expr *Exp, const Stmt &Stm, + ASTContext &Context); private: using MutationFinder = const Stmt *(ExprMutationAnalyzer::*)(const Expr *); diff --git a/clang/lib/Analysis/ExprMutationAnalyzer.cpp b/clang/lib/Analysis/ExprMutationAnalyzer.cpp --- a/clang/lib/Analysis/ExprMutationAnalyzer.cpp +++ b/clang/lib/Analysis/ExprMutationAnalyzer.cpp @@ -194,35 +194,48 @@ return nullptr; } -bool ExprMutationAnalyzer::isUnevaluated(const Expr *Exp) { - return selectFirst( +auto isUnevaluatedMatcher(const Stmt *Exp) { + return anyOf( + // `Exp` is part of the underlying expression of + // decltype/typeof if it has an ancestor of + // typeLoc. + hasAncestor(typeLoc(unless(hasAncestor(unaryExprOrTypeTraitExpr())))), + hasAncestor(expr(anyOf( + // `UnaryExprOrTypeTraitExpr` is unevaluated + // unless it's sizeof on VLA. + unaryExprOrTypeTraitExpr( + unless(sizeOfExpr(hasArgumentOfType(variableArrayType())))), + // `CXXTypeidExpr` is unevaluated unless it's + // applied to an expression of glvalue of + // polymorphic class type. + cxxTypeidExpr(unless(isPotentiallyEvaluated())), + // The controlling expression of + // `GenericSelectionExpr` is unevaluated. + genericSelectionExpr( + hasControllingExpr(hasDescendant(equalsNode(Exp)))), + cxxNoexceptExpr())))); +} + +bool ExprMutationAnalyzer::isUnevaluated(const Expr *Exp, const Stmt &Stm, + ASTContext &Context) { + return selectFirst(NodeID::value, + match(findAll(expr(canResolveToExpr(equalsNode(Exp)), + isUnevaluatedMatcher(Exp)) + .bind(NodeID::value)), + Stm, Context)) != nullptr; +} + +bool ExprMutationAnalyzer::isUnevaluated(const Stmt *Exp, const Stmt &Stm, + ASTContext &Context) { + return selectFirst( NodeID::value, - match( - findAll( - expr(canResolveToExpr(equalsNode(Exp)), - anyOf( - // `Exp` is part of the underlying expression of - // decltype/typeof if it has an ancestor of - // typeLoc. - hasAncestor(typeLoc(unless( - hasAncestor(unaryExprOrTypeTraitExpr())))), - hasAncestor(expr(anyOf( - // `UnaryExprOrTypeTraitExpr` is unevaluated - // unless it's sizeof on VLA. - unaryExprOrTypeTraitExpr(unless(sizeOfExpr( - hasArgumentOfType(variableArrayType())))), - // `CXXTypeidExpr` is unevaluated unless it's - // applied to an expression of glvalue of - // polymorphic class type. - cxxTypeidExpr( - unless(isPotentiallyEvaluated())), - // The controlling expression of - // `GenericSelectionExpr` is unevaluated. - genericSelectionExpr(hasControllingExpr( - hasDescendant(equalsNode(Exp)))), - cxxNoexceptExpr()))))) - .bind(NodeID::value)), - Stm, Context)) != nullptr; + match(findAll(stmt(equalsNode(Exp), isUnevaluatedMatcher(Exp)) + .bind(NodeID::value)), + Stm, Context)) != nullptr; +} + +bool ExprMutationAnalyzer::isUnevaluated(const Expr *Exp) { + return isUnevaluated(Exp, Stm, Context); } const Stmt *