From: Michael Han Date: Mon, 7 Jan 2013 16:57:11 +0000 (+0000) Subject: Add fixit hints for misplaced C++11 attributes around class specifiers. X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=07fc1ba7553f2f5bf26984091197311decd9028e;p=clang Add fixit hints for misplaced C++11 attributes around class specifiers. Following r168626, in class declaration or definition, there are a combination of syntactic locations where C++11 attributes could appear, and among those the only valid location permitted by standard is between class-key and class-name. So for those attributes appear at wrong locations, fixit is used to move them to expected location and we recover by applying them to the class specifier. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@171757 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/Parse/Parser.h b/include/clang/Parse/Parser.h index e66ae854c4..17816492aa 100644 --- a/include/clang/Parse/Parser.h +++ b/include/clang/Parse/Parser.h @@ -2060,7 +2060,10 @@ private: AccessSpecifier AS, bool EnteringContext, DeclSpecContext DSC, ParsedAttributesWithRange &Attributes); - void ParseCXXMemberSpecification(SourceLocation StartLoc, unsigned TagType, + void ParseCXXMemberSpecification(SourceLocation StartLoc, + SourceLocation AttrFixitLoc, + ParsedAttributes &Attrs, + unsigned TagType, Decl *TagDecl); ExprResult ParseCXXMemberInitializer(Decl *D, bool IsFunction, SourceLocation &EqualLoc); diff --git a/lib/Parse/ParseDeclCXX.cpp b/lib/Parse/ParseDeclCXX.cpp index 5fa8240b68..26389832da 100644 --- a/lib/Parse/ParseDeclCXX.cpp +++ b/lib/Parse/ParseDeclCXX.cpp @@ -1106,6 +1106,10 @@ void Parser::ParseClassSpecifier(tok::TokenKind TagTokKind, // styles of attributes? MaybeParseCXX11Attributes(attrs); + // Source location used by FIXIT to insert misplaced + // C++11 attributes + SourceLocation AttrFixitLoc = Tok.getLocation(); + if (TagType == DeclSpec::TST_struct && !Tok.is(tok::identifier) && Tok.getIdentifierInfo() && @@ -1322,9 +1326,25 @@ void Parser::ParseClassSpecifier(tok::TokenKind TagTokKind, // Forbid misplaced attributes. In cases of a reference, we pass attributes // to caller to handle. - // FIXME: provide fix-it hints if we can. - if (TUK != Sema::TUK_Reference) - ProhibitAttributes(Attributes); + if (TUK != Sema::TUK_Reference) { + // If this is not a reference, then the only possible + // valid place for C++11 attributes to appear here + // is between class-key and class-name. If there are + // any attributes after class-name, we try a fixit to move + // them to the right place. + 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 misplaced attributes to the attribute list + // of the class so they can be applied on the class later. + attrs.takeAllFrom(Attributes); + } + } // If this is an elaborated type specifier, and we delayed // diagnostics before, just merge them into the current pool. @@ -1508,7 +1528,8 @@ void Parser::ParseClassSpecifier(tok::TokenKind TagTokKind, (getLangOpts().CPlusPlus && Tok.is(tok::colon)) || isCXX11FinalKeyword()); if (getLangOpts().CPlusPlus) - ParseCXXMemberSpecification(StartLoc, TagType, TagOrTempResult.get()); + ParseCXXMemberSpecification(StartLoc, AttrFixitLoc, attrs, TagType, + TagOrTempResult.get()); else ParseStructUnionBody(StartLoc, TagType, TagOrTempResult.get()); } @@ -2346,6 +2367,8 @@ ExprResult Parser::ParseCXXMemberInitializer(Decl *D, bool IsFunction, /// access-specifier ':' member-specification[opt] /// void Parser::ParseCXXMemberSpecification(SourceLocation RecordLoc, + SourceLocation AttrFixitLoc, + ParsedAttributes &Attrs, unsigned TagType, Decl *TagDecl) { assert((TagType == DeclSpec::TST_struct || TagType == DeclSpec::TST_interface || @@ -2414,10 +2437,24 @@ void Parser::ParseCXXMemberSpecification(SourceLocation RecordLoc, diag::ext_override_control_keyword) << "final"; } - // Forbid C++11 attributes that appear here. - ParsedAttributesWithRange Attrs(AttrFactory); - MaybeParseCXX11Attributes(Attrs); - ProhibitAttributes(Attrs); + // Parse any C++11 attributes after 'final' keyword. + // 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); + } } if (Tok.is(tok::colon)) { diff --git a/test/FixIt/fixit-cxx11-attributes.cpp b/test/FixIt/fixit-cxx11-attributes.cpp new file mode 100644 index 0000000000..7c8efcb8ec --- /dev/null +++ b/test/FixIt/fixit-cxx11-attributes.cpp @@ -0,0 +1,34 @@ +// RUN: %clang_cc1 -verify -std=c++11 %s +// RUN: cp %s %t +// RUN: not %clang_cc1 -x c++ -std=c++11 -fixit %t +// RUN: %clang_cc1 -Wall -pedantic -x c++ -std=c++11 %t +// RUN: %clang_cc1 -std=c++11 -fsyntax-only -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s + +namespace ClassSpecifier { + class [[]] [[]] + attr_after_class_name_decl [[]] [[]]; // expected-error {{an attribute list cannot appear here}} + // CHECK: fix-it:{{.*}}:{9:5-9:5} + // CHECK: fix-it:{{.*}}:{9:32-9:41} + + class [[]] [[]] + attr_after_class_name_definition [[]] [[]] [[]]{}; // expected-error {{an attribute list cannot appear here}} + // CHECK: fix-it:{{.*}}:{14:4-14:4} + // CHECK: fix-it:{{.*}}:{14:37-14:51} + + class base {}; + class [[]] [[]] final_class + alignas(float) [[]] final // expected-error {{an attribute list cannot appear here}} + alignas(float) [[]] [[]] alignas(float): base{}; // expected-error {{an attribute list cannot appear here}} + // CHECK: fix-it:{{.*}}:{19:19-19:19} + // CHECK: fix-it:{{.*}}:{20:5-20:25} + // CHECK: fix-it:{{.*}}:{19:19-19:19} + // CHECK: fix-it:{{.*}}:{21:5-21:44} + + class [[]] [[]] final_class_another + [[]] [[]] alignas(16) final // expected-error {{an attribute list cannot appear here}} + [[]] [[]] alignas(16) [[]]{}; // expected-error {{an attribute list cannot appear here}} + // CHECK: fix-it:{{.*}}:{27:19-27:19} + // CHECK: fix-it:{{.*}}:{28:5-28:27} + // CHECK: fix-it:{{.*}}:{27:19-27:19} + // CHECK: fix-it:{{.*}}:{29:5-29:31} +} diff --git a/test/Parser/cxx0x-attributes.cpp b/test/Parser/cxx0x-attributes.cpp index 90e73004f6..a1268a8118 100644 --- a/test/Parser/cxx0x-attributes.cpp +++ b/test/Parser/cxx0x-attributes.cpp @@ -64,7 +64,6 @@ class [[]] class_attr {}; union [[]] union_attr; // Checks attributes placed at wrong syntactic locations of class specifiers. -// FIXME: provide fix-it hint. class [[]] [[]] attr_after_class_name_decl [[]] [[]]; // expected-error {{an attribute list cannot appear here}}