[clang] [Clang][Parser] Reject unbalanced unknown attribute arguments (PR #220259)

via cfe-commits cfe-commits at lists.llvm.org
Wed Oct 21 05:54:43 PDT 2026


https://github.com/nehaGautam07 updated https://github.com/llvm/llvm-project/pull/220259

>From a68ba345063247506807acaf2990ba1fc4aa749e Mon Sep 17 00:00:00 2001
From: neharaj <neharajgautam07 at gmail.com>
Date: Sat, 5 Sep 2026 11:52:10 +0000
Subject: [PATCH 1/7] [Clang][Parser] Reject unbalanced unknown attribute
 arguments

---
 clang/include/clang/Parse/Parser.h               | 4 +++-
 clang/lib/Parse/ParseDeclCXX.cpp                 | 5 ++++-
 clang/lib/Parse/Parser.cpp                       | 6 ++++++
 clang/test/Parser/cxx11-unbalanced-attr-args.cpp | 7 +++++++
 4 files changed, 20 insertions(+), 2 deletions(-)
 create mode 100644 clang/test/Parser/cxx11-unbalanced-attr-args.cpp

diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h
index ae91153e34e3a..4a1b330fc995e 100644
--- a/clang/include/clang/Parse/Parser.h
+++ b/clang/include/clang/Parse/Parser.h
@@ -570,7 +570,9 @@ class Parser : public CodeCompletionHandler {
     StopAtSemi = 1 << 0, ///< Stop skipping at semicolon
     /// Stop skipping at specified token, but don't skip the token itself
     StopBeforeMatch = 1 << 1,
-    StopAtCodeCompletion = 1 << 2 ///< Stop at code completion
+    StopAtCodeCompletion = 1 << 2, ///< Stop at code completion
+    /// Do not swallow an unmatched ')', ']', or '}'.
+    StopAtUnbalanced = 1 << 3
   };
 
   friend constexpr SkipUntilFlags operator|(SkipUntilFlags L,
diff --git a/clang/lib/Parse/ParseDeclCXX.cpp b/clang/lib/Parse/ParseDeclCXX.cpp
index d701cbaa43bdd..0e67f83f76b3b 100644
--- a/clang/lib/Parse/ParseDeclCXX.cpp
+++ b/clang/lib/Parse/ParseDeclCXX.cpp
@@ -4581,7 +4581,10 @@ bool Parser::ParseCXX11AttributeArgs(
                     ScopeName, AttrName, getTargetInfo(), getLangOpts())) {
     // Eat the left paren, then skip to the ending right paren.
     ConsumeParen();
-    SkipUntil(tok::r_paren);
+    if (!SkipUntil(tok::r_paren, StopAtUnbalanced)) {
+      Diag(Tok, diag::err_expected) << tok::r_paren;
+      SkipUntil(tok::r_paren, StopAtSemi);
+    }
     return false;
   }
 
diff --git a/clang/lib/Parse/Parser.cpp b/clang/lib/Parse/Parser.cpp
index 84059a41238cc..c67bb8503735c 100644
--- a/clang/lib/Parse/Parser.cpp
+++ b/clang/lib/Parse/Parser.cpp
@@ -394,16 +394,22 @@ bool Parser::SkipUntil(ArrayRef<tok::TokenKind> Toks, SkipUntilFlags Flags) {
     case tok::r_paren:
       if (ParenCount && !isFirstTokenSkipped)
         return false;  // Matches something.
+      if (HasFlagsSet(Flags, StopAtUnbalanced))
+        return false;
       ConsumeParen();
       break;
     case tok::r_square:
       if (BracketCount && !isFirstTokenSkipped)
         return false;  // Matches something.
+      if (HasFlagsSet(Flags, StopAtUnbalanced))
+        return false;
       ConsumeBracket();
       break;
     case tok::r_brace:
       if (BraceCount && !isFirstTokenSkipped)
         return false;  // Matches something.
+      if (HasFlagsSet(Flags, StopAtUnbalanced))
+        return false;
       ConsumeBrace();
       break;
 
diff --git a/clang/test/Parser/cxx11-unbalanced-attr-args.cpp b/clang/test/Parser/cxx11-unbalanced-attr-args.cpp
new file mode 100644
index 0000000000000..ba132bca19c72
--- /dev/null
+++ b/clang/test/Parser/cxx11-unbalanced-attr-args.cpp
@@ -0,0 +1,7 @@
+// RUN: %clang_cc1 -fsyntax-only -verify -std=c++11 %s
+// RUN: %clang_cc1 -fsyntax-only -verify -x c -std=c23 %s
+
+[[X1(])]]; // expected-error {{expected ')'}} expected-warning {{unknown attribute 'X1' ignored}}
+[[X1(})]]; // expected-error {{expected ')'}} expected-warning {{unknown attribute 'X1' ignored}}
+[[X1]]; // expected-warning {{unknown attribute 'X1' ignored}}
+[[X1()]]; // expected-warning {{unknown attribute 'X1' ignored}}
\ No newline at end of file

