clang-tools 3.8.0

- Main Page
- Namespaces
- Classes
- Files

- File List
- File Members

ImplicitBoolCastCheck.cpp
Go to the documentation of this file.
1 //===--- ImplicitBoolCastCheck.cpp - clang-tidy----------------------------===//
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 "ImplicitBoolCastCheck.h"
11 #include "clang/AST/ASTContext.h"
12 #include "clang/ASTMatchers/ASTMatchFinder.h"
13 #include "clang/Lex/Lexer.h"
14
15 using namespace clang::ast_matchers;
16
17 namespace clang {
18 namespace tidy {
19
20 namespace {
21
22 const internal::VariadicDynCastAllOfMatcher<Stmt, ParenExpr> parenExpr;
23
24 AST_MATCHER_P(CastExpr, hasCastKind, CastKind, Kind) {
25 return Node.getCastKind() == Kind;
26 }
27
28 AST_MATCHER(QualType, isBool) {
29 return !Node.isNull() && Node->isBooleanType();
30 }
31
32 AST_MATCHER(Stmt, isMacroExpansion) {
33 SourceManager &SM = Finder->getASTContext().getSourceManager();
34 SourceLocation Loc = Node.getLocStart();
35 return SM.isMacroBodyExpansion(Loc) || SM.isMacroArgExpansion(Loc);
36 }
37
38 bool isNULLMacroExpansion(const Stmt *Statement, ASTContext &Context) {
39 SourceManager &SM = Context.getSourceManager();
40 const LangOptions &LO = Context.getLangOpts();
41 SourceLocation Loc = Statement->getLocStart();
42 return SM.isMacroBodyExpansion(Loc) &&
43 clang::Lexer::getImmediateMacroName(Loc, SM, LO) == "NULL";
44 }
45
46 AST_MATCHER(Stmt, isNULLMacroExpansion) {
47 return isNULLMacroExpansion(&Node, Finder->getASTContext());
48 }
49
50 ast_matchers::internal::Matcher<Expr> createExceptionCasesMatcher() {
51 return expr(anyOf(hasParent(explicitCastExpr()),
52 allOf(isMacroExpansion(), unless(isNULLMacroExpansion())),
53 isInTemplateInstantiation(),
54 hasAncestor(functionTemplateDecl())));
55 }
56
57 StatementMatcher createImplicitCastFromBoolMatcher() {
58 return implicitCastExpr(
59 unless(createExceptionCasesMatcher()),
60 anyOf(hasCastKind(CK_IntegralCast), hasCastKind(CK_IntegralToFloating),
61 // Prior to C++11 cast from bool literal to pointer was allowed.
62 allOf(anyOf(hasCastKind(CK_NullToPointer),
63 hasCastKind(CK_NullToMemberPointer)),
64 hasSourceExpression(cxxBoolLiteral()))),
65 hasSourceExpression(expr(hasType(qualType(isBool())))));
66 }
67
68 StringRef
69 getZeroLiteralToCompareWithForGivenType(CastKind CastExpressionKind,
70 QualType CastSubExpressionType,
71 ASTContext &Context) {
72 switch (CastExpressionKind) {
73 case CK_IntegralToBoolean:
74 return CastSubExpressionType->isUnsignedIntegerType() ? "0u" : "0";
75
76 case CK_FloatingToBoolean:
77 return Context.hasSameType(CastSubExpressionType, Context.FloatTy) ? "0.0f"
78 : "0.0";
79
80 case CK_PointerToBoolean:
81 case CK_MemberPointerToBoolean: // Fall-through on purpose.
82 return Context.getLangOpts().CPlusPlus11 ? "nullptr" : "0";
83
84 default:
85 llvm_unreachable("Unexpected cast kind");
86 }
87 }
88
89 bool isUnaryLogicalNotOperator(const Stmt *Statement) {
90 const auto *UnaryOperatorExpression =
91 llvm::dyn_cast<UnaryOperator>(Statement);
92 return UnaryOperatorExpression != nullptr &&
93 UnaryOperatorExpression->getOpcode() == UO_LNot;
94 }
95
96 bool areParensNeededForOverloadedOperator(OverloadedOperatorKind OperatorKind) {
97 switch (OperatorKind) {
98 case OO_New:
99 case OO_Delete: // Fall-through on purpose.
100 case OO_Array_New:
101 case OO_Array_Delete:
102 case OO_ArrowStar:
103 case OO_Arrow:
104 case OO_Call:
105 case OO_Subscript:
106 return false;
107
108 default:
109 return true;
110 }
111 }
112
113 bool areParensNeededForStatement(const Stmt *Statement) {
114 if (const CXXOperatorCallExpr *OverloadedOperatorCall =
115 llvm::dyn_cast<CXXOperatorCallExpr>(Statement)) {
116 return areParensNeededForOverloadedOperator(
117 OverloadedOperatorCall->getOperator());
118 }
119
120 return llvm::isa<BinaryOperator>(Statement) ||
121 llvm::isa<UnaryOperator>(Statement);
122 }
123
124 void addFixItHintsForGenericExpressionCastToBool(
125 DiagnosticBuilder &Diagnostic, const ImplicitCastExpr *CastExpression,
126 const Stmt *ParentStatement, ASTContext &Context) {
127 // In case of expressions like (! integer), we should remove the redundant not
128 // operator and use inverted comparison (integer == 0).
129 bool InvertComparison =
130 ParentStatement != nullptr && isUnaryLogicalNotOperator(ParentStatement);
131 if (InvertComparison) {
132 SourceLocation ParentStartLoc = ParentStatement->getLocStart();
133 SourceLocation ParentEndLoc =
134 llvm::cast<UnaryOperator>(ParentStatement)->getSubExpr()->getLocStart();
135 Diagnostic.AddFixItHint(FixItHint::CreateRemoval(
136 CharSourceRange::getCharRange(ParentStartLoc, ParentEndLoc)));
137
138 auto FurtherParents = Context.getParents(*ParentStatement);
139 ParentStatement = FurtherParents[0].get<Stmt>();
140 }
141
142 const Expr *SubExpression = CastExpression->getSubExpr();
143
144 bool NeedInnerParens = areParensNeededForStatement(SubExpression);
145 bool NeedOuterParens = ParentStatement != nullptr &&
146 areParensNeededForStatement(ParentStatement);
147
148 std::string StartLocInsertion;
149
150 if (NeedOuterParens) {
151 StartLocInsertion += "(";
152 }
153 if (NeedInnerParens) {
154 StartLocInsertion += "(";
155 }
156
157 if (!StartLocInsertion.empty()) {
158 SourceLocation StartLoc = CastExpression->getLocStart();
159 Diagnostic.AddFixItHint(
160 FixItHint::CreateInsertion(StartLoc, StartLocInsertion));
161 }
162
163 std::string EndLocInsertion;
164
165 if (NeedInnerParens) {
166 EndLocInsertion += ")";
167 }
168
169 if (InvertComparison) {
170 EndLocInsertion += " == ";
171 } else {
172 EndLocInsertion += " != ";
173 }
174
175 EndLocInsertion += getZeroLiteralToCompareWithForGivenType(
176 CastExpression->getCastKind(), SubExpression->getType(), Context);
177
178 if (NeedOuterParens) {
179 EndLocInsertion += ")";
180 }
181
182 SourceLocation EndLoc = Lexer::getLocForEndOfToken(
183 CastExpression->getLocEnd(), 0, Context.getSourceManager(),
184 Context.getLangOpts());
185 Diagnostic.AddFixItHint(FixItHint::CreateInsertion(EndLoc, EndLocInsertion));
186 }
187
188 StringRef getEquivalentBoolLiteralForExpression(const Expr *Expression,
189 ASTContext &Context) {
190 if (isNULLMacroExpansion(Expression, Context)) {
191 return "false";
192 }
193
194 if (const auto *IntLit = llvm::dyn_cast<IntegerLiteral>(Expression)) {
195 return (IntLit->getValue() == 0) ? "false" : "true";
196 }
197
198 if (const auto *FloatLit = llvm::dyn_cast<FloatingLiteral>(Expression)) {
199 llvm::APFloat FloatLitAbsValue = FloatLit->getValue();
200 FloatLitAbsValue.clearSign();
201 return (FloatLitAbsValue.bitcastToAPInt() == 0) ? "false" : "true";
202 }
203
204 if (const auto *CharLit = llvm::dyn_cast<CharacterLiteral>(Expression)) {
205 return (CharLit->getValue() == 0) ? "false" : "true";
206 }
207
208 if (llvm::isa<StringLiteral>(Expression->IgnoreCasts())) {
209 return "true";
210 }
211
212 return StringRef();
213 }
214
215 void addFixItHintsForLiteralCastToBool(DiagnosticBuilder &Diagnostic,
216 const ImplicitCastExpr *CastExpression,
217 StringRef EquivalentLiteralExpression) {
218 SourceLocation StartLoc = CastExpression->getLocStart();
219 SourceLocation EndLoc = CastExpression->getLocEnd();
220
221 Diagnostic.AddFixItHint(FixItHint::CreateReplacement(
222 CharSourceRange::getTokenRange(StartLoc, EndLoc),
223 EquivalentLiteralExpression));
224 }
225
226 void addFixItHintsForGenericExpressionCastFromBool(
227 DiagnosticBuilder &Diagnostic, const ImplicitCastExpr *CastExpression,
228 ASTContext &Context, StringRef OtherType) {
229 const Expr *SubExpression = CastExpression->getSubExpr();
230 bool NeedParens = !llvm::isa<ParenExpr>(SubExpression);
231
232 std::string StartLocInsertion = "static_cast<";
233 StartLocInsertion += OtherType.str();
234 StartLocInsertion += ">";
235 if (NeedParens) {
236 StartLocInsertion += "(";
237 }
238
239 SourceLocation StartLoc = CastExpression->getLocStart();
240 Diagnostic.AddFixItHint(
241 FixItHint::CreateInsertion(StartLoc, StartLocInsertion));
242
243 if (NeedParens) {
244 SourceLocation EndLoc = Lexer::getLocForEndOfToken(
245 CastExpression->getLocEnd(), 0, Context.getSourceManager(),
246 Context.getLangOpts());
247
248 Diagnostic.AddFixItHint(FixItHint::CreateInsertion(EndLoc, ")"));
249 }
250 }
251
252 StringRef getEquivalentLiteralForBoolLiteral(
253 const CXXBoolLiteralExpr *BoolLiteralExpression, QualType DestinationType,
254 ASTContext &Context) {
255 // Prior to C++11, false literal could be implicitly converted to pointer.
256 if (!Context.getLangOpts().CPlusPlus11 &&
257 (DestinationType->isPointerType() ||
258 DestinationType->isMemberPointerType()) &&
259 BoolLiteralExpression->getValue() == false) {
260 return "0";
261 }
262
263 if (DestinationType->isFloatingType()) {
264 if (BoolLiteralExpression->getValue() == true) {
265 return Context.hasSameType(DestinationType, Context.FloatTy) ? "1.0f"
266 : "1.0";
267 }
268 return Context.hasSameType(DestinationType, Context.FloatTy) ? "0.0f"
269 : "0.0";
270 }
271
272 if (BoolLiteralExpression->getValue() == true) {
273 return DestinationType->isUnsignedIntegerType() ? "1u" : "1";
274 }
275 return DestinationType->isUnsignedIntegerType() ? "0u" : "0";
276 }
277
278 void addFixItHintsForLiteralCastFromBool(DiagnosticBuilder &Diagnostic,
279 const ImplicitCastExpr *CastExpression,
280 ASTContext &Context,
281 QualType DestinationType) {
282 SourceLocation StartLoc = CastExpression->getLocStart();
283 SourceLocation EndLoc = CastExpression->getLocEnd();
284 const auto *BoolLiteralExpression =
285 llvm::dyn_cast<CXXBoolLiteralExpr>(CastExpression->getSubExpr());
286
287 Diagnostic.AddFixItHint(FixItHint::CreateReplacement(
288 CharSourceRange::getTokenRange(StartLoc, EndLoc),
289 getEquivalentLiteralForBoolLiteral(BoolLiteralExpression, DestinationType,
290 Context)));
291 }
292
293 StatementMatcher createConditionalExpressionMatcher() {
294 return stmt(anyOf(ifStmt(), conditionalOperator(),
295 parenExpr(hasParent(conditionalOperator()))));
296 }
297
298 bool isAllowedConditionalCast(const ImplicitCastExpr *CastExpression,
299 ASTContext &Context) {
300 auto AllowedConditionalMatcher = stmt(hasParent(stmt(
301 anyOf(createConditionalExpressionMatcher(),
302 unaryOperator(hasOperatorName("!"),
303 hasParent(createConditionalExpressionMatcher()))))));
304
305 auto MatchResult = match(AllowedConditionalMatcher, *CastExpression, Context);
306 return !MatchResult.empty();
307 }
308
309 } // anonymous namespace
310
311 void ImplicitBoolCastCheck::registerMatchers(MatchFinder *Finder) {
312 // This check doesn't make much sense if we run it on language without
313 // built-in bool support.
314 if (!getLangOpts().Bool) {
315 return;
316 }
317
318 Finder->addMatcher(
319 implicitCastExpr(
320 // Exclude cases common to implicit cast to and from bool.
321 unless(createExceptionCasesMatcher()),
322 // Exclude case of using if or while statements with variable
323 // declaration, e.g.:
324 // if (int var = functionCall()) {}
325 unless(
326 hasParent(stmt(anyOf(ifStmt(), whileStmt()), has(declStmt())))),
327 anyOf(hasCastKind(CK_IntegralToBoolean),
328 hasCastKind(CK_FloatingToBoolean),
329 hasCastKind(CK_PointerToBoolean),
330 hasCastKind(CK_MemberPointerToBoolean)),
331 // Retrive also parent statement, to check if we need additional
332 // parens in replacement.
333 anyOf(hasParent(stmt().bind("parentStmt")), anything()))
334 .bind("implicitCastToBool"),
335 this);
336
337 Finder->addMatcher(
338 implicitCastExpr(
339 createImplicitCastFromBoolMatcher(),
340 // Exclude comparisons of bools, as they are always cast to integers
341 // in such context:
342 // bool_expr_a == bool_expr_b
343 // bool_expr_a != bool_expr_b
344 unless(hasParent(binaryOperator(
345 anyOf(hasOperatorName("=="), hasOperatorName("!=")),
346 hasLHS(createImplicitCastFromBoolMatcher()),
347 hasRHS(createImplicitCastFromBoolMatcher())))),
348 // Check also for nested casts, for example: bool -> int -> float.
349 anyOf(hasParent(implicitCastExpr().bind("furtherImplicitCast")),
350 anything()))
351 .bind("implicitCastFromBool"),
352 this);
353 }
354
355 void ImplicitBoolCastCheck::check(const MatchFinder::MatchResult &Result) {
356 if (const auto *CastToBool =
357 Result.Nodes.getNodeAs<ImplicitCastExpr>("implicitCastToBool")) {
358 const auto *ParentStatement = Result.Nodes.getNodeAs<Stmt>("parentStmt");
359 return handleCastToBool(CastToBool, ParentStatement, *Result.Context);
360 }
361
362 if (const auto *CastFromBool =
363 Result.Nodes.getNodeAs<ImplicitCastExpr>("implicitCastFromBool")) {
364 const auto *FurtherImplicitCastExpression =
365 Result.Nodes.getNodeAs<ImplicitCastExpr>("furtherImplicitCast");
366 return handleCastFromBool(CastFromBool, FurtherImplicitCastExpression,
367 *Result.Context);
368 }
369 }
370
371 void ImplicitBoolCastCheck::handleCastToBool(
372 const ImplicitCastExpr *CastExpression, const Stmt *ParentStatement,
373 ASTContext &Context) {
374 if (AllowConditionalPointerCasts &&
375 (CastExpression->getCastKind() == CK_PointerToBoolean ||
376 CastExpression->getCastKind() == CK_MemberPointerToBoolean) &&
377 isAllowedConditionalCast(CastExpression, Context)) {
378 return;
379 }
380
381 if (AllowConditionalIntegerCasts &&
382 CastExpression->getCastKind() == CK_IntegralToBoolean &&
383 isAllowedConditionalCast(CastExpression, Context)) {
384 return;
385 }
386
387 std::string OtherType = CastExpression->getSubExpr()->getType().getAsString();
388 DiagnosticBuilder Diagnostic =
389 diag(CastExpression->getLocStart(), "implicit cast '%0' -> bool")
390 << OtherType;
391
392 StringRef EquivalentLiteralExpression = getEquivalentBoolLiteralForExpression(
393 CastExpression->getSubExpr(), Context);
394 if (!EquivalentLiteralExpression.empty()) {
395 addFixItHintsForLiteralCastToBool(Diagnostic, CastExpression,
396 EquivalentLiteralExpression);
397 } else {
398 addFixItHintsForGenericExpressionCastToBool(Diagnostic, CastExpression,
399 ParentStatement, Context);
400 }
401 }
402
403 void ImplicitBoolCastCheck::handleCastFromBool(
404 const ImplicitCastExpr *CastExpression,
405 const ImplicitCastExpr *FurtherImplicitCastExpression,
406 ASTContext &Context) {
407 QualType DestinationType = (FurtherImplicitCastExpression != nullptr)
408 ? FurtherImplicitCastExpression->getType()
409 : CastExpression->getType();
410 std::string DestinationTypeString = DestinationType.getAsString();
411 DiagnosticBuilder Diagnostic =
412 diag(CastExpression->getLocStart(), "implicit cast bool -> '%0'")
413 << DestinationTypeString;
414
415 if (llvm::isa<CXXBoolLiteralExpr>(CastExpression->getSubExpr())) {
416 addFixItHintsForLiteralCastFromBool(Diagnostic, CastExpression, Context,
417 DestinationType);
418 } else {
419 addFixItHintsForGenericExpressionCastFromBool(
420 Diagnostic, CastExpression, Context, DestinationTypeString);
421 }
422 }
423
424 } // namespace tidy
425 } // namespace clang
Loc
SourceLocation Loc
'#' location in the include directive
Definition: IncludeOrderCheck.cpp:34
ImplicitBoolCastCheck.h
Finder
std::unique_ptr< ast_matchers::MatchFinder > Finder
Definition: ClangTidy.cpp:188
clang::tidy::AST_MATCHER
AST_MATCHER(Stmt, isInsideOfRangeBeginEndStmt)
Definition: ProBoundsArrayToPointerDecayCheck.cpp:25
SM
SourceManager & SM
Definition: IncludeOrderCheck.cpp:44
clang::tidy::AST_MATCHER_P
AST_MATCHER_P(CXXForRangeStmt, hasRangeBeginEndStmt, ast_matchers::internal::Matcher< DeclStmt >, InnerMatcher)
Definition: ProBoundsArrayToPointerDecayCheck.cpp:19
Context
ClangTidyContext & Context
Definition: ClangTidy.cpp:93
Result
const NamedDecl * Result
Definition: USRFinder.cpp:121