forked from OSchip/llvm-project
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.
This is cherry-picked from a14779f504
and adapted to updated Clang APIs.
Reviewed By: gribozavr2, aaron.ballman
Differential Revision: https://reviews.llvm.org/D84005
This commit is contained in:
parent
d538c5837a
commit
a5b8757506
|
@ -1878,6 +1878,13 @@ def ObjCBridgeRelated : InheritableAttr {
|
|||
let Documentation = [Undocumented];
|
||||
}
|
||||
|
||||
def NSErrorDomain : InheritableAttr {
|
||||
let Spellings = [GNU<"ns_error_domain">];
|
||||
let Subjects = SubjectList<[Enum], ErrorDiag>;
|
||||
let Args = [DeclArgument<Var, "ErrorDomain">];
|
||||
let Documentation = [NSErrorDomainDocs];
|
||||
}
|
||||
|
||||
def NSReturnsRetained : DeclOrTypeAttr {
|
||||
let Spellings = [Clang<"ns_returns_retained">];
|
||||
// let Subjects = SubjectList<[ObjCMethod, ObjCProperty, Function]>;
|
||||
|
|
|
@ -3339,6 +3339,38 @@ arguments, with arbitrary offsets.
|
|||
}];
|
||||
}
|
||||
|
||||
def NSErrorDomainDocs : Documentation {
|
||||
let Category = DocCatDecl;
|
||||
let Content = [{
|
||||
In Cocoa frameworks in Objective-C, one can group related error codes in enums
|
||||
and categorize these enums with error domains.
|
||||
|
||||
The ``ns_error_domain`` attribute indicates a global ``NSString`` constant
|
||||
representing the error domain that an error code belongs to. For pointer
|
||||
uniqueness and code size this is a constant symbol, not a literal.
|
||||
|
||||
The domain and error code need to be used together. The ``ns_error_domain``
|
||||
attribute links error codes to their domain at the source level.
|
||||
|
||||
This metadata is useful for documentation purposes, for static analysis, and for
|
||||
improving interoperability between Objective-C and Swift. It is not used for
|
||||
code generation in Objective-C.
|
||||
|
||||
For example:
|
||||
|
||||
.. code-block:: objc
|
||||
|
||||
#define NS_ERROR_ENUM(_type, _name, _domain) \
|
||||
enum _name : _type _name; enum __attribute__((ns_error_domain(_domain))) _name : _type
|
||||
|
||||
extern NSString *const MyErrorDomain;
|
||||
typedef NS_ERROR_ENUM(unsigned char, MyErrorEnum, MyErrorDomain) {
|
||||
MyErrFirst,
|
||||
MyErrSecond,
|
||||
};
|
||||
}];
|
||||
}
|
||||
|
||||
def OMPDeclareSimdDocs : Documentation {
|
||||
let Category = DocCatFunction;
|
||||
let Heading = "#pragma omp declare simd";
|
||||
|
|
|
@ -9476,6 +9476,11 @@ 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_invalid_decl : Error<
|
||||
"domain argument %select{|%1 }0does not refer to global constant">;
|
||||
def err_nserrordomain_wrong_type : Error<
|
||||
"domain argument %0 does not point to an NSString constant">;
|
||||
|
||||
def warn_nsconsumed_attribute_mismatch : Warning<
|
||||
err_nsconsumed_attribute_mismatch.Text>, InGroup<NSConsumedMismatch>;
|
||||
def warn_nsreturns_retained_attribute_mismatch : Warning<
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#include "clang/AST/ExprCXX.h"
|
||||
#include "clang/AST/Mangle.h"
|
||||
#include "clang/AST/RecursiveASTVisitor.h"
|
||||
#include "clang/AST/Type.h"
|
||||
#include "clang/Basic/CharInfo.h"
|
||||
#include "clang/Basic/SourceManager.h"
|
||||
#include "clang/Basic/TargetBuiltins.h"
|
||||
|
@ -30,12 +31,15 @@
|
|||
#include "clang/Sema/DelayedDiagnostic.h"
|
||||
#include "clang/Sema/Initialization.h"
|
||||
#include "clang/Sema/Lookup.h"
|
||||
#include "clang/Sema/ParsedAttr.h"
|
||||
#include "clang/Sema/Scope.h"
|
||||
#include "clang/Sema/ScopeInfo.h"
|
||||
#include "clang/Sema/SemaInternal.h"
|
||||
#include "llvm/ADT/Optional.h"
|
||||
#include "llvm/ADT/STLExtras.h"
|
||||
#include "llvm/ADT/StringExtras.h"
|
||||
#include "llvm/Support/MathExtras.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
|
||||
using namespace clang;
|
||||
using namespace sema;
|
||||
|
@ -5322,6 +5326,30 @@ static void handleObjCRequiresSuperAttr(Sema &S, Decl *D,
|
|||
D->addAttr(::new (S.Context) ObjCRequiresSuperAttr(S.Context, Attrs));
|
||||
}
|
||||
|
||||
static void handleNSErrorDomain(Sema &S, Decl *D, const ParsedAttr &AL) {
|
||||
auto *E = AL.getArgAsExpr(0);
|
||||
auto Loc = E ? E->getBeginLoc() : AL.getLoc();
|
||||
|
||||
auto *DRE = dyn_cast<DeclRefExpr>(AL.getArgAsExpr(0));
|
||||
if (!DRE) {
|
||||
S.Diag(Loc, diag::err_nserrordomain_invalid_decl) << 0;
|
||||
return;
|
||||
}
|
||||
|
||||
auto *VD = dyn_cast<VarDecl>(DRE->getDecl());
|
||||
if (!VD) {
|
||||
S.Diag(Loc, diag::err_nserrordomain_invalid_decl) << 1 << DRE->getDecl();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isNSStringType(VD->getType(), S.Context)) {
|
||||
S.Diag(Loc, diag::err_nserrordomain_wrong_type) << VD;
|
||||
return;
|
||||
}
|
||||
|
||||
D->addAttr(::new (S.Context) NSErrorDomainAttr(S.Context, AL, VD));
|
||||
}
|
||||
|
||||
static void handleObjCBridgeAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
|
||||
IdentifierLoc *Parm = AL.isArgIdent(0) ? AL.getArgAsIdent(0) : nullptr;
|
||||
|
||||
|
@ -7093,6 +7121,9 @@ static void ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D,
|
|||
case ParsedAttr::AT_ObjCBoxable:
|
||||
handleObjCBoxable(S, D, AL);
|
||||
break;
|
||||
case ParsedAttr::AT_NSErrorDomain:
|
||||
handleNSErrorDomain(S, D, AL);
|
||||
break;
|
||||
case ParsedAttr::AT_CFAuditedTransfer:
|
||||
handleSimpleAttributeWithExclusions<CFAuditedTransferAttr,
|
||||
CFUnknownTransferAttr>(S, D, AL);
|
||||
|
|
|
@ -15,3 +15,14 @@ using C = int (*)() [[gnu::cdecl]];
|
|||
int fun_asm() asm("test");
|
||||
// CHECK: int var_asm asm("test");
|
||||
int var_asm asm("test");
|
||||
|
||||
|
||||
@interface NSString
|
||||
@end
|
||||
|
||||
extern NSString *const MyErrorDomain;
|
||||
// CHECK: enum __attribute__((ns_error_domain(MyErrorDomain))) MyErrorEnum {
|
||||
enum __attribute__((ns_error_domain(MyErrorDomain))) MyErrorEnum {
|
||||
MyErrFirst,
|
||||
MyErrSecond,
|
||||
};
|
||||
|
|
|
@ -80,6 +80,7 @@
|
|||
// CHECK-NEXT: MipsShortCall (SubjectMatchRule_function)
|
||||
// CHECK-NEXT: NSConsumed (SubjectMatchRule_variable_is_parameter)
|
||||
// CHECK-NEXT: NSConsumesSelf (SubjectMatchRule_objc_method)
|
||||
// CHECK-NEXT: NSErrorDomain (SubjectMatchRule_enum)
|
||||
// CHECK-NEXT: Naked (SubjectMatchRule_function)
|
||||
// CHECK-NEXT: NoBuiltin (SubjectMatchRule_function)
|
||||
// CHECK-NEXT: NoCommon (SubjectMatchRule_variable)
|
||||
|
|
|
@ -0,0 +1,66 @@
|
|||
// RUN: %clang_cc1 -verify %s -x objective-c
|
||||
// RUN: %clang_cc1 -verify %s -x objective-c++
|
||||
|
||||
|
||||
#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,
|
||||
};
|
||||
|
||||
@interface NSString
|
||||
@end
|
||||
|
||||
extern NSString *const MyErrorDomain;
|
||||
typedef NS_ERROR_ENUM(unsigned char, MyErrorEnum, MyErrorDomain) {
|
||||
MyErrFirst,
|
||||
MyErrSecond,
|
||||
};
|
||||
|
||||
typedef NSString *const NsErrorDomain;
|
||||
extern NsErrorDomain MyTypedefErrorDomain;
|
||||
typedef NS_ERROR_ENUM(unsigned char, MyTypedefErrorEnum, MyTypedefErrorDomain) {
|
||||
MyTypedefErrFirst,
|
||||
MyTypedefErrSecond,
|
||||
};
|
||||
|
||||
extern char *const WrongErrorDomainType;
|
||||
enum __attribute__((ns_error_domain(WrongErrorDomainType))) MyWrongErrorDomainType { MyWrongErrorDomain };
|
||||
// expected-error@-1{{domain argument 'WrongErrorDomainType' does not point to an NSString constant}}
|
||||
|
||||
struct __attribute__((ns_error_domain(MyErrorDomain))) MyStructWithErrorDomain {};
|
||||
// expected-error@-1{{'ns_error_domain' attribute only applies to enums}}
|
||||
|
||||
int __attribute__((ns_error_domain(MyErrorDomain))) NotTagDecl;
|
||||
// expected-error@-1{{'ns_error_domain' attribute only applies to enums}}
|
||||
|
||||
enum __attribute__((ns_error_domain())) NoArg { NoArgError };
|
||||
// expected-error@-1{{'ns_error_domain' attribute takes one argument}}
|
||||
|
||||
enum __attribute__((ns_error_domain(MyErrorDomain, MyErrorDomain))) TwoArgs { TwoArgsError };
|
||||
// expected-error@-1{{'ns_error_domain' attribute takes one argument}}
|
||||
|
||||
typedef NS_ERROR_ENUM(unsigned char, MyErrorEnumInvalid, InvalidDomain) {
|
||||
// expected-error@-1{{use of undeclared identifier 'InvalidDomain'}}
|
||||
MyErrFirstInvalid,
|
||||
MyErrSecondInvalid,
|
||||
};
|
||||
|
||||
typedef NS_ERROR_ENUM(unsigned char, MyErrorEnumInvalid, "domain-string");
|
||||
// expected-error@-1{{domain argument does not refer to global constant}}
|
||||
|
||||
void foo() {}
|
||||
typedef NS_ERROR_ENUM(unsigned char, MyErrorEnumInvalidFunction, foo);
|
||||
// expected-error@-1{{domain argument 'foo' does not refer to global constant}}
|
|
@ -329,6 +329,8 @@ namespace {
|
|||
// empty string but are then recorded as a nullptr.
|
||||
OS << "\" << (get" << getUpperName() << "() ? get" << getUpperName()
|
||||
<< "()->getName() : \"\") << \"";
|
||||
else if (type == "VarDecl *")
|
||||
OS << "\" << get" << getUpperName() << "()->getName() << \"";
|
||||
else if (type == "TypeSourceInfo *")
|
||||
OS << "\" << get" << getUpperName() << "().getAsString() << \"";
|
||||
else if (type == "ParamIdx")
|
||||
|
|
Loading…
Reference in New Issue