Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -707,6 +707,7 @@ Improvements to Clang's diagnostics
}

- Fix -Wdangling false positives on conditional operators (#120206).
- Clang now diagnoses unused private fields with the ``[[warn_unused]]`` attribute (#GH62472).

Improvements to Clang's time-trace
----------------------------------
Expand Down
43 changes: 26 additions & 17 deletions clang/lib/Sema/SemaDeclCXX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3307,6 +3307,29 @@ void Sema::CheckShadowInheritedFields(const SourceLocation &Loc,
}
}

template <typename AttrType>
inline static bool HasAttribute(const QualType &T) {
if (const TagDecl *TD = T->getAsTagDecl())
return TD->hasAttr<AttrType>();
if (const TypedefType *TDT = T->getAs<TypedefType>())
return TDT->getDecl()->hasAttr<AttrType>();
return false;
}

inline static bool IsUnusedPrivateField(FieldDecl *FD) {
if (FD->getAccess() == AS_private && FD->getDeclName()) {
QualType FieldType = FD->getType();
if (HasAttribute<WarnUnusedAttr>(FieldType))
return true;

return !FD->isImplicit() && !FD->hasAttr<UnusedAttr>() &&
!FD->getParent()->isDependentContext() &&
!HasAttribute<UnusedAttr>(FieldType) &&
!InitializationHasSideEffects(*FD);
}
return false;
}

NamedDecl *
Sema::ActOnCXXMemberDeclarator(Scope *S, AccessSpecifier AS, Declarator &D,
MultiTemplateParamsArg TemplateParameterLists,
Expand Down Expand Up @@ -3589,25 +3612,11 @@ Sema::ActOnCXXMemberDeclarator(Scope *S, AccessSpecifier AS, Declarator &D,
FieldDecl *FD = cast<FieldDecl>(Member);
FieldCollector->Add(FD);

if (!Diags.isIgnored(diag::warn_unused_private_field, FD->getLocation())) {
if (!Diags.isIgnored(diag::warn_unused_private_field, FD->getLocation()) &&
IsUnusedPrivateField(FD)) {
// Remember all explicit private FieldDecls that have a name, no side
// effects and are not part of a dependent type declaration.

auto DeclHasUnusedAttr = [](const QualType &T) {
if (const TagDecl *TD = T->getAsTagDecl())
return TD->hasAttr<UnusedAttr>();
if (const TypedefType *TDT = T->getAs<TypedefType>())
return TDT->getDecl()->hasAttr<UnusedAttr>();
return false;
};

if (!FD->isImplicit() && FD->getDeclName() &&
FD->getAccess() == AS_private &&
!FD->hasAttr<UnusedAttr>() &&
!FD->getParent()->isDependentContext() &&
!DeclHasUnusedAttr(FD->getType()) &&
!InitializationHasSideEffects(*FD))
UnusedPrivateFields.insert(FD);
UnusedPrivateFields.insert(FD);
}
}

Expand Down
13 changes: 13 additions & 0 deletions clang/test/SemaCXX/warn-unused-private-field.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -329,3 +329,16 @@ class C {
MaybeUnusedTypedef t; // no-warning
};
}

namespace GH62472 {
class [[gnu::warn_unused]] S {
public:
S();
};

class C {
private:
const int i = 0; // expected-warning {{private field 'i' is not used}}
const S s; // expected-warning {{private field 's' is not used}}
};
}
Loading