r280578 - [ms] Add support for parsing uuid as a Microsoft attribute.

Nico Weber via cfe-commits cfe-commits at lists.llvm.org
Fri Sep 2 20:25:23 PDT 2016


Author: nico
Date: Fri Sep  2 22:25:22 2016
New Revision: 280578

URL: http://llvm.org/viewvc/llvm-project?rev=280578&view=rev
Log:
[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

Added:
    cfe/trunk/test/Parser/ms-square-bracket-attributes.mm
Modified:
    cfe/trunk/include/clang/Basic/Attr.td
    cfe/trunk/include/clang/Parse/Parser.h
    cfe/trunk/lib/Parse/ParseDeclCXX.cpp
    cfe/trunk/test/CodeGenCXX/microsoft-uuidof.cpp
    cfe/trunk/test/Parser/MicrosoftExtensions.cpp
    cfe/trunk/test/Sema/MicrosoftExtensions.c

Modified: cfe/trunk/include/clang/Basic/Attr.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/Attr.td?rev=280578&r1=280577&r2=280578&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/Attr.td (original)
+++ cfe/trunk/include/clang/Basic/Attr.td Fri Sep  2 22:25:22 2016
@@ -198,6 +198,7 @@ class Spelling<string name, string varie
 
 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];

Modified: cfe/trunk/include/clang/Parse/Parser.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Parse/Parser.h?rev=280578&r1=280577&r2=280578&view=diff
==============================================================================
--- cfe/trunk/include/clang/Parse/Parser.h (original)
+++ cfe/trunk/include/clang/Parse/Parser.h Fri Sep  2 22:25:22 2016
@@ -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,

Modified: cfe/trunk/lib/Parse/ParseDeclCXX.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Parse/ParseDeclCXX.cpp?rev=280578&r1=280577&r2=280578&view=diff
==============================================================================
--- cfe/trunk/lib/Parse/ParseDeclCXX.cpp (original)
+++ cfe/trunk/lib/Parse/ParseDeclCXX.cpp Fri Sep  2 22:25:22 2016
@@ -3922,6 +3922,93 @@ SourceLocation Parser::SkipCXX11Attribut
   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(Pa
     // 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();

Modified: cfe/trunk/test/CodeGenCXX/microsoft-uuidof.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGenCXX/microsoft-uuidof.cpp?rev=280578&r1=280577&r2=280578&view=diff
==============================================================================
--- cfe/trunk/test/CodeGenCXX/microsoft-uuidof.cpp (original)
+++ cfe/trunk/test/CodeGenCXX/microsoft-uuidof.cpp Fri Sep  2 22:25:22 2016
@@ -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.

Modified: cfe/trunk/test/Parser/MicrosoftExtensions.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Parser/MicrosoftExtensions.cpp?rev=280578&r1=280577&r2=280578&view=diff
==============================================================================
--- cfe/trunk/test/Parser/MicrosoftExtensions.cpp (original)
+++ cfe/trunk/test/Parser/MicrosoftExtensions.cpp Fri Sep  2 22:25:22 2016
@@ -49,6 +49,7 @@ struct __declspec(uuid(3)) uuid_attr_bad
 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}}

Added: cfe/trunk/test/Parser/ms-square-bracket-attributes.mm
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Parser/ms-square-bracket-attributes.mm?rev=280578&view=auto
==============================================================================
--- cfe/trunk/test/Parser/ms-square-bracket-attributes.mm (added)
+++ cfe/trunk/test/Parser/ms-square-bracket-attributes.mm Fri Sep  2 22:25:22 2016
@@ -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 at +1 {{uuid attribute contains a malformed GUID}}
+[uuid(u8"000000A0-0000-0000-C000-000000000049")] struct struct_with_uuid_u8;
+// expected-error at +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 at +1 {{expected ')'}} expected-note at +1 {{to match this '('}}
+[uuid("{000000A0-0000-0000-C000-000000000049}", "1")] struct S {};
+// expected-error at +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 at +1 {{uuid attribute contains a malformed GUID}}
+[uuid( 000000A0-0000-0000-C000-000000000049)] struct struct_with_uuid2;
+// expected-error at +1 {{uuid attribute contains a malformed GUID}}
+[uuid(000000A0-0000 -0000-C000-000000000049)] struct struct_with_uuid2;
+// expected-error at +2 {{uuid attribute contains a malformed GUID}}
+[uuid(000000A0-0000
+-0000-C000-000000000049)] struct struct_with_uuid2;
+// expected-error at +1 {{uuid attribute contains a malformed GUID}}
+[uuid(000000A0-0000/**/-0000-C000-000000000049)] struct struct_with_uuid2;
+// expected-error at +1 {{uuid attribute contains a malformed GUID}}
+[uuid(000000A0-0000-0000-C000-000000000049 )] struct struct_with_uuid2;
+// expected-error at +2 {{uuid attribute contains a malformed GUID}}
+[uuid(000000A0-0000-0000-C000-000000000049
+)
+] struct struct_with_uuid2;
+// expected-error at +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 at +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 at +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 at +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 at +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);
+}
+
+ at interface NSObject
+- (void)retain;
+ at end
+int message_send(id uuid) {
+  [uuid retain]; 
+}
+NSObject* uuid(const char*);
+int message_send2() {
+  [uuid("a5a7bd07-3b14-49bc-9399-de066d4d72cd") retain]; 
+}

Modified: cfe/trunk/test/Sema/MicrosoftExtensions.c
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Sema/MicrosoftExtensions.c?rev=280578&r1=280577&r2=280578&view=diff
==============================================================================
--- cfe/trunk/test/Sema/MicrosoftExtensions.c (original)
+++ cfe/trunk/test/Sema/MicrosoftExtensions.c Fri Sep  2 22:25:22 2016
@@ -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;




More information about the cfe-commits mailing list