10 #include "../utils/Matchers.h"
11 #include "clang/AST/ASTContext.h"
12 #include "clang/ASTMatchers/ASTMatchers.h"
13 #include "clang/Lex/Lexer.h"
14 #include "llvm/ADT/StringRef.h"
16 using namespace clang::ast_matchers;
20 namespace readability {
22 ContainerSizeEmptyCheck::ContainerSizeEmptyCheck(StringRef
Name,
32 const auto ValidContainer = cxxRecordDecl(isSameOrDerivedFrom(
35 isConst(), parameterCountIs(0), isPublic(), hasName(
"size"),
36 returns(qualType(isInteger(), unless(booleanType()))))
38 has(cxxMethodDecl(isConst(), parameterCountIs(0), isPublic(),
39 hasName(
"empty"), returns(booleanType()))
43 const auto WrongUse = anyOf(
44 hasParent(binaryOperator(
45 matchers::isComparisonOperator(),
46 hasEitherOperand(ignoringImpCasts(anyOf(
47 integerLiteral(equals(1)), integerLiteral(equals(0))))))
48 .bind(
"SizeBinaryOp")),
49 hasParent(implicitCastExpr(
50 hasImplicitDestinationType(booleanType()),
52 hasParent(unaryOperator(hasOperatorName(
"!")).bind(
"NegOnSize")),
54 hasParent(explicitCastExpr(hasDestinationType(booleanType()))));
57 cxxMemberCallExpr(on(expr(anyOf(hasType(ValidContainer),
58 hasType(pointsTo(ValidContainer)),
59 hasType(references(ValidContainer))))
61 callee(cxxMethodDecl(hasName(
"size"))), WrongUse,
62 unless(hasAncestor(cxxMethodDecl(
63 ofClass(equalsBoundNode(
"container"))))))
64 .bind(
"SizeCallExpr"),
69 const auto *MemberCall =
70 Result.Nodes.getNodeAs<CXXMemberCallExpr>(
"SizeCallExpr");
71 const auto *BinaryOp = Result.Nodes.getNodeAs<BinaryOperator>(
"SizeBinaryOp");
72 const auto *E = Result.Nodes.getNodeAs<Expr>(
"STLObject");
74 std::string ReplacementText =
75 Lexer::getSourceText(CharSourceRange::getTokenRange(E->getSourceRange()),
77 if (E->getType()->isPointerType())
78 ReplacementText +=
"->empty()";
80 ReplacementText +=
".empty()";
83 bool Negation =
false;
84 const bool ContainerIsLHS =
85 !llvm::isa<IntegerLiteral>(BinaryOp->getLHS()->IgnoreImpCasts());
86 const auto OpCode = BinaryOp->getOpcode();
89 if (
const auto *Literal = llvm::dyn_cast<IntegerLiteral>(
90 BinaryOp->getRHS()->IgnoreImpCasts()))
91 Value = Literal->getValue().getLimitedValue();
96 llvm::dyn_cast<IntegerLiteral>(BinaryOp->getLHS()->IgnoreImpCasts())
105 if (Value == 1 && (OpCode == BinaryOperatorKind::BO_EQ ||
106 OpCode == BinaryOperatorKind::BO_NE))
110 if ((OpCode == BinaryOperatorKind::BO_GE && Value == 0 && ContainerIsLHS) ||
111 (OpCode == BinaryOperatorKind::BO_LE && Value == 0 && !ContainerIsLHS))
116 if ((OpCode == BinaryOperatorKind::BO_GT && ContainerIsLHS) ||
117 (OpCode == BinaryOperatorKind::BO_LT && !ContainerIsLHS))
119 if ((OpCode == BinaryOperatorKind::BO_LE && ContainerIsLHS) ||
120 (OpCode == BinaryOperatorKind::BO_GE && !ContainerIsLHS))
124 if (OpCode == BinaryOperatorKind::BO_NE && Value == 0)
126 if ((OpCode == BinaryOperatorKind::BO_GT ||
127 OpCode == BinaryOperatorKind::BO_GE) &&
130 if ((OpCode == BinaryOperatorKind::BO_LT ||
131 OpCode == BinaryOperatorKind::BO_LE) &&
136 ReplacementText =
"!" + ReplacementText;
137 Hint = FixItHint::CreateReplacement(BinaryOp->getSourceRange(),
143 if (
const auto *UnaryOp =
144 Result.Nodes.getNodeAs<UnaryOperator>(
"NegOnSize"))
145 Hint = FixItHint::CreateReplacement(UnaryOp->getSourceRange(),
148 Hint = FixItHint::CreateReplacement(MemberCall->getSourceRange(),
149 "!" + ReplacementText);
152 diag(MemberCall->getLocStart(),
"the 'empty' method should be used to check "
153 "for emptiness instead of 'size'")
156 const auto *Container = Result.Nodes.getNodeAs<NamedDecl>(
"container");
157 const auto *Empty = Result.Nodes.getNodeAs<FunctionDecl>(
"empty");
159 diag(Empty->getLocation(),
"method %0::empty() defined here",
LangOptions getLangOpts() const
Returns the language options from the context.
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
ClangTidyChecks that register ASTMatchers should do the actual work in here.
std::unique_ptr< ast_matchers::MatchFinder > Finder
Base class for all clang-tidy checks.
void registerMatchers(ast_matchers::MatchFinder *Finder) override
Override this to register AST matchers with Finder.
ClangTidyContext & Context
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
DiagnosticBuilder diag(SourceLocation Loc, StringRef Description, DiagnosticIDs::Level Level=DiagnosticIDs::Warning)
Add a diagnostic with the check's name.