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 Context.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();
93 bool NeedOuterParens =
96 std::string StartLocInsertion;
98 if (NeedOuterParens) {
99 StartLocInsertion +=
"(";
101 if (NeedInnerParens) {
102 StartLocInsertion +=
"(";
105 if (!StartLocInsertion.empty()) {
106 Diag << FixItHint::CreateInsertion(Cast->getBeginLoc(), StartLocInsertion);
109 std::string EndLocInsertion;
111 if (NeedInnerParens) {
112 EndLocInsertion +=
")";
115 if (InvertComparison) {
116 EndLocInsertion +=
" == ";
118 EndLocInsertion +=
" != ";
122 Cast->getCastKind(), SubExpr->getType(), Context);
124 if (UseUpperCaseLiteralSuffix)
125 EndLocInsertion += ZeroLiteral.upper();
127 EndLocInsertion += ZeroLiteral;
129 if (NeedOuterParens) {
130 EndLocInsertion +=
")";
133 SourceLocation EndLoc = Lexer::getLocForEndOfToken(
134 Cast->getEndLoc(), 0, Context.getSourceManager(), Context.getLangOpts());
135 Diag << FixItHint::CreateInsertion(EndLoc, EndLocInsertion);
139 ASTContext &Context) {
140 if (isNULLMacroExpansion(Expression, Context)) {
144 if (
const auto *IntLit =
145 dyn_cast<IntegerLiteral>(Expression->IgnoreParens())) {
146 return (IntLit->getValue() == 0) ?
"false" :
"true";
149 if (
const auto *FloatLit = dyn_cast<FloatingLiteral>(Expression)) {
150 llvm::APFloat FloatLitAbsValue = FloatLit->getValue();
151 FloatLitAbsValue.clearSign();
152 return (FloatLitAbsValue.bitcastToAPInt() == 0) ?
"false" :
"true";
155 if (
const auto *CharLit = dyn_cast<CharacterLiteral>(Expression)) {
156 return (CharLit->getValue() == 0) ?
"false" :
"true";
159 if (isa<StringLiteral>(Expression->IgnoreCasts())) {
167 SourceRange PrefixRange(Loc.getLocWithOffset(-1), Loc);
168 StringRef SpaceBeforeStmtStr = Lexer::getSourceText(
169 CharSourceRange::getCharRange(PrefixRange), Context.getSourceManager(),
170 Context.getLangOpts(),
nullptr);
171 if (SpaceBeforeStmtStr.empty())
174 const StringRef AllowedCharacters(
" \t\n\v\f\r(){}[]<>;,+=-|&~!^*/");
175 return !AllowedCharacters.contains(SpaceBeforeStmtStr.back());
179 const ImplicitCastExpr *Cast,
181 StringRef OtherType) {
182 if (!Context.getLangOpts().CPlusPlus) {
183 Diag << FixItHint::CreateInsertion(Cast->getBeginLoc(),
184 (Twine(
"(") + OtherType +
")").str());
188 const Expr *SubExpr = Cast->getSubExpr();
189 const bool NeedParens = !isa<ParenExpr>(SubExpr->IgnoreImplicit());
192 Diag << FixItHint::CreateInsertion(
193 Cast->getBeginLoc(), (Twine() + (NeedSpace ?
" " :
"") +
"static_cast<" +
194 OtherType +
">" + (NeedParens ?
"(" :
""))
198 SourceLocation EndLoc = Lexer::getLocForEndOfToken(
199 Cast->getEndLoc(), 0, Context.getSourceManager(),
200 Context.getLangOpts());
202 Diag << FixItHint::CreateInsertion(EndLoc,
")");
208 QualType DestType, ASTContext &Context) {
210 if (!Context.getLangOpts().CPlusPlus11 &&
211 (DestType->isPointerType() || DestType->isMemberPointerType()) &&
212 BoolLiteral->getValue() ==
false) {
216 if (DestType->isFloatingType()) {
217 if (Context.hasSameType(DestType, Context.FloatTy)) {
218 return BoolLiteral->getValue() ?
"1.0f" :
"0.0f";
220 return BoolLiteral->getValue() ?
"1.0" :
"0.0";
223 if (DestType->isUnsignedIntegerType()) {
224 return BoolLiteral->getValue() ?
"1u" :
"0u";
226 return BoolLiteral->getValue() ?
"1" :
"0";
230 ASTContext &Context) {
231 std::queue<const Stmt *> Q;
234 TraversalKindScope RAII(Context, TK_AsIs);
237 for (
const auto &N : Context.getParents(*Q.front())) {
238 const Stmt *S = N.get<Stmt>();
241 if (isa<IfStmt>(S) || isa<ConditionalOperator>(S) || isa<ForStmt>(S) ||
242 isa<WhileStmt>(S) || isa<DoStmt>(S) ||
243 isa<BinaryConditionalOperator>(S))
245 if (isa<ParenExpr>(S) || isa<ImplicitCastExpr>(S) ||
247 (isa<BinaryOperator>(S) && cast<BinaryOperator>(S)->isLogicalOp())) {
261 AllowIntegerConditions(Options.get(
"AllowIntegerConditions", false)),
262 AllowPointerConditions(Options.get(
"AllowPointerConditions", false)),
263 UseUpperCaseLiteralSuffix(
264 Options.get(
"UseUpperCaseLiteralSuffix", false)) {}
268 Options.store(Opts,
"AllowIntegerConditions", AllowIntegerConditions);
269 Options.store(Opts,
"AllowPointerConditions", AllowPointerConditions);
270 Options.store(Opts,
"UseUpperCaseLiteralSuffix", UseUpperCaseLiteralSuffix);
274 auto ExceptionCases =
275 expr(anyOf(allOf(isMacroExpansion(), unless(isNULLMacroExpansion())),
276 has(ignoringImplicit(
277 memberExpr(hasDeclaration(fieldDecl(hasBitWidth(1)))))),
278 hasParent(explicitCastExpr()),
279 expr(hasType(qualType().bind(
"type")),
280 hasParent(initListExpr(hasParent(explicitCastExpr(
281 hasType(qualType(equalsBoundNode(
"type"))))))))));
282 auto ImplicitCastFromBool = implicitCastExpr(
283 anyOf(hasCastKind(CK_IntegralCast), hasCastKind(CK_IntegralToFloating),
285 allOf(anyOf(hasCastKind(CK_NullToPointer),
286 hasCastKind(CK_NullToMemberPointer)),
287 hasSourceExpression(cxxBoolLiteral()))),
288 hasSourceExpression(expr(hasType(booleanType()))));
290 binaryOperator(hasOperatorName(
"^"), hasLHS(ImplicitCastFromBool),
291 hasRHS(ImplicitCastFromBool));
292 auto ComparisonInCall = allOf(
293 hasParent(callExpr()),
294 hasSourceExpression(binaryOperator(hasAnyOperatorName(
"==",
"!="))));
296 auto IsInCompilerGeneratedFunction = hasAncestor(namedDecl(anyOf(
297 isImplicit(), functionDecl(isDefaulted()), functionTemplateDecl())));
302 anyOf(hasCastKind(CK_IntegralToBoolean),
303 hasCastKind(CK_FloatingToBoolean),
304 hasCastKind(CK_PointerToBoolean),
305 hasCastKind(CK_MemberPointerToBoolean)),
307 unless(allOf(isC23(),
308 hasSourceExpression(ignoringParens(
309 binaryOperator(hasAnyOperatorName(
310 ">",
">=",
"==",
"!=",
"<",
"<=")))))),
315 stmt(anyOf(ifStmt(), whileStmt()), has(declStmt())))),
317 unless(ExceptionCases), unless(has(BoolXor)),
319 unless(ComparisonInCall),
322 optionally(hasParent(stmt().bind(
"parentStmt"))),
323 unless(isInTemplateInstantiation()),
324 unless(IsInCompilerGeneratedFunction))
325 .bind(
"implicitCastToBool")),
328 auto BoolComparison = binaryOperator(hasAnyOperatorName(
"==",
"!="),
329 hasLHS(ImplicitCastFromBool),
330 hasRHS(ImplicitCastFromBool));
331 auto BoolOpAssignment = binaryOperator(hasAnyOperatorName(
"|=",
"&="),
332 hasLHS(expr(hasType(booleanType()))));
333 auto BitfieldAssignment = binaryOperator(
334 hasLHS(memberExpr(hasDeclaration(fieldDecl(hasBitWidth(1))))));
335 auto BitfieldConstruct = cxxConstructorDecl(hasDescendant(cxxCtorInitializer(
336 withInitializer(equalsBoundNode(
"implicitCastFromBool")),
337 forField(hasBitWidth(1)))));
342 ImplicitCastFromBool, unless(ExceptionCases),
348 binaryOperator(anyOf(BoolComparison, BoolXor,
349 BoolOpAssignment, BitfieldAssignment)))),
350 implicitCastExpr().bind(
"implicitCastFromBool"),
351 unless(hasParent(BitfieldConstruct)),
354 hasParent(implicitCastExpr().bind(
"furtherImplicitCast"))),
355 unless(isInTemplateInstantiation()),
356 unless(IsInCompilerGeneratedFunction))),
361 const MatchFinder::MatchResult &Result) {
363 if (
const auto *CastToBool =
364 Result.Nodes.getNodeAs<ImplicitCastExpr>(
"implicitCastToBool")) {
365 const auto *Parent = Result.Nodes.getNodeAs<Stmt>(
"parentStmt");
366 handleCastToBool(CastToBool, Parent, *Result.Context);
370 if (
const auto *CastFromBool =
371 Result.Nodes.getNodeAs<ImplicitCastExpr>(
"implicitCastFromBool")) {
372 const auto *NextImplicitCast =
373 Result.Nodes.getNodeAs<ImplicitCastExpr>(
"furtherImplicitCast");
374 handleCastFromBool(CastFromBool, NextImplicitCast, *Result.Context);
378void ImplicitBoolConversionCheck::handleCastToBool(
const ImplicitCastExpr *Cast,
380 ASTContext &Context) {
381 if (AllowPointerConditions &&
382 (Cast->getCastKind() == CK_PointerToBoolean ||
383 Cast->getCastKind() == CK_MemberPointerToBoolean) &&
388 if (AllowIntegerConditions && Cast->getCastKind() == CK_IntegralToBoolean &&
393 auto Diag = diag(Cast->getBeginLoc(),
"implicit conversion %0 -> 'bool'")
394 << Cast->getSubExpr()->getType();
396 StringRef EquivalentLiteral =
398 if (!EquivalentLiteral.empty()) {
399 Diag << tooling::fixit::createReplacement(*Cast, EquivalentLiteral);
402 UseUpperCaseLiteralSuffix);
406void ImplicitBoolConversionCheck::handleCastFromBool(
407 const ImplicitCastExpr *Cast,
const ImplicitCastExpr *NextImplicitCast,
408 ASTContext &Context) {
410 NextImplicitCast ? NextImplicitCast->getType() : Cast->getType();
411 auto Diag = diag(Cast->getBeginLoc(),
"implicit conversion 'bool' -> %0")
414 if (
const auto *BoolLiteral =
415 dyn_cast<CXXBoolLiteralExpr>(Cast->getSubExpr()->IgnoreParens())) {
417 const auto EquivalentForBoolLiteral =
419 if (UseUpperCaseLiteralSuffix)
420 Diag << tooling::fixit::createReplacement(
421 *Cast, EquivalentForBoolLiteral.upper());
423 Diag << tooling::fixit::createReplacement(*Cast,
424 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