From 85d4ce9d83bd0c164c3302e88aa5574bdcdaf1eb Mon Sep 17 00:00:00 2001 From: Michael Ilseman Date: Fri, 29 Jan 2016 15:53:43 -0800 Subject: [PATCH] Introduce ns_error_domain attribute. ns_error_domain can be used by, e.g. NS_ERROR_ENUM, in order to identify a global declaration representing the domain constant. Introduces the attribute, Sema handling, diagnostics, and test case. --- include/clang/Basic/Attr.td | 6 ++++ include/clang/Basic/DiagnosticSemaKinds.td | 7 ++++ lib/Sema/SemaDeclAttr.cpp | 36 ++++++++++++++++++++ test/Analysis/ns_error_enum.m | 38 ++++++++++++++++++++++ 4 files changed, 87 insertions(+) create mode 100644 test/Analysis/ns_error_enum.m diff --git a/include/clang/Basic/Attr.td b/include/clang/Basic/Attr.td index 48db0632261..bbfa818d7ad 100644 --- a/include/clang/Basic/Attr.td +++ b/include/clang/Basic/Attr.td @@ -1080,6 +1080,12 @@ def ObjCBridgeRelated : InheritableAttr { let Documentation = [Undocumented]; } +def NSErrorDomain : Attr { + let Spellings = [GNU<"ns_error_domain">]; + let Documentation = [Undocumented]; + let Args = [IdentifierArgument<"ErrorDomain">]; +} + def NSReturnsRetained : InheritableAttr { let Spellings = [GNU<"ns_returns_retained">]; // let Subjects = SubjectList<[ObjCMethod, ObjCProperty, Function]>; diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index 0f94579fb2e..fa0b5fe85d7 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -7402,6 +7402,13 @@ def err_nsconsumed_attribute_mismatch : Error< def err_nsreturns_retained_attribute_mismatch : Error< "overriding method has mismatched ns_returns_%select{not_retained|retained}0" " attributes">; + +def err_nserrordomain_not_tagdecl : Error< + "ns_error_domain attribute only valid on enum/struct/union/class">; +def err_nserrordomain_invalid_decl : Error< + "domain argument %0 not valid top-level declaration">; +def err_nserrordomain_requires_identifier : Error< + "domain argument must be an identifier">; def note_getter_unavailable : Note< "or because setter is declared here, but no getter method %0 is found">; diff --git a/lib/Sema/SemaDeclAttr.cpp b/lib/Sema/SemaDeclAttr.cpp index 3a7d303f111..ac04bff621f 100644 --- a/lib/Sema/SemaDeclAttr.cpp +++ b/lib/Sema/SemaDeclAttr.cpp @@ -4057,6 +4057,38 @@ static void handleObjCRequiresSuperAttr(Sema &S, Decl *D, attr.getAttributeSpellingListIndex())); } +static void handleNSErrorDomain(Sema &S, Decl *D, const AttributeList &Attr) { + if (!isa(D)) { + S.Diag(D->getLocStart(), diag::err_nserrordomain_not_tagdecl); + return; + } + IdentifierLoc *identLoc = + Attr.isArgIdent(0) ? Attr.getArgAsIdent(0) : nullptr; + if (!identLoc || !identLoc->Ident) { + // Try to locate the argument directly + SourceLocation loc = Attr.getLoc(); + if (Attr.isArgExpr(0) && Attr.getArgAsExpr(0)) + loc = Attr.getArgAsExpr(0)->getLocStart(); + + S.Diag(loc, diag::err_nserrordomain_requires_identifier); + return; + } + + // Verify that the identifier is a valid decl in the C decl namespace + LookupResult lookupResult(S, DeclarationName(identLoc->Ident), + SourceLocation(), + Sema::LookupNameKind::LookupOrdinaryName); + if (!S.LookupName(lookupResult, S.TUScope)) { + S.Diag(identLoc->Loc, diag::err_nserrordomain_invalid_decl) + << identLoc->Ident; + return; + } + + D->addAttr(::new (S.Context) + NSErrorDomainAttr(Attr.getRange(), S.Context, identLoc->Ident, + Attr.getAttributeSpellingListIndex())); +} + static void handleCFAuditedTransferAttr(Sema &S, Decl *D, const AttributeList &Attr) { if (checkAttrMutualExclusion(S, D, Attr)) @@ -5197,6 +5229,10 @@ static void ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, case AttributeList::AT_ObjCBoxable: handleObjCBoxable(S, D, Attr); break; + + case AttributeList::AT_NSErrorDomain: + handleNSErrorDomain(S, D, Attr); + break; case AttributeList::AT_CFAuditedTransfer: handleCFAuditedTransferAttr(S, D, Attr); diff --git a/test/Analysis/ns_error_enum.m b/test/Analysis/ns_error_enum.m new file mode 100644 index 00000000000..5adb4a780be --- /dev/null +++ b/test/Analysis/ns_error_enum.m @@ -0,0 +1,38 @@ +// RUN: %clang_cc1 -verify %s + +#define CF_ENUM(_type, _name) enum _name : _type _name; enum _name : _type +#define NS_ENUM(_type, _name) CF_ENUM(_type, _name) + +#define NS_ERROR_ENUM(_type, _name, _domain) \ + enum _name : _type _name; enum __attribute__((ns_error_domain(_domain))) _name : _type + +typedef NS_ENUM(unsigned, MyEnum) { + MyFirst, + MySecond, +}; + +typedef NS_ENUM(invalidType, MyInvalidEnum) { +// expected-error@-1{{unknown type name 'invalidType'}} +// expected-error@-2{{unknown type name 'invalidType'}} + MyFirstInvalid, + MySecondInvalid, +}; + +const char *MyErrorDomain; +typedef NS_ERROR_ENUM(unsigned char, MyErrorEnum, MyErrorDomain) { + MyErrFirst, + MyErrSecond, +}; +struct __attribute__((ns_error_domain(MyErrorDomain))) MyStructErrorDomain {}; + +typedef NS_ERROR_ENUM(unsigned char, MyErrorEnumInvalid, InvalidDomain) { + // expected-error@-1{{domain argument 'InvalidDomain' not valid top-level declaration}} + MyErrFirstInvalid, + MyErrSecondInvalid, +}; + +typedef NS_ERROR_ENUM(unsigned char, MyErrorEnumInvalid, "domain-string"); + // expected-error@-1{{domain argument must be an identifier}} + +int __attribute__((ns_error_domain(MyErrorDomain))) NotTagDecl; + // expected-error@-1{{ns_error_domain attribute only valid on enum/struct/union/class}}