433 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			433 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			C++
		
	
	
	
//===--- ImplicitBoolCastCheck.cpp - clang-tidy----------------------------===//
 | 
						|
//
 | 
						|
//                     The LLVM Compiler Infrastructure
 | 
						|
//
 | 
						|
// This file is distributed under the University of Illinois Open Source
 | 
						|
// License. See LICENSE.TXT for details.
 | 
						|
//
 | 
						|
//===----------------------------------------------------------------------===//
 | 
						|
 | 
						|
#include "ImplicitBoolCastCheck.h"
 | 
						|
#include "clang/AST/ASTContext.h"
 | 
						|
#include "clang/ASTMatchers/ASTMatchFinder.h"
 | 
						|
#include "clang/Lex/Lexer.h"
 | 
						|
 | 
						|
using namespace clang::ast_matchers;
 | 
						|
 | 
						|
namespace clang {
 | 
						|
namespace tidy {
 | 
						|
namespace readability {
 | 
						|
 | 
						|
namespace {
 | 
						|
 | 
						|
AST_MATCHER(Stmt, isMacroExpansion) {
 | 
						|
  SourceManager &SM = Finder->getASTContext().getSourceManager();
 | 
						|
  SourceLocation Loc = Node.getLocStart();
 | 
						|
  return SM.isMacroBodyExpansion(Loc) || SM.isMacroArgExpansion(Loc);
 | 
						|
}
 | 
						|
 | 
						|
bool isNULLMacroExpansion(const Stmt *Statement, ASTContext &Context) {
 | 
						|
  SourceManager &SM = Context.getSourceManager();
 | 
						|
  const LangOptions &LO = Context.getLangOpts();
 | 
						|
  SourceLocation Loc = Statement->getLocStart();
 | 
						|
  return SM.isMacroBodyExpansion(Loc) &&
 | 
						|
         clang::Lexer::getImmediateMacroName(Loc, SM, LO) == "NULL";
 | 
						|
}
 | 
						|
 | 
						|
AST_MATCHER(Stmt, isNULLMacroExpansion) {
 | 
						|
  return isNULLMacroExpansion(&Node, Finder->getASTContext());
 | 
						|
}
 | 
						|
 | 
						|
ast_matchers::internal::Matcher<Expr> createExceptionCasesMatcher() {
 | 
						|
  return expr(anyOf(hasParent(explicitCastExpr()),
 | 
						|
                    allOf(isMacroExpansion(), unless(isNULLMacroExpansion())),
 | 
						|
                    isInTemplateInstantiation(),
 | 
						|
                    hasAncestor(functionTemplateDecl())));
 | 
						|
}
 | 
						|
 | 
						|
StatementMatcher createImplicitCastFromBoolMatcher() {
 | 
						|
  return implicitCastExpr(
 | 
						|
      unless(createExceptionCasesMatcher()),
 | 
						|
      anyOf(hasCastKind(CK_IntegralCast), hasCastKind(CK_IntegralToFloating),
 | 
						|
            // Prior to C++11 cast from bool literal to pointer was allowed.
 | 
						|
            allOf(anyOf(hasCastKind(CK_NullToPointer),
 | 
						|
                        hasCastKind(CK_NullToMemberPointer)),
 | 
						|
                  hasSourceExpression(cxxBoolLiteral()))),
 | 
						|
      hasSourceExpression(expr(hasType(qualType(booleanType())))));
 | 
						|
}
 | 
						|
 | 
						|
StringRef
 | 
						|
getZeroLiteralToCompareWithForGivenType(CastKind CastExpressionKind,
 | 
						|
                                        QualType CastSubExpressionType,
 | 
						|
                                        ASTContext &Context) {
 | 
						|
  switch (CastExpressionKind) {
 | 
						|
  case CK_IntegralToBoolean:
 | 
						|
    return CastSubExpressionType->isUnsignedIntegerType() ? "0u" : "0";
 | 
						|
 | 
						|
  case CK_FloatingToBoolean:
 | 
						|
    return Context.hasSameType(CastSubExpressionType, Context.FloatTy) ? "0.0f"
 | 
						|
                                                                       : "0.0";
 | 
						|
 | 
						|
  case CK_PointerToBoolean:
 | 
						|
  case CK_MemberPointerToBoolean: // Fall-through on purpose.
 | 
						|
    return Context.getLangOpts().CPlusPlus11 ? "nullptr" : "0";
 | 
						|
 | 
						|
  default:
 | 
						|
    llvm_unreachable("Unexpected cast kind");
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
bool isUnaryLogicalNotOperator(const Stmt *Statement) {
 | 
						|
  const auto *UnaryOperatorExpression =
 | 
						|
      llvm::dyn_cast<UnaryOperator>(Statement);
 | 
						|
  return UnaryOperatorExpression != nullptr &&
 | 
						|
         UnaryOperatorExpression->getOpcode() == UO_LNot;
 | 
						|
}
 | 
						|
 | 
						|
bool areParensNeededForOverloadedOperator(OverloadedOperatorKind OperatorKind) {
 | 
						|
  switch (OperatorKind) {
 | 
						|
  case OO_New:
 | 
						|
  case OO_Delete: // Fall-through on purpose.
 | 
						|
  case OO_Array_New:
 | 
						|
  case OO_Array_Delete:
 | 
						|
  case OO_ArrowStar:
 | 
						|
  case OO_Arrow:
 | 
						|
  case OO_Call:
 | 
						|
  case OO_Subscript:
 | 
						|
    return false;
 | 
						|
 | 
						|
  default:
 | 
						|
    return true;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
bool areParensNeededForStatement(const Stmt *Statement) {
 | 
						|
  if (const auto *OverloadedOperatorCall =
 | 
						|
          llvm::dyn_cast<CXXOperatorCallExpr>(Statement)) {
 | 
						|
    return areParensNeededForOverloadedOperator(
 | 
						|
        OverloadedOperatorCall->getOperator());
 | 
						|
  }
 | 
						|
 | 
						|
  return llvm::isa<BinaryOperator>(Statement) ||
 | 
						|
         llvm::isa<UnaryOperator>(Statement);
 | 
						|
}
 | 
						|
 | 
						|
void addFixItHintsForGenericExpressionCastToBool(
 | 
						|
    DiagnosticBuilder &Diagnostic, const ImplicitCastExpr *CastExpression,
 | 
						|
    const Stmt *ParentStatement, ASTContext &Context) {
 | 
						|
  // In case of expressions like (! integer), we should remove the redundant not
 | 
						|
  // operator and use inverted comparison (integer == 0).
 | 
						|
  bool InvertComparison =
 | 
						|
      ParentStatement != nullptr && isUnaryLogicalNotOperator(ParentStatement);
 | 
						|
  if (InvertComparison) {
 | 
						|
    SourceLocation ParentStartLoc = ParentStatement->getLocStart();
 | 
						|
    SourceLocation ParentEndLoc =
 | 
						|
        llvm::cast<UnaryOperator>(ParentStatement)->getSubExpr()->getLocStart();
 | 
						|
    Diagnostic.AddFixItHint(FixItHint::CreateRemoval(
 | 
						|
        CharSourceRange::getCharRange(ParentStartLoc, ParentEndLoc)));
 | 
						|
 | 
						|
    auto FurtherParents = Context.getParents(*ParentStatement);
 | 
						|
    ParentStatement = FurtherParents[0].get<Stmt>();
 | 
						|
  }
 | 
						|
 | 
						|
  const Expr *SubExpression = CastExpression->getSubExpr();
 | 
						|
 | 
						|
  bool NeedInnerParens = areParensNeededForStatement(SubExpression);
 | 
						|
  bool NeedOuterParens = ParentStatement != nullptr &&
 | 
						|
                         areParensNeededForStatement(ParentStatement);
 | 
						|
 | 
						|
  std::string StartLocInsertion;
 | 
						|
 | 
						|
  if (NeedOuterParens) {
 | 
						|
    StartLocInsertion += "(";
 | 
						|
  }
 | 
						|
  if (NeedInnerParens) {
 | 
						|
    StartLocInsertion += "(";
 | 
						|
  }
 | 
						|
 | 
						|
  if (!StartLocInsertion.empty()) {
 | 
						|
    SourceLocation StartLoc = CastExpression->getLocStart();
 | 
						|
    Diagnostic.AddFixItHint(
 | 
						|
        FixItHint::CreateInsertion(StartLoc, StartLocInsertion));
 | 
						|
  }
 | 
						|
 | 
						|
  std::string EndLocInsertion;
 | 
						|
 | 
						|
  if (NeedInnerParens) {
 | 
						|
    EndLocInsertion += ")";
 | 
						|
  }
 | 
						|
 | 
						|
  if (InvertComparison) {
 | 
						|
    EndLocInsertion += " == ";
 | 
						|
  } else {
 | 
						|
    EndLocInsertion += " != ";
 | 
						|
  }
 | 
						|
 | 
						|
  EndLocInsertion += getZeroLiteralToCompareWithForGivenType(
 | 
						|
      CastExpression->getCastKind(), SubExpression->getType(), Context);
 | 
						|
 | 
						|
  if (NeedOuterParens) {
 | 
						|
    EndLocInsertion += ")";
 | 
						|
  }
 | 
						|
 | 
						|
  SourceLocation EndLoc = Lexer::getLocForEndOfToken(
 | 
						|
      CastExpression->getLocEnd(), 0, Context.getSourceManager(),
 | 
						|
      Context.getLangOpts());
 | 
						|
  Diagnostic.AddFixItHint(FixItHint::CreateInsertion(EndLoc, EndLocInsertion));
 | 
						|
}
 | 
						|
 | 
						|
StringRef getEquivalentBoolLiteralForExpression(const Expr *Expression,
 | 
						|
                                                ASTContext &Context) {
 | 
						|
  if (isNULLMacroExpansion(Expression, Context)) {
 | 
						|
    return "false";
 | 
						|
  }
 | 
						|
 | 
						|
  if (const auto *IntLit = llvm::dyn_cast<IntegerLiteral>(Expression)) {
 | 
						|
    return (IntLit->getValue() == 0) ? "false" : "true";
 | 
						|
  }
 | 
						|
 | 
						|
  if (const auto *FloatLit = llvm::dyn_cast<FloatingLiteral>(Expression)) {
 | 
						|
    llvm::APFloat FloatLitAbsValue = FloatLit->getValue();
 | 
						|
    FloatLitAbsValue.clearSign();
 | 
						|
    return (FloatLitAbsValue.bitcastToAPInt() == 0) ? "false" : "true";
 | 
						|
  }
 | 
						|
 | 
						|
  if (const auto *CharLit = llvm::dyn_cast<CharacterLiteral>(Expression)) {
 | 
						|
    return (CharLit->getValue() == 0) ? "false" : "true";
 | 
						|
  }
 | 
						|
 | 
						|
  if (llvm::isa<StringLiteral>(Expression->IgnoreCasts())) {
 | 
						|
    return "true";
 | 
						|
  }
 | 
						|
 | 
						|
  return StringRef();
 | 
						|
}
 | 
						|
 | 
						|
void addFixItHintsForLiteralCastToBool(DiagnosticBuilder &Diagnostic,
 | 
						|
                                       const ImplicitCastExpr *CastExpression,
 | 
						|
                                       StringRef EquivalentLiteralExpression) {
 | 
						|
  SourceLocation StartLoc = CastExpression->getLocStart();
 | 
						|
  SourceLocation EndLoc = CastExpression->getLocEnd();
 | 
						|
 | 
						|
  Diagnostic.AddFixItHint(FixItHint::CreateReplacement(
 | 
						|
      CharSourceRange::getTokenRange(StartLoc, EndLoc),
 | 
						|
      EquivalentLiteralExpression));
 | 
						|
}
 | 
						|
 | 
						|
void addFixItHintsForGenericExpressionCastFromBool(
 | 
						|
    DiagnosticBuilder &Diagnostic, const ImplicitCastExpr *CastExpression,
 | 
						|
    ASTContext &Context, StringRef OtherType) {
 | 
						|
  const Expr *SubExpression = CastExpression->getSubExpr();
 | 
						|
  bool NeedParens = !llvm::isa<ParenExpr>(SubExpression);
 | 
						|
 | 
						|
  std::string StartLocInsertion = "static_cast<";
 | 
						|
  StartLocInsertion += OtherType.str();
 | 
						|
  StartLocInsertion += ">";
 | 
						|
  if (NeedParens) {
 | 
						|
    StartLocInsertion += "(";
 | 
						|
  }
 | 
						|
 | 
						|
  SourceLocation StartLoc = CastExpression->getLocStart();
 | 
						|
  Diagnostic.AddFixItHint(
 | 
						|
      FixItHint::CreateInsertion(StartLoc, StartLocInsertion));
 | 
						|
 | 
						|
  if (NeedParens) {
 | 
						|
    SourceLocation EndLoc = Lexer::getLocForEndOfToken(
 | 
						|
        CastExpression->getLocEnd(), 0, Context.getSourceManager(),
 | 
						|
        Context.getLangOpts());
 | 
						|
 | 
						|
    Diagnostic.AddFixItHint(FixItHint::CreateInsertion(EndLoc, ")"));
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
StringRef getEquivalentLiteralForBoolLiteral(
 | 
						|
    const CXXBoolLiteralExpr *BoolLiteralExpression, QualType DestinationType,
 | 
						|
    ASTContext &Context) {
 | 
						|
  // Prior to C++11, false literal could be implicitly converted to pointer.
 | 
						|
  if (!Context.getLangOpts().CPlusPlus11 &&
 | 
						|
      (DestinationType->isPointerType() ||
 | 
						|
       DestinationType->isMemberPointerType()) &&
 | 
						|
      BoolLiteralExpression->getValue() == false) {
 | 
						|
    return "0";
 | 
						|
  }
 | 
						|
 | 
						|
  if (DestinationType->isFloatingType()) {
 | 
						|
    if (BoolLiteralExpression->getValue() == true) {
 | 
						|
      return Context.hasSameType(DestinationType, Context.FloatTy) ? "1.0f"
 | 
						|
                                                                   : "1.0";
 | 
						|
    }
 | 
						|
    return Context.hasSameType(DestinationType, Context.FloatTy) ? "0.0f"
 | 
						|
                                                                 : "0.0";
 | 
						|
  }
 | 
						|
 | 
						|
  if (BoolLiteralExpression->getValue() == true) {
 | 
						|
    return DestinationType->isUnsignedIntegerType() ? "1u" : "1";
 | 
						|
  }
 | 
						|
  return DestinationType->isUnsignedIntegerType() ? "0u" : "0";
 | 
						|
}
 | 
						|
 | 
						|
void addFixItHintsForLiteralCastFromBool(DiagnosticBuilder &Diagnostic,
 | 
						|
                                         const ImplicitCastExpr *CastExpression,
 | 
						|
                                         ASTContext &Context,
 | 
						|
                                         QualType DestinationType) {
 | 
						|
  SourceLocation StartLoc = CastExpression->getLocStart();
 | 
						|
  SourceLocation EndLoc = CastExpression->getLocEnd();
 | 
						|
  const auto *BoolLiteralExpression =
 | 
						|
      llvm::dyn_cast<CXXBoolLiteralExpr>(CastExpression->getSubExpr());
 | 
						|
 | 
						|
  Diagnostic.AddFixItHint(FixItHint::CreateReplacement(
 | 
						|
      CharSourceRange::getTokenRange(StartLoc, EndLoc),
 | 
						|
      getEquivalentLiteralForBoolLiteral(BoolLiteralExpression, DestinationType,
 | 
						|
                                         Context)));
 | 
						|
}
 | 
						|
 | 
						|
StatementMatcher createConditionalExpressionMatcher() {
 | 
						|
  return stmt(anyOf(ifStmt(), conditionalOperator(),
 | 
						|
                    parenExpr(hasParent(conditionalOperator()))));
 | 
						|
}
 | 
						|
 | 
						|
bool isAllowedConditionalCast(const ImplicitCastExpr *CastExpression,
 | 
						|
                              ASTContext &Context) {
 | 
						|
  auto AllowedConditionalMatcher = stmt(hasParent(stmt(
 | 
						|
      anyOf(createConditionalExpressionMatcher(),
 | 
						|
            unaryOperator(hasOperatorName("!"),
 | 
						|
                          hasParent(createConditionalExpressionMatcher()))))));
 | 
						|
 | 
						|
  auto MatchResult = match(AllowedConditionalMatcher, *CastExpression, Context);
 | 
						|
  return !MatchResult.empty();
 | 
						|
}
 | 
						|
 | 
						|
} // anonymous namespace
 | 
						|
 | 
						|
ImplicitBoolCastCheck::ImplicitBoolCastCheck(StringRef Name,
 | 
						|
                                             ClangTidyContext *Context)
 | 
						|
    : ClangTidyCheck(Name, Context),
 | 
						|
      AllowConditionalIntegerCasts(
 | 
						|
          Options.get("AllowConditionalIntegerCasts", false)),
 | 
						|
      AllowConditionalPointerCasts(
 | 
						|
          Options.get("AllowConditionalPointerCasts", false)) {}
 | 
						|
 | 
						|
void ImplicitBoolCastCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
 | 
						|
  Options.store(Opts, "AllowConditionalIntegerCasts",
 | 
						|
                AllowConditionalIntegerCasts);
 | 
						|
  Options.store(Opts, "AllowConditionalPointerCasts",
 | 
						|
                AllowConditionalPointerCasts);
 | 
						|
}
 | 
						|
 | 
						|
void ImplicitBoolCastCheck::registerMatchers(MatchFinder *Finder) {
 | 
						|
  // This check doesn't make much sense if we run it on language without
 | 
						|
  // built-in bool support.
 | 
						|
  if (!getLangOpts().Bool) {
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  Finder->addMatcher(
 | 
						|
      implicitCastExpr(
 | 
						|
          // Exclude cases common to implicit cast to and from bool.
 | 
						|
          unless(createExceptionCasesMatcher()),
 | 
						|
          // Exclude case of using if or while statements with variable
 | 
						|
          // declaration, e.g.:
 | 
						|
          //   if (int var = functionCall()) {}
 | 
						|
          unless(
 | 
						|
              hasParent(stmt(anyOf(ifStmt(), whileStmt()), has(declStmt())))),
 | 
						|
          anyOf(hasCastKind(CK_IntegralToBoolean),
 | 
						|
                hasCastKind(CK_FloatingToBoolean),
 | 
						|
                hasCastKind(CK_PointerToBoolean),
 | 
						|
                hasCastKind(CK_MemberPointerToBoolean)),
 | 
						|
          // Retrive also parent statement, to check if we need additional
 | 
						|
          // parens in replacement.
 | 
						|
          anyOf(hasParent(stmt().bind("parentStmt")), anything()))
 | 
						|
          .bind("implicitCastToBool"),
 | 
						|
      this);
 | 
						|
 | 
						|
  Finder->addMatcher(
 | 
						|
      implicitCastExpr(
 | 
						|
          createImplicitCastFromBoolMatcher(),
 | 
						|
          // Exclude comparisons of bools, as they are always cast to integers
 | 
						|
          // in such context:
 | 
						|
          //   bool_expr_a == bool_expr_b
 | 
						|
          //   bool_expr_a != bool_expr_b
 | 
						|
          unless(hasParent(binaryOperator(
 | 
						|
              anyOf(hasOperatorName("=="), hasOperatorName("!=")),
 | 
						|
              hasLHS(createImplicitCastFromBoolMatcher()),
 | 
						|
              hasRHS(createImplicitCastFromBoolMatcher())))),
 | 
						|
          // Check also for nested casts, for example: bool -> int -> float.
 | 
						|
          anyOf(hasParent(implicitCastExpr().bind("furtherImplicitCast")),
 | 
						|
                anything()))
 | 
						|
          .bind("implicitCastFromBool"),
 | 
						|
      this);
 | 
						|
}
 | 
						|
 | 
						|
void ImplicitBoolCastCheck::check(const MatchFinder::MatchResult &Result) {
 | 
						|
  if (const auto *CastToBool =
 | 
						|
          Result.Nodes.getNodeAs<ImplicitCastExpr>("implicitCastToBool")) {
 | 
						|
    const auto *ParentStatement = Result.Nodes.getNodeAs<Stmt>("parentStmt");
 | 
						|
    return handleCastToBool(CastToBool, ParentStatement, *Result.Context);
 | 
						|
  }
 | 
						|
 | 
						|
  if (const auto *CastFromBool =
 | 
						|
          Result.Nodes.getNodeAs<ImplicitCastExpr>("implicitCastFromBool")) {
 | 
						|
    const auto *FurtherImplicitCastExpression =
 | 
						|
        Result.Nodes.getNodeAs<ImplicitCastExpr>("furtherImplicitCast");
 | 
						|
    return handleCastFromBool(CastFromBool, FurtherImplicitCastExpression,
 | 
						|
                              *Result.Context);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
void ImplicitBoolCastCheck::handleCastToBool(
 | 
						|
    const ImplicitCastExpr *CastExpression, const Stmt *ParentStatement,
 | 
						|
    ASTContext &Context) {
 | 
						|
  if (AllowConditionalPointerCasts &&
 | 
						|
      (CastExpression->getCastKind() == CK_PointerToBoolean ||
 | 
						|
       CastExpression->getCastKind() == CK_MemberPointerToBoolean) &&
 | 
						|
      isAllowedConditionalCast(CastExpression, Context)) {
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  if (AllowConditionalIntegerCasts &&
 | 
						|
      CastExpression->getCastKind() == CK_IntegralToBoolean &&
 | 
						|
      isAllowedConditionalCast(CastExpression, Context)) {
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  std::string OtherType = CastExpression->getSubExpr()->getType().getAsString();
 | 
						|
  DiagnosticBuilder Diagnostic =
 | 
						|
      diag(CastExpression->getLocStart(), "implicit cast '%0' -> bool")
 | 
						|
      << OtherType;
 | 
						|
 | 
						|
  StringRef EquivalentLiteralExpression = getEquivalentBoolLiteralForExpression(
 | 
						|
      CastExpression->getSubExpr(), Context);
 | 
						|
  if (!EquivalentLiteralExpression.empty()) {
 | 
						|
    addFixItHintsForLiteralCastToBool(Diagnostic, CastExpression,
 | 
						|
                                      EquivalentLiteralExpression);
 | 
						|
  } else {
 | 
						|
    addFixItHintsForGenericExpressionCastToBool(Diagnostic, CastExpression,
 | 
						|
                                                ParentStatement, Context);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
void ImplicitBoolCastCheck::handleCastFromBool(
 | 
						|
    const ImplicitCastExpr *CastExpression,
 | 
						|
    const ImplicitCastExpr *FurtherImplicitCastExpression,
 | 
						|
    ASTContext &Context) {
 | 
						|
  QualType DestinationType = (FurtherImplicitCastExpression != nullptr)
 | 
						|
                                 ? FurtherImplicitCastExpression->getType()
 | 
						|
                                 : CastExpression->getType();
 | 
						|
  std::string DestinationTypeString = DestinationType.getAsString();
 | 
						|
  DiagnosticBuilder Diagnostic =
 | 
						|
      diag(CastExpression->getLocStart(), "implicit cast bool -> '%0'")
 | 
						|
      << DestinationTypeString;
 | 
						|
 | 
						|
  if (llvm::isa<CXXBoolLiteralExpr>(CastExpression->getSubExpr())) {
 | 
						|
    addFixItHintsForLiteralCastFromBool(Diagnostic, CastExpression, Context,
 | 
						|
                                        DestinationType);
 | 
						|
  } else {
 | 
						|
    addFixItHintsForGenericExpressionCastFromBool(
 | 
						|
        Diagnostic, CastExpression, Context, DestinationTypeString);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
} // namespace readability
 | 
						|
} // namespace tidy
 | 
						|
} // namespace clang
 |