11 #include "clang/AST/ASTContext.h"
12 #include "clang/ASTMatchers/ASTMatchFinder.h"
13 #include "clang/Lex/Lexer.h"
14 #include "clang/Tooling/FixIt.h"
17 using namespace clang::ast_matchers;
21 namespace readability {
26 SourceManager &
SM =
Finder->getASTContext().getSourceManager();
27 SourceLocation
Loc = Node.getLocStart();
28 return SM.isMacroBodyExpansion(Loc) || SM.isMacroArgExpansion(Loc);
31 bool isNULLMacroExpansion(
const Stmt *Statement, ASTContext &
Context) {
32 SourceManager &SM = Context.getSourceManager();
33 const LangOptions &LO = Context.getLangOpts();
34 SourceLocation Loc = Statement->getLocStart();
35 return SM.isMacroBodyExpansion(Loc) &&
36 Lexer::getImmediateMacroName(Loc, SM, LO) ==
"NULL";
40 return isNULLMacroExpansion(&Node,
Finder->getASTContext());
43 StringRef getZeroLiteralToCompareWithForType(CastKind CastExprKind,
45 ASTContext &Context) {
46 switch (CastExprKind) {
47 case CK_IntegralToBoolean:
48 return Type->isUnsignedIntegerType() ?
"0u" :
"0";
50 case CK_FloatingToBoolean:
51 return Context.hasSameType(Type, Context.FloatTy) ?
"0.0f" :
"0.0";
53 case CK_PointerToBoolean:
54 case CK_MemberPointerToBoolean:
55 return Context.getLangOpts().CPlusPlus11 ?
"nullptr" :
"0";
58 llvm_unreachable(
"Unexpected cast kind");
62 bool isUnaryLogicalNotOperator(
const Stmt *Statement) {
63 const auto *UnaryOperatorExpr = dyn_cast<UnaryOperator>(Statement);
64 return UnaryOperatorExpr && UnaryOperatorExpr->getOpcode() == UO_LNot;
67 bool areParensNeededForOverloadedOperator(OverloadedOperatorKind OperatorKind) {
68 switch (OperatorKind) {
84 bool areParensNeededForStatement(
const Stmt *Statement) {
85 if (
const auto *OperatorCall = dyn_cast<CXXOperatorCallExpr>(Statement)) {
86 return areParensNeededForOverloadedOperator(OperatorCall->getOperator());
89 return isa<BinaryOperator>(Statement) || isa<UnaryOperator>(Statement);
92 void fixGenericExprCastToBool(DiagnosticBuilder &Diag,
93 const ImplicitCastExpr *Cast,
const Stmt *Parent,
94 ASTContext &Context) {
97 bool InvertComparison =
98 Parent !=
nullptr && isUnaryLogicalNotOperator(Parent);
99 if (InvertComparison) {
100 SourceLocation ParentStartLoc = Parent->getLocStart();
101 SourceLocation ParentEndLoc =
102 cast<UnaryOperator>(Parent)->getSubExpr()->getLocStart();
103 Diag << FixItHint::CreateRemoval(
104 CharSourceRange::getCharRange(ParentStartLoc, ParentEndLoc));
106 Parent = Context.getParents(*Parent)[0].get<Stmt>();
109 const Expr *SubExpr = Cast->getSubExpr();
111 bool NeedInnerParens = areParensNeededForStatement(SubExpr);
112 bool NeedOuterParens =
113 Parent !=
nullptr && areParensNeededForStatement(Parent);
115 std::string StartLocInsertion;
117 if (NeedOuterParens) {
118 StartLocInsertion +=
"(";
120 if (NeedInnerParens) {
121 StartLocInsertion +=
"(";
124 if (!StartLocInsertion.empty()) {
125 Diag << FixItHint::CreateInsertion(Cast->getLocStart(), StartLocInsertion);
128 std::string EndLocInsertion;
130 if (NeedInnerParens) {
131 EndLocInsertion +=
")";
134 if (InvertComparison) {
135 EndLocInsertion +=
" == ";
137 EndLocInsertion +=
" != ";
140 EndLocInsertion += getZeroLiteralToCompareWithForType(
141 Cast->getCastKind(), SubExpr->getType(),
Context);
143 if (NeedOuterParens) {
144 EndLocInsertion +=
")";
147 SourceLocation EndLoc = Lexer::getLocForEndOfToken(
148 Cast->getLocEnd(), 0, Context.getSourceManager(), Context.getLangOpts());
149 Diag << FixItHint::CreateInsertion(EndLoc, EndLocInsertion);
152 StringRef getEquivalentBoolLiteralForExpr(
const Expr *Expression,
153 ASTContext &Context) {
154 if (isNULLMacroExpansion(Expression, Context)) {
158 if (
const auto *IntLit = dyn_cast<IntegerLiteral>(Expression)) {
159 return (IntLit->getValue() == 0) ?
"false" :
"true";
162 if (
const auto *FloatLit = dyn_cast<FloatingLiteral>(Expression)) {
163 llvm::APFloat FloatLitAbsValue = FloatLit->getValue();
164 FloatLitAbsValue.clearSign();
165 return (FloatLitAbsValue.bitcastToAPInt() == 0) ?
"false" :
"true";
168 if (
const auto *CharLit = dyn_cast<CharacterLiteral>(Expression)) {
169 return (CharLit->getValue() == 0) ?
"false" :
"true";
172 if (isa<StringLiteral>(Expression->IgnoreCasts())) {
179 void fixGenericExprCastFromBool(DiagnosticBuilder &Diag,
180 const ImplicitCastExpr *Cast,
181 ASTContext &Context, StringRef OtherType) {
182 const Expr *SubExpr = Cast->getSubExpr();
183 bool NeedParens = !isa<ParenExpr>(SubExpr);
185 Diag << FixItHint::CreateInsertion(
187 (Twine(
"static_cast<") + OtherType +
">" + (NeedParens ?
"(" :
""))
191 SourceLocation EndLoc = Lexer::getLocForEndOfToken(
192 Cast->getLocEnd(), 0, Context.getSourceManager(),
193 Context.getLangOpts());
195 Diag << FixItHint::CreateInsertion(EndLoc,
")");
199 StringRef getEquivalentForBoolLiteral(
const CXXBoolLiteralExpr *BoolLiteral,
200 QualType DestType, ASTContext &Context) {
202 if (!Context.getLangOpts().CPlusPlus11 &&
203 (DestType->isPointerType() || DestType->isMemberPointerType()) &&
204 BoolLiteral->getValue() ==
false) {
208 if (DestType->isFloatingType()) {
209 if (Context.hasSameType(DestType, Context.FloatTy)) {
210 return BoolLiteral->getValue() ?
"1.0f" :
"0.0f";
212 return BoolLiteral->getValue() ?
"1.0" :
"0.0";
215 if (DestType->isUnsignedIntegerType()) {
216 return BoolLiteral->getValue() ?
"1u" :
"0u";
218 return BoolLiteral->getValue() ?
"1" :
"0";
221 bool isAllowedConditionalCast(
const ImplicitCastExpr *Cast,
222 ASTContext &Context) {
223 std::queue<const Stmt *> Q;
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<BinaryConditionalOperator>(S))
233 if (isa<ParenExpr>(S) || isa<ImplicitCastExpr>(S) ||
234 isUnaryLogicalNotOperator(S) ||
235 (isa<BinaryOperator>(S) && cast<BinaryOperator>(S)->isLogicalOp())) {
248 ImplicitBoolCastCheck::ImplicitBoolCastCheck(StringRef
Name,
251 AllowConditionalIntegerCasts(
252 Options.get(
"AllowConditionalIntegerCasts", false)),
253 AllowConditionalPointerCasts(
254 Options.get(
"AllowConditionalPointerCasts", false)) {}
258 AllowConditionalIntegerCasts);
260 AllowConditionalPointerCasts);
270 auto exceptionCases =
271 expr(anyOf(allOf(isMacroExpansion(), unless(isNULLMacroExpansion())),
272 hasParent(explicitCastExpr())));
273 auto implicitCastFromBool = implicitCastExpr(
274 anyOf(hasCastKind(CK_IntegralCast), hasCastKind(CK_IntegralToFloating),
276 allOf(anyOf(hasCastKind(CK_NullToPointer),
277 hasCastKind(CK_NullToMemberPointer)),
278 hasSourceExpression(cxxBoolLiteral()))),
279 hasSourceExpression(expr(hasType(booleanType()))),
280 unless(exceptionCases));
282 binaryOperator(hasOperatorName(
"^"), hasLHS(implicitCastFromBool),
283 hasRHS(implicitCastFromBool));
286 anyOf(hasCastKind(CK_IntegralToBoolean),
287 hasCastKind(CK_FloatingToBoolean),
288 hasCastKind(CK_PointerToBoolean),
289 hasCastKind(CK_MemberPointerToBoolean)),
294 hasParent(stmt(anyOf(ifStmt(), whileStmt()), has(declStmt())))),
296 unless(exceptionCases), unless(has(boolXor)),
299 anyOf(hasParent(stmt().bind(
"parentStmt")), anything()),
300 unless(isInTemplateInstantiation()),
301 unless(hasAncestor(functionTemplateDecl())))
302 .bind(
"implicitCastToBool"),
305 auto boolComparison = binaryOperator(
306 anyOf(hasOperatorName(
"=="), hasOperatorName(
"!=")),
307 hasLHS(implicitCastFromBool), hasRHS(implicitCastFromBool));
308 auto boolOpAssignment =
309 binaryOperator(anyOf(hasOperatorName(
"|="), hasOperatorName(
"&=")),
310 hasLHS(expr(hasType(booleanType()))));
313 implicitCastFromBool,
318 unless(hasParent(binaryOperator(
319 anyOf(boolComparison, boolXor, boolOpAssignment)))),
321 anyOf(hasParent(implicitCastExpr().bind(
"furtherImplicitCast")),
323 unless(isInTemplateInstantiation()),
324 unless(hasAncestor(functionTemplateDecl())))
325 .bind(
"implicitCastFromBool"),
330 if (
const auto *CastToBool =
331 Result.Nodes.getNodeAs<ImplicitCastExpr>(
"implicitCastToBool")) {
332 const auto *Parent = Result.Nodes.getNodeAs<Stmt>(
"parentStmt");
333 return handleCastToBool(CastToBool, Parent, *Result.Context);
336 if (
const auto *CastFromBool =
337 Result.Nodes.getNodeAs<ImplicitCastExpr>(
"implicitCastFromBool")) {
338 const auto *NextImplicitCast =
339 Result.Nodes.getNodeAs<ImplicitCastExpr>(
"furtherImplicitCast");
340 return handleCastFromBool(CastFromBool, NextImplicitCast, *Result.Context);
344 void ImplicitBoolCastCheck::handleCastToBool(
const ImplicitCastExpr *Cast,
346 ASTContext &Context) {
347 if (AllowConditionalPointerCasts &&
348 (Cast->getCastKind() == CK_PointerToBoolean ||
349 Cast->getCastKind() == CK_MemberPointerToBoolean) &&
350 isAllowedConditionalCast(Cast, Context)) {
354 if (AllowConditionalIntegerCasts &&
355 Cast->getCastKind() == CK_IntegralToBoolean &&
356 isAllowedConditionalCast(Cast, Context)) {
360 auto Diag =
diag(Cast->getLocStart(),
"implicit cast %0 -> bool")
361 << Cast->getSubExpr()->getType();
363 StringRef EquivalentLiteral =
364 getEquivalentBoolLiteralForExpr(Cast->getSubExpr(), Context);
365 if (!EquivalentLiteral.empty()) {
366 Diag << tooling::fixit::createReplacement(*Cast, EquivalentLiteral);
368 fixGenericExprCastToBool(Diag, Cast, Parent, Context);
372 void ImplicitBoolCastCheck::handleCastFromBool(
373 const ImplicitCastExpr *Cast,
const ImplicitCastExpr *NextImplicitCast,
374 ASTContext &Context) {
376 NextImplicitCast ? NextImplicitCast->getType() : Cast->getType();
377 auto Diag =
diag(Cast->getLocStart(),
"implicit cast bool -> %0") << DestType;
379 if (
const auto *BoolLiteral =
380 dyn_cast<CXXBoolLiteralExpr>(Cast->getSubExpr())) {
381 Diag << tooling::fixit::createReplacement(
382 *Cast, getEquivalentForBoolLiteral(BoolLiteral, DestType, Context));
384 fixGenericExprCastFromBool(Diag, Cast, Context, DestType.getAsString());
SourceLocation Loc
'#' location in the include directive
LangOptions getLangOpts() const
Returns the language options from the context.
std::unique_ptr< ast_matchers::MatchFinder > Finder
void registerMatchers(ast_matchers::MatchFinder *Finder) override
Override this to register AST matchers with Finder.
Base class for all clang-tidy checks.
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.
std::map< std::string, std::string > OptionMap
ClangTidyContext & Context
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
void storeOptions(ClangTidyOptions::OptionMap &Opts) override
Should store all options supported by this check with their current values or default values for opti...
AST_MATCHER(VarDecl, isAsm)
DiagnosticBuilder diag(SourceLocation Loc, StringRef Description, DiagnosticIDs::Level Level=DiagnosticIDs::Warning)
Add a diagnostic with the check's name.
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
ClangTidyChecks that register ASTMatchers should do the actual work in here.