diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index b9986434d09d2..367c1ae2803e0 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -585,6 +585,8 @@ Improvements to Clang's diagnostics - For an rvalue reference bound to a temporary struct with an integer member, Clang will detect constant integer overflow in the initializer for the integer member (#GH46755). +- Fixed a false negative ``-Wunused-private-field`` diagnostic when a defaulted comparison operator is defined out of class (#GH116961). + Improvements to Clang's time-trace ---------------------------------- diff --git a/clang/lib/Sema/SemaExprMember.cpp b/clang/lib/Sema/SemaExprMember.cpp index 434768b99d631..85d5dfcb3db6d 100644 --- a/clang/lib/Sema/SemaExprMember.cpp +++ b/clang/lib/Sema/SemaExprMember.cpp @@ -1874,8 +1874,16 @@ Sema::BuildFieldReferenceExpr(Expr *BaseExpr, bool IsArrow, Context.getAttributedType(attr::NoDeref, MemberType, MemberType); } - auto *CurMethod = dyn_cast(CurContext); - if (!(CurMethod && CurMethod->isDefaulted())) + auto isDefaultedSpecialMember = [this](const DeclContext *Ctx) { + auto *Method = dyn_cast(CurContext); + if (!Method || !Method->isDefaulted()) + return false; + + return getDefaultedFunctionKind(Method).isSpecialMember(); + }; + + // Implicit special members should not mark fields as used. + if (!isDefaultedSpecialMember(CurContext)) UnusedPrivateFields.remove(Field); ExprResult Base = PerformObjectMemberConversion(BaseExpr, SS.getScopeRep(), diff --git a/clang/test/SemaCXX/warn-unused-private-field.cpp b/clang/test/SemaCXX/warn-unused-private-field.cpp index 1128eacc309d9..bf104b1a76a65 100644 --- a/clang/test/SemaCXX/warn-unused-private-field.cpp +++ b/clang/test/SemaCXX/warn-unused-private-field.cpp @@ -20,6 +20,26 @@ class SpaceShipDefaultCompare { int operator<=>(const SpaceShipDefaultCompare &) const = default; }; +class EqDefaultCompareOutOfClass { + int used; // no warning, the compiler generated AST for the comparison operator + // references the fields of the class, and this should be considered + // a use. + // This test case is needed because clang does not emit the body + // of the defaulted operator when it is defined in-class until it + // finds a call to it. `-Wunused-private-field` is suppressed in + // a different way in that case. + bool operator==(const EqDefaultCompareOutOfClass &) const; +}; + +bool EqDefaultCompareOutOfClass::operator==(const EqDefaultCompareOutOfClass &) const = default; + +class FriendEqDefaultCompareOutOfClass { + int used; // no warning, same reasoning just tested via a friend declaration. + friend bool operator==(const FriendEqDefaultCompareOutOfClass &, const FriendEqDefaultCompareOutOfClass &); +}; + +bool operator==(const FriendEqDefaultCompareOutOfClass &, const FriendEqDefaultCompareOutOfClass &) = default; + #endif class NotFullyDefined {