Index: clang/docs/LanguageExtensions.rst =================================================================== --- clang/docs/LanguageExtensions.rst +++ clang/docs/LanguageExtensions.rst @@ -619,6 +619,19 @@ Query for this feature with ``__has_extension(enumerator_attributes)``. +C++11 Attributes on using-declarations +================================ + +Clang allows C++-style ``[[]]`` attributes to be written on using-declarations. +For instance: + +.. code-block:: c++ + + [[clang::using_if_exists]] using foo::bar; + +You can test for support for this extension with +``__has_extension(cxx_attributes_on_using_declarations)``. + 'User-Specified' System Frameworks ================================== Index: clang/docs/ReleaseNotes.rst =================================================================== --- clang/docs/ReleaseNotes.rst +++ clang/docs/ReleaseNotes.rst @@ -130,6 +130,9 @@ ``[[unlikely]]``. As an extension they can be used in C++11 and newer. This extension is enabled by default. +- Added support for C++11-style ``[[]]`` attributes on using-declarations, as a + clang extension. + Windows Support --------------- Index: clang/include/clang/Basic/DiagnosticParseKinds.td =================================================================== --- clang/include/clang/Basic/DiagnosticParseKinds.td +++ clang/include/clang/Basic/DiagnosticParseKinds.td @@ -686,6 +686,9 @@ def err_using_attribute_ns_conflict : Error< "attribute with scope specifier cannot follow default scope specifier">; def err_attributes_not_allowed : Error<"an attribute list cannot appear here">; +def ext_cxx11_attr_placement : ExtWarn< + "ISO C++ does not allow an attribute list to appear here">, + InGroup>; def err_attributes_misplaced : Error<"misplaced attributes; expected attributes here">; def err_l_square_l_square_not_attribute : Error< "C++11 only allows consecutive left square brackets when " Index: clang/include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- clang/include/clang/Basic/DiagnosticSemaKinds.td +++ clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -3859,6 +3859,9 @@ def warn_attribute_sentinel_not_variadic : Warning< "'sentinel' attribute only supported for variadic %select{functions|blocks}0">, InGroup; +def warn_deprecated_ignored_on_using : Warning< + "%0 currently has no effect on using-declarations">, + InGroup; def err_attribute_sentinel_less_than_zero : Error< "'sentinel' parameter 1 less than zero">; def err_attribute_sentinel_not_zero_or_one : Error< Index: clang/include/clang/Basic/Features.def =================================================================== --- clang/include/clang/Basic/Features.def +++ clang/include/clang/Basic/Features.def @@ -255,6 +255,7 @@ EXTENSION(gnu_asm, LangOpts.GNUAsm) EXTENSION(gnu_asm_goto_with_outputs, LangOpts.GNUAsm) EXTENSION(matrix_types, LangOpts.MatrixTypes) +EXTENSION(cxx_attributes_on_using_declarations, LangOpts.CPlusPlus11) FEATURE(cxx_abi_relative_vtable, LangOpts.CPlusPlus && LangOpts.RelativeCXXABIVTables) Index: clang/include/clang/Parse/Parser.h =================================================================== --- clang/include/clang/Parse/Parser.h +++ clang/include/clang/Parse/Parser.h @@ -2644,6 +2644,10 @@ void ProhibitCXX11Attributes(ParsedAttributesWithRange &Attrs, unsigned DiagID); + /// Emit warnings for C++11 and C2x attributes that are in a position that + /// clang accepts as an extension. + void DiagnoseCXX11AttributeExtension(ParsedAttributesWithRange &Attrs); + /// Skip C++11 and C2x attributes and return the end location of the /// last one. /// \returns SourceLocation() if there are no attributes. @@ -2992,6 +2996,7 @@ const ParsedTemplateInfo &TemplateInfo, SourceLocation UsingLoc, SourceLocation &DeclEnd, + ParsedAttributesWithRange &Attrs, AccessSpecifier AS = AS_none); Decl *ParseAliasDeclarationAfterDeclarator( const ParsedTemplateInfo &TemplateInfo, SourceLocation UsingLoc, Index: clang/lib/Parse/ParseDecl.cpp =================================================================== --- clang/lib/Parse/ParseDecl.cpp +++ clang/lib/Parse/ParseDecl.cpp @@ -1602,6 +1602,13 @@ } } +void Parser::DiagnoseCXX11AttributeExtension(ParsedAttributesWithRange &Attrs) { + for (const ParsedAttr &PA : Attrs) { + if (PA.isCXX11Attribute() || PA.isC2xAttribute()) + Diag(PA.getLoc(), diag::ext_cxx11_attr_placement) << PA << PA.getRange(); + } +} + // Usually, `__attribute__((attrib)) class Foo {} var` means that attribute // applies to var, not the type Foo. // As an exception to the rule, __declspec(align(...)) before the Index: clang/lib/Parse/ParseDeclCXX.cpp =================================================================== --- clang/lib/Parse/ParseDeclCXX.cpp +++ clang/lib/Parse/ParseDeclCXX.cpp @@ -497,12 +497,8 @@ } // Otherwise, it must be a using-declaration or an alias-declaration. - - // Using declarations can't have attributes. - ProhibitAttributes(attrs); - return ParseUsingDeclaration(Context, TemplateInfo, UsingLoc, DeclEnd, - AS_none); + attrs, AS_none); } /// ParseUsingDirective - Parse C++ using-directive, assumes @@ -674,6 +670,7 @@ Parser::ParseUsingDeclaration(DeclaratorContext Context, const ParsedTemplateInfo &TemplateInfo, SourceLocation UsingLoc, SourceLocation &DeclEnd, + ParsedAttributesWithRange &PrefixAttrs, AccessSpecifier AS) { // Check for misplaced attributes before the identifier in an // alias-declaration. @@ -687,6 +684,17 @@ MaybeParseGNUAttributes(Attrs); MaybeParseCXX11Attributes(Attrs); + // If we had any misplaced attributes from earlier, this is where they + // should have been written. + if (MisplacedAttrs.Range.isValid()) { + Diag(MisplacedAttrs.Range.getBegin(), diag::err_attributes_not_allowed) + << FixItHint::CreateInsertionFromRange( + Tok.getLocation(), + CharSourceRange::getTokenRange(MisplacedAttrs.Range)) + << FixItHint::CreateRemoval(MisplacedAttrs.Range); + Attrs.takeAllFrom(MisplacedAttrs); + } + // Maybe this is an alias-declaration. if (Tok.is(tok::equal)) { if (InvalidDeclarator) { @@ -694,16 +702,7 @@ return nullptr; } - // If we had any misplaced attributes from earlier, this is where they - // should have been written. - if (MisplacedAttrs.Range.isValid()) { - Diag(MisplacedAttrs.Range.getBegin(), diag::err_attributes_not_allowed) - << FixItHint::CreateInsertionFromRange( - Tok.getLocation(), - CharSourceRange::getTokenRange(MisplacedAttrs.Range)) - << FixItHint::CreateRemoval(MisplacedAttrs.Range); - Attrs.takeAllFrom(MisplacedAttrs); - } + ProhibitAttributes(PrefixAttrs); Decl *DeclFromDeclSpec = nullptr; Decl *AD = ParseAliasDeclarationAfterDeclarator( @@ -711,10 +710,7 @@ return Actions.ConvertDeclToDeclGroup(AD, DeclFromDeclSpec); } - // C++11 attributes are not allowed on a using-declaration, but GNU ones - // are. - ProhibitAttributes(MisplacedAttrs); - ProhibitAttributes(Attrs); + DiagnoseCXX11AttributeExtension(PrefixAttrs); // Diagnose an attempt to declare a templated using-declaration. // In C++11, alias-declarations can be templates: @@ -732,8 +728,11 @@ SmallVector DeclsInGroup; while (true) { - // Parse (optional) attributes (most likely GNU strong-using extension). + // Parse (optional) attributes. MaybeParseGNUAttributes(Attrs); + MaybeParseCXX11Attributes(Attrs); + DiagnoseCXX11AttributeExtension(Attrs); + Attrs.addAll(PrefixAttrs.begin(), PrefixAttrs.end()); if (InvalidDeclarator) SkipUntil(tok::comma, tok::semi, StopBeforeMatch); @@ -2597,8 +2596,6 @@ MaybeParseMicrosoftAttributes(attrs); if (Tok.is(tok::kw_using)) { - ProhibitAttributes(attrs); - // Eat 'using'. SourceLocation UsingLoc = ConsumeToken(); @@ -2617,7 +2614,7 @@ SourceLocation DeclEnd; // Otherwise, it must be a using-declaration or an alias-declaration. return ParseUsingDeclaration(DeclaratorContext::Member, TemplateInfo, - UsingLoc, DeclEnd, AS); + UsingLoc, DeclEnd, attrs, AS); } // Hold late-parsed attributes so we can attach a Decl to them later. Index: clang/lib/Sema/SemaDeclAttr.cpp =================================================================== --- clang/lib/Sema/SemaDeclAttr.cpp +++ clang/lib/Sema/SemaDeclAttr.cpp @@ -2416,6 +2416,13 @@ } static void handleAvailabilityAttr(Sema &S, Decl *D, const ParsedAttr &AL) { + if (isa(D) || isa(D) || + isa(D)) { + S.Diag(AL.getRange().getBegin(), diag::warn_deprecated_ignored_on_using) + << AL; + return; + } + if (!checkAttributeNumArgs(S, AL, 1)) return; IdentifierLoc *Platform = AL.getArgAsIdent(0); @@ -6942,6 +6949,12 @@ // namespace. return; } + } else if (isa(D) || + isa(D) || + isa(D)) { + S.Diag(AL.getRange().getBegin(), diag::warn_deprecated_ignored_on_using) + << AL; + return; } // Handle the cases where the attribute has a text message. Index: clang/lib/Sema/SemaDeclCXX.cpp =================================================================== --- clang/lib/Sema/SemaDeclCXX.cpp +++ clang/lib/Sema/SemaDeclCXX.cpp @@ -12002,9 +12002,9 @@ return nullptr; DeclContext *LookupContext = computeDeclContext(SS); - NamedDecl *D; NestedNameSpecifierLoc QualifierLoc = SS.getWithLocInContext(Context); if (!LookupContext || EllipsisLoc.isValid()) { + NamedDecl *D; if (HasTypenameKeyword) { // FIXME: not all declaration name kinds are legal here D = UnresolvedUsingTypenameDecl::Create(Context, CurContext, @@ -12018,6 +12018,7 @@ } D->setAccess(AS); CurContext->addDecl(D); + ProcessDeclAttributeList(S, D, AttrList); return D; } @@ -12027,6 +12028,7 @@ UsingName, HasTypenameKeyword); UD->setAccess(AS); CurContext->addDecl(UD); + ProcessDeclAttributeList(S, UD, AttrList); UD->setInvalidDecl(Invalid); return UD; }; Index: clang/test/Parser/cxx0x-attributes.cpp =================================================================== --- clang/test/Parser/cxx0x-attributes.cpp +++ clang/test/Parser/cxx0x-attributes.cpp @@ -131,12 +131,12 @@ [[]] static_assert(true, ""); //expected-error {{an attribute list cannot appear here}} [[]] asm(""); // expected-error {{an attribute list cannot appear here}} -[[]] using ns::i; // expected-error {{an attribute list cannot appear here}} +[[]] using ns::i; [[unknown]] using namespace ns; // expected-warning {{unknown attribute 'unknown' ignored}} [[noreturn]] using namespace ns; // expected-error {{'noreturn' attribute only applies to functions}} namespace [[]] ns2 {} // expected-warning {{attributes on a namespace declaration are a C++17 extension}} -using [[]] alignas(4) [[]] ns::i; // expected-error {{an attribute list cannot appear here}} +using [[]] alignas(4) [[]] ns::i; // expected-error {{an attribute list cannot appear here}} expected-error {{'alignas' attribute only applies to variables, data members and tag types}} expected-warning {{ISO C++}} using [[]] alignas(4) [[]] foobar = int; // expected-error {{an attribute list cannot appear here}} expected-error {{'alignas' attribute only applies to}} void bad_attributes_in_do_while() { @@ -157,7 +157,16 @@ [[]] using T = int; // expected-error {{an attribute list cannot appear here}} using T [[]] = int; // ok template using U [[]] = T; -using ns::i [[]]; // expected-error {{an attribute list cannot appear here}} +using ns::i [[]]; +using ns::i [[]], ns::i [[]]; // expected-warning {{use of multiple declarators in a single using declaration is a C++17 extension}} +struct using_in_struct_base { + typedef int i, j, k, l; +}; +struct using_in_struct : using_in_struct_base { + [[]] using using_in_struct_base::i; + using using_in_struct_base::j [[]]; + [[]] using using_in_struct_base::k [[]], using_in_struct_base::l [[]]; // expected-warning {{use of multiple declarators in a single using declaration is a C++17 extension}} +}; using [[]] ns::i; // expected-error {{an attribute list cannot appear here}} using T [[unknown]] = int; // expected-warning {{unknown attribute 'unknown' ignored}} using T [[noreturn]] = int; // expected-error {{'noreturn' attribute only applies to functions}} Index: clang/test/SemaCXX/cxx11-attributes-on-using-declaration.cpp =================================================================== --- /dev/null +++ clang/test/SemaCXX/cxx11-attributes-on-using-declaration.cpp @@ -0,0 +1,27 @@ +// RUN: %clang_cc1 -pedantic -triple x86_64-apple-macos11 -std=c++20 -fsyntax-only -verify %s + +static_assert(__has_extension(cxx_attributes_on_using_declarations), ""); + +namespace NS { typedef int x; } + +[[clang::annotate("foo")]] using NS::x; // expected-warning{{ISO C++ does not allow an attribute list to appear here}} + + +[[deprecated]] using NS::x; // expected-warning {{'deprecated' currently has no effect on using-declarations}} expected-warning{{ISO C++ does not allow}} +using NS::x [[deprecated]]; // expected-warning {{'deprecated' currently has no effect on using-declarations}} expected-warning{{ISO C++ does not allow}} +using NS::x __attribute__((deprecated)); // expected-warning {{'deprecated' currently has no effect on using-declarations}} +using NS::x __attribute__((availability(macos,introduced=1))); // expected-warning {{'availability' currently has no effect on using-declarations}} + +[[clang::availability(macos,introduced=1)]] using NS::x; // expected-warning {{'availability' currently has no effect on using-declarations}} expected-warning{{ISO C++ does not allow}} + +// expected-warning@+1 3 {{ISO C++ does not allow an attribute list to appear here}} +[[clang::annotate("A")]] using NS::x [[clang::annotate("Y")]], NS::x [[clang::annotate("Z")]]; + +template +struct S : T { + [[deprecated]] using typename T::x; // expected-warning{{ISO C++ does not allow}} expected-warning {{'deprecated' currently has no effect on using-declarations}} + [[deprecated]] using T::y; // expected-warning{{ISO C++ does not allow}} expected-warning {{'deprecated' currently has no effect on using-declarations}} + + using typename T::z [[deprecated]]; // expected-warning{{ISO C++ does not allow}} expected-warning {{'deprecated' currently has no effect on using-declarations}} + using T::a [[deprecated]]; // expected-warning{{ISO C++ does not allow}} expected-warning {{'deprecated' currently has no effect on using-declarations}} +};