| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,252 @@ | ||
| //===--- RedundantCastingCheck.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 "RedundantCastingCheck.h" | ||
| #include "../utils/FixItHintUtils.h" | ||
| #include "clang/AST/ASTContext.h" | ||
| #include "clang/ASTMatchers/ASTMatchFinder.h" | ||
| #include "clang/Lex/Lexer.h" | ||
|
|
||
| using namespace clang::ast_matchers; | ||
|
|
||
| namespace clang::tidy::readability { | ||
|
|
||
| static bool areTypesEqual(QualType S, QualType D) { | ||
| if (S == D) | ||
| return true; | ||
|
|
||
| const auto *TS = S->getAs<TypedefType>(); | ||
| const auto *TD = D->getAs<TypedefType>(); | ||
| if (TS != TD) | ||
| return false; | ||
|
|
||
| QualType PtrS = S->getPointeeType(); | ||
| QualType PtrD = D->getPointeeType(); | ||
|
|
||
| if (!PtrS.isNull() && !PtrD.isNull()) | ||
| return areTypesEqual(PtrS, PtrD); | ||
|
|
||
| const DeducedType *DT = S->getContainedDeducedType(); | ||
| if (DT && DT->isDeduced()) | ||
| return D == DT->getDeducedType(); | ||
|
|
||
| return false; | ||
| } | ||
|
|
||
| static bool areTypesEqual(QualType TypeS, QualType TypeD, | ||
| bool IgnoreTypeAliases) { | ||
| const QualType CTypeS = TypeS.getCanonicalType(); | ||
| const QualType CTypeD = TypeD.getCanonicalType(); | ||
| if (CTypeS != CTypeD) | ||
| return false; | ||
|
|
||
| return IgnoreTypeAliases || areTypesEqual(TypeS.getLocalUnqualifiedType(), | ||
| TypeD.getLocalUnqualifiedType()); | ||
| } | ||
|
|
||
| static bool areBinaryOperatorOperandsTypesEqualToOperatorResultType( | ||
| const Expr *E, bool IgnoreTypeAliases) { | ||
| if (!E) | ||
| return true; | ||
| const Expr *WithoutImplicitAndParen = E->IgnoreParenImpCasts(); | ||
| if (!WithoutImplicitAndParen) | ||
| return true; | ||
| if (const auto *B = dyn_cast<BinaryOperator>(WithoutImplicitAndParen)) { | ||
| const QualType Type = WithoutImplicitAndParen->getType(); | ||
| if (Type.isNull()) | ||
| return true; | ||
|
|
||
| const QualType NonReferenceType = Type.getNonReferenceType(); | ||
| const QualType LHSType = B->getLHS()->IgnoreImplicit()->getType(); | ||
| if (LHSType.isNull() || !areTypesEqual(LHSType.getNonReferenceType(), | ||
| NonReferenceType, IgnoreTypeAliases)) | ||
| return false; | ||
| const QualType RHSType = B->getRHS()->IgnoreImplicit()->getType(); | ||
| if (RHSType.isNull() || !areTypesEqual(RHSType.getNonReferenceType(), | ||
| NonReferenceType, IgnoreTypeAliases)) | ||
| return false; | ||
| } | ||
| return true; | ||
| } | ||
|
|
||
| static const Decl *getSourceExprDecl(const Expr *SourceExpr) { | ||
| const Expr *CleanSourceExpr = SourceExpr->IgnoreParenImpCasts(); | ||
| if (const auto *E = dyn_cast<DeclRefExpr>(CleanSourceExpr)) { | ||
| return E->getDecl(); | ||
| } | ||
|
|
||
| if (const auto *E = dyn_cast<CallExpr>(CleanSourceExpr)) { | ||
| return E->getCalleeDecl(); | ||
| } | ||
|
|
||
| if (const auto *E = dyn_cast<MemberExpr>(CleanSourceExpr)) { | ||
| return E->getMemberDecl(); | ||
| } | ||
| return nullptr; | ||
| } | ||
|
|
||
| RedundantCastingCheck::RedundantCastingCheck(StringRef Name, | ||
| ClangTidyContext *Context) | ||
| : ClangTidyCheck(Name, Context), | ||
| IgnoreMacros(Options.getLocalOrGlobal("IgnoreMacros", true)), | ||
| IgnoreTypeAliases(Options.getLocalOrGlobal("IgnoreTypeAliases", false)) {} | ||
|
|
||
| void RedundantCastingCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) { | ||
| Options.store(Opts, "IgnoreMacros", IgnoreMacros); | ||
| Options.store(Opts, "IgnoreTypeAliases", IgnoreTypeAliases); | ||
| } | ||
|
|
||
| void RedundantCastingCheck::registerMatchers(MatchFinder *Finder) { | ||
| auto SimpleType = qualType(hasCanonicalType( | ||
| qualType(anyOf(builtinType(), references(builtinType()), | ||
| references(pointsTo(qualType())), pointsTo(qualType()))))); | ||
|
|
||
| auto BitfieldMemberExpr = memberExpr(member(fieldDecl(isBitField()))); | ||
|
|
||
| Finder->addMatcher( | ||
| explicitCastExpr( | ||
| unless(hasCastKind(CK_ConstructorConversion)), | ||
| unless(hasCastKind(CK_UserDefinedConversion)), | ||
| unless(cxxFunctionalCastExpr(hasDestinationType(unless(SimpleType)))), | ||
|
|
||
| hasDestinationType(qualType().bind("dstType")), | ||
| hasSourceExpression(anyOf( | ||
| expr(unless(initListExpr()), unless(BitfieldMemberExpr), | ||
| hasType(qualType().bind("srcType"))) | ||
| .bind("source"), | ||
| initListExpr(unless(hasInit(1, expr())), | ||
| hasInit(0, expr(unless(BitfieldMemberExpr), | ||
| hasType(qualType().bind("srcType"))) | ||
| .bind("source")))))) | ||
| .bind("cast"), | ||
| this); | ||
| } | ||
|
|
||
| void RedundantCastingCheck::check(const MatchFinder::MatchResult &Result) { | ||
| const auto *SourceExpr = Result.Nodes.getNodeAs<Expr>("source"); | ||
| auto TypeD = *Result.Nodes.getNodeAs<QualType>("dstType"); | ||
|
|
||
| if (SourceExpr->getValueKind() == VK_LValue && | ||
| TypeD.getCanonicalType()->isRValueReferenceType()) | ||
| return; | ||
|
|
||
| const auto TypeS = | ||
| Result.Nodes.getNodeAs<QualType>("srcType")->getNonReferenceType(); | ||
| TypeD = TypeD.getNonReferenceType(); | ||
|
|
||
| if (!areTypesEqual(TypeS, TypeD, IgnoreTypeAliases)) | ||
| return; | ||
|
|
||
| if (!areBinaryOperatorOperandsTypesEqualToOperatorResultType( | ||
| SourceExpr, IgnoreTypeAliases)) | ||
| return; | ||
|
|
||
| const auto *CastExpr = Result.Nodes.getNodeAs<ExplicitCastExpr>("cast"); | ||
| if (IgnoreMacros && | ||
| (CastExpr->getBeginLoc().isMacroID() || | ||
| CastExpr->getEndLoc().isMacroID() || CastExpr->getExprLoc().isMacroID())) | ||
| return; | ||
|
|
||
| { | ||
| auto Diag = diag(CastExpr->getExprLoc(), | ||
| "redundant explicit casting to the same type %0 as the " | ||
| "sub-expression, remove this casting"); | ||
| Diag << TypeD; | ||
|
|
||
| const SourceManager &SM = *Result.SourceManager; | ||
| const SourceLocation SourceExprBegin = | ||
| SM.getExpansionLoc(SourceExpr->getBeginLoc()); | ||
| const SourceLocation SourceExprEnd = | ||
| SM.getExpansionLoc(SourceExpr->getEndLoc()); | ||
|
|
||
| if (SourceExprBegin != CastExpr->getBeginLoc()) | ||
| Diag << FixItHint::CreateRemoval(SourceRange( | ||
| CastExpr->getBeginLoc(), SourceExprBegin.getLocWithOffset(-1))); | ||
|
|
||
| const SourceLocation NextToken = Lexer::getLocForEndOfToken( | ||
| SourceExprEnd, 0U, SM, Result.Context->getLangOpts()); | ||
|
|
||
| if (SourceExprEnd != CastExpr->getEndLoc()) { | ||
| Diag << FixItHint::CreateRemoval( | ||
| SourceRange(NextToken, CastExpr->getEndLoc())); | ||
| } | ||
|
|
||
| if (utils::fixit::areParensNeededForStatement(*SourceExpr)) { | ||
| Diag << FixItHint::CreateInsertion(SourceExprBegin, "(") | ||
| << FixItHint::CreateInsertion(NextToken, ")"); | ||
| } | ||
| } | ||
|
|
||
| const auto *SourceExprDecl = getSourceExprDecl(SourceExpr); | ||
| if (!SourceExprDecl) | ||
| return; | ||
|
|
||
| if (const auto *D = dyn_cast<CXXConstructorDecl>(SourceExprDecl)) { | ||
| diag(D->getLocation(), | ||
| "source type originates from the invocation of this constructor", | ||
| DiagnosticIDs::Note); | ||
| return; | ||
| } | ||
|
|
||
| if (const auto *D = dyn_cast<FunctionDecl>(SourceExprDecl)) { | ||
| diag(D->getLocation(), | ||
| "source type originates from the invocation of this " | ||
| "%select{function|method}0", | ||
| DiagnosticIDs::Note) | ||
| << isa<CXXMethodDecl>(D) << D->getReturnTypeSourceRange(); | ||
| return; | ||
| } | ||
|
|
||
| if (const auto *D = dyn_cast<FieldDecl>(SourceExprDecl)) { | ||
| diag(D->getLocation(), | ||
| "source type originates from referencing this member", | ||
| DiagnosticIDs::Note) | ||
| << SourceRange(D->getTypeSpecStartLoc(), D->getTypeSpecEndLoc()); | ||
| return; | ||
| } | ||
|
|
||
| if (const auto *D = dyn_cast<ParmVarDecl>(SourceExprDecl)) { | ||
| diag(D->getLocation(), | ||
| "source type originates from referencing this parameter", | ||
| DiagnosticIDs::Note) | ||
| << SourceRange(D->getTypeSpecStartLoc(), D->getTypeSpecEndLoc()); | ||
| return; | ||
| } | ||
|
|
||
| if (const auto *D = dyn_cast<VarDecl>(SourceExprDecl)) { | ||
| diag(D->getLocation(), | ||
| "source type originates from referencing this variable", | ||
| DiagnosticIDs::Note) | ||
| << SourceRange(D->getTypeSpecStartLoc(), D->getTypeSpecEndLoc()); | ||
| return; | ||
| } | ||
|
|
||
| if (const auto *D = dyn_cast<EnumConstantDecl>(SourceExprDecl)) { | ||
| diag(D->getLocation(), | ||
| "source type originates from referencing this enum constant", | ||
| DiagnosticIDs::Note); | ||
| return; | ||
| } | ||
|
|
||
| if (const auto *D = dyn_cast<BindingDecl>(SourceExprDecl)) { | ||
| diag(D->getLocation(), | ||
| "source type originates from referencing this bound variable", | ||
| DiagnosticIDs::Note); | ||
| return; | ||
| } | ||
|
|
||
| if (const auto *D = dyn_cast<NonTypeTemplateParmDecl>(SourceExprDecl)) { | ||
| diag(D->getLocation(), | ||
| "source type originates from referencing this non-type template " | ||
| "parameter", | ||
| DiagnosticIDs::Note); | ||
| return; | ||
| } | ||
| } | ||
|
|
||
| } // namespace clang::tidy::readability |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,38 @@ | ||
| //===--- RedundantCastingCheck.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_REDUNDANTCASTINGCHECK_H | ||
| #define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_REDUNDANTCASTINGCHECK_H | ||
|
|
||
| #include "../ClangTidyCheck.h" | ||
|
|
||
| namespace clang::tidy::readability { | ||
|
|
||
| /// Detects explicit type casting operations that involve the same source and | ||
| /// destination types, and subsequently recommend their removal. | ||
| /// | ||
| /// For the user-facing documentation see: | ||
| /// http://clang.llvm.org/extra/clang-tidy/checks/readability/redundant-casting.html | ||
| class RedundantCastingCheck : public ClangTidyCheck { | ||
| public: | ||
| RedundantCastingCheck(StringRef Name, ClangTidyContext *Context); | ||
| void registerMatchers(ast_matchers::MatchFinder *Finder) override; | ||
| void check(const ast_matchers::MatchFinder::MatchResult &Result) override; | ||
| void storeOptions(ClangTidyOptions::OptionMap &Opts) override; | ||
| std::optional<TraversalKind> getCheckTraversalKind() const override { | ||
| return TK_IgnoreUnlessSpelledInSource; | ||
| } | ||
|
|
||
| private: | ||
| const bool IgnoreMacros; | ||
| const bool IgnoreTypeAliases; | ||
| }; | ||
|
|
||
| } // namespace clang::tidy::readability | ||
|
|
||
| #endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_REDUNDANTCASTINGCHECK_H |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,132 @@ | ||
| //===--- RedundantInlineSpecifierCheck.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 "RedundantInlineSpecifierCheck.h" | ||
| #include "clang/AST/ASTContext.h" | ||
| #include "clang/AST/Decl.h" | ||
| #include "clang/AST/DeclCXX.h" | ||
| #include "clang/AST/DeclTemplate.h" | ||
| #include "clang/AST/ExprCXX.h" | ||
| #include "clang/ASTMatchers/ASTMatchers.h" | ||
| #include "clang/Basic/Diagnostic.h" | ||
| #include "clang/Basic/SourceLocation.h" | ||
| #include "clang/Basic/SourceManager.h" | ||
| #include "clang/Lex/Token.h" | ||
|
|
||
| #include "../utils/LexerUtils.h" | ||
|
|
||
| using namespace clang::ast_matchers; | ||
|
|
||
| namespace clang::tidy::readability { | ||
|
|
||
| namespace { | ||
| AST_POLYMORPHIC_MATCHER(isInlineSpecified, | ||
| AST_POLYMORPHIC_SUPPORTED_TYPES(FunctionDecl, | ||
| VarDecl)) { | ||
| if (const auto *FD = dyn_cast<FunctionDecl>(&Node)) | ||
| return FD->isInlineSpecified(); | ||
| if (const auto *VD = dyn_cast<VarDecl>(&Node)) | ||
| return VD->isInlineSpecified(); | ||
| llvm_unreachable("Not a valid polymorphic type"); | ||
| } | ||
|
|
||
| AST_POLYMORPHIC_MATCHER_P(isInternalLinkage, | ||
| AST_POLYMORPHIC_SUPPORTED_TYPES(FunctionDecl, | ||
| VarDecl), | ||
| bool, strictMode) { | ||
| if (!strictMode) | ||
| return false; | ||
| if (const auto *FD = dyn_cast<FunctionDecl>(&Node)) | ||
| return FD->getStorageClass() == SC_Static || FD->isInAnonymousNamespace(); | ||
| if (const auto *VD = dyn_cast<VarDecl>(&Node)) | ||
| return VD->isInAnonymousNamespace(); | ||
| llvm_unreachable("Not a valid polymorphic type"); | ||
| } | ||
| } // namespace | ||
|
|
||
| static SourceLocation getInlineTokenLocation(SourceRange RangeLocation, | ||
| const SourceManager &Sources, | ||
| const LangOptions &LangOpts) { | ||
| SourceLocation Loc = RangeLocation.getBegin(); | ||
| if (Loc.isMacroID()) | ||
| return {}; | ||
|
|
||
| Token FirstToken; | ||
| Lexer::getRawToken(Loc, FirstToken, Sources, LangOpts, true); | ||
| std::optional<Token> CurrentToken = FirstToken; | ||
| while (CurrentToken && CurrentToken->getLocation() < RangeLocation.getEnd() && | ||
| CurrentToken->isNot(tok::eof)) { | ||
| if (CurrentToken->is(tok::raw_identifier) && | ||
| CurrentToken->getRawIdentifier() == "inline") | ||
| return CurrentToken->getLocation(); | ||
|
|
||
| CurrentToken = | ||
| Lexer::findNextToken(CurrentToken->getLocation(), Sources, LangOpts); | ||
| } | ||
| return {}; | ||
| } | ||
|
|
||
| void RedundantInlineSpecifierCheck::registerMatchers(MatchFinder *Finder) { | ||
| Finder->addMatcher( | ||
| functionDecl(isInlineSpecified(), | ||
| anyOf(isConstexpr(), isDeleted(), isDefaulted(), | ||
| isInternalLinkage(StrictMode), | ||
| allOf(isDefinition(), hasAncestor(recordDecl())))) | ||
| .bind("fun_decl"), | ||
| this); | ||
|
|
||
| if (StrictMode) | ||
| Finder->addMatcher( | ||
| functionTemplateDecl( | ||
| has(functionDecl(allOf(isInlineSpecified(), isDefinition())))) | ||
| .bind("templ_decl"), | ||
| this); | ||
|
|
||
| if (getLangOpts().CPlusPlus17) { | ||
| Finder->addMatcher( | ||
| varDecl(isInlineSpecified(), | ||
| anyOf(isInternalLinkage(StrictMode), | ||
| allOf(isConstexpr(), hasAncestor(recordDecl())))) | ||
| .bind("var_decl"), | ||
| this); | ||
| } | ||
| } | ||
|
|
||
| template <typename T> | ||
| void RedundantInlineSpecifierCheck::handleMatchedDecl( | ||
| const T *MatchedDecl, const SourceManager &Sources, | ||
| const MatchFinder::MatchResult &Result, StringRef Message) { | ||
| SourceLocation Loc = getInlineTokenLocation( | ||
| MatchedDecl->getSourceRange(), Sources, Result.Context->getLangOpts()); | ||
| if (Loc.isValid()) | ||
| diag(Loc, Message) << MatchedDecl << FixItHint::CreateRemoval(Loc); | ||
| } | ||
|
|
||
| void RedundantInlineSpecifierCheck::check( | ||
| const MatchFinder::MatchResult &Result) { | ||
| const SourceManager &Sources = *Result.SourceManager; | ||
|
|
||
| if (const auto *MatchedDecl = | ||
| Result.Nodes.getNodeAs<FunctionDecl>("fun_decl")) { | ||
| handleMatchedDecl( | ||
| MatchedDecl, Sources, Result, | ||
| "function %0 has inline specifier but is implicitly inlined"); | ||
| } else if (const auto *MatchedDecl = | ||
| Result.Nodes.getNodeAs<VarDecl>("var_decl")) { | ||
| handleMatchedDecl( | ||
| MatchedDecl, Sources, Result, | ||
| "variable %0 has inline specifier but is implicitly inlined"); | ||
| } else if (const auto *MatchedDecl = | ||
| Result.Nodes.getNodeAs<FunctionTemplateDecl>("templ_decl")) { | ||
| handleMatchedDecl( | ||
| MatchedDecl, Sources, Result, | ||
| "function %0 has inline specifier but is implicitly inlined"); | ||
| } | ||
| } | ||
|
|
||
| } // namespace clang::tidy::readability |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,42 @@ | ||
| //===--- RedundantInlineSpecifierCheck.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_REDUNDANTINLINESPECIFIERCHECK_H | ||
| #define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_REDUNDANTINLINESPECIFIERCHECK_H | ||
|
|
||
| #include "../ClangTidyCheck.h" | ||
|
|
||
| namespace clang::tidy::readability { | ||
|
|
||
| /// Detects redundant ``inline`` specifiers on function and variable | ||
| /// declarations. | ||
| /// | ||
| /// For the user-facing documentation see: | ||
| /// http://clang.llvm.org/extra/clang-tidy/checks/readability/redundant-inline-specifier.html | ||
| class RedundantInlineSpecifierCheck : public ClangTidyCheck { | ||
| public: | ||
| RedundantInlineSpecifierCheck(StringRef Name, ClangTidyContext *Context) | ||
| : ClangTidyCheck(Name, Context), | ||
| StrictMode(Options.getLocalOrGlobal("StrictMode", false)) {} | ||
| void registerMatchers(ast_matchers::MatchFinder *Finder) override; | ||
| void check(const ast_matchers::MatchFinder::MatchResult &Result) override; | ||
| std::optional<TraversalKind> getCheckTraversalKind() const override { | ||
| return TK_IgnoreUnlessSpelledInSource; | ||
| } | ||
|
|
||
| private: | ||
| template <typename T> | ||
| void handleMatchedDecl(const T *MatchedDecl, const SourceManager &Sources, | ||
| const ast_matchers::MatchFinder::MatchResult &Result, | ||
| StringRef Message); | ||
| const bool StrictMode; | ||
| }; | ||
|
|
||
| } // namespace clang::tidy::readability | ||
|
|
||
| #endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_REDUNDANTINLINESPECIFIERCHECK_H |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,73 @@ | ||
| .. title:: clang-tidy - bugprone-chained-comparison | ||
|
|
||
| bugprone-chained-comparison | ||
| =========================== | ||
|
|
||
| Check detects chained comparison operators that can lead to unintended | ||
| behavior or logical errors. | ||
|
|
||
| Chained comparisons are expressions that use multiple comparison operators | ||
| to compare three or more values. For example, the expression ``a < b < c`` | ||
| compares the values of ``a``, ``b``, and ``c``. However, this expression does | ||
| not evaluate as ``(a < b) && (b < c)``, which is probably what the developer | ||
| intended. Instead, it evaluates as ``(a < b) < c``, which may produce | ||
| unintended results, especially when the types of ``a``, ``b``, and ``c`` are | ||
| different. | ||
|
|
||
| To avoid such errors, the check will issue a warning when a chained | ||
| comparison operator is detected, suggesting to use parentheses to specify | ||
| the order of evaluation or to use a logical operator to separate comparison | ||
| expressions. | ||
|
|
||
| Consider the following examples: | ||
|
|
||
| .. code-block:: c++ | ||
|
|
||
| int a = 2, b = 6, c = 4; | ||
| if (a < b < c) { | ||
| // This block will be executed | ||
| } | ||
|
|
||
|
|
||
| In this example, the developer intended to check if ``a`` is less than ``b`` | ||
| and ``b`` is less than ``c``. However, the expression ``a < b < c`` is | ||
| equivalent to ``(a < b) < c``. Since ``a < b`` is ``true``, the expression | ||
| ``(a < b) < c`` is evaluated as ``1 < c``, which is equivalent to ``true < c`` | ||
| and is invalid in this case as ``b < c`` is ``false``. | ||
|
|
||
| Even that above issue could be detected as comparison of ``int`` to ``bool``, | ||
| there is more dangerous example: | ||
|
|
||
| .. code-block:: c++ | ||
|
|
||
| bool a = false, b = false, c = true; | ||
| if (a == b == c) { | ||
| // This block will be executed | ||
| } | ||
|
|
||
| In this example, the developer intended to check if ``a``, ``b``, and ``c`` are | ||
| all equal. However, the expression ``a == b == c`` is evaluated as | ||
| ``(a == b) == c``. Since ``a == b`` is true, the expression ``(a == b) == c`` | ||
| is evaluated as ``true == c``, which is equivalent to ``true == true``. | ||
| This comparison yields ``true``, even though ``a`` and ``b`` are ``false``, and | ||
| are not equal to ``c``. | ||
|
|
||
| To avoid this issue, the developer can use a logical operator to separate the | ||
| comparison expressions, like this: | ||
|
|
||
| .. code-block:: c++ | ||
|
|
||
| if (a == b && b == c) { | ||
| // This block will not be executed | ||
| } | ||
|
|
||
|
|
||
| Alternatively, use of parentheses in the comparison expressions can make the | ||
| developer's intention more explicit and help avoid misunderstanding. | ||
|
|
||
| .. code-block:: c++ | ||
|
|
||
| if ((a == b) == c) { | ||
| // This block will be executed | ||
| } | ||
|
|
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,37 @@ | ||
| .. title:: clang-tidy - readability-redundant-casting | ||
|
|
||
| readability-redundant-casting | ||
| ============================= | ||
|
|
||
| Detects explicit type casting operations that involve the same source and | ||
| destination types, and subsequently recommend their removal. Covers a range of | ||
| explicit casting operations, including ``static_cast``, ``const_cast``, C-style | ||
| casts, and ``reinterpret_cast``. Its primary objective is to enhance code | ||
| readability and maintainability by eliminating unnecessary type casting. | ||
|
|
||
| .. code-block:: c++ | ||
|
|
||
| int value = 42; | ||
| int result = static_cast<int>(value); | ||
|
|
||
| In this example, the ``static_cast<int>(value)`` is redundant, as it performs | ||
| a cast from an ``int`` to another ``int``. | ||
|
|
||
| Casting operations involving constructor conversions, user-defined conversions, | ||
| functional casts, type-dependent casts, casts between distinct type aliases that | ||
| refer to the same underlying type, as well as bitfield-related casts and casts | ||
| directly from lvalue to rvalue, are all disregarded by the check. | ||
|
|
||
| Options | ||
| ------- | ||
|
|
||
| .. option:: IgnoreMacros | ||
|
|
||
| If set to `true`, the check will not give warnings inside macros. Default | ||
| is `true`. | ||
|
|
||
| .. option:: IgnoreTypeAliases | ||
|
|
||
| When set to `false`, the check will consider type aliases, and when set to | ||
| `true`, it will resolve all type aliases and operate on the underlying | ||
| types. Default is `false`. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,32 @@ | ||
| .. title:: clang-tidy - readability-redundant-inline-specifier | ||
|
|
||
| readability-redundant-inline-specifier | ||
| ====================================== | ||
|
|
||
| Detects redundant ``inline`` specifiers on function and variable declarations. | ||
|
|
||
| Examples: | ||
|
|
||
| .. code-block:: c++ | ||
|
|
||
| constexpr inline void f() {} | ||
|
|
||
| In the example above the keyword ``inline`` is redundant since constexpr | ||
| functions are implicitly inlined | ||
|
|
||
| .. code-block:: c++ | ||
|
|
||
| class MyClass { | ||
| inline void myMethod() {} | ||
| }; | ||
|
|
||
| In the example above the keyword ``inline`` is redundant since member functions | ||
| defined entirely inside a class/struct/union definition are implicitly inlined. | ||
|
|
||
| Options | ||
| ------- | ||
|
|
||
| .. option:: StrictMode | ||
|
|
||
| If set to `true`, the check will also flag functions and variables that | ||
| already have internal linkage as redundant. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,76 @@ | ||
| // RUN: %check_clang_tidy %s bugprone-chained-comparison %t | ||
|
|
||
| void badly_chained_1(int x, int y, int z) | ||
| { | ||
| int result = x < y < z; | ||
| } | ||
| // CHECK-MESSAGES: :[[@LINE-2]]:18: warning: chained comparison 'v0 < v1 < v2' may generate unintended results, use parentheses to specify order of evaluation or a logical operator to separate comparison expressions [bugprone-chained-comparison] | ||
|
|
||
| void badly_chained_2(int x, int y, int z) | ||
| { | ||
| int result = x <= y <= z; | ||
| } | ||
| // CHECK-MESSAGES: :[[@LINE-2]]:18: warning: chained comparison 'v0 <= v1 <= v2' may generate unintended results, use parentheses to specify order of evaluation or a logical operator to separate comparison expressions [bugprone-chained-comparison] | ||
|
|
||
| void badly_chained_3(int x, int y, int z) | ||
| { | ||
| int result = x > y > z; | ||
| } | ||
| // CHECK-MESSAGES: :[[@LINE-2]]:18: warning: chained comparison 'v0 > v1 > v2' may generate unintended results, use parentheses to specify order of evaluation or a logical operator to separate comparison expressions [bugprone-chained-comparison] | ||
|
|
||
| void badly_chained_4(int x, int y, int z) | ||
| { | ||
| int result = x >= y >= z; | ||
| } | ||
| // CHECK-MESSAGES: :[[@LINE-2]]:18: warning: chained comparison 'v0 >= v1 >= v2' may generate unintended results, use parentheses to specify order of evaluation or a logical operator to separate comparison expressions [bugprone-chained-comparison] | ||
|
|
||
| void badly_chained_5(int x, int y, int z) | ||
| { | ||
| int result = x == y != z; | ||
| } | ||
| // CHECK-MESSAGES: :[[@LINE-2]]:18: warning: chained comparison 'v0 == v1 != v2' may generate unintended results, use parentheses to specify order of evaluation or a logical operator to separate comparison expressions [bugprone-chained-comparison] | ||
|
|
||
| void badly_chained_6(int x, int y, int z) | ||
| { | ||
| int result = x != y == z; | ||
| } | ||
| // CHECK-MESSAGES: :[[@LINE-2]]:18: warning: chained comparison 'v0 != v1 == v2' may generate unintended results, use parentheses to specify order of evaluation or a logical operator to separate comparison expressions [bugprone-chained-comparison] | ||
|
|
||
| void badly_chained_multiple(int a, int b, int c, int d, int e, int f, int g, int h) | ||
| { | ||
| int result = a == b == c == d == e == f == g == h; | ||
| } | ||
| // CHECK-MESSAGES: :[[@LINE-2]]:18: warning: chained comparison 'v0 == v1 == v2 == v3 == v4 == v5 == v6 == v7' may generate unintended results, use parentheses to specify order of evaluation or a logical operator to separate comparison expressions [bugprone-chained-comparison] | ||
|
|
||
| void badly_chained_limit(int v[29]) | ||
| { | ||
| // CHECK-MESSAGES: :[[@LINE+1]]:18: warning: chained comparison 'v0 == v1 == v2 == v3 == v4 == v5 == v6 == v7 == v8 == v9 == v10 == v11 == v12 == v13 == v14 == v15 == v16 == v17 == v18 == v19 == v20 == v21 == v22 == v23 == v24 == v25 == v26 == v27 == v28' may generate unintended results, use parentheses to specify order of evaluation or a logical operator to separate comparison expressions [bugprone-chained-comparison] | ||
| int result = v[0] == v[1] == v[2] == v[3] == v[4] == v[5] == v[6] == v[7] == | ||
| v[8] == v[9] == v[10] == v[11] == v[12] == v[13] == v[14] == | ||
| v[15] == v[16] == v[17] == v[18] == v[19] == v[20] == v[21] == | ||
| v[22] == v[23] == v[24] == v[25] == v[26] == v[27] == v[28]; | ||
|
|
||
| } | ||
|
|
||
| void badly_chained_parens2(int x, int y, int z, int t, int a, int b) | ||
| { | ||
| int result = (x < y) < (z && t) > (a == b); | ||
| } | ||
| // CHECK-MESSAGES: :[[@LINE-2]]:18: warning: chained comparison 'v0 < v1 > v2' may generate unintended results, use parentheses to specify order of evaluation or a logical operator to separate comparison expressions [bugprone-chained-comparison] | ||
|
|
||
| void badly_chained_inner(int x, int y, int z, int t, int u) | ||
| { | ||
| int result = x && y < z < t && u; | ||
| } | ||
| // CHECK-MESSAGES: :[[@LINE-2]]:23: warning: chained comparison 'v0 < v1 < v2' may generate unintended results, use parentheses to specify order of evaluation or a logical operator to separate comparison expressions [bugprone-chained-comparison] | ||
|
|
||
| void properly_chained_1(int x, int y, int z) | ||
| { | ||
| int result = x < y && y < z; | ||
| } | ||
|
|
||
| void properly_chained_2(int x, int y, int z) | ||
| { | ||
| int result = (x < y) == z; | ||
| } | ||
|
|
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,91 @@ | ||
| // RUN: %check_clang_tidy -std=c++98-or-later %s bugprone-chained-comparison %t | ||
|
|
||
| void badly_chained_1(int x, int y, int z) | ||
| { | ||
| bool result = x < y < z; | ||
| } | ||
| // CHECK-MESSAGES: :[[@LINE-2]]:19: warning: chained comparison 'v0 < v1 < v2' may generate unintended results, use parentheses to specify order of evaluation or a logical operator to separate comparison expressions [bugprone-chained-comparison] | ||
|
|
||
| void badly_chained_2(int x, int y, int z) | ||
| { | ||
| bool result = x <= y <= z; | ||
| } | ||
| // CHECK-MESSAGES: :[[@LINE-2]]:19: warning: chained comparison 'v0 <= v1 <= v2' may generate unintended results, use parentheses to specify order of evaluation or a logical operator to separate comparison expressions [bugprone-chained-comparison] | ||
|
|
||
| void badly_chained_3(int x, int y, int z) | ||
| { | ||
| bool result = x > y > z; | ||
| } | ||
| // CHECK-MESSAGES: :[[@LINE-2]]:19: warning: chained comparison 'v0 > v1 > v2' may generate unintended results, use parentheses to specify order of evaluation or a logical operator to separate comparison expressions [bugprone-chained-comparison] | ||
|
|
||
| void badly_chained_4(int x, int y, int z) | ||
| { | ||
| bool result = x >= y >= z; | ||
| } | ||
| // CHECK-MESSAGES: :[[@LINE-2]]:19: warning: chained comparison 'v0 >= v1 >= v2' may generate unintended results, use parentheses to specify order of evaluation or a logical operator to separate comparison expressions [bugprone-chained-comparison] | ||
|
|
||
| void badly_chained_5(int x, int y, int z) | ||
| { | ||
| bool result = x == y != z; | ||
| } | ||
| // CHECK-MESSAGES: :[[@LINE-2]]:19: warning: chained comparison 'v0 == v1 != v2' may generate unintended results, use parentheses to specify order of evaluation or a logical operator to separate comparison expressions [bugprone-chained-comparison] | ||
|
|
||
| void badly_chained_6(bool x, bool y, bool z) | ||
| { | ||
| bool result = x != y == z; | ||
| } | ||
| // CHECK-MESSAGES: :[[@LINE-2]]:19: warning: chained comparison 'v0 != v1 == v2' may generate unintended results, use parentheses to specify order of evaluation or a logical operator to separate comparison expressions [bugprone-chained-comparison] | ||
|
|
||
| void badly_chained_multiple(bool a, bool b, bool c, bool d, bool e, bool f, bool g, bool h) | ||
| { | ||
| bool result = a == b == c == d == e == f == g == h; | ||
| } | ||
| // CHECK-MESSAGES: :[[@LINE-2]]:19: warning: chained comparison 'v0 == v1 == v2 == v3 == v4 == v5 == v6 == v7' may generate unintended results, use parentheses to specify order of evaluation or a logical operator to separate comparison expressions [bugprone-chained-comparison] | ||
|
|
||
| void badly_chained_limit(bool v[29]) | ||
| { | ||
| // CHECK-MESSAGES: :[[@LINE+1]]:19: warning: chained comparison 'v0 == v1 == v2 == v3 == v4 == v5 == v6 == v7 == v8 == v9 == v10 == v11 == v12 == v13 == v14 == v15 == v16 == v17 == v18 == v19 == v20 == v21 == v22 == v23 == v24 == v25 == v26 == v27 == v28' may generate unintended results, use parentheses to specify order of evaluation or a logical operator to separate comparison expressions [bugprone-chained-comparison] | ||
| bool result = v[0] == v[1] == v[2] == v[3] == v[4] == v[5] == v[6] == v[7] == | ||
| v[8] == v[9] == v[10] == v[11] == v[12] == v[13] == v[14] == | ||
| v[15] == v[16] == v[17] == v[18] == v[19] == v[20] == v[21] == | ||
| v[22] == v[23] == v[24] == v[25] == v[26] == v[27] == v[28]; | ||
|
|
||
| } | ||
|
|
||
| void badly_chained_parens2(int x, int y, int z, int t, int a, int b) | ||
| { | ||
| bool result = (x < y) < (z && t) > (a == b); | ||
| } | ||
| // CHECK-MESSAGES: :[[@LINE-2]]:19: warning: chained comparison 'v0 < v1 > v2' may generate unintended results, use parentheses to specify order of evaluation or a logical operator to separate comparison expressions [bugprone-chained-comparison] | ||
|
|
||
| void badly_chained_inner(int x, int y, int z, int t, int u) | ||
| { | ||
| bool result = x && y < z < t && u; | ||
| } | ||
| // CHECK-MESSAGES: :[[@LINE-2]]:24: warning: chained comparison 'v0 < v1 < v2' may generate unintended results, use parentheses to specify order of evaluation or a logical operator to separate comparison expressions [bugprone-chained-comparison] | ||
|
|
||
| void properly_chained_1(int x, int y, int z) | ||
| { | ||
| bool result = x < y && y < z; | ||
| } | ||
|
|
||
| void properly_chained_2(int x, int y, bool z) | ||
| { | ||
| bool result = (x < y) == z; | ||
| } | ||
|
|
||
| struct Value { | ||
| bool operator<(const Value&) const; | ||
| }; | ||
|
|
||
| bool operator==(bool, const Value&); | ||
|
|
||
| bool badWithCppOperator(Value a, Value b, Value c) { | ||
| return a < b == c; | ||
| } | ||
| // CHECK-MESSAGES: :[[@LINE-2]]:12: warning: chained comparison 'v0 < v1 == v2' may generate unintended results, use parentheses to specify order of evaluation or a logical operator to separate comparison expressions [bugprone-chained-comparison] | ||
|
|
||
| bool mixedBinaryAndCpp(Value a, Value b, bool c) { | ||
| return a < b == c; | ||
| } | ||
| // CHECK-MESSAGES: :[[@LINE-2]]:12: warning: chained comparison 'v0 < v1 == v2' may generate unintended results, use parentheses to specify order of evaluation or a logical operator to separate comparison expressions [bugprone-chained-comparison] |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,29 @@ | ||
| // RUN: %check_clang_tidy -check-suffix=REMOVE %s modernize-use-auto %t -- \ | ||
| // RUN: -config="{CheckOptions: {modernize-use-auto.RemoveStars: 'true', modernize-use-auto.MinTypeNameLength: '0'}}" | ||
| // RUN: %check_clang_tidy %s modernize-use-auto %t -- \ | ||
| // RUN: -config="{CheckOptions: {modernize-use-auto.RemoveStars: 'false', modernize-use-auto.MinTypeNameLength: '0'}}" | ||
|
|
||
| void pointerToFunction() { | ||
| void (*(*(f1)))() = static_cast<void (**)()>(nullptr); | ||
| // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing | ||
| // CHECK-FIXES-REMOVE: auto f1 = | ||
| // CHECK-FIXES: auto *f1 = | ||
| } | ||
|
|
||
| void pointerToArray() { | ||
| int(*a1)[2] = new int[10][2]; | ||
| // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing | ||
| // CHECK-FIXES-REMOVE: auto a1 = | ||
| // CHECK-FIXES: auto *a1 = | ||
| } | ||
|
|
||
| void memberFunctionPointer() { | ||
| class A { | ||
| void f(); | ||
| }; | ||
| void(A::* a1)() = static_cast<void(A::*)()>(nullptr); | ||
| // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing | ||
| // CHECK-FIXES-REMOVE: auto a1 = | ||
| // CHECK-FIXES: auto *a1 = | ||
| } | ||
|
|
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,223 @@ | ||
| // RUN: %check_clang_tidy -std=c++11-or-later %s readability-redundant-casting %t -- -- -fno-delayed-template-parsing | ||
| // RUN: %check_clang_tidy -std=c++11-or-later -check-suffix=,MACROS %s readability-redundant-casting %t -- \ | ||
| // RUN: -config='{CheckOptions: { readability-redundant-casting.IgnoreMacros: false }}' \ | ||
| // RUN: -- -fno-delayed-template-parsing | ||
| // RUN: %check_clang_tidy -std=c++11-or-later -check-suffix=,ALIASES %s readability-redundant-casting %t -- \ | ||
| // RUN: -config='{CheckOptions: { readability-redundant-casting.IgnoreTypeAliases: true }}' \ | ||
| // RUN: -- -fno-delayed-template-parsing | ||
|
|
||
| struct A {}; | ||
| struct B : A {}; | ||
| A getA(); | ||
|
|
||
| void testRedundantStaticCasting(A& value) { | ||
| A& a1 = static_cast<A&>(value); | ||
| // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: redundant explicit casting to the same type 'A' as the sub-expression, remove this casting [readability-redundant-casting] | ||
| // CHECK-MESSAGES: :[[@LINE-3]]:36: note: source type originates from referencing this parameter | ||
| // CHECK-FIXES: {{^}} A& a1 = value; | ||
| } | ||
|
|
||
| void testRedundantConstCasting1(A& value) { | ||
| A& a2 = const_cast<A&>(value); | ||
| // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: redundant explicit casting to the same type 'A' as the sub-expression, remove this casting [readability-redundant-casting] | ||
| // CHECK-MESSAGES: :[[@LINE-3]]:36: note: source type originates from referencing this parameter | ||
| // CHECK-FIXES: {{^}} A& a2 = value; | ||
| } | ||
|
|
||
| void testRedundantConstCasting2(const A& value) { | ||
| const A& a3 = const_cast<const A&>(value); | ||
| // CHECK-MESSAGES: :[[@LINE-1]]:17: warning: redundant explicit casting to the same type 'const A' as the sub-expression, remove this casting [readability-redundant-casting] | ||
| // CHECK-MESSAGES: :[[@LINE-3]]:42: note: source type originates from referencing this parameter | ||
| // CHECK-FIXES: {{^}} const A& a3 = value; | ||
| } | ||
|
|
||
| void testRedundantReinterpretCasting(A& value) { | ||
| A& a4 = reinterpret_cast<A&>(value); | ||
| // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: redundant explicit casting to the same type 'A' as the sub-expression, remove this casting [readability-redundant-casting] | ||
| // CHECK-MESSAGES: :[[@LINE-3]]:41: note: source type originates from referencing this parameter | ||
| // CHECK-FIXES: {{^}} A& a4 = value; | ||
| } | ||
|
|
||
| void testRedundantCCasting(A& value) { | ||
| A& a5 = (A&)(value); | ||
| // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: redundant explicit casting to the same type 'A' as the sub-expression, remove this casting [readability-redundant-casting] | ||
| // CHECK-MESSAGES: :[[@LINE-3]]:31: note: source type originates from referencing this parameter | ||
| // CHECK-FIXES: {{^}} A& a5 = value; | ||
| } | ||
|
|
||
| void testDoubleCasting(A& value) { | ||
| A& a6 = static_cast<A&>(reinterpret_cast<A&>(value)); | ||
| // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: redundant explicit casting to the same type 'A' as the sub-expression, remove this casting [readability-redundant-casting] | ||
| // CHECK-MESSAGES: :[[@LINE-2]]:27: warning: redundant explicit casting to the same type 'A' as the sub-expression, remove this casting [readability-redundant-casting] | ||
| // CHECK-MESSAGES: :[[@LINE-4]]:27: note: source type originates from referencing this parameter | ||
| // CHECK-FIXES: {{^}} A& a6 = value; | ||
| } | ||
|
|
||
| void testDiffrentTypesCast(B& value) { | ||
| A& a7 = static_cast<A&>(value); | ||
| } | ||
|
|
||
| void testCastingWithAuto() { | ||
| auto a = getA(); | ||
| A& a8 = static_cast<A&>(a); | ||
| // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: redundant explicit casting to the same type 'A' as the sub-expression, remove this casting [readability-redundant-casting] | ||
| // CHECK-MESSAGES: :[[@LINE-3]]:8: note: source type originates from referencing this variable | ||
| // CHECK-FIXES: {{^}} A& a8 = a; | ||
| } | ||
|
|
||
| void testCastingWithConstAuto() { | ||
| const auto a = getA(); | ||
| const A& a9 = static_cast<const A&>(a); | ||
| // CHECK-MESSAGES: :[[@LINE-1]]:17: warning: redundant explicit casting to the same type 'const A' as the sub-expression, remove this casting [readability-redundant-casting] | ||
| // CHECK-MESSAGES: :[[@LINE-3]]:14: note: source type originates from referencing this variable | ||
| // CHECK-FIXES: {{^}} const A& a9 = a; | ||
| } | ||
|
|
||
| void testCastingWithAutoPtr(A& ptr) { | ||
| auto* a = &ptr; | ||
| A* a10 = static_cast<A*>(a); | ||
| // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: redundant explicit casting to the same type 'A *' as the sub-expression, remove this casting [readability-redundant-casting] | ||
| // CHECK-MESSAGES: :[[@LINE-3]]:9: note: source type originates from referencing this variable | ||
| // CHECK-FIXES: {{^}} A* a10 = a; | ||
| } | ||
|
|
||
| template<typename T> | ||
| void testRedundantTemplateCasting(T& value) { | ||
| A& a = static_cast<A&>(value); | ||
| T& t = static_cast<T&>(value); | ||
| // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: redundant explicit casting to the same type 'T' as the sub-expression, remove this casting [readability-redundant-casting] | ||
| // CHECK-MESSAGES: :[[@LINE-4]]:38: note: source type originates from referencing this parameter | ||
| // CHECK-FIXES: {{^}} T& t = value; | ||
| } | ||
|
|
||
| void testTemplate() { | ||
| A value; | ||
| testRedundantTemplateCasting(value); | ||
| } | ||
|
|
||
| void testValidRefConstCast() { | ||
| const auto a = getA(); | ||
| A& a11 = const_cast<A&>(a); | ||
| } | ||
|
|
||
| void testValidPtrConstCast(const A* ptr) { | ||
| A* a12 = const_cast<A*>(ptr); | ||
| } | ||
|
|
||
| #define CAST(X) static_cast<int>(X) | ||
|
|
||
| void testMacroCasting(int value) { | ||
| int a = CAST(value); | ||
| // CHECK-MESSAGES-MACROS: :[[@LINE-1]]:11: warning: redundant explicit casting to the same type 'int' as the sub-expression, remove this casting [readability-redundant-casting] | ||
| } | ||
|
|
||
| #define PTR_NAME name | ||
|
|
||
| void testMacroCasting(A* PTR_NAME) { | ||
| A* a13 = static_cast<A*>(PTR_NAME); | ||
| // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: redundant explicit casting to the same type 'A *' as the sub-expression, remove this casting [readability-redundant-casting] | ||
| // CHECK-FIXES: {{^}} A* a13 = PTR_NAME; | ||
| } | ||
|
|
||
| struct CastBool { | ||
| operator bool() const { | ||
| return true; | ||
| } | ||
| }; | ||
|
|
||
| void testUserOperatorCast(const CastBool& value) { | ||
| bool b = static_cast<bool>(value); | ||
| } | ||
|
|
||
| using TypeA = A; | ||
|
|
||
| void testTypedefCast(A& value) { | ||
| TypeA& a = static_cast<TypeA&>(value); | ||
| // CHECK-MESSAGES-ALIASES: :[[@LINE-1]]:14: warning: redundant explicit casting to the same type 'TypeA' (aka 'A') as the sub-expression, remove this casting [readability-redundant-casting] | ||
| // CHECK-FIXES-ALIASES: {{^}} TypeA& a = value; | ||
| } | ||
|
|
||
| void testTypedefCast2(TypeA& value) { | ||
| A& a = static_cast<A&>(value); | ||
| // CHECK-MESSAGES-ALIASES: :[[@LINE-1]]:10: warning: redundant explicit casting to the same type 'A' as the sub-expression, remove this casting [readability-redundant-casting] | ||
| // CHECK-FIXES-ALIASES: {{^}} A& a = value; | ||
| } | ||
|
|
||
| void testFunctionalCastWithPrimitive(int a) { | ||
| int b = int(a); | ||
| // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: redundant explicit casting to the same type 'int' as the sub-expression, remove this casting [readability-redundant-casting] | ||
| // CHECK-FIXES: {{^}} int b = a; | ||
| } | ||
|
|
||
| void testFunctionalCastWithInitExpr(unsigned a) { | ||
| unsigned b = ~unsigned{!a}; | ||
| unsigned c = unsigned{0}; | ||
| } | ||
|
|
||
| void testBinaryOperator(char c) { | ||
| int a = int(c - 'C'); | ||
| } | ||
|
|
||
| struct BIT { | ||
| bool b:1; | ||
| }; | ||
|
|
||
| template<typename ...Args> | ||
| void make(Args&& ...); | ||
|
|
||
| void testBinaryOperator(BIT b) { | ||
| make((bool)b.b); | ||
| } | ||
|
|
||
| struct Class { | ||
| using Iterator = const char*; | ||
|
|
||
| Iterator begin() { | ||
| return static_cast<Iterator>(first()); | ||
| // CHECK-MESSAGES-ALIASES: :[[@LINE-1]]:12: warning: redundant explicit casting to the same type 'Iterator' (aka 'const char *') as the sub-expression, remove this casting [readability-redundant-casting] | ||
| // CHECK-MESSAGES-ALIASES: :[[@LINE+4]]:15: note: source type originates from the invocation of this method | ||
| // CHECK-FIXES-ALIASES: {{^}} return first(); | ||
| } | ||
|
|
||
| const char* first(); | ||
| }; | ||
|
|
||
| void testAddOperation(int aa, int bb) { | ||
| int c = static_cast<int>(aa + bb) * aa; | ||
| // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: redundant explicit casting to the same type 'int' as the sub-expression, remove this casting [readability-redundant-casting] | ||
| // CHECK-FIXES: {{^}} int c = (aa + bb) * aa; | ||
| } | ||
|
|
||
| void testAddOperationWithParen(int a, int b) { | ||
| int c = static_cast<int>((a+b))*a; | ||
| // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: redundant explicit casting to the same type 'int' as the sub-expression, remove this casting [readability-redundant-casting] | ||
| // CHECK-FIXES: {{^}} int c = (a+b)*a; | ||
| } | ||
|
|
||
| void testRValueCast(int&& a) { | ||
| int&& b = static_cast<int&&>(a); | ||
| int&& c = static_cast<int&&>(10); | ||
| // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: redundant explicit casting to the same type 'int' as the sub-expression, remove this casting [readability-redundant-casting] | ||
| // CHECK-FIXES: {{^}} int&& c = 10; | ||
| } | ||
|
|
||
| template <int V> | ||
| void testRedundantNTTPCasting() { | ||
| int a = static_cast<int>(V); | ||
| // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: redundant explicit casting to the same type 'int' as the sub-expression, remove this casting [readability-redundant-casting] | ||
| // CHECK-MESSAGES: :[[@LINE-4]]:15: note: source type originates from referencing this non-type template parameter | ||
| // CHECK-FIXES: {{^}} int a = V; | ||
| } | ||
|
|
||
| template <typename T, T V> | ||
| void testValidNTTPCasting() { | ||
| int a = static_cast<int>(V); | ||
| } | ||
|
|
||
| template <typename T, T V> | ||
| void testRedundantDependentNTTPCasting() { | ||
| T a = static_cast<T>(V); | ||
| // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: redundant explicit casting to the same type 'T' as the sub-expression, remove this casting [readability-redundant-casting] | ||
| // CHECK-MESSAGES: :[[@LINE-4]]:25: note: source type originates from referencing this non-type template parameter | ||
| // CHECK-FIXES: {{^}} T a = V; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,137 @@ | ||
| // RUN: %check_clang_tidy -std=c++17 %s readability-redundant-inline-specifier %t | ||
| // RUN: %check_clang_tidy -std=c++17 -check-suffixes=,STRICT %s readability-redundant-inline-specifier %t -- -config="{CheckOptions: {readability-redundant-inline-specifier.StrictMode: 'true'}}" | ||
|
|
||
| template <typename T> inline T f() | ||
| // CHECK-MESSAGES-STRICT: :[[@LINE-1]]:23: warning: function 'f' has inline specifier but is implicitly inlined [readability-redundant-inline-specifier] | ||
| // CHECK-FIXES-STRICT: template <typename T> T f() | ||
| { | ||
| return T{}; | ||
| } | ||
|
|
||
| template <> inline double f<double>() = delete; | ||
| // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: function 'f<double>' has inline specifier but is implicitly inlined [readability-redundant-inline-specifier] | ||
| // CHECK-FIXES: template <> double f<double>() = delete; | ||
|
|
||
| inline int g(float a) | ||
| { | ||
| return static_cast<int>(a - 5.F); | ||
| } | ||
|
|
||
| inline int g(double) = delete; | ||
| // CHECK-MESSAGES: :[[@LINE-1]]:1: warning: function 'g' has inline specifier but is implicitly inlined [readability-redundant-inline-specifier] | ||
| // CHECK-FIXES: int g(double) = delete; | ||
|
|
||
| class C | ||
| { | ||
| public: | ||
| inline C& operator=(const C&) = delete; | ||
| // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: function 'operator=' has inline specifier but is implicitly inlined [readability-redundant-inline-specifier] | ||
| // CHECK-FIXES: C& operator=(const C&) = delete; | ||
|
|
||
| inline C(const C&) = default; | ||
| // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: function 'C' has inline specifier but is implicitly inlined [readability-redundant-inline-specifier] | ||
| // CHECK-FIXES: C(const C&) = default; | ||
|
|
||
| constexpr inline C& operator=(int a); | ||
| // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: function 'operator=' has inline specifier but is implicitly inlined [readability-redundant-inline-specifier] | ||
| // CHECK-FIXES: constexpr C& operator=(int a); | ||
|
|
||
| inline C() {} | ||
| // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: function 'C' has inline specifier but is implicitly inlined [readability-redundant-inline-specifier] | ||
| // CHECK-FIXES: C() {} | ||
|
|
||
| constexpr inline C(int); | ||
| // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: function 'C' has inline specifier but is implicitly inlined [readability-redundant-inline-specifier] | ||
| // CHECK-FIXES: constexpr C(int); | ||
|
|
||
| inline int Get42() const { return 42; } | ||
| // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: function 'Get42' has inline specifier but is implicitly inlined [readability-redundant-inline-specifier] | ||
| // CHECK-FIXES: int Get42() const { return 42; } | ||
|
|
||
| static inline constexpr int C_STATIC = 42; | ||
| // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: variable 'C_STATIC' has inline specifier but is implicitly inlined [readability-redundant-inline-specifier] | ||
| // CHECK-FIXES: static constexpr int C_STATIC = 42; | ||
|
|
||
| static constexpr int C_STATIC_2 = 42; | ||
| }; | ||
|
|
||
| constexpr inline int Get42() { return 42; } | ||
| // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: function 'Get42' has inline specifier but is implicitly inlined [readability-redundant-inline-specifier] | ||
| // CHECK-FIXES: constexpr int Get42() { return 42; } | ||
|
|
||
|
|
||
| static constexpr inline int NAMESPACE_STATIC = 42; | ||
|
|
||
| inline static int fn0(int i) | ||
| // CHECK-MESSAGES-STRICT: :[[@LINE-1]]:1: warning: function 'fn0' has inline specifier but is implicitly inlined [readability-redundant-inline-specifier] | ||
| // CHECK-FIXES-STRICT: static int fn0(int i) | ||
| { | ||
| return i - 1; | ||
| } | ||
|
|
||
| static constexpr inline int fn1(int i) | ||
| // CHECK-MESSAGES: :[[@LINE-1]]:18: warning: function 'fn1' has inline specifier but is implicitly inlined [readability-redundant-inline-specifier] | ||
| // CHECK-FIXES: static constexpr int fn1(int i) | ||
| { | ||
| return i - 1; | ||
| } | ||
|
|
||
| namespace | ||
| { | ||
| inline int fn2(int i) | ||
| // CHECK-MESSAGES-STRICT: :[[@LINE-1]]:5: warning: function 'fn2' has inline specifier but is implicitly inlined [readability-redundant-inline-specifier] | ||
| // CHECK-FIXES-STRICT: int fn2(int i) | ||
| { | ||
| return i - 1; | ||
| } | ||
|
|
||
| inline constexpr int fn3(int i) | ||
| // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: function 'fn3' has inline specifier but is implicitly inlined [readability-redundant-inline-specifier] | ||
| // CHECK-FIXES: constexpr int fn3(int i) | ||
| { | ||
| return i - 1; | ||
| } | ||
|
|
||
| inline constexpr int MY_CONSTEXPR_VAR = 42; | ||
| // CHECK-MESSAGES-STRICT: :[[@LINE-1]]:5: warning: variable 'MY_CONSTEXPR_VAR' has inline specifier but is implicitly inlined [readability-redundant-inline-specifier] | ||
| // CHECK-FIXES-STRICT: constexpr int MY_CONSTEXPR_VAR = 42; | ||
| } | ||
|
|
||
| namespace ns | ||
| { | ||
| inline int fn4(int i) | ||
| { | ||
| return i - 1; | ||
| } | ||
|
|
||
| inline constexpr int fn5(int i) | ||
| // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: function 'fn5' has inline specifier but is implicitly inlined [readability-redundant-inline-specifier] | ||
| // CHECK-FIXES: constexpr int fn5(int i) | ||
| { | ||
| return i - 1; | ||
| } | ||
| } | ||
|
|
||
| auto fn6 = [](){}; | ||
|
|
||
| template <typename T> inline T fn7(); | ||
|
|
||
| template <typename T> T fn7() | ||
| { | ||
| return T{}; | ||
| } | ||
|
|
||
| template <typename T> T fn8(); | ||
|
|
||
| template <typename T> inline T fn8() | ||
| // CHECK-MESSAGES-STRICT: :[[@LINE-1]]:23: warning: function 'fn8' has inline specifier but is implicitly inlined [readability-redundant-inline-specifier] | ||
| // CHECK-FIXES-STRICT: template <typename T> T fn8() | ||
| { | ||
| return T{}; | ||
| } | ||
|
|
||
| #define INLINE_MACRO() inline void fn9() { } | ||
| INLINE_MACRO() | ||
|
|
||
| #define INLINE_KW inline | ||
| INLINE_KW void fn10() { } |