diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -255,6 +255,9 @@ - Reject non-type template arguments formed by casting a non-zero integer to a pointer in pre-C++17 modes, instead of treating them as null pointers. +- Fix missing diagnostics for uses of declarations when performing typename access, + such as when performing member access on a '[[deprecated]]' type alias. + `Issue 58547 ` Improvements to Clang's diagnostics ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -2566,6 +2566,10 @@ bool isSimpleTypeSpecifier(tok::TokenKind Kind) const; + enum class DiagCtorKind { None, Implicit, Typename }; + QualType getTypeDeclType(DeclContext *LookupCtx, DiagCtorKind DCK, + TypeDecl *TD, SourceLocation NameLoc); + ParsedType getTypeName(const IdentifierInfo &II, SourceLocation NameLoc, Scope *S, CXXScopeSpec *SS = nullptr, bool isClassName = false, bool HasTrailingDot = false, diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -169,6 +169,26 @@ return false; } +QualType Sema::getTypeDeclType(DeclContext *LookupCtx, DiagCtorKind DCK, + TypeDecl *TD, SourceLocation NameLoc) { + auto *LookupRD = dyn_cast_or_null(LookupCtx); + auto *FoundRD = dyn_cast(TD); + if (DCK != DiagCtorKind::None && LookupRD && FoundRD && + FoundRD->isInjectedClassName() && + declaresSameEntity(LookupRD, cast(FoundRD->getParent()))) { + Diag(NameLoc, + DCK == DiagCtorKind::Typename + ? diag::ext_out_of_line_qualified_id_type_names_constructor + : diag::err_out_of_line_qualified_id_type_names_constructor) + << TD->getIdentifier() << /*Type*/ 1 + << 0 /*if any keyword was present, it was 'typename'*/; + } + + DiagnoseUseOfDecl(TD, NameLoc); + MarkAnyDeclReferenced(TD->getLocation(), TD, /*OdrUse=*/false); + return Context.getTypeDeclType(TD); +} + namespace { enum class UnqualifiedTypeNameLookupResult { NotFound, @@ -332,10 +352,11 @@ bool IsClassTemplateDeductionContext, ImplicitTypenameContext AllowImplicitTypename, IdentifierInfo **CorrectedII) { + bool IsImplicitTypename = !isClassName && !IsCtorOrDtorName; // FIXME: Consider allowing this outside C++1z mode as an extension. bool AllowDeducedTemplate = IsClassTemplateDeductionContext && - getLangOpts().CPlusPlus17 && !IsCtorOrDtorName && - !isClassName && !HasTrailingDot; + getLangOpts().CPlusPlus17 && IsImplicitTypename && + !HasTrailingDot; // Determine where we will perform name lookup. DeclContext *LookupCtx = nullptr; @@ -359,11 +380,9 @@ // refer to a member of an unknown specialization. // In C++2a, in several contexts a 'typename' is not required. Also // allow this as an extension. - if (AllowImplicitTypename == ImplicitTypenameContext::No && - !isClassName && !IsCtorOrDtorName) - return nullptr; - bool IsImplicitTypename = !isClassName && !IsCtorOrDtorName; if (IsImplicitTypename) { + if (AllowImplicitTypename == ImplicitTypenameContext::No) + return nullptr; SourceLocation QualifiedLoc = SS->getRange().getBegin(); if (getLangOpts().CPlusPlus20) Diag(QualifiedLoc, diag::warn_cxx17_compat_implicit_typename); @@ -537,18 +556,10 @@ // C++ [class.qual]p2: A lookup that would find the injected-class-name // instead names the constructors of the class, except when naming a class. // This is ill-formed when we're not actually forming a ctor or dtor name. - auto *LookupRD = dyn_cast_or_null(LookupCtx); - auto *FoundRD = dyn_cast(TD); - if (!isClassName && !IsCtorOrDtorName && LookupRD && FoundRD && - FoundRD->isInjectedClassName() && - declaresSameEntity(LookupRD, cast(FoundRD->getParent()))) - Diag(NameLoc, diag::err_out_of_line_qualified_id_type_names_constructor) - << &II << /*Type*/1; - - DiagnoseUseOfDecl(IIDecl, NameLoc); - - T = Context.getTypeDeclType(TD); - MarkAnyDeclReferenced(TD->getLocation(), TD, /*OdrUse=*/false); + T = getTypeDeclType(LookupCtx, + IsImplicitTypename ? DiagCtorKind::Implicit + : DiagCtorKind::None, + TD, NameLoc); } else if (ObjCInterfaceDecl *IDecl = dyn_cast(IIDecl)) { (void)DiagnoseUseOfDecl(IDecl, NameLoc); if (!HasTrailingDot) diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -10963,20 +10963,14 @@ // // FIXME: That's not strictly true: mem-initializer-id lookup does not // ignore functions, but that appears to be an oversight. - auto *LookupRD = dyn_cast_or_null(Ctx); - auto *FoundRD = dyn_cast(Type); - if (Keyword == ETK_Typename && LookupRD && FoundRD && - FoundRD->isInjectedClassName() && - declaresSameEntity(LookupRD, cast(FoundRD->getParent()))) - Diag(IILoc, diag::ext_out_of_line_qualified_id_type_names_constructor) - << &II << 1 << 0 /*'typename' keyword used*/; - + QualType T = getTypeDeclType( + Ctx, + Keyword == ETK_Typename ? DiagCtorKind::Typename : DiagCtorKind::None, + Type, IILoc); // We found a type. Build an ElaboratedType, since the // typename-specifier was just sugar. - MarkAnyDeclReferenced(Type->getLocation(), Type, /*OdrUse=*/false); - return Context.getElaboratedType(Keyword, - QualifierLoc.getNestedNameSpecifier(), - Context.getTypeDeclType(Type)); + return Context.getElaboratedType( + Keyword, QualifierLoc.getNestedNameSpecifier(), T); } // C++ [dcl.type.simple]p2: diff --git a/clang/test/CXX/dcl.dcl/dcl.attr/dcl.attr.deprecated/p1.cpp b/clang/test/CXX/dcl.dcl/dcl.attr/dcl.attr.deprecated/p1.cpp --- a/clang/test/CXX/dcl.dcl/dcl.attr/dcl.attr.deprecated/p1.cpp +++ b/clang/test/CXX/dcl.dcl/dcl.attr/dcl.attr.deprecated/p1.cpp @@ -58,3 +58,17 @@ FunS2 f;// No warning, entire function is deprecated, so usage here should be fine. } + +namespace GH58547 { +struct A { + using ta [[deprecated]] = int; // expected-note 2{{marked deprecated here}} +}; + +using t1 = typename A::ta; // expected-warning {{'ta' is deprecated}} + +template struct B { + using tb = typename B1::ta; // expected-warning {{'ta' is deprecated}} +}; + +template struct B; // expected-note {{requested here}} +} // namespace GH58547 diff --git a/libcxx/test/libcxx/depr/depr.default.allocator/allocator.members/allocate.depr_in_cxx17.verify.cpp b/libcxx/test/libcxx/depr/depr.default.allocator/allocator.members/allocate.depr_in_cxx17.verify.cpp --- a/libcxx/test/libcxx/depr/depr.default.allocator/allocator.members/allocate.depr_in_cxx17.verify.cpp +++ b/libcxx/test/libcxx/depr/depr.default.allocator/allocator.members/allocate.depr_in_cxx17.verify.cpp @@ -13,7 +13,8 @@ // Deprecated in C++17 -// UNSUPPORTED: c++03, c++11, c++14 +// FIXME: Remove 'clang-16' from UNSUPPORTED by 2022-11-05 (bugfix D136533). +// UNSUPPORTED: c++03, c++11, c++14, clang-16 // ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_ENABLE_CXX20_REMOVED_ALLOCATOR_MEMBERS @@ -23,7 +24,11 @@ int main(int, char**) { std::allocator a; - TEST_IGNORE_NODISCARD a.allocate(3, nullptr); // expected-warning {{'allocate' is deprecated}} - + TEST_IGNORE_NODISCARD a.allocate(3, nullptr); + // expected-warning@-1 {{'allocate' is deprecated}} +#if defined(TEST_CLANG_VER) && TEST_CLANG_VER >= 1600 + // expected-warning@*:* {{'pointer' is deprecated}} + // expected-warning@*:* {{'const_pointer' is deprecated}} +#endif return 0; }