PR15300: Support C++11 attributes on base-specifiers. We don't support any such

attributes yet, so just issue the appropriate diagnostics. Also generalize the
fixit for attributes-in-the-wrong-place code and reuse it here, if attributes
are placed after the access-specifier or 'virtual' in a base specifier.

llvm-svn: 175575
This commit is contained in:
Richard Smith 2013-02-19 23:47:15 +00:00
parent 9d6f7037ba
commit 4c96e99235
11 changed files with 129 additions and 53 deletions

View File

@ -1801,9 +1801,8 @@ def warn_cxx11_gnu_attribute_on_type : Warning<
def warn_unhandled_ms_attribute_ignored : Warning<
"__declspec attribute %0 is not supported">,
InGroup<IgnoredAttributes>;
def warn_attribute_invalid_on_stmt : Warning<
"attribute %0 cannot be specified on a statement">,
InGroup<IgnoredAttributes>;
def err_attribute_invalid_on_stmt : Error<
"%0 attribute cannot be applied to a statement">;
def warn_declspec_attribute_ignored : Warning<
"attribute %0 is ignored, place it after "
"\"%select{class|struct|union|interface|enum}1\" to apply attribute to "
@ -1905,6 +1904,8 @@ def err_attribute_can_be_applied_only_to_value_decl : Error<
"%0 attribute can only be applied to value declarations">;
def warn_attribute_not_on_decl : Warning<
"%0 attribute ignored when parsing type">, InGroup<IgnoredAttributes>;
def err_base_specifier_attribute : Error<
"%0 attribute cannot be applied to a base specifier">;
// Availability attribute
def warn_availability_unknown_platform : Warning<

View File

