10#include "clang/AST/ASTContext.h"
11#include "clang/ASTMatchers/ASTMatchFinder.h"
12#include "clang/Lex/Lexer.h"
13#include "clang/Tooling/FixIt.h"
23 SourceManager &SM = Finder->getASTContext().getSourceManager();
24 SourceLocation
Loc =
Node.getBeginLoc();
25 return SM.isMacroBodyExpansion(
Loc) || SM.isMacroArgExpansion(
Loc);
28bool isNULLMacroExpansion(
const Stmt *Statement, ASTContext &Context) {
29 SourceManager &SM = Context.getSourceManager();
30 const LangOptions &LO = Context.getLangOpts();
31 SourceLocation
Loc = Statement->getBeginLoc();
32 return SM.isMacroBodyExpansion(
Loc) &&
33 Lexer::getImmediateMacroName(
Loc, SM, LO) ==
"NULL";
37 return isNULLMacroExpansion(&Node, Finder->getASTContext());
40StringRef getZeroLiteralToCompareWithForType(CastKind CastExprKind,
42 ASTContext &Context) {
43 switch (CastExprKind) {
44 case CK_IntegralToBoolean:
45 return Type->isUnsignedIntegerType() ?
"0u" :
"0";
47 case CK_FloatingToBoolean:
48 return Context.hasSameType(
Type, Context.FloatTy) ?
"0.0f" :
"0.0";
50 case CK_PointerToBoolean:
51 case CK_MemberPointerToBoolean:
52 return Context.getLangOpts().CPlusPlus11 ?
"nullptr" :
"0";
55 llvm_unreachable(
"Unexpected cast kind");
59bool isUnaryLogicalNotOperator(
const Stmt *Statement) {
60 const auto *UnaryOperatorExpr = dyn_cast<UnaryOperator>(Statement);
61 return UnaryOperatorExpr && UnaryOperatorExpr->getOpcode() == UO_LNot;
64bool areParensNeededForOverloadedOperator(OverloadedOperatorKind OperatorKind) {
65 switch (OperatorKind) {
81bool areParensNeededForStatement(
const Stmt *Statement) {
82 if (
const auto *OperatorCall = dyn_cast<CXXOperatorCallExpr>(Statement)) {
83 return areParensNeededForOverloadedOperator(OperatorCall->getOperator());
86 return isa<BinaryOperator>(Statement) || isa<UnaryOperator>(Statement);
89void fixGenericExprCastToBool(DiagnosticBuilder &Diag,
90 const ImplicitCastExpr *Cast,
const Stmt *
Parent,
91 ASTContext &Context) {
94 bool InvertComparison =
96 if (InvertComparison) {
97 SourceLocation ParentStartLoc =
Parent->getBeginLoc();
98 SourceLocation ParentEndLoc =
99 cast<UnaryOperator>(
Parent)->getSubExpr()->getBeginLoc();
100 Diag << FixItHint::CreateRemoval(
101 CharSourceRange::getCharRange(ParentStartLoc, ParentEndLoc));
106 const Expr *SubExpr = Cast->getSubExpr();
108 bool NeedInnerParens = areParensNeededForStatement(SubExpr);
109 bool NeedOuterParens =
110 Parent !=
nullptr && areParensNeededForStatement(
Parent);
112 std::string StartLocInsertion;
114 if (NeedOuterParens) {
115 StartLocInsertion +=
"(";
117 if (NeedInnerParens) {
118 StartLocInsertion +=
"(";
121 if (!StartLocInsertion.empty()) {
122 Diag << FixItHint::CreateInsertion(Cast->getBeginLoc(), StartLocInsertion);
125 std::string EndLocInsertion;
127 if (NeedInnerParens) {
128 EndLocInsertion +=
")";
131 if (InvertComparison) {
132 EndLocInsertion +=
" == ";
134 EndLocInsertion +=
" != ";
137 EndLocInsertion += getZeroLiteralToCompareWithForType(
138 Cast->getCastKind(), SubExpr->getType(), Context);
140 if (NeedOuterParens) {
141 EndLocInsertion +=
")";
144 SourceLocation EndLoc = Lexer::getLocForEndOfToken(
145 Cast->getEndLoc(), 0, Context.getSourceManager(), Context.getLangOpts());
146 Diag << FixItHint::CreateInsertion(EndLoc, EndLocInsertion);
149StringRef getEquivalentBoolLiteralForExpr(
const Expr *Expression,
150 ASTContext &Context) {
151 if (isNULLMacroExpansion(Expression, Context)) {
155 if (
const auto *IntLit = dyn_cast<IntegerLiteral>(Expression)) {
156 return (IntLit->getValue() == 0) ?
"false" :
"true";
159 if (
const auto *FloatLit = dyn_cast<FloatingLiteral>(Expression)) {
160 llvm::APFloat FloatLitAbsValue = FloatLit->getValue();
161 FloatLitAbsValue.clearSign();
162 return (FloatLitAbsValue.bitcastToAPInt() == 0) ?
"false" :
"true";
165 if (
const auto *CharLit = dyn_cast<CharacterLiteral>(Expression)) {
166 return (CharLit->getValue() == 0) ?
"false" :
"true";
169 if (isa<StringLiteral>(Expression->IgnoreCasts())) {
176void fixGenericExprCastFromBool(DiagnosticBuilder &Diag,
177 const ImplicitCastExpr *Cast,
178 ASTContext &Context, StringRef OtherType) {
179 const Expr *SubExpr = Cast->getSubExpr();
180 bool NeedParens = !isa<ParenExpr>(SubExpr);
182 Diag << FixItHint::CreateInsertion(
184 (Twine(
"static_cast<") + OtherType +
">" + (NeedParens ?
"(" :
""))
188 SourceLocation EndLoc = Lexer::getLocForEndOfToken(
189 Cast->getEndLoc(), 0, Context.getSourceManager(),
190 Context.getLangOpts());
192 Diag << FixItHint::CreateInsertion(EndLoc,
")");
196StringRef getEquivalentForBoolLiteral(
const CXXBoolLiteralExpr *BoolLiteral,
197 QualType DestType, ASTContext &Context) {
199 if (!Context.getLangOpts().CPlusPlus11 &&
200 (DestType->isPointerType() || DestType->isMemberPointerType()) &&
201 BoolLiteral->getValue() ==
false) {
205 if (DestType->isFloatingType()) {
206 if (Context.hasSameType(DestType, Context.FloatTy)) {
207 return BoolLiteral->getValue() ?
"1.0f" :
"0.0f";
209 return BoolLiteral->getValue() ?
"1.0" :
"0.0";
212 if (DestType->isUnsignedIntegerType()) {
213 return BoolLiteral->getValue() ?
"1u" :
"0u";
215 return BoolLiteral->getValue() ?
"1" :
"0";
218bool isCastAllowedInCondition(
const ImplicitCastExpr *Cast,
219 ASTContext &Context) {
220 std::queue<const Stmt *> Q;
223 TraversalKindScope RAII(Context, TK_AsIs);
226 for (
const auto &N : Context.getParents(*Q.front())) {
227 const Stmt *S = N.get<Stmt>();
230 if (isa<IfStmt>(S) || isa<ConditionalOperator>(S) || isa<ForStmt>(S) ||
231 isa<WhileStmt>(S) || isa<DoStmt>(S) ||
232 isa<BinaryConditionalOperator>(S))
234 if (isa<ParenExpr>(S) || isa<ImplicitCastExpr>(S) ||
235 isUnaryLogicalNotOperator(S) ||
236 (isa<BinaryOperator>(S) && cast<BinaryOperator>(S)->isLogicalOp())) {
252 AllowIntegerConditions(Options.get(
"AllowIntegerConditions", false)),
253 AllowPointerConditions(Options.get(
"AllowPointerConditions", false)) {}
257 Options.
store(Opts,
"AllowIntegerConditions", AllowIntegerConditions);
258 Options.
store(Opts,
"AllowPointerConditions", AllowPointerConditions);
262 auto ExceptionCases =
263 expr(anyOf(allOf(isMacroExpansion(), unless(isNULLMacroExpansion())),
264 has(ignoringImplicit(
265 memberExpr(hasDeclaration(fieldDecl(hasBitWidth(1)))))),
266 hasParent(explicitCastExpr()),
267 expr(hasType(qualType().bind(
"type")),
268 hasParent(initListExpr(hasParent(explicitCastExpr(
269 hasType(qualType(equalsBoundNode(
"type"))))))))));
270 auto ImplicitCastFromBool = implicitCastExpr(
271 anyOf(hasCastKind(CK_IntegralCast), hasCastKind(CK_IntegralToFloating),
273 allOf(anyOf(hasCastKind(CK_NullToPointer),
274 hasCastKind(CK_NullToMemberPointer)),
275 hasSourceExpression(cxxBoolLiteral()))),
276 hasSourceExpression(expr(hasType(booleanType()))),
277 unless(ExceptionCases));
279 binaryOperator(hasOperatorName(
"^"), hasLHS(ImplicitCastFromBool),
280 hasRHS(ImplicitCastFromBool));
284 anyOf(hasCastKind(CK_IntegralToBoolean),
285 hasCastKind(CK_FloatingToBoolean),
286 hasCastKind(CK_PointerToBoolean),
287 hasCastKind(CK_MemberPointerToBoolean)),
292 stmt(anyOf(ifStmt(), whileStmt()), has(declStmt())))),
294 unless(ExceptionCases), unless(has(BoolXor)),
297 optionally(hasParent(stmt().bind(
"parentStmt"))),
298 unless(isInTemplateInstantiation()),
299 unless(hasAncestor(functionTemplateDecl())))
300 .bind(
"implicitCastToBool")),
303 auto BoolComparison = binaryOperator(hasAnyOperatorName(
"==",
"!="),
304 hasLHS(ImplicitCastFromBool),
305 hasRHS(ImplicitCastFromBool));
306 auto BoolOpAssignment = binaryOperator(hasAnyOperatorName(
"|=",
"&="),
307 hasLHS(expr(hasType(booleanType()))));
308 auto BitfieldAssignment = binaryOperator(
309 hasLHS(memberExpr(hasDeclaration(fieldDecl(hasBitWidth(1))))));
310 auto BitfieldConstruct = cxxConstructorDecl(hasDescendant(cxxCtorInitializer(
311 withInitializer(equalsBoundNode(
"implicitCastFromBool")),
312 forField(hasBitWidth(1)))));
317 ImplicitCastFromBool,
323 binaryOperator(anyOf(BoolComparison, BoolXor,
324 BoolOpAssignment, BitfieldAssignment)))),
325 implicitCastExpr().bind(
"implicitCastFromBool"),
326 unless(hasParent(BitfieldConstruct)),
328 anyOf(hasParent(implicitCastExpr().bind(
"furtherImplicitCast")),
330 unless(isInTemplateInstantiation()),
331 unless(hasAncestor(functionTemplateDecl())))),
336 const MatchFinder::MatchResult &Result) {
338 if (
const auto *CastToBool =
339 Result.Nodes.getNodeAs<ImplicitCastExpr>(
"implicitCastToBool")) {
340 const auto *
Parent = Result.Nodes.getNodeAs<Stmt>(
"parentStmt");
341 return handleCastToBool(CastToBool,
Parent, *Result.Context);
344 if (
const auto *CastFromBool =
345 Result.Nodes.getNodeAs<ImplicitCastExpr>(
"implicitCastFromBool")) {
346 const auto *NextImplicitCast =
347 Result.Nodes.getNodeAs<ImplicitCastExpr>(
"furtherImplicitCast");
348 return handleCastFromBool(CastFromBool, NextImplicitCast, *Result.Context);
352void ImplicitBoolConversionCheck::handleCastToBool(
const ImplicitCastExpr *Cast,
354 ASTContext &Context) {
355 if (AllowPointerConditions &&
356 (Cast->getCastKind() == CK_PointerToBoolean ||
357 Cast->getCastKind() == CK_MemberPointerToBoolean) &&
358 isCastAllowedInCondition(Cast, Context)) {
362 if (AllowIntegerConditions && Cast->getCastKind() == CK_IntegralToBoolean &&
363 isCastAllowedInCondition(Cast, Context)) {
367 auto Diag =
diag(Cast->getBeginLoc(),
"implicit conversion %0 -> bool")
368 << Cast->getSubExpr()->getType();
370 StringRef EquivalentLiteral =
371 getEquivalentBoolLiteralForExpr(Cast->getSubExpr(), Context);
372 if (!EquivalentLiteral.empty()) {
373 Diag << tooling::fixit::createReplacement(*Cast, EquivalentLiteral);
375 fixGenericExprCastToBool(Diag, Cast,
Parent, Context);
379void ImplicitBoolConversionCheck::handleCastFromBool(
380 const ImplicitCastExpr *Cast,
const ImplicitCastExpr *NextImplicitCast,
381 ASTContext &Context) {
383 NextImplicitCast ? NextImplicitCast->getType() : Cast->getType();
384 auto Diag =
diag(Cast->getBeginLoc(),
"implicit conversion bool -> %0")
387 if (
const auto *BoolLiteral =
388 dyn_cast<CXXBoolLiteralExpr>(Cast->getSubExpr())) {
389 Diag << tooling::fixit::createReplacement(
390 *Cast, getEquivalentForBoolLiteral(BoolLiteral, DestType, Context));
392 fixGenericExprCastFromBool(Diag, Cast, Context, DestType.getAsString());
::clang::DynTypedNode Node
void store(ClangTidyOptions::OptionMap &Options, StringRef LocalName, StringRef Value) const
Stores an option with the check-local name LocalName with string value Value to Options.
Base class for all clang-tidy checks.
DiagnosticBuilder diag(SourceLocation Loc, StringRef Description, DiagnosticIDs::Level Level=DiagnosticIDs::Warning)
Add a diagnostic with the check's name.
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
ClangTidyChecks that register ASTMatchers should do the actual work in here.
void storeOptions(ClangTidyOptions::OptionMap &Opts) override
Should store all options supported by this check with their current values or default values for opti...
ImplicitBoolConversionCheck(StringRef Name, ClangTidyContext *Context)
void registerMatchers(ast_matchers::MatchFinder *Finder) override
Override this to register AST matchers with Finder.
AST_MATCHER(CXXMethodDecl, isStatic)
llvm::StringMap< ClangTidyValue > OptionMap