Mercurial > hg > CbC > CbC_llvm
view clang-tools-extra/clang-tidy/readability/ImplicitBoolConversionCheck.cpp @ 236:c4bab56944e8 llvm-original
LLVM 16
author | kono |
---|---|
date | Wed, 09 Nov 2022 17:45:10 +0900 |
parents | 79ff65ed7e25 |
children | 1f2b6ac9f198 |
line wrap: on
line source
//===--- ImplicitBoolConversionCheck.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 "ImplicitBoolConversionCheck.h" #include "clang/AST/ASTContext.h" #include "clang/ASTMatchers/ASTMatchFinder.h" #include "clang/Lex/Lexer.h" #include "clang/Tooling/FixIt.h" #include <queue> using namespace clang::ast_matchers; namespace clang { namespace tidy { namespace readability { namespace { AST_MATCHER(Stmt, isMacroExpansion) { SourceManager &SM = Finder->getASTContext().getSourceManager(); SourceLocation Loc = Node.getBeginLoc(); 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->getBeginLoc(); return SM.isMacroBodyExpansion(Loc) && Lexer::getImmediateMacroName(Loc, SM, LO) == "NULL"; } AST_MATCHER(Stmt, isNULLMacroExpansion) { return isNULLMacroExpansion(&Node, Finder->getASTContext()); } StringRef getZeroLiteralToCompareWithForType(CastKind CastExprKind, QualType Type, ASTContext &Context) { switch (CastExprKind) { case CK_IntegralToBoolean: return Type->isUnsignedIntegerType() ? "0u" : "0"; case CK_FloatingToBoolean: return Context.hasSameType(Type, 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 *UnaryOperatorExpr = dyn_cast<UnaryOperator>(Statement); return UnaryOperatorExpr && UnaryOperatorExpr->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 *OperatorCall = dyn_cast<CXXOperatorCallExpr>(Statement)) { return areParensNeededForOverloadedOperator(OperatorCall->getOperator()); } return isa<BinaryOperator>(Statement) || isa<UnaryOperator>(Statement); } void fixGenericExprCastToBool(DiagnosticBuilder &Diag, const ImplicitCastExpr *Cast, const Stmt *Parent, ASTContext &Context) { // In case of expressions like (! integer), we should remove the redundant not // operator and use inverted comparison (integer == 0). bool InvertComparison = Parent != nullptr && isUnaryLogicalNotOperator(Parent); if (InvertComparison) { SourceLocation ParentStartLoc = Parent->getBeginLoc(); SourceLocation ParentEndLoc = cast<UnaryOperator>(Parent)->getSubExpr()->getBeginLoc(); Diag << FixItHint::CreateRemoval( CharSourceRange::getCharRange(ParentStartLoc, ParentEndLoc)); Parent = Context.getParents(*Parent)[0].get<Stmt>(); } const Expr *SubExpr = Cast->getSubExpr(); bool NeedInnerParens = areParensNeededForStatement(SubExpr); bool NeedOuterParens = Parent != nullptr && areParensNeededForStatement(Parent); std::string StartLocInsertion; if (NeedOuterParens) { StartLocInsertion += "("; } if (NeedInnerParens) { StartLocInsertion += "("; } if (!StartLocInsertion.empty()) { Diag << FixItHint::CreateInsertion(Cast->getBeginLoc(), StartLocInsertion); } std::string EndLocInsertion; if (NeedInnerParens) { EndLocInsertion += ")"; } if (InvertComparison) { EndLocInsertion += " == "; } else { EndLocInsertion += " != "; } EndLocInsertion += getZeroLiteralToCompareWithForType( Cast->getCastKind(), SubExpr->getType(), Context); if (NeedOuterParens) { EndLocInsertion += ")"; } SourceLocation EndLoc = Lexer::getLocForEndOfToken( Cast->getEndLoc(), 0, Context.getSourceManager(), Context.getLangOpts()); Diag << FixItHint::CreateInsertion(EndLoc, EndLocInsertion); } StringRef getEquivalentBoolLiteralForExpr(const Expr *Expression, ASTContext &Context) { if (isNULLMacroExpansion(Expression, Context)) { return "false"; } if (const auto *IntLit = dyn_cast<IntegerLiteral>(Expression)) { return (IntLit->getValue() == 0) ? "false" : "true"; } if (const auto *FloatLit = dyn_cast<FloatingLiteral>(Expression)) { llvm::APFloat FloatLitAbsValue = FloatLit->getValue(); FloatLitAbsValue.clearSign(); return (FloatLitAbsValue.bitcastToAPInt() == 0) ? "false" : "true"; } if (const auto *CharLit = dyn_cast<CharacterLiteral>(Expression)) { return (CharLit->getValue() == 0) ? "false" : "true"; } if (isa<StringLiteral>(Expression->IgnoreCasts())) { return "true"; } return StringRef(); } void fixGenericExprCastFromBool(DiagnosticBuilder &Diag, const ImplicitCastExpr *Cast, ASTContext &Context, StringRef OtherType) { const Expr *SubExpr = Cast->getSubExpr(); bool NeedParens = !isa<ParenExpr>(SubExpr); Diag << FixItHint::CreateInsertion( Cast->getBeginLoc(), (Twine("static_cast<") + OtherType + ">" + (NeedParens ? "(" : "")) .str()); if (NeedParens) { SourceLocation EndLoc = Lexer::getLocForEndOfToken( Cast->getEndLoc(), 0, Context.getSourceManager(), Context.getLangOpts()); Diag << FixItHint::CreateInsertion(EndLoc, ")"); } } StringRef getEquivalentForBoolLiteral(const CXXBoolLiteralExpr *BoolLiteral, QualType DestType, ASTContext &Context) { // Prior to C++11, false literal could be implicitly converted to pointer. if (!Context.getLangOpts().CPlusPlus11 && (DestType->isPointerType() || DestType->isMemberPointerType()) && BoolLiteral->getValue() == false) { return "0"; } if (DestType->isFloatingType()) { if (Context.hasSameType(DestType, Context.FloatTy)) { return BoolLiteral->getValue() ? "1.0f" : "0.0f"; } return BoolLiteral->getValue() ? "1.0" : "0.0"; } if (DestType->isUnsignedIntegerType()) { return BoolLiteral->getValue() ? "1u" : "0u"; } return BoolLiteral->getValue() ? "1" : "0"; } bool isCastAllowedInCondition(const ImplicitCastExpr *Cast, ASTContext &Context) { std::queue<const Stmt *> Q; Q.push(Cast); TraversalKindScope RAII(Context, TK_AsIs); while (!Q.empty()) { for (const auto &N : Context.getParents(*Q.front())) { const Stmt *S = N.get<Stmt>(); if (!S) return false; if (isa<IfStmt>(S) || isa<ConditionalOperator>(S) || isa<ForStmt>(S) || isa<WhileStmt>(S) || isa<BinaryConditionalOperator>(S)) return true; if (isa<ParenExpr>(S) || isa<ImplicitCastExpr>(S) || isUnaryLogicalNotOperator(S) || (isa<BinaryOperator>(S) && cast<BinaryOperator>(S)->isLogicalOp())) { Q.push(S); } else { return false; } } Q.pop(); } return false; } } // anonymous namespace ImplicitBoolConversionCheck::ImplicitBoolConversionCheck( StringRef Name, ClangTidyContext *Context) : ClangTidyCheck(Name, Context), AllowIntegerConditions(Options.get("AllowIntegerConditions", false)), AllowPointerConditions(Options.get("AllowPointerConditions", false)) {} void ImplicitBoolConversionCheck::storeOptions( ClangTidyOptions::OptionMap &Opts) { Options.store(Opts, "AllowIntegerConditions", AllowIntegerConditions); Options.store(Opts, "AllowPointerConditions", AllowPointerConditions); } void ImplicitBoolConversionCheck::registerMatchers(MatchFinder *Finder) { auto ExceptionCases = expr(anyOf(allOf(isMacroExpansion(), unless(isNULLMacroExpansion())), has(ignoringImplicit( memberExpr(hasDeclaration(fieldDecl(hasBitWidth(1)))))), hasParent(explicitCastExpr()))); auto ImplicitCastFromBool = implicitCastExpr( 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(booleanType()))), unless(ExceptionCases)); auto BoolXor = binaryOperator(hasOperatorName("^"), hasLHS(ImplicitCastFromBool), hasRHS(ImplicitCastFromBool)); Finder->addMatcher( traverse(TK_AsIs, implicitCastExpr( anyOf(hasCastKind(CK_IntegralToBoolean), hasCastKind(CK_FloatingToBoolean), hasCastKind(CK_PointerToBoolean), hasCastKind(CK_MemberPointerToBoolean)), // 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())))), // Exclude cases common to implicit cast to and from bool. unless(ExceptionCases), unless(has(BoolXor)), // Retrieve also parent statement, to check if we need // additional parens in replacement. anyOf(hasParent(stmt().bind("parentStmt")), anything()), unless(isInTemplateInstantiation()), unless(hasAncestor(functionTemplateDecl()))) .bind("implicitCastToBool")), this); auto BoolComparison = binaryOperator(hasAnyOperatorName("==", "!="), hasLHS(ImplicitCastFromBool), hasRHS(ImplicitCastFromBool)); auto BoolOpAssignment = binaryOperator(hasAnyOperatorName("|=", "&="), hasLHS(expr(hasType(booleanType())))); auto BitfieldAssignment = binaryOperator( hasLHS(memberExpr(hasDeclaration(fieldDecl(hasBitWidth(1)))))); auto BitfieldConstruct = cxxConstructorDecl(hasDescendant(cxxCtorInitializer( withInitializer(equalsBoundNode("implicitCastFromBool")), forField(hasBitWidth(1))))); Finder->addMatcher( traverse( TK_AsIs, implicitCastExpr( ImplicitCastFromBool, // 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(BoolComparison, BoolXor, BoolOpAssignment, BitfieldAssignment)))), implicitCastExpr().bind("implicitCastFromBool"), unless(hasParent(BitfieldConstruct)), // Check also for nested casts, for example: bool -> int -> float. anyOf(hasParent(implicitCastExpr().bind("furtherImplicitCast")), anything()), unless(isInTemplateInstantiation()), unless(hasAncestor(functionTemplateDecl())))), this); } void ImplicitBoolConversionCheck::check( const MatchFinder::MatchResult &Result) { if (const auto *CastToBool = Result.Nodes.getNodeAs<ImplicitCastExpr>("implicitCastToBool")) { const auto *Parent = Result.Nodes.getNodeAs<Stmt>("parentStmt"); return handleCastToBool(CastToBool, Parent, *Result.Context); } if (const auto *CastFromBool = Result.Nodes.getNodeAs<ImplicitCastExpr>("implicitCastFromBool")) { const auto *NextImplicitCast = Result.Nodes.getNodeAs<ImplicitCastExpr>("furtherImplicitCast"); return handleCastFromBool(CastFromBool, NextImplicitCast, *Result.Context); } } void ImplicitBoolConversionCheck::handleCastToBool(const ImplicitCastExpr *Cast, const Stmt *Parent, ASTContext &Context) { if (AllowPointerConditions && (Cast->getCastKind() == CK_PointerToBoolean || Cast->getCastKind() == CK_MemberPointerToBoolean) && isCastAllowedInCondition(Cast, Context)) { return; } if (AllowIntegerConditions && Cast->getCastKind() == CK_IntegralToBoolean && isCastAllowedInCondition(Cast, Context)) { return; } auto Diag = diag(Cast->getBeginLoc(), "implicit conversion %0 -> bool") << Cast->getSubExpr()->getType(); StringRef EquivalentLiteral = getEquivalentBoolLiteralForExpr(Cast->getSubExpr(), Context); if (!EquivalentLiteral.empty()) { Diag << tooling::fixit::createReplacement(*Cast, EquivalentLiteral); } else { fixGenericExprCastToBool(Diag, Cast, Parent, Context); } } void ImplicitBoolConversionCheck::handleCastFromBool( const ImplicitCastExpr *Cast, const ImplicitCastExpr *NextImplicitCast, ASTContext &Context) { QualType DestType = NextImplicitCast ? NextImplicitCast->getType() : Cast->getType(); auto Diag = diag(Cast->getBeginLoc(), "implicit conversion bool -> %0") << DestType; if (const auto *BoolLiteral = dyn_cast<CXXBoolLiteralExpr>(Cast->getSubExpr())) { Diag << tooling::fixit::createReplacement( *Cast, getEquivalentForBoolLiteral(BoolLiteral, DestType, Context)); } else { fixGenericExprCastFromBool(Diag, Cast, Context, DestType.getAsString()); } } } // namespace readability } // namespace tidy } // namespace clang