clang-tools 23.0.0git
UseAnyOfAllOfCheck.cpp
Go to the documentation of this file.
1//===----------------------------------------------------------------------===//
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 const 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
39AST_MATCHER(Expr, isUnsafeTemporaryRangeInit) {
40 return Node.IgnoreParenCasts()->isPRValue();
41}
42} // namespace
43
44namespace tidy::readability {
45
46void UseAnyOfAllOfCheck::registerMatchers(MatchFinder *Finder) {
47 auto Returns = [](bool V) {
48 return returnStmt(hasReturnValue(cxxBoolLiteral(equals(V))));
49 };
50
51 const auto ReturnsButNotTrue =
52 returnStmt(hasReturnValue(unless(cxxBoolLiteral(equals(true)))));
53 const auto ReturnsButNotFalse =
54 returnStmt(hasReturnValue(unless(cxxBoolLiteral(equals(false)))));
55 const auto RangeInitMatcher =
56 optionally(expr(isUnsafeTemporaryRangeInit()).bind("unsafe_range_init"));
57
58 Finder->addMatcher(
59 cxxForRangeStmt(
60 hasRangeInit(RangeInitMatcher),
61 nextStmt(Returns(false).bind("final_return")),
62 hasBody(allOf(hasDescendant(Returns(true)),
63 unless(anyOf(hasDescendant(breakStmt()),
64 hasDescendant(gotoStmt()),
65 hasDescendant(ReturnsButNotTrue))))))
66 .bind("any_of_loop"),
67 this);
68
69 Finder->addMatcher(
70 cxxForRangeStmt(
71 hasRangeInit(RangeInitMatcher),
72 nextStmt(Returns(true).bind("final_return")),
73 hasBody(allOf(hasDescendant(Returns(false)),
74 unless(anyOf(hasDescendant(breakStmt()),
75 hasDescendant(gotoStmt()),
76 hasDescendant(ReturnsButNotFalse))))))
77 .bind("all_of_loop"),
78 this);
79}
80
81static bool isViableLoop(const CXXForRangeStmt &S, ASTContext &Context) {
82 ExprMutationAnalyzer Mutations(*S.getBody(), Context);
83 if (Mutations.isMutated(S.getLoopVariable()))
84 return false;
85 const auto Matches =
86 match(findAll(declRefExpr().bind("decl_ref")), *S.getBody(), Context);
87
88 return llvm::none_of(Matches, [&Mutations](auto &DeclRef) {
89 // TODO: allow modifications of loop-local variables
90 return Mutations.isMutated(
91 DeclRef.template getNodeAs<DeclRefExpr>("decl_ref")->getDecl());
92 });
93}
94
95void UseAnyOfAllOfCheck::check(const MatchFinder::MatchResult &Result) {
96 const auto *AnyOfS = Result.Nodes.getNodeAs<CXXForRangeStmt>("any_of_loop");
97 const auto *AllOfS = Result.Nodes.getNodeAs<CXXForRangeStmt>("all_of_loop");
98 const CXXForRangeStmt *S = AnyOfS ? AnyOfS : AllOfS;
99
100 if (!S || !isViableLoop(*S, *Result.Context))
101 return;
102
103 const bool IsAnyOf = (AnyOfS != nullptr);
104
105 diag(S->getForLoc(),
106 "replace loop by 'std%select{|::ranges}0::%select{all_of|any_of}1()'")
107 << getLangOpts().CPlusPlus20 << IsAnyOf;
108
109 if (const auto *Init = Result.Nodes.getNodeAs<Expr>("unsafe_range_init")) {
110 if (getLangOpts().CPlusPlus20)
111 diag(Init->getExprLoc(),
112 "reusing the temporary range directly in the replacement may be "
113 "unsafe; consider materializing it in a local variable first, or "
114 "use 'std::ranges' algorithms which handle temporary ranges safely",
115 DiagnosticIDs::Note);
116 else
117 diag(Init->getExprLoc(),
118 "reusing the temporary range directly in the replacement may be "
119 "unsafe; consider materializing it in a local variable first",
120 DiagnosticIDs::Note);
121 }
122}
123
124} // namespace tidy::readability
125} // namespace clang
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
void registerMatchers(ast_matchers::MatchFinder *Finder) override
AST_MATCHER_P(Stmt, isStatementIdenticalToBoundNode, std::string, ID)
AST_MATCHER(BinaryOperator, isRelationalOperator)
static bool isViableLoop(const CXXForRangeStmt &S, ASTContext &Context)
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//