[clang] [clang] Fixed missing sentinel attribute diagnostic discrepancy with explicit object parameters (PR #208842)
via cfe-commits
cfe-commits at lists.llvm.org
Fri Jul 10 14:46:25 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clang
Author: Tony Guillot (to268)
<details>
<summary>Changes</summary>
When using the sentinel attribute on a variadic function with an explicit object parameter, the missing sentinel attribute diagnostic takes into account the explicit object parameter as an argument. It was diagnosing `missing sentinel in function call` instead of `not enough variable arguments in '%0' declaration to fit a sentinel`, compared to other similar variadic functions.
Fixes #<!-- -->200007
---
Full diff: https://github.com/llvm/llvm-project/pull/208842.diff
4 Files Affected:
- (modified) clang/docs/ReleaseNotes.md (+3)
- (modified) clang/lib/Sema/SemaExpr.cpp (+6-1)
- (modified) clang/test/Sema/attr-sentinel.c (+7-7)
- (modified) clang/test/SemaCXX/attr-sentinel.cpp (+87-1)
``````````diff
diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index 4e3ea3b91d20f..3ed55f2977f78 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -736,6 +736,9 @@ latest release, please see the [Clang Web Site](https://clang.llvm.org) or the
- Added a clearer diagnostic for uninitialized decomposition declarations. (#GH90107)
+- Fixed missing sentinel attribute diagnostic discrepancy with explicit object
+ parameters in variadic functions. (#GH200007)
+
### Improvements to Clang's time-trace
### Improvements to Coverage Mapping
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index 4e9c0eccbe43e..5d842ef6e3cfa 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -467,9 +467,14 @@ void Sema::DiagnoseSentinelCalls(const NamedDecl *D, SourceLocation Loc,
// The number of arguments which should follow the sentinel.
unsigned NumArgsAfterSentinel = Attr->getSentinel();
+ unsigned RequiredNumParams = NumFormalParams + NumArgsAfterSentinel + 1;
+ if (const auto *MD = dyn_cast<CXXMethodDecl>(D))
+ if (MD->isExplicitObjectMemberFunction())
+ RequiredNumParams++;
+
// If there aren't enough arguments for all the formal parameters,
// the sentinel, and the args after the sentinel, complain.
- if (Args.size() < NumFormalParams + NumArgsAfterSentinel + 1) {
+ if (Args.size() < RequiredNumParams) {
Diag(Loc, diag::warn_not_enough_argument) << D->getDeclName();
Diag(D->getLocation(), diag::note_sentinel_here) << int(CalleeKind);
return;
diff --git a/clang/test/Sema/attr-sentinel.c b/clang/test/Sema/attr-sentinel.c
index 9af11296b07f6..04a9a66ad90f5 100644
--- a/clang/test/Sema/attr-sentinel.c
+++ b/clang/test/Sema/attr-sentinel.c
@@ -2,7 +2,7 @@
#define NULL (void*)0
-#define ATTR __attribute__ ((__sentinel__))
+#define ATTR __attribute__ ((__sentinel__))
void foo1 (int x, ...) ATTR; // expected-note 3 {{function has been explicitly marked sentinel here}}
void foo5 (int x, ...) __attribute__ ((__sentinel__(1))); // expected-note {{function has been explicitly marked sentinel here}}
@@ -36,7 +36,7 @@ void test1(void) {
// PR11002
FOOMACRO(1, 2); // expected-warning {{missing sentinel in function call}}
}
-
+
void (*e) (int arg, const char * format, ...) __attribute__ ((__sentinel__ (1,1)));
@@ -44,16 +44,16 @@ void (*e) (int arg, const char * format, ...) __attribute__ ((__sentinel__ (1,1)
void test2(void) {
void (*b) (int arg, const char * format, ...) __attribute__ ((__sentinel__)); // expected-note {{function has been explicitly marked sentinel here}}
void (*z) (int arg, const char * format, ...) __attribute__ ((__sentinel__ (2))); // expected-note {{function has been explicitly marked sentinel here}}
-
-
+
+
void (*y) (int arg, const char * format, ...) __attribute__ ((__sentinel__ (5))); // expected-note {{function has been explicitly marked sentinel here}}
-
+
b(1, "%s", (void*)0); // OK
b(1, "%s", 0); // expected-warning {{missing sentinel in function call}}
z(1, "%s",4 ,1,0); // expected-warning {{missing sentinel in function call}}
z(1, "%s", (void*)0, 1, 0); // OK
-
+
y(1, "%s", 1,2,3,4,5,6,7); // expected-warning {{missing sentinel in function call}}
-
+
y(1, "%s", (void*)0,3,4,5,6,7); // OK
}
diff --git a/clang/test/SemaCXX/attr-sentinel.cpp b/clang/test/SemaCXX/attr-sentinel.cpp
index 92c6e21444fa3..e11ac16fe6bcf 100644
--- a/clang/test/SemaCXX/attr-sentinel.cpp
+++ b/clang/test/SemaCXX/attr-sentinel.cpp
@@ -1,4 +1,5 @@
-// RUN: %clang_cc1 -fsyntax-only -verify %s
+// RUN: %clang_cc1 -fsyntax-only -verify=expected,cxx17 -std=c++17 %s
+// RUN: %clang_cc1 -fsyntax-only -verify=expected,cxx23 -std=c++23 %s
void f(int, ...) __attribute__((sentinel));
@@ -21,3 +22,88 @@ void class_test() {
s2->a(1,2,3); // expected-warning {{missing sentinel in function call}}
s(1,2,3); // expected-warning {{missing sentinel in function call}}
}
+
+namespace consistent_diganostic_test {
+struct Ty {
+ void baseline0(int, ...) __attribute__((sentinel)); // #BASELINE0_NOTE
+ void baseline1(int, ...) __attribute__((sentinel(1))); // #BASELINE1_NOTE
+ void baseline2(int, ...) __attribute__((sentinel(2))); // #BASELINE2_NOTE
+
+ template<typename T>
+ int&& foo(T&& val, ...) __attribute__((sentinel(1))); // #FOO_NOTE
+
+ template<class Self>
+ int&& bar(this Self&& self, ...) __attribute__((sentinel(1))); // #BAR_NOTE
+ // cxx17-error at -1 {{explicit object parameters are incompatible with C++ standards before C++2b}}
+};
+
+void test_baseline0() {
+ auto sty = Ty{};
+
+ sty.baseline0(1);
+ // expected-warning at -1 {{not enough variable arguments in 'baseline0' declaration to fit a sentinel}}
+ // expected-note@#BASELINE0_NOTE {{function has been explicitly marked sentinel here}}
+
+ sty.baseline0(1, 2);
+ // expected-warning at -1 {{missing sentinel in function call}}
+ // expected-note@#BASELINE0_NOTE {{function has been explicitly marked sentinel here}}
+
+ sty.baseline0(1, 2, nullptr);
+}
+
+void test_baseline1() {
+ auto sty = Ty{};
+
+ sty.baseline1(1, 3);
+ // expected-warning at -1 {{not enough variable arguments in 'baseline1' declaration to fit a sentinel}}
+ // expected-note@#BASELINE1_NOTE {{function has been explicitly marked sentinel here}}
+
+ sty.baseline1(1, 2, 3);
+ // expected-warning at -1 {{missing sentinel in function call}}
+ // expected-note@#BASELINE1_NOTE {{function has been explicitly marked sentinel here}}
+
+ sty.baseline1(1, 2, nullptr, 3);
+}
+
+void test_baseline2() {
+ auto sty = Ty{};
+
+ sty.baseline2(1, 2, 3);
+ // expected-warning at -1 {{not enough variable arguments in 'baseline2' declaration to fit a sentinel}}
+ // expected-note@#BASELINE2_NOTE {{function has been explicitly marked sentinel here}}
+
+ sty.baseline2(1, 2, 3, 4);
+ // expected-warning at -1 {{missing sentinel in function call}}
+ // expected-note@#BASELINE2_NOTE {{function has been explicitly marked sentinel here}}
+
+ sty.baseline2(1, 2, nullptr, 3, 4);
+}
+
+void test_foo() {
+ auto sty = Ty{};
+
+ sty.foo(1, 2);
+ // expected-warning at -1 {{not enough variable arguments in 'foo' declaration to fit a sentinel}}
+ // expected-note@#FOO_NOTE {{function has been explicitly marked sentinel here}}
+
+ sty.foo(1, 2, 3);
+ // expected-warning at -1 {{missing sentinel in function call}}
+ // expected-note@#FOO_NOTE {{function has been explicitly marked sentinel here}}
+
+ sty.foo(1, nullptr, 3);
+}
+
+void test_bar() {
+ auto sty = Ty{};
+
+ sty.bar(1, 2);
+ // expected-warning at -1 {{not enough variable arguments in 'bar' declaration to fit a sentinel}}
+ // expected-note@#BAR_NOTE {{function has been explicitly marked sentinel here}}
+
+ sty.bar(1, 2, 3);
+ // expected-warning at -1 {{missing sentinel in function call}}
+ // expected-note@#BAR_NOTE {{function has been explicitly marked sentinel here}}
+
+ sty.bar(1, nullptr, 3);
+}
+} // namespace consistent_diganostic_test
``````````
</details>
https://github.com/llvm/llvm-project/pull/208842
More information about the cfe-commits
mailing list