[clang-cl] Diagnose duplicate uuids.

This mostly behaves cl.exe's behavior, even though clang-cl is stricter in some
corner cases and more lenient in others (see the included test).

To make the uuid declared previously here diagnostic work correctly, tweak
stripTypeAttributesOffDeclSpec() to keep attributes in the right order.

https://reviews.llvm.org/D24469

llvm-svn: 281367
This commit is contained in:
Nico Weber 2016-09-13 18:55:26 +00:00
parent 39ccd24126
commit 88f5ed9430
7 changed files with 152 additions and 4 deletions

View File

@ -2255,6 +2255,8 @@ def err_attribute_only_once_per_parameter : Error<
"%0 attribute can only be applied once per parameter">;
def err_attribute_uuid_malformed_guid : Error<
"uuid attribute contains a malformed GUID">;
def err_mismatched_uuid : Error<"uuid does not match previous declaration">;
def note_previous_uuid : Note<"previous uuid specified here">;
def warn_attribute_pointers_only : Warning<
"%0 attribute only applies to%select{| constant}1 pointer arguments">,
InGroup<IgnoredAttributes>;

View File

@ -748,6 +748,19 @@ public:
list = newList;
}
void addAllAtEnd(AttributeList *newList) {
if (!list) {
list = newList;
return;
}
AttributeList *lastInList = list;
while (AttributeList *next = lastInList->getNext())
lastInList = next;
lastInList->setNext(newList);
}
void set(AttributeList *newList) {
list = newList;
}

View File

