[clang] [clang][Sema] Fix false-positive -Wshadow for friend functions (PR #221249)

ahmed mohamed kamel via cfe-commits cfe-commits at lists.llvm.org
Fri Sep 11 06:57:21 PDT 2026


https://github.com/AhmedKamel10 updated https://github.com/llvm/llvm-project/pull/221249

>From 86319ac68e5acded16ec7fdbafc0e18e4d722be6 Mon Sep 17 00:00:00 2001
From: ahmedkamel10 <amkzaher at gmail.com>
Date: Fri, 4 Sep 2026 18:11:08 +0300
Subject: [PATCH 1/3] [clang][Sema] Fix false-positive -Wshadow for friend
 functions

A friend function defined inline in a class body has no implicit
'this', so a parameter with the same name as a field cannot actually
shadow it. CheckShadow only exempted static members and members with
an explicit object parameter (via a valid CXXMethodDecl cast); it
never handled the case where the enclosing function isn't a member
function at all, which silently fell through to the generic
diagnostic. Extend the exemption to cover that case.

Fixes #221190
---
 clang/docs/ReleaseNotes.md         |  2 ++
 clang/lib/Sema/SemaDecl.cpp        | 12 +++++-------
 clang/test/SemaCXX/warn-shadow.cpp | 11 +++++++++++
 3 files changed, 18 insertions(+), 7 deletions(-)

diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index 6a2201012693e..f03819ad9e97a 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -492,6 +492,8 @@ features cannot lower the translation-unit ABI level;
   `operator delete`, since such a delete expression never invokes the
   destructor. (#GH65524)
 
+- Fixed a false-positive `-Wshadow` warning when a function parameter in an inline-defined friend function shares the name of a non-static class member variable. (#GH221190)
+
 ### Improvements to Clang's time-trace
 
 ### Improvements to Coverage Mapping
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index a9047f61a8bf5..0c65539e796ab 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -8627,13 +8627,11 @@ void Sema::CheckShadow(NamedDecl *D, NamedDecl *ShadowedDecl,
   DeclContext *NewDC = D->getDeclContext();
 
   if (FieldDecl *FD = dyn_cast<FieldDecl>(ShadowedDecl)) {
-    if (const auto *MD =
-            dyn_cast<CXXMethodDecl>(getFunctionLevelDeclContext())) {
-      // Fields aren't shadowed in C++ static members or in member functions
-      // with an explicit object parameter.
-      if (MD->isStatic() || MD->isExplicitObjectMemberFunction())
-        return;
-    }
+    const auto *MD = dyn_cast<CXXMethodDecl>(getFunctionLevelDeclContext());
+    // Fields aren't shadowed in C++ static members or in member functions
+    // with an explicit object parameter.
+    if (!MD || MD->isStatic() || MD->isExplicitObjectMemberFunction())
+      return;
     // Fields shadowed by constructor parameters are a special case. Usually
     // the constructor initializes the field with the parameter.
     if (isa<CXXConstructorDecl>(NewDC))
diff --git a/clang/test/SemaCXX/warn-shadow.cpp b/clang/test/SemaCXX/warn-shadow.cpp
index 98a235a73c7e5..766766e465bdf 100644
--- a/clang/test/SemaCXX/warn-shadow.cpp
+++ b/clang/test/SemaCXX/warn-shadow.cpp
@@ -90,6 +90,17 @@ class A {
   }
 };
 
+class FriendFunction {
+  int x; // expected-note {{previous declaration is here}}
+
+  friend bool operator==(const FriendFunction &f, int x) {
+    return f.x == x;
+  }
+
+  void test(int x) { // expected-warning {{declaration shadows a field of 'FriendFunction'}}
+  }
+};
+
 struct path {
   using value_type = char;
   typedef char value_type2;

>From 6092cb2b4e92afc2afe4059a578d8aabe7cb2597 Mon Sep 17 00:00:00 2001
From: ahmedkamel10 <amkzaher at gmail.com>
Date: Fri, 11 Sep 2026 16:16:24 +0300
Subject: [PATCH 2/3] [clang][Sema] Distinguish friend functions from
 NSDMI-lambda context in -Wshadow

---
 clang/lib/Sema/SemaDecl.cpp        | 15 ++++++++++-----
 clang/test/SemaCXX/warn-shadow.cpp | 10 ++++++++--
 2 files changed, 18 insertions(+), 7 deletions(-)

diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 0c65539e796ab..80e358e9f1c33 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -8627,11 +8627,16 @@ void Sema::CheckShadow(NamedDecl *D, NamedDecl *ShadowedDecl,
   DeclContext *NewDC = D->getDeclContext();
 
   if (FieldDecl *FD = dyn_cast<FieldDecl>(ShadowedDecl)) {
-    const auto *MD = dyn_cast<CXXMethodDecl>(getFunctionLevelDeclContext());
-    // Fields aren't shadowed in C++ static members or in member functions
-    // with an explicit object parameter.
-    if (!MD || MD->isStatic() || MD->isExplicitObjectMemberFunction())
-      return;
+      DeclContext *FnDC = getFunctionLevelDeclContext();
+      if (const auto *MD = dyn_cast<CXXMethodDecl>(FnDC)) {
+        if (MD->isStatic() || MD->isExplicitObjectMemberFunction())
+          return;
+        
+      }
+      else if (isa<FunctionDecl>(FnDC)) {
+        return;
+      }
+
     // Fields shadowed by constructor parameters are a special case. Usually
     // the constructor initializes the field with the parameter.
     if (isa<CXXConstructorDecl>(NewDC))
diff --git a/clang/test/SemaCXX/warn-shadow.cpp b/clang/test/SemaCXX/warn-shadow.cpp
index 766766e465bdf..30dac1bb67dec 100644
--- a/clang/test/SemaCXX/warn-shadow.cpp
+++ b/clang/test/SemaCXX/warn-shadow.cpp
@@ -92,15 +92,21 @@ class A {
 
 class FriendFunction {
   int x; // expected-note {{previous declaration is here}}
-
   friend bool operator==(const FriendFunction &f, int x) {
     return f.x == x;
   }
-
   void test(int x) { // expected-warning {{declaration shadows a field of 'FriendFunction'}}
   }
 };
 
+struct NSDMILambda {
+  int a; // expected-note {{previous declaration is here}}
+  int x = [this] {
+    int a = 0; // expected-warning {{declaration shadows a field of 'NSDMILambda'}}
+    return a;
+  }();
+};
+
 struct path {
   using value_type = char;
   typedef char value_type2;

>From c064bc4c17d9155fb029dc3449a510b5263ca065 Mon Sep 17 00:00:00 2001
From: ahmedkamel10 <amkzaher at gmail.com>
Date: Fri, 11 Sep 2026 16:48:27 +0300
Subject: [PATCH 3/3] [clang][Sema] Distinguish friend functions from
 NSDMI-lambda context in -Wshadow

---
 clang/lib/Sema/SemaDecl.cpp        | 15 ++++++---------
 clang/test/SemaCXX/warn-shadow.cpp |  1 -
 2 files changed, 6 insertions(+), 10 deletions(-)

diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 80e358e9f1c33..3dc6f9a9770ce 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -8627,16 +8627,13 @@ void Sema::CheckShadow(NamedDecl *D, NamedDecl *ShadowedDecl,
   DeclContext *NewDC = D->getDeclContext();
 
   if (FieldDecl *FD = dyn_cast<FieldDecl>(ShadowedDecl)) {
-      DeclContext *FnDC = getFunctionLevelDeclContext();
-      if (const auto *MD = dyn_cast<CXXMethodDecl>(FnDC)) {
-        if (MD->isStatic() || MD->isExplicitObjectMemberFunction())
-          return;
-        
-      }
-      else if (isa<FunctionDecl>(FnDC)) {
+    DeclContext *FnDC = getFunctionLevelDeclContext();
+    if (const auto *MD = dyn_cast<CXXMethodDecl>(FnDC)) {
+      if (MD->isStatic() || MD->isExplicitObjectMemberFunction())
         return;
-      }
-
+    } else if (isa<FunctionDecl>(FnDC)) {
+      return;
+    }
     // Fields shadowed by constructor parameters are a special case. Usually
     // the constructor initializes the field with the parameter.
     if (isa<CXXConstructorDecl>(NewDC))
diff --git a/clang/test/SemaCXX/warn-shadow.cpp b/clang/test/SemaCXX/warn-shadow.cpp
index 30dac1bb67dec..fc184e5a1eb90 100644
--- a/clang/test/SemaCXX/warn-shadow.cpp
+++ b/clang/test/SemaCXX/warn-shadow.cpp
@@ -106,7 +106,6 @@ struct NSDMILambda {
     return a;
   }();
 };
-
 struct path {
   using value_type = char;
   typedef char value_type2;



More information about the cfe-commits mailing list