>From 6756cc54b4e3e0ef9bc643d3847c9b89fb52fe1b Mon Sep 17 00:00:00 2001
From: neharaj <neharajgautam07 at gmail.com>
Date: Sun, 6 Sep 2026 11:25:33 +0000
Subject: [PATCH 2/7] Retrigger CI


>From f79dfafc594a56216daa053fbc65538a2c5682ee Mon Sep 17 00:00:00 2001
From: neharaj <neharajgautam07 at gmail.com>
Date: Thu, 10 Sep 2026 09:24:15 +0000
Subject: [PATCH 3/7] [Clang][Parser] Diagnose unbalanced attr args as a
 pedantic warning

---
 clang/include/clang/Basic/DiagnosticParseKinds.td |  2 ++
 clang/lib/Parse/ParseDeclCXX.cpp                  |  2 +-
 clang/test/Parser/cxx11-unbalanced-attr-args.cpp  | 14 +++++++-------
 3 files changed, 10 insertions(+), 8 deletions(-)

diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td
index a06d3c899453c..ba8d0636a17c1 100644
--- a/clang/include/clang/Basic/DiagnosticParseKinds.td
+++ b/clang/include/clang/Basic/DiagnosticParseKinds.td
@@ -826,6 +826,8 @@ def err_using_attribute_ns_conflict : Error<
   "attribute with scope specifier cannot follow default scope specifier">;
 def err_attributes_not_allowed : Error<"an attribute list cannot appear here">;
 def err_keyword_not_allowed : Error<"%0 cannot appear here">;
+def ext_unbalanced_attribute_args : ExtWarn<
+  "attribute argument list is not a balanced token sequence">, InGroup<Pedantic>;
 def ext_cxx11_attr_placement : ExtWarn<
   "ISO C++ does not allow %select{an attribute list|%0}1 to appear here">,
   InGroup<DiagGroup<"cxx-attribute-extension">>;
diff --git a/clang/lib/Parse/ParseDeclCXX.cpp b/clang/lib/Parse/ParseDeclCXX.cpp
index 0e67f83f76b3b..2b8b052721b8d 100644
--- a/clang/lib/Parse/ParseDeclCXX.cpp
+++ b/clang/lib/Parse/ParseDeclCXX.cpp
@@ -4582,7 +4582,7 @@ bool Parser::ParseCXX11AttributeArgs(
     // Eat the left paren, then skip to the ending right paren.
     ConsumeParen();
     if (!SkipUntil(tok::r_paren, StopAtUnbalanced)) {
-      Diag(Tok, diag::err_expected) << tok::r_paren;
+      Diag(Tok, diag::ext_unbalanced_attribute_args);
       SkipUntil(tok::r_paren, StopAtSemi);
     }
     return false;
diff --git a/clang/test/Parser/cxx11-unbalanced-attr-args.cpp b/clang/test/Parser/cxx11-unbalanced-attr-args.cpp
index ba132bca19c72..a24e1cf46cdde 100644
--- a/clang/test/Parser/cxx11-unbalanced-attr-args.cpp
+++ b/clang/test/Parser/cxx11-unbalanced-attr-args.cpp
@@ -1,7 +1,7 @@
-// RUN: %clang_cc1 -fsyntax-only -verify -std=c++11 %s
-// RUN: %clang_cc1 -fsyntax-only -verify -x c -std=c23 %s
-
-[[X1(])]]; // expected-error {{expected ')'}} expected-warning {{unknown attribute 'X1' ignored}}
-[[X1(})]]; // expected-error {{expected ')'}} expected-warning {{unknown attribute 'X1' ignored}}
-[[X1]]; // expected-warning {{unknown attribute 'X1' ignored}}
-[[X1()]]; // expected-warning {{unknown attribute 'X1' ignored}}
\ No newline at end of file
+// RUN: %clang_cc1 -fsyntax-only -verify -std=c++11 %s
+// RUN: %clang_cc1 -fsyntax-only -verify -x c -std=c23 %s
+
+[[X1(])]]; // expected-warning {{attribute argument list is not a balanced token sequence}} expected-warning {{unknown attribute 'X1' ignored}}
+[[X1(})]]; // expected-warning {{attribute argument list is not a balanced token sequence}} expected-warning {{unknown attribute 'X1' ignored}}
+[[X1]]; // expected-warning {{unknown attribute 'X1' ignored}}
+[[X1()]]; // expected-warning {{unknown attribute 'X1' ignored}}

