11 #include "clang/AST/ASTContext.h"
12 #include "clang/ASTMatchers/ASTMatchFinder.h"
13 #include "clang/Lex/Lexer.h"
15 using namespace clang::ast_matchers;
19 namespace readability {
24 SourceManager &
SM =
Finder->getASTContext().getSourceManager();
25 SourceLocation
Loc = Node.getLocStart();
26 return SM.isMacroBodyExpansion(Loc) || SM.isMacroArgExpansion(Loc);
29 bool isNULLMacroExpansion(
const Stmt *Statement, ASTContext &
Context) {
30 SourceManager &SM = Context.getSourceManager();
31 const LangOptions &LO = Context.getLangOpts();
32 SourceLocation Loc = Statement->getLocStart();
33 return SM.isMacroBodyExpansion(Loc) &&
34 clang::Lexer::getImmediateMacroName(Loc, SM, LO) ==
"NULL";
38 return isNULLMacroExpansion(&Node,
Finder->getASTContext());
41 ast_matchers::internal::Matcher<Expr> createExceptionCasesMatcher() {
42 return expr(anyOf(hasParent(explicitCastExpr()),
43 allOf(isMacroExpansion(), unless(isNULLMacroExpansion())),
44 isInTemplateInstantiation(),
45 hasAncestor(functionTemplateDecl())));
48 StatementMatcher createImplicitCastFromBoolMatcher() {
49 return implicitCastExpr(
50 unless(createExceptionCasesMatcher()),
51 anyOf(hasCastKind(CK_IntegralCast), hasCastKind(CK_IntegralToFloating),
53 allOf(anyOf(hasCastKind(CK_NullToPointer),
54 hasCastKind(CK_NullToMemberPointer)),
55 hasSourceExpression(cxxBoolLiteral()))),
56 hasSourceExpression(expr(hasType(qualType(booleanType())))));
60 getZeroLiteralToCompareWithForGivenType(CastKind CastExpressionKind,
61 QualType CastSubExpressionType,
62 ASTContext &Context) {
63 switch (CastExpressionKind) {
64 case CK_IntegralToBoolean:
65 return CastSubExpressionType->isUnsignedIntegerType() ?
"0u" :
"0";
67 case CK_FloatingToBoolean:
68 return Context.hasSameType(CastSubExpressionType, Context.FloatTy) ?
"0.0f"
71 case CK_PointerToBoolean:
72 case CK_MemberPointerToBoolean:
73 return Context.getLangOpts().CPlusPlus11 ?
"nullptr" :
"0";
76 llvm_unreachable(
"Unexpected cast kind");
80 bool isUnaryLogicalNotOperator(
const Stmt *Statement) {
81 const auto *UnaryOperatorExpression =
82 llvm::dyn_cast<UnaryOperator>(Statement);
83 return UnaryOperatorExpression !=
nullptr &&
84 UnaryOperatorExpression->getOpcode() == UO_LNot;
87 bool areParensNeededForOverloadedOperator(OverloadedOperatorKind OperatorKind) {
88 switch (OperatorKind) {
104 bool areParensNeededForStatement(
const Stmt *Statement) {
105 if (
const auto *OverloadedOperatorCall =
106 llvm::dyn_cast<CXXOperatorCallExpr>(Statement)) {
107 return areParensNeededForOverloadedOperator(
108 OverloadedOperatorCall->getOperator());
111 return llvm::isa<BinaryOperator>(Statement) ||
112 llvm::isa<UnaryOperator>(Statement);
115 void addFixItHintsForGenericExpressionCastToBool(
116 DiagnosticBuilder &Diagnostic,
const ImplicitCastExpr *CastExpression,
117 const Stmt *ParentStatement, ASTContext &Context) {
120 bool InvertComparison =
121 ParentStatement !=
nullptr && isUnaryLogicalNotOperator(ParentStatement);
122 if (InvertComparison) {
123 SourceLocation ParentStartLoc = ParentStatement->getLocStart();
124 SourceLocation ParentEndLoc =
125 llvm::cast<UnaryOperator>(ParentStatement)->getSubExpr()->getLocStart();
126 Diagnostic.AddFixItHint(FixItHint::CreateRemoval(
127 CharSourceRange::getCharRange(ParentStartLoc, ParentEndLoc)));
129 auto FurtherParents = Context.getParents(*ParentStatement);
130 ParentStatement = FurtherParents[0].get<Stmt>();
133 const Expr *SubExpression = CastExpression->getSubExpr();
135 bool NeedInnerParens = areParensNeededForStatement(SubExpression);
136 bool NeedOuterParens = ParentStatement !=
nullptr &&
137 areParensNeededForStatement(ParentStatement);
139 std::string StartLocInsertion;
141 if (NeedOuterParens) {
142 StartLocInsertion +=
"(";
144 if (NeedInnerParens) {
145 StartLocInsertion +=
"(";
148 if (!StartLocInsertion.empty()) {
149 SourceLocation StartLoc = CastExpression->getLocStart();
150 Diagnostic.AddFixItHint(
151 FixItHint::CreateInsertion(StartLoc, StartLocInsertion));
154 std::string EndLocInsertion;
156 if (NeedInnerParens) {
157 EndLocInsertion +=
")";
160 if (InvertComparison) {
161 EndLocInsertion +=
" == ";
163 EndLocInsertion +=
" != ";
166 EndLocInsertion += getZeroLiteralToCompareWithForGivenType(
167 CastExpression->getCastKind(), SubExpression->getType(),
Context);
169 if (NeedOuterParens) {
170 EndLocInsertion +=
")";
173 SourceLocation EndLoc = Lexer::getLocForEndOfToken(
174 CastExpression->getLocEnd(), 0, Context.getSourceManager(),
175 Context.getLangOpts());
176 Diagnostic.AddFixItHint(FixItHint::CreateInsertion(EndLoc, EndLocInsertion));
179 StringRef getEquivalentBoolLiteralForExpression(
const Expr *Expression,
180 ASTContext &Context) {
181 if (isNULLMacroExpansion(Expression, Context)) {
185 if (
const auto *IntLit = llvm::dyn_cast<IntegerLiteral>(Expression)) {
186 return (IntLit->getValue() == 0) ?
"false" :
"true";
189 if (
const auto *FloatLit = llvm::dyn_cast<FloatingLiteral>(Expression)) {
190 llvm::APFloat FloatLitAbsValue = FloatLit->getValue();
191 FloatLitAbsValue.clearSign();
192 return (FloatLitAbsValue.bitcastToAPInt() == 0) ?
"false" :
"true";
195 if (
const auto *CharLit = llvm::dyn_cast<CharacterLiteral>(Expression)) {
196 return (CharLit->getValue() == 0) ?
"false" :
"true";
199 if (llvm::isa<StringLiteral>(Expression->IgnoreCasts())) {
206 void addFixItHintsForLiteralCastToBool(DiagnosticBuilder &Diagnostic,
207 const ImplicitCastExpr *CastExpression,
208 StringRef EquivalentLiteralExpression) {
209 SourceLocation StartLoc = CastExpression->getLocStart();
210 SourceLocation EndLoc = CastExpression->getLocEnd();
212 Diagnostic.AddFixItHint(FixItHint::CreateReplacement(
213 CharSourceRange::getTokenRange(StartLoc, EndLoc),
214 EquivalentLiteralExpression));
217 void addFixItHintsForGenericExpressionCastFromBool(
218 DiagnosticBuilder &Diagnostic,
const ImplicitCastExpr *CastExpression,
219 ASTContext &Context, StringRef OtherType) {
220 const Expr *SubExpression = CastExpression->getSubExpr();
221 bool NeedParens = !llvm::isa<ParenExpr>(SubExpression);
223 std::string StartLocInsertion =
"static_cast<";
224 StartLocInsertion += OtherType.str();
225 StartLocInsertion +=
">";
227 StartLocInsertion +=
"(";
230 SourceLocation StartLoc = CastExpression->getLocStart();
231 Diagnostic.AddFixItHint(
232 FixItHint::CreateInsertion(StartLoc, StartLocInsertion));
235 SourceLocation EndLoc = Lexer::getLocForEndOfToken(
236 CastExpression->getLocEnd(), 0, Context.getSourceManager(),
237 Context.getLangOpts());
239 Diagnostic.AddFixItHint(FixItHint::CreateInsertion(EndLoc,
")"));
243 StringRef getEquivalentLiteralForBoolLiteral(
244 const CXXBoolLiteralExpr *BoolLiteralExpression, QualType DestinationType,
245 ASTContext &Context) {
247 if (!Context.getLangOpts().CPlusPlus11 &&
248 (DestinationType->isPointerType() ||
249 DestinationType->isMemberPointerType()) &&
250 BoolLiteralExpression->getValue() ==
false) {
254 if (DestinationType->isFloatingType()) {
255 if (BoolLiteralExpression->getValue() ==
true) {
256 return Context.hasSameType(DestinationType, Context.FloatTy) ?
"1.0f"
259 return Context.hasSameType(DestinationType, Context.FloatTy) ?
"0.0f"
263 if (BoolLiteralExpression->getValue() ==
true) {
264 return DestinationType->isUnsignedIntegerType() ?
"1u" :
"1";
266 return DestinationType->isUnsignedIntegerType() ?
"0u" :
"0";
269 void addFixItHintsForLiteralCastFromBool(DiagnosticBuilder &Diagnostic,
270 const ImplicitCastExpr *CastExpression,
272 QualType DestinationType) {
273 SourceLocation StartLoc = CastExpression->getLocStart();
274 SourceLocation EndLoc = CastExpression->getLocEnd();
275 const auto *BoolLiteralExpression =
276 llvm::dyn_cast<CXXBoolLiteralExpr>(CastExpression->getSubExpr());
278 Diagnostic.AddFixItHint(FixItHint::CreateReplacement(
279 CharSourceRange::getTokenRange(StartLoc, EndLoc),
280 getEquivalentLiteralForBoolLiteral(BoolLiteralExpression, DestinationType,
284 StatementMatcher createConditionalExpressionMatcher() {
285 return stmt(anyOf(ifStmt(), conditionalOperator(),
286 parenExpr(hasParent(conditionalOperator()))));
289 bool isAllowedConditionalCast(
const ImplicitCastExpr *CastExpression,
290 ASTContext &Context) {
291 auto AllowedConditionalMatcher = stmt(hasParent(stmt(
292 anyOf(createConditionalExpressionMatcher(),
293 unaryOperator(hasOperatorName(
"!"),
294 hasParent(createConditionalExpressionMatcher()))))));
296 auto MatchResult = match(AllowedConditionalMatcher, *CastExpression, Context);
297 return !MatchResult.empty();
302 ImplicitBoolCastCheck::ImplicitBoolCastCheck(StringRef
Name,
305 AllowConditionalIntegerCasts(
306 Options.get(
"AllowConditionalIntegerCasts", false)),
307 AllowConditionalPointerCasts(
308 Options.get(
"AllowConditionalPointerCasts", false)) {}
312 AllowConditionalIntegerCasts);
314 AllowConditionalPointerCasts);
327 unless(createExceptionCasesMatcher()),
332 hasParent(stmt(anyOf(ifStmt(), whileStmt()), has(declStmt())))),
333 anyOf(hasCastKind(CK_IntegralToBoolean),
334 hasCastKind(CK_FloatingToBoolean),
335 hasCastKind(CK_PointerToBoolean),
336 hasCastKind(CK_MemberPointerToBoolean)),
339 anyOf(hasParent(stmt().bind(
"parentStmt")), anything()))
340 .bind(
"implicitCastToBool"),
345 createImplicitCastFromBoolMatcher(),
350 unless(hasParent(binaryOperator(
351 anyOf(hasOperatorName(
"=="), hasOperatorName(
"!=")),
352 hasLHS(createImplicitCastFromBoolMatcher()),
353 hasRHS(createImplicitCastFromBoolMatcher())))),
355 anyOf(hasParent(implicitCastExpr().bind(
"furtherImplicitCast")),
357 .bind(
"implicitCastFromBool"),
362 if (
const auto *CastToBool =
363 Result.Nodes.getNodeAs<ImplicitCastExpr>(
"implicitCastToBool")) {
364 const auto *ParentStatement = Result.Nodes.getNodeAs<Stmt>(
"parentStmt");
365 return handleCastToBool(CastToBool, ParentStatement, *Result.Context);
368 if (
const auto *CastFromBool =
369 Result.Nodes.getNodeAs<ImplicitCastExpr>(
"implicitCastFromBool")) {
370 const auto *FurtherImplicitCastExpression =
371 Result.Nodes.getNodeAs<ImplicitCastExpr>(
"furtherImplicitCast");
372 return handleCastFromBool(CastFromBool, FurtherImplicitCastExpression,
377 void ImplicitBoolCastCheck::handleCastToBool(
378 const ImplicitCastExpr *CastExpression,
const Stmt *ParentStatement,
379 ASTContext &Context) {
380 if (AllowConditionalPointerCasts &&
381 (CastExpression->getCastKind() == CK_PointerToBoolean ||
382 CastExpression->getCastKind() == CK_MemberPointerToBoolean) &&
383 isAllowedConditionalCast(CastExpression, Context)) {
387 if (AllowConditionalIntegerCasts &&
388 CastExpression->getCastKind() == CK_IntegralToBoolean &&
389 isAllowedConditionalCast(CastExpression, Context)) {
393 std::string OtherType = CastExpression->getSubExpr()->getType().getAsString();
394 DiagnosticBuilder Diagnostic =
395 diag(CastExpression->getLocStart(),
"implicit cast '%0' -> bool")
398 StringRef EquivalentLiteralExpression = getEquivalentBoolLiteralForExpression(
399 CastExpression->getSubExpr(), Context);
400 if (!EquivalentLiteralExpression.empty()) {
401 addFixItHintsForLiteralCastToBool(Diagnostic, CastExpression,
402 EquivalentLiteralExpression);
404 addFixItHintsForGenericExpressionCastToBool(Diagnostic, CastExpression,
405 ParentStatement, Context);
409 void ImplicitBoolCastCheck::handleCastFromBool(
410 const ImplicitCastExpr *CastExpression,
411 const ImplicitCastExpr *FurtherImplicitCastExpression,
412 ASTContext &Context) {
413 QualType DestinationType = (FurtherImplicitCastExpression !=
nullptr)
414 ? FurtherImplicitCastExpression->getType()
415 : CastExpression->getType();
416 std::string DestinationTypeString = DestinationType.getAsString();
417 DiagnosticBuilder Diagnostic =
418 diag(CastExpression->getLocStart(),
"implicit cast bool -> '%0'")
419 << DestinationTypeString;
421 if (llvm::isa<CXXBoolLiteralExpr>(CastExpression->getSubExpr())) {
422 addFixItHintsForLiteralCastFromBool(Diagnostic, CastExpression, Context,
425 addFixItHintsForGenericExpressionCastFromBool(
426 Diagnostic, CastExpression, Context, DestinationTypeString);
SourceLocation Loc
'#' location in the include directive
AST_MATCHER(Type, isStrictlyInteger)
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...
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.