10#include "../utils/FixItHintUtils.h"
11#include "clang/AST/ASTContext.h"
12#include "clang/ASTMatchers/ASTMatchFinder.h"
13#include "clang/Lex/Lexer.h"
14#include "clang/Tooling/FixIt.h"
24 SourceManager &SM = Finder->getASTContext().getSourceManager();
25 SourceLocation
Loc =
Node.getBeginLoc();
26 return SM.isMacroBodyExpansion(
Loc) || SM.isMacroArgExpansion(
Loc);
29bool isNULLMacroExpansion(
const Stmt *Statement, ASTContext &Context) {
30 SourceManager &SM = Context.getSourceManager();
31 const LangOptions &LO = Context.getLangOpts();
32 SourceLocation
Loc = Statement->getBeginLoc();
33 return SM.isMacroBodyExpansion(
Loc) &&
34 Lexer::getImmediateMacroName(
Loc, SM, LO) ==
"NULL";
38 return isNULLMacroExpansion(&Node, Finder->getASTContext());
41StringRef getZeroLiteralToCompareWithForType(CastKind CastExprKind,
43 ASTContext &Context) {
44 switch (CastExprKind) {
45 case CK_IntegralToBoolean:
46 return Type->isUnsignedIntegerType() ?
"0u" :
"0";
48 case CK_FloatingToBoolean:
49 return Context.hasSameType(
Type, Context.FloatTy) ?
"0.0f" :
"0.0";
51 case CK_PointerToBoolean:
52 case CK_MemberPointerToBoolean:
53 return Context.getLangOpts().CPlusPlus11 ?
"nullptr" :
"0";
56 llvm_unreachable(
"Unexpected cast kind");
60bool isUnaryLogicalNotOperator(
const Stmt *Statement) {
61 const auto *UnaryOperatorExpr = dyn_cast<UnaryOperator>(Statement);
62 return UnaryOperatorExpr && UnaryOperatorExpr->getOpcode() == UO_LNot;
65void fixGenericExprCastToBool(DiagnosticBuilder &Diag,
66 const ImplicitCastExpr *Cast,
const Stmt *
Parent,
67 ASTContext &Context) {
70 bool InvertComparison =
72 if (InvertComparison) {
73 SourceLocation ParentStartLoc =
Parent->getBeginLoc();
74 SourceLocation ParentEndLoc =
75 cast<UnaryOperator>(
Parent)->getSubExpr()->getBeginLoc();
76 Diag << FixItHint::CreateRemoval(
77 CharSourceRange::getCharRange(ParentStartLoc, ParentEndLoc));
82 const Expr *SubExpr = Cast->getSubExpr();
85 bool NeedOuterParens =
88 std::string StartLocInsertion;
90 if (NeedOuterParens) {
91 StartLocInsertion +=
"(";
93 if (NeedInnerParens) {
94 StartLocInsertion +=
"(";
97 if (!StartLocInsertion.empty()) {
98 Diag << FixItHint::CreateInsertion(Cast->getBeginLoc(), StartLocInsertion);
101 std::string EndLocInsertion;
103 if (NeedInnerParens) {
104 EndLocInsertion +=
")";
107 if (InvertComparison) {
108 EndLocInsertion +=
" == ";
110 EndLocInsertion +=
" != ";
113 EndLocInsertion += getZeroLiteralToCompareWithForType(
114 Cast->getCastKind(), SubExpr->getType(), Context);
116 if (NeedOuterParens) {
117 EndLocInsertion +=
")";
120 SourceLocation EndLoc = Lexer::getLocForEndOfToken(
121 Cast->getEndLoc(), 0, Context.getSourceManager(), Context.getLangOpts());
122 Diag << FixItHint::CreateInsertion(EndLoc, EndLocInsertion);
125StringRef getEquivalentBoolLiteralForExpr(
const Expr *Expression,
126 ASTContext &Context) {
127 if (isNULLMacroExpansion(Expression, Context)) {
131 if (
const auto *IntLit =
132 dyn_cast<IntegerLiteral>(Expression->IgnoreParens())) {
133 return (IntLit->getValue() == 0) ?
"false" :
"true";
136 if (
const auto *FloatLit = dyn_cast<FloatingLiteral>(Expression)) {
137 llvm::APFloat FloatLitAbsValue = FloatLit->getValue();
138 FloatLitAbsValue.clearSign();
139 return (FloatLitAbsValue.bitcastToAPInt() == 0) ?
"false" :
"true";
142 if (
const auto *CharLit = dyn_cast<CharacterLiteral>(Expression)) {
143 return (CharLit->getValue() == 0) ?
"false" :
"true";
146 if (isa<StringLiteral>(Expression->IgnoreCasts())) {
153bool needsSpacePrefix(SourceLocation
Loc, ASTContext &Context) {
154 SourceRange PrefixRange(
Loc.getLocWithOffset(-1),
Loc);
155 StringRef SpaceBeforeStmtStr = Lexer::getSourceText(
156 CharSourceRange::getCharRange(PrefixRange), Context.getSourceManager(),
157 Context.getLangOpts(),
nullptr);
158 if (SpaceBeforeStmtStr.empty())
161 const StringRef AllowedCharacters(
" \t\n\v\f\r(){}[]<>;,+=-|&~!^*/");
162 return !AllowedCharacters.contains(SpaceBeforeStmtStr.back());
165void fixGenericExprCastFromBool(DiagnosticBuilder &Diag,
166 const ImplicitCastExpr *Cast,
167 ASTContext &Context, StringRef OtherType) {
168 const Expr *SubExpr = Cast->getSubExpr();
169 const bool NeedParens = !isa<ParenExpr>(SubExpr->IgnoreImplicit());
170 const bool NeedSpace = needsSpacePrefix(Cast->getBeginLoc(), Context);
172 Diag << FixItHint::CreateInsertion(
173 Cast->getBeginLoc(), (Twine() + (NeedSpace ?
" " :
"") +
"static_cast<" +
174 OtherType +
">" + (NeedParens ?
"(" :
""))
178 SourceLocation EndLoc = Lexer::getLocForEndOfToken(
179 Cast->getEndLoc(), 0, Context.getSourceManager(),
180 Context.getLangOpts());
182 Diag << FixItHint::CreateInsertion(EndLoc,
")");
186StringRef getEquivalentForBoolLiteral(
const CXXBoolLiteralExpr *BoolLiteral,
187 QualType DestType, ASTContext &Context) {
189 if (!Context.getLangOpts().CPlusPlus11 &&
190 (DestType->isPointerType() || DestType->isMemberPointerType()) &&
191 BoolLiteral->getValue() ==
false) {
195 if (DestType->isFloatingType()) {
196 if (Context.hasSameType(DestType, Context.FloatTy)) {
197 return BoolLiteral->getValue() ?
"1.0f" :
"0.0f";
199 return BoolLiteral->getValue() ?
"1.0" :
"0.0";
202 if (DestType->isUnsignedIntegerType()) {
203 return BoolLiteral->getValue() ?
"1u" :
"0u";
205 return BoolLiteral->getValue() ?
"1" :
"0";
208bool isCastAllowedInCondition(
const ImplicitCastExpr *Cast,
209 ASTContext &Context) {
210 std::queue<const Stmt *> Q;
213 TraversalKindScope RAII(Context, TK_AsIs);
216 for (
const auto &N : Context.getParents(*Q.front())) {
217 const Stmt *S = N.get<Stmt>();
220 if (isa<IfStmt>(S) || isa<ConditionalOperator>(S) || isa<ForStmt>(S) ||
221 isa<WhileStmt>(S) || isa<DoStmt>(S) ||
222 isa<BinaryConditionalOperator>(S))
224 if (isa<ParenExpr>(S) || isa<ImplicitCastExpr>(S) ||
225 isUnaryLogicalNotOperator(S) ||
226 (isa<BinaryOperator>(S) && cast<BinaryOperator>(S)->isLogicalOp())) {
242 AllowIntegerConditions(Options.get(
"AllowIntegerConditions", false)),
243 AllowPointerConditions(Options.get(
"AllowPointerConditions", false)) {}
247 Options.
store(Opts,
"AllowIntegerConditions", AllowIntegerConditions);
248 Options.
store(Opts,
"AllowPointerConditions", AllowPointerConditions);
252 auto ExceptionCases =
253 expr(anyOf(allOf(isMacroExpansion(), unless(isNULLMacroExpansion())),
254 has(ignoringImplicit(
255 memberExpr(hasDeclaration(fieldDecl(hasBitWidth(1)))))),
256 hasParent(explicitCastExpr()),
257 expr(hasType(qualType().bind(
"type")),
258 hasParent(initListExpr(hasParent(explicitCastExpr(
259 hasType(qualType(equalsBoundNode(
"type"))))))))));
260 auto ImplicitCastFromBool = implicitCastExpr(
261 anyOf(hasCastKind(CK_IntegralCast), hasCastKind(CK_IntegralToFloating),
263 allOf(anyOf(hasCastKind(CK_NullToPointer),
264 hasCastKind(CK_NullToMemberPointer)),
265 hasSourceExpression(cxxBoolLiteral()))),
266 hasSourceExpression(expr(hasType(booleanType()))));
268 binaryOperator(hasOperatorName(
"^"), hasLHS(ImplicitCastFromBool),
269 hasRHS(ImplicitCastFromBool));
273 anyOf(hasCastKind(CK_IntegralToBoolean),
274 hasCastKind(CK_FloatingToBoolean),
275 hasCastKind(CK_PointerToBoolean),
276 hasCastKind(CK_MemberPointerToBoolean)),
281 stmt(anyOf(ifStmt(), whileStmt()), has(declStmt())))),
283 unless(ExceptionCases), unless(has(BoolXor)),
286 optionally(hasParent(stmt().bind(
"parentStmt"))),
287 unless(isInTemplateInstantiation()),
288 unless(hasAncestor(functionTemplateDecl())))
289 .bind(
"implicitCastToBool")),
292 auto BoolComparison = binaryOperator(hasAnyOperatorName(
"==",
"!="),
293 hasLHS(ImplicitCastFromBool),
294 hasRHS(ImplicitCastFromBool));
295 auto BoolOpAssignment = binaryOperator(hasAnyOperatorName(
"|=",
"&="),
296 hasLHS(expr(hasType(booleanType()))));
297 auto BitfieldAssignment = binaryOperator(
298 hasLHS(memberExpr(hasDeclaration(fieldDecl(hasBitWidth(1))))));
299 auto BitfieldConstruct = cxxConstructorDecl(hasDescendant(cxxCtorInitializer(
300 withInitializer(equalsBoundNode(
"implicitCastFromBool")),
301 forField(hasBitWidth(1)))));
306 ImplicitCastFromBool, unless(ExceptionCases),
312 binaryOperator(anyOf(BoolComparison, BoolXor,
313 BoolOpAssignment, BitfieldAssignment)))),
314 implicitCastExpr().bind(
"implicitCastFromBool"),
315 unless(hasParent(BitfieldConstruct)),
317 anyOf(hasParent(implicitCastExpr().bind(
"furtherImplicitCast")),
319 unless(isInTemplateInstantiation()),
320 unless(hasAncestor(functionTemplateDecl())))),
325 const MatchFinder::MatchResult &Result) {
327 if (
const auto *CastToBool =
328 Result.Nodes.getNodeAs<ImplicitCastExpr>(
"implicitCastToBool")) {
329 const auto *
Parent = Result.Nodes.getNodeAs<Stmt>(
"parentStmt");
330 return handleCastToBool(CastToBool,
Parent, *Result.Context);
333 if (
const auto *CastFromBool =
334 Result.Nodes.getNodeAs<ImplicitCastExpr>(
"implicitCastFromBool")) {
335 const auto *NextImplicitCast =
336 Result.Nodes.getNodeAs<ImplicitCastExpr>(
"furtherImplicitCast");
337 return handleCastFromBool(CastFromBool, NextImplicitCast, *Result.Context);
341void ImplicitBoolConversionCheck::handleCastToBool(
const ImplicitCastExpr *Cast,
343 ASTContext &Context) {
344 if (AllowPointerConditions &&
345 (Cast->getCastKind() == CK_PointerToBoolean ||
346 Cast->getCastKind() == CK_MemberPointerToBoolean) &&
347 isCastAllowedInCondition(Cast, Context)) {
351 if (AllowIntegerConditions && Cast->getCastKind() == CK_IntegralToBoolean &&
352 isCastAllowedInCondition(Cast, Context)) {
356 auto Diag =
diag(Cast->getBeginLoc(),
"implicit conversion %0 -> 'bool'")
357 << Cast->getSubExpr()->getType();
359 StringRef EquivalentLiteral =
360 getEquivalentBoolLiteralForExpr(Cast->getSubExpr(), Context);
361 if (!EquivalentLiteral.empty()) {
362 Diag << tooling::fixit::createReplacement(*Cast, EquivalentLiteral);
364 fixGenericExprCastToBool(Diag, Cast,
Parent, Context);
368void ImplicitBoolConversionCheck::handleCastFromBool(
369 const ImplicitCastExpr *Cast,
const ImplicitCastExpr *NextImplicitCast,
370 ASTContext &Context) {
372 NextImplicitCast ? NextImplicitCast->getType() : Cast->getType();
373 auto Diag =
diag(Cast->getBeginLoc(),
"implicit conversion 'bool' -> %0")
376 if (
const auto *BoolLiteral =
377 dyn_cast<CXXBoolLiteralExpr>(Cast->getSubExpr()->IgnoreParens())) {
378 Diag << tooling::fixit::createReplacement(
379 *Cast, getEquivalentForBoolLiteral(BoolLiteral, DestType, Context));
381 fixGenericExprCastFromBool(Diag, Cast, Context, DestType.getAsString());
llvm::SmallString< 256U > Name
::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)
bool areParensNeededForStatement(const Stmt &Node)
llvm::StringMap< ClangTidyValue > OptionMap