blob: 24022599837906e70f5b0f35fc8a2922222f6823 [file] [log] [blame]
Alexander Kornienko1b677db2015-03-09 12:18:39 +00001//===--- ContainerSizeEmptyCheck.cpp - clang-tidy -------------------------===//
Alexander Kornienko4babd682015-01-15 15:46:58 +00002//
3// The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
Alexander Kornienko1b677db2015-03-09 12:18:39 +00009#include "ContainerSizeEmptyCheck.h"
Kirill Bobyrevacb6b352016-09-13 08:58:11 +000010#include "../utils/Matchers.h"
Alexander Kornienko4babd682015-01-15 15:46:58 +000011#include "clang/AST/ASTContext.h"
12#include "clang/ASTMatchers/ASTMatchers.h"
Alexander Kornienko4babd682015-01-15 15:46:58 +000013#include "clang/Lex/Lexer.h"
Chandler Carruthf7662782015-02-13 09:07:58 +000014#include "llvm/ADT/StringRef.h"
Alexander Kornienko4babd682015-01-15 15:46:58 +000015
16using namespace clang::ast_matchers;
17
Alexander Kornienko4babd682015-01-15 15:46:58 +000018namespace clang {
Alexander Kornienko4babd682015-01-15 15:46:58 +000019namespace tidy {
20namespace readability {
21
22ContainerSizeEmptyCheck::ContainerSizeEmptyCheck(StringRef Name,
23 ClangTidyContext *Context)
24 : ClangTidyCheck(Name, Context) {}
25
26void ContainerSizeEmptyCheck::registerMatchers(MatchFinder *Finder) {
Aaron Ballman1f1b0672015-09-02 16:05:21 +000027 // Only register the matchers for C++; the functionality currently does not
28 // provide any benefit to other languages, despite being benign.
29 if (!getLangOpts().CPlusPlus)
30 return;
31
Kirill Bobyrev0d0bbfd2016-09-13 10:19:13 +000032 const auto ValidContainer = cxxRecordDecl(isSameOrDerivedFrom(
Kirill Bobyrevacb6b352016-09-13 08:58:11 +000033 namedDecl(
34 has(cxxMethodDecl(
35 isConst(), parameterCountIs(0), isPublic(), hasName("size"),
36 returns(qualType(isInteger(), unless(booleanType()))))
37 .bind("size")),
38 has(cxxMethodDecl(isConst(), parameterCountIs(0), isPublic(),
39 hasName("empty"), returns(booleanType()))
40 .bind("empty")))
41 .bind("container")));
Etienne Bergeron9d265992016-04-21 16:57:56 +000042
Alexander Kornienko4babd682015-01-15 15:46:58 +000043 const auto WrongUse = anyOf(
Gabor Horvath533c01d2016-04-19 13:29:05 +000044 hasParent(binaryOperator(
Etienne Bergeron9d265992016-04-21 16:57:56 +000045 matchers::isComparisonOperator(),
Gabor Horvath533c01d2016-04-19 13:29:05 +000046 hasEitherOperand(ignoringImpCasts(anyOf(
47 integerLiteral(equals(1)), integerLiteral(equals(0))))))
48 .bind("SizeBinaryOp")),
Alexander Kornienko4babd682015-01-15 15:46:58 +000049 hasParent(implicitCastExpr(
Gabor Horvatha4fd3be2016-02-09 09:26:11 +000050 hasImplicitDestinationType(booleanType()),
Alexander Kornienko4babd682015-01-15 15:46:58 +000051 anyOf(
52 hasParent(unaryOperator(hasOperatorName("!")).bind("NegOnSize")),
53 anything()))),
Gabor Horvatha4fd3be2016-02-09 09:26:11 +000054 hasParent(explicitCastExpr(hasDestinationType(booleanType()))));
Alexander Kornienko4babd682015-01-15 15:46:58 +000055
56 Finder->addMatcher(
Kirill Bobyrev0d0bbfd2016-09-13 10:19:13 +000057 cxxMemberCallExpr(on(expr(anyOf(hasType(ValidContainer),
58 hasType(pointsTo(ValidContainer)),
Alexander Kornienkof6cd3672017-03-20 22:15:27 +000059 hasType(references(ValidContainer))))),
Alexander Kornienkob5ca17f2016-12-21 23:44:23 +000060 callee(cxxMethodDecl(hasName("size"))), WrongUse,
61 unless(hasAncestor(cxxMethodDecl(
62 ofClass(equalsBoundNode("container"))))))
Aaron Ballmanb9ea09c2015-09-17 13:31:25 +000063 .bind("SizeCallExpr"),
Alexander Kornienko4babd682015-01-15 15:46:58 +000064 this);
65}
66
67void ContainerSizeEmptyCheck::check(const MatchFinder::MatchResult &Result) {
68 const auto *MemberCall =
69 Result.Nodes.getNodeAs<CXXMemberCallExpr>("SizeCallExpr");
70 const auto *BinaryOp = Result.Nodes.getNodeAs<BinaryOperator>("SizeBinaryOp");
Alexander Kornienkof6cd3672017-03-20 22:15:27 +000071 const auto *E = MemberCall->getImplicitObjectArgument();
Alexander Kornienko4babd682015-01-15 15:46:58 +000072 FixItHint Hint;
Gabor Horvathafad84c2016-09-24 02:13:45 +000073 std::string ReplacementText =
74 Lexer::getSourceText(CharSourceRange::getTokenRange(E->getSourceRange()),
75 *Result.SourceManager, getLangOpts());
Alexander Kornienko4babd682015-01-15 15:46:58 +000076 if (E->getType()->isPointerType())
77 ReplacementText += "->empty()";
78 else
79 ReplacementText += ".empty()";
80
81 if (BinaryOp) { // Determine the correct transformation.
82 bool Negation = false;
Gabor Horvatha4e35ec2015-12-12 11:31:25 +000083 const bool ContainerIsLHS =
84 !llvm::isa<IntegerLiteral>(BinaryOp->getLHS()->IgnoreImpCasts());
Alexander Kornienko4babd682015-01-15 15:46:58 +000085 const auto OpCode = BinaryOp->getOpcode();
86 uint64_t Value = 0;
87 if (ContainerIsLHS) {
Gabor Horvatha4e35ec2015-12-12 11:31:25 +000088 if (const auto *Literal = llvm::dyn_cast<IntegerLiteral>(
89 BinaryOp->getRHS()->IgnoreImpCasts()))
Alexander Kornienko4babd682015-01-15 15:46:58 +000090 Value = Literal->getValue().getLimitedValue();
91 else
92 return;
93 } else {
Gabor Horvatha4e35ec2015-12-12 11:31:25 +000094 Value =
95 llvm::dyn_cast<IntegerLiteral>(BinaryOp->getLHS()->IgnoreImpCasts())
96 ->getValue()
97 .getLimitedValue();
Alexander Kornienko4babd682015-01-15 15:46:58 +000098 }
99
100 // Constant that is not handled.
101 if (Value > 1)
102 return;
103
Gabor Horvath533c01d2016-04-19 13:29:05 +0000104 if (Value == 1 && (OpCode == BinaryOperatorKind::BO_EQ ||
105 OpCode == BinaryOperatorKind::BO_NE))
106 return;
107
Alexander Kornienko4babd682015-01-15 15:46:58 +0000108 // Always true, no warnings for that.
109 if ((OpCode == BinaryOperatorKind::BO_GE && Value == 0 && ContainerIsLHS) ||
110 (OpCode == BinaryOperatorKind::BO_LE && Value == 0 && !ContainerIsLHS))
111 return;
112
Gabor Horvath1f30cf62015-12-28 17:20:33 +0000113 // Do not warn for size > 1, 1 < size, size <= 1, 1 >= size.
114 if (Value == 1) {
115 if ((OpCode == BinaryOperatorKind::BO_GT && ContainerIsLHS) ||
116 (OpCode == BinaryOperatorKind::BO_LT && !ContainerIsLHS))
117 return;
118 if ((OpCode == BinaryOperatorKind::BO_LE && ContainerIsLHS) ||
119 (OpCode == BinaryOperatorKind::BO_GE && !ContainerIsLHS))
120 return;
121 }
Gabor Horvathc6ff9c32015-12-21 09:43:52 +0000122
Alexander Kornienko4babd682015-01-15 15:46:58 +0000123 if (OpCode == BinaryOperatorKind::BO_NE && Value == 0)
124 Negation = true;
125 if ((OpCode == BinaryOperatorKind::BO_GT ||
126 OpCode == BinaryOperatorKind::BO_GE) &&
127 ContainerIsLHS)
128 Negation = true;
129 if ((OpCode == BinaryOperatorKind::BO_LT ||
130 OpCode == BinaryOperatorKind::BO_LE) &&
131 !ContainerIsLHS)
132 Negation = true;
133
134 if (Negation)
135 ReplacementText = "!" + ReplacementText;
136 Hint = FixItHint::CreateReplacement(BinaryOp->getSourceRange(),
137 ReplacementText);
138
139 } else {
140 // If there is a conversion above the size call to bool, it is safe to just
141 // replace size with empty.
142 if (const auto *UnaryOp =
143 Result.Nodes.getNodeAs<UnaryOperator>("NegOnSize"))
144 Hint = FixItHint::CreateReplacement(UnaryOp->getSourceRange(),
145 ReplacementText);
146 else
147 Hint = FixItHint::CreateReplacement(MemberCall->getSourceRange(),
148 "!" + ReplacementText);
149 }
Kirill Bobyrevacb6b352016-09-13 08:58:11 +0000150
Alexander Kornienko301130e2015-11-09 15:53:28 +0000151 diag(MemberCall->getLocStart(), "the 'empty' method should be used to check "
152 "for emptiness instead of 'size'")
Alexander Kornienko4babd682015-01-15 15:46:58 +0000153 << Hint;
Kirill Bobyrevacb6b352016-09-13 08:58:11 +0000154
155 const auto *Container = Result.Nodes.getNodeAs<NamedDecl>("container");
156 const auto *Empty = Result.Nodes.getNodeAs<FunctionDecl>("empty");
157
158 diag(Empty->getLocation(), "method %0::empty() defined here",
159 DiagnosticIDs::Note)
160 << Container;
Alexander Kornienko4babd682015-01-15 15:46:58 +0000161}
162
163} // namespace readability
164} // namespace tidy
165} // namespace clang