9 #include "../utils/ASTUtils.h" 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" 20 namespace readability {
24 ContainerSizeEmptyCheck::ContainerSizeEmptyCheck(StringRef
Name,
34 const auto ValidContainer = qualType(hasUnqualifiedDesugaredType(
35 recordType(hasDeclaration(cxxRecordDecl(isSameOrDerivedFrom(
38 isConst(), parameterCountIs(0),
isPublic(),
40 returns(qualType(isInteger(), unless(booleanType()))))
42 has(cxxMethodDecl(isConst(), parameterCountIs(0),
isPublic(),
43 hasName(
"empty"), returns(booleanType()))
45 .bind(
"container")))))));
47 const auto WrongUse = anyOf(
48 hasParent(binaryOperator(
49 matchers::isComparisonOperator(),
50 hasEitherOperand(ignoringImpCasts(anyOf(
51 integerLiteral(equals(1)), integerLiteral(equals(0))))))
52 .bind(
"SizeBinaryOp")),
53 hasParent(implicitCastExpr(
54 hasImplicitDestinationType(booleanType()),
56 hasParent(unaryOperator(hasOperatorName(
"!")).bind(
"NegOnSize")),
58 hasParent(explicitCastExpr(hasDestinationType(booleanType()))));
61 cxxMemberCallExpr(on(expr(anyOf(hasType(ValidContainer),
62 hasType(pointsTo(ValidContainer)),
63 hasType(references(ValidContainer))))),
64 callee(cxxMethodDecl(hasName(
"size"))), WrongUse,
65 unless(hasAncestor(cxxMethodDecl(
66 ofClass(equalsBoundNode(
"container"))))))
67 .bind(
"SizeCallExpr"),
71 const auto DefaultConstructor = cxxConstructExpr(
72 hasDeclaration(cxxConstructorDecl(isDefaultConstructor())));
74 const auto WrongComparend = anyOf(
75 ignoringImpCasts(stringLiteral(hasSize(0))),
76 ignoringImpCasts(cxxBindTemporaryExpr(has(DefaultConstructor))),
77 ignoringImplicit(DefaultConstructor),
79 hasDeclaration(cxxConstructorDecl(isCopyConstructor())),
80 has(expr(ignoringImpCasts(DefaultConstructor)))),
82 hasDeclaration(cxxConstructorDecl(isMoveConstructor())),
83 has(expr(ignoringImpCasts(DefaultConstructor)))));
89 expr(hasType(pointsTo(ValidContainer))).bind(
"Pointee"))),
90 expr(hasType(ValidContainer)).bind(
"STLObject"));
93 anyOf(hasOverloadedOperatorName(
"=="),
94 hasOverloadedOperatorName(
"!=")),
95 anyOf(allOf(hasArgument(0, WrongComparend), hasArgument(1, STLArg)),
96 allOf(hasArgument(0, STLArg), hasArgument(1, WrongComparend))),
98 cxxMethodDecl(ofClass(equalsBoundNode(
"container"))))))
104 const auto *MemberCall =
105 Result.Nodes.getNodeAs<CXXMemberCallExpr>(
"SizeCallExpr");
106 const auto *BinCmp = Result.Nodes.getNodeAs<CXXOperatorCallExpr>(
"BinCmp");
107 const auto *BinaryOp = Result.Nodes.getNodeAs<BinaryOperator>(
"SizeBinaryOp");
108 const auto *Pointee = Result.Nodes.getNodeAs<Expr>(
"Pointee");
111 ? MemberCall->getImplicitObjectArgument()
112 : (Pointee ? Pointee : Result.Nodes.getNodeAs<Expr>(
"STLObject"));
114 std::string ReplacementText =
119 ReplacementText =
"(" + ReplacementText +
")";
121 if (E->getType()->isPointerType())
122 ReplacementText +=
"->empty()";
124 ReplacementText +=
".empty()";
127 if (BinCmp->getOperator() == OO_ExclaimEqual) {
128 ReplacementText =
"!" + ReplacementText;
131 FixItHint::CreateReplacement(BinCmp->getSourceRange(), ReplacementText);
132 }
else if (BinaryOp) {
133 bool Negation =
false;
134 const bool ContainerIsLHS =
135 !llvm::isa<IntegerLiteral>(BinaryOp->getLHS()->IgnoreImpCasts());
136 const auto OpCode = BinaryOp->getOpcode();
138 if (ContainerIsLHS) {
139 if (
const auto *Literal = llvm::dyn_cast<IntegerLiteral>(
140 BinaryOp->getRHS()->IgnoreImpCasts()))
141 Value = Literal->getValue().getLimitedValue();
146 llvm::dyn_cast<IntegerLiteral>(BinaryOp->getLHS()->IgnoreImpCasts())
155 if (Value == 1 && (OpCode == BinaryOperatorKind::BO_EQ ||
156 OpCode == BinaryOperatorKind::BO_NE))
160 if ((OpCode == BinaryOperatorKind::BO_GE && Value == 0 && ContainerIsLHS) ||
161 (OpCode == BinaryOperatorKind::BO_LE && Value == 0 && !ContainerIsLHS))
166 if ((OpCode == BinaryOperatorKind::BO_GT && ContainerIsLHS) ||
167 (OpCode == BinaryOperatorKind::BO_LT && !ContainerIsLHS))
169 if ((OpCode == BinaryOperatorKind::BO_LE && ContainerIsLHS) ||
170 (OpCode == BinaryOperatorKind::BO_GE && !ContainerIsLHS))
174 if (OpCode == BinaryOperatorKind::BO_NE && Value == 0)
176 if ((OpCode == BinaryOperatorKind::BO_GT ||
177 OpCode == BinaryOperatorKind::BO_GE) &&
180 if ((OpCode == BinaryOperatorKind::BO_LT ||
181 OpCode == BinaryOperatorKind::BO_LE) &&
186 ReplacementText =
"!" + ReplacementText;
187 Hint = FixItHint::CreateReplacement(BinaryOp->getSourceRange(),
193 if (
const auto *UnaryOp =
194 Result.Nodes.getNodeAs<UnaryOperator>(
"NegOnSize"))
195 Hint = FixItHint::CreateReplacement(UnaryOp->getSourceRange(),
198 Hint = FixItHint::CreateReplacement(MemberCall->getSourceRange(),
199 "!" + ReplacementText);
203 diag(MemberCall->getBeginLoc(),
204 "the 'empty' method should be used to check " 205 "for emptiness instead of 'size'")
208 diag(BinCmp->getBeginLoc(),
209 "the 'empty' method should be used to check " 210 "for emptiness instead of comparing to an empty object")
214 const auto *Container = Result.Nodes.getNodeAs<NamedDecl>(
"container");
215 if (
const auto *CTS = dyn_cast<ClassTemplateSpecializationDecl>(Container)) {
220 if (CTS->getSpecializationKind() == TSK_ImplicitInstantiation)
221 Container = CTS->getSpecializedTemplate();
223 const auto *
Empty = Result.Nodes.getNodeAs<FunctionDecl>(
"empty");
225 diag(
Empty->getLocation(),
"method %0::empty() defined here",
bool IsBinaryOrTernary(const Expr *E)
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
ClangTidyChecks that register ASTMatchers should do the actual work in here.
Base class for all clang-tidy checks.
const LangOptions & getLangOpts() const
Returns the language options from the context.
void registerMatchers(ast_matchers::MatchFinder *Finder) override
Override this to register AST matchers with Finder.
static constexpr llvm::StringLiteral Name
llvm::Optional< Range > getTokenRange(const SourceManager &SM, const LangOptions &LangOpts, SourceLocation TokLoc)
Returns the taken range at TokLoc.
static bool isPublic(const clang::AccessSpecifier AS, const clang::Linkage Link)
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
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.