let Documentation = [SwiftAsyncDocs];
}
+def SwiftAsyncError : InheritableAttr {
+ let Spellings = [Clang<"swift_async_error">];
+ let Subjects = SubjectList<[Function, ObjCMethod]>;
+ let Args = [EnumArgument<"Convention", "ConventionKind",
+ ["none", "nonnull_error", "zero_argument", "nonzero_argument"],
+ ["None", "NonNullError", "ZeroArgument", "NonZeroArgument"]>,
+ UnsignedArgument<"HandlerParamIdx", /*opt=*/1>];
+ let Documentation = [SwiftAsyncErrorDocs];
+}
+
def Suppress : StmtAttr {
let Spellings = [CXX11<"gsl", "suppress">];
let Args = [VariadicStringArgument<"DiagnosticIdentifiers">];
}];
}
+def SwiftAsyncErrorDocs : Documentation {
+ let Category = SwiftDocs;
+ let Heading = "swift_async_error";
+ let Content = [{
+The ``swift_async_error`` attribute specifies how an error state will be
+represented in a swift async method. It's a bit analogous to the ``swift_error``
+attribute for the generated async method. The ``swift_async_error`` attribute
+can indicate a variety of different ways of representing an error.
+
+- ``__attribute__((swift_async_error(zero_argument, N)))``, specifies that the
+ async method is considered to have failed if the Nth argument to the
+ completion handler is zero.
+
+- ``__attribute__((swift_async_error(nonzero_argument, N)))``, specifies that
+ the async method is considered to have failed if the Nth argument to the
+ completion handler is non-zero.
+
+- ``__attribute__((swift_async_error(nonnull_error)))``, specifies that the
+ async method is considered to have failed if the ``NSError *`` argument to the
+ completion handler is non-null.
+
+- ``__attribute__((swift_async_error(none)))``, specifies that the async method
+ cannot fail.
+
+
+For instance:
+
+.. code-block:: objc
+
+ @interface MyClass : NSObject
+ -(void)asyncMethod:(void (^)(char, int, float))handler
+ __attribute__((swift_async(swift_private, 1)))
+ __attribute__((swift_async_error(zero_argument, 2)));
+ @end
+
+Here, the ``swift_async`` attribute specifies that ``handler`` is the completion
+handler for this method, and the ``swift_async_error`` attribute specifies that
+the ``int`` parameter is the one that represents the error.
+}];
+}
+
def SuppressDocs : Documentation {
let Category = DocCatStmt;
let Content = [{
"'swift_async' completion handler parameter must have block type returning"
" 'void', type here is %0">;
+def err_swift_async_error_without_swift_async : Error<
+ "%0 attribute must be applied to a %select{function|method}1 annotated "
+ "with non-'none' attribute 'swift_async'">;
+def err_swift_async_error_no_error_parameter : Error<
+ "%0 attribute with 'nonnull_error' convention can only be applied to a "
+ "%select{function|method}1 with a completion handler with an error "
+ "parameter">;
+def err_swift_async_error_non_integral : Error<
+ "%0 attribute with '%1' convention must have an integral-typed parameter "
+ "in completion handler at index %2, type here is %3">;
+
def warn_ignored_objc_externally_retained : Warning<
"'objc_externally_retained' can only be applied to local variables "
"%select{of retainable type|with strong ownership}0">,
D->addAttr(::new (S.Context) SwiftErrorAttr(S.Context, AL, Convention));
}
+static void checkSwiftAsyncErrorBlock(Sema &S, Decl *D,
+ const SwiftAsyncErrorAttr *ErrorAttr,
+ const SwiftAsyncAttr *AsyncAttr) {
+ if (AsyncAttr->getKind() == SwiftAsyncAttr::None) {
+ if (ErrorAttr->getConvention() != SwiftAsyncErrorAttr::None) {
+ S.Diag(AsyncAttr->getLocation(),
+ diag::err_swift_async_error_without_swift_async)
+ << AsyncAttr << isa<ObjCMethodDecl>(D);
+ }
+ return;
+ }
+
+ const ParmVarDecl *HandlerParam = getFunctionOrMethodParam(
+ D, AsyncAttr->getCompletionHandlerIndex().getASTIndex());
+ // handleSwiftAsyncAttr already verified the type is correct, so no need to
+ // double-check it here.
+ const auto *FuncTy = HandlerParam->getType()
+ ->getAs<BlockPointerType>()
+ ->getPointeeType()
+ ->getAs<FunctionProtoType>();
+ ArrayRef<QualType> BlockParams;
+ if (FuncTy)
+ BlockParams = FuncTy->getParamTypes();
+
+ switch (ErrorAttr->getConvention()) {
+ case SwiftAsyncErrorAttr::ZeroArgument:
+ case SwiftAsyncErrorAttr::NonZeroArgument: {
+ uint32_t ParamIdx = ErrorAttr->getHandlerParamIdx();
+ if (ParamIdx == 0 || ParamIdx > BlockParams.size()) {
+ S.Diag(ErrorAttr->getLocation(),
+ diag::err_attribute_argument_out_of_bounds) << ErrorAttr << 2;
+ return;
+ }
+ QualType ErrorParam = BlockParams[ParamIdx - 1];
+ if (!ErrorParam->isIntegralType(S.Context)) {
+ StringRef ConvStr =
+ ErrorAttr->getConvention() == SwiftAsyncErrorAttr::ZeroArgument
+ ? "zero_argument"
+ : "nonzero_argument";
+ S.Diag(ErrorAttr->getLocation(), diag::err_swift_async_error_non_integral)
+ << ErrorAttr << ConvStr << ParamIdx << ErrorParam;
+ return;
+ }
+ break;
+ }
+ case SwiftAsyncErrorAttr::NonNullError: {
+ bool AnyErrorParams = false;
+ for (QualType Param : BlockParams) {
+ // Check for NSError *.
+ if (const auto *ObjCPtrTy = Param->getAs<ObjCObjectPointerType>()) {
+ if (const auto *ID = ObjCPtrTy->getInterfaceDecl()) {
+ if (ID->getIdentifier() == S.getNSErrorIdent()) {
+ AnyErrorParams = true;
+ break;
+ }
+ }
+ }
+ // Check for CFError *.
+ if (const auto *PtrTy = Param->getAs<PointerType>()) {
+ if (const auto *RT = PtrTy->getPointeeType()->getAs<RecordType>()) {
+ if (S.isCFError(RT->getDecl())) {
+ AnyErrorParams = true;
+ break;
+ }
+ }
+ }
+ }
+
+ if (!AnyErrorParams) {
+ S.Diag(ErrorAttr->getLocation(),
+ diag::err_swift_async_error_no_error_parameter)
+ << ErrorAttr << isa<ObjCMethodDecl>(D);
+ return;
+ }
+ break;
+ }
+ case SwiftAsyncErrorAttr::None:
+ break;
+ }
+}
+
+static void handleSwiftAsyncError(Sema &S, Decl *D, const ParsedAttr &AL) {
+ IdentifierLoc *IDLoc = AL.getArgAsIdent(0);
+ SwiftAsyncErrorAttr::ConventionKind ConvKind;
+ if (!SwiftAsyncErrorAttr::ConvertStrToConventionKind(IDLoc->Ident->getName(),
+ ConvKind)) {
+ S.Diag(AL.getLoc(), diag::warn_attribute_type_not_supported)
+ << AL << IDLoc->Ident;
+ return;
+ }
+
+ uint32_t ParamIdx = 0;
+ switch (ConvKind) {
+ case SwiftAsyncErrorAttr::ZeroArgument:
+ case SwiftAsyncErrorAttr::NonZeroArgument: {
+ if (!checkAttributeNumArgs(S, AL, 2))
+ return;
+
+ Expr *IdxExpr = AL.getArgAsExpr(1);
+ if (!checkUInt32Argument(S, AL, IdxExpr, ParamIdx))
+ return;
+ break;
+ }
+ case SwiftAsyncErrorAttr::NonNullError:
+ case SwiftAsyncErrorAttr::None: {
+ if (!checkAttributeNumArgs(S, AL, 1))
+ return;
+ break;
+ }
+ }
+
+ auto *ErrorAttr =
+ ::new (S.Context) SwiftAsyncErrorAttr(S.Context, AL, ConvKind, ParamIdx);
+ D->addAttr(ErrorAttr);
+
+ if (auto *AsyncAttr = D->getAttr<SwiftAsyncAttr>())
+ checkSwiftAsyncErrorBlock(S, D, ErrorAttr, AsyncAttr);
+}
+
// For a function, this will validate a compound Swift name, e.g.
// <code>init(foo:bar:baz:)</code> or <code>controllerForName(_:)</code>, and
// the function will output the number of parameter names, and whether this is a
}
}
- D->addAttr(::new (S.Context) SwiftAsyncAttr(S.Context, AL, Kind, Idx));
+ auto *AsyncAttr =
+ ::new (S.Context) SwiftAsyncAttr(S.Context, AL, Kind, Idx);
+ D->addAttr(AsyncAttr);
+
+ if (auto *ErrorAttr = D->getAttr<SwiftAsyncErrorAttr>())
+ checkSwiftAsyncErrorBlock(S, D, ErrorAttr, AsyncAttr);
}
//===----------------------------------------------------------------------===//
case ParsedAttr::AT_SwiftAsync:
handleSwiftAsyncAttr(S, D, AL);
break;
+ case ParsedAttr::AT_SwiftAsyncError:
+ handleSwiftAsyncError(S, D, AL);
+ break;
// XRay attributes.
case ParsedAttr::AT_XRayLogArgs:
// CHECK-NEXT: SetTypestate (SubjectMatchRule_function_is_member)
// CHECK-NEXT: SpeculativeLoadHardening (SubjectMatchRule_function, SubjectMatchRule_objc_method)
// CHECK-NEXT: SwiftAsync (SubjectMatchRule_function, SubjectMatchRule_objc_method)
+// CHECK-NEXT: SwiftAsyncError (SubjectMatchRule_function, SubjectMatchRule_objc_method)
// CHECK-NEXT: SwiftAsyncName (SubjectMatchRule_objc_method, SubjectMatchRule_function)
// CHECK-NEXT: SwiftBridgedTypedef (SubjectMatchRule_type_alias)
// CHECK-NEXT: SwiftContext (SubjectMatchRule_variable_is_parameter)
--- /dev/null
+// RUN: %clang_cc1 %s -fblocks -fsyntax-only -verify
+
+#define ASYNC(...) __attribute__((swift_async(__VA_ARGS__)))
+#define ASYNC_ERROR(...) __attribute__((swift_async_error(__VA_ARGS__)))
+
+ASYNC(swift_private, 1)
+ASYNC_ERROR(zero_argument, 1)
+void test_good(void (^handler)(int));
+
+ASYNC(swift_private, 2)
+ASYNC_ERROR(nonzero_argument, 2)
+void test_good2(double, void (^handler)(double, int, double));
+
+enum SomeEnum { SE_a, SE_b };
+
+ASYNC(swift_private, 1)
+ASYNC_ERROR(nonzero_argument, 1)
+void test_good3(void (^handler)(enum SomeEnum, double));
+
+ASYNC_ERROR(zero_argument, 1)
+ASYNC(swift_private, 1)
+void test_rev_order(void (^handler)(int));
+
+@class NSError;
+
+ASYNC(swift_private, 1)
+ASYNC_ERROR(nonnull_error)
+void test_nserror(void (^handler)(NSError *));
+
+typedef struct __attribute__((objc_bridge(NSError))) __CFError * CFErrorRef;
+
+ASYNC(swift_private, 1)
+ASYNC_ERROR(nonnull_error)
+void test_cferror(void (^handler)(CFErrorRef));
+
+ASYNC(swift_private, 1)
+ASYNC_ERROR(nonnull_error) // expected-error {{'swift_async_error' attribute with 'nonnull_error' convention can only be applied to a function with a completion handler with an error parameter}}
+void test_interror(void (^handler)(int));
+
+ASYNC(swift_private, 1)
+ASYNC_ERROR(zero_argument, 1) // expected-error {{'swift_async_error' attribute with 'zero_argument' convention must have an integral-typed parameter in completion handler at index 1, type here is 'double'}}
+void test_not_integral(void (^handler)(double));
+
+ASYNC(swift_private, 1)
+ASYNC_ERROR(none)
+void test_none(void (^)());
+
+ASYNC(none)
+ASYNC_ERROR(none)
+void test_double_none(void (^)());
+
+ASYNC(none)
+ASYNC_ERROR(none, 1) // expected-error {{'swift_async_error' attribute takes one argument}}
+void test_double_none_args();
+
+ASYNC(swift_private, 1)
+ASYNC_ERROR(nonnull_error, 1) // expected-error{{'swift_async_error' attribute takes one argument}}
+void test_args(void (^)(void));
+
+ASYNC(swift_private, 1)
+ASYNC_ERROR(zero_argument, 1, 1) // expected-error{{'swift_async_error' attribute takes no more than 2 arguments}}
+void test_args2(void (^)(int));
+
+ASYNC_ERROR(none) int x; // expected-warning{{'swift_async_error' attribute only applies to functions and Objective-C methods}}
+
+@interface ObjC
+-(void)m1:(void (^)(int))handler
+ ASYNC(swift_private, 1)
+ ASYNC_ERROR(zero_argument, 1);
+
+-(void)m2:(int)first withSecond:(void (^)(int))handler
+ ASYNC(swift_private, 2)
+ ASYNC_ERROR(nonzero_argument, 1);
+
+-(void)m3:(void (^)(void))block
+ ASYNC_ERROR(zero_argument, 1) // expected-error {{'swift_async_error' attribute parameter 2 is out of bounds}}
+ ASYNC(swift_private, 1);
+
+-(void)m4:(void (^)(double, int, float))handler
+ ASYNC(swift_private, 1)
+ ASYNC_ERROR(nonzero_argument, 1); // expected-error{{swift_async_error' attribute with 'nonzero_argument' convention must have an integral-typed parameter in completion handler at index 1, type here is 'double'}}
+
+-(void)m5:(void (^)(NSError *))handler
+ ASYNC(swift_private, 1)
+ ASYNC_ERROR(nonnull_error);
+
+-(void)m6:(void (^)(void *))handler
+ ASYNC(swift_private, 1)
+ ASYNC_ERROR(nonnull_error); // expected-error{{'swift_async_error' attribute with 'nonnull_error' convention can only be applied to a method with a completion handler with an error parameter}}
+@end
+
+// 'swift_error' and 'swift_async_error' are OK on one function.
+ASYNC(swift_private, 1)
+ASYNC_ERROR(nonnull_error)
+__attribute__((swift_error(nonnull_error)))
+void swift_error_and_swift_async_error(void (^handler)(NSError *), NSError **);
+
+@interface TestNoSwiftAsync
+// swift_async_error can make sense without swift_async.
+-(void)doAThingWithCompletion:(void (^)(NSError *))completion
+ ASYNC_ERROR(nonnull_error);
+@end