>From bc93de7d3c8b2312099a42669129407bcccc66a5 Mon Sep 17 00:00:00 2001
From: neharaj <neharajgautam07 at gmail.com>
Date: Thu, 10 Sep 2026 12:08:49 +0000
Subject: [PATCH 4/7] [Clang][Parser] Put unbalanced attr-args warning in its
 own group

---
 clang/include/clang/Basic/DiagnosticParseKinds.td | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td
index ba8d0636a17c1..9b631b0056969 100644
--- a/clang/include/clang/Basic/DiagnosticParseKinds.td
+++ b/clang/include/clang/Basic/DiagnosticParseKinds.td
@@ -827,7 +827,8 @@ def err_using_attribute_ns_conflict : Error<
 def err_attributes_not_allowed : Error<"an attribute list cannot appear here">;
 def err_keyword_not_allowed : Error<"%0 cannot appear here">;
 def ext_unbalanced_attribute_args : ExtWarn<
-  "attribute argument list is not a balanced token sequence">, InGroup<Pedantic>;
+  "attribute argument list is not a balanced token sequence">,
+  InGroup<DiagGroup<"unbalanced-attribute-args">>;
 def ext_cxx11_attr_placement : ExtWarn<
   "ISO C++ does not allow %select{an attribute list|%0}1 to appear here">,
   InGroup<DiagGroup<"cxx-attribute-extension">>;

>From da966504a81e948fb4c395e1e01d892409f6ec92 Mon Sep 17 00:00:00 2001
From: neharaj <neharajgautam07 at gmail.com>
Date: Wed, 30 Sep 2026 06:18:21 +0000
Subject: [PATCH 5/7] clang: make unbalanced attribute args pedantic warning

---
 clang/include/clang/Basic/DiagnosticParseKinds.td |  2 +-
 clang/test/Parser/cxx11-unbalanced-attr-args.cpp  | 12 ++++++++----
 2 files changed, 9 insertions(+), 5 deletions(-)

diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td
index 9b631b0056969..48c03e6b16468 100644
--- a/clang/include/clang/Basic/DiagnosticParseKinds.td
+++ b/clang/include/clang/Basic/DiagnosticParseKinds.td
@@ -826,7 +826,7 @@ def err_using_attribute_ns_conflict : Error<
   "attribute with scope specifier cannot follow default scope specifier">;
 def err_attributes_not_allowed : Error<"an attribute list cannot appear here">;
 def err_keyword_not_allowed : Error<"%0 cannot appear here">;
-def ext_unbalanced_attribute_args : ExtWarn<
+def ext_unbalanced_attribute_args : Extension<
   "attribute argument list is not a balanced token sequence">,
   InGroup<DiagGroup<"unbalanced-attribute-args">>;
 def ext_cxx11_attr_placement : ExtWarn<
diff --git a/clang/test/Parser/cxx11-unbalanced-attr-args.cpp b/clang/test/Parser/cxx11-unbalanced-attr-args.cpp
index a24e1cf46cdde..ec74c482fd6b2 100644
--- a/clang/test/Parser/cxx11-unbalanced-attr-args.cpp
+++ b/clang/test/Parser/cxx11-unbalanced-attr-args.cpp
@@ -1,7 +1,11 @@
 // RUN: %clang_cc1 -fsyntax-only -verify -std=c++11 %s
+// RUN: %clang_cc1 -fsyntax-only -verify=expected,pedantic -std=c++11 -pedantic %s
 // RUN: %clang_cc1 -fsyntax-only -verify -x c -std=c23 %s
+// RUN: %clang_cc1 -fsyntax-only -verify=expected,pedantic -x c -std=c23 -pedantic %s
 
-[[X1(])]]; // expected-warning {{attribute argument list is not a balanced token sequence}} expected-warning {{unknown attribute 'X1' ignored}}
-[[X1(})]]; // expected-warning {{attribute argument list is not a balanced token sequence}} expected-warning {{unknown attribute 'X1' ignored}}
-[[X1]]; // expected-warning {{unknown attribute 'X1' ignored}}
-[[X1()]]; // expected-warning {{unknown attribute 'X1' ignored}}
+void test() {
+  [[X1(])]]; // expected-warning {{unknown attribute 'X1' ignored}} pedantic-warning {{attribute argument list is not a balanced token sequence}}
+  [[X1(})]]; // expected-warning {{unknown attribute 'X1' ignored}} pedantic-warning {{attribute argument list is not a balanced token sequence}}
+  [[X1]];    // expected-warning {{unknown attribute 'X1' ignored}}
+  [[X1()]];  // expected-warning {{unknown attribute 'X1' ignored}}
+}

>From e04271fc2d8b8e7d1d0bda2e9214957e8a9fb519 Mon Sep 17 00:00:00 2001
From: neharaj <neharajgautam07 at gmail.com>
Date: Wed, 7 Oct 2026 11:32:22 +0000
Subject: [PATCH 6/7] [Clang][Parser] Add tests for unbalanced attribute
 arguments

---
 .../Parser/cxx11-unbalanced-attr-args.cpp     | 20 +++++++++++++++++++
 1 file changed, 20 insertions(+)

diff --git a/clang/test/Parser/cxx11-unbalanced-attr-args.cpp b/clang/test/Parser/cxx11-unbalanced-attr-args.cpp
index ec74c482fd6b2..09416a92b8382 100644
--- a/clang/test/Parser/cxx11-unbalanced-attr-args.cpp
+++ b/clang/test/Parser/cxx11-unbalanced-attr-args.cpp
@@ -9,3 +9,23 @@ void test() {
   [[X1]];    // expected-warning {{unknown attribute 'X1' ignored}}
   [[X1()]];  // expected-warning {{unknown attribute 'X1' ignored}}
 }
