diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 191b4cc0ce07a..30fbdc9769c95 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -641,6 +641,20 @@ Improvements to Clang's diagnostics - Clang now diagnoses dangling references for C++20's parenthesized aggregate initialization (#101957). +- Fixed a bug where Clang would not emit ``-Wunused-private-field`` warnings when an unrelated class + defined a defaulted comparison operator (#GH116270). + + .. code-block:: c++ + + class A { + private: + int a; // warning: private field 'a' is not used, no diagnostic previously + }; + + class C { + bool operator==(const C&) = default; + }; + Improvements to Clang's time-trace ---------------------------------- diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index 7e8e321c4b90e..f7a0b3c059ec9 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -7535,8 +7535,15 @@ void Sema::CheckExplicitlyDefaultedFunction(Scope *S, FunctionDecl *FD) { return; } - if (DefKind.isComparison()) - UnusedPrivateFields.clear(); + if (DefKind.isComparison()) { + auto PT = FD->getParamDecl(0)->getType(); + if (const CXXRecordDecl *RD = + PT.getNonReferenceType()->getAsCXXRecordDecl()) { + for (FieldDecl *Field : RD->fields()) { + UnusedPrivateFields.remove(Field); + } + } + } if (DefKind.isSpecialMember() ? CheckExplicitlyDefaultedSpecialMember(cast(FD), diff --git a/clang/test/SemaCXX/warn-unused-private-field.cpp b/clang/test/SemaCXX/warn-unused-private-field.cpp index bf104b1a76a65..0cc6f687f1b35 100644 --- a/clang/test/SemaCXX/warn-unused-private-field.cpp +++ b/clang/test/SemaCXX/warn-unused-private-field.cpp @@ -40,6 +40,20 @@ class FriendEqDefaultCompareOutOfClass { bool operator==(const FriendEqDefaultCompareOutOfClass &, const FriendEqDefaultCompareOutOfClass &) = default; +class HasUnusedField { + int unused_; // expected-warning{{private field 'unused_' is not used}} +}; + +class FriendEqDefaultCompare { + int used; + friend auto operator==(FriendEqDefaultCompare, FriendEqDefaultCompare) -> bool = default; +}; + +class UnrelatedFriendEqDefaultCompare { + friend auto operator==(UnrelatedFriendEqDefaultCompare, UnrelatedFriendEqDefaultCompare) -> bool = default; + int operator<=>(const UnrelatedFriendEqDefaultCompare &) const = default; +}; + #endif class NotFullyDefined {