https://github.com/vgvassilev created 
https://github.com/llvm/llvm-project/pull/208909

AttributeCommonInfo::getAttributeSpellingListIndex() aborts for an unknown 
attribute that has a scope, such as [[ns::foo]]. It calls 
calculateAttributeSpellingListIndex(), which runs name/scope string-switches 
generated for the known attributes. Those switches intentionally have no 
default: getScopeFromNormalizedScopeName() falls off the end and hits 
llvm_unreachable ("Fell off the end of a string-switch") for the unknown scope.

Any caller that asks a scoped unknown attribute for its spelling index 
therefore crashes. An unrecognized attribute has no entry in those tables and 
carries a single no-spelling, so short-circuit 
calculateAttributeSpellingListIndex() for UnknownAttribute and return index 0 
before the switches run.

Add a unit test that constructs an unknown scoped AttributeCommonInfo and asks 
for its spelling index; it aborts without the fix.

>From 405c07fa60e8a16b7e6b92e19864901b183029dd Mon Sep 17 00:00:00 2001
From: Vassil Vassilev <[email protected]>
Date: Sat, 11 Jul 2026 12:38:09 +0000
Subject: [PATCH] [Basic] Fix crash computing the spelling index of an unknown
 scoped attribute

AttributeCommonInfo::getAttributeSpellingListIndex() aborts for an unknown
attribute that has a scope, such as [[ns::foo]]. It calls
calculateAttributeSpellingListIndex(), which runs name/scope string-switches
generated for the known attributes. Those switches intentionally have no
default: getScopeFromNormalizedScopeName() falls off the end and hits
llvm_unreachable ("Fell off the end of a string-switch") for the unknown scope.

Any caller that asks a scoped unknown attribute for its spelling index therefore
crashes. An unrecognized attribute has no entry in those tables and carries a
single no-spelling, so short-circuit calculateAttributeSpellingListIndex() for
UnknownAttribute and return index 0 before the switches run.

Add a unit test that constructs an unknown scoped AttributeCommonInfo and asks
for its spelling index; it aborts without the fix.
---
 clang/lib/Basic/Attributes.cpp           |  9 ++++++
 clang/unittests/Basic/AttributesTest.cpp | 39 ++++++++++++++++++++++++
 clang/unittests/Basic/CMakeLists.txt     |  1 +
 3 files changed, 49 insertions(+)
 create mode 100644 clang/unittests/Basic/AttributesTest.cpp

diff --git a/clang/lib/Basic/Attributes.cpp b/clang/lib/Basic/Attributes.cpp
index 5878a4e3f83a4..8ee45a338c57c 100644
--- a/clang/lib/Basic/Attributes.cpp
+++ b/clang/lib/Basic/Attributes.cpp
@@ -240,6 +240,15 @@ getScopeFromNormalizedScopeName(StringRef ScopeName) {
 }
 
 unsigned AttributeCommonInfo::calculateAttributeSpellingListIndex() const {
+  // An unknown attribute (getParsedKind() == UnknownAttribute) has no entry in
+  // the generated spelling tables. The name/scope StringSwitches below assume 
a
+  // known attribute and intentionally have no default, so computing an index
+  // for an unknown *scoped* attribute such as [[ns::foo]] would fall off the
+  // end (getScopeFromNormalizedScopeName aborts). It has a single no-spelling,
+  // so report index 0 instead.
+  if (getParsedKind() == AttributeCommonInfo::UnknownAttribute)
+    return 0;
+
   // Both variables will be used in tablegen generated
   // attribute spell list index matching code.
   auto Syntax = static_cast<AttributeCommonInfo::Syntax>(getSyntax());
diff --git a/clang/unittests/Basic/AttributesTest.cpp 
b/clang/unittests/Basic/AttributesTest.cpp
new file mode 100644
index 0000000000000..3968be012ad12
--- /dev/null
+++ b/clang/unittests/Basic/AttributesTest.cpp
@@ -0,0 +1,39 @@
+//===- unittests/Basic/AttributesTest.cpp - Attribute parsing tests 
-------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM 
Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Basic/AttributeCommonInfo.h"
+#include "clang/Basic/AttributeScopeInfo.h"
+#include "clang/Basic/IdentifierTable.h"
+#include "clang/Basic/LangOptions.h"
+#include "clang/Basic/SourceLocation.h"
+#include "gtest/gtest.h"
+
+using namespace clang;
+
+namespace {
+
+// getAttributeSpellingListIndex() must not crash for an unknown *scoped*
+// attribute such as [[ns::foo]]. Such an attribute has no entry in the
+// generated spelling tables, and the name/scope string-switches used to 
compute
+// the index have no default case; the computation must short-circuit for an
+// unknown attribute and report the single no-spelling instead of falling off
+// the end of a switch.
+TEST(AttributesTest, UnknownScopedAttributeSpellingIndex) {
+  LangOptions LangOpts;
+  IdentifierTable Idents(LangOpts);
+  const IdentifierInfo *Name = &Idents.get("foo");
+  const IdentifierInfo *Scope = &Idents.get("ns");
+
+  AttributeCommonInfo CI(Name, AttributeScopeInfo(Scope, SourceLocation()),
+                         SourceRange(), AttributeCommonInfo::Form::CXX11());
+
+  ASSERT_EQ(CI.getParsedKind(), AttributeCommonInfo::UnknownAttribute);
+  EXPECT_EQ(CI.getAttributeSpellingListIndex(), 0u);
+}
+
+} // namespace
diff --git a/clang/unittests/Basic/CMakeLists.txt 
b/clang/unittests/Basic/CMakeLists.txt
index 32dce09866892..d2f500c6c013e 100644
--- a/clang/unittests/Basic/CMakeLists.txt
+++ b/clang/unittests/Basic/CMakeLists.txt
@@ -2,6 +2,7 @@
 # distinct target enables faster iteration times at low cost.
 add_distinct_clang_unittest(BasicTests
   AtomicLineLoggerTest.cpp
+  AttributesTest.cpp
   CharInfoTest.cpp
   DarwinSDKInfoTest.cpp
   DiagnosticTest.cpp

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to