https://github.com/4m4n-x-B4w4ne created https://github.com/llvm/llvm-project/pull/124319
This Pull Request is to fix issue #24841. I am also cherry-pick commit a3c7fca28faee679a59afd58c2e814025771ff63. @LegalizeAdulthood @PiotrZSL >From 581cc0eda9d42458ff71f2c913a2e03d2de0b5f2 Mon Sep 17 00:00:00 2001 From: NagaChaitanya Vellanki <pnag...@protonmail.com> Date: Fri, 19 Jul 2024 14:26:23 -0700 Subject: [PATCH 1/2] This commit is to resolve the issue #24841 --- .../clang-tidy/modernize/CMakeLists.txt | 1 + .../modernize/ModernizeTidyModule.cpp | 3 + .../modernize/UseCppStyleCommentsCheck.cpp | 126 ++++++ .../modernize/UseCppStyleCommentsCheck.h | 40 ++ .../ImplicitBoolConversionCheck.cpp | 428 ------------------ .../readability/ImplicitBoolConversionCheck.h | 44 -- clang-tools-extra/docs/ReleaseNotes.rst | 79 +++- .../modernize/use-cpp-style-comments.rst | 38 ++ .../modernize/use-cpp-style-comments.cpp | 68 +++ 9 files changed, 337 insertions(+), 490 deletions(-) create mode 100644 clang-tools-extra/clang-tidy/modernize/UseCppStyleCommentsCheck.cpp create mode 100644 clang-tools-extra/clang-tidy/modernize/UseCppStyleCommentsCheck.h delete mode 100644 clang-tools-extra/clang-tidy/readability/ImplicitBoolConversionCheck.cpp delete mode 100644 clang-tools-extra/clang-tidy/readability/ImplicitBoolConversionCheck.h create mode 100644 clang-tools-extra/docs/clang-tidy/checks/modernize/use-cpp-style-comments.rst create mode 100644 clang-tools-extra/test/clang-tidy/checkers/modernize/use-cpp-style-comments.cpp diff --git a/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt b/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt index bab1167fb15ff2..f07dd5efecc58e 100644 --- a/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt +++ b/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt @@ -31,6 +31,7 @@ add_clang_library(clangTidyModernizeModule STATIC UseAutoCheck.cpp UseBoolLiteralsCheck.cpp UseConstraintsCheck.cpp + UseCppStyleCommentsCheck.cpp UseDefaultMemberInitCheck.cpp UseDesignatedInitializersCheck.cpp UseEmplaceCheck.cpp diff --git a/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp b/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp index fc46c72982fdce..1917d562f7ce8d 100644 --- a/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp +++ b/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp @@ -32,6 +32,7 @@ #include "UseAutoCheck.h" #include "UseBoolLiteralsCheck.h" #include "UseConstraintsCheck.h" +#include "UseCppStyleCommentsCheck.h" #include "UseDefaultMemberInitCheck.h" #include "UseDesignatedInitializersCheck.h" #include "UseEmplaceCheck.h" @@ -107,6 +108,8 @@ class ModernizeModule : public ClangTidyModule { "modernize-use-bool-literals"); CheckFactories.registerCheck<UseConstraintsCheck>( "modernize-use-constraints"); + CheckFactories.registerCheck<UseCppStyleCommentsCheck>( + "modernize-use-cpp-style-comments"); CheckFactories.registerCheck<UseDefaultMemberInitCheck>( "modernize-use-default-member-init"); CheckFactories.registerCheck<UseEmplaceCheck>("modernize-use-emplace"); diff --git a/clang-tools-extra/clang-tidy/modernize/UseCppStyleCommentsCheck.cpp b/clang-tools-extra/clang-tidy/modernize/UseCppStyleCommentsCheck.cpp new file mode 100644 index 00000000000000..3c25c50bab759c --- /dev/null +++ b/clang-tools-extra/clang-tidy/modernize/UseCppStyleCommentsCheck.cpp @@ -0,0 +1,126 @@ +//===--- UseCppStyleCommentsCheck.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 "UseCppStyleCommentsCheck.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include "clang/Lex/Lexer.h" +#include "clang/Lex/Preprocessor.h" +#include <iostream> +using namespace clang::ast_matchers; + +namespace clang::tidy::modernize { +class UseCppStyleCommentsCheck::CStyleCommentHandler : public CommentHandler { +public: + CStyleCommentHandler(UseCppStyleCommentsCheck &Check) + : Check(Check), + CStyleCommentMatch( + "^[ \t]*/\\*+[ \t\r\n]*(.*[ \t\r\n]*)*[ \t\r\n]*\\*+/[ \t\r\n]*$"){} + + std::string convertToCppStyleComment(const SourceManager &SM, const SourceRange &Range) { + StringRef CommentText = Lexer::getSourceText( + CharSourceRange::getTokenRange(Range), SM, LangOptions()); + + std::string InnerText = CommentText.str(); + InnerText.erase(0, 2); + InnerText.erase(InnerText.size() - 2, 2); + + std::string Result; + std::istringstream Stream(InnerText); + std::string Line; + + if (std::getline(Stream, Line)) { + size_t startPos = Line.find_first_not_of(" \t"); + if (startPos != std::string::npos) { + Line = Line.substr(startPos); + } else { + Line.clear(); + } + Result += "// " + Line; + } + + while (std::getline(Stream, Line)) { + size_t startPos = Line.find_first_not_of(" \t"); + if (startPos != std::string::npos) { + Line = Line.substr(startPos); + } else { + Line.clear(); + } + Result += "\n// " + Line; + } + return Result; + } + + bool HandleComment(Preprocessor &PP, SourceRange Range) override { + const SourceManager &SM = PP.getSourceManager(); + + if (Range.getBegin().isMacroID() || + SM.isInSystemHeader(Range.getBegin())) { + return false; + } + + const StringRef Text = + Lexer::getSourceText(CharSourceRange::getCharRange(Range), + SM, PP.getLangOpts()); + + SmallVector<StringRef> Matches; + if (!CStyleCommentMatch.match(Text, &Matches)) { + return false; + } + + SourceLocation CommentStart = Range.getBegin(); + SourceLocation CommentEnd = Range.getEnd(); + + unsigned StartLine = SM.getSpellingLineNumber(CommentStart); + unsigned EndLine = SM.getSpellingLineNumber(CommentEnd); + + + if (StartLine == EndLine) { + SourceLocation LineBegin = SM.translateLineCol( + SM.getFileID(CommentStart), StartLine, 1); + SourceLocation LineEnd = SM.translateLineCol(SM.getFileID(CommentEnd), EndLine, std::numeric_limits<unsigned>::max()); + StringRef LineContent = + Lexer::getSourceText(CharSourceRange::getCharRange(LineBegin, LineEnd), + SM, PP.getLangOpts()); + size_t CommentStartOffset = SM.getSpellingColumnNumber(CommentStart) - 1; + StringRef AfterComment = LineContent.drop_front(CommentStartOffset + Text.size()); + + if (!AfterComment.trim().empty()) { + return false; + } + } + + Check.diag( + Range.getBegin(), + "use C++ style comments '//' instead of C style comments '/*...*/'") + << clang::FixItHint::CreateReplacement( + Range, convertToCppStyleComment(SM, Range)); + + return false; +} + + +private: + UseCppStyleCommentsCheck &Check; + llvm::Regex CStyleCommentMatch; +}; + +UseCppStyleCommentsCheck::UseCppStyleCommentsCheck(StringRef Name, + ClangTidyContext *Context) + : ClangTidyCheck(Name, Context), + Handler(std::make_unique<CStyleCommentHandler>(*this)) {} + +void UseCppStyleCommentsCheck::registerPPCallbacks( + const SourceManager &SM, Preprocessor *PP, Preprocessor *ModuleExpanderPP) { + PP->addCommentHandler(Handler.get()); +} + +void UseCppStyleCommentsCheck::check(const MatchFinder::MatchResult &Result) {} + +UseCppStyleCommentsCheck::~UseCppStyleCommentsCheck() = default; +} // namespace clang::tidy::modernize diff --git a/clang-tools-extra/clang-tidy/modernize/UseCppStyleCommentsCheck.h b/clang-tools-extra/clang-tidy/modernize/UseCppStyleCommentsCheck.h new file mode 100644 index 00000000000000..3d604683de5a24 --- /dev/null +++ b/clang-tools-extra/clang-tidy/modernize/UseCppStyleCommentsCheck.h @@ -0,0 +1,40 @@ +//===--- UseCppStyleCommentsCheck.h - 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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USE_CPP_STYLE_COMMENTS_CHECK_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USE_CPP_STYLE_COMMENTS_CHECK_H + +#include "../ClangTidyCheck.h" +#include <sstream> +namespace clang::tidy::modernize { +/// Detects C Style comments and suggests to use C++ style comments instead. +/// +/// For the user-facing documentation see: +/// http://clang.llvm.org/extra/clang-tidy/checks/modernize/use-cpp-style-comments.html +class UseCppStyleCommentsCheck : public ClangTidyCheck { +public: + UseCppStyleCommentsCheck(StringRef Name, ClangTidyContext *Context); + + ~UseCppStyleCommentsCheck() override; + + bool isLanguageVersionSupported(const LangOptions &LangOpts) const override { + return LangOpts.CPlusPlus; + } + + void registerPPCallbacks(const SourceManager &SM, Preprocessor *PP, + Preprocessor *ModuleExpanderPP) override; + + void check(const ast_matchers::MatchFinder::MatchResult &Result) override; + +private: + class CStyleCommentHandler; + std::unique_ptr<CStyleCommentHandler> Handler; +}; +} // namespace clang::tidy::modernize + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USE_CPP_STYLE_COMMENTS_CHECK_H diff --git a/clang-tools-extra/clang-tidy/readability/ImplicitBoolConversionCheck.cpp b/clang-tools-extra/clang-tidy/readability/ImplicitBoolConversionCheck.cpp deleted file mode 100644 index f9fd1d903e231e..00000000000000 --- a/clang-tools-extra/clang-tidy/readability/ImplicitBoolConversionCheck.cpp +++ /dev/null @@ -1,428 +0,0 @@ -//===--- 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 "../utils/FixItHintUtils.h" -#include "clang/AST/ASTContext.h" -#include "clang/ASTMatchers/ASTMatchFinder.h" -#include "clang/ASTMatchers/ASTMatchers.h" -#include "clang/Lex/Lexer.h" -#include "clang/Tooling/FixIt.h" -#include <queue> - -using namespace clang::ast_matchers; - -namespace clang::tidy::readability { - -namespace { - -AST_MATCHER(Stmt, isMacroExpansion) { - SourceManager &SM = Finder->getASTContext().getSourceManager(); - SourceLocation Loc = Node.getBeginLoc(); - return SM.isMacroBodyExpansion(Loc) || SM.isMacroArgExpansion(Loc); -} - -AST_MATCHER(Stmt, isC23) { return Finder->getASTContext().getLangOpts().C23; } - -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 || Context.getLangOpts().C23) - ? "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; -} - -void fixGenericExprCastToBool(DiagnosticBuilder &Diag, - const ImplicitCastExpr *Cast, const Stmt *Parent, - ASTContext &Context, - bool UseUpperCaseLiteralSuffix) { - // 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 = utils::fixit::areParensNeededForStatement(*SubExpr); - bool NeedOuterParens = - Parent != nullptr && utils::fixit::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 += " != "; - } - - const StringRef ZeroLiteral = getZeroLiteralToCompareWithForType( - Cast->getCastKind(), SubExpr->getType(), Context); - - if (UseUpperCaseLiteralSuffix) - EndLocInsertion += ZeroLiteral.upper(); - else - EndLocInsertion += ZeroLiteral; - - 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->IgnoreParens())) { - 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 {}; -} - -bool needsSpacePrefix(SourceLocation Loc, ASTContext &Context) { - SourceRange PrefixRange(Loc.getLocWithOffset(-1), Loc); - StringRef SpaceBeforeStmtStr = Lexer::getSourceText( - CharSourceRange::getCharRange(PrefixRange), Context.getSourceManager(), - Context.getLangOpts(), nullptr); - if (SpaceBeforeStmtStr.empty()) - return true; - - const StringRef AllowedCharacters(" \t\n\v\f\r(){}[]<>;,+=-|&~!^*/"); - return !AllowedCharacters.contains(SpaceBeforeStmtStr.back()); -} - -void fixGenericExprCastFromBool(DiagnosticBuilder &Diag, - const ImplicitCastExpr *Cast, - ASTContext &Context, StringRef OtherType) { - if (!Context.getLangOpts().CPlusPlus) { - Diag << FixItHint::CreateInsertion(Cast->getBeginLoc(), - (Twine("(") + OtherType + ")").str()); - return; - } - - const Expr *SubExpr = Cast->getSubExpr(); - const bool NeedParens = !isa<ParenExpr>(SubExpr->IgnoreImplicit()); - const bool NeedSpace = needsSpacePrefix(Cast->getBeginLoc(), Context); - - Diag << FixItHint::CreateInsertion( - Cast->getBeginLoc(), (Twine() + (NeedSpace ? " " : "") + "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<DoStmt>(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)), - UseUpperCaseLiteralSuffix( - Options.get("UseUpperCaseLiteralSuffix", false)) {} - -void ImplicitBoolConversionCheck::storeOptions( - ClangTidyOptions::OptionMap &Opts) { - Options.store(Opts, "AllowIntegerConditions", AllowIntegerConditions); - Options.store(Opts, "AllowPointerConditions", AllowPointerConditions); - Options.store(Opts, "UseUpperCaseLiteralSuffix", UseUpperCaseLiteralSuffix); -} - -void ImplicitBoolConversionCheck::registerMatchers(MatchFinder *Finder) { - auto ExceptionCases = - expr(anyOf(allOf(isMacroExpansion(), unless(isNULLMacroExpansion())), - has(ignoringImplicit( - memberExpr(hasDeclaration(fieldDecl(hasBitWidth(1)))))), - hasParent(explicitCastExpr()), - expr(hasType(qualType().bind("type")), - hasParent(initListExpr(hasParent(explicitCastExpr( - hasType(qualType(equalsBoundNode("type")))))))))); - 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())))); - auto BoolXor = - binaryOperator(hasOperatorName("^"), hasLHS(ImplicitCastFromBool), - hasRHS(ImplicitCastFromBool)); - auto ComparisonInCall = allOf( - hasParent(callExpr()), - hasSourceExpression(binaryOperator(hasAnyOperatorName("==", "!=")))); - - auto IsInCompilerGeneratedFunction = hasAncestor(namedDecl(anyOf( - isImplicit(), functionDecl(isDefaulted()), functionTemplateDecl()))); - - Finder->addMatcher( - traverse(TK_AsIs, - implicitCastExpr( - anyOf(hasCastKind(CK_IntegralToBoolean), - hasCastKind(CK_FloatingToBoolean), - hasCastKind(CK_PointerToBoolean), - hasCastKind(CK_MemberPointerToBoolean)), - // Exclude cases of C23 comparison result. - unless(allOf(isC23(), - hasSourceExpression(ignoringParens( - binaryOperator(hasAnyOperatorName( - ">", ">=", "==", "!=", "<", "<=")))))), - // 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)), - // Exclude C23 cases common to implicit cast to bool. - unless(ComparisonInCall), - // Retrieve also parent statement, to check if we need - // additional parens in replacement. - optionally(hasParent(stmt().bind("parentStmt"))), - unless(isInTemplateInstantiation()), - unless(IsInCompilerGeneratedFunction)) - .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, unless(ExceptionCases), - // 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(IsInCompilerGeneratedFunction))), - 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, - UseUpperCaseLiteralSuffix); - } -} - -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()->IgnoreParens())) { - - const auto EquivalentForBoolLiteral = - getEquivalentForBoolLiteral(BoolLiteral, DestType, Context); - if (UseUpperCaseLiteralSuffix) - Diag << tooling::fixit::createReplacement( - *Cast, EquivalentForBoolLiteral.upper()); - else - Diag << tooling::fixit::createReplacement(*Cast, - EquivalentForBoolLiteral); - - } else { - fixGenericExprCastFromBool(Diag, Cast, Context, DestType.getAsString()); - } -} - -} // namespace clang::tidy::readability diff --git a/clang-tools-extra/clang-tidy/readability/ImplicitBoolConversionCheck.h b/clang-tools-extra/clang-tidy/readability/ImplicitBoolConversionCheck.h deleted file mode 100644 index 5947f7316e67cc..00000000000000 --- a/clang-tools-extra/clang-tidy/readability/ImplicitBoolConversionCheck.h +++ /dev/null @@ -1,44 +0,0 @@ -//===--- ImplicitBoolConversionCheck.h - clang-tidy--------------*- C++ -*-===// -// -// 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 -// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_IMPLICIT_BOOL_CONVERSION_H -#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_IMPLICIT_BOOL_CONVERSION_H - -#include "../ClangTidyCheck.h" - -namespace clang::tidy::readability { - -/// Checks for use of implicit bool conversions in expressions. -/// -/// For the user-facing documentation see: -/// http://clang.llvm.org/extra/clang-tidy/checks/readability/implicit-bool-conversion.html -class ImplicitBoolConversionCheck : public ClangTidyCheck { -public: - ImplicitBoolConversionCheck(StringRef Name, ClangTidyContext *Context); - bool isLanguageVersionSupported(const LangOptions &LangOpts) const override { - return LangOpts.Bool; - } - void storeOptions(ClangTidyOptions::OptionMap &Opts) override; - void registerMatchers(ast_matchers::MatchFinder *Finder) override; - void check(const ast_matchers::MatchFinder::MatchResult &Result) override; - -private: - void handleCastToBool(const ImplicitCastExpr *CastExpression, - const Stmt *ParentStatement, ASTContext &Context); - void handleCastFromBool(const ImplicitCastExpr *Cast, - const ImplicitCastExpr *NextImplicitCast, - ASTContext &Context); - - const bool AllowIntegerConditions; - const bool AllowPointerConditions; - const bool UseUpperCaseLiteralSuffix; -}; - -} // namespace clang::tidy::readability - -#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_IMPLICIT_BOOL_CONVERSION_H diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst index 94e15639c4a92e..a89cd1a8b80e50 100644 --- a/clang-tools-extra/docs/ReleaseNotes.rst +++ b/clang-tools-extra/docs/ReleaseNotes.rst @@ -108,19 +108,23 @@ Improvements to clang-query Improvements to clang-tidy -------------------------- -- Improved :program:`clang-tidy`'s `--verify-config` flag by adding support for - the configuration options of the `Clang Static Analyzer Checks - <https://clang.llvm.org/docs/analyzer/checkers.html>`_. +- Improved :program:`clang-tidy-diff.py` script. Add the `-only-check-in-db` + option to exclude files not present in the compilation database, avoiding + false-negative results. - Improved :program:`run-clang-tidy.py` script. Fixed minor shutdown noise happening on certain platforms when interrupting the script. -- Improved :program:`clang-tidy` by accepting parameters file in command line. +- Improved :program:`clang-tidy`: -- Removed :program:`clang-tidy`'s global options for most of checks. All options - are changed to local options except `IncludeStyle`, `StrictMode` and - `IgnoreMacros`. Global scoped `StrictMode` and `IgnoreMacros` are deprecated - and will be removed in further releases. + - add support for `--verify-config` flag to check the configuration options of + the `Clang Static Analyzer Checks <https://clang.llvm.org/docs/analyzer/checkers.html>`_. + - accept parameters file in command line. + - fix incorrect configuration file path resolving when file paths contain ``..``. + - remove global options for most of checks. All options are changed to local + options except `IncludeStyle`, `StrictMode` and `IgnoreMacros`. Global scoped + `StrictMode` and `IgnoreMacros` are deprecated and will be removed in further + releases. .. csv-table:: :header: "Check", "Options removed from global option" @@ -145,6 +149,13 @@ New checks Warns about code that tries to cast between pointers by means of ``std::bit_cast`` or ``memcpy``. +- New :doc:`bugprone-incorrect-enable-shared-from-this + <clang-tidy/checks/bugprone/incorrect-enable-shared-from-this>` check. + + Detect classes or structs that do not publicly inherit from + ``std::enable_shared_from_this``, because unintended behavior will + otherwise occur when calling ``shared_from_this``. + - New :doc:`bugprone-nondeterministic-pointer-iteration-order <clang-tidy/checks/bugprone/nondeterministic-pointer-iteration-order>` check. @@ -162,6 +173,11 @@ New checks Replace comparisons between signed and unsigned integers with their safe C++20 ``std::cmp_*`` alternative, if available. + +- New :doc:`modernize-use-cpp-style-comments + <clang-tidy/checks/modernize/use-cpp-style-comments>` check. + + Detects C Style comments and suggests to use C++ style comments instead. - New :doc:`portability-template-virtual-member-function <clang-tidy/checks/portability/template-virtual-member-function>` check. @@ -192,7 +208,7 @@ Changes in existing checks the offending code with ``reinterpret_cast``, to more clearly express intent. - Improved :doc:`bugprone-dangling-handle - <clang-tidy/checks/bugprone/dangling-handle>` check to treat `std::span` as a + <clang-tidy/checks/bugprone/dangling-handle>` check to treat ``std::span`` as a handle class. - Improved :doc:`bugprone-exception-escape @@ -203,6 +219,11 @@ Changes in existing checks <clang-tidy/checks/bugprone/forwarding-reference-overload>` check by fixing a crash when determining if an ``enable_if[_t]`` was found. +- Improve :doc:`bugprone-narrowing-conversions + <clang-tidy/checks/bugprone/narrowing-conversions>` to avoid incorrect check + results when floating point type is not ``float``, ``double`` and + ``long double``. + - Improved :doc:`bugprone-optional-value-conversion <clang-tidy/checks/bugprone/optional-value-conversion>` to support detecting conversion directly by ``std::make_unique`` and ``std::make_shared``. @@ -230,7 +251,7 @@ Changes in existing checks - Improved :doc:`bugprone-unchecked-optional-access <clang-tidy/checks/bugprone/unchecked-optional-access>` to support - `bsl::optional` and `bdlb::NullableValue` from + ``bsl::optional`` and ``bdlb::NullableValue`` from <https://github.com/bloomberg/bde>_. - Improved :doc:`bugprone-unhandled-self-assignment @@ -286,13 +307,13 @@ Changes in existing checks - Improved :doc:`misc-use-internal-linkage <clang-tidy/checks/misc/use-internal-linkage>` check to insert ``static`` - keyword before type qualifiers such as ``const`` and ``volatile`` and fix - false positives for function declaration without body and fix false positives - for C++20 export declarations and fix false positives for global scoped + keyword before type qualifiers such as ``const`` and ``volatile``. Also, fix + false positives for function declaration without body, C++20 consteval + functions, C++20 export declarations, and global scoped overloaded ``operator new`` and ``operator delete``. - Improved :doc:`modernize-avoid-c-arrays - <clang-tidy/checks/modernize/avoid-c-arrays>` check to suggest using + <clang-tidy/checks/modernize/avoid-c-arrays>` check to suggest using ``std::span`` as a replacement for parameters of incomplete C array type in C++20 and ``std::array`` or ``std::vector`` before C++20. @@ -305,10 +326,19 @@ Changes in existing checks a false positive when only an implicit conversion happened inside an initializer list. +- Improved :doc:`modernize-raw-string-literal + <clang-tidy/checks/modernize/raw-string-literal>` check to fix incorrect + fix-it when the string contains a user-defined suffix. + - Improved :doc:`modernize-use-designated-initializers <clang-tidy/checks/modernize/use-designated-initializers>` check to fix a crash when a class is declared but not defined. +- Improved :doc:`modernize-use-integer-sign-comparison + <clang-tidy/checks/modernize/use-integer-sign-comparison>` check to + add an option ``EnableQtSupport``, that makes C++17 ``q20::cmp_*`` alternative + available for Qt-based applications. + - Improved :doc:`modernize-use-nullptr <clang-tidy/checks/modernize/use-nullptr>` check to also recognize ``NULL``/``__null`` (but not ``0``) when used with a templated type. @@ -339,6 +369,10 @@ Changes in existing checks <clang-tidy/checks/performance/move-const-arg>` check to fix a crash when an argument type is declared but not defined. +- Improved :doc:`performance-unnecessary-copy-initialization + <clang-tidy/checks/performance/unnecessary-copy-initialization>` check + to consider static member functions the same way as free functions. + - Improved :doc:`readability-container-contains <clang-tidy/checks/readability/container-contains>` check to let it work on any class that has a ``contains`` method. Fix some false negatives in the @@ -350,19 +384,28 @@ Changes in existing checks file path for anonymous enums in the diagnostic, and by fixing a typo in the diagnostic. +- Improved :doc:`readability-identifier-naming + <clang-tidy/checks/readability/identifier-naming>` check to + validate ``namespace`` aliases. + - Improved :doc:`readability-implicit-bool-conversion <clang-tidy/checks/readability/implicit-bool-conversion>` check by adding the option `UseUpperCaseLiteralSuffix` to select the case of the literal suffix in fixes and fixing false positive for implicit conversion of comparison result in C23. +- Improved :doc:`readability-redundant-casting + <clang-tidy/checks/readability/redundant-casting>` check + by addressing a false positive in aggregate initialization through + parenthesized list. + - Improved :doc:`readability-redundant-smartptr-get <clang-tidy/checks/readability/redundant-smartptr-get>` check to - remove `->`, when redundant `get()` is removed. + remove ``->``, when redundant ``get()`` is removed. -- Improved :doc:`readability-identifier-naming - <clang-tidy/checks/readability/identifier-naming>` check to - validate ``namespace`` aliases. +- Improved :doc:`readability-use-std-min-max + <clang-tidy/checks/readability/use-std-min-max>` check to use correct template + type in ``std::min`` and ``std::max`` when operand is integer literal. Removed checks ^^^^^^^^^^^^^^ diff --git a/clang-tools-extra/docs/clang-tidy/checks/modernize/use-cpp-style-comments.rst b/clang-tools-extra/docs/clang-tidy/checks/modernize/use-cpp-style-comments.rst new file mode 100644 index 00000000000000..5eaf759a3db6d6 --- /dev/null +++ b/clang-tools-extra/docs/clang-tidy/checks/modernize/use-cpp-style-comments.rst @@ -0,0 +1,38 @@ +.. title:: clang-tidy - use-cpp-style-comments + +modernize-use-cpp-style-comments +======================= + +Replace C-style comments with C++-style comments. +C-style comments (`/* ... */`) are inherited from C, while C++ introduces +`//` as a more concise, readable, and less error-prone alternative. Modern C++ +guidelines recommend using C++-style comments for consistency and +maintainability. This check identifies and replaces C-style comments with +equivalent C++-style comments. + +Examples: + +Input: +.. code-block::c++ + /* This is a single-line comment */ + int x = 42; /* Inline comment */ + + /* This is a + multi-line comment */ + + +Output: +.. code-block::c++ + // This is a single-line comment + int x = 42; // Inline comment + + // This is a + // multi-line comment + +.. note:: + + Inline Comments are neither fixed nor warned. + Example: + .. code-block:: c++ + int a = /* this is a comment */ 5; + diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-cpp-style-comments.cpp b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-cpp-style-comments.cpp new file mode 100644 index 00000000000000..f56750591e035f --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-cpp-style-comments.cpp @@ -0,0 +1,68 @@ +// RUN: %check_clang_tidy -std=c++11 %s modernize-use-cpp-style-comments %t -- -- + +// Single-line full C-style comment +static const int CONSTANT = 42; /* Important constant value */ +// CHECK-MESSAGES: :[[@LINE-1]]:33: warning: use C++ style comments '//' instead of C style comments '/*...*/' [modernize-use-cpp-style-comments] +// CHECK-FIXES: static const int CONSTANT = 42; // Important constant value + +// Inline comment that should NOT be transformed +int a = /* inline comment */ 5; + +// Multiline full-line comment +/* This is a multiline comment + that spans several lines + and should be converted to C++ style */ +// CHECK-MESSAGES: :[[@LINE-3]]:1: warning: use C++ style comments '//' instead of C style comments '/*...*/' [modernize-use-cpp-style-comments] +// CHECK-FIXES: // This is a multiline comment +// CHECK-FIXES: // that spans several lines +// CHECK-FIXES: // and should be converted to C++ style +void fnWithSomeBools(bool A,bool B) {} +// Function with parameter inline comments +void processData(int data /* input data */, + bool validate /* perform validation */) { + // These inline comments should NOT be transformed + fnWithSomeBools(/*ControlsA=*/ true, /*ControlsB=*/ false); +} + +// Multiline comment with asterisk styling +/******************************* + * Block comment with asterisks + * Should be converted to C++ style + *******************************/ +// CHECK-MESSAGES: :[[@LINE-4]]:1: warning: use C++ style comments '//' instead of C style comments '/*...*/' [modernize-use-cpp-style-comments] +// CHECK-FIXES: // ****************************** +// CHECK-FIXES: // * Block comment with asterisks +// CHECK-FIXES: // * Should be converted to C++ style +// CHECK-FIXES: // ****************************** +int calculateSomething() { return 1;} +// Comment at end of complex line +int complexCalculation = calculateSomething(); /* Result of complex calculation */ +// CHECK-MESSAGES: :[[@LINE-1]]:48: warning: use C++ style comments '//' instead of C style comments '/*...*/' [modernize-use-cpp-style-comments] +// CHECK-FIXES: int complexCalculation = calculateSomething(); // Result of complex calculation + +// Nested comments and edge cases +void edgeCaseFunction() { + int x = 10 /* First value */ + 20 /* Second value */; // Inline comments should not transform + + /* Comment with special characters !@#$%^&*()_+ */ + // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use C++ style comments '//' instead of C style comments '/*...*/' [modernize-use-cpp-style-comments] + // CHECK-FIXES: // Comment with special characters !@#$%^&*()_+ +} + +// Multiline comment with various indentations + /* This comment is indented + and should preserve indentation when converted */ +// CHECK-MESSAGES: :[[@LINE-2]]:5: warning: use C++ style comments '//' instead of C style comments '/*...*/' [modernize-use-cpp-style-comments] +// CHECK-FIXES: // This comment is indented +// CHECK-FIXES: // and should preserve indentation when converted + +// Complex function with mixed comment types +void complexFunction() { + /* Full line comment at start of block */ + // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use C++ style comments '//' instead of C style comments '/*...*/' [modernize-use-cpp-style-comments] + // CHECK-FIXES: // Full line comment at start of block + + int x = 10; /* Inline comment not to be transformed */ + // CHECK-MESSAGES: :[[@LINE-1]]:17: warning: use C++ style comments '//' instead of C style comments '/*...*/' [modernize-use-cpp-style-comments] + // CHECK-FIXES: int x = 10; // Inline comment not to be transformed +} \ No newline at end of file >From 8bf94c6e3a4202f22efbcb7d1988c9b234c4e172 Mon Sep 17 00:00:00 2001 From: 4m4n-x-b4w4ne <amanmbaw...@gmail.com> Date: Fri, 24 Jan 2025 23:13:22 +0530 Subject: [PATCH 2/2] Revert "Added options to readability-implicit-bool-conversion (#120087)" This reverts commit 5bec2b71b44ddff44aa4d8534b58a5561389bb1d. reverting previous commits on this branch --- .../ImplicitBoolConversionCheck.cpp | 428 ++++++++++++++++++ .../readability/ImplicitBoolConversionCheck.h | 44 ++ 2 files changed, 472 insertions(+) create mode 100644 clang-tools-extra/clang-tidy/readability/ImplicitBoolConversionCheck.cpp create mode 100644 clang-tools-extra/clang-tidy/readability/ImplicitBoolConversionCheck.h diff --git a/clang-tools-extra/clang-tidy/readability/ImplicitBoolConversionCheck.cpp b/clang-tools-extra/clang-tidy/readability/ImplicitBoolConversionCheck.cpp new file mode 100644 index 00000000000000..f9fd1d903e231e --- /dev/null +++ b/clang-tools-extra/clang-tidy/readability/ImplicitBoolConversionCheck.cpp @@ -0,0 +1,428 @@ +//===--- 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 "../utils/FixItHintUtils.h" +#include "clang/AST/ASTContext.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include "clang/ASTMatchers/ASTMatchers.h" +#include "clang/Lex/Lexer.h" +#include "clang/Tooling/FixIt.h" +#include <queue> + +using namespace clang::ast_matchers; + +namespace clang::tidy::readability { + +namespace { + +AST_MATCHER(Stmt, isMacroExpansion) { + SourceManager &SM = Finder->getASTContext().getSourceManager(); + SourceLocation Loc = Node.getBeginLoc(); + return SM.isMacroBodyExpansion(Loc) || SM.isMacroArgExpansion(Loc); +} + +AST_MATCHER(Stmt, isC23) { return Finder->getASTContext().getLangOpts().C23; } + +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 || Context.getLangOpts().C23) + ? "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; +} + +void fixGenericExprCastToBool(DiagnosticBuilder &Diag, + const ImplicitCastExpr *Cast, const Stmt *Parent, + ASTContext &Context, + bool UseUpperCaseLiteralSuffix) { + // 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 = utils::fixit::areParensNeededForStatement(*SubExpr); + bool NeedOuterParens = + Parent != nullptr && utils::fixit::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 += " != "; + } + + const StringRef ZeroLiteral = getZeroLiteralToCompareWithForType( + Cast->getCastKind(), SubExpr->getType(), Context); + + if (UseUpperCaseLiteralSuffix) + EndLocInsertion += ZeroLiteral.upper(); + else + EndLocInsertion += ZeroLiteral; + + 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->IgnoreParens())) { + 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 {}; +} + +bool needsSpacePrefix(SourceLocation Loc, ASTContext &Context) { + SourceRange PrefixRange(Loc.getLocWithOffset(-1), Loc); + StringRef SpaceBeforeStmtStr = Lexer::getSourceText( + CharSourceRange::getCharRange(PrefixRange), Context.getSourceManager(), + Context.getLangOpts(), nullptr); + if (SpaceBeforeStmtStr.empty()) + return true; + + const StringRef AllowedCharacters(" \t\n\v\f\r(){}[]<>;,+=-|&~!^*/"); + return !AllowedCharacters.contains(SpaceBeforeStmtStr.back()); +} + +void fixGenericExprCastFromBool(DiagnosticBuilder &Diag, + const ImplicitCastExpr *Cast, + ASTContext &Context, StringRef OtherType) { + if (!Context.getLangOpts().CPlusPlus) { + Diag << FixItHint::CreateInsertion(Cast->getBeginLoc(), + (Twine("(") + OtherType + ")").str()); + return; + } + + const Expr *SubExpr = Cast->getSubExpr(); + const bool NeedParens = !isa<ParenExpr>(SubExpr->IgnoreImplicit()); + const bool NeedSpace = needsSpacePrefix(Cast->getBeginLoc(), Context); + + Diag << FixItHint::CreateInsertion( + Cast->getBeginLoc(), (Twine() + (NeedSpace ? " " : "") + "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<DoStmt>(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)), + UseUpperCaseLiteralSuffix( + Options.get("UseUpperCaseLiteralSuffix", false)) {} + +void ImplicitBoolConversionCheck::storeOptions( + ClangTidyOptions::OptionMap &Opts) { + Options.store(Opts, "AllowIntegerConditions", AllowIntegerConditions); + Options.store(Opts, "AllowPointerConditions", AllowPointerConditions); + Options.store(Opts, "UseUpperCaseLiteralSuffix", UseUpperCaseLiteralSuffix); +} + +void ImplicitBoolConversionCheck::registerMatchers(MatchFinder *Finder) { + auto ExceptionCases = + expr(anyOf(allOf(isMacroExpansion(), unless(isNULLMacroExpansion())), + has(ignoringImplicit( + memberExpr(hasDeclaration(fieldDecl(hasBitWidth(1)))))), + hasParent(explicitCastExpr()), + expr(hasType(qualType().bind("type")), + hasParent(initListExpr(hasParent(explicitCastExpr( + hasType(qualType(equalsBoundNode("type")))))))))); + 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())))); + auto BoolXor = + binaryOperator(hasOperatorName("^"), hasLHS(ImplicitCastFromBool), + hasRHS(ImplicitCastFromBool)); + auto ComparisonInCall = allOf( + hasParent(callExpr()), + hasSourceExpression(binaryOperator(hasAnyOperatorName("==", "!=")))); + + auto IsInCompilerGeneratedFunction = hasAncestor(namedDecl(anyOf( + isImplicit(), functionDecl(isDefaulted()), functionTemplateDecl()))); + + Finder->addMatcher( + traverse(TK_AsIs, + implicitCastExpr( + anyOf(hasCastKind(CK_IntegralToBoolean), + hasCastKind(CK_FloatingToBoolean), + hasCastKind(CK_PointerToBoolean), + hasCastKind(CK_MemberPointerToBoolean)), + // Exclude cases of C23 comparison result. + unless(allOf(isC23(), + hasSourceExpression(ignoringParens( + binaryOperator(hasAnyOperatorName( + ">", ">=", "==", "!=", "<", "<=")))))), + // 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)), + // Exclude C23 cases common to implicit cast to bool. + unless(ComparisonInCall), + // Retrieve also parent statement, to check if we need + // additional parens in replacement. + optionally(hasParent(stmt().bind("parentStmt"))), + unless(isInTemplateInstantiation()), + unless(IsInCompilerGeneratedFunction)) + .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, unless(ExceptionCases), + // 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(IsInCompilerGeneratedFunction))), + 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, + UseUpperCaseLiteralSuffix); + } +} + +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()->IgnoreParens())) { + + const auto EquivalentForBoolLiteral = + getEquivalentForBoolLiteral(BoolLiteral, DestType, Context); + if (UseUpperCaseLiteralSuffix) + Diag << tooling::fixit::createReplacement( + *Cast, EquivalentForBoolLiteral.upper()); + else + Diag << tooling::fixit::createReplacement(*Cast, + EquivalentForBoolLiteral); + + } else { + fixGenericExprCastFromBool(Diag, Cast, Context, DestType.getAsString()); + } +} + +} // namespace clang::tidy::readability diff --git a/clang-tools-extra/clang-tidy/readability/ImplicitBoolConversionCheck.h b/clang-tools-extra/clang-tidy/readability/ImplicitBoolConversionCheck.h new file mode 100644 index 00000000000000..5947f7316e67cc --- /dev/null +++ b/clang-tools-extra/clang-tidy/readability/ImplicitBoolConversionCheck.h @@ -0,0 +1,44 @@ +//===--- ImplicitBoolConversionCheck.h - clang-tidy--------------*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_IMPLICIT_BOOL_CONVERSION_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_IMPLICIT_BOOL_CONVERSION_H + +#include "../ClangTidyCheck.h" + +namespace clang::tidy::readability { + +/// Checks for use of implicit bool conversions in expressions. +/// +/// For the user-facing documentation see: +/// http://clang.llvm.org/extra/clang-tidy/checks/readability/implicit-bool-conversion.html +class ImplicitBoolConversionCheck : public ClangTidyCheck { +public: + ImplicitBoolConversionCheck(StringRef Name, ClangTidyContext *Context); + bool isLanguageVersionSupported(const LangOptions &LangOpts) const override { + return LangOpts.Bool; + } + void storeOptions(ClangTidyOptions::OptionMap &Opts) override; + void registerMatchers(ast_matchers::MatchFinder *Finder) override; + void check(const ast_matchers::MatchFinder::MatchResult &Result) override; + +private: + void handleCastToBool(const ImplicitCastExpr *CastExpression, + const Stmt *ParentStatement, ASTContext &Context); + void handleCastFromBool(const ImplicitCastExpr *Cast, + const ImplicitCastExpr *NextImplicitCast, + ASTContext &Context); + + const bool AllowIntegerConditions; + const bool AllowPointerConditions; + const bool UseUpperCaseLiteralSuffix; +}; + +} // namespace clang::tidy::readability + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_IMPLICIT_BOOL_CONVERSION_H _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits