[clang][Parse] Add parsing support for C++ attributes on using-declarations

This is a re-application of dc67299 which was reverted in f63adf5b because
it broke the build. The issue should now be fixed.

Attribution note: The original author of this patch is Erik Pilkington.
I'm only trying to land it after rebasing.

Differential Revision: https://reviews.llvm.org/D91630
This commit is contained in:
Louis Dionne 2021-05-31 16:18:30 -04:00
parent 94b0aec0f5
commit 97d234935f
12 changed files with 130 additions and 34 deletions

View File

@ -632,6 +632,20 @@ Attributes on the ``enum`` declaration do not apply to individual enumerators.
Query for this feature with ``__has_extension(enumerator_attributes)``. 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;
using foo::baz [[clang::using_if_exists]];
You can test for support for this extension with
``__has_extension(cxx_attributes_on_using_declarations)``.
'User-Specified' System Frameworks 'User-Specified' System Frameworks
================================== ==================================

View File

@ -113,6 +113,9 @@ Attribute Changes in Clang
- ... - ...
- Added support for C++11-style ``[[]]`` attributes on using-declarations, as a
clang extension.
Windows Support Windows Support
--------------- ---------------

View File

@ -693,6 +693,9 @@ def ext_using_attribute_ns : ExtWarn<
def err_using_attribute_ns_conflict : Error< def err_using_attribute_ns_conflict : Error<
"attribute with scope specifier cannot follow default scope specifier">; "attribute with scope specifier cannot follow default scope specifier">;
def err_attributes_not_allowed : Error<"an attribute list cannot appear here">; 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<DiagGroup<"cxx-attribute-extension">>;
def err_attributes_misplaced : Error<"misplaced attributes; expected attributes here">; def err_attributes_misplaced : Error<"misplaced attributes; expected attributes here">;
def err_l_square_l_square_not_attribute : Error< def err_l_square_l_square_not_attribute : Error<
"C++11 only allows consecutive left square brackets when " "C++11 only allows consecutive left square brackets when "

View File

@ -3957,6 +3957,9 @@ def warn_attribute_sentinel_named_arguments : Warning<
def warn_attribute_sentinel_not_variadic : Warning< def warn_attribute_sentinel_not_variadic : Warning<
"'sentinel' attribute only supported for variadic %select{functions|blocks}0">, "'sentinel' attribute only supported for variadic %select{functions|blocks}0">,
InGroup<IgnoredAttributes>; InGroup<IgnoredAttributes>;
def warn_deprecated_ignored_on_using : Warning<
"%0 currently has no effect on a using declaration">,
InGroup<IgnoredAttributes>;
def err_attribute_sentinel_less_than_zero : Error< def err_attribute_sentinel_less_than_zero : Error<
"'sentinel' parameter 1 less than zero">; "'sentinel' parameter 1 less than zero">;
def err_attribute_sentinel_not_zero_or_one : Error< def err_attribute_sentinel_not_zero_or_one : Error<

View File

@ -259,6 +259,7 @@ EXTENSION(gnu_asm, LangOpts.GNUAsm)
EXTENSION(gnu_asm_goto_with_outputs, LangOpts.GNUAsm) EXTENSION(gnu_asm_goto_with_outputs, LangOpts.GNUAsm)
EXTENSION(matrix_types, LangOpts.MatrixTypes) EXTENSION(matrix_types, LangOpts.MatrixTypes)
EXTENSION(matrix_types_scalar_division, true) EXTENSION(matrix_types_scalar_division, true)
EXTENSION(cxx_attributes_on_using_declarations, LangOpts.CPlusPlus11)
FEATURE(cxx_abi_relative_vtable, LangOpts.CPlusPlus && LangOpts.RelativeCXXABIVTables) FEATURE(cxx_abi_relative_vtable, LangOpts.CPlusPlus && LangOpts.RelativeCXXABIVTables)

View File

