Skip to content

Commit 5ae3fdc

Browse files
committed
[clang] Fix missing diagnostic of declaration use when accessing TypeDecls through typename access
We were missing a call to DiagnoseUseOfDecl when performing typename access. This refactors the code so that TypeDecl lookups funnel through a helper which performs all the necessary checks, removing some related duplication on the way. When diagnosing availability uses through typedefs, this changes the implementation so it won't dig through subst nodes, as those uses should be diagnosed through the template specialization it was accessed from. This takes care of the regression reported here: #129681 (comment) Fixes #58547 Differential Revision: https://reviews.llvm.org/D136533
1 parent 754eeea commit 5ae3fdc

File tree

7 files changed

+92
-36
lines changed

7 files changed

+92
-36
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,9 @@ Potentially Breaking Changes
3535
============================
3636

3737
- The Objective-C ARC migrator (ARCMigrate) has been removed.
38+
- Fix missing diagnostics for uses of declarations when performing typename access,
39+
such as when performing member access on a '[[deprecated]]' type alias.
40+
(#GH58547)
3841

3942
C/C++ Language Potentially Breaking Changes
4043
-------------------------------------------

clang/include/clang/Sema/Sema.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3168,6 +3168,13 @@ class Sema final : public SemaBase {
31683168

31693169
DeclGroupPtrTy ConvertDeclToDeclGroup(Decl *Ptr, Decl *OwnedType = nullptr);
31703170

3171+
enum class DiagCtorKind { None, Implicit, Typename };
3172+
/// Returns the TypeDeclType for the given type declaration,
3173+
/// as ASTContext::getTypeDeclType would, but
3174+
/// performs the required semantic checks for name lookup of said entity.
3175+
QualType getTypeDeclType(DeclContext *LookupCtx, DiagCtorKind DCK,
3176+
TypeDecl *TD, SourceLocation NameLoc);
3177+
31713178
/// If the identifier refers to a type name within this scope,
31723179
/// return the declaration of that type.
31733180
///

clang/lib/Sema/SemaAvailability.cpp

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -99,16 +99,29 @@ ShouldDiagnoseAvailabilityOfDecl(Sema &S, const NamedDecl *D,
9999
// For typedefs, if the typedef declaration appears available look
100100
// to the underlying type to see if it is more restrictive.
101101
while (const auto *TD = dyn_cast<TypedefNameDecl>(D)) {
102-
if (Result == AR_Available) {
103-
if (const auto *TT = TD->getUnderlyingType()->getAs<TagType>()) {
102+
if (Result != AR_Available)
103+
break;
104+
for (const Type *T = TD->getUnderlyingType().getTypePtr(); /**/; /**/) {
105+
if (auto *TT = dyn_cast<TagType>(T)) {
104106
D = TT->getDecl();
105-
Result = D->getAvailability(Message);
107+
} else if (isa<SubstTemplateTypeParmType>(T)) {
108+
// A Subst* node represents a use through a template.
109+
// Any uses of the underlying declaration happened through it's template
110+
// specialization.
111+
goto done;
112+
} else {
113+
const Type *NextT =
114+
T->getLocallyUnqualifiedSingleStepDesugaredType().getTypePtr();
115+
if (NextT == T)
116+
goto done;
117+
T = NextT;
106118
continue;
107119
}
120+
Result = D->getAvailability(Message);
121+
break;
108122
}
109-
break;
110123
}
111-
124+
done:
112125
// For alias templates, get the underlying declaration.
113126
if (const auto *ADecl = dyn_cast<TypeAliasTemplateDecl>(D)) {
114127
D = ADecl->getTemplatedDecl();

clang/lib/Sema/SemaDecl.cpp

Lines changed: 29 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,26 @@ class TypeNameValidatorCCC final : public CorrectionCandidateCallback {
139139

140140
} // end anonymous namespace
141141

142+
QualType Sema::getTypeDeclType(DeclContext *LookupCtx, DiagCtorKind DCK,
143+
TypeDecl *TD, SourceLocation NameLoc) {
144+
auto *LookupRD = dyn_cast_or_null<CXXRecordDecl>(LookupCtx);
145+
auto *FoundRD = dyn_cast<CXXRecordDecl>(TD);
146+
if (DCK != DiagCtorKind::None && LookupRD && FoundRD &&
147+
FoundRD->isInjectedClassName() &&
148+
declaresSameEntity(LookupRD, cast<Decl>(FoundRD->getParent()))) {
149+
Diag(NameLoc,
150+
DCK == DiagCtorKind::Typename
151+
? diag::ext_out_of_line_qualified_id_type_names_constructor
152+
: diag::err_out_of_line_qualified_id_type_names_constructor)
153+
<< TD->getIdentifier() << /*Type=*/1
154+
<< 0 /*if any keyword was present, it was 'typename'*/;
155+
}
156+
157+
DiagnoseUseOfDecl(TD, NameLoc);
158+
MarkAnyDeclReferenced(TD->getLocation(), TD, /*OdrUse=*/false);
159+
return Context.getTypeDeclType(TD);
160+
}
161+
142162
namespace {
143163
enum class UnqualifiedTypeNameLookupResult {
144164
NotFound,
@@ -295,10 +315,11 @@ ParsedType Sema::getTypeName(const IdentifierInfo &II, SourceLocation NameLoc,
295315
bool IsClassTemplateDeductionContext,
296316
ImplicitTypenameContext AllowImplicitTypename,
297317
IdentifierInfo **CorrectedII) {
318+
bool IsImplicitTypename = !isClassName && !IsCtorOrDtorName;
298319
// FIXME: Consider allowing this outside C++1z mode as an extension.
299320
bool AllowDeducedTemplate = IsClassTemplateDeductionContext &&
300-
getLangOpts().CPlusPlus17 && !IsCtorOrDtorName &&
301-
!isClassName && !HasTrailingDot;
321+
getLangOpts().CPlusPlus17 && IsImplicitTypename &&
322+
!HasTrailingDot;
302323

303324
// Determine where we will perform name lookup.
304325
DeclContext *LookupCtx = nullptr;
@@ -322,11 +343,9 @@ ParsedType Sema::getTypeName(const IdentifierInfo &II, SourceLocation NameLoc,
322343
// refer to a member of an unknown specialization.
323344
// In C++2a, in several contexts a 'typename' is not required. Also
324345
// allow this as an extension.
325-
if (AllowImplicitTypename == ImplicitTypenameContext::No &&
326-
!isClassName && !IsCtorOrDtorName)
327-
return nullptr;
328-
bool IsImplicitTypename = !isClassName && !IsCtorOrDtorName;
329346
if (IsImplicitTypename) {
347+
if (AllowImplicitTypename == ImplicitTypenameContext::No)
348+
return nullptr;
330349
SourceLocation QualifiedLoc = SS->getRange().getBegin();
331350
if (getLangOpts().CPlusPlus20)
332351
Diag(QualifiedLoc, diag::warn_cxx17_compat_implicit_typename);
@@ -515,18 +534,10 @@ ParsedType Sema::getTypeName(const IdentifierInfo &II, SourceLocation NameLoc,
515534
// C++ [class.qual]p2: A lookup that would find the injected-class-name
516535
// instead names the constructors of the class, except when naming a class.
517536
// This is ill-formed when we're not actually forming a ctor or dtor name.
518-
auto *LookupRD = dyn_cast_or_null<CXXRecordDecl>(LookupCtx);
519-
auto *FoundRD = dyn_cast<CXXRecordDecl>(TD);
520-
if (!isClassName && !IsCtorOrDtorName && LookupRD && FoundRD &&
521-
FoundRD->isInjectedClassName() &&
522-
declaresSameEntity(LookupRD, cast<Decl>(FoundRD->getParent())))
523-
Diag(NameLoc, diag::err_out_of_line_qualified_id_type_names_constructor)
524-
<< &II << /*Type*/1;
525-
526-
DiagnoseUseOfDecl(IIDecl, NameLoc);
527-
528-
T = Context.getTypeDeclType(TD);
529-
MarkAnyDeclReferenced(TD->getLocation(), TD, /*OdrUse=*/false);
537+
T = getTypeDeclType(LookupCtx,
538+
IsImplicitTypename ? DiagCtorKind::Implicit
539+
: DiagCtorKind::None,
540+
TD, NameLoc);
530541
} else if (ObjCInterfaceDecl *IDecl = dyn_cast<ObjCInterfaceDecl>(IIDecl)) {
531542
(void)DiagnoseUseOfDecl(IDecl, NameLoc);
532543
if (!HasTrailingDot)

clang/lib/Sema/SemaTemplate.cpp

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -10930,20 +10930,15 @@ Sema::CheckTypenameType(ElaboratedTypeKeyword Keyword,
1093010930
//
1093110931
// FIXME: That's not strictly true: mem-initializer-id lookup does not
1093210932
// ignore functions, but that appears to be an oversight.
10933-
auto *LookupRD = dyn_cast_or_null<CXXRecordDecl>(Ctx);
10934-
auto *FoundRD = dyn_cast<CXXRecordDecl>(Type);
10935-
if (Keyword == ElaboratedTypeKeyword::Typename && LookupRD && FoundRD &&
10936-
FoundRD->isInjectedClassName() &&
10937-
declaresSameEntity(LookupRD, cast<Decl>(FoundRD->getParent())))
10938-
Diag(IILoc, diag::ext_out_of_line_qualified_id_type_names_constructor)
10939-
<< &II << 1 << 0 /*'typename' keyword used*/;
10940-
10933+
QualType T = getTypeDeclType(Ctx,
10934+
Keyword == ElaboratedTypeKeyword::Typename
10935+
? DiagCtorKind::Typename
10936+
: DiagCtorKind::None,
10937+
Type, IILoc);
1094110938
// We found a type. Build an ElaboratedType, since the
1094210939
// typename-specifier was just sugar.
10943-
MarkAnyDeclReferenced(Type->getLocation(), Type, /*OdrUse=*/false);
10944-
return Context.getElaboratedType(Keyword,
10945-
QualifierLoc.getNestedNameSpecifier(),
10946-
Context.getTypeDeclType(Type));
10940+
return Context.getElaboratedType(
10941+
Keyword, QualifierLoc.getNestedNameSpecifier(), T);
1094710942
}
1094810943

1094910944
// C++ [dcl.type.simple]p2:

clang/test/CXX/dcl.dcl/dcl.attr/dcl.attr.deprecated/p1.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,3 +58,17 @@ template <typename T>
5858
FunS2 f;// No warning, entire function is deprecated, so usage here should be fine.
5959

6060
}
61+
62+
namespace GH58547 {
63+
struct A {
64+
using ta [[deprecated]] = int; // expected-note 2{{marked deprecated here}}
65+
};
66+
67+
using t1 = typename A::ta; // expected-warning {{'ta' is deprecated}}
68+
69+
template <class B1> struct B {
70+
using tb = typename B1::ta; // expected-warning {{'ta' is deprecated}}
71+
};
72+
73+
template struct B<A>; // expected-note {{requested here}}
74+
} // namespace GH58547

clang/test/Sema/attr-availability-macosx.cpp

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,17 @@ bool try_acquire_for(T duration) { // expected-note{{'try_acquire_for<int>' has
1414
int main() {
1515
try_acquire_for(1); // expected-warning{{'try_acquire_for<int>' is only available on macOS 11 or newer}}
1616
// expected-note@-1{{enclose 'try_acquire_for<int>' in a __builtin_available check to silence this warning}}
17-
}
17+
}
18+
19+
namespace typename_template {
20+
struct [[clang::availability(macos, introduced = 16)]] A {};
21+
22+
template<class T> struct B { using type = T; };
23+
template <class T> struct C {
24+
typename B<T>::type v;
25+
};
26+
27+
struct [[clang::availability(macos, introduced = 16)]] D {
28+
C<A> c;
29+
};
30+
} // namespace typename_template

0 commit comments

Comments
 (0)