Skip to content

Commit 7f1d757

Browse files
authored
[clang-tidy] Fix false-positives in readability-container-size-empty (#74140)
Added support for size-like method returning signed type, and corrected false positive caused by always-false check for size bellow zero. Closes #72619
1 parent 59e79f0 commit 7f1d757

File tree

3 files changed

+83
-11
lines changed

3 files changed

+83
-11
lines changed

clang-tools-extra/clang-tidy/readability/ContainerSizeEmptyCheck.cpp

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -291,10 +291,14 @@ void ContainerSizeEmptyCheck::check(const MatchFinder::MatchResult &Result) {
291291
OpCode == BinaryOperatorKind::BO_NE))
292292
return;
293293

294-
// Always true, no warnings for that.
295-
if ((OpCode == BinaryOperatorKind::BO_GE && Value == 0 && ContainerIsLHS) ||
296-
(OpCode == BinaryOperatorKind::BO_LE && Value == 0 && !ContainerIsLHS))
297-
return;
294+
// Always true/false, no warnings for that.
295+
if (Value == 0) {
296+
if ((OpCode == BinaryOperatorKind::BO_GT && !ContainerIsLHS) ||
297+
(OpCode == BinaryOperatorKind::BO_LT && ContainerIsLHS) ||
298+
(OpCode == BinaryOperatorKind::BO_LE && !ContainerIsLHS) ||
299+
(OpCode == BinaryOperatorKind::BO_GE && ContainerIsLHS))
300+
return;
301+
}
298302