@ -2635,6 +2635,10 @@ private:
/// locations where attributes are not allowed. /// locations where attributes are not allowed.
void DiagnoseAndSkipCXX11Attributes(); void DiagnoseAndSkipCXX11Attributes();
/// Emit warnings for C++11 and C2x attributes that are in a position that
/// clang accepts as an extension.
void DiagnoseCXX11AttributeExtension(ParsedAttributesWithRange &Attrs);
/// Parses syntax-generic attribute arguments for attributes which are /// Parses syntax-generic attribute arguments for attributes which are
/// known to the implementation, and adds them to the given ParsedAttributes /// known to the implementation, and adds them to the given ParsedAttributes
/// list with the given attribute syntax. Returns the number of arguments /// list with the given attribute syntax. Returns the number of arguments
@ -3061,6 +3065,7 @@ private:
const ParsedTemplateInfo &TemplateInfo, const ParsedTemplateInfo &TemplateInfo,
SourceLocation UsingLoc, SourceLocation UsingLoc,
SourceLocation &DeclEnd, SourceLocation &DeclEnd,
ParsedAttributesWithRange &Attrs,
AccessSpecifier AS = AS_none); AccessSpecifier AS = AS_none);
Decl *ParseAliasDeclarationAfterDeclarator( Decl *ParseAliasDeclarationAfterDeclarator(
const ParsedTemplateInfo &TemplateInfo, SourceLocation UsingLoc, const ParsedTemplateInfo &TemplateInfo, SourceLocation UsingLoc,

View File

@ -1650,6 +1650,13 @@ void Parser::ProhibitCXX11Attributes(ParsedAttributesWithRange &Attrs,
} }
} }
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 // Usually, `__attribute__((attrib)) class Foo {} var` means that attribute
// applies to var, not the type Foo. // applies to var, not the type Foo.
// As an exception to the rule, __declspec(align(...)) before the // As an exception to the rule, __declspec(align(...)) before the

View File

