//===--- PreferIsaOrDynCastInConditionalsCheck.cpp - clang-tidy //---------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "PreferIsaOrDynCastInConditionalsCheck.h" #include "clang/AST/ASTContext.h" #include "clang/ASTMatchers/ASTMatchFinder.h" #include "clang/Lex/Lexer.h" using namespace clang::ast_matchers; namespace clang { namespace ast_matchers { AST_MATCHER(Expr, isMacroID) { return Node.getExprLoc().isMacroID(); } } // namespace ast_matchers namespace tidy { namespace llvm_check { void PreferIsaOrDynCastInConditionalsCheck::registerMatchers( MatchFinder *Finder) { auto Condition = hasCondition(implicitCastExpr(has( callExpr( allOf(unless(isMacroID()), unless(cxxMemberCallExpr()), anyOf(callee(namedDecl(hasName("cast"))), callee(namedDecl(hasName("dyn_cast")).bind("dyn_cast"))))) .bind("call")))); auto Any = anyOf( has(declStmt(containsDeclaration( 0, varDecl(hasInitializer( callExpr(allOf(unless(isMacroID()), unless(cxxMemberCallExpr()), callee(namedDecl(hasName("cast"))))) .bind("assign")))))), Condition); auto CallExpression = callExpr( allOf( unless(isMacroID()), unless(cxxMemberCallExpr()), allOf(callee(namedDecl(hasAnyName("isa", "cast", "cast_or_null", "dyn_cast", "dyn_cast_or_null")) .bind("func")), hasArgument(0, anyOf(declRefExpr().bind("arg"), cxxMemberCallExpr().bind("arg")))))) .bind("rhs"); Finder->addMatcher( traverse(ast_type_traits::TK_AsIs, stmt(anyOf( ifStmt(Any), whileStmt(Any), doStmt(Condition), binaryOperator( allOf(unless(isExpansionInFileMatching( "llvm/include/llvm/Support/Casting.h")), hasOperatorName("&&"), hasLHS(implicitCastExpr().bind("lhs")), hasRHS(anyOf(implicitCastExpr(has(CallExpression)), CallExpression)))) .bind("and")))), this); } void PreferIsaOrDynCastInConditionalsCheck::check( const MatchFinder::MatchResult &Result) { if (const auto *MatchedDecl = Result.Nodes.getNodeAs("assign")) { SourceLocation StartLoc = MatchedDecl->getCallee()->getExprLoc(); SourceLocation EndLoc = StartLoc.getLocWithOffset(StringRef("cast").size() - 1); diag(MatchedDecl->getBeginLoc(), "cast<> in conditional will assert rather than return a null pointer") << FixItHint::CreateReplacement(SourceRange(StartLoc, EndLoc), "dyn_cast"); } else if (const auto *MatchedDecl = Result.Nodes.getNodeAs("call")) { SourceLocation StartLoc = MatchedDecl->getCallee()->getExprLoc(); SourceLocation EndLoc = StartLoc.getLocWithOffset(StringRef("cast").size() - 1); StringRef Message = "cast<> in conditional will assert rather than return a null pointer"; if (Result.Nodes.getNodeAs("dyn_cast")) Message = "return value from dyn_cast<> not used"; diag(MatchedDecl->getBeginLoc(), Message) << FixItHint::CreateReplacement(SourceRange(StartLoc, EndLoc), "isa"); } else if (const auto *MatchedDecl = Result.Nodes.getNodeAs("and")) { const auto *LHS = Result.Nodes.getNodeAs("lhs"); const auto *RHS = Result.Nodes.getNodeAs("rhs"); const auto *Arg = Result.Nodes.getNodeAs("arg"); const auto *Func = Result.Nodes.getNodeAs("func"); assert(LHS && "LHS is null"); assert(RHS && "RHS is null"); assert(Arg && "Arg is null"); assert(Func && "Func is null"); StringRef LHSString(Lexer::getSourceText( CharSourceRange::getTokenRange(LHS->getSourceRange()), *Result.SourceManager, getLangOpts())); StringRef ArgString(Lexer::getSourceText( CharSourceRange::getTokenRange(Arg->getSourceRange()), *Result.SourceManager, getLangOpts())); if (ArgString != LHSString) return; StringRef RHSString(Lexer::getSourceText( CharSourceRange::getTokenRange(RHS->getSourceRange()), *Result.SourceManager, getLangOpts())); std::string Replacement("isa_and_nonnull"); Replacement += RHSString.substr(Func->getName().size()); diag(MatchedDecl->getBeginLoc(), "isa_and_nonnull<> is preferred over an explicit test for null " "followed by calling isa<>") << FixItHint::CreateReplacement(SourceRange(MatchedDecl->getBeginLoc(), MatchedDecl->getEndLoc()), Replacement); } } } // namespace llvm_check } // namespace tidy } // namespace clang