From: Nico Weber Date: Sat, 3 Sep 2016 03:25:22 +0000 (+0000) Subject: [ms] Add support for parsing uuid as a Microsoft attribute. X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=989beae06d0bbad94d847e01429579cddb3a2faa;p=clang [ms] Add support for parsing uuid as a Microsoft attribute. Some Windows SDK classes, for example Windows::Storage::Streams::IBufferByteAccess, use the ATL way of spelling attributes: [uuid("....")] class IBufferByteAccess {}; To be able to use __uuidof() to grab the uuid off these types, clang needs to support uuid as a Microsoft attribute. There was already code to skip Microsoft attributes, extend that to look for uuid and parse it. Use the new "Microsoft" attribute type added in r280575 (and r280574, r280576) for this. Final part of https://reviews.llvm.org/D23895 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@280578 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/Basic/Attr.td b/include/clang/Basic/Attr.td index 912d151538..b72222fc5c 100644 --- a/include/clang/Basic/Attr.td +++ b/include/clang/Basic/Attr.td @@ -198,6 +198,7 @@ class Spelling { class GNU : Spelling; class Declspec : Spelling; +class Microsoft : Spelling; class CXX11 : Spelling { 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/include/clang/Parse/Parser.h b/include/clang/Parse/Parser.h index 41b9a18dcf..dae0757978 100644 --- a/include/clang/Parse/Parser.h +++ b/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/lib/Parse/ParseDeclCXX.cpp b/lib/Parse/ParseDeclCXX.cpp index 45157acdc4..ed99fedeab 100644 --- a/lib/Parse/ParseDeclCXX.cpp +++ b/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(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/test/CodeGenCXX/microsoft-uuidof.cpp b/test/CodeGenCXX/microsoft-uuidof.cpp index 62e4b880ad..9b4ff68d9a 100644 --- a/test/CodeGenCXX/microsoft-uuidof.cpp +++ b/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/test/Parser/MicrosoftExtensions.cpp b/test/Parser/MicrosoftExtensions.cpp index efb9490697..4aba8f0948 100644 --- a/test/Parser/MicrosoftExtensions.cpp +++ b/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/test/Parser/ms-square-bracket-attributes.mm b/test/Parser/ms-square-bracket-attributes.mm new file mode 100644 index 0000000000..41d69fc9d2 --- /dev/null +++ b/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/test/Sema/MicrosoftExtensions.c b/test/Sema/MicrosoftExtensions.c index 62e5285970..79dba88a99 100644 --- a/test/Sema/MicrosoftExtensions.c +++ b/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;