clang-tools 3.8.0

- Main Page
- Namespaces
- Classes
- Files

- File List
- File Members

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
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
Finder
std::unique_ptr< ast_matchers::MatchFinder > Finder
Definition: ClangTidy.cpp:188
SM
SourceManager & SM
Definition: IncludeOrderCheck.cpp:44
clang::tidy::google::readability::needsConstCast
static bool needsConstCast(QualType SourceType, QualType DestType)
Definition: AvoidCStyleCastsCheck.cpp:36
clang::tidy::google::readability::pointedTypesAreEqual
static bool pointedTypesAreEqual(QualType SourceType, QualType DestType)
Definition: AvoidCStyleCastsCheck.cpp:48
Result
const NamedDecl * Result
Definition: USRFinder.cpp:121
AvoidCStyleCastsCheck.h