xref: /llvm-project/clang-tools-extra/clang-tidy/cppcoreguidelines/ProTypeCstyleCastCheck.cpp (revision 7d2ea6c422d3f5712b7253407005e1a465a76946)
1 //===--- ProTypeCstyleCastCheck.cpp - clang-tidy---------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "ProTypeCstyleCastCheck.h"
10 #include "clang/AST/ASTContext.h"
11 #include "clang/ASTMatchers/ASTMatchFinder.h"
12 #include "clang/Lex/Lexer.h"
13 
14 using namespace clang::ast_matchers;
15 
16 namespace clang::tidy::cppcoreguidelines {
17 
needsConstCast(QualType SourceType,QualType DestType)18 static bool needsConstCast(QualType SourceType, QualType DestType) {
19   SourceType = SourceType.getNonReferenceType();
20   DestType = DestType.getNonReferenceType();
21   while (SourceType->isPointerType() && DestType->isPointerType()) {
22     SourceType = SourceType->getPointeeType();
23     DestType = DestType->getPointeeType();
24     if (SourceType.isConstQualified() && !DestType.isConstQualified())
25       return true;
26   }
27   return false;
28 }
29 
registerMatchers(MatchFinder * Finder)30 void ProTypeCstyleCastCheck::registerMatchers(MatchFinder *Finder) {
31   Finder->addMatcher(
32       cStyleCastExpr(unless(isInTemplateInstantiation())).bind("cast"), this);
33 }
34 
check(const MatchFinder::MatchResult & Result)35 void ProTypeCstyleCastCheck::check(const MatchFinder::MatchResult &Result) {
36   const auto *MatchedCast = Result.Nodes.getNodeAs<CStyleCastExpr>("cast");
37 
38   if (MatchedCast->getCastKind() == CK_BitCast ||
39       MatchedCast->getCastKind() == CK_LValueBitCast ||
40       MatchedCast->getCastKind() == CK_IntegralToPointer ||
41       MatchedCast->getCastKind() == CK_PointerToIntegral ||
42       MatchedCast->getCastKind() == CK_ReinterpretMemberPointer) {
43     diag(MatchedCast->getBeginLoc(),
44          "do not use C-style cast to convert between unrelated types");
45     return;
46   }
47 
48   QualType SourceType = MatchedCast->getSubExpr()->getType();
49 
50   if (MatchedCast->getCastKind() == CK_BaseToDerived) {
51     const auto *SourceDecl = SourceType->getPointeeCXXRecordDecl();
52     if (!SourceDecl) // The cast is from object to reference.
53       SourceDecl = SourceType->getAsCXXRecordDecl();
54     if (!SourceDecl)
55       return;
56 
57     if (SourceDecl->isPolymorphic()) {
58       // Leave type spelling exactly as it was (unlike
59       // getTypeAsWritten().getAsString() which would spell enum types 'enum
60       // X').
61       StringRef DestTypeString = Lexer::getSourceText(
62           CharSourceRange::getTokenRange(
63               MatchedCast->getLParenLoc().getLocWithOffset(1),
64               MatchedCast->getRParenLoc().getLocWithOffset(-1)),
65           *Result.SourceManager, getLangOpts());
66 
67       auto DiagBuilder = diag(
68           MatchedCast->getBeginLoc(),
69           "do not use C-style cast to downcast from a base to a derived class; "
70           "use dynamic_cast instead");
71 
72       const Expr *SubExpr =
73           MatchedCast->getSubExprAsWritten()->IgnoreImpCasts();
74       std::string CastText = ("dynamic_cast<" + DestTypeString + ">").str();
75       if (!isa<ParenExpr>(SubExpr)) {
76         CastText.push_back('(');
77         DiagBuilder << FixItHint::CreateInsertion(
78             Lexer::getLocForEndOfToken(SubExpr->getEndLoc(), 0,
79                                        *Result.SourceManager, getLangOpts()),
80             ")");
81       }
82       auto ParenRange = CharSourceRange::getTokenRange(
83           MatchedCast->getLParenLoc(), MatchedCast->getRParenLoc());
84       DiagBuilder << FixItHint::CreateReplacement(ParenRange, CastText);
85     } else {
86       diag(
87           MatchedCast->getBeginLoc(),
88           "do not use C-style cast to downcast from a base to a derived class");
89     }
90     return;
91   }
92 
93   if (MatchedCast->getCastKind() == CK_NoOp &&
94       needsConstCast(SourceType, MatchedCast->getType())) {
95     diag(MatchedCast->getBeginLoc(),
96          "do not use C-style cast to cast away constness");
97   }
98 }
99 
100 } // namespace clang::tidy::cppcoreguidelines
101