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#include "clang/Tooling/FixIt.h"
25 const SourceManager &SM = Finder->getASTContext().getSourceManager();
26 const SourceLocation Loc = Node.getBeginLoc();
27 return SM.isMacroBodyExpansion(Loc) || SM.isMacroArgExpansion(Loc);
30AST_MATCHER(Stmt, isC) {
return Finder->getASTContext().getLangOpts().C99; }
34bool isNULLMacroExpansion(
const Stmt *Statement, ASTContext &Context) {
35 const SourceManager &SM = Context.getSourceManager();
36 const LangOptions &LO = Context.getLangOpts();
37 const SourceLocation Loc = Statement->getBeginLoc();
38 return SM.isMacroBodyExpansion(Loc) &&
39 Lexer::getImmediateMacroName(Loc, SM, LO) ==
"NULL";
43 return isNULLMacroExpansion(&Node, Finder->getASTContext());
50 ASTContext &Context) {
51 switch (CastExprKind) {
52 case CK_IntegralToBoolean:
53 return Type->isUnsignedIntegerType() ?
"0u" :
"0";
55 case CK_FloatingToBoolean:
56 return ASTContext::hasSameType(Type, Context.FloatTy) ?
"0.0f" :
"0.0";
58 case CK_PointerToBoolean:
59 case CK_MemberPointerToBoolean:
60 return (Context.getLangOpts().CPlusPlus11 || Context.getLangOpts().C23)
65 llvm_unreachable(
"Unexpected cast kind");
70 const auto *UnaryOperatorExpr = dyn_cast<UnaryOperator>(Statement);
71 return UnaryOperatorExpr && UnaryOperatorExpr->getOpcode() == UO_LNot;
75 const ImplicitCastExpr *Cast,
76 const Stmt *Parent, ASTContext &Context,
77 bool UseUpperCaseLiteralSuffix) {
80 const bool InvertComparison =
82 if (InvertComparison) {
83 const SourceLocation ParentStartLoc = Parent->getBeginLoc();
84 const SourceLocation ParentEndLoc =
85 cast<UnaryOperator>(Parent)->getSubExpr()->getBeginLoc();
86 Diag << FixItHint::CreateRemoval(
87 CharSourceRange::getCharRange(ParentStartLoc, ParentEndLoc));
89 Parent = Context.getParents(*Parent)[0].get<Stmt>();
92 const Expr *SubExpr = Cast->getSubExpr();
94 const bool NeedInnerParens =
96 const bool NeedOuterParens =
99 std::string StartLocInsertion;
102 StartLocInsertion +=
'(';
104 StartLocInsertion +=
'(';
106 if (!StartLocInsertion.empty())
107 Diag << FixItHint::CreateInsertion(Cast->getBeginLoc(), StartLocInsertion);
109 std::string EndLocInsertion;
112 EndLocInsertion +=
')';
114 if (InvertComparison)
115 EndLocInsertion +=
" == ";
117 EndLocInsertion +=
" != ";
120 Cast->getCastKind(), SubExpr->getType(), Context);
122 if (UseUpperCaseLiteralSuffix)
123 EndLocInsertion += ZeroLiteral.upper();
125 EndLocInsertion += ZeroLiteral;
128 EndLocInsertion +=
')';
130 const SourceLocation EndLoc = Lexer::getLocForEndOfToken(
131 Cast->getEndLoc(), 0, Context.getSourceManager(), Context.getLangOpts());
132 Diag << FixItHint::CreateInsertion(EndLoc, EndLocInsertion);
136 ASTContext &Context) {
137 if (isNULLMacroExpansion(Expression, Context))
140 if (
const auto *IntLit = dyn_cast<IntegerLiteral>(Expression->IgnoreParens()))
141 return (IntLit->getValue() == 0) ?
"false" :
"true";
143 if (
const auto *FloatLit = dyn_cast<FloatingLiteral>(Expression)) {
144 llvm::APFloat FloatLitAbsValue = FloatLit->getValue();
145 FloatLitAbsValue.clearSign();
146 return (FloatLitAbsValue.bitcastToAPInt() == 0) ?
"false" :
"true";
149 if (
const auto *CharLit = dyn_cast<CharacterLiteral>(Expression))
150 return (CharLit->getValue() == 0) ?
"false" :
"true";
152 if (isa<StringLiteral>(Expression->IgnoreCasts()))
159 const SourceRange PrefixRange(Loc.getLocWithOffset(-1), Loc);
160 const StringRef SpaceBeforeStmtStr = Lexer::getSourceText(
161 CharSourceRange::getCharRange(PrefixRange), Context.getSourceManager(),
162 Context.getLangOpts(),
nullptr);
163 if (SpaceBeforeStmtStr.empty())
166 const StringRef AllowedCharacters(
" \t\n\v\f\r(){}[]<>;,+=-|&~!^*/");
167 return !AllowedCharacters.contains(SpaceBeforeStmtStr.back());
171 const ImplicitCastExpr *Cast,
173 StringRef OtherType) {
174 if (!Context.getLangOpts().CPlusPlus) {
175 Diag << FixItHint::CreateInsertion(Cast->getBeginLoc(),
176 (Twine(
"(") + OtherType +
")").str());
180 const Expr *SubExpr = Cast->getSubExpr();
181 const bool NeedParens = !isa<ParenExpr>(SubExpr->IgnoreImplicit());
184 Diag << FixItHint::CreateInsertion(
185 Cast->getBeginLoc(), (Twine() + (NeedSpace ?
" " :
"") +
"static_cast<" +
186 OtherType +
">" + (NeedParens ?
"(" :
""))
190 const SourceLocation EndLoc = Lexer::getLocForEndOfToken(
191 Cast->getEndLoc(), 0, Context.getSourceManager(),
192 Context.getLangOpts());
194 Diag << FixItHint::CreateInsertion(EndLoc,
")");
200 QualType DestType, ASTContext &Context) {
202 if (!Context.getLangOpts().CPlusPlus11 &&
203 (DestType->isPointerType() || DestType->isMemberPointerType()) &&
204 BoolLiteral->getValue() ==
false) {
208 if (DestType->isFloatingType()) {
209 if (ASTContext::hasSameType(DestType, Context.FloatTy))
210 return BoolLiteral->getValue() ?
"1.0f" :
"0.0f";
211 return BoolLiteral->getValue() ?
"1.0" :
"0.0";
214 if (DestType->isUnsignedIntegerType())
215 return BoolLiteral->getValue() ?
"1u" :
"0u";
216 return BoolLiteral->getValue() ?
"1" :
"0";
220 ASTContext &Context) {
221 std::queue<const Stmt *> Q;
224 const TraversalKindScope RAII(Context, TK_AsIs);
227 for (
const auto &N : Context.getParents(*Q.front())) {
228 const Stmt *S = N.get<Stmt>();
231 if (isa<IfStmt>(S) || isa<ConditionalOperator>(S) || isa<ForStmt>(S) ||
232 isa<WhileStmt>(S) || isa<DoStmt>(S) ||
233 isa<BinaryConditionalOperator>(S))
235 if (isa<ParenExpr>(S) || isa<ImplicitCastExpr>(S) ||
237 (isa<BinaryOperator>(S) && cast<BinaryOperator>(S)->isLogicalOp())) {
249 const Expr *SubExpr = Cast->getSubExpr()->IgnoreParenImpCasts();
250 if (
const auto *BinOp = dyn_cast<BinaryOperator>(SubExpr))
251 return BinOp->isLogicalOp();
252 if (
const auto *UnOp = dyn_cast<UnaryOperator>(SubExpr))
253 return UnOp->getOpcode() == UO_LNot;
260 AllowIntegerConditions(Options.get(
"AllowIntegerConditions", false)),
261 AllowPointerConditions(Options.get(
"AllowPointerConditions", false)),
262 AllowLogicalOperatorConversion(
263 Options.get(
"AllowLogicalOperatorConversion", false)),
264 UseUpperCaseLiteralSuffix(
265 Options.get(
"UseUpperCaseLiteralSuffix", false)) {}
269 Options.store(Opts,
"AllowIntegerConditions", AllowIntegerConditions);
270 Options.store(Opts,
"AllowPointerConditions", AllowPointerConditions);
271 Options.store(Opts,
"AllowLogicalOperatorConversion",
272 AllowLogicalOperatorConversion);
273 Options.store(Opts,
"UseUpperCaseLiteralSuffix", UseUpperCaseLiteralSuffix);
277 auto ExceptionCases =
278 expr(anyOf(allOf(isMacroExpansion(), unless(isNULLMacroExpansion())),
279 has(ignoringImplicit(
280 memberExpr(hasDeclaration(fieldDecl(hasBitWidth(1)))))),
281 hasParent(explicitCastExpr()),
282 expr(hasType(qualType().bind(
"type")),
283 hasParent(initListExpr(hasParent(explicitCastExpr(
284 hasType(qualType(equalsBoundNode(
"type"))))))))));
285 auto ImplicitCastFromBool = implicitCastExpr(
286 anyOf(hasCastKind(CK_IntegralCast), hasCastKind(CK_IntegralToFloating),
288 allOf(anyOf(hasCastKind(CK_NullToPointer),
289 hasCastKind(CK_NullToMemberPointer)),
290 hasSourceExpression(cxxBoolLiteral()))),
291 hasSourceExpression(expr(hasType(booleanType()))));
293 binaryOperator(hasOperatorName(
"^"), hasLHS(ImplicitCastFromBool),
294 hasRHS(ImplicitCastFromBool));
295 auto ComparisonInCall = allOf(
296 hasParent(callExpr()),
297 hasSourceExpression(binaryOperator(hasAnyOperatorName(
"==",
"!="))));
299 auto IsInCompilerGeneratedFunction = hasAncestor(namedDecl(anyOf(
300 isImplicit(), functionDecl(isDefaulted()), functionTemplateDecl())));
305 anyOf(hasCastKind(CK_IntegralToBoolean),
306 hasCastKind(CK_FloatingToBoolean),
307 hasCastKind(CK_PointerToBoolean),
308 hasCastKind(CK_MemberPointerToBoolean)),
311 hasSourceExpression(ignoringParens(
312 binaryOperator(hasAnyOperatorName(
313 ">",
">=",
"==",
"!=",
"<",
"<=")))))),
318 stmt(anyOf(ifStmt(), whileStmt()), has(declStmt())))),
320 unless(ExceptionCases), unless(has(BoolXor)),
322 unless(ComparisonInCall),
325 optionally(hasParent(stmt().bind(
"parentStmt"))),
326 unless(isInTemplateInstantiation()),
327 unless(IsInCompilerGeneratedFunction))
328 .bind(
"implicitCastToBool")),
331 auto BoolComparison = binaryOperator(hasAnyOperatorName(
"==",
"!="),
332 hasLHS(ImplicitCastFromBool),
333 hasRHS(ImplicitCastFromBool));
334 auto BoolOpAssignment = binaryOperator(hasAnyOperatorName(
"|=",
"&="),
335 hasLHS(expr(hasType(booleanType()))));
336 auto BitfieldAssignment = binaryOperator(
337 hasLHS(memberExpr(hasDeclaration(fieldDecl(hasBitWidth(1))))));
338 auto BitfieldConstruct = cxxConstructorDecl(hasDescendant(cxxCtorInitializer(
339 withInitializer(equalsBoundNode(
"implicitCastFromBool")),
340 forField(hasBitWidth(1)))));
345 ImplicitCastFromBool, unless(ExceptionCases),
351 binaryOperator(anyOf(BoolComparison, BoolXor,
352 BoolOpAssignment, BitfieldAssignment)))),
354 unless(allOf(isC(), hasParent(binaryOperator(
355 hasAnyOperatorName(
"&&",
"||"))))),
356 implicitCastExpr().bind(
"implicitCastFromBool"),
357 unless(hasParent(BitfieldConstruct)),
360 hasParent(implicitCastExpr().bind(
"furtherImplicitCast"))),
361 unless(isInTemplateInstantiation()),
362 unless(IsInCompilerGeneratedFunction))),
367 const MatchFinder::MatchResult &Result) {
368 if (
const auto *CastToBool =
369 Result.Nodes.getNodeAs<ImplicitCastExpr>(
"implicitCastToBool")) {
370 const auto *Parent = Result.Nodes.getNodeAs<Stmt>(
"parentStmt");
371 handleCastToBool(CastToBool, Parent, *Result.Context);
375 if (
const auto *CastFromBool =
376 Result.Nodes.getNodeAs<ImplicitCastExpr>(
"implicitCastFromBool")) {
377 const auto *NextImplicitCast =
378 Result.Nodes.getNodeAs<ImplicitCastExpr>(
"furtherImplicitCast");
379 handleCastFromBool(CastFromBool, NextImplicitCast, *Result.Context);
383void ImplicitBoolConversionCheck::handleCastToBool(
const ImplicitCastExpr *Cast,
385 ASTContext &Context) {
386 if (AllowPointerConditions &&
387 (Cast->getCastKind() == CK_PointerToBoolean ||
388 Cast->getCastKind() == CK_MemberPointerToBoolean) &&
393 if (AllowIntegerConditions && Cast->getCastKind() == CK_IntegralToBoolean &&
398 if (AllowLogicalOperatorConversion &&
399 Cast->getCastKind() == CK_IntegralToBoolean &&
404 auto Diag = diag(Context.getSourceManager().getFileLoc(Cast->getBeginLoc()),
405 "implicit conversion %0 -> 'bool'")
406 << Cast->getSubExpr()->getType();
408 const StringRef EquivalentLiteral =
410 if (!EquivalentLiteral.empty()) {
411 Diag << tooling::fixit::createReplacement(*Cast, EquivalentLiteral);
414 UseUpperCaseLiteralSuffix);
418void ImplicitBoolConversionCheck::handleCastFromBool(
419 const ImplicitCastExpr *Cast,
const ImplicitCastExpr *NextImplicitCast,
420 ASTContext &Context) {
421 const QualType DestType =
422 NextImplicitCast ? NextImplicitCast->getType() : Cast->getType();
423 auto Diag = diag(Context.getSourceManager().getFileLoc(Cast->getBeginLoc()),
424 "implicit conversion 'bool' -> %0")
427 if (
const auto *BoolLiteral =
428 dyn_cast<CXXBoolLiteralExpr>(Cast->getSubExpr()->IgnoreParens())) {
429 const auto EquivalentForBoolLiteral =
431 if (UseUpperCaseLiteralSuffix)
432 Diag << tooling::fixit::createReplacement(
433 *Cast, EquivalentForBoolLiteral.upper());
435 Diag << tooling::fixit::createReplacement(*Cast,
436 EquivalentForBoolLiteral);
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
void storeOptions(ClangTidyOptions::OptionMap &Opts) override
ImplicitBoolConversionCheck(StringRef Name, ClangTidyContext *Context)
void registerMatchers(ast_matchers::MatchFinder *Finder) override
AST_MATCHER(BinaryOperator, isRelationalOperator)
static bool isCastAllowedInCondition(const ImplicitCastExpr *Cast, ASTContext &Context)
static bool isUnaryLogicalNotOperator(const Stmt *Statement)
static void fixGenericExprCastToBool(DiagnosticBuilder &Diag, const ImplicitCastExpr *Cast, const Stmt *Parent, ASTContext &Context, bool UseUpperCaseLiteralSuffix)
static StringRef getZeroLiteralToCompareWithForType(CastKind CastExprKind, QualType Type, ASTContext &Context)
static bool isLogicalOperatorResult(const ImplicitCastExpr *Cast)
static void fixGenericExprCastFromBool(DiagnosticBuilder &Diag, const ImplicitCastExpr *Cast, ASTContext &Context, StringRef OtherType)
static StringRef getEquivalentBoolLiteralForExpr(const Expr *Expression, ASTContext &Context)
static bool needsSpacePrefix(SourceLocation Loc, ASTContext &Context)
static StringRef getEquivalentForBoolLiteral(const CXXBoolLiteralExpr *BoolLiteral, QualType DestType, ASTContext &Context)
bool areParensNeededForStatement(const Stmt &Node)
llvm::StringMap< ClangTidyValue > OptionMap