10 #include "clang/AST/ASTContext.h"
11 #include "clang/ASTMatchers/ASTMatchFinder.h"
12 #include "clang/Lex/Lexer.h"
13 #include "clang/Tooling/FixIt.h"
20 namespace readability {
25 SourceManager &SM = Finder->getASTContext().getSourceManager();
26 SourceLocation
Loc = Node.getBeginLoc();
27 return SM.isMacroBodyExpansion(
Loc) || SM.isMacroArgExpansion(
Loc);
30 bool isNULLMacroExpansion(
const Stmt *Statement, ASTContext &Context) {
31 SourceManager &SM = Context.getSourceManager();
32 const LangOptions &LO = Context.getLangOpts();
33 SourceLocation
Loc = Statement->getBeginLoc();
34 return SM.isMacroBodyExpansion(
Loc) &&
35 Lexer::getImmediateMacroName(
Loc, SM, LO) ==
"NULL";
39 return isNULLMacroExpansion(&Node, Finder->getASTContext());
42 StringRef getZeroLiteralToCompareWithForType(CastKind CastExprKind,
44 ASTContext &Context) {
45 switch (CastExprKind) {
46 case CK_IntegralToBoolean:
47 return Type->isUnsignedIntegerType() ?
"0u" :
"0";
49 case CK_FloatingToBoolean:
50 return Context.hasSameType(
Type, Context.FloatTy) ?
"0.0f" :
"0.0";
52 case CK_PointerToBoolean:
53 case CK_MemberPointerToBoolean:
54 return Context.getLangOpts().CPlusPlus11 ?
"nullptr" :
"0";
57 llvm_unreachable(
"Unexpected cast kind");
61 bool isUnaryLogicalNotOperator(
const Stmt *Statement) {
62 const auto *UnaryOperatorExpr = dyn_cast<UnaryOperator>(Statement);
63 return UnaryOperatorExpr && UnaryOperatorExpr->getOpcode() == UO_LNot;
66 bool areParensNeededForOverloadedOperator(OverloadedOperatorKind OperatorKind) {
67 switch (OperatorKind) {
83 bool areParensNeededForStatement(
const Stmt *Statement) {
84 if (
const auto *OperatorCall = dyn_cast<CXXOperatorCallExpr>(Statement)) {
85 return areParensNeededForOverloadedOperator(OperatorCall->getOperator());
88 return isa<BinaryOperator>(Statement) || isa<UnaryOperator>(Statement);
91 void fixGenericExprCastToBool(DiagnosticBuilder &Diag,
92 const ImplicitCastExpr *Cast,
const Stmt *
Parent,
93 ASTContext &Context) {
96 bool InvertComparison =
98 if (InvertComparison) {
99 SourceLocation ParentStartLoc =
Parent->getBeginLoc();
100 SourceLocation ParentEndLoc =
101 cast<UnaryOperator>(
Parent)->getSubExpr()->getBeginLoc();
102 Diag << FixItHint::CreateRemoval(
103 CharSourceRange::getCharRange(ParentStartLoc, ParentEndLoc));
108 const Expr *SubExpr = Cast->getSubExpr();
110 bool NeedInnerParens = areParensNeededForStatement(SubExpr);
111 bool NeedOuterParens =
112 Parent !=
nullptr && areParensNeededForStatement(
Parent);
114 std::string StartLocInsertion;
116 if (NeedOuterParens) {
117 StartLocInsertion +=
"(";
119 if (NeedInnerParens) {
120 StartLocInsertion +=
"(";
123 if (!StartLocInsertion.empty()) {
124 Diag << FixItHint::CreateInsertion(Cast->getBeginLoc(), StartLocInsertion);
127 std::string EndLocInsertion;
129 if (NeedInnerParens) {
130 EndLocInsertion +=
")";
133 if (InvertComparison) {
134 EndLocInsertion +=
" == ";
136 EndLocInsertion +=
" != ";
139 EndLocInsertion += getZeroLiteralToCompareWithForType(
140 Cast->getCastKind(), SubExpr->getType(), Context);
142 if (NeedOuterParens) {
143 EndLocInsertion +=
")";
146 SourceLocation EndLoc = Lexer::getLocForEndOfToken(
147 Cast->getEndLoc(), 0, Context.getSourceManager(), Context.getLangOpts());
148 Diag << FixItHint::CreateInsertion(EndLoc, EndLocInsertion);
151 StringRef getEquivalentBoolLiteralForExpr(
const Expr *Expression,
152 ASTContext &Context) {
153 if (isNULLMacroExpansion(Expression, Context)) {
157 if (
const auto *IntLit = dyn_cast<IntegerLiteral>(Expression)) {
158 return (IntLit->getValue() == 0) ?
"false" :
"true";
161 if (
const auto *FloatLit = dyn_cast<FloatingLiteral>(Expression)) {
162 llvm::APFloat FloatLitAbsValue = FloatLit->getValue();
163 FloatLitAbsValue.clearSign();
164 return (FloatLitAbsValue.bitcastToAPInt() == 0) ?
"false" :
"true";
167 if (
const auto *CharLit = dyn_cast<CharacterLiteral>(Expression)) {
168 return (CharLit->getValue() == 0) ?
"false" :
"true";
171 if (isa<StringLiteral>(Expression->IgnoreCasts())) {
178 void fixGenericExprCastFromBool(DiagnosticBuilder &Diag,
179 const ImplicitCastExpr *Cast,
180 ASTContext &Context, StringRef OtherType) {
181 const Expr *SubExpr = Cast->getSubExpr();
182 bool NeedParens = !isa<ParenExpr>(SubExpr);
184 Diag << FixItHint::CreateInsertion(
186 (Twine(
"static_cast<") + OtherType +
">" + (NeedParens ?
"(" :
""))
190 SourceLocation EndLoc = Lexer::getLocForEndOfToken(
191 Cast->getEndLoc(), 0, Context.getSourceManager(),
192 Context.getLangOpts());
194 Diag << FixItHint::CreateInsertion(EndLoc,
")");
198 StringRef getEquivalentForBoolLiteral(
const CXXBoolLiteralExpr *BoolLiteral,
199 QualType DestType, ASTContext &Context) {
201 if (!Context.getLangOpts().CPlusPlus11 &&
202 (DestType->isPointerType() || DestType->isMemberPointerType()) &&
203 BoolLiteral->getValue() ==
false) {
207 if (DestType->isFloatingType()) {
208 if (Context.hasSameType(DestType, Context.FloatTy)) {
209 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";
217 return BoolLiteral->getValue() ?
"1" :
"0";
220 bool isCastAllowedInCondition(
const ImplicitCastExpr *Cast,
221 ASTContext &Context) {
222 std::queue<const Stmt *> Q;
225 TraversalKindScope RAII(Context, ast_type_traits::TK_AsIs);
228 for (
const auto &N : Context.getParents(*Q.front())) {
229 const Stmt *S = N.get<Stmt>();
232 if (isa<IfStmt>(S) || isa<ConditionalOperator>(S) || isa<ForStmt>(S) ||
233 isa<WhileStmt>(S) || isa<BinaryConditionalOperator>(S))
235 if (isa<ParenExpr>(S) || isa<ImplicitCastExpr>(S) ||
236 isUnaryLogicalNotOperator(S) ||
237 (isa<BinaryOperator>(S) && cast<BinaryOperator>(S)->isLogicalOp())) {
250 ImplicitBoolConversionCheck::ImplicitBoolConversionCheck(
253 AllowIntegerConditions(Options.get(
"AllowIntegerConditions", false)),
254 AllowPointerConditions(Options.get(
"AllowPointerConditions", false)) {}
258 Options.
store(Opts,
"AllowIntegerConditions", AllowIntegerConditions);
259 Options.
store(Opts,
"AllowPointerConditions", AllowPointerConditions);
263 auto exceptionCases =
264 expr(anyOf(allOf(isMacroExpansion(), unless(isNULLMacroExpansion())),
265 has(ignoringImplicit(memberExpr(hasDeclaration(fieldDecl(hasBitWidth(1)))))),
266 hasParent(explicitCastExpr())));
267 auto implicitCastFromBool = implicitCastExpr(
268 anyOf(hasCastKind(CK_IntegralCast), hasCastKind(CK_IntegralToFloating),
270 allOf(anyOf(hasCastKind(CK_NullToPointer),
271 hasCastKind(CK_NullToMemberPointer)),
272 hasSourceExpression(cxxBoolLiteral()))),
273 hasSourceExpression(expr(hasType(booleanType()))),
274 unless(exceptionCases));
276 binaryOperator(hasOperatorName(
"^"), hasLHS(implicitCastFromBool),
277 hasRHS(implicitCastFromBool));
279 traverse(ast_type_traits::TK_AsIs,
281 anyOf(hasCastKind(CK_IntegralToBoolean),
282 hasCastKind(CK_FloatingToBoolean),
283 hasCastKind(CK_PointerToBoolean),
284 hasCastKind(CK_MemberPointerToBoolean)),
289 stmt(anyOf(ifStmt(), whileStmt()), has(declStmt())))),
291 unless(exceptionCases), unless(has(boolXor)),
294 anyOf(hasParent(stmt().bind(
"parentStmt")), anything()),
295 unless(isInTemplateInstantiation()),
296 unless(hasAncestor(functionTemplateDecl())))
297 .bind(
"implicitCastToBool")),
300 auto boolComparison = binaryOperator(hasAnyOperatorName(
"==",
"!="),
301 hasLHS(implicitCastFromBool),
302 hasRHS(implicitCastFromBool));
303 auto boolOpAssignment = binaryOperator(hasAnyOperatorName(
"|=",
"&="),
304 hasLHS(expr(hasType(booleanType()))));
305 auto bitfieldAssignment = binaryOperator(
306 hasLHS(memberExpr(hasDeclaration(fieldDecl(hasBitWidth(1))))));
307 auto bitfieldConstruct = cxxConstructorDecl(hasDescendant(cxxCtorInitializer(
308 withInitializer(equalsBoundNode(
"implicitCastFromBool")),
309 forField(hasBitWidth(1)))));
312 ast_type_traits::TK_AsIs,
314 implicitCastFromBool,
320 binaryOperator(anyOf(boolComparison, boolXor,
321 boolOpAssignment, bitfieldAssignment)))),
322 implicitCastExpr().bind(
"implicitCastFromBool"),
323 unless(hasParent(bitfieldConstruct)),
325 anyOf(hasParent(implicitCastExpr().bind(
"furtherImplicitCast")),
327 unless(isInTemplateInstantiation()),
328 unless(hasAncestor(functionTemplateDecl())))),
333 const MatchFinder::MatchResult &Result) {
335 if (
const auto *CastToBool =
336 Result.Nodes.getNodeAs<ImplicitCastExpr>(
"implicitCastToBool")) {
337 const auto *
Parent = Result.Nodes.getNodeAs<Stmt>(
"parentStmt");
338 return handleCastToBool(CastToBool,
Parent, *Result.Context);
341 if (
const auto *CastFromBool =
342 Result.Nodes.getNodeAs<ImplicitCastExpr>(
"implicitCastFromBool")) {
343 const auto *NextImplicitCast =
344 Result.Nodes.getNodeAs<ImplicitCastExpr>(
"furtherImplicitCast");
345 return handleCastFromBool(CastFromBool, NextImplicitCast, *Result.Context);
349 void ImplicitBoolConversionCheck::handleCastToBool(
const ImplicitCastExpr *Cast,
351 ASTContext &Context) {
352 if (AllowPointerConditions &&
353 (Cast->getCastKind() == CK_PointerToBoolean ||
354 Cast->getCastKind() == CK_MemberPointerToBoolean) &&
355 isCastAllowedInCondition(Cast, Context)) {
359 if (AllowIntegerConditions && Cast->getCastKind() == CK_IntegralToBoolean &&
360 isCastAllowedInCondition(Cast, Context)) {
364 auto Diag =
diag(Cast->getBeginLoc(),
"implicit conversion %0 -> bool")
365 << Cast->getSubExpr()->getType();
367 StringRef EquivalentLiteral =
368 getEquivalentBoolLiteralForExpr(Cast->getSubExpr(), Context);
369 if (!EquivalentLiteral.empty()) {
370 Diag << tooling::fixit::createReplacement(*Cast, EquivalentLiteral);
372 fixGenericExprCastToBool(Diag, Cast,
Parent, Context);
376 void ImplicitBoolConversionCheck::handleCastFromBool(
377 const ImplicitCastExpr *Cast,
const ImplicitCastExpr *NextImplicitCast,
378 ASTContext &Context) {
380 NextImplicitCast ? NextImplicitCast->getType() : Cast->getType();
381 auto Diag =
diag(Cast->getBeginLoc(),
"implicit conversion bool -> %0")
384 if (
const auto *BoolLiteral =
385 dyn_cast<CXXBoolLiteralExpr>(Cast->getSubExpr())) {
386 Diag << tooling::fixit::createReplacement(
387 *Cast, getEquivalentForBoolLiteral(BoolLiteral, DestType, Context));
389 fixGenericExprCastFromBool(Diag, Cast, Context, DestType.getAsString());