Skip to content

Commit 5dab5bf

Browse files
authored
[Clang] handle [[warn_unused]] attribute for unused private fields (llvm#120734)
Fixes llvm#62472
1 parent 310f558 commit 5dab5bf

File tree

3 files changed

+58
-23
lines changed

3 files changed

+58
-23
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -825,6 +825,7 @@ Improvements to Clang's diagnostics
825825
}
826826

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

829830
- Fixed a bug where Clang hung on an unsupported optional scope specifier ``::`` when parsing
830831
Objective-C. Clang now emits a diagnostic message instead of hanging.

clang/lib/Sema/SemaDeclCXX.cpp

Lines changed: 26 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -3316,6 +3316,29 @@ void Sema::CheckShadowInheritedFields(const SourceLocation &Loc,
33163316
}
33173317
}
33183318

3319+
template <typename AttrType>
3320+
inline static bool HasAttribute(const QualType &T) {
3321+
if (const TagDecl *TD = T->getAsTagDecl())
3322+
return TD->hasAttr<AttrType>();
3323+
if (const TypedefType *TDT = T->getAs<TypedefType>())
3324+
return TDT->getDecl()->hasAttr<AttrType>();
3325+
return false;
3326+
}
3327+
3328+
static bool IsUnusedPrivateField(const FieldDecl *FD) {
3329+
if (FD->getAccess() == AS_private && FD->getDeclName()) {
3330+
QualType FieldType = FD->getType();
3331+
if (HasAttribute<WarnUnusedAttr>(FieldType))
3332+
return true;
3333+
3334+
return !FD->isImplicit() && !FD->hasAttr<UnusedAttr>() &&
3335+
!FD->getParent()->isDependentContext() &&
3336+
!HasAttribute<UnusedAttr>(FieldType) &&
3337+
!InitializationHasSideEffects(*FD);
3338+
}
3339+
return false;
3340+
}
3341+
33193342
NamedDecl *
33203343
Sema::ActOnCXXMemberDeclarator(Scope *S, AccessSpecifier AS, Declarator &D,
33213344
MultiTemplateParamsArg TemplateParameterLists,
@@ -3598,25 +3621,11 @@ Sema::ActOnCXXMemberDeclarator(Scope *S, AccessSpecifier AS, Declarator &D,
35983621
FieldDecl *FD = cast<FieldDecl>(Member);
35993622
FieldCollector->Add(FD);
36003623

3601-
if (!Diags.isIgnored(diag::warn_unused_private_field, FD->getLocation())) {
3624+
if (!Diags.isIgnored(diag::warn_unused_private_field, FD->getLocation()) &&
3625+
IsUnusedPrivateField(FD)) {
36023626
// Remember all explicit private FieldDecls that have a name, no side
36033627
// effects and are not part of a dependent type declaration.
3604-
3605-
auto DeclHasUnusedAttr = [](const QualType &T) {
3606-
if (const TagDecl *TD = T->getAsTagDecl())
3607-
return TD->hasAttr<UnusedAttr>();
3608-
if (const TypedefType *TDT = T->getAs<TypedefType>())
3609-
return TDT->getDecl()->hasAttr<UnusedAttr>();
3610-
return false;
3611-
};
3612-
3613-
if (!FD->isImplicit() && FD->getDeclName() &&
3614-
FD->getAccess() == AS_private &&
3615-
!FD->hasAttr<UnusedAttr>() &&
3616-
!FD->getParent()->isDependentContext() &&
3617-
!DeclHasUnusedAttr(FD->getType()) &&
3618-
!InitializationHasSideEffects(*FD))
3619-
UnusedPrivateFields.insert(FD);
3628+
UnusedPrivateFields.insert(FD);
36203629
}
36213630
}
36223631

clang/test/SemaCXX/warn-unused-private-field.cpp

Lines changed: 31 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
// RUN: %clang_cc1 -fsyntax-only -Wunused-private-field -Wused-but-marked-unused -Wno-uninitialized -verify -std=c++11 %s
2-
// RUN: %clang_cc1 -fsyntax-only -Wunused-private-field -Wused-but-marked-unused -Wno-uninitialized -verify -std=c++17 %s
3-
// RUN: %clang_cc1 -fsyntax-only -Wunused-private-field -Wused-but-marked-unused -Wno-uninitialized -verify -std=c++20 %s
1+
// RUN: %clang_cc1 -fsyntax-only -Wunused -Wused-but-marked-unused -Wno-uninitialized -verify -std=c++11 %s
2+
// RUN: %clang_cc1 -fsyntax-only -Wunused -Wused-but-marked-unused -Wno-uninitialized -verify -std=c++17 %s
3+
// RUN: %clang_cc1 -fsyntax-only -Wunused -Wused-but-marked-unused -Wno-uninitialized -verify -std=c++20 %s
44

55
#if __cplusplus >= 202002L
66

@@ -108,7 +108,7 @@ class ClassWithTemplateFriend {
108108
template <typename T> class TemplateFriend {
109109
public:
110110
TemplateFriend(ClassWithTemplateFriend my_friend) {
111-
int var = my_friend.used_by_friend_;
111+
int var = my_friend.used_by_friend_; // expected-warning {{unused variable 'var'}}
112112
}
113113
};
114114

@@ -181,10 +181,10 @@ class EverythingUsed {
181181
public:
182182
EverythingUsed() : as_array_index_(0), var_(by_initializer_) {
183183
var_ = sizeof(sizeof_);
184-
int *use = &by_reference_;
184+
int *use = &by_reference_; // expected-warning {{unused variable 'use'}}
185185
int test[2];
186186
test[as_array_index_] = 42;
187-
int EverythingUsed::*ptr = &EverythingUsed::by_pointer_to_member_;
187+
int EverythingUsed::*ptr = &EverythingUsed::by_pointer_to_member_; // expected-warning {{unused variable 'ptr'}}
188188
}
189189

190190
template<class T>
@@ -329,3 +329,28 @@ class C {
329329
MaybeUnusedTypedef t; // no-warning
330330
};
331331
}
332+
333+
namespace GH62472 {
334+
class [[gnu::warn_unused]] S {
335+
public:
336+
S();
337+
};
338+
339+
struct [[maybe_unused]] T {};
340+
341+
void f() {
342+
int i = 0; // expected-warning {{unused variable 'i'}}
343+
S s; // expected-warning {{unused variable 's'}}
344+
T t; // ok
345+
}
346+
347+
class C {
348+
private:
349+
const int i = 0; // expected-warning {{private field 'i' is not used}}
350+
int j = 0; // expected-warning {{private field 'j' is not used}}
351+
const S s1; // expected-warning {{private field 's1' is not used}}
352+
const T t1; // ok
353+
S s2; // expected-warning {{private field 's2' is not used}}
354+
T t2; // ok
355+
};
356+
}

0 commit comments

Comments
 (0)