]> granicus.if.org Git - clang/commitdiff
[ms] Add support for parsing uuid as a Microsoft attribute.
authorNico Weber <nicolasweber@gmx.de>
Sat, 3 Sep 2016 03:25:22 +0000 (03:25 +0000)
committerNico Weber <nicolasweber@gmx.de>
Sat, 3 Sep 2016 03:25:22 +0000 (03:25 +0000)
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

include/clang/Basic/Attr.td
include/clang/Parse/Parser.h
lib/Parse/ParseDeclCXX.cpp
test/CodeGenCXX/microsoft-uuidof.cpp
test/Parser/MicrosoftExtensions.cpp
test/Parser/ms-square-bracket-attributes.mm [new file with mode: 0644]
test/Sema/MicrosoftExtensions.c

index 912d1515382868710cf89628131d9b2888c678f1..b72222fc5cf5c3ba943992e4b233152cfc386a1b 100644 (file)
@@ -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];
index 41b9a18dcf8ebc0ee8c1ad796e5575bd4060fe52..dae0757978a7cfd61e4e256ba02ce11f566416cd 100644 (file)
@@ -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,
index 45157acdc47acce4a09cb1e1e4643c57d43ecc58..ed99fedeab370d7db0de7d080bb299ecdd6fa475 100644 (file)
@@ -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();
index 62e4b880ad46ef6ac57f50286e619b2b583f6f25..9b4ff68d9aa8430e606e6d3a237e7c542e58e3ca 100644 (file)
@@ -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.
index efb9490697e762df1696db26ff864abe341f1305..4aba8f0948edc4e1a68bdd443753c596f380cb03 100644 (file)
@@ -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 (file)
index 0000000..41d69fc
--- /dev/null
@@ -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]; 
+}
index 62e5285970a7ee37e9f29ae18825c5330a1f931c..79dba88a9903be0949cd7b1088a02e9c5d742398 100644 (file)
@@ -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;