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 SourceManager &SM = Finder->getASTContext().getSourceManager();
26 SourceLocation Loc = Node.getBeginLoc();
27 return SM.isMacroBodyExpansion(Loc) || SM.isMacroArgExpansion(Loc);
30AST_MATCHER(Stmt, isC23) {
return Finder->getASTContext().getLangOpts().C23; }
32bool isNULLMacroExpansion(
const Stmt *Statement, ASTContext &Context) {
33 SourceManager &SM = Context.getSourceManager();
34 const LangOptions &LO = Context.getLangOpts();
35 SourceLocation Loc = Statement->getBeginLoc();
36 return SM.isMacroBodyExpansion(Loc) &&
37 Lexer::getImmediateMacroName(Loc, SM, LO) ==
"NULL";
41 return isNULLMacroExpansion(&Node, Finder->getASTContext());
48 ASTContext &Context) {
49 switch (CastExprKind) {
50 case CK_IntegralToBoolean:
51 return Type->isUnsignedIntegerType() ?
"0u" :
"0";
53 case CK_FloatingToBoolean:
54 return ASTContext::hasSameType(Type, Context.FloatTy) ?
"0.0f" :
"0.0";
56 case CK_PointerToBoolean:
57 case CK_MemberPointerToBoolean:
58 return (Context.getLangOpts().CPlusPlus11 || Context.getLangOpts().C23)
63 llvm_unreachable(
"Unexpected cast kind");
68 const auto *UnaryOperatorExpr = dyn_cast<UnaryOperator>(Statement);
69 return UnaryOperatorExpr && UnaryOperatorExpr->getOpcode() == UO_LNot;
73 const ImplicitCastExpr *Cast,
74 const Stmt *Parent, ASTContext &Context,
75 bool UseUpperCaseLiteralSuffix) {
78 bool InvertComparison =
80 if (InvertComparison) {
81 SourceLocation ParentStartLoc = Parent->getBeginLoc();
82 SourceLocation ParentEndLoc =
83 cast<UnaryOperator>(Parent)->getSubExpr()->getBeginLoc();
84 Diag << FixItHint::CreateRemoval(
85 CharSourceRange::getCharRange(ParentStartLoc, ParentEndLoc));
87 Parent = Context.getParents(*Parent)[0].get<Stmt>();
90 const Expr *SubExpr = Cast->getSubExpr();
92 bool NeedInnerParens =
94 bool NeedOuterParens =
97 std::string StartLocInsertion;
99 if (NeedOuterParens) {
100 StartLocInsertion +=
"(";
102 if (NeedInnerParens) {
103 StartLocInsertion +=
"(";
106 if (!StartLocInsertion.empty()) {
107 Diag << FixItHint::CreateInsertion(Cast->getBeginLoc(), StartLocInsertion);
110 std::string EndLocInsertion;
112 if (NeedInnerParens) {
113 EndLocInsertion +=
")";
116 if (InvertComparison) {
117 EndLocInsertion +=
" == ";
119 EndLocInsertion +=
" != ";
123 Cast->getCastKind(), SubExpr->getType(), Context);
125 if (UseUpperCaseLiteralSuffix)
126 EndLocInsertion += ZeroLiteral.upper();
128 EndLocInsertion += ZeroLiteral;
130 if (NeedOuterParens) {
131 EndLocInsertion +=
")";
134 SourceLocation EndLoc = Lexer::getLocForEndOfToken(
135 Cast->getEndLoc(), 0, Context.getSourceManager(), Context.getLangOpts());
136 Diag << FixItHint::CreateInsertion(EndLoc, EndLocInsertion);
140 ASTContext &Context) {
141 if (isNULLMacroExpansion(Expression, Context)) {
145 if (
const auto *IntLit =
146 dyn_cast<IntegerLiteral>(Expression->IgnoreParens())) {
147 return (IntLit->getValue() == 0) ?
"false" :
"true";
150 if (
const auto *FloatLit = dyn_cast<FloatingLiteral>(Expression)) {
151 llvm::APFloat FloatLitAbsValue = FloatLit->getValue();
152 FloatLitAbsValue.clearSign();
153 return (FloatLitAbsValue.bitcastToAPInt() == 0) ?
"false" :
"true";
156 if (
const auto *CharLit = dyn_cast<CharacterLiteral>(Expression)) {
157 return (CharLit->getValue() == 0) ?
"false" :
"true";
160 if (isa<StringLiteral>(Expression->IgnoreCasts())) {
168 SourceRange PrefixRange(Loc.getLocWithOffset(-1), Loc);
169 StringRef SpaceBeforeStmtStr = Lexer::getSourceText(
170 CharSourceRange::getCharRange(PrefixRange), Context.getSourceManager(),
171 Context.getLangOpts(),
nullptr);
172 if (SpaceBeforeStmtStr.empty())
175 const StringRef AllowedCharacters(
" \t\n\v\f\r(){}[]<>;,+=-|&~!^*/");
176 return !AllowedCharacters.contains(SpaceBeforeStmtStr.back());
180 const ImplicitCastExpr *Cast,
182 StringRef OtherType) {
183 if (!Context.getLangOpts().CPlusPlus) {
184 Diag << FixItHint::CreateInsertion(Cast->getBeginLoc(),
185 (Twine(
"(") + OtherType +
")").str());
189 const Expr *SubExpr = Cast->getSubExpr();
190 const bool NeedParens = !isa<ParenExpr>(SubExpr->IgnoreImplicit());
193 Diag << FixItHint::CreateInsertion(
194 Cast->getBeginLoc(), (Twine() + (NeedSpace ?
" " :
"") +
"static_cast<" +
195 OtherType +
">" + (NeedParens ?
"(" :
""))
199 SourceLocation EndLoc = Lexer::getLocForEndOfToken(
200 Cast->getEndLoc(), 0, Context.getSourceManager(),
201 Context.getLangOpts());
203 Diag << FixItHint::CreateInsertion(EndLoc,
")");
209 QualType DestType, ASTContext &Context) {
211 if (!Context.getLangOpts().CPlusPlus11 &&
212 (DestType->isPointerType() || DestType->isMemberPointerType()) &&
213 BoolLiteral->getValue() ==
false) {
217 if (DestType->isFloatingType()) {
218 if (ASTContext::hasSameType(DestType, Context.FloatTy)) {
219 return BoolLiteral->getValue() ?
"1.0f" :
"0.0f";
221 return BoolLiteral->getValue() ?
"1.0" :
"0.0";
224 if (DestType->isUnsignedIntegerType()) {
225 return BoolLiteral->getValue() ?
"1u" :
"0u";
227 return BoolLiteral->getValue() ?
"1" :
"0";
231 ASTContext &Context) {
232 std::queue<const Stmt *> Q;
235 TraversalKindScope RAII(Context, TK_AsIs);
238 for (
const auto &N : Context.getParents(*Q.front())) {
239 const Stmt *S = N.get<Stmt>();
242 if (isa<IfStmt>(S) || isa<ConditionalOperator>(S) || isa<ForStmt>(S) ||
243 isa<WhileStmt>(S) || isa<DoStmt>(S) ||
244 isa<BinaryConditionalOperator>(S))
246 if (isa<ParenExpr>(S) || isa<ImplicitCastExpr>(S) ||
248 (isa<BinaryOperator>(S) && cast<BinaryOperator>(S)->isLogicalOp())) {
262 AllowIntegerConditions(Options.get(
"AllowIntegerConditions", false)),
263 AllowPointerConditions(Options.get(
"AllowPointerConditions", false)),
264 UseUpperCaseLiteralSuffix(
265 Options.get(
"UseUpperCaseLiteralSuffix", false)) {}
269 Options.store(Opts,
"AllowIntegerConditions", AllowIntegerConditions);
270 Options.store(Opts,
"AllowPointerConditions", AllowPointerConditions);
271 Options.store(Opts,
"UseUpperCaseLiteralSuffix", UseUpperCaseLiteralSuffix);
275 auto ExceptionCases =
276 expr(anyOf(allOf(isMacroExpansion(), unless(isNULLMacroExpansion())),
277 has(ignoringImplicit(
278 memberExpr(hasDeclaration(fieldDecl(hasBitWidth(1)))))),
279 hasParent(explicitCastExpr()),
280 expr(hasType(qualType().bind(
"type")),
281 hasParent(initListExpr(hasParent(explicitCastExpr(
282 hasType(qualType(equalsBoundNode(
"type"))))))))));
283 auto ImplicitCastFromBool = implicitCastExpr(
284 anyOf(hasCastKind(CK_IntegralCast), hasCastKind(CK_IntegralToFloating),
286 allOf(anyOf(hasCastKind(CK_NullToPointer),
287 hasCastKind(CK_NullToMemberPointer)),
288 hasSourceExpression(cxxBoolLiteral()))),
289 hasSourceExpression(expr(hasType(booleanType()))));
291 binaryOperator(hasOperatorName(
"^"), hasLHS(ImplicitCastFromBool),
292 hasRHS(ImplicitCastFromBool));
293 auto ComparisonInCall = allOf(
294 hasParent(callExpr()),
295 hasSourceExpression(binaryOperator(hasAnyOperatorName(
"==",
"!="))));
297 auto IsInCompilerGeneratedFunction = hasAncestor(namedDecl(anyOf(
298 isImplicit(), functionDecl(isDefaulted()), functionTemplateDecl())));
303 anyOf(hasCastKind(CK_IntegralToBoolean),
304 hasCastKind(CK_FloatingToBoolean),
305 hasCastKind(CK_PointerToBoolean),
306 hasCastKind(CK_MemberPointerToBoolean)),
308 unless(allOf(isC23(),
309 hasSourceExpression(ignoringParens(
310 binaryOperator(hasAnyOperatorName(
311 ">",
">=",
"==",
"!=",
"<",
"<=")))))),
316 stmt(anyOf(ifStmt(), whileStmt()), has(declStmt())))),
318 unless(ExceptionCases), unless(has(BoolXor)),
320 unless(ComparisonInCall),
323 optionally(hasParent(stmt().bind(
"parentStmt"))),
324 unless(isInTemplateInstantiation()),
325 unless(IsInCompilerGeneratedFunction))
326 .bind(
"implicitCastToBool")),
329 auto BoolComparison = binaryOperator(hasAnyOperatorName(
"==",
"!="),
330 hasLHS(ImplicitCastFromBool),
331 hasRHS(ImplicitCastFromBool));
332 auto BoolOpAssignment = binaryOperator(hasAnyOperatorName(
"|=",
"&="),
333 hasLHS(expr(hasType(booleanType()))));
334 auto BitfieldAssignment = binaryOperator(
335 hasLHS(memberExpr(hasDeclaration(fieldDecl(hasBitWidth(1))))));
336 auto BitfieldConstruct = cxxConstructorDecl(hasDescendant(cxxCtorInitializer(
337 withInitializer(equalsBoundNode(
"implicitCastFromBool")),
338 forField(hasBitWidth(1)))));
343 ImplicitCastFromBool, unless(ExceptionCases),
349 binaryOperator(anyOf(BoolComparison, BoolXor,
350 BoolOpAssignment, BitfieldAssignment)))),
351 implicitCastExpr().bind(
"implicitCastFromBool"),
352 unless(hasParent(BitfieldConstruct)),
355 hasParent(implicitCastExpr().bind(
"furtherImplicitCast"))),
356 unless(isInTemplateInstantiation()),
357 unless(IsInCompilerGeneratedFunction))),
362 const MatchFinder::MatchResult &Result) {
364 if (
const auto *CastToBool =
365 Result.Nodes.getNodeAs<ImplicitCastExpr>(
"implicitCastToBool")) {
366 const auto *Parent = Result.Nodes.getNodeAs<Stmt>(
"parentStmt");
367 handleCastToBool(CastToBool, Parent, *Result.Context);
371 if (
const auto *CastFromBool =
372 Result.Nodes.getNodeAs<ImplicitCastExpr>(
"implicitCastFromBool")) {
373 const auto *NextImplicitCast =
374 Result.Nodes.getNodeAs<ImplicitCastExpr>(
"furtherImplicitCast");
375 handleCastFromBool(CastFromBool, NextImplicitCast, *Result.Context);
379void ImplicitBoolConversionCheck::handleCastToBool(
const ImplicitCastExpr *Cast,
381 ASTContext &Context) {
382 if (AllowPointerConditions &&
383 (Cast->getCastKind() == CK_PointerToBoolean ||
384 Cast->getCastKind() == CK_MemberPointerToBoolean) &&
389 if (AllowIntegerConditions && Cast->getCastKind() == CK_IntegralToBoolean &&
394 auto Diag = diag(Cast->getBeginLoc(),
"implicit conversion %0 -> 'bool'")
395 << Cast->getSubExpr()->getType();
397 StringRef EquivalentLiteral =
399 if (!EquivalentLiteral.empty()) {
400 Diag << tooling::fixit::createReplacement(*Cast, EquivalentLiteral);
403 UseUpperCaseLiteralSuffix);
407void ImplicitBoolConversionCheck::handleCastFromBool(
408 const ImplicitCastExpr *Cast,
const ImplicitCastExpr *NextImplicitCast,
409 ASTContext &Context) {
411 NextImplicitCast ? NextImplicitCast->getType() : Cast->getType();
412 auto Diag = diag(Cast->getBeginLoc(),
"implicit conversion 'bool' -> %0")
415 if (
const auto *BoolLiteral =
416 dyn_cast<CXXBoolLiteralExpr>(Cast->getSubExpr()->IgnoreParens())) {
418 const auto EquivalentForBoolLiteral =
420 if (UseUpperCaseLiteralSuffix)
421 Diag << tooling::fixit::createReplacement(
422 *Cast, EquivalentForBoolLiteral.upper());
424 Diag << tooling::fixit::createReplacement(*Cast,
425 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 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