brintos

brintos / llvm-project-archived public Read only

0
0
Text · 16.4 KiB · c135b42 Raw
433 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 "ImplicitBoolConversionCheck.h"10#include "../utils/FixItHintUtils.h"11#include "clang/AST/ASTContext.h"12#include "clang/ASTMatchers/ASTMatchFinder.h"13#include "clang/ASTMatchers/ASTMatchers.h"14#include "clang/Lex/Lexer.h"15#include "clang/Tooling/FixIt.h"16#include <queue>17 18using namespace clang::ast_matchers;19 20namespace clang::tidy::readability {21 22namespace {23 24AST_MATCHER(Stmt, isMacroExpansion) {25  const SourceManager &SM = Finder->getASTContext().getSourceManager();26  const SourceLocation Loc = Node.getBeginLoc();27  return SM.isMacroBodyExpansion(Loc) || SM.isMacroArgExpansion(Loc);28}29 30AST_MATCHER(Stmt, isC23) { return Finder->getASTContext().getLangOpts().C23; }31 32// Preserve same name as AST_MATCHER(isNULLMacroExpansion)33// NOLINTNEXTLINE(llvm-prefer-static-over-anonymous-namespace)34bool isNULLMacroExpansion(const Stmt *Statement, ASTContext &Context) {35  const SourceManager &SM = Context.getSourceManager();36  const LangOptions &LO = Context.getLangOpts();37  const SourceLocation Loc = Statement->getBeginLoc();38  return SM.isMacroBodyExpansion(Loc) &&39         Lexer::getImmediateMacroName(Loc, SM, LO) == "NULL";40}41 42AST_MATCHER(Stmt, isNULLMacroExpansion) {43  return isNULLMacroExpansion(&Node, Finder->getASTContext());44}45 46} // namespace47 48static StringRef getZeroLiteralToCompareWithForType(CastKind CastExprKind,49                                                    QualType Type,50                                                    ASTContext &Context) {51  switch (CastExprKind) {52  case CK_IntegralToBoolean:53    return Type->isUnsignedIntegerType() ? "0u" : "0";54 55  case CK_FloatingToBoolean:56    return ASTContext::hasSameType(Type, Context.FloatTy) ? "0.0f" : "0.0";57 58  case CK_PointerToBoolean:59  case CK_MemberPointerToBoolean: // Fall-through on purpose.60    return (Context.getLangOpts().CPlusPlus11 || Context.getLangOpts().C23)61               ? "nullptr"62               : "0";63 64  default:65    llvm_unreachable("Unexpected cast kind");66  }67}68 69static bool isUnaryLogicalNotOperator(const Stmt *Statement) {70  const auto *UnaryOperatorExpr = dyn_cast<UnaryOperator>(Statement);71  return UnaryOperatorExpr && UnaryOperatorExpr->getOpcode() == UO_LNot;72}73 74static void fixGenericExprCastToBool(DiagnosticBuilder &Diag,75                                     const ImplicitCastExpr *Cast,76                                     const Stmt *Parent, ASTContext &Context,77                                     bool UseUpperCaseLiteralSuffix) {78  // In case of expressions like (! integer), we should remove the redundant not79  // operator and use inverted comparison (integer == 0).80  const bool InvertComparison =81      Parent != nullptr && isUnaryLogicalNotOperator(Parent);82  if (InvertComparison) {83    const SourceLocation ParentStartLoc = Parent->getBeginLoc();84    const SourceLocation ParentEndLoc =85        cast<UnaryOperator>(Parent)->getSubExpr()->getBeginLoc();86    Diag << FixItHint::CreateRemoval(87        CharSourceRange::getCharRange(ParentStartLoc, ParentEndLoc));88 89    Parent = Context.getParents(*Parent)[0].get<Stmt>();90  }91 92  const Expr *SubExpr = Cast->getSubExpr();93 94  const bool NeedInnerParens =95      utils::fixit::areParensNeededForStatement(*SubExpr->IgnoreImpCasts());96  const bool NeedOuterParens =97      Parent != nullptr && utils::fixit::areParensNeededForStatement(*Parent);98 99  std::string StartLocInsertion;100 101  if (NeedOuterParens) {102    StartLocInsertion += "(";103  }104  if (NeedInnerParens) {105    StartLocInsertion += "(";106  }107 108  if (!StartLocInsertion.empty()) {109    Diag << FixItHint::CreateInsertion(Cast->getBeginLoc(), StartLocInsertion);110  }111 112  std::string EndLocInsertion;113 114  if (NeedInnerParens) {115    EndLocInsertion += ")";116  }117 118  if (InvertComparison) {119    EndLocInsertion += " == ";120  } else {121    EndLocInsertion += " != ";122  }123 124  const StringRef ZeroLiteral = getZeroLiteralToCompareWithForType(125      Cast->getCastKind(), SubExpr->getType(), Context);126 127  if (UseUpperCaseLiteralSuffix)128    EndLocInsertion += ZeroLiteral.upper();129  else130    EndLocInsertion += ZeroLiteral;131 132  if (NeedOuterParens) {133    EndLocInsertion += ")";134  }135 136  const SourceLocation EndLoc = Lexer::getLocForEndOfToken(137      Cast->getEndLoc(), 0, Context.getSourceManager(), Context.getLangOpts());138  Diag << FixItHint::CreateInsertion(EndLoc, EndLocInsertion);139}140 141static StringRef getEquivalentBoolLiteralForExpr(const Expr *Expression,142                                                 ASTContext &Context) {143  if (isNULLMacroExpansion(Expression, Context)) {144    return "false";145  }146 147  if (const auto *IntLit =148          dyn_cast<IntegerLiteral>(Expression->IgnoreParens())) {149    return (IntLit->getValue() == 0) ? "false" : "true";150  }151 152  if (const auto *FloatLit = dyn_cast<FloatingLiteral>(Expression)) {153    llvm::APFloat FloatLitAbsValue = FloatLit->getValue();154    FloatLitAbsValue.clearSign();155    return (FloatLitAbsValue.bitcastToAPInt() == 0) ? "false" : "true";156  }157 158  if (const auto *CharLit = dyn_cast<CharacterLiteral>(Expression)) {159    return (CharLit->getValue() == 0) ? "false" : "true";160  }161 162  if (isa<StringLiteral>(Expression->IgnoreCasts())) {163    return "true";164  }165 166  return {};167}168 169static bool needsSpacePrefix(SourceLocation Loc, ASTContext &Context) {170  const SourceRange PrefixRange(Loc.getLocWithOffset(-1), Loc);171  const StringRef SpaceBeforeStmtStr = Lexer::getSourceText(172      CharSourceRange::getCharRange(PrefixRange), Context.getSourceManager(),173      Context.getLangOpts(), nullptr);174  if (SpaceBeforeStmtStr.empty())175    return true;176 177  const StringRef AllowedCharacters(" \t\n\v\f\r(){}[]<>;,+=-|&~!^*/");178  return !AllowedCharacters.contains(SpaceBeforeStmtStr.back());179}180 181static void fixGenericExprCastFromBool(DiagnosticBuilder &Diag,182                                       const ImplicitCastExpr *Cast,183                                       ASTContext &Context,184                                       StringRef OtherType) {185  if (!Context.getLangOpts().CPlusPlus) {186    Diag << FixItHint::CreateInsertion(Cast->getBeginLoc(),187                                       (Twine("(") + OtherType + ")").str());188    return;189  }190 191  const Expr *SubExpr = Cast->getSubExpr();192  const bool NeedParens = !isa<ParenExpr>(SubExpr->IgnoreImplicit());193  const bool NeedSpace = needsSpacePrefix(Cast->getBeginLoc(), Context);194 195  Diag << FixItHint::CreateInsertion(196      Cast->getBeginLoc(), (Twine() + (NeedSpace ? " " : "") + "static_cast<" +197                            OtherType + ">" + (NeedParens ? "(" : ""))198                               .str());199 200  if (NeedParens) {201    const SourceLocation EndLoc = Lexer::getLocForEndOfToken(202        Cast->getEndLoc(), 0, Context.getSourceManager(),203        Context.getLangOpts());204 205    Diag << FixItHint::CreateInsertion(EndLoc, ")");206  }207}208 209static StringRef210getEquivalentForBoolLiteral(const CXXBoolLiteralExpr *BoolLiteral,211                            QualType DestType, ASTContext &Context) {212  // Prior to C++11, false literal could be implicitly converted to pointer.213  if (!Context.getLangOpts().CPlusPlus11 &&214      (DestType->isPointerType() || DestType->isMemberPointerType()) &&215      BoolLiteral->getValue() == false) {216    return "0";217  }218 219  if (DestType->isFloatingType()) {220    if (ASTContext::hasSameType(DestType, Context.FloatTy)) {221      return BoolLiteral->getValue() ? "1.0f" : "0.0f";222    }223    return BoolLiteral->getValue() ? "1.0" : "0.0";224  }225 226  if (DestType->isUnsignedIntegerType()) {227    return BoolLiteral->getValue() ? "1u" : "0u";228  }229  return BoolLiteral->getValue() ? "1" : "0";230}231 232static bool isCastAllowedInCondition(const ImplicitCastExpr *Cast,233                                     ASTContext &Context) {234  std::queue<const Stmt *> Q;235  Q.push(Cast);236 237  const TraversalKindScope RAII(Context, TK_AsIs);238 239  while (!Q.empty()) {240    for (const auto &N : Context.getParents(*Q.front())) {241      const Stmt *S = N.get<Stmt>();242      if (!S)243        return false;244      if (isa<IfStmt>(S) || isa<ConditionalOperator>(S) || isa<ForStmt>(S) ||245          isa<WhileStmt>(S) || isa<DoStmt>(S) ||246          isa<BinaryConditionalOperator>(S))247        return true;248      if (isa<ParenExpr>(S) || isa<ImplicitCastExpr>(S) ||249          isUnaryLogicalNotOperator(S) ||250          (isa<BinaryOperator>(S) && cast<BinaryOperator>(S)->isLogicalOp())) {251        Q.push(S);252      } else {253        return false;254      }255    }256    Q.pop();257  }258  return false;259}260 261ImplicitBoolConversionCheck::ImplicitBoolConversionCheck(262    StringRef Name, ClangTidyContext *Context)263    : ClangTidyCheck(Name, Context),264      AllowIntegerConditions(Options.get("AllowIntegerConditions", false)),265      AllowPointerConditions(Options.get("AllowPointerConditions", false)),266      UseUpperCaseLiteralSuffix(267          Options.get("UseUpperCaseLiteralSuffix", false)) {}268 269void ImplicitBoolConversionCheck::storeOptions(270    ClangTidyOptions::OptionMap &Opts) {271  Options.store(Opts, "AllowIntegerConditions", AllowIntegerConditions);272  Options.store(Opts, "AllowPointerConditions", AllowPointerConditions);273  Options.store(Opts, "UseUpperCaseLiteralSuffix", UseUpperCaseLiteralSuffix);274}275 276void ImplicitBoolConversionCheck::registerMatchers(MatchFinder *Finder) {277  auto ExceptionCases =278      expr(anyOf(allOf(isMacroExpansion(), unless(isNULLMacroExpansion())),279                 has(ignoringImplicit(280                     memberExpr(hasDeclaration(fieldDecl(hasBitWidth(1)))))),281                 hasParent(explicitCastExpr()),282                 expr(hasType(qualType().bind("type")),283                      hasParent(initListExpr(hasParent(explicitCastExpr(284                          hasType(qualType(equalsBoundNode("type"))))))))));285  auto ImplicitCastFromBool = implicitCastExpr(286      anyOf(hasCastKind(CK_IntegralCast), hasCastKind(CK_IntegralToFloating),287            // Prior to C++11 cast from bool literal to pointer was allowed.288            allOf(anyOf(hasCastKind(CK_NullToPointer),289                        hasCastKind(CK_NullToMemberPointer)),290                  hasSourceExpression(cxxBoolLiteral()))),291      hasSourceExpression(expr(hasType(booleanType()))));292  auto BoolXor =293      binaryOperator(hasOperatorName("^"), hasLHS(ImplicitCastFromBool),294                     hasRHS(ImplicitCastFromBool));295  auto ComparisonInCall = allOf(296      hasParent(callExpr()),297      hasSourceExpression(binaryOperator(hasAnyOperatorName("==", "!="))));298 299  auto IsInCompilerGeneratedFunction = hasAncestor(namedDecl(anyOf(300      isImplicit(), functionDecl(isDefaulted()), functionTemplateDecl())));301 302  Finder->addMatcher(303      traverse(TK_AsIs,304               implicitCastExpr(305                   anyOf(hasCastKind(CK_IntegralToBoolean),306                         hasCastKind(CK_FloatingToBoolean),307                         hasCastKind(CK_PointerToBoolean),308                         hasCastKind(CK_MemberPointerToBoolean)),309                   // Exclude cases of C23 comparison result.310                   unless(allOf(isC23(),311                                hasSourceExpression(ignoringParens(312                                    binaryOperator(hasAnyOperatorName(313                                        ">", ">=", "==", "!=", "<", "<=")))))),314                   // Exclude case of using if or while statements with variable315                   // declaration, e.g.:316                   //   if (int var = functionCall()) {}317                   unless(hasParent(318                       stmt(anyOf(ifStmt(), whileStmt()), has(declStmt())))),319                   // Exclude cases common to implicit cast to and from bool.320                   unless(ExceptionCases), unless(has(BoolXor)),321                   // Exclude C23 cases common to implicit cast to bool.322                   unless(ComparisonInCall),323                   // Retrieve also parent statement, to check if we need324                   // additional parens in replacement.325                   optionally(hasParent(stmt().bind("parentStmt"))),326                   unless(isInTemplateInstantiation()),327                   unless(IsInCompilerGeneratedFunction))328                   .bind("implicitCastToBool")),329      this);330 331  auto BoolComparison = binaryOperator(hasAnyOperatorName("==", "!="),332                                       hasLHS(ImplicitCastFromBool),333                                       hasRHS(ImplicitCastFromBool));334  auto BoolOpAssignment = binaryOperator(hasAnyOperatorName("|=", "&="),335                                         hasLHS(expr(hasType(booleanType()))));336  auto BitfieldAssignment = binaryOperator(337      hasLHS(memberExpr(hasDeclaration(fieldDecl(hasBitWidth(1))))));338  auto BitfieldConstruct = cxxConstructorDecl(hasDescendant(cxxCtorInitializer(339      withInitializer(equalsBoundNode("implicitCastFromBool")),340      forField(hasBitWidth(1)))));341  Finder->addMatcher(342      traverse(343          TK_AsIs,344          implicitCastExpr(345              ImplicitCastFromBool, unless(ExceptionCases),346              // Exclude comparisons of bools, as they are always cast to347              // integers in such context:348              //   bool_expr_a == bool_expr_b349              //   bool_expr_a != bool_expr_b350              unless(hasParent(351                  binaryOperator(anyOf(BoolComparison, BoolXor,352                                       BoolOpAssignment, BitfieldAssignment)))),353              implicitCastExpr().bind("implicitCastFromBool"),354              unless(hasParent(BitfieldConstruct)),355              // Check also for nested casts, for example: bool -> int -> float.356              optionally(357                  hasParent(implicitCastExpr().bind("furtherImplicitCast"))),358              unless(isInTemplateInstantiation()),359              unless(IsInCompilerGeneratedFunction))),360      this);361}362 363void ImplicitBoolConversionCheck::check(364    const MatchFinder::MatchResult &Result) {365  if (const auto *CastToBool =366          Result.Nodes.getNodeAs<ImplicitCastExpr>("implicitCastToBool")) {367    const auto *Parent = Result.Nodes.getNodeAs<Stmt>("parentStmt");368    handleCastToBool(CastToBool, Parent, *Result.Context);369    return;370  }371 372  if (const auto *CastFromBool =373          Result.Nodes.getNodeAs<ImplicitCastExpr>("implicitCastFromBool")) {374    const auto *NextImplicitCast =375        Result.Nodes.getNodeAs<ImplicitCastExpr>("furtherImplicitCast");376    handleCastFromBool(CastFromBool, NextImplicitCast, *Result.Context);377  }378}379 380void ImplicitBoolConversionCheck::handleCastToBool(const ImplicitCastExpr *Cast,381                                                   const Stmt *Parent,382                                                   ASTContext &Context) {383  if (AllowPointerConditions &&384      (Cast->getCastKind() == CK_PointerToBoolean ||385       Cast->getCastKind() == CK_MemberPointerToBoolean) &&386      isCastAllowedInCondition(Cast, Context)) {387    return;388  }389 390  if (AllowIntegerConditions && Cast->getCastKind() == CK_IntegralToBoolean &&391      isCastAllowedInCondition(Cast, Context)) {392    return;393  }394 395  auto Diag = diag(Cast->getBeginLoc(), "implicit conversion %0 -> 'bool'")396              << Cast->getSubExpr()->getType();397 398  const StringRef EquivalentLiteral =399      getEquivalentBoolLiteralForExpr(Cast->getSubExpr(), Context);400  if (!EquivalentLiteral.empty()) {401    Diag << tooling::fixit::createReplacement(*Cast, EquivalentLiteral);402  } else {403    fixGenericExprCastToBool(Diag, Cast, Parent, Context,404                             UseUpperCaseLiteralSuffix);405  }406}407 408void ImplicitBoolConversionCheck::handleCastFromBool(409    const ImplicitCastExpr *Cast, const ImplicitCastExpr *NextImplicitCast,410    ASTContext &Context) {411  const QualType DestType =412      NextImplicitCast ? NextImplicitCast->getType() : Cast->getType();413  auto Diag = diag(Cast->getBeginLoc(), "implicit conversion 'bool' -> %0")414              << DestType;415 416  if (const auto *BoolLiteral =417          dyn_cast<CXXBoolLiteralExpr>(Cast->getSubExpr()->IgnoreParens())) {418    const auto EquivalentForBoolLiteral =419        getEquivalentForBoolLiteral(BoolLiteral, DestType, Context);420    if (UseUpperCaseLiteralSuffix)421      Diag << tooling::fixit::createReplacement(422          *Cast, EquivalentForBoolLiteral.upper());423    else424      Diag << tooling::fixit::createReplacement(*Cast,425                                                EquivalentForBoolLiteral);426 427  } else {428    fixGenericExprCastFromBool(Diag, Cast, Context, DestType.getAsString());429  }430}431 432} // namespace clang::tidy::readability433