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