diff --git a/clang-tools-extra/test/clang-tidy/checkers/readability/use-anyofallof-nocrash.cpp b/clang-tools-extra/test/clang-tidy/checkers/readability/use-anyofallof-nocrash.cpp new file mode 100644 --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/readability/use-anyofallof-nocrash.cpp @@ -0,0 +1,20 @@ +// RUN: %check_clang_tidy -std=c++14,c++17 %s readability-use-anyofallof %t -- + +struct V { + V* begin() const; + V* end() const; +}; + +void bad_all_of(V &elem) { + auto rec = [](const auto& e, auto&& self) -> bool { + // CHECK-MESSAGES: :[[@LINE+1]]:5: warning: replace loop by 'std::all_of()' [readability-use-anyofallof] + for (auto& child : e) { + if (!self(child, self)) { + return false; + } + } + return true; + }; + + rec(elem, rec); +} 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 @@ -18,12 +18,20 @@ class FunctionParmMutationAnalyzer; +using DeclSet = llvm::DenseSet; + /// Analyzes whether any mutative operations are applied to an expression within /// a given statement. class ExprMutationAnalyzer { public: - ExprMutationAnalyzer(const Stmt &Stm, ASTContext &Context) - : Stm(Stm), Context(Context) {} + ExprMutationAnalyzer(const Stmt &Stm, ASTContext &Context, + DeclSet *DeclAnalyzed = nullptr) + : Stm(Stm), Context(Context), DeclAnalyzed(DeclAnalyzed) { + if (nullptr == DeclAnalyzed) { + OwningDeclAnalyzed = std::make_unique(); + this->DeclAnalyzed = OwningDeclAnalyzed.get(); + } + } bool isMutated(const Expr *Exp) { return findMutation(Exp) != nullptr; } bool isMutated(const Decl *Dec) { return findMutation(Dec) != nullptr; } @@ -74,13 +82,16 @@ FuncParmAnalyzer; ResultMap Results; ResultMap PointeeResults; + DeclSet *DeclAnalyzed; + std::unique_ptr OwningDeclAnalyzed; }; // A convenient wrapper around ExprMutationAnalyzer for analyzing function // params. class FunctionParmMutationAnalyzer { public: - FunctionParmMutationAnalyzer(const FunctionDecl &Func, ASTContext &Context); + FunctionParmMutationAnalyzer(const FunctionDecl &Func, ASTContext &Context, + DeclSet *DeclAnalyzed = nullptr); bool isMutated(const ParmVarDecl *Parm) { return findMutation(Parm) != nullptr; 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 @@ -568,6 +568,11 @@ if (!Func->getBody() || !Func->getPrimaryTemplate()) return Exp; + if (DeclAnalyzed->contains(Func)) { + return nullptr; + } + DeclAnalyzed->insert(Func); + const auto *Parm = Nodes.getNodeAs("parm"); const ArrayRef AllParams = Func->getPrimaryTemplate()->getTemplatedDecl()->parameters(); @@ -586,7 +591,8 @@ std::unique_ptr &Analyzer = FuncParmAnalyzer[Func]; if (!Analyzer) - Analyzer.reset(new FunctionParmMutationAnalyzer(*Func, Context)); + Analyzer.reset( + new FunctionParmMutationAnalyzer(*Func, Context, DeclAnalyzed)); if (Analyzer->findMutation(Parm)) return Exp; continue; @@ -599,8 +605,8 @@ } FunctionParmMutationAnalyzer::FunctionParmMutationAnalyzer( - const FunctionDecl &Func, ASTContext &Context) - : BodyAnalyzer(*Func.getBody(), Context) { + const FunctionDecl &Func, ASTContext &Context, DeclSet *DeclAnalyzed) + : BodyAnalyzer(*Func.getBody(), Context, DeclAnalyzed) { if (const auto *Ctor = dyn_cast(&Func)) { // CXXCtorInitializer might also mutate Param but they're not part of // function body, check them eagerly here since they're typically trivial. diff --git a/clang/unittests/Analysis/ExprMutationAnalyzerTest.cpp b/clang/unittests/Analysis/ExprMutationAnalyzerTest.cpp --- a/clang/unittests/Analysis/ExprMutationAnalyzerTest.cpp +++ b/clang/unittests/Analysis/ExprMutationAnalyzerTest.cpp @@ -1309,6 +1309,30 @@ EXPECT_FALSE(isMutated(Results, AST.get())); } +TEST(ExprMutationAnalyzerTest, RangeForNonArrayByConstRefNoCrash) { + const auto AST = buildASTFromCode(R"( + struct V { + V* begin() const; + V* end() const; + }; + void f(V &elem) { + auto rec = [](const auto& e, auto&& self) -> bool { + for (auto& child : e) { + if (!self(child, self)) { + return false; + } + } + return true; + }; + + rec(elem, rec); + } + )"); + const auto Results = + match(withEnclosingCompound(declRefTo("elem")), AST->getASTContext()); + EXPECT_FALSE(isMutated(Results, AST.get())); +} + // section: unevaluated expressions TEST(ExprMutationAnalyzerTest, UnevaluatedExpressions) {