[clang-tools-extra] [clang-tidy] Fix FP in readability-container-size-empty with compairing to unrelated type (PR #190535)
Baranov Victor via cfe-commits
cfe-commits at lists.llvm.org
Mon Apr 13 10:46:25 PDT 2026
https://github.com/vbvictor updated https://github.com/llvm/llvm-project/pull/190535
>From 5282747199d3c7f867b28206301726cb7d36acf2 Mon Sep 17 00:00:00 2001
From: Victor Baranov <bar.victor.2002 at gmail.com>
Date: Mon, 13 Apr 2026 20:46:10 +0300
Subject: [PATCH] fix
---
.../readability/ContainerSizeEmptyCheck.cpp | 28 ++++---
clang-tools-extra/docs/ReleaseNotes.rst | 3 +
.../readability/container-size-empty.cpp | 74 +++++++++++++++++++
3 files changed, 96 insertions(+), 9 deletions(-)
diff --git a/clang-tools-extra/clang-tidy/readability/ContainerSizeEmptyCheck.cpp b/clang-tools-extra/clang-tidy/readability/ContainerSizeEmptyCheck.cpp
index 2101fd2248e8a..cbad3d244d841 100644
--- a/clang-tools-extra/clang-tidy/readability/ContainerSizeEmptyCheck.cpp
+++ b/clang-tools-extra/clang-tidy/readability/ContainerSizeEmptyCheck.cpp
@@ -144,15 +144,18 @@ void ContainerSizeEmptyCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
}
void ContainerSizeEmptyCheck::registerMatchers(MatchFinder *Finder) {
- const auto ValidContainerRecord = cxxRecordDecl(isSameOrDerivedFrom(namedDecl(
- has(cxxMethodDecl(
- isConst(), parameterCountIs(0), isPublic(),
- hasAnyName("size", "length"),
- returns(qualType(isIntegralType(), unless(booleanType()))))
- .bind("size")),
- has(cxxMethodDecl(isConst(), parameterCountIs(0), isPublic(),
- hasName("empty"), returns(booleanType()))
- .bind("empty")))));
+ const auto ValidContainerRecord =
+ cxxRecordDecl(
+ isSameOrDerivedFrom(namedDecl(
+ has(cxxMethodDecl(isConst(), parameterCountIs(0), isPublic(),
+ hasAnyName("size", "length"),
+ returns(qualType(isIntegralType(),
+ unless(booleanType()))))
+ .bind("size")),
+ has(cxxMethodDecl(isConst(), parameterCountIs(0), isPublic(),
+ hasName("empty"), returns(booleanType()))
+ .bind("empty")))))
+ .bind("ContainerDecl");
const auto ValidContainerNonTemplateType =
qualType(hasUnqualifiedDesugaredType(
@@ -235,6 +238,13 @@ void ContainerSizeEmptyCheck::registerMatchers(MatchFinder *Finder) {
Finder->addMatcher(
binaryOperation(
hasAnyOperatorName("==", "!="), hasOperands(WrongComparend, STLArg),
+ unless(hasEitherOperand(cxxConstructExpr(
+ argumentCountIs(0),
+ unless(hasType(qualType(hasCanonicalType(hasDeclaration(
+ // 'equalsBoundNode' needs the 'ContainerDecl' binding
+ // from 'STLArg' to already exist, so this constraint must
+ // appear after 'hasOperands' matcher
+ namedDecl(equalsBoundNode("ContainerDecl")))))))))),
unless(allOf(hasLHS(hasType(ExcludedComparisonTypesMatcher)),
hasRHS(hasType(SameExcludedComparisonTypesMatcher)))),
NotInEmptyMethodOfContainer)
diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst
index 69dc5b9633398..565e0dfedb971 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -399,6 +399,9 @@ Changes in existing checks
- Reduce verbosity by removing the note indicating source location of the
``empty`` function.
+ - Fixed a false positive with suggesting ``empty`` when comparing a container
+ to a default-constructed object of an unrelated type.
+
- Improved :doc:`readability-else-after-return
<clang-tidy/checks/readability/else-after-return>` check:
diff --git a/clang-tools-extra/test/clang-tidy/checkers/readability/container-size-empty.cpp b/clang-tools-extra/test/clang-tidy/checkers/readability/container-size-empty.cpp
index 2b8b3261ac765..cd2eebb16138b 100644
--- a/clang-tools-extra/test/clang-tidy/checkers/readability/container-size-empty.cpp
+++ b/clang-tools-extra/test/clang-tidy/checkers/readability/container-size-empty.cpp
@@ -954,3 +954,77 @@ struct DestructorUser {
}
};
}
+
+namespace GH162287 {
+
+struct Label {
+ virtual ~Label();
+};
+bool operator==(std::string, const Label&);
+bool operator==(std::string, std::vector<char>);
+bool operator==(const Label&, std::string);
+
+void testUnrelatedType() {
+ std::string s{"aa"};
+ if (s == Label{})
+ ;
+ if (s == Label())
+ ;
+ if (s == std::vector<char>{})
+ ;
+ if (Label() == s)
+ ;
+}
+
+void testValid() {
+ std::string s{"aa"};
+ std::vector<int> v;
+ Container c;
+
+ // CHECK-MESSAGES: :[[@LINE+2]]:7: warning: the 'empty' method should be used to check for emptiness instead of comparing to an empty object
+ // CHECK-FIXES: if (s.empty())
+ if (s == std::string{})
+ ;
+ // CHECK-MESSAGES: :[[@LINE+2]]:7: warning: the 'empty' method should be used to check for emptiness instead of comparing to an empty object
+ // CHECK-FIXES: if (s.empty())
+ if (std::string() == s)
+ ;
+ // CHECK-MESSAGES: :[[@LINE+2]]:7: warning: the 'empty' method should be used to check for emptiness instead of comparing to an empty object
+ // CHECK-FIXES: if (c.empty())
+ if (c == Container())
+ ;
+ Container *p = nullptr;
+ // CHECK-MESSAGES: :[[@LINE+2]]:7: warning: the 'empty' method should be used to check for emptiness instead of comparing to an empty object
+ // CHECK-FIXES: if (p->empty())
+ if (*p == Container())
+ ;
+ using MyString = std::string;
+ MyString ms{"aa"};
+ // CHECK-MESSAGES: :[[@LINE+2]]:7: warning: the 'empty' method should be used to check for emptiness instead of comparing to an empty object
+ // CHECK-FIXES: if (ms.empty())
+ if (ms == std::string())
+ ;
+ bool b1 = s == Label();
+ // CHECK-MESSAGES: :[[@LINE+2]]:13: warning: the 'empty' method should be used to check for emptiness instead of comparing to an empty object
+ // CHECK-FIXES: bool b2 = c.empty();
+ bool b2 = c == Container();
+ // CHECK-MESSAGES: :[[@LINE+2]]:7: warning: the 'empty' method should be used to check for emptiness instead of comparing to an empty object
+ // CHECK-FIXES: if (v.empty())
+ if (v == std::vector<int>())
+ ;
+}
+
+template <typename T>
+bool testUnrelatedInTemplate(std::string s) {
+ return s == Label{};
+}
+template bool testUnrelatedInTemplate<int>(std::string);
+
+template <typename T>
+bool testDependentValidContainer(TemplatedContainer<T> c) {
+ return c == TemplatedContainer<T>();
+ // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: the 'empty' method should be used to check for emptiness instead of comparing to an empty object
+ // CHECK-FIXES: return c.empty();
+}
+template bool testDependentValidContainer<int>(TemplatedContainer<int>);
+}
More information about the cfe-commits
mailing list