1 //===--- UseAnyOfAllOfCheck.cpp - clang-tidy-------------------------------===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
9 #include "UseAnyOfAllOfCheck.h"
10 #include "clang/AST/ASTContext.h"
11 #include "clang/ASTMatchers/ASTMatchFinder.h"
12 #include "clang/Analysis/Analyses/ExprMutationAnalyzer.h"
13 #include "clang/Frontend/CompilerInstance.h"
15 using namespace clang::ast_matchers
;
19 /// Matches a Stmt whose parent is a CompoundStmt, and which is directly
20 /// followed by a Stmt matching the inner matcher.
21 AST_MATCHER_P(Stmt
, nextStmt
, ast_matchers::internal::Matcher
<Stmt
>,
23 DynTypedNodeList Parents
= Finder
->getASTContext().getParents(Node
);
24 if (Parents
.size() != 1)
27 auto *C
= Parents
[0].get
<CompoundStmt
>();
31 const auto *I
= llvm::find(C
->body(), &Node
);
32 assert(I
!= C
->body_end() && "C is parent of Node");
33 if (++I
== C
->body_end())
34 return false; // Node is last statement.
36 return InnerMatcher
.matches(**I
, Finder
, Builder
);
40 namespace tidy::readability
{
42 void UseAnyOfAllOfCheck::registerMatchers(MatchFinder
*Finder
) {
43 auto Returns
= [](bool V
) {
44 return returnStmt(hasReturnValue(cxxBoolLiteral(equals(V
))));
47 auto ReturnsButNotTrue
=
48 returnStmt(hasReturnValue(unless(cxxBoolLiteral(equals(true)))));
49 auto ReturnsButNotFalse
=
50 returnStmt(hasReturnValue(unless(cxxBoolLiteral(equals(false)))));
54 nextStmt(Returns(false).bind("final_return")),
55 hasBody(allOf(hasDescendant(Returns(true)),
56 unless(anyOf(hasDescendant(breakStmt()),
57 hasDescendant(gotoStmt()),
58 hasDescendant(ReturnsButNotTrue
))))))
64 nextStmt(Returns(true).bind("final_return")),
65 hasBody(allOf(hasDescendant(Returns(false)),
66 unless(anyOf(hasDescendant(breakStmt()),
67 hasDescendant(gotoStmt()),
68 hasDescendant(ReturnsButNotFalse
))))))
73 static bool isViableLoop(const CXXForRangeStmt
&S
, ASTContext
&Context
) {
75 ExprMutationAnalyzer
Mutations(*S
.getBody(), Context
);
76 if (Mutations
.isMutated(S
.getLoopVariable()))
79 match(findAll(declRefExpr().bind("decl_ref")), *S
.getBody(), Context
);
81 return llvm::none_of(Matches
, [&Mutations
](auto &DeclRef
) {
82 // TODO: allow modifications of loop-local variables
83 return Mutations
.isMutated(
84 DeclRef
.template getNodeAs
<DeclRefExpr
>("decl_ref")->getDecl());
88 void UseAnyOfAllOfCheck::check(const MatchFinder::MatchResult
&Result
) {
90 if (const auto *S
= Result
.Nodes
.getNodeAs
<CXXForRangeStmt
>("any_of_loop")) {
91 if (!isViableLoop(*S
, *Result
.Context
))
94 diag(S
->getForLoc(), "replace loop by 'std%select{|::ranges}0::any_of()'")
95 << getLangOpts().CPlusPlus20
;
96 } else if (const auto *S
=
97 Result
.Nodes
.getNodeAs
<CXXForRangeStmt
>("all_of_loop")) {
98 if (!isViableLoop(*S
, *Result
.Context
))
101 diag(S
->getForLoc(), "replace loop by 'std%select{|::ranges}0::all_of()'")
102 << getLangOpts().CPlusPlus20
;
106 } // namespace tidy::readability