[clang] [Sema] Reject unqualified lookup of local nested class name (PR #192678)

via cfe-commits cfe-commits at lists.llvm.org
Fri Apr 17 08:04:30 PDT 2026


https://github.com/arrowten created https://github.com/llvm/llvm-project/pull/192678

Clang incorrectly accepts unqualified use of a nested class declared inside a local class, even when it is not visible in the current scope.

>From dafafa6e4109648dfec40e691be3ab315101b30b Mon Sep 17 00:00:00 2001
From: Ajay Wakodikar <ajaywakodikarsocial at gmail.com>
Date: Fri, 17 Apr 2026 10:48:20 -0400
Subject: [PATCH] [Sema] Reject unqualified lookup of local nested class name

---
 clang/lib/Sema/SemaLookup.cpp                 | 12 +++++
 .../Sema/unqualified-lookup-local-class.cpp   | 51 +++++++++++++++++++
 2 files changed, 63 insertions(+)
 create mode 100644 clang/test/Sema/unqualified-lookup-local-class.cpp

diff --git a/clang/lib/Sema/SemaLookup.cpp b/clang/lib/Sema/SemaLookup.cpp
index b96065f8619d2..21c3e479e2738 100644
--- a/clang/lib/Sema/SemaLookup.cpp
+++ b/clang/lib/Sema/SemaLookup.cpp
@@ -1369,6 +1369,18 @@ bool Sema::CppLookupName(LookupResult &R, Scope *S) {
           // namespace scope
           SearchNamespaceScope = false;
         }
+
+        if (R.getLookupKind() == LookupOrdinaryName) {
+          DeclContext *DC = ND->getDeclContext();
+
+          if (DC->isRecord()) {
+          // Only restrict when we're in function or block scope
+            if (S->getFnParent() && !S->isClassScope()) {
+              continue;
+            }
+          }
+        }
+
         R.addDecl(ND);
       }
     }
diff --git a/clang/test/Sema/unqualified-lookup-local-class.cpp b/clang/test/Sema/unqualified-lookup-local-class.cpp
new file mode 100644
index 0000000000000..6893425d4fb50
--- /dev/null
+++ b/clang/test/Sema/unqualified-lookup-local-class.cpp
@@ -0,0 +1,51 @@
+// RUN: %clang_cc1 -std=c++20 -verify %s
+
+struct A1 {
+  int x;
+  void f() { x = 1; } // OK
+};
+
+struct Base {
+  int y;
+};
+struct Derived : Base {
+  void f() { y = 2; } // OK
+};
+
+struct A2 { int z; };
+struct B2 : A2 {
+  using A2::z;
+  void f() { z = 3; } // OK
+};
+
+void pass1() {
+  struct A { struct B {}; };
+  A::B b; // OK
+}
+
+void pass2() {
+  class A { public: class B; };
+  class A::B {};
+  A::B b; // OK
+}
+
+void fail1() {
+  class A { public: class B; };
+  class A::B {};
+  B b; // expected-error {{'B'}}
+}
+
+template<class T>
+struct Wrapper { Wrapper(T) {} };
+
+void fail2() {
+  class A { public: class B; };
+  class A::B {};
+  Wrapper w = Wrapper{B{}}; // expected-error {{'B'}}
+}
+
+void fail3() {
+  struct A { public: struct B; };
+  struct A::B {};
+  B b; // expected-error {{'B'}}
+}



More information about the cfe-commits mailing list