From 4c96e99235b32b594abaf93e5c16c81aae7acb3c Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Tue, 19 Feb 2013 23:47:15 +0000 Subject: [PATCH] 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 --- clang/include/clang/Basic/DiagnosticSemaKinds.td | 7 ++-- clang/include/clang/Parse/Parser.h | 13 +++++- clang/include/clang/Sema/Sema.h | 1 + clang/lib/Parse/ParseDecl.cpp | 19 +++++++++ clang/lib/Parse/ParseDeclCXX.cpp | 50 ++++++++++++------------ clang/lib/Sema/SemaDeclCXX.cpp | 17 ++++++++ clang/lib/Sema/SemaStmtAttr.cpp | 4 +- clang/test/FixIt/fixit-cxx11-attributes.cpp | 17 ++++++++ clang/test/Parser/cxx11-base-spec-attributes.cpp | 10 +++++ clang/test/Parser/cxx11-stmt-attributes.cpp | 40 +++++++++---------- clang/test/Parser/objcxx11-attributes.mm | 4 +- 11 files changed, 129 insertions(+), 53 deletions(-) create mode 100644 clang/test/Parser/cxx11-base-spec-attributes.cpp diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 44bcf63..2bacb2c 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -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; -def warn_attribute_invalid_on_stmt : Warning< - "attribute %0 cannot be specified on a statement">, - InGroup; +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; +def err_base_specifier_attribute : Error< + "%0 attribute cannot be applied to a base specifier">; // Availability attribute def warn_availability_unknown_platform : Warning< diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h index e5511a1..ff4d46e 100644 --- a/clang/include/clang/Parse/Parser.h +++ b/clang/include/clang/Parse/Parser.h @@ -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, diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index ef39bd2..07f035f 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -4526,6 +4526,7 @@ public: BaseResult ActOnBaseSpecifier(Decl *classdecl, SourceRange SpecifierRange, + ParsedAttributes &Attrs, bool Virtual, AccessSpecifier Access, ParsedType basetype, SourceLocation BaseLoc, diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp index 0dd1e0a..cc47ee1 100644 --- a/clang/lib/Parse/ParseDecl.cpp +++ b/clang/lib/Parse/ParseDecl.cpp @@ -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; diff --git a/clang/lib/Parse/ParseDeclCXX.cpp b/clang/lib/Parse/ParseDeclCXX.cpp index 1a1eeb9..4d428ce 100644 --- a/clang/lib/Parse/ParseDeclCXX.cpp +++ b/clang/lib/Parse/ParseDeclCXX.cpp @@ -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)) { diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index 1711c87..8b0ccd7 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -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); diff --git a/clang/lib/Sema/SemaStmtAttr.cpp b/clang/lib/Sema/SemaStmtAttr.cpp index b268b45..eb0188a0 100644 --- a/clang/lib/Sema/SemaStmtAttr.cpp +++ b/clang/lib/Sema/SemaStmtAttr.cpp @@ -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; } } diff --git a/clang/test/FixIt/fixit-cxx11-attributes.cpp b/clang/test/FixIt/fixit-cxx11-attributes.cpp index 7c8efcb..f28bdfc 100644 --- a/clang/test/FixIt/fixit-cxx11-attributes.cpp +++ b/clang/test/FixIt/fixit-cxx11-attributes.cpp @@ -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}:"" +} diff --git a/clang/test/Parser/cxx11-base-spec-attributes.cpp b/clang/test/Parser/cxx11-base-spec-attributes.cpp new file mode 100644 index 0000000..7338c51 --- /dev/null +++ b/clang/test/Parser/cxx11-base-spec-attributes.cpp @@ -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}} diff --git a/clang/test/Parser/cxx11-stmt-attributes.cpp b/clang/test/Parser/cxx11-stmt-attributes.cpp index a995847..2f727a2 100644 --- a/clang/test/Parser/cxx11-stmt-attributes.cpp +++ b/clang/test/Parser/cxx11-stmt-attributes.cpp @@ -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}} } diff --git a/clang/test/Parser/objcxx11-attributes.mm b/clang/test/Parser/objcxx11-attributes.mm index c72f26d..c1d8c41 100644 --- a/clang/test/Parser/objcxx11-attributes.mm +++ b/clang/test/Parser/objcxx11-attributes.mm @@ -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 -- 2.7.4