+
+void test1() {
+  [[X1())]]; // expected-error {{expected ','}} expected-warning {{unknown attribute 'X1' ignored}}
+}
+
+void test2() {
+  [[X1([)]]; // expected-error {{expected ']'}} pedantic-warning {{attribute argument list is not a balanced token sequence}}
+} // expected-error {{expected statement}}
+
+void test3() {
+  [[X1({)]]; // expected-error {{expected ']'}} pedantic-warning {{attribute argument list is not a balanced token sequence}}
+} // expected-error {{expected statement}}
+
+void test4() {
+  [[X1(()]]; // expected-error {{expected ']'}} pedantic-warning {{attribute argument list is not a balanced token sequence}}
+} // expected-error {{expected statement}}
+
+void test5() {
+  [[X1([[[[[]]]])]]; // expected-warning {{unknown attribute 'X1' ignored}}
+}

>From 3fd796ba1737d6555765a90373e29065bfe8ac1b Mon Sep 17 00:00:00 2001
From: neharaj <neharajgautam07 at gmail.com>
Date: Wed, 21 Oct 2026 12:54:12 +0000
Subject: [PATCH 7/7] [Clang][Parser] Count nested tokens when skipping unknown
 attribute arguments

---
 clang/lib/Parse/ParseDeclCXX.cpp              | 10 ++-
 clang/lib/Parse/Parser.cpp                    | 79 +++++++++++++++++--
 .../Parser/cxx11-unbalanced-attr-args.cpp     | 23 ++++--
 3 files changed, 96 insertions(+), 16 deletions(-)

diff --git a/clang/lib/Parse/ParseDeclCXX.cpp b/clang/lib/Parse/ParseDeclCXX.cpp
index 2b8b052721b8d..a32e5e28b06bc 100644
--- a/clang/lib/Parse/ParseDeclCXX.cpp
+++ b/clang/lib/Parse/ParseDeclCXX.cpp
@@ -4580,11 +4580,15 @@ bool Parser::ParseCXX11AttributeArgs(
                                  : AttributeCommonInfo::Syntax::AS_C23,
                     ScopeName, AttrName, getTargetInfo(), getLangOpts())) {
     // Eat the left paren, then skip to the ending right paren.
+    unsigned SavedParenCount = ParenCount;
+    unsigned SavedBracketCount = BracketCount;
+    unsigned SavedBraceCount = BraceCount;
     ConsumeParen();
-    if (!SkipUntil(tok::r_paren, StopAtUnbalanced)) {
+    if (!SkipUntil(tok::r_paren, StopAtUnbalanced))
       Diag(Tok, diag::ext_unbalanced_attribute_args);
-      SkipUntil(tok::r_paren, StopAtSemi);
-    }
+    ParenCount = SavedParenCount;
+    BracketCount = SavedBracketCount;
+    BraceCount = SavedBraceCount;
     return false;
   }
 
diff --git a/clang/lib/Parse/Parser.cpp b/clang/lib/Parse/Parser.cpp
index c67bb8503735c..123ba234e0847 100644
--- a/clang/lib/Parse/Parser.cpp
+++ b/clang/lib/Parse/Parser.cpp
@@ -292,6 +292,79 @@ static bool HasFlagsSet(Parser::SkipUntilFlags L, Parser::SkipUntilFlags R) {
 }
 
 bool Parser::SkipUntil(ArrayRef<tok::TokenKind> Toks, SkipUntilFlags Flags) {
+  // Unknown-attribute arguments only. The loop below is unchanged.
+if (HasFlagsSet(Flags, StopAtUnbalanced)) {
+  bool Unbalanced = false;
+  unsigned ParenDepth = 0, BracketDepth = 0, BraceDepth = 0;
+  while (true) {
+    if (Tok.is(tok::eof))
+      return false;
+
+    if (ParenDepth == 0 && BracketDepth == 0 && BraceDepth == 0) {
+      for (unsigned I = 0, E = Toks.size(); I != E; ++I) {
+        if (Tok.is(Toks[I])) {
+          if (!HasFlagsSet(Flags, StopBeforeMatch))
+            ConsumeAnyToken();
+          return !Unbalanced;
+        }
+      }
+    }
+
+    // ']]' closes the attribute. Do not treat it as a bracket we opened.
+    if (Tok.is(tok::r_square) && NextToken().is(tok::r_square) &&
+        BracketDepth <= 1)
+      return false;
+
+    switch (Tok.getKind()) {
+    case tok::l_paren:
+      ++ParenDepth;
+      ConsumeParen();
+      break;
+    case tok::l_square:
+      ++BracketDepth;
+      ConsumeBracket();
+      break;
+    case tok::l_brace:
+      ++BraceDepth;
+      ConsumeBrace();
+      break;
+    case tok::r_paren:
+      if (ParenDepth == 0) {
+        Unbalanced = true;
+        ConsumeParen();
+        break;
+      }
+      --ParenDepth;
+      ConsumeParen();
+      break;
+    case tok::r_square:
+      if (BracketDepth == 0) {
+        Unbalanced = true;
+        ConsumeBracket();
+        break;
+      }
+      --BracketDepth;
+      ConsumeBracket();
+      break;
+    case tok::r_brace:
+      if (BraceDepth == 0) {
+        Unbalanced = true;
+        ConsumeBrace();
+        break;
+      }
+      --BraceDepth;
+      ConsumeBrace();
+      break;
+    case tok::code_completion:
+      if (!HasFlagsSet(Flags, StopAtCodeCompletion))
+        handleUnexpectedCodeCompletionToken();
+      return false;
+    default:
+      ConsumeAnyToken();
+      break;
+    }
+  }
+}
   // We always want this function to skip at least one token if the first token
   // isn't T and if not at EOF.
   bool isFirstTokenSkipped = true;
@@ -394,22 +467,16 @@ bool Parser::SkipUntil(ArrayRef<tok::TokenKind> Toks, SkipUntilFlags Flags) {
     case tok::r_paren:
       if (ParenCount && !isFirstTokenSkipped)
         return false;  // Matches something.
-      if (HasFlagsSet(Flags, StopAtUnbalanced))
-        return false;
       ConsumeParen();
       break;
     case tok::r_square:
       if (BracketCount && !isFirstTokenSkipped)
         return false;  // Matches something.
-      if (HasFlagsSet(Flags, StopAtUnbalanced))
-        return false;
       ConsumeBracket();
       break;
     case tok::r_brace:
       if (BraceCount && !isFirstTokenSkipped)
         return false;  // Matches something.
-      if (HasFlagsSet(Flags, StopAtUnbalanced))
-        return false;
       ConsumeBrace();
       break;
 
diff --git a/clang/test/Parser/cxx11-unbalanced-attr-args.cpp b/clang/test/Parser/cxx11-unbalanced-attr-args.cpp
index 09416a92b8382..e4024cc88ccc4 100644
--- a/clang/test/Parser/cxx11-unbalanced-attr-args.cpp
+++ b/clang/test/Parser/cxx11-unbalanced-attr-args.cpp
@@ -8,6 +8,10 @@ void test() {
   [[X1(})]]; // expected-warning {{unknown attribute 'X1' ignored}} pedantic-warning {{attribute argument list is not a balanced token sequence}}
   [[X1]];    // expected-warning {{unknown attribute 'X1' ignored}}
   [[X1()]];  // expected-warning {{unknown attribute 'X1' ignored}}
+  [[X1([])]]; // expected-warning {{unknown attribute 'X1' ignored}}
+  [[X1({})]]; // expected-warning {{unknown attribute 'X1' ignored}}
+  [[X1(())]]; // expected-warning {{unknown attribute 'X1' ignored}}
+  [[X1(a;b)]]; // expected-warning {{unknown attribute 'X1' ignored}}
 }
 
 void test1() {
@@ -15,17 +19,22 @@ void test1() {
 }
 
 void test2() {
-  [[X1([)]]; // expected-error {{expected ']'}} pedantic-warning {{attribute argument list is not a balanced token sequence}}
-} // expected-error {{expected statement}}
+  [[X1([)]]; // expected-warning {{unknown attribute 'X1' ignored}} pedantic-warning {{attribute argument list is not a balanced token sequence}}
+}
 
 void test3() {
-  [[X1({)]]; // expected-error {{expected ']'}} pedantic-warning {{attribute argument list is not a balanced token sequence}}
-} // expected-error {{expected statement}}
+  [[X1({)]]; // expected-warning {{unknown attribute 'X1' ignored}} pedantic-warning {{attribute argument list is not a balanced token sequence}}
+}
 
 void test4() {
-  [[X1(()]]; // expected-error {{expected ']'}} pedantic-warning {{attribute argument list is not a balanced token sequence}}
-} // expected-error {{expected statement}}
+  [[X1(()]]; // expected-warning {{unknown attribute 'X1' ignored}} pedantic-warning {{attribute argument list is not a balanced token sequence}}
+}
 
 void test5() {
-  [[X1([[[[[]]]])]]; // expected-warning {{unknown attribute 'X1' ignored}}
+  [[X1([[[[[]]]])]]; // expected-warning {{unknown attribute 'X1' ignored}} pedantic-warning {{attribute argument list is not a balanced token sequence}}
 }
+
+void test6() {
+  [[X1(]a)]]; // expected-warning {{unknown attribute 'X1' ignored}} pedantic-warning {{attribute argument list is not a balanced token sequence}}
+  [[X1(];)]]; // expected-warning {{unknown attribute 'X1' ignored}} pedantic-warning {{attribute argument list is not a balanced token sequence}}
+}
\ No newline at end of file



More information about the cfe-commits mailing list