Skip to content
This repository was archived by the owner on Mar 28, 2020. It is now read-only.

Introduce ns_error_domain attribute. #3

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions include/clang/Basic/Attr.td
Original file line number Diff line number Diff line change
Expand Up @@ -1080,6 +1080,12 @@ def ObjCBridgeRelated : InheritableAttr {
let Documentation = [Undocumented];
}

def NSErrorDomain : Attr {
let Spellings = [GNU<"ns_error_domain">];
let Documentation = [Undocumented];
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add some documentation here, otherwise it won't get documented anywhere.

let Args = [IdentifierArgument<"ErrorDomain">];
}

def NSReturnsRetained : InheritableAttr {
let Spellings = [GNU<"ns_returns_retained">];
// let Subjects = SubjectList<[ObjCMethod, ObjCProperty, Function]>;
Expand Down
7 changes: 7 additions & 0 deletions include/clang/Basic/DiagnosticSemaKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -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">;
Expand Down
36 changes: 36 additions & 0 deletions lib/Sema/SemaDeclAttr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<TagDecl>(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;
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps you should be checking that it's actually a global-scope variable?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

... in which case you could record the declaration in the attribute, rather than just the identifier, so clients don't need to look it up themselves.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I wanted to leave the actual Decl in the attribute, but it didn't seem like I could without modifying the tablegen backend for Attributes. I'm certainly open to doing that, but not sure if it's worth it. Right now, only FunctionDecl and IdentifierInfo are supported, it seems, in ClangAttrEmitter.cpp's "createArgument".

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, that's fine

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<CFUnknownTransferAttr>(S, D, Attr))
Expand Down Expand Up @@ -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);
Expand Down
38 changes: 38 additions & 0 deletions test/Analysis/ns_error_enum.m
Original file line number Diff line number Diff line change
@@ -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}}