brintos

brintos / llvm-project-archived public Read only

0
0
Text · 4.9 KiB · 8966745 Raw
127 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 "PreferIsaOrDynCastInConditionalsCheck.h"10#include "clang/AST/ASTContext.h"11#include "clang/ASTMatchers/ASTMatchFinder.h"12#include "clang/Lex/Lexer.h"13#include "llvm/Support/FormatVariadic.h"14 15using namespace clang::ast_matchers;16 17namespace clang::tidy::llvm_check {18 19namespace {20AST_MATCHER(Expr, isMacroID) { return Node.getExprLoc().isMacroID(); }21} // namespace22 23void PreferIsaOrDynCastInConditionalsCheck::registerMatchers(24    MatchFinder *Finder) {25  auto AnyCalleeName = [](ArrayRef<StringRef> CalleeName) {26    return allOf(unless(isMacroID()), unless(cxxMemberCallExpr()),27                 callee(expr(ignoringImpCasts(28                     declRefExpr(to(namedDecl(hasAnyName(CalleeName))),29                                 hasAnyTemplateArgumentLoc(anything()))30                         .bind("callee")))));31  };32 33  auto CondExpr = hasCondition(implicitCastExpr(34      has(callExpr(AnyCalleeName({"cast", "dyn_cast"})).bind("cond"))));35 36  auto CondExprOrCondVar =37      anyOf(hasConditionVariableStatement(containsDeclaration(38                0, varDecl(hasInitializer(callExpr(AnyCalleeName({"cast"}))))39                       .bind("var"))),40            CondExpr);41 42  auto CallWithBindedArg =43      callExpr(44          AnyCalleeName(45              {"isa", "cast", "cast_or_null", "dyn_cast", "dyn_cast_or_null"}),46          hasArgument(0, mapAnyOf(declRefExpr, cxxMemberCallExpr).bind("arg")))47          .bind("rhs");48 49  Finder->addMatcher(50      stmt(anyOf(ifStmt(CondExprOrCondVar), forStmt(CondExprOrCondVar),51                 whileStmt(CondExprOrCondVar), doStmt(CondExpr),52                 binaryOperator(unless(isExpansionInFileMatching(53                                    "llvm/include/llvm/Support/Casting.h")),54                                hasOperatorName("&&"),55                                hasLHS(implicitCastExpr().bind("lhs")),56                                hasRHS(ignoringImpCasts(CallWithBindedArg)))57                     .bind("and"))),58      this);59}60 61void PreferIsaOrDynCastInConditionalsCheck::check(62    const MatchFinder::MatchResult &Result) {63  const auto *Callee = Result.Nodes.getNodeAs<DeclRefExpr>("callee");64 65  assert(Callee && "Callee should be binded if anything is matched");66 67  // The first and last letter of the identifier68  //   llvm::cast<T>(x)69  //         ^  ^70  //  StartLoc  EndLoc71  const SourceLocation StartLoc = Callee->getLocation();72  const SourceLocation EndLoc = Callee->getNameInfo().getEndLoc();73 74  if (Result.Nodes.getNodeAs<VarDecl>("var")) {75    diag(StartLoc,76         "cast<> in conditional will assert rather than return a null pointer")77        << FixItHint::CreateReplacement(SourceRange(StartLoc, EndLoc),78                                        "dyn_cast");79  } else if (Result.Nodes.getNodeAs<CallExpr>("cond")) {80    StringRef Message =81        "cast<> in conditional will assert rather than return a null pointer";82    if (Callee->getDecl()->getName() == "dyn_cast")83      Message = "return value from dyn_cast<> not used";84 85    diag(StartLoc, Message)86        << FixItHint::CreateReplacement(SourceRange(StartLoc, EndLoc), "isa");87  } else if (Result.Nodes.getNodeAs<BinaryOperator>("and")) {88    const auto *LHS = Result.Nodes.getNodeAs<ImplicitCastExpr>("lhs");89    const auto *RHS = Result.Nodes.getNodeAs<CallExpr>("rhs");90    const auto *Arg = Result.Nodes.getNodeAs<Expr>("arg");91 92    assert(LHS && "LHS is null");93    assert(RHS && "RHS is null");94    assert(Arg && "Arg is null");95 96    auto GetText = [&](SourceRange R) {97      return Lexer::getSourceText(CharSourceRange::getTokenRange(R),98                                  *Result.SourceManager, getLangOpts());99    };100 101    const StringRef LHSString = GetText(LHS->getSourceRange());102    const StringRef ArgString = GetText(Arg->getSourceRange());103 104    if (ArgString != LHSString)105      return;106 107    // It is not clear which is preferred between `isa_and_nonnull` and108    // `isa_and_present`. See109    // https://discourse.llvm.org/t/psa-swapping-out-or-null-with-if-present/65018110    const std::string Replacement = llvm::formatv(111        "{}isa_and_nonnull{}",112        GetText(Callee->getQualifierLoc().getSourceRange()),113        GetText(SourceRange(Callee->getLAngleLoc(), RHS->getEndLoc())));114 115    diag(LHS->getBeginLoc(),116         "isa_and_nonnull<> is preferred over an explicit test for null "117         "followed by calling isa<>")118        << FixItHint::CreateReplacement(119               SourceRange(LHS->getBeginLoc(), RHS->getEndLoc()), Replacement);120  } else {121    llvm_unreachable(122        R"(One of "var", "cond" and "and" should be binded if anything is matched)");123  }124}125 126} // namespace clang::tidy::llvm_check127