@ -497,11 +497,7 @@ Parser::ParseUsingDirectiveOrDeclaration(DeclaratorContext Context,
} }
// Otherwise, it must be a using-declaration or an alias-declaration. // Otherwise, it must be a using-declaration or an alias-declaration.
return ParseUsingDeclaration(Context, TemplateInfo, UsingLoc, DeclEnd, attrs,
// Using declarations can't have attributes.
ProhibitAttributes(attrs);
return ParseUsingDeclaration(Context, TemplateInfo, UsingLoc, DeclEnd,
AS_none); AS_none);
} }
@ -627,7 +623,8 @@ bool Parser::ParseUsingDeclarator(DeclaratorContext Context,
if (getLangOpts().CPlusPlus11 && Context == DeclaratorContext::Member && if (getLangOpts().CPlusPlus11 && Context == DeclaratorContext::Member &&
Tok.is(tok::identifier) && Tok.is(tok::identifier) &&
(NextToken().is(tok::semi) || NextToken().is(tok::comma) || (NextToken().is(tok::semi) || NextToken().is(tok::comma) ||
NextToken().is(tok::ellipsis)) && NextToken().is(tok::ellipsis) || NextToken().is(tok::l_square) ||
NextToken().is(tok::kw___attribute)) &&
D.SS.isNotEmpty() && LastII == Tok.getIdentifierInfo() && D.SS.isNotEmpty() && LastII == Tok.getIdentifierInfo() &&
!D.SS.getScopeRep()->getAsNamespace() && !D.SS.getScopeRep()->getAsNamespace() &&
!D.SS.getScopeRep()->getAsNamespaceAlias()) { !D.SS.getScopeRep()->getAsNamespaceAlias()) {
@ -670,11 +667,10 @@ bool Parser::ParseUsingDeclarator(DeclaratorContext Context,
/// alias-declaration: C++11 [dcl.dcl]p1 /// alias-declaration: C++11 [dcl.dcl]p1
/// 'using' identifier attribute-specifier-seq[opt] = type-id ; /// 'using' identifier attribute-specifier-seq[opt] = type-id ;
/// ///
Parser::DeclGroupPtrTy Parser::DeclGroupPtrTy Parser::ParseUsingDeclaration(
Parser::ParseUsingDeclaration(DeclaratorContext Context, DeclaratorContext Context, const ParsedTemplateInfo &TemplateInfo,
const ParsedTemplateInfo &TemplateInfo, SourceLocation UsingLoc, SourceLocation &DeclEnd,
SourceLocation UsingLoc, SourceLocation &DeclEnd, ParsedAttributesWithRange &PrefixAttrs, AccessSpecifier AS) {
AccessSpecifier AS) {
// Check for misplaced attributes before the identifier in an // Check for misplaced attributes before the identifier in an
// alias-declaration. // alias-declaration.
ParsedAttributesWithRange MisplacedAttrs(AttrFactory); ParsedAttributesWithRange MisplacedAttrs(AttrFactory);
@ -686,6 +682,17 @@ Parser::ParseUsingDeclaration(DeclaratorContext Context,
ParsedAttributesWithRange Attrs(AttrFactory); ParsedAttributesWithRange Attrs(AttrFactory);
MaybeParseAttributes(PAKM_GNU | PAKM_CXX11, Attrs); MaybeParseAttributes(PAKM_GNU | PAKM_CXX11, 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. // Maybe this is an alias-declaration.
if (Tok.is(tok::equal)) { if (Tok.is(tok::equal)) {
if (InvalidDeclarator) { if (InvalidDeclarator) {
@ -693,16 +700,7 @@ Parser::ParseUsingDeclaration(DeclaratorContext Context,
return nullptr; return nullptr;
} }
// If we had any misplaced attributes from earlier, this is where they ProhibitAttributes(PrefixAttrs);
// 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);
}
Decl *DeclFromDeclSpec = nullptr; Decl *DeclFromDeclSpec = nullptr;
Decl *AD = ParseAliasDeclarationAfterDeclarator( Decl *AD = ParseAliasDeclarationAfterDeclarator(
@ -710,10 +708,7 @@ Parser::ParseUsingDeclaration(DeclaratorContext Context,
return Actions.ConvertDeclToDeclGroup(AD, DeclFromDeclSpec); return Actions.ConvertDeclToDeclGroup(AD, DeclFromDeclSpec);
} }
// C++11 attributes are not allowed on a using-declaration, but GNU ones DiagnoseCXX11AttributeExtension(PrefixAttrs);
// are.
ProhibitAttributes(MisplacedAttrs);
ProhibitAttributes(Attrs);
// Diagnose an attempt to declare a templated using-declaration. // Diagnose an attempt to declare a templated using-declaration.
// In C++11, alias-declarations can be templates: // In C++11, alias-declarations can be templates:
@ -731,8 +726,10 @@ Parser::ParseUsingDeclaration(DeclaratorContext Context,
SmallVector<Decl *, 8> DeclsInGroup; SmallVector<Decl *, 8> DeclsInGroup;
while (true) { while (true) {
// Parse (optional) attributes (most likely GNU strong-using extension). // Parse (optional) attributes.
MaybeParseGNUAttributes(Attrs); MaybeParseAttributes(PAKM_GNU | PAKM_CXX11, Attrs);
DiagnoseCXX11AttributeExtension(Attrs);
Attrs.addAll(PrefixAttrs.begin(), PrefixAttrs.end());
if (InvalidDeclarator) if (InvalidDeclarator)
SkipUntil(tok::comma, tok::semi, StopBeforeMatch); SkipUntil(tok::comma, tok::semi, StopBeforeMatch);
@ -2639,8 +2636,6 @@ Parser::ParseCXXClassMemberDeclaration(AccessSpecifier AS,
MaybeParseMicrosoftAttributes(attrs); MaybeParseMicrosoftAttributes(attrs);
if (Tok.is(tok::kw_using)) { if (Tok.is(tok::kw_using)) {
ProhibitAttributes(attrs);
// Eat 'using'. // Eat 'using'.
SourceLocation UsingLoc = ConsumeToken(); SourceLocation UsingLoc = ConsumeToken();
@ -2659,7 +2654,7 @@ Parser::ParseCXXClassMemberDeclaration(AccessSpecifier AS,
SourceLocation DeclEnd; SourceLocation DeclEnd;
// Otherwise, it must be a using-declaration or an alias-declaration. // Otherwise, it must be a using-declaration or an alias-declaration.
return ParseUsingDeclaration(DeclaratorContext::Member, TemplateInfo, 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. // Hold late-parsed attributes so we can attach a Decl to them later.

View File

@ -2446,6 +2446,13 @@ AvailabilityAttr *Sema::mergeAvailabilityAttr(
} }
static void handleAvailabilityAttr(Sema &S, Decl *D, const ParsedAttr &AL) { static void handleAvailabilityAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
if (isa<UsingDecl, UnresolvedUsingTypenameDecl, UnresolvedUsingValueDecl>(
D)) {
S.Diag(AL.getRange().getBegin(), diag::warn_deprecated_ignored_on_using)
<< AL;
return;
}
if (!AL.checkExactlyNumArgs(S, 1)) if (!AL.checkExactlyNumArgs(S, 1))
return; return;
IdentifierLoc *Platform = AL.getArgAsIdent(0); IdentifierLoc *Platform = AL.getArgAsIdent(0);
@ -7257,6 +7264,11 @@ static void handleDeprecatedAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
// namespace. // namespace.
return; return;
} }
} else if (isa<UsingDecl, UnresolvedUsingTypenameDecl,
UnresolvedUsingValueDecl>(D)) {
S.Diag(AL.getRange().getBegin(), diag::warn_deprecated_ignored_on_using)
<< AL;
return;
} }
// Handle the cases where the attribute has a text message. // Handle the cases where the attribute has a text message.

View File

@ -12071,9 +12071,9 @@ NamedDecl *Sema::BuildUsingDeclaration(
return nullptr; return nullptr;
DeclContext *LookupContext = computeDeclContext(SS); DeclContext *LookupContext = computeDeclContext(SS);
NamedDecl *D;
NestedNameSpecifierLoc QualifierLoc = SS.getWithLocInContext(Context); NestedNameSpecifierLoc QualifierLoc = SS.getWithLocInContext(Context);
if (!LookupContext || EllipsisLoc.isValid()) { if (!LookupContext || EllipsisLoc.isValid()) {
NamedDecl *D;
if (HasTypenameKeyword) { if (HasTypenameKeyword) {
// FIXME: not all declaration name kinds are legal here // FIXME: not all declaration name kinds are legal here
D = UnresolvedUsingTypenameDecl::Create(Context, CurContext, D = UnresolvedUsingTypenameDecl::Create(Context, CurContext,
@ -12087,6 +12087,7 @@ NamedDecl *Sema::BuildUsingDeclaration(
} }
D->setAccess(AS); D->setAccess(AS);
CurContext->addDecl(D); CurContext->addDecl(D);
ProcessDeclAttributeList(S, D, AttrList);
return D; return D;
} }
@ -12096,6 +12097,7 @@ NamedDecl *Sema::BuildUsingDeclaration(
UsingName, HasTypenameKeyword); UsingName, HasTypenameKeyword);
UD->setAccess(AS); UD->setAccess(AS);
CurContext->addDecl(UD); CurContext->addDecl(UD);
ProcessDeclAttributeList(S, UD, AttrList);
UD->setInvalidDecl(Invalid); UD->setInvalidDecl(Invalid);
return UD; return UD;
}; };

View File

@ -131,12 +131,12 @@ extern "C++" [[]] { } // expected-error {{an attribute list cannot appear here}}
[[]] static_assert(true, ""); //expected-error {{an attribute list cannot appear here}} [[]] static_assert(true, ""); //expected-error {{an attribute list cannot appear here}}
[[]] asm(""); // 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}} [[unknown]] using namespace ns; // expected-warning {{unknown attribute 'unknown' ignored}}
[[noreturn]] using namespace ns; // expected-error {{'noreturn' attribute only applies to functions}} [[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}} 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}} 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() { void bad_attributes_in_do_while() {
@ -157,7 +157,16 @@ void bad_attributes_in_do_while() {
[[]] using T = int; // expected-error {{an attribute list cannot appear here}} [[]] using T = int; // expected-error {{an attribute list cannot appear here}}
using T [[]] = int; // ok using T [[]] = int; // ok
template<typename T> using U [[]] = T; template<typename T> 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 [[]] ns::i; // expected-error {{an attribute list cannot appear here}}
using T [[unknown]] = int; // expected-warning {{unknown attribute 'unknown' ignored}} using T [[unknown]] = int; // expected-warning {{unknown attribute 'unknown' ignored}}
using T [[noreturn]] = int; // expected-error {{'noreturn' attribute only applies to functions}} using T [[noreturn]] = int; // expected-error {{'noreturn' attribute only applies to functions}}

View File

@ -0,0 +1,42 @@
// 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 a using declaration}} expected-warning{{ISO C++ does not allow}}
using NS::x [[deprecated]]; // expected-warning {{'deprecated' currently has no effect on a using declaration}} expected-warning{{ISO C++ does not allow}}
using NS::x __attribute__((deprecated)); // expected-warning {{'deprecated' currently has no effect on a using declaration}}
using NS::x __attribute__((availability(macos,introduced=1))); // expected-warning {{'availability' currently has no effect on a using declaration}}
[[clang::availability(macos,introduced=1)]] using NS::x; // expected-warning {{'availability' currently has no effect on a using declaration}} 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 <class T>
struct S : T {
[[deprecated]] using typename T::x; // expected-warning{{ISO C++ does not allow}} expected-warning {{'deprecated' currently has no effect on a using declaration}}
[[deprecated]] using T::y; // expected-warning{{ISO C++ does not allow}} expected-warning {{'deprecated' currently has no effect on a using declaration}}
using typename T::z [[deprecated]]; // expected-warning{{ISO C++ does not allow}} expected-warning {{'deprecated' currently has no effect on a using declaration}}
using T::a [[deprecated]]; // expected-warning{{ISO C++ does not allow}} expected-warning {{'deprecated' currently has no effect on a using declaration}}
};
struct Base {};
template <class B>
struct DepBase1 : B {
using B::B [[]];
};
template <class B>
struct DepBase2 : B {
using B::B __attribute__(());
};
DepBase1<Base> db1;
DepBase2<Base> db2;