clang-tools 19.0.0git
UseAnyOfAllOfCheck.cpp
Go to the documentation of this file.
1//===--- UseAnyOfAllOfCheck.cpp - clang-tidy-------------------------------===//
2//
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
6//
7//===----------------------------------------------------------------------===//
8
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"
14
15using namespace clang::ast_matchers;
16
17namespace clang {
18namespace {
19/// Matches a Stmt whose parent is a CompoundStmt, and which is directly
20/// followed by a Stmt matching the inner matcher.
21AST_MATCHER_P(Stmt, nextStmt, ast_matchers::internal::Matcher<Stmt>,
22 InnerMatcher) {
23 DynTypedNodeList Parents = Finder->getASTContext().getParents(Node);
24 if (Parents.size() != 1)
25 return false;
26
27 auto *C = Parents[0].get<CompoundStmt>();
28 if (!C)
29 return false;
30
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.
35
36 return InnerMatcher.matches(**I, Finder, Builder);
37}
38} // namespace
39
40namespace tidy::readability {
41
42void UseAnyOfAllOfCheck::registerMatchers(MatchFinder *Finder) {
43 auto Returns = [](bool V) {
44 return returnStmt(hasReturnValue(cxxBoolLiteral(equals(V))));
45 };
46
47 auto ReturnsButNotTrue =
48 returnStmt(hasReturnValue(unless(cxxBoolLiteral(equals(true)))));
49 auto ReturnsButNotFalse =
50 returnStmt(hasReturnValue(unless(cxxBoolLiteral(equals(false)))));
51
52 Finder->addMatcher(
53 cxxForRangeStmt(
54 nextStmt(Returns(false).bind("final_return")),
55 hasBody(allOf(hasDescendant(Returns(true)),
56 unless(anyOf(hasDescendant(breakStmt()),
57 hasDescendant(gotoStmt()),
58 hasDescendant(ReturnsButNotTrue))))))
59 .bind("any_of_loop"),
60 this);
61
62 Finder->addMatcher(
63 cxxForRangeStmt(
64 nextStmt(Returns(true).bind("final_return")),
65 hasBody(allOf(hasDescendant(Returns(false)),
66 unless(anyOf(hasDescendant(breakStmt()),
67 hasDescendant(gotoStmt()),
68 hasDescendant(ReturnsButNotFalse))))))
69 .bind("all_of_loop"),
70 this);
71}
72
73static bool isViableLoop(const CXXForRangeStmt &S, ASTContext &Context) {
74
75 ExprMutationAnalyzer Mutations(*S.getBody(), Context);
76 if (Mutations.isMutated(S.getLoopVariable()))
77 return false;
78 const auto Matches =
79 match(findAll(declRefExpr().bind("decl_ref")), *S.getBody(), Context);
80
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());
85 });
86}
87
88void UseAnyOfAllOfCheck::check(const MatchFinder::MatchResult &Result) {
89
90 if (const auto *S = Result.Nodes.getNodeAs<CXXForRangeStmt>("any_of_loop")) {
91 if (!isViableLoop(*S, *Result.Context))
92 return;
93
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))
99 return;
100
101 diag(S->getForLoc(), "replace loop by 'std%select{|::ranges}0::all_of()'")
102 << getLangOpts().CPlusPlus20;
103 }
104}
105
106} // namespace tidy::readability
107} // namespace clang
CodeCompletionBuilder Builder
const Criteria C
const DeclRefExpr * DeclRef
DiagnosticBuilder diag(SourceLocation Loc, StringRef Description, DiagnosticIDs::Level Level=DiagnosticIDs::Warning)
Add a diagnostic with the check's name.
const LangOptions & getLangOpts() const
Returns the language options from the context.
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
ClangTidyChecks that register ASTMatchers should do the actual work in here.
void registerMatchers(ast_matchers::MatchFinder *Finder) override
Override this to register AST matchers with Finder.
AST_MATCHER_P(UserDefinedLiteral, hasLiteral, clang::ast_matchers::internal::Matcher< Expr >, InnerMatcher)
static bool isViableLoop(const CXXForRangeStmt &S, ASTContext &Context)
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//