299303
// Do not warn for size > 1, 1 < size, size <= 1, 1 >= size.
300304
if (Value == 1) {
@@ -306,12 +310,32 @@ void ContainerSizeEmptyCheck::check(const MatchFinder::MatchResult &Result) {
306310
return;
307311
}
308312

313+
// Do not warn for size < 1, 1 > size, size <= 0, 0 >= size for non signed
314+
// types
315+
if ((OpCode == BinaryOperatorKind::BO_GT && Value == 1 &&
316+
!ContainerIsLHS) ||
317+
(OpCode == BinaryOperatorKind::BO_LT && Value == 1 && ContainerIsLHS) ||
318+
(OpCode == BinaryOperatorKind::BO_GE && Value == 0 &&
319+
!ContainerIsLHS) ||
320+
(OpCode == BinaryOperatorKind::BO_LE && Value == 0 && ContainerIsLHS)) {
321+
const Expr *Container = ContainerIsLHS
322+
? BinaryOp->getLHS()->IgnoreImpCasts()
323+
: BinaryOp->getRHS()->IgnoreImpCasts();
324+
if (Container->getType()
325+
.getCanonicalType()
326+
.getNonReferenceType()
327+
->isSignedIntegerType())
328+
return;
329+
}
330+
309331
if (OpCode == BinaryOperatorKind::BO_NE && Value == 0)
310332
Negation = true;
333+
311334
if ((OpCode == BinaryOperatorKind::BO_GT ||
312335
OpCode == BinaryOperatorKind::BO_GE) &&
313336
ContainerIsLHS)
314337
Negation = true;
338+
315339
if ((OpCode == BinaryOperatorKind::BO_LT ||
316340
OpCode == BinaryOperatorKind::BO_LE) &&
317341
!ContainerIsLHS)

clang-tools-extra/docs/ReleaseNotes.rst

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -467,7 +467,9 @@ Changes in existing checks
467467
- Improved :doc:`readability-container-size-empty
468468
<clang-tidy/checks/readability/container-size-empty>` check to
469469
detect comparison between string and empty string literals and support
470-
``length()`` method as an alternative to ``size()``.
470+
``length()`` method as an alternative to ``size()``. Resolved false positives
471+
tied to negative values from size-like methods, and one triggered by size
472+
checks below zero.
471473

472474
- Improved :doc:`readability-function-size
473475
<clang-tidy/checks/readability/function-size>` check configuration to use

clang-tools-extra/test/clang-tidy/checkers/readability/container-size-empty.cpp

Lines changed: 52 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ class TemplatedContainer {
3333
public:
3434
bool operator==(const TemplatedContainer<T>& other) const;
3535
bool operator!=(const TemplatedContainer<T>& other) const;
36-
int size() const;
36+
unsigned long size() const;
3737
bool empty() const;
3838
};
3939

@@ -42,7 +42,7 @@ class PrivateEmpty {
4242
public:
4343
bool operator==(const PrivateEmpty<T>& other) const;
4444
bool operator!=(const PrivateEmpty<T>& other) const;
45-
int size() const;
45+
unsigned long size() const;
4646
private:
4747
bool empty() const;
4848
};
@@ -61,7 +61,7 @@ struct EnumSize {
6161
class Container {
6262
public:
6363
bool operator==(const Container& other) const;
64-
int size() const;
64+
unsigned long size() const;
6565
bool empty() const;
6666
};
6767

@@ -70,13 +70,13 @@ class Derived : public Container {
7070

7171
class Container2 {
7272
public:
73-
int size() const;
73+
unsigned long size() const;
7474
bool empty() const { return size() == 0; }
7575
};
7676

7777
class Container3 {
7878
public:
79-
int size() const;
79+
unsigned long size() const;
8080
bool empty() const;
8181
};
8282

@@ -85,7 +85,7 @@ bool Container3::empty() const { return this->size() == 0; }
8585
class Container4 {
8686
public:
8787
bool operator==(const Container4& rhs) const;
88-
int size() const;
88+
unsigned long size() const;
8989
bool empty() const { return *this == Container4(); }
9090
};
9191

@@ -815,3 +815,49 @@ bool testNotEmptyStringLiterals(const std::string& s)
815815
using namespace std::string_literals;
816816
return s == "foo"s;
817817
}
818+
819+
namespace PR72619 {
820+
struct SS {
821+
bool empty() const;
822+
int size() const;
823+
};
824+
825+
struct SU {
826+
bool empty() const;
827+
unsigned size() const;
828+
};
829+
830+
void f(const SU& s) {
831+
if (s.size() < 0) {}
832+
if (0 > s.size()) {}
833+
if (s.size() >= 0) {}
834+
if (0 <= s.size()) {}
835+
if (s.size() < 1)
836+
;
837+
// CHECK-MESSAGES: :[[@LINE-2]]:9: warning: the 'empty' method should be used to check for emptiness instead of 'size' [readability-container-size-empty]
838+
// CHECK-FIXES: {{^ }}if (s.empty()){{$}}
839+
if (1 > s.size())
840+
;
841+
// CHECK-MESSAGES: :[[@LINE-2]]:13: warning: the 'empty' method should be used to check for emptiness instead of 'size' [readability-container-size-empty]
842+
// CHECK-FIXES: {{^ }}if (s.empty()){{$}}
843+
if (s.size() <= 0)
844+
;
845+
// CHECK-MESSAGES: :[[@LINE-2]]:9: warning: the 'empty' method should be used to check for emptiness instead of 'size' [readability-container-size-empty]
846+
// CHECK-FIXES: {{^ }}if (s.empty()){{$}}
847+
if (0 >= s.size())
848+
;
849+
// CHECK-MESSAGES: :[[@LINE-2]]:14: warning: the 'empty' method should be used to check for emptiness instead of 'size' [readability-container-size-empty]
850+
// CHECK-FIXES: {{^ }}if (s.empty()){{$}}
851+
}
852+
853+
void f(const SS& s) {
854+
if (s.size() < 0) {}
855+
if (0 > s.size()) {}
856+
if (s.size() >= 0) {}
857+
if (0 <= s.size()) {}
858+
if (s.size() < 1) {}
859+
if (1 > s.size()) {}
860+
if (s.size() <= 0) {}
861+
if (0 >= s.size()) {}
862+
}
863+
}

0 commit comments

Comments
 (0)