| //===--- 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.get("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()))); |
| |
| const ast_matchers::internal::VariadicDynCastAllOfMatcher< |
| Stmt, CXXParenListInitExpr> |
| cxxParenListInitExpr; // NOLINT(readability-identifier-naming) |
| |
| 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), |
| unless(cxxParenListInitExpr()), |
| 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 |