Skip to content

Commit 4c4fd6b

Browse files
authored
[clang] Fix missing diagnostic of declaration use when accessing TypeDecls through typename access (#129681)
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. Fixes #58547 Differential Revision: https://reviews.llvm.org/D136533
1 parent 1d8eb43 commit 4c4fd6b

File tree

5 files changed

+60
-30
lines changed

5 files changed

+60
-30
lines changed

clang/docs/ReleaseNotes.rst

+3
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

+7
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/SemaDecl.cpp

+29-18
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,26 @@ class TypeNameValidatorCCC final : public CorrectionCandidateCallback {
137137

138138
} // end anonymous namespace
139139

140+
QualType Sema::getTypeDeclType(DeclContext *LookupCtx, DiagCtorKind DCK,
141+
TypeDecl *TD, SourceLocation NameLoc) {
142+
auto *LookupRD = dyn_cast_or_null<CXXRecordDecl>(LookupCtx);
143+
auto *FoundRD = dyn_cast<CXXRecordDecl>(TD);
144+
if (DCK != DiagCtorKind::None && LookupRD && FoundRD &&
145+
FoundRD->isInjectedClassName() &&
146+
declaresSameEntity(LookupRD, cast<Decl>(FoundRD->getParent()))) {
147+
Diag(NameLoc,
148+
DCK == DiagCtorKind::Typename
149+
? diag::ext_out_of_line_qualified_id_type_names_constructor
150+
: diag::err_out_of_line_qualified_id_type_names_constructor)
151+
<< TD->getIdentifier() << /*Type=*/1
152+
<< 0 /*if any keyword was present, it was 'typename'*/;
153+
}
154+
155+
DiagnoseUseOfDecl(TD, NameLoc);
156+
MarkAnyDeclReferenced(TD->getLocation(), TD, /*OdrUse=*/false);
157+
return Context.getTypeDeclType(TD);
158+
}
159+
140160
namespace {
141161
enum class UnqualifiedTypeNameLookupResult {
142162
NotFound,
@@ -293,10 +313,11 @@ ParsedType Sema::getTypeName(const IdentifierInfo &II, SourceLocation NameLoc,
293313
bool IsClassTemplateDeductionContext,
294314
ImplicitTypenameContext AllowImplicitTypename,
295315
IdentifierInfo **CorrectedII) {
316+
bool IsImplicitTypename = !isClassName && !IsCtorOrDtorName;
296317
// FIXME: Consider allowing this outside C++1z mode as an extension.
297318
bool AllowDeducedTemplate = IsClassTemplateDeductionContext &&
298-
getLangOpts().CPlusPlus17 && !IsCtorOrDtorName &&
299-
!isClassName && !HasTrailingDot;
319+
getLangOpts().CPlusPlus17 && IsImplicitTypename &&
320+
!HasTrailingDot;
300321

301322
// Determine where we will perform name lookup.
302323
DeclContext *LookupCtx = nullptr;
@@ -320,11 +341,9 @@ ParsedType Sema::getTypeName(const IdentifierInfo &II, SourceLocation NameLoc,
320341
// refer to a member of an unknown specialization.
321342
// In C++2a, in several contexts a 'typename' is not required. Also
322343
// allow this as an extension.
323-
if (AllowImplicitTypename == ImplicitTypenameContext::No &&
324-
!isClassName && !IsCtorOrDtorName)
325-
return nullptr;
326-
bool IsImplicitTypename = !isClassName && !IsCtorOrDtorName;
327344
if (IsImplicitTypename) {
345+
if (AllowImplicitTypename == ImplicitTypenameContext::No)
346+
return nullptr;
328347
SourceLocation QualifiedLoc = SS->getRange().getBegin();
329348
if (getLangOpts().CPlusPlus20)
330349
Diag(QualifiedLoc, diag::warn_cxx17_compat_implicit_typename);
@@ -513,18 +532,10 @@ ParsedType Sema::getTypeName(const IdentifierInfo &II, SourceLocation NameLoc,
513532
// C++ [class.qual]p2: A lookup that would find the injected-class-name
514533
// instead names the constructors of the class, except when naming a class.
515534
// This is ill-formed when we're not actually forming a ctor or dtor name.
516-
auto *LookupRD = dyn_cast_or_null<CXXRecordDecl>(LookupCtx);
517-
auto *FoundRD = dyn_cast<CXXRecordDecl>(TD);
518-
if (!isClassName && !IsCtorOrDtorName && LookupRD && FoundRD &&
519-
FoundRD->isInjectedClassName() &&
520-
declaresSameEntity(LookupRD, cast<Decl>(FoundRD->getParent())))
521-
Diag(NameLoc, diag::err_out_of_line_qualified_id_type_names_constructor)
522-
<< &II << /*Type*/1;
523-
524-
DiagnoseUseOfDecl(IIDecl, NameLoc);
525-
526-
T = Context.getTypeDeclType(TD);
527-
MarkAnyDeclReferenced(TD->getLocation(), TD, /*OdrUse=*/false);
535+
T = getTypeDeclType(LookupCtx,
536+
IsImplicitTypename ? DiagCtorKind::Implicit
537+
: DiagCtorKind::None,
538+
TD, NameLoc);
528539
} else if (ObjCInterfaceDecl *IDecl = dyn_cast<ObjCInterfaceDecl>(IIDecl)) {
529540
(void)DiagnoseUseOfDecl(IDecl, NameLoc);
530541
if (!HasTrailingDot)

clang/lib/Sema/SemaTemplate.cpp

+7-12
Original file line numberDiff line numberDiff line change
@@ -10914,20 +10914,15 @@ Sema::CheckTypenameType(ElaboratedTypeKeyword Keyword,
1091410914
//
1091510915
// FIXME: That's not strictly true: mem-initializer-id lookup does not
1091610916
// ignore functions, but that appears to be an oversight.
10917-
auto *LookupRD = dyn_cast_or_null<CXXRecordDecl>(Ctx);
10918-
auto *FoundRD = dyn_cast<CXXRecordDecl>(Type);
10919-
if (Keyword == ElaboratedTypeKeyword::Typename && LookupRD && FoundRD &&
10920-
FoundRD->isInjectedClassName() &&
10921-
declaresSameEntity(LookupRD, cast<Decl>(FoundRD->getParent())))
10922-
Diag(IILoc, diag::ext_out_of_line_qualified_id_type_names_constructor)
10923-
<< &II << 1 << 0 /*'typename' keyword used*/;
10924-
10917+
QualType T = getTypeDeclType(Ctx,
10918+
Keyword == ElaboratedTypeKeyword::Typename
10919+
? DiagCtorKind::Typename
10920+
: DiagCtorKind::None,
10921+
Type, IILoc);
1092510922
// We found a type. Build an ElaboratedType, since the
1092610923
// typename-specifier was just sugar.
10927-
MarkAnyDeclReferenced(Type->getLocation(), Type, /*OdrUse=*/false);
10928-
return Context.getElaboratedType(Keyword,
10929-
QualifierLoc.getNestedNameSpecifier(),
10930-
Context.getTypeDeclType(Type));
10924+
return Context.getElaboratedType(
10925+
Keyword, QualifierLoc.getNestedNameSpecifier(), T);
1093110926
}
1093210927

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

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

+14
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

0 commit comments

Comments
 (0)