diff options
-rw-r--r-- | clang/include/clang/Basic/Attr.td | 3 | ||||
-rw-r--r-- | clang/include/clang/Parse/Parser.h | 1 | ||||
-rw-r--r-- | clang/lib/Parse/ParseDeclCXX.cpp | 100 | ||||
-rw-r--r-- | clang/test/CodeGenCXX/microsoft-uuidof.cpp | 8 | ||||
-rw-r--r-- | clang/test/Parser/MicrosoftExtensions.cpp | 4 | ||||
-rw-r--r-- | clang/test/Parser/ms-square-bracket-attributes.mm | 145 | ||||
-rw-r--r-- | clang/test/Sema/MicrosoftExtensions.c | 2 |
7 files changed, 261 insertions, 2 deletions
diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td index 912d1515382..b72222fc5cf 100644 --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -198,6 +198,7 @@ class Spelling<string name, string variety> { class GNU<string name> : Spelling<name, "GNU">; class Declspec<string name> : Spelling<name, "Declspec">; +class Microsoft<string name> : Spelling<name, "Microsoft">; class CXX11<string namespace, string name, int version = 1> : Spelling<name, "CXX11"> { string Namespace = namespace; @@ -1584,7 +1585,7 @@ def Used : InheritableAttr { } def Uuid : InheritableAttr { - let Spellings = [Declspec<"uuid">]; + let Spellings = [Declspec<"uuid">, Microsoft<"uuid">]; let Args = [StringArgument<"Guid">]; // let Subjects = SubjectList<[CXXRecord]>; let LangOpts = [MicrosoftExt, Borland]; diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h index 41b9a18dcf8..dae0757978a 100644 --- a/clang/include/clang/Parse/Parser.h +++ b/clang/include/clang/Parse/Parser.h @@ -2212,6 +2212,7 @@ private: if (getLangOpts().MicrosoftExt && Tok.is(tok::l_square)) ParseMicrosoftAttributes(attrs, endLoc); } + void ParseMicrosoftUuidAttributeArgs(ParsedAttributes &Attrs); void ParseMicrosoftAttributes(ParsedAttributes &attrs, SourceLocation *endLoc = nullptr); void MaybeParseMicrosoftDeclSpecs(ParsedAttributes &Attrs, diff --git a/clang/lib/Parse/ParseDeclCXX.cpp b/clang/lib/Parse/ParseDeclCXX.cpp index 45157acdc47..ed99fedeab3 100644 --- a/clang/lib/Parse/ParseDeclCXX.cpp +++ b/clang/lib/Parse/ParseDeclCXX.cpp @@ -3922,6 +3922,93 @@ SourceLocation Parser::SkipCXX11Attributes() { return EndLoc; } +/// Parse uuid() attribute when it appears in a [] Microsoft attribute. +void Parser::ParseMicrosoftUuidAttributeArgs(ParsedAttributes &Attrs) { + assert(Tok.is(tok::identifier) && "Not a Microsoft attribute list"); + IdentifierInfo *UuidIdent = Tok.getIdentifierInfo(); + assert(UuidIdent->getName() == "uuid" && "Not a Microsoft attribute list"); + + SourceLocation UuidLoc = Tok.getLocation(); + ConsumeToken(); + + // Ignore the left paren location for now. + BalancedDelimiterTracker T(*this, tok::l_paren); + if (T.consumeOpen()) { + Diag(Tok, diag::err_expected) << tok::l_paren; + return; + } + + ArgsVector ArgExprs; + if (Tok.is(tok::string_literal)) { + // Easy case: uuid("...") -- quoted string. + ExprResult StringResult = ParseStringLiteralExpression(); + if (StringResult.isInvalid()) + return; + ArgExprs.push_back(StringResult.get()); + } else { + // something like uuid({000000A0-0000-0000-C000-000000000049}) -- no + // quotes in the parens. Just append the spelling of all tokens encountered + // until the closing paren. + + SmallString<42> StrBuffer; // 2 "", 36 bytes UUID, 2 optional {}, 1 nul + StrBuffer += "\""; + + // Since none of C++'s keywords match [a-f]+, accepting just tok::l_brace, + // tok::r_brace, tok::minus, tok::identifier (think C000) and + // tok::numeric_constant (0000) should be enough. But the spelling of the + // uuid argument is checked later anyways, so there's no harm in accepting + // almost anything here. + // cl is very strict about whitespace in this form and errors out if any + // is present, so check the space flags on the tokens. + SourceLocation StartLoc = Tok.getLocation(); + while (Tok.isNot(tok::r_paren)) { + if (Tok.hasLeadingSpace() || Tok.isAtStartOfLine()) { + Diag(Tok, diag::err_attribute_uuid_malformed_guid); + SkipUntil(tok::r_paren, StopAtSemi); + return; + } + SmallString<16> SpellingBuffer; + SpellingBuffer.resize(Tok.getLength() + 1); + bool Invalid = false; + StringRef TokSpelling = PP.getSpelling(Tok, SpellingBuffer, &Invalid); + if (Invalid) { + SkipUntil(tok::r_paren, StopAtSemi); + return; + } + StrBuffer += TokSpelling; + ConsumeAnyToken(); + } + StrBuffer += "\""; + + if (Tok.hasLeadingSpace() || Tok.isAtStartOfLine()) { + Diag(Tok, diag::err_attribute_uuid_malformed_guid); + ConsumeParen(); + return; + } + + // Pretend the user wrote the appropriate string literal here. + // ActOnStringLiteral() copies the string data into the literal, so it's + // ok that the Token points to StrBuffer. + Token Toks[1]; + Toks[0].startToken(); + Toks[0].setKind(tok::string_literal); + Toks[0].setLocation(StartLoc); + Toks[0].setLiteralData(StrBuffer.data()); + Toks[0].setLength(StrBuffer.size()); + StringLiteral *UuidString = + cast<StringLiteral>(Actions.ActOnStringLiteral(Toks, nullptr).get()); + ArgExprs.push_back(UuidString); + } + + if (!T.consumeClose()) { + // FIXME: Warn that this syntax is deprecated, with a Fix-It suggesting + // using __declspec(uuid()) instead. + Attrs.addNew(UuidIdent, SourceRange(UuidLoc, T.getCloseLocation()), nullptr, + SourceLocation(), ArgExprs.data(), ArgExprs.size(), + AttributeList::AS_Microsoft); + } +} + /// ParseMicrosoftAttributes - Parse Microsoft attributes [Attr] /// /// [MS] ms-attribute: @@ -3938,7 +4025,18 @@ void Parser::ParseMicrosoftAttributes(ParsedAttributes &attrs, // FIXME: If this is actually a C++11 attribute, parse it as one. BalancedDelimiterTracker T(*this, tok::l_square); T.consumeOpen(); - SkipUntil(tok::r_square, StopAtSemi | StopBeforeMatch); + + // Skip most ms attributes except for a whitelist. + while (true) { + SkipUntil(tok::r_square, tok::identifier, StopAtSemi | StopBeforeMatch); + if (Tok.isNot(tok::identifier)) // ']', but also eof + break; + if (Tok.getIdentifierInfo()->getName() == "uuid") + ParseMicrosoftUuidAttributeArgs(attrs); + else + ConsumeToken(); + } + T.consumeClose(); if (endLoc) *endLoc = T.getCloseLocation(); diff --git a/clang/test/CodeGenCXX/microsoft-uuidof.cpp b/clang/test/CodeGenCXX/microsoft-uuidof.cpp index 62e4b880ad4..9b4ff68d9aa 100644 --- a/clang/test/CodeGenCXX/microsoft-uuidof.cpp +++ b/clang/test/CodeGenCXX/microsoft-uuidof.cpp @@ -1,4 +1,5 @@ // RUN: %clang_cc1 -emit-llvm %s -o - -DDEFINE_GUID -triple=i386-pc-linux -fms-extensions | FileCheck %s --check-prefix=CHECK-DEFINE-GUID +// RUN: %clang_cc1 -emit-llvm %s -o - -DDEFINE_GUID -DBRACKET_ATTRIB -triple=i386-pc-linux -fms-extensions | FileCheck %s --check-prefix=CHECK-DEFINE-GUID // RUN: %clang_cc1 -emit-llvm %s -o - -triple=i386-pc-linux -fms-extensions | FileCheck %s // RUN: %clang_cc1 -emit-llvm %s -o - -triple=x86_64-pc-linux -fms-extensions | FileCheck %s --check-prefix=CHECK-64 // RUN: %clang_cc1 -emit-llvm %s -o - -DDEFINE_GUID -DWRONG_GUID -triple=i386-pc-linux -fms-extensions | FileCheck %s --check-prefix=CHECK-DEFINE-WRONG-GUID @@ -17,10 +18,17 @@ struct _GUID { #endif typedef struct _GUID GUID; +#ifdef BRACKET_ATTRIB +[uuid(12345678-1234-1234-1234-1234567890aB)] struct S1 { } s1; +[uuid(87654321-4321-4321-4321-ba0987654321)] struct S2 { }; +[uuid({12345678-1234-1234-1234-1234567890ac})] struct Curly; +[uuid({12345678-1234-1234-1234-1234567890ac})] struct Curly; +#else struct __declspec(uuid("12345678-1234-1234-1234-1234567890aB")) S1 { } s1; struct __declspec(uuid("87654321-4321-4321-4321-ba0987654321")) S2 { }; struct __declspec(uuid("{12345678-1234-1234-1234-1234567890ac}")) Curly; struct __declspec(uuid("{12345678-1234-1234-1234-1234567890ac}")) Curly; +#endif #ifdef DEFINE_GUID // Make sure we can properly generate code when the UUID has curly braces on it. diff --git a/clang/test/Parser/MicrosoftExtensions.cpp b/clang/test/Parser/MicrosoftExtensions.cpp index efb9490697e..4aba8f0948e 100644 --- a/clang/test/Parser/MicrosoftExtensions.cpp +++ b/clang/test/Parser/MicrosoftExtensions.cpp @@ -49,6 +49,7 @@ struct __declspec(uuid(3)) uuid_attr_bad2 { };// expected-error {{'uuid' attribu struct __declspec(uuid("0000000-0000-0000-1234-0000500000047")) uuid_attr_bad3 { };// expected-error {{uuid attribute contains a malformed GUID}} struct __declspec(uuid("0000000-0000-0000-Z234-000000000047")) uuid_attr_bad4 { };// expected-error {{uuid attribute contains a malformed GUID}} struct __declspec(uuid("000000000000-0000-1234-000000000047")) uuid_attr_bad5 { };// expected-error {{uuid attribute contains a malformed GUID}} +[uuid("000000000000-0000-1234-000000000047")] struct uuid_attr_bad6 { };// expected-error {{uuid attribute contains a malformed GUID}} __declspec(uuid("000000A0-0000-0000-C000-000000000046")) int i; // expected-warning {{'uuid' attribute only applies to classes}} @@ -59,6 +60,8 @@ struct struct_without_uuid { }; struct __declspec(uuid("000000A0-0000-0000-C000-000000000049")) struct_with_uuid2; +[uuid("000000A0-0000-0000-C000-000000000049")] struct struct_with_uuid3; + struct struct_with_uuid2 {} ; @@ -69,6 +72,7 @@ int uuid_sema_test() __uuidof(struct_with_uuid); __uuidof(struct_with_uuid2); + __uuidof(struct_with_uuid3); __uuidof(struct_without_uuid); // expected-error {{cannot call operator __uuidof on a type with no GUID}} __uuidof(struct_with_uuid*); __uuidof(struct_without_uuid*); // expected-error {{cannot call operator __uuidof on a type with no GUID}} diff --git a/clang/test/Parser/ms-square-bracket-attributes.mm b/clang/test/Parser/ms-square-bracket-attributes.mm new file mode 100644 index 00000000000..41d69fc9d27 --- /dev/null +++ b/clang/test/Parser/ms-square-bracket-attributes.mm @@ -0,0 +1,145 @@ +// RUN: %clang_cc1 -fsyntax-only -std=c++14 -verify -fms-extensions %s + +typedef struct _GUID { + unsigned long Data1; + unsigned short Data2; + unsigned short Data3; + unsigned char Data4[8]; +} GUID; + +namespace { +// cl.exe supports [] attributes on decls like so: +[uuid( "000000A0-0000-0000-C000-000000000049" )] struct struct_with_uuid; + +// Optionally, the uuid can be surrounded by one set of braces. +[uuid( + "{000000A0-0000-0000-C000-000000000049}" +)] struct struct_with_uuid_brace; + +// uuids must be ascii string literals. +// expected-error@+1 {{uuid attribute contains a malformed GUID}} +[uuid(u8"000000A0-0000-0000-C000-000000000049")] struct struct_with_uuid_u8; +// expected-error@+1 {{uuid attribute contains a malformed GUID}} +[uuid(L"000000A0-0000-0000-C000-000000000049")] struct struct_with_uuid_L; + +// cl.exe doesn't allow raw string literals in []-style attributes, but does +// allow it for __declspec(uuid()) (u8 literals etc are not allowed there +// either). Since raw string literals not being allowed seems like an +// implementation artifact in cl and not allowing them makes the parse code +// a bit unnatural, do allow this. +[uuid(R"(000000A0-0000-0000-C000-000000000049)")] struct struct_with_uuid_raw; + +// Likewise, cl supports UCNs in declspec uuid, but not in []-style uuid. +// clang-cl allows them in both. +[uuid("000000A0-0000\u002D0000-C000-000000000049")] struct struct_with_uuid_ucn; + +// cl doesn't allow string concatenation in []-style attributes, for no good +// reason. clang-cl allows them. +[uuid("000000A0-00" "00-0000-C000-000000000049")] struct struct_with_uuid_split; + +// expected-error@+1 {{expected ')'}} expected-note@+1 {{to match this '('}} +[uuid("{000000A0-0000-0000-C000-000000000049}", "1")] struct S {}; +// expected-error@+1 {{expected '('}} +[uuid{"000000A0-0000-0000-C000-000000000049"}] struct T {}; + + +// In addition to uuids in string literals, cl also allows uuids that are not +// in a string literal, only delimited by (). The contents of () are almost +// treated like a literal (spaces there aren't ignored), but macro substitution, +// \ newline escapes, and so on are performed. + +[ uuid (000000A0-0000-0000-C000-000000000049) ] struct struct_with_uuid2; +[uuid({000000A0-0000-0000-C000-000000000049})] struct struct_with_uuid2_brace; + +// The non-quoted form doesn't allow any whitespace inside the parens: +// expected-error@+1 {{uuid attribute contains a malformed GUID}} +[uuid( 000000A0-0000-0000-C000-000000000049)] struct struct_with_uuid2; +// expected-error@+1 {{uuid attribute contains a malformed GUID}} +[uuid(000000A0-0000 -0000-C000-000000000049)] struct struct_with_uuid2; +// expected-error@+2 {{uuid attribute contains a malformed GUID}} +[uuid(000000A0-0000 +-0000-C000-000000000049)] struct struct_with_uuid2; +// expected-error@+1 {{uuid attribute contains a malformed GUID}} +[uuid(000000A0-0000/**/-0000-C000-000000000049)] struct struct_with_uuid2; +// expected-error@+1 {{uuid attribute contains a malformed GUID}} +[uuid(000000A0-0000-0000-C000-000000000049 )] struct struct_with_uuid2; +// expected-error@+2 {{uuid attribute contains a malformed GUID}} +[uuid(000000A0-0000-0000-C000-000000000049 +) +] struct struct_with_uuid2; +// expected-error@+1 {{uuid attribute contains a malformed GUID}} +[uuid({000000A0-0000-""0000-C000-000000000049})] struct struct_with_uuid2; + +// Line continuations and macro substitution are fine though: +[uuid(000000A0-0000-0000-\ +C000-000000000049)] struct struct_with_uuid2_cont; +#define UUID 000000A0-0000-0000-C000-000000000049 +#define UUID_PART 000000A0-0000 +[uuid(UUID)] struct struct_with_uuid2_macro; +[uuid(UUID_PART-0000-C000-000000000049)] struct struct_with_uuid2_macro_part; + +// Both cl and clang-cl accept trigraphs here (with /Zc:trigraphs, off by +// default) +// expected-warning@+1 2{{trigraph converted}} +[uuid(??<000000A0-0000-0000-C000-000000000049??>)] +struct struct_with_uuid2_trigraph; + +// UCNs cannot be used in this form because they're prohibited by C99. +// expected-error@+1 {{character '-' cannot be specified by a universal character name}} +[uuid(000000A0-0000\u002D0000-C000-000000000049)] struct struct_with_uuid2_ucn; + +// Invalid digits. +// expected-error@+1 {{uuid attribute contains a malformed GUID}} +[uuid(0Z0000A0-0000-0000-C000-000000000049)] struct struct_with_uuid2; + +void use_it() { + (void)__uuidof(struct_with_uuid); + (void)__uuidof(struct_with_uuid_brace); + (void)__uuidof(struct_with_uuid_raw); + (void)__uuidof(struct_with_uuid_ucn); + (void)__uuidof(struct_with_uuid_split); + + (void)__uuidof(struct_with_uuid2); + (void)__uuidof(struct_with_uuid2_brace); + (void)__uuidof(struct_with_uuid2_cont); + (void)__uuidof(struct_with_uuid2_macro); + (void)__uuidof(struct_with_uuid2_macro_part); + (void)__uuidof(struct_with_uuid2_trigraph); +} +} + +// clang supports these on toplevel decls, but not on local decls since this +// syntax is ambiguous with lambdas and Objective-C message send expressions. +// This file documents clang's shortcomings and lists a few constructs that +// one has to keep in mind when trying to fix this. System headers only seem +// to use these attributes on toplevel decls, so supporting this is not very +// important. + +void local_class() { + // FIXME: MSVC accepts, but we reject due to ambiguity. + // expected-error@+1 {{expected body of lambda expression}} + [uuid("a5a7bd07-3b14-49bc-9399-de066d4d72cd")] struct Local { + int x; + }; +} + +void useit(int); +int lambda() { + int uuid = 42; + [uuid]() { useit(uuid); }(); + + // C++14 lambda init captures: + [uuid(00000000-0000-0000-0000-000000000000)] { return uuid; }(); + [uuid("00000000-0000-0000-0000-000000000000")](int n) { return uuid[n]; }(3); +} + +@interface NSObject +- (void)retain; +@end +int message_send(id uuid) { + [uuid retain]; +} +NSObject* uuid(const char*); +int message_send2() { + [uuid("a5a7bd07-3b14-49bc-9399-de066d4d72cd") retain]; +} diff --git a/clang/test/Sema/MicrosoftExtensions.c b/clang/test/Sema/MicrosoftExtensions.c index 62e5285970a..79dba88a990 100644 --- a/clang/test/Sema/MicrosoftExtensions.c +++ b/clang/test/Sema/MicrosoftExtensions.c @@ -28,6 +28,8 @@ struct D { struct __declspec(uuid("00000000-0000-0000-C000-000000000046")) IUnknown {}; /* expected-error {{'uuid' attribute is not supported in C}} */ +[uuid("00000000-0000-0000-C000-000000000046")] struct IUnknown2 {}; /* expected-error {{'uuid' attribute is not supported in C}} */ + typedef struct notnested { long bad1; long bad2; |