clang-tools 6.0.0

AvoidCStyleCastsCheck.cpp
Go to the documentation of this file.
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 AvoidCStyleCastsCheck::registerMatchers(
24 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()))
33 .bind("cast"),
34 this);
35 }
36
37 static bool needsConstCast(QualType SourceType, QualType DestType) {
38 while ((SourceType->isPointerType() && DestType->isPointerType()) ||
39 (SourceType->isReferenceType() && DestType->isReferenceType())) {
40 SourceType = SourceType->getPointeeType();
41 DestType = DestType->getPointeeType();
42 if (SourceType.isConstQualified() && !DestType.isConstQualified()) {
43 return (SourceType->isPointerType() == DestType->isPointerType()) &&
44 (SourceType->isReferenceType() == DestType->isReferenceType());
45 }
46 }
47 return false;
48 }
49
50 static bool pointedUnqualifiedTypesAreEqual(QualType T1, QualType T2) {
51 while ((T1->isPointerType() && T2->isPointerType()) ||
52 (T1->isReferenceType() && T2->isReferenceType())) {
53 T1 = T1->getPointeeType();
54 T2 = T2->getPointeeType();
55 }
56 return T1.getUnqualifiedType() == T2.getUnqualifiedType();
57 }
58
59 void AvoidCStyleCastsCheck::check(const MatchFinder::MatchResult &Result) {
60 const auto *CastExpr = Result.Nodes.getNodeAs<CStyleCastExpr>("cast");
61
62 // Ignore casts in macros.
63 if (CastExpr->getExprLoc().isMacroID())
64 return;
65
66 // Casting to void is an idiomatic way to mute "unused variable" and similar
67 // warnings.
68 if (CastExpr->getCastKind() == CK_ToVoid)
69 return;
70
71 auto isFunction = [](QualType T) {
72 T = T.getCanonicalType().getNonReferenceType();
73 return T->isFunctionType() || T->isFunctionPointerType() ||
74 T->isMemberFunctionPointerType();
75 };
76
77 const QualType DestTypeAsWritten =
78 CastExpr->getTypeAsWritten().getUnqualifiedType();
79 const QualType SourceTypeAsWritten =
80 CastExpr->getSubExprAsWritten()->getType().getUnqualifiedType();
81 const QualType SourceType = SourceTypeAsWritten.getCanonicalType();
82 const QualType DestType = DestTypeAsWritten.getCanonicalType();
83
84 auto ReplaceRange = CharSourceRange::getCharRange(
85 CastExpr->getLParenLoc(), CastExpr->getSubExprAsWritten()->getLocStart());
86
87 bool FnToFnCast =
88 isFunction(SourceTypeAsWritten) && isFunction(DestTypeAsWritten);
89
90 if (CastExpr->getCastKind() == CK_NoOp && !FnToFnCast) {
91 // Function pointer/reference casts may be needed to resolve ambiguities in
92 // case of overloaded functions, so detection of redundant casts is trickier
93 // in this case. Don't emit "redundant cast" warnings for function
94 // pointer/reference types.
95 if (SourceTypeAsWritten == DestTypeAsWritten) {
96 diag(CastExpr->getLocStart(), "redundant cast to the same type")
97 << FixItHint::CreateRemoval(ReplaceRange);
98 return;
99 }
100 }
101
102 // The rest of this check is only relevant to C++.
103 if (!getLangOpts().CPlusPlus)
104 return;
105 // Ignore code inside extern "C" {} blocks.
106 if (!match(expr(hasAncestor(linkageSpecDecl())), *CastExpr, *Result.Context)
107 .empty())
108 return;
109 // Ignore code in .c files and headers included from them, even if they are
110 // compiled as C++.
111 if (getCurrentMainFile().endswith(".c"))
112 return;
113
114 SourceManager &SM = *Result.SourceManager;
115
116 // Ignore code in .c files #included in other files (which shouldn't be done,
117 // but people still do this for test and other purposes).
118 if (SM.getFilename(SM.getSpellingLoc(CastExpr->getLocStart())).endswith(".c"))
119 return;
120
121 // Leave type spelling exactly as it was (unlike
122 // getTypeAsWritten().getAsString() which would spell enum types 'enum X').
123 StringRef DestTypeString =
124 Lexer::getSourceText(CharSourceRange::getTokenRange(
125 CastExpr->getLParenLoc().getLocWithOffset(1),
126 CastExpr->getRParenLoc().getLocWithOffset(-1)),
127 SM, getLangOpts());
128
129 auto Diag =
130 diag(CastExpr->getLocStart(), "C-style casts are discouraged; use %0");
131
132 auto ReplaceWithCast = [&](std::string CastText) {
133 const Expr *SubExpr = CastExpr->getSubExprAsWritten()->IgnoreImpCasts();
134 if (!isa<ParenExpr>(SubExpr)) {
135 CastText.push_back('(');
136 Diag << FixItHint::CreateInsertion(
137 Lexer::getLocForEndOfToken(SubExpr->getLocEnd(), 0, SM,
138 getLangOpts()),
139 ")");
140 }
141 Diag << FixItHint::CreateReplacement(ReplaceRange, CastText);
142 };
143 auto ReplaceWithNamedCast = [&](StringRef CastType) {
144 Diag << CastType;
145 ReplaceWithCast((CastType + "<" + DestTypeString + ">").str());
146 };
147
148 // Suggest appropriate C++ cast. See [expr.cast] for cast notation semantics.
149 switch (CastExpr->getCastKind()) {
150 case CK_FunctionToPointerDecay:
151 ReplaceWithNamedCast("static_cast");
152 return;
153 case CK_ConstructorConversion:
154 if (!CastExpr->getTypeAsWritten().hasQualifiers() &&
155 DestTypeAsWritten->isRecordType() &&
156 !DestTypeAsWritten->isElaboratedTypeSpecifier()) {
157 Diag << "constructor call syntax";
158 // FIXME: Validate DestTypeString, maybe.
159 ReplaceWithCast(DestTypeString.str());
160 } else {
161 ReplaceWithNamedCast("static_cast");
162 }
163 return;
164 case CK_NoOp:
165 if (FnToFnCast) {
166 ReplaceWithNamedCast("static_cast");
167 return;
168 }
169 if (SourceType == DestType) {
170 Diag << "static_cast (if needed, the cast may be redundant)";
171 ReplaceWithCast(("static_cast<" + DestTypeString + ">").str());
172 return;
173 }
174 if (needsConstCast(SourceType, DestType) &&
175 pointedUnqualifiedTypesAreEqual(SourceType, DestType)) {
176 ReplaceWithNamedCast("const_cast");
177 return;
178 }
179 if (DestType->isReferenceType()) {
180 QualType Dest = DestType.getNonReferenceType();
181 QualType Source = SourceType.getNonReferenceType();
182 if (Source == Dest.withConst() ||
183 SourceType.getNonReferenceType() == DestType.getNonReferenceType()) {
184 ReplaceWithNamedCast("const_cast");
185 return;
186 }
187 break;
188 }
189 // FALLTHROUGH
190 case clang::CK_IntegralCast:
191 // Convert integral and no-op casts between builtin types and enums to
192 // static_cast. A cast from enum to integer may be unnecessary, but it's
193 // still retained.
194 if ((SourceType->isBuiltinType() || SourceType->isEnumeralType()) &&
195 (DestType->isBuiltinType() || DestType->isEnumeralType())) {
196 ReplaceWithNamedCast("static_cast");
197 return;
198 }
199 break;
200 case CK_BitCast:
201 // FIXME: Suggest const_cast<...>(reinterpret_cast<...>(...)) replacement.
202 if (!needsConstCast(SourceType, DestType)) {
203 if (SourceType->isVoidPointerType())
204 ReplaceWithNamedCast("static_cast");
205 else
206 ReplaceWithNamedCast("reinterpret_cast");
207 return;
208 }
209 break;
210 default:
211 break;
212 }
213
214 Diag << "static_cast/const_cast/reinterpret_cast";
215 }
216
217 } // namespace readability
218 } // namespace google
219 } // namespace tidy
220 } // namespace clang
clang::ast_matchers
Definition: ClangTidyDiagnosticConsumer.h:27
clang::tidy::google::readability::needsConstCast
static bool needsConstCast(QualType SourceType, QualType DestType)
Definition: AvoidCStyleCastsCheck.cpp:37
clang::tidy::google::readability::pointedUnqualifiedTypesAreEqual
static bool pointedUnqualifiedTypesAreEqual(QualType T1, QualType T2)
Definition: AvoidCStyleCastsCheck.cpp:50
clang
Definition: AndroidTidyModule.cpp:28
AvoidCStyleCastsCheck.h