@ -1814,6 +1814,17 @@ private:
return DiagnoseProhibitedCXX11Attribute();
}
bool DiagnoseProhibitedCXX11Attribute();
void CheckMisplacedCXX11Attribute(ParsedAttributesWithRange &Attrs,
SourceLocation CorrectLocation) {
if (!getLangOpts().CPlusPlus11)
return;
if ((Tok.isNot(tok::l_square) || NextToken().isNot(tok::l_square)) &&
Tok.isNot(tok::kw_alignas))
return;
DiagnoseMisplacedCXX11Attribute(Attrs, CorrectLocation);
}
void DiagnoseMisplacedCXX11Attribute(ParsedAttributesWithRange &Attrs,
SourceLocation CorrectLocation);
void ProhibitAttributes(ParsedAttributesWithRange &attrs) {
if (!attrs.Range.isValid()) return;
@ -2059,7 +2070,7 @@ private:
ParsedAttributesWithRange &Attributes);
void ParseCXXMemberSpecification(SourceLocation StartLoc,
SourceLocation AttrFixitLoc,
ParsedAttributes &Attrs,
ParsedAttributesWithRange &Attrs,
unsigned TagType,
Decl *TagDecl);
ExprResult ParseCXXMemberInitializer(Decl *D, bool IsFunction,

View File

@ -4526,6 +4526,7 @@ public:
BaseResult ActOnBaseSpecifier(Decl *classdecl,
SourceRange SpecifierRange,
ParsedAttributes &Attrs,
bool Virtual, AccessSpecifier Access,
ParsedType basetype,
SourceLocation BaseLoc,

View File

@ -1144,6 +1144,25 @@ bool Parser::DiagnoseProhibitedCXX11Attribute() {
llvm_unreachable("All cases handled above.");
}
/// DiagnoseMisplacedCXX11Attribute - We have found the opening square brackets
/// of a C++11 attribute-specifier in a location where an attribute is not
/// permitted, but we know where the attributes ought to be written. Parse them
/// anyway, and provide a fixit moving them to the right place.
void Parser::DiagnoseMisplacedCXX11Attribute(ParsedAttributesWithRange &Attrs,
SourceLocation CorrectLocation) {
assert((Tok.is(tok::l_square) && NextToken().is(tok::l_square)) ||
Tok.is(tok::kw_alignas));
// Consume the attributes.
SourceLocation Loc = Tok.getLocation();
ParseCXX11Attributes(Attrs);
CharSourceRange AttrRange(SourceRange(Loc, Attrs.Range.getEnd()), true);
Diag(Loc, diag::err_attributes_not_allowed)
<< FixItHint::CreateInsertionFromRange(CorrectLocation, AttrRange)
<< FixItHint::CreateRemoval(AttrRange);
}
void Parser::DiagnoseProhibitedAttributes(ParsedAttributesWithRange &attrs) {
Diag(attrs.Range.getBegin(), diag::err_attributes_not_allowed)
<< attrs.Range;

View File

@ -802,15 +802,18 @@ void Parser::ParseUnderlyingTypeSpecifier(DeclSpec &DS) {
/// class. The result is either a type or null, depending on whether a type
/// name was found.
///
/// base-type-specifier: [C++ 10.1]
/// base-type-specifier: [C++11 class.derived]
/// class-or-decltype
/// class-or-decltype: [C++ 10.1]
/// class-or-decltype: [C++11 class.derived]
/// nested-name-specifier[opt] class-name
/// decltype-specifier
/// class-name: [C++ 9.1]
/// class-name: [C++ class.name]
/// identifier
/// simple-template-id
///
/// In C++98, instead of base-type-specifier, we have:
///
/// ::[opt] nested-name-specifier[opt] class-name
Parser::TypeResult Parser::ParseBaseTypeSpecifier(SourceLocation &BaseLoc,
SourceLocation &EndLocation) {
// Ignore attempts to use typename
@ -1636,26 +1639,33 @@ void Parser::ParseBaseClause(Decl *ClassDecl) {
/// 'public bar' and 'virtual private baz' are each base-specifiers.
///
/// base-specifier: [C++ class.derived]
/// ::[opt] nested-name-specifier[opt] class-name
/// 'virtual' access-specifier[opt] ::[opt] nested-name-specifier[opt]
/// base-type-specifier
/// access-specifier 'virtual'[opt] ::[opt] nested-name-specifier[opt]
/// base-type-specifier
/// attribute-specifier-seq[opt] base-type-specifier
/// attribute-specifier-seq[opt] 'virtual' access-specifier[opt]
/// base-type-specifier
/// attribute-specifier-seq[opt] access-specifier 'virtual'[opt]
/// base-type-specifier
Parser::BaseResult Parser::ParseBaseSpecifier(Decl *ClassDecl) {
bool IsVirtual = false;
SourceLocation StartLoc = Tok.getLocation();
ParsedAttributesWithRange Attributes(AttrFactory);
MaybeParseCXX11Attributes(Attributes);
// Parse the 'virtual' keyword.
if (Tok.is(tok::kw_virtual)) {
ConsumeToken();
IsVirtual = true;
}
CheckMisplacedCXX11Attribute(Attributes, StartLoc);
// Parse an (optional) access specifier.
AccessSpecifier Access = getAccessSpecifierIfPresent();
if (Access != AS_none)
ConsumeToken();
CheckMisplacedCXX11Attribute(Attributes, StartLoc);
// Parse the 'virtual' keyword (again!), in case it came after the
// access specifier.
if (Tok.is(tok::kw_virtual)) {
@ -1669,6 +1679,8 @@ Parser::BaseResult Parser::ParseBaseSpecifier(Decl *ClassDecl) {
IsVirtual = true;
}
CheckMisplacedCXX11Attribute(Attributes, StartLoc);
// Parse the class-name.
SourceLocation EndLocation;
SourceLocation BaseLoc;
@ -1688,8 +1700,9 @@ Parser::BaseResult Parser::ParseBaseSpecifier(Decl *ClassDecl) {
// Notify semantic analysis that we have parsed a complete
// base-specifier.
return Actions.ActOnBaseSpecifier(ClassDecl, Range, IsVirtual, Access,
BaseType.get(), BaseLoc, EllipsisLoc);
return Actions.ActOnBaseSpecifier(ClassDecl, Range, Attributes, IsVirtual,
Access, BaseType.get(), BaseLoc,
EllipsisLoc);
}
/// getAccessSpecifierIfPresent - Determine whether the next token is
@ -2384,7 +2397,7 @@ ExprResult Parser::ParseCXXMemberInitializer(Decl *D, bool IsFunction,
///
void Parser::ParseCXXMemberSpecification(SourceLocation RecordLoc,
SourceLocation AttrFixitLoc,
ParsedAttributes &Attrs,
ParsedAttributesWithRange &Attrs,
unsigned TagType, Decl *TagDecl) {
assert((TagType == DeclSpec::TST_struct ||
TagType == DeclSpec::TST_interface ||
@ -2457,20 +2470,7 @@ void Parser::ParseCXXMemberSpecification(SourceLocation RecordLoc,
// These attributes are not allowed to appear here,
// and the only possible place for them to appertain
// to the class would be between class-key and class-name.
ParsedAttributesWithRange Attributes(AttrFactory);
MaybeParseCXX11Attributes(Attributes);
SourceRange AttrRange = Attributes.Range;
if (AttrRange.isValid()) {
Diag(AttrRange.getBegin(), diag::err_attributes_not_allowed)
<< AttrRange
<< FixItHint::CreateInsertionFromRange(AttrFixitLoc,
CharSourceRange(AttrRange, true))
<< FixItHint::CreateRemoval(AttrRange);
// Recover by adding attributes to the attribute list of the class
// so they can be applied on the class later.
Attrs.takeAllFrom(Attributes);
}
CheckMisplacedCXX11Attribute(Attrs, AttrFixitLoc);
}
if (Tok.is(tok::colon)) {

View File

@ -1172,6 +1172,7 @@ Sema::CheckBaseSpecifier(CXXRecordDecl *Class,
/// 'public bar' and 'virtual private baz' are each base-specifiers.
BaseResult
Sema::ActOnBaseSpecifier(Decl *classdecl, SourceRange SpecifierRange,
ParsedAttributes &Attributes,
bool Virtual, AccessSpecifier Access,
ParsedType basetype, SourceLocation BaseLoc,
SourceLocation EllipsisLoc) {
@ -1183,6 +1184,22 @@ Sema::ActOnBaseSpecifier(Decl *classdecl, SourceRange SpecifierRange,
if (!Class)
return true;
// We do not support any C++11 attributes on base-specifiers yet.
// Diagnose any attributes we see.
if (!Attributes.empty()) {
for (AttributeList *Attr = Attributes.getList(); Attr;
Attr = Attr->getNext()) {
if (Attr->isInvalid() ||
Attr->getKind() == AttributeList::IgnoredAttribute)
continue;
Diag(Attr->getLoc(),
Attr->getKind() == AttributeList::UnknownAttribute
? diag::warn_unknown_attribute_ignored
: diag::err_base_specifier_attribute)
<< Attr->getName();
}
}
TypeSourceInfo *TInfo = 0;
GetTypeFromParser(basetype, &TInfo);

View File

@ -58,8 +58,8 @@ static Attr *ProcessStmtAttribute(Sema &S, Stmt *St, const AttributeList &A,
default:
// if we're here, then we parsed a known attribute, but didn't recognize
// it as a statement attribute => it is declaration attribute
S.Diag(A.getRange().getBegin(), diag::warn_attribute_invalid_on_stmt)
<< A.getName()->getName() << St->getLocStart();
S.Diag(A.getRange().getBegin(), diag::err_attribute_invalid_on_stmt)
<< A.getName() << St->getLocStart();
return 0;
}
}

View File

@ -32,3 +32,20 @@ namespace ClassSpecifier {
// CHECK: fix-it:{{.*}}:{27:19-27:19}
// CHECK: fix-it:{{.*}}:{29:5-29:31}
}
namespace BaseSpecifier {
struct base1 {};
struct base2 {};
class with_base_spec : public [[a]] // expected-error {{an attribute list cannot appear here}} expected-warning {{unknown}}
// CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:26-[[@LINE-1]]:26}:"[{{\[}}a]]"
// CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:33-[[@LINE-2]]:39}:""
virtual [[b]] base1, // expected-error {{an attribute list cannot appear here}} expected-warning {{unknown}}
// CHECK: fix-it:"{{.*}}":{[[@LINE-4]]:26-[[@LINE-4]]:26}:"[{{\[}}b]]"
// CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:34-[[@LINE-2]]:40}:""
virtual [[c]] // expected-error {{an attribute list cannot appear here}} expected-warning {{unknown}}
// CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:26-[[@LINE-1]]:26}:"[{{\[}}c]]"
// CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:34-[[@LINE-2]]:40}:""
public [[d]] base2 {}; // expected-error {{an attribute list cannot appear here}} expected-warning {{unknown}}
// CHECK: fix-it:"{{.*}}":{[[@LINE-4]]:26-[[@LINE-4]]:26}:"[{{\[}}d]]"
// CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:33-[[@LINE-2]]:39}:""
}

View File

@ -0,0 +1,10 @@
// RUN: %clang_cc1 -std=c++11 %s -verify
struct A {};
struct B : [[]] A {};
struct C : [[]] virtual A {};
struct D : [[]] public virtual A {};
struct E : public [[]] virtual A {}; // expected-error {{an attribute list cannot appear here}}
struct F : virtual [[]] public A {}; // expected-error {{an attribute list cannot appear here}}
struct G : [[noreturn]] A {}; // expected-error {{'noreturn' attribute cannot be applied to a base specifier}}
struct H : [[unknown::foobar]] A {}; // expected-warning {{unknown attribute 'foobar' ignored}}

View File

@ -27,11 +27,11 @@ void foo(int i) {
[[unknown_attribute]] return; // expected-warning {{unknown attribute 'unknown_attribute' ignored}}
alignas(8) ; // expected-warning {{attribute alignas cannot be specified on a statement}}
[[noreturn]] { } // expected-warning {{attribute noreturn cannot be specified on a statement}}
[[noreturn]] if (0) { } // expected-warning {{attribute noreturn cannot be specified on a statement}}
[[noreturn]] for (;;); // expected-warning {{attribute noreturn cannot be specified on a statement}}
[[noreturn]] do { // expected-warning {{attribute noreturn cannot be specified on a statement}}
alignas(8) ; // expected-error {{'alignas' attribute cannot be applied to a statement}}
[[noreturn]] { } // expected-error {{'noreturn' attribute cannot be applied to a statement}}
[[noreturn]] if (0) { } // expected-error {{'noreturn' attribute cannot be applied to a statement}}
[[noreturn]] for (;;); // expected-error {{'noreturn' attribute cannot be applied to a statement}}
[[noreturn]] do { // expected-error {{'noreturn' attribute cannot be applied to a statement}}
[[unavailable]] continue; // expected-warning {{unknown attribute 'unavailable' ignored}}
} while (0);
[[unknown_attributqqq]] while (0); // expected-warning {{unknown attribute 'unknown_attributqqq' ignored}}
@ -42,7 +42,7 @@ void foo(int i) {
[[unused]] switch (i) { // expected-warning {{unknown attribute 'unused' ignored}}
[[uuid]] case 0: // expected-warning {{unknown attribute 'uuid' ignored}}
[[visibility]] default: // expected-warning {{unknown attribute 'visibility' ignored}}
[[carries_dependency]] break; // expected-warning {{attribute carries_dependency cannot be specified on a statement}}
[[carries_dependency]] break; // expected-error {{'carries_dependency' attribute cannot be applied to a statement}}
}
[[fastcall]] goto there; // expected-warning {{unknown attribute 'fastcall' ignored}}
@ -54,26 +54,26 @@ void foo(int i) {
[[weakref]] return; // expected-warning {{unknown attribute 'weakref' ignored}}
[[carries_dependency]] ; // expected-warning {{attribute carries_dependency cannot be specified on a statement}}
[[carries_dependency]] { } // expected-warning {{attribute carries_dependency cannot be specified on a statement}}
[[carries_dependency]] if (0) { } // expected-warning {{attribute carries_dependency cannot be specified on a statement}}
[[carries_dependency]] for (;;); // expected-warning {{attribute carries_dependency cannot be specified on a statement}}
[[carries_dependency]] do { // expected-warning {{attribute carries_dependency cannot be specified on a statement}}
[[carries_dependency]] continue; // expected-warning {{attribute carries_dependency cannot be specified on a statement}} ignored}}
[[carries_dependency]] ; // expected-error {{'carries_dependency' attribute cannot be applied to a statement}}
[[carries_dependency]] { } // expected-error {{'carries_dependency' attribute cannot be applied to a statement}}
[[carries_dependency]] if (0) { } // expected-error {{'carries_dependency' attribute cannot be applied to a statement}}
[[carries_dependency]] for (;;); // expected-error {{'carries_dependency' attribute cannot be applied to a statement}}
[[carries_dependency]] do { // expected-error {{'carries_dependency' attribute cannot be applied to a statement}}
[[carries_dependency]] continue; // expected-error {{'carries_dependency' attribute cannot be applied to a statement}} ignored}}
} while (0);
[[carries_dependency]] while (0); // expected-warning {{attribute carries_dependency cannot be specified on a statement}}
[[carries_dependency]] while (0); // expected-error {{'carries_dependency' attribute cannot be applied to a statement}}
[[carries_dependency]] switch (i) { // expected-warning {{attribute carries_dependency cannot be specified on a statement}} ignored}}
[[carries_dependency]] case 0: // expected-warning {{attribute carries_dependency cannot be specified on a statement}}
[[carries_dependency]] default: // expected-warning {{attribute carries_dependency cannot be specified on a statement}}
[[carries_dependency]] break; // expected-warning {{attribute carries_dependency cannot be specified on a statement}}
[[carries_dependency]] switch (i) { // expected-error {{'carries_dependency' attribute cannot be applied to a statement}} ignored}}
[[carries_dependency]] case 0: // expected-error {{'carries_dependency' attribute cannot be applied to a statement}}
[[carries_dependency]] default: // expected-error {{'carries_dependency' attribute cannot be applied to a statement}}
[[carries_dependency]] break; // expected-error {{'carries_dependency' attribute cannot be applied to a statement}}
}
[[carries_dependency]] goto here; // expected-warning {{attribute carries_dependency cannot be specified on a statement}}
[[carries_dependency]] goto here; // expected-error {{'carries_dependency' attribute cannot be applied to a statement}}
[[carries_dependency]] try { // expected-warning {{attribute carries_dependency cannot be specified on a statement}}
[[carries_dependency]] try { // expected-error {{'carries_dependency' attribute cannot be applied to a statement}}
} catch (...) {
}
[[carries_dependency]] return; // expected-warning {{attribute carries_dependency cannot be specified on a statement}}
[[carries_dependency]] return; // expected-error {{'carries_dependency' attribute cannot be applied to a statement}}
}

View File

@ -32,11 +32,11 @@ void f(X *noreturn) {
// An attribute is OK.
[[]];
[[int(), noreturn]]; // expected-warning {{unknown attribute 'int' ignored}} \
// expected-warning {{attribute noreturn cannot be specified on a statement}}
// expected-error {{'noreturn' attribute cannot be applied to a statement}}
[[class, test(foo 'x' bar),,,]]; // expected-warning {{unknown attribute 'test' ignored}}\
// expected-warning {{unknown attribute 'class' ignored}}
[[bitand, noreturn]]; // expected-warning {{attribute noreturn cannot be specified on a statement}} \
[[bitand, noreturn]]; // expected-error {{'noreturn' attribute cannot be applied to a statement}} \
expected-warning {{unknown attribute 'bitand' ignored}}
// FIXME: Suppress vexing parse warning