11 #include "clang/AST/ASTContext.h"
12 #include "clang/ASTMatchers/ASTMatchFinder.h"
13 #include "clang/Lex/Lexer.h"
15 using namespace clang::ast_matchers;
22 const internal::VariadicDynCastAllOfMatcher<Stmt, ParenExpr> parenExpr;
25 return Node.getCastKind() == Kind;
29 return !Node.isNull() && Node->isBooleanType();
33 SourceManager &
SM =
Finder->getASTContext().getSourceManager();
34 SourceLocation
Loc = Node.getLocStart();
35 return SM.isMacroBodyExpansion(Loc) || SM.isMacroArgExpansion(Loc);
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";
47 return isNULLMacroExpansion(&Node,
Finder->getASTContext());
50 ast_matchers::internal::Matcher<Expr> createExceptionCasesMatcher() {
51 return expr(anyOf(hasParent(explicitCastExpr()),
52 allOf(isMacroExpansion(), unless(isNULLMacroExpansion())),
53 isInTemplateInstantiation(),
54 hasAncestor(functionTemplateDecl())));
57 StatementMatcher createImplicitCastFromBoolMatcher() {
58 return implicitCastExpr(
59 unless(createExceptionCasesMatcher()),
60 anyOf(hasCastKind(CK_IntegralCast), hasCastKind(CK_IntegralToFloating),
62 allOf(anyOf(hasCastKind(CK_NullToPointer),
63 hasCastKind(CK_NullToMemberPointer)),
64 hasSourceExpression(cxxBoolLiteral()))),
65 hasSourceExpression(expr(hasType(qualType(isBool())))));
69 getZeroLiteralToCompareWithForGivenType(CastKind CastExpressionKind,
70 QualType CastSubExpressionType,
71 ASTContext &Context) {
72 switch (CastExpressionKind) {
73 case CK_IntegralToBoolean:
74 return CastSubExpressionType->isUnsignedIntegerType() ?
"0u" :
"0";
76 case CK_FloatingToBoolean:
77 return Context.hasSameType(CastSubExpressionType, Context.FloatTy) ?
"0.0f"
80 case CK_PointerToBoolean:
81 case CK_MemberPointerToBoolean:
82 return Context.getLangOpts().CPlusPlus11 ?
"nullptr" :
"0";
85 llvm_unreachable(
"Unexpected cast kind");
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;
96 bool areParensNeededForOverloadedOperator(OverloadedOperatorKind OperatorKind) {
97 switch (OperatorKind) {
101 case OO_Array_Delete:
113 bool areParensNeededForStatement(
const Stmt *Statement) {
114 if (
const CXXOperatorCallExpr *OverloadedOperatorCall =
115 llvm::dyn_cast<CXXOperatorCallExpr>(Statement)) {
116 return areParensNeededForOverloadedOperator(
117 OverloadedOperatorCall->getOperator());
120 return llvm::isa<BinaryOperator>(Statement) ||
121 llvm::isa<UnaryOperator>(Statement);
124 void addFixItHintsForGenericExpressionCastToBool(
125 DiagnosticBuilder &Diagnostic,
const ImplicitCastExpr *CastExpression,
126 const Stmt *ParentStatement, ASTContext &Context) {
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)));
138 auto FurtherParents = Context.getParents(*ParentStatement);
139 ParentStatement = FurtherParents[0].get<Stmt>();
142 const Expr *SubExpression = CastExpression->getSubExpr();
144 bool NeedInnerParens = areParensNeededForStatement(SubExpression);
145 bool NeedOuterParens = ParentStatement !=
nullptr &&
146 areParensNeededForStatement(ParentStatement);
148 std::string StartLocInsertion;
150 if (NeedOuterParens) {
151 StartLocInsertion +=
"(";
153 if (NeedInnerParens) {
154 StartLocInsertion +=
"(";
157 if (!StartLocInsertion.empty()) {
158 SourceLocation StartLoc = CastExpression->getLocStart();
159 Diagnostic.AddFixItHint(
160 FixItHint::CreateInsertion(StartLoc, StartLocInsertion));
163 std::string EndLocInsertion;
165 if (NeedInnerParens) {
166 EndLocInsertion +=
")";
169 if (InvertComparison) {
170 EndLocInsertion +=
" == ";
172 EndLocInsertion +=
" != ";
175 EndLocInsertion += getZeroLiteralToCompareWithForGivenType(
176 CastExpression->getCastKind(), SubExpression->getType(),
Context);
178 if (NeedOuterParens) {
179 EndLocInsertion +=
")";
182 SourceLocation EndLoc = Lexer::getLocForEndOfToken(
183 CastExpression->getLocEnd(), 0, Context.getSourceManager(),
184 Context.getLangOpts());
185 Diagnostic.AddFixItHint(FixItHint::CreateInsertion(EndLoc, EndLocInsertion));
188 StringRef getEquivalentBoolLiteralForExpression(
const Expr *Expression,
189 ASTContext &Context) {
190 if (isNULLMacroExpansion(Expression, Context)) {
194 if (
const auto *IntLit = llvm::dyn_cast<IntegerLiteral>(Expression)) {
195 return (IntLit->getValue() == 0) ?
"false" :
"true";
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";
204 if (
const auto *CharLit = llvm::dyn_cast<CharacterLiteral>(Expression)) {
205 return (CharLit->getValue() == 0) ?
"false" :
"true";
208 if (llvm::isa<StringLiteral>(Expression->IgnoreCasts())) {
215 void addFixItHintsForLiteralCastToBool(DiagnosticBuilder &Diagnostic,
216 const ImplicitCastExpr *CastExpression,
217 StringRef EquivalentLiteralExpression) {
218 SourceLocation StartLoc = CastExpression->getLocStart();
219 SourceLocation EndLoc = CastExpression->getLocEnd();
221 Diagnostic.AddFixItHint(FixItHint::CreateReplacement(
222 CharSourceRange::getTokenRange(StartLoc, EndLoc),
223 EquivalentLiteralExpression));
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);
232 std::string StartLocInsertion =
"static_cast<";
233 StartLocInsertion += OtherType.str();
234 StartLocInsertion +=
">";
236 StartLocInsertion +=
"(";
239 SourceLocation StartLoc = CastExpression->getLocStart();
240 Diagnostic.AddFixItHint(
241 FixItHint::CreateInsertion(StartLoc, StartLocInsertion));
244 SourceLocation EndLoc = Lexer::getLocForEndOfToken(
245 CastExpression->getLocEnd(), 0, Context.getSourceManager(),
246 Context.getLangOpts());
248 Diagnostic.AddFixItHint(FixItHint::CreateInsertion(EndLoc,
")"));
252 StringRef getEquivalentLiteralForBoolLiteral(
253 const CXXBoolLiteralExpr *BoolLiteralExpression, QualType DestinationType,
254 ASTContext &Context) {
256 if (!Context.getLangOpts().CPlusPlus11 &&
257 (DestinationType->isPointerType() ||
258 DestinationType->isMemberPointerType()) &&
259 BoolLiteralExpression->getValue() ==
false) {
263 if (DestinationType->isFloatingType()) {
264 if (BoolLiteralExpression->getValue() ==
true) {
265 return Context.hasSameType(DestinationType, Context.FloatTy) ?
"1.0f"
268 return Context.hasSameType(DestinationType, Context.FloatTy) ?
"0.0f"
272 if (BoolLiteralExpression->getValue() ==
true) {
273 return DestinationType->isUnsignedIntegerType() ?
"1u" :
"1";
275 return DestinationType->isUnsignedIntegerType() ?
"0u" :
"0";
278 void addFixItHintsForLiteralCastFromBool(DiagnosticBuilder &Diagnostic,
279 const ImplicitCastExpr *CastExpression,
281 QualType DestinationType) {
282 SourceLocation StartLoc = CastExpression->getLocStart();
283 SourceLocation EndLoc = CastExpression->getLocEnd();
284 const auto *BoolLiteralExpression =
285 llvm::dyn_cast<CXXBoolLiteralExpr>(CastExpression->getSubExpr());
287 Diagnostic.AddFixItHint(FixItHint::CreateReplacement(
288 CharSourceRange::getTokenRange(StartLoc, EndLoc),
289 getEquivalentLiteralForBoolLiteral(BoolLiteralExpression, DestinationType,
293 StatementMatcher createConditionalExpressionMatcher() {
294 return stmt(anyOf(ifStmt(), conditionalOperator(),
295 parenExpr(hasParent(conditionalOperator()))));
298 bool isAllowedConditionalCast(
const ImplicitCastExpr *CastExpression,
299 ASTContext &Context) {
300 auto AllowedConditionalMatcher = stmt(hasParent(stmt(
301 anyOf(createConditionalExpressionMatcher(),
302 unaryOperator(hasOperatorName(
"!"),
303 hasParent(createConditionalExpressionMatcher()))))));
305 auto MatchResult = match(AllowedConditionalMatcher, *CastExpression, Context);
306 return !MatchResult.empty();
311 void ImplicitBoolCastCheck::registerMatchers(MatchFinder *
Finder) {
314 if (!getLangOpts().Bool) {
321 unless(createExceptionCasesMatcher()),
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)),
333 anyOf(hasParent(stmt().bind(
"parentStmt")), anything()))
334 .bind(
"implicitCastToBool"),
339 createImplicitCastFromBoolMatcher(),
344 unless(hasParent(binaryOperator(
345 anyOf(hasOperatorName(
"=="), hasOperatorName(
"!=")),
346 hasLHS(createImplicitCastFromBoolMatcher()),
347 hasRHS(createImplicitCastFromBoolMatcher())))),
349 anyOf(hasParent(implicitCastExpr().bind(
"furtherImplicitCast")),
351 .bind(
"implicitCastFromBool"),
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);
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,
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)) {
381 if (AllowConditionalIntegerCasts &&
382 CastExpression->getCastKind() == CK_IntegralToBoolean &&
383 isAllowedConditionalCast(CastExpression, Context)) {
387 std::string OtherType = CastExpression->getSubExpr()->getType().getAsString();
388 DiagnosticBuilder Diagnostic =
389 diag(CastExpression->getLocStart(),
"implicit cast '%0' -> bool")
392 StringRef EquivalentLiteralExpression = getEquivalentBoolLiteralForExpression(
393 CastExpression->getSubExpr(),
Context);
394 if (!EquivalentLiteralExpression.empty()) {
395 addFixItHintsForLiteralCastToBool(Diagnostic, CastExpression,
396 EquivalentLiteralExpression);
398 addFixItHintsForGenericExpressionCastToBool(Diagnostic, CastExpression,
399 ParentStatement, Context);
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;
415 if (llvm::isa<CXXBoolLiteralExpr>(CastExpression->getSubExpr())) {
416 addFixItHintsForLiteralCastFromBool(Diagnostic, CastExpression, Context,
419 addFixItHintsForGenericExpressionCastFromBool(
420 Diagnostic, CastExpression, Context, DestinationTypeString);
SourceLocation Loc
'#' location in the include directive
std::unique_ptr< ast_matchers::MatchFinder > Finder
AST_MATCHER(Stmt, isInsideOfRangeBeginEndStmt)
AST_MATCHER_P(CXXForRangeStmt, hasRangeBeginEndStmt, ast_matchers::internal::Matcher< DeclStmt >, InnerMatcher)
ClangTidyContext & Context