1 //===--- AvoidCStyleCastsCheck.cpp - clang-tidy -----------------*- C++ -*-===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 
10 #include "AvoidCStyleCastsCheck.h"
11 #include "clang/AST/ASTContext.h"
12 #include "clang/ASTMatchers/ASTMatchFinder.h"
13 #include "clang/ASTMatchers/ASTMatchers.h"
14 #include "clang/Lex/Lexer.h"
15 
16 using namespace clang::ast_matchers;
17 
18 namespace clang {
19 namespace tidy {
20 namespace google {
21 namespace readability {
22 
23 void
24 AvoidCStyleCastsCheck::registerMatchers(ast_matchers::MatchFinder *Finder) {
25   Finder->addMatcher(
26       cStyleCastExpr(
27           // Filter out (EnumType)IntegerLiteral construct, which is generated
28           // for non-type template arguments of enum types.
29           // FIXME: Remove this once this is fixed in the AST.
30           unless(hasParent(substNonTypeTemplateParmExpr())),
31           // Avoid matches in template instantiations.
32           unless(isInTemplateInstantiation())).bind("cast"),
33       this);
34 }
35 
36 static bool needsConstCast(QualType SourceType, QualType DestType) {
37   SourceType = SourceType.getNonReferenceType();
38   DestType = DestType.getNonReferenceType();
39   while (SourceType->isPointerType() && DestType->isPointerType()) {
40     SourceType = SourceType->getPointeeType();
41     DestType = DestType->getPointeeType();
42     if (SourceType.isConstQualified() && !DestType.isConstQualified())
43       return true;
44   }
45   return false;
46 }
47 
48 static bool pointedTypesAreEqual(QualType SourceType, QualType DestType) {
49   SourceType = SourceType.getNonReferenceType();
50   DestType = DestType.getNonReferenceType();
51   while (SourceType->isPointerType() && DestType->isPointerType()) {
52     SourceType = SourceType->getPointeeType();
53     DestType = DestType->getPointeeType();
54   }
55   return SourceType.getUnqualifiedType() == DestType.getUnqualifiedType();
56 }
57 
58 void AvoidCStyleCastsCheck::check(const MatchFinder::MatchResult &Result) {
59   const auto *CastExpr = Result.Nodes.getNodeAs<CStyleCastExpr>("cast");
60 
61   auto ParenRange = CharSourceRange::getTokenRange(CastExpr->getLParenLoc(),
62                                                    CastExpr->getRParenLoc());
63   // Ignore casts in macros.
64   if (ParenRange.getBegin().isMacroID() || ParenRange.getEnd().isMacroID())
65     return;
66 
67   // Casting to void is an idiomatic way to mute "unused variable" and similar
68   // warnings.
69   if (CastExpr->getTypeAsWritten()->isVoidType())
70     return;
71 
72   QualType SourceType = CastExpr->getSubExprAsWritten()->getType();
73   QualType DestType = CastExpr->getTypeAsWritten();
74 
75   if (SourceType == DestType) {
76     diag(CastExpr->getLocStart(), "redundant cast to the same type")
77         << FixItHint::CreateRemoval(ParenRange);
78     return;
79   }
80   SourceType = SourceType.getCanonicalType();
81   DestType = DestType.getCanonicalType();
82   if (SourceType == DestType) {
83     diag(CastExpr->getLocStart(),
84          "possibly redundant cast between typedefs of the same type");
85     return;
86   }
87 
88 
89   // The rest of this check is only relevant to C++.
90   if (!Result.Context->getLangOpts().CPlusPlus)
91     return;
92   // Ignore code inside extern "C" {} blocks.
93   if (!match(expr(hasAncestor(linkageSpecDecl())), *CastExpr, *Result.Context)
94            .empty())
95     return;
96   // Ignore code in .c files and headers included from them, even if they are
97   // compiled as C++.
98   if (getCurrentMainFile().endswith(".c"))
99     return;
100   // Ignore code in .c files #included in other files (which shouldn't be done,
101   // but people still do this for test and other purposes).
102   SourceManager &SM = *Result.SourceManager;
103   if (SM.getFilename(SM.getSpellingLoc(CastExpr->getLocStart())).endswith(".c"))
104     return;
105 
106   // Leave type spelling exactly as it was (unlike
107   // getTypeAsWritten().getAsString() which would spell enum types 'enum X').
108   StringRef DestTypeString =
109       Lexer::getSourceText(CharSourceRange::getTokenRange(
110                                CastExpr->getLParenLoc().getLocWithOffset(1),
111                                CastExpr->getRParenLoc().getLocWithOffset(-1)),
112                            SM, Result.Context->getLangOpts());
113 
114   auto diag_builder =
115       diag(CastExpr->getLocStart(), "C-style casts are discouraged; use %0");
116 
117   auto ReplaceWithCast = [&](StringRef CastType) {
118     diag_builder << CastType;
119 
120     const Expr *SubExpr = CastExpr->getSubExprAsWritten()->IgnoreImpCasts();
121     std::string CastText = (CastType + "<" + DestTypeString + ">").str();
122     if (!isa<ParenExpr>(SubExpr)) {
123       CastText.push_back('(');
124       diag_builder << FixItHint::CreateInsertion(
125           Lexer::getLocForEndOfToken(SubExpr->getLocEnd(), 0, SM,
126                                      Result.Context->getLangOpts()),
127           ")");
128     }
129     diag_builder << FixItHint::CreateReplacement(ParenRange, CastText);
130   };
131 
132   // Suggest appropriate C++ cast. See [expr.cast] for cast notation semantics.
133   switch (CastExpr->getCastKind()) {
134   case CK_NoOp:
135     if (needsConstCast(SourceType, DestType) &&
136         pointedTypesAreEqual(SourceType, DestType)) {
137       ReplaceWithCast("const_cast");
138       return;
139     }
140     if (DestType->isReferenceType() &&
141         (SourceType.getNonReferenceType() ==
142              DestType.getNonReferenceType().withConst() ||
143          SourceType.getNonReferenceType() == DestType.getNonReferenceType())) {
144       ReplaceWithCast("const_cast");
145       return;
146     }
147     // FALLTHROUGH
148   case clang::CK_IntegralCast:
149     // Convert integral and no-op casts between builtin types and enums to
150     // static_cast. A cast from enum to integer may be unnecessary, but it's
151     // still retained.
152     if ((SourceType->isBuiltinType() || SourceType->isEnumeralType()) &&
153         (DestType->isBuiltinType() || DestType->isEnumeralType())) {
154       ReplaceWithCast("static_cast");
155       return;
156     }
157     break;
158   case CK_BitCast:
159     // FIXME: Suggest const_cast<...>(reinterpret_cast<...>(...)) replacement.
160     if (!needsConstCast(SourceType, DestType)) {
161       ReplaceWithCast("reinterpret_cast");
162       return;
163     }
164     break;
165   default:
166     break;
167   }
168 
169   diag_builder << "static_cast/const_cast/reinterpret_cast";
170 }
171 
172 } // namespace readability
173 } // namespace google
174 } // namespace tidy
175 } // namespace clang
176