@ -2208,6 +2208,8 @@ public:
VisibilityAttr *mergeVisibilityAttr(Decl *D, SourceRange Range,
VisibilityAttr::VisibilityType Vis,
unsigned AttrSpellingListIndex);
UuidAttr *mergeUuidAttr(Decl *D, SourceRange Range,
unsigned AttrSpellingListIndex, StringRef Uuid);
DLLImportAttr *mergeDLLImportAttr(Decl *D, SourceRange Range,
unsigned AttrSpellingListIndex);
DLLExportAttr *mergeDLLExportAttr(Decl *D, SourceRange Range,

View File

@ -1439,6 +1439,8 @@ void Parser::stripTypeAttributesOffDeclSpec(ParsedAttributesWithRange &Attrs,
ParsedAttributes &PA = DS.getAttributes();
AttributeList *AL = PA.getList();
AttributeList *Prev = nullptr;
AttributeList *TypeAttrHead = nullptr;
AttributeList *TypeAttrTail = nullptr;
while (AL) {
AttributeList *Next = AL->getNext();
@ -1446,8 +1448,12 @@ void Parser::stripTypeAttributesOffDeclSpec(ParsedAttributesWithRange &Attrs,
AL->isDeclspecAttribute()) ||
AL->isMicrosoftAttribute()) {
// Stitch the attribute into the tag's attribute list.
AL->setNext(nullptr);
Attrs.add(AL);
if (TypeAttrTail)
TypeAttrTail->setNext(AL);
else
TypeAttrHead = AL;
TypeAttrTail = AL;
TypeAttrTail->setNext(nullptr);
// Remove the attribute from the variable's attribute list.
if (Prev) {
@ -1465,6 +1471,12 @@ void Parser::stripTypeAttributesOffDeclSpec(ParsedAttributesWithRange &Attrs,
AL = Next;
}
// Find end of type attributes Attrs and add NewTypeAttributes in the same
// order they were in originally. (Remember, in AttributeList things earlier
// in source order are later in the list, since new attributes are added to
// the front of the list.)
Attrs.addAllAtEnd(TypeAttrHead);
}
/// ParseDeclaration - Parse a full 'declaration', which consists of

View File

@ -2246,6 +2246,13 @@ static bool mergeAlignedAttrs(Sema &S, NamedDecl *New, Decl *Old) {
static bool mergeDeclAttribute(Sema &S, NamedDecl *D,
const InheritableAttr *Attr,
Sema::AvailabilityMergeKind AMK) {
// This function copies an attribute Attr from a previous declaration to the
// new declaration D if the new declaration doesn't itself have that attribute
// yet or if that attribute allows duplicates.
// If you're adding a new attribute that requires logic different from
// "use explicit attribute on decl if present, else use attribute from
// previous decl", for example if the attribute needs to be consistent
// between redeclarations, you need to call a custom merge function here.
InheritableAttr *NewAttr = nullptr;
unsigned AttrSpellingListIndex = Attr->getSpellingListIndex();
if (const auto *AA = dyn_cast<AvailabilityAttr>(Attr))
@ -2304,6 +2311,9 @@ static bool mergeDeclAttribute(Sema &S, NamedDecl *D,
(AMK == Sema::AMK_Override ||
AMK == Sema::AMK_ProtocolImplementation))
NewAttr = nullptr;
else if (const auto *UA = dyn_cast<UuidAttr>(Attr))
NewAttr = S.mergeUuidAttr(D, UA->getRange(), AttrSpellingListIndex,
UA->getGuid());
else if (Attr->duplicatesAllowed() || !DeclHasAttr(D, Attr))
NewAttr = cast<InheritableAttr>(Attr->clone(S.Context));

View File

@ -4604,6 +4604,19 @@ static void handleObjCPreciseLifetimeAttr(Sema &S, Decl *D,
// Microsoft specific attribute handlers.
//===----------------------------------------------------------------------===//
UuidAttr *Sema::mergeUuidAttr(Decl *D, SourceRange Range,
unsigned AttrSpellingListIndex, StringRef Uuid) {
if (const auto *UA = D->getAttr<UuidAttr>()) {
if (UA->getGuid() == Uuid)
return nullptr;
Diag(UA->getLocation(), diag::err_mismatched_uuid);
Diag(Range.getBegin(), diag::note_previous_uuid);
D->dropAttr<UuidAttr>();
}
return ::new (Context) UuidAttr(Range, Context, Uuid, AttrSpellingListIndex);
}
static void handleUuidAttr(Sema &S, Decl *D, const AttributeList &Attr) {
if (!S.LangOpts.CPlusPlus) {
S.Diag(Attr.getLoc(), diag::err_attribute_not_supported_in_lang)
@ -4645,8 +4658,10 @@ static void handleUuidAttr(Sema &S, Decl *D, const AttributeList &Attr) {
}
}
D->addAttr(::new (S.Context) UuidAttr(Attr.getRange(), S.Context, StrRef,
Attr.getAttributeSpellingListIndex()));
UuidAttr *UA = S.mergeUuidAttr(D, Attr.getRange(),
Attr.getAttributeSpellingListIndex(), StrRef);
if (UA)
D->addAttr(UA);
}
static void handleMSInheritanceAttr(Sema &S, Decl *D, const AttributeList &Attr) {

View File

@ -0,0 +1,94 @@
// RUN: %clang_cc1 -fsyntax-only -verify -fms-extensions %s
typedef struct _GUID {
unsigned long Data1;
unsigned short Data2;
unsigned short Data3;
unsigned char Data4[8];
} GUID;
namespace {
// cl.exe's behavior with merging uuid attributes is a bit erratic:
// * In []-style attributes, a single [] list must not list a duplicate uuid
// (even if it's the same uuid), and only a single declaration of a class
// must have a uuid else the compiler errors out (even if two declarations of
// a class have the same uuid).
// * For __declspec(uuid(...)), it's ok if several declarations of a class have
// an uuid, as long as it's the same uuid each time. If uuids on declarations
// don't match, the compiler errors out.
// * If there are several __declspec(uuid(...))s on one declaration, the
// compiler only warns about this and uses the last uuid. It even warns if
// the uuids are the same.
// clang-cl implements the following simpler (but largely compatible) behavior
// instead:
// * [] and __declspec uuids have the same behavior.
// * If there are several uuids on a a class (no matter if on the same decl or
// on several decls), it is an error if they don't match.
// * Having several uuids that match is ok.
// Both cl and clang-cl accept this:
class __declspec(uuid("000000A0-0000-0000-C000-000000000049")) C1;
class __declspec(uuid("000000A0-0000-0000-C000-000000000049")) C1;
class __declspec(uuid("000000A0-0000-0000-C000-000000000049")) C1 {};
// Both cl and clang-cl error out on this:
// expected-note@+1 2{{previous uuid specified here}}
class __declspec(uuid("000000A0-0000-0000-C000-000000000049")) C2;
// expected-error@+1 {{uuid does not match previous declaration}}
class __declspec(uuid("110000A0-0000-0000-C000-000000000049")) C2;
// expected-error@+1 {{uuid does not match previous declaration}}
class __declspec(uuid("220000A0-0000-0000-C000-000000000049")) C2 {};
// expected-note@+1 {{previous uuid specified here}}
class __declspec(uuid("000000A0-0000-0000-C000-000000000049")) C2_2;
class C2_2;
// expected-error@+1 {{uuid does not match previous declaration}}
class __declspec(uuid("110000A0-0000-0000-C000-000000000049")) C2_2;
// clang-cl accepts this, but cl errors out:
[uuid("000000A0-0000-0000-C000-000000000049")] class C3;
[uuid("000000A0-0000-0000-C000-000000000049")] class C3;
[uuid("000000A0-0000-0000-C000-000000000049")] class C3 {};
// Both cl and clang-cl error out on this (but for different reasons):
// expected-note@+1 2{{previous uuid specified here}}
[uuid("000000A0-0000-0000-C000-000000000049")] class C4;
// expected-error@+1 {{uuid does not match previous declaration}}
[uuid("110000A0-0000-0000-C000-000000000049")] class C4;
// expected-error@+1 {{uuid does not match previous declaration}}
[uuid("220000A0-0000-0000-C000-000000000049")] class C4 {};
// Both cl and clang-cl error out on this:
// expected-note@+1 {{previous uuid specified here}}
class __declspec(uuid("000000A0-0000-0000-C000-000000000049"))
// expected-error@+1 {{uuid does not match previous declaration}}
__declspec(uuid("110000A0-0000-0000-C000-000000000049")) C5;
// expected-note@+1 {{previous uuid specified here}}
[uuid("000000A0-0000-0000-C000-000000000049"),
// expected-error@+1 {{uuid does not match previous declaration}}
uuid("110000A0-0000-0000-C000-000000000049")] class C6;
// cl doesn't diagnose having one uuid each as []-style attributes and as
// __declspec, even if the uuids differ. clang-cl errors if they differ.
[uuid("000000A0-0000-0000-C000-000000000049")]
class __declspec(uuid("000000A0-0000-0000-C000-000000000049")) C7;
// expected-note@+1 {{previous uuid specified here}}
[uuid("000000A0-0000-0000-C000-000000000049")]
// expected-error@+1 {{uuid does not match previous declaration}}
class __declspec(uuid("110000A0-0000-0000-C000-000000000049")) C8;
// cl warns on this, but clang-cl is fine with it (which is consistent with
// e.g. specifying __multiple_inheritance several times, which cl accepts
// without warning too).
class __declspec(uuid("000000A0-0000-0000-C000-000000000049"))
__declspec(uuid("000000A0-0000-0000-C000-000000000049")) C9;
// cl errors out on this, but clang-cl is fine with it (to be consistent with
// the previous case).
[uuid("000000A0-0000-0000-C000-000000000049"),
uuid("000000A0-0000-0000-C000-000000000049")] class C10;
}