brintos

brintos / llvm-project-archived public Read only

0
0
Text · 3.6 KiB · 6364915 Raw
106 lines · cpp
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-exception6//7//===----------------------------------------------------------------------===//8 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"14 15using namespace clang::ast_matchers;16 17namespace clang {18namespace {19/// Matches a Stmt whose parent is a CompoundStmt, and which is directly20/// 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} // namespace39 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  ExprMutationAnalyzer Mutations(*S.getBody(), Context);75  if (Mutations.isMutated(S.getLoopVariable()))76    return false;77  const auto Matches =78      match(findAll(declRefExpr().bind("decl_ref")), *S.getBody(), Context);79 80  return llvm::none_of(Matches, [&Mutations](auto &DeclRef) {81    // TODO: allow modifications of loop-local variables82    return Mutations.isMutated(83        DeclRef.template getNodeAs<DeclRefExpr>("decl_ref")->getDecl());84  });85}86 87void UseAnyOfAllOfCheck::check(const MatchFinder::MatchResult &Result) {88  if (const auto *S = Result.Nodes.getNodeAs<CXXForRangeStmt>("any_of_loop")) {89    if (!isViableLoop(*S, *Result.Context))90      return;91 92    diag(S->getForLoc(), "replace loop by 'std%select{|::ranges}0::any_of()'")93        << getLangOpts().CPlusPlus20;94  } else if (const auto *S =95                 Result.Nodes.getNodeAs<CXXForRangeStmt>("all_of_loop")) {96    if (!isViableLoop(*S, *Result.Context))97      return;98 99    diag(S->getForLoc(), "replace loop by 'std%select{|::ranges}0::all_of()'")100        << getLangOpts().CPlusPlus20;101  }102}103 104} // namespace tidy::readability105} // namespace clang106