Hi all,
A couple weeks ago, a change to improve the diagnostics of the ClangImporter was landed. The new diagnostics are hidden behind two flags, enabling lazily and eagerly triggered diagnostics respectively. I am proposing lazily triggered diagnostics be enabled by default and replacing the -enable-experimental-clang-importer-diagnostics
flag with a -disable-experimental-clang-importer-diagnostics
flag allowing users to opt out. Notably, these lazily triggered diagnostics are only produced in association with a Clang node that was referenced from Swift and failed to import. This has two consequences:
- No noise about unreferenced diagnostics will be produced.
- The diagnostics cannot be reasonably ignored, as they help explain a fatal compile error (reference to unimportable entity).
Examples
Below I present a series of examples that help illustrate the nature of these diagnostics. Here is a header from which I will attempt to import declarations into Swift. For each declaration, I have declared an "unreferenced" counterpart.
#include <Foundation.h>
// Unsupported built-in types
int unsupported_parameter_type(int param1, _Complex int param2);
_Complex int referenced_unsupported_return_type();
int unreferenced_unsupported_parameter_type(int param1, _Complex int param2);
_Complex int unsupported_return_type();
struct PartialImport {
int a;
int b;
int _Complex unsupported_field;
int _Complex unreferenced_unsupported_field;
};
struct PartialImport partialImport = {1, 2, 3, 4};
// Unsupported macros
#define INVALID_INTEGER_LITERAL_1 10_9
#define STRING_LITERAL "MyString"
#define FUNC_LIKE_MACRO() 0
#define UNREFERENCED_FUNC_LIKE_MACRO() 0
#define INVALID_ARITHMETIC_1 5 + INVALID_INTERGER_LITERAL_1
#define UNREFERENCED_INVALID_ARITHMETIC_1 5 + INVALID_INTERGER_LITERAL_1
#define INVALID_ARITHMETIC_2 1 + STRING_LITERAL
#define UNREFERENCED_INVALID_ARITHMETIC_2 1 + STRING_LITERAL
#define INVALID_ARITHMETIC_3 1 + 'c'
#define UNREFERENCED_INVALID_ARITHMETIC_3 1 + 'c'
#define INVALID_ARITHMETIC_4 3 % 2
#define UNREFERENCED_INVALID_ARITHMETIC_4 3 % 2
#define CHAR_LITERAL 'a'
#define UNREFERENCED_CHAR_LITERAL 'a'
#define UNSUPPORTED_1 FUNC_LIKE_MACRO()
#define UNREFERENCED_UNSUPPORTED_1 FUNC_LIKE_MACRO()
// Incomplete Objective-C entities
@class ForwardDeclaredInterface;
@protocol ForwardDeclaredProtocol;
@interface Bar : NSObject
@property id<ForwardDeclaredProtocol> propertyUsingAForwardDeclaredProtocol;
@property id<ForwardDeclaredProtocol> unreferencedPropertyUsingAForwardDeclaredProtocol;
@property ForwardDeclaredInterface* propertyUsingAForwardDeclaredInterface;
@property ForwardDeclaredInterface* unreferencedPropertyUsingAForwardDeclaredInterface;
- (NSObject<ForwardDeclaredProtocol> *) methodReturningForwardDeclaredProtocol;
- (NSObject<ForwardDeclaredProtocol> *) unreferencedMethodReturningForwardDeclaredProtocol;
- (ForwardDeclaredInterface *) methodReturningForwardDeclaredInterface;
- (ForwardDeclaredInterface *) unreferencedMethodReturningForwardDeclaredInterface;
- (int)methodTakingAForwardDeclaredProtocolAsAParameter:(id<ForwardDeclaredProtocol>)param1;
- (int)unreferencedMethodTakingAForwardDeclaredProtocolAsAParameter:(id<ForwardDeclaredProtocol>)param1;
- (int)methodTakingAForwardDeclaredInterfaceAsAParameter:(ForwardDeclaredInterface *)param1 andAnother:(ForwardDeclaredInterface *)param2;
- (int)unreferencedMethodTakingAForwardDeclaredInterfaceAsAParameter:(ForwardDeclaredInterface *)param1 andAnother:(ForwardDeclaredInterface *)param2;
@end
ForwardDeclaredInterface* CFunctionReturningAForwardDeclaredInterface();
ForwardDeclaredInterface* unreferencedCFunctionReturningAForwardDeclaredInterface();
void CFunctionTakingAForwardDeclaredInterfaceAsAParameter(ForwardDeclaredInterface* param1);
void unreferencedCFunctionTakingAForwardDeclaredInterfaceAsAParameter(ForwardDeclaredInterface* param1);
NSObject<ForwardDeclaredProtocol> *CFunctionReturningAForwardDeclaredProtocol();
NSObject<ForwardDeclaredProtocol> *unreferencedCFunctionReturningAForwardDeclaredProtocol();
void CFunctionTakingAForwardDeclaredProtocolAsAParameter(id<ForwardDeclaredProtocol> param1);
void unreferencedCFunctionTakingAForwardDeclaredProtocolAsAParameter(id<ForwardDeclaredProtocol> param1);
I created a small test ClangModule from this header and imported it into a Swift file. I attempt to reference
the declarations from Swift like so:
import Test
// Unsupported built-ins
unsupported_parameter_type(1,2)
unsupported_return_type()
unsupported_parameter_type(1,2)
unsupported_return_type()
let s: PartialImport
s.unsupported_field = 5
partialImport.unsupported_field = 5
// Unsupported macros
_ = FUNC_LIKE_MACRO()
_ = INVALID_ARITHMETIC_1
_ = INVALID_ARITHMETIC_2
_ = INVALID_ARITHMETIC_3
_ = INVALID_ARITHMETIC_4
_ = CHAR_LITERAL
UNSUPPORTED_1
// Incomplete Objective-C entities
let bar = Bar()
_ = bar.methodReturningForwardDeclaredInterface()
_ = bar.methodTakingAForwardDeclaredInterfaceAsAParameter(nil, andAnother: nil)
bar.propertyUsingAForwardDeclaredInterface = nil
_ = CFunctionReturningAForwardDeclaredInterface()
CFunctionTakingAForwardDeclaredInterfaceAsAParameter(nil)
_ = bar.methodReturningForwardDeclaredProtocol()
_ = bar.methodTakingAForwardDeclaredProtocolAsAParameter(nil)
bar.propertyUsingAForwardDeclaredProtocol = nil
_ = CFunctionReturningAForwardDeclaredProtocol()
CFunctionTakingAForwardDeclaredProtocolAsAParameter(nil)
When I typecheck the file, I get the following diagnostics:
/home/nuria/Git/swift-project/swift/scratch/test.h:4:1: warning: function 'unsupported_parameter_type' not imported
int unsupported_parameter_type(int param1, _Complex int param2);
^
/home/nuria/Git/swift-project/swift/scratch/test.h:4:44: note: parameter 'param2' not imported
int unsupported_parameter_type(int param1, _Complex int param2);
^
/home/nuria/Git/swift-project/swift/scratch/test.h:4:44: note: built-in type 'Complex' not supported
int unsupported_parameter_type(int param1, _Complex int param2);
^
test.swift:4:1: error: cannot find 'unsupported_parameter_type' in scope
unsupported_parameter_type(1,2)
^~~~~~~~~~~~~~~~~~~~~~~~~~
/home/nuria/Git/swift-project/swift/scratch/test.h:8:1: warning: function 'unsupported_return_type' not imported
_Complex int unsupported_return_type();
^
/home/nuria/Git/swift-project/swift/scratch/test.h:8:1: note: return type not imported
_Complex int unsupported_return_type();
^
/home/nuria/Git/swift-project/swift/scratch/test.h:8:1: note: built-in type 'Complex' not supported
_Complex int unsupported_return_type();
^
test.swift:5:1: error: cannot find 'unsupported_return_type' in scope
unsupported_return_type()
^~~~~~~~~~~~~~~~~~~~~~~
test.swift:6:1: error: cannot find 'unsupported_parameter_type' in scope
unsupported_parameter_type(1,2)
^~~~~~~~~~~~~~~~~~~~~~~~~~
test.swift:7:1: error: cannot find 'unsupported_return_type' in scope
unsupported_return_type()
^~~~~~~~~~~~~~~~~~~~~~~
/home/nuria/Git/swift-project/swift/scratch/test.h:13:3: warning: field 'unsupported_field' not imported
int _Complex unsupported_field;
^
/home/nuria/Git/swift-project/swift/scratch/test.h:13:3: note: built-in type 'Complex' not supported
int _Complex unsupported_field;
^
test.swift:10:3: error: value of type 'PartialImport' has no member 'unsupported_field'
s.unsupported_field = 5
~ ^~~~~~~~~~~~~~~~~
test.swift:11:15: error: value of type 'PartialImport' has no member 'unsupported_field'
partialImport.unsupported_field = 5
~~~~~~~~~~~~~ ^~~~~~~~~~~~~~~~~
/home/nuria/Git/swift-project/swift/scratch/test.h:23:9: error: macro 'FUNC_LIKE_MACRO' not imported: function like macros not supported
#define FUNC_LIKE_MACRO() 0
^
test.swift:15:5: error: cannot find 'FUNC_LIKE_MACRO' in scope
_ = FUNC_LIKE_MACRO()
^~~~~~~~~~~~~~~
/home/nuria/Git/swift-project/swift/scratch/test.h:26:9: error: macro 'INVALID_ARITHMETIC_1' not imported: structure not supported
#define INVALID_ARITHMETIC_1 5 + INVALID_INTERGER_LITERAL_1
^
test.swift:16:5: error: cannot find 'INVALID_ARITHMETIC_1' in scope
_ = INVALID_ARITHMETIC_1
^~~~~~~~~~~~~~~~~~~~
/home/nuria/Git/swift-project/swift/scratch/test.h:29:9: error: macro 'INVALID_ARITHMETIC_2' not imported: structure not supported
#define INVALID_ARITHMETIC_2 1 + STRING_LITERAL
^
test.swift:17:5: error: cannot find 'INVALID_ARITHMETIC_2' in scope
_ = INVALID_ARITHMETIC_2
^~~~~~~~~~~~~~~~~~~~
/home/nuria/Git/swift-project/swift/scratch/test.h:32:9: error: macro 'INVALID_ARITHMETIC_3' not imported: structure not supported
#define INVALID_ARITHMETIC_3 1 + 'c'
^
test.swift:18:5: error: cannot find 'INVALID_ARITHMETIC_3' in scope
_ = INVALID_ARITHMETIC_3
^~~~~~~~~~~~~~~~~~~~
/home/nuria/Git/swift-project/swift/scratch/test.h:35:9: error: macro 'INVALID_ARITHMETIC_4' not imported
#define INVALID_ARITHMETIC_4 3 % 2
^
/home/nuria/Git/swift-project/swift/scratch/test.h:35:32: note: operator '%' not supported in macro arithmetic
#define INVALID_ARITHMETIC_4 3 % 2
^
test.swift:19:5: error: cannot find 'INVALID_ARITHMETIC_4' in scope
_ = INVALID_ARITHMETIC_4
^~~~~~~~~~~~~~~~~~~~
/home/nuria/Git/swift-project/swift/scratch/test.h:38:9: error: macro 'CHAR_LITERAL' not imported
#define CHAR_LITERAL 'a'
^
/home/nuria/Git/swift-project/swift/scratch/test.h:38:22: note: only numeric and string macro literals supported
#define CHAR_LITERAL 'a'
^
test.swift:20:5: error: cannot find 'CHAR_LITERAL' in scope
_ = CHAR_LITERAL
^~~~~~~~~~~~
/home/nuria/Git/swift-project/swift/scratch/test.h:41:9: error: macro 'UNSUPPORTED_1' not imported: structure not supported
#define UNSUPPORTED_1 FUNC_LIKE_MACRO()
^
test.swift:21:1: error: cannot find 'UNSUPPORTED_1' in scope
UNSUPPORTED_1
^~~~~~~~~~~~~
/home/nuria/Git/swift-project/swift/scratch/test.h:55:1: warning: method 'methodReturningForwardDeclaredInterface' not imported
- (ForwardDeclaredInterface *) methodReturningForwardDeclaredInterface;
^
/home/nuria/Git/swift-project/swift/scratch/test.h:55:1: note: return type not imported
- (ForwardDeclaredInterface *) methodReturningForwardDeclaredInterface;
^
/home/nuria/Git/swift-project/swift/scratch/test.h:55:1: note: interface 'ForwardDeclaredInterface' is incomplete
- (ForwardDeclaredInterface *) methodReturningForwardDeclaredInterface;
^
/home/nuria/Git/swift-project/swift/scratch/test.h:45:1: note: interface 'ForwardDeclaredInterface' forward declared here
@class ForwardDeclaredInterface;
^
test.swift:25:9: error: value of type 'Bar' has no member 'methodReturningForwardDeclaredInterface'
_ = bar.methodReturningForwardDeclaredInterface()
~~~ ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/home/nuria/Git/swift-project/swift/scratch/test.h:59:1: warning: method 'methodTakingAForwardDeclaredInterfaceAsAParameter:andAnother:' not imported
- (int)methodTakingAForwardDeclaredInterfaceAsAParameter:(ForwardDeclaredInterface *)param1 andAnother:(ForwardDeclaredInterface *)param2;
^
/home/nuria/Git/swift-project/swift/scratch/test.h:59:59: note: parameter 'param1' not imported
- (int)methodTakingAForwardDeclaredInterfaceAsAParameter:(ForwardDeclaredInterface *)param1 andAnother:(ForwardDeclaredInterface *)param2;
^
/home/nuria/Git/swift-project/swift/scratch/test.h:59:59: note: interface 'ForwardDeclaredInterface' is incomplete
- (int)methodTakingAForwardDeclaredInterfaceAsAParameter:(ForwardDeclaredInterface *)param1 andAnother:(ForwardDeclaredInterface *)param2;
^
/home/nuria/Git/swift-project/swift/scratch/test.h:45:1: note: interface 'ForwardDeclaredInterface' forward declared here
@class ForwardDeclaredInterface;
^
test.swift:26:9: error: value of type 'Bar' has no member 'methodTakingAForwardDeclaredInterfaceAsAParameter'
_ = bar.methodTakingAForwardDeclaredInterfaceAsAParameter(nil as Any?, andAnother: nil as Any?)
~~~ ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/home/nuria/Git/swift-project/swift/scratch/test.h:51:1: warning: property 'propertyUsingAForwardDeclaredInterface' not imported
@property ForwardDeclaredInterface* propertyUsingAForwardDeclaredInterface;
^
/home/nuria/Git/swift-project/swift/scratch/test.h:51:1: note: interface 'ForwardDeclaredInterface' is incomplete
@property ForwardDeclaredInterface* propertyUsingAForwardDeclaredInterface;
^
/home/nuria/Git/swift-project/swift/scratch/test.h:45:1: note: interface 'ForwardDeclaredInterface' forward declared here
@class ForwardDeclaredInterface;
^
test.swift:27:5: error: value of type 'Bar' has no member 'propertyUsingAForwardDeclaredInterface'
bar.propertyUsingAForwardDeclaredInterface = nil as Any?
~~~ ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/home/nuria/Git/swift-project/swift/scratch/test.h:63:1: warning: function 'CFunctionReturningAForwardDeclaredInterface' not imported
ForwardDeclaredInterface* CFunctionReturningAForwardDeclaredInterface();
^
/home/nuria/Git/swift-project/swift/scratch/test.h:63:1: note: return type not imported
ForwardDeclaredInterface* CFunctionReturningAForwardDeclaredInterface();
^
/home/nuria/Git/swift-project/swift/scratch/test.h:63:1: note: interface 'ForwardDeclaredInterface' is incomplete
ForwardDeclaredInterface* CFunctionReturningAForwardDeclaredInterface();
^
/home/nuria/Git/swift-project/swift/scratch/test.h:45:1: note: interface 'ForwardDeclaredInterface' forward declared here
@class ForwardDeclaredInterface;
^
test.swift:28:5: error: cannot find 'CFunctionReturningAForwardDeclaredInterface' in scope
_ = CFunctionReturningAForwardDeclaredInterface()
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/home/nuria/Git/swift-project/swift/scratch/test.h:65:1: warning: function 'CFunctionTakingAForwardDeclaredInterfaceAsAParameter' not imported
void CFunctionTakingAForwardDeclaredInterfaceAsAParameter(ForwardDeclaredInterface* param1);
^
/home/nuria/Git/swift-project/swift/scratch/test.h:65:59: note: parameter 'param1' not imported
void CFunctionTakingAForwardDeclaredInterfaceAsAParameter(ForwardDeclaredInterface* param1);
^
/home/nuria/Git/swift-project/swift/scratch/test.h:65:59: note: interface 'ForwardDeclaredInterface' is incomplete
void CFunctionTakingAForwardDeclaredInterfaceAsAParameter(ForwardDeclaredInterface* param1);
^
/home/nuria/Git/swift-project/swift/scratch/test.h:45:1: note: interface 'ForwardDeclaredInterface' forward declared here
@class ForwardDeclaredInterface;
^
test.swift:29:1: error: cannot find 'CFunctionTakingAForwardDeclaredInterfaceAsAParameter' in scope
CFunctionTakingAForwardDeclaredInterfaceAsAParameter(nil as Any?)
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/home/nuria/Git/swift-project/swift/scratch/test.h:53:1: warning: method 'methodReturningForwardDeclaredProtocol' not imported
- (NSObject<ForwardDeclaredProtocol> *) methodReturningForwardDeclaredProtocol;
^
/home/nuria/Git/swift-project/swift/scratch/test.h:53:1: note: return type not imported
- (NSObject<ForwardDeclaredProtocol> *) methodReturningForwardDeclaredProtocol;
^
/home/nuria/Git/swift-project/swift/scratch/test.h:53:1: note: protocol 'ForwardDeclaredProtocol' is incomplete
- (NSObject<ForwardDeclaredProtocol> *) methodReturningForwardDeclaredProtocol;
^
/home/nuria/Git/swift-project/swift/scratch/test.h:46:1: note: protocol 'ForwardDeclaredProtocol' forward declared here
@protocol ForwardDeclaredProtocol;
^
test.swift:30:9: error: value of type 'Bar' has no member 'methodReturningForwardDeclaredProtocol'
_ = bar.methodReturningForwardDeclaredProtocol()
~~~ ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/home/nuria/Git/swift-project/swift/scratch/test.h:57:1: warning: method 'methodTakingAForwardDeclaredProtocolAsAParameter:' not imported
- (int)methodTakingAForwardDeclaredProtocolAsAParameter:(id<ForwardDeclaredProtocol>)param1;
^
/home/nuria/Git/swift-project/swift/scratch/test.h:57:58: note: parameter 'param1' not imported
- (int)methodTakingAForwardDeclaredProtocolAsAParameter:(id<ForwardDeclaredProtocol>)param1;
^
/home/nuria/Git/swift-project/swift/scratch/test.h:57:58: note: protocol 'ForwardDeclaredProtocol' is incomplete
- (int)methodTakingAForwardDeclaredProtocolAsAParameter:(id<ForwardDeclaredProtocol>)param1;
^
/home/nuria/Git/swift-project/swift/scratch/test.h:46:1: note: protocol 'ForwardDeclaredProtocol' forward declared here
@protocol ForwardDeclaredProtocol;
^
test.swift:31:9: error: value of type 'Bar' has no member 'methodTakingAForwardDeclaredProtocolAsAParameter'
_ = bar.methodTakingAForwardDeclaredProtocolAsAParameter(nil as Any?)
~~~ ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/home/nuria/Git/swift-project/swift/scratch/test.h:49:1: warning: property 'propertyUsingAForwardDeclaredProtocol' not imported
@property id<ForwardDeclaredProtocol> propertyUsingAForwardDeclaredProtocol;
^
/home/nuria/Git/swift-project/swift/scratch/test.h:49:1: note: protocol 'ForwardDeclaredProtocol' is incomplete
@property id<ForwardDeclaredProtocol> propertyUsingAForwardDeclaredProtocol;
^
/home/nuria/Git/swift-project/swift/scratch/test.h:46:1: note: protocol 'ForwardDeclaredProtocol' forward declared here
@protocol ForwardDeclaredProtocol;
^
test.swift:32:5: error: value of type 'Bar' has no member 'propertyUsingAForwardDeclaredProtocol'
bar.propertyUsingAForwardDeclaredProtocol = nil as Any?
~~~ ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/home/nuria/Git/swift-project/swift/scratch/test.h:68:1: warning: function 'CFunctionReturningAForwardDeclaredProtocol' not imported
NSObject<ForwardDeclaredProtocol> *CFunctionReturningAForwardDeclaredProtocol();
^
/home/nuria/Git/swift-project/swift/scratch/test.h:68:1: note: return type not imported
NSObject<ForwardDeclaredProtocol> *CFunctionReturningAForwardDeclaredProtocol();
^
/home/nuria/Git/swift-project/swift/scratch/test.h:68:1: note: protocol 'ForwardDeclaredProtocol' is incomplete
NSObject<ForwardDeclaredProtocol> *CFunctionReturningAForwardDeclaredProtocol();
^
/home/nuria/Git/swift-project/swift/scratch/test.h:46:1: note: protocol 'ForwardDeclaredProtocol' forward declared here
@protocol ForwardDeclaredProtocol;
^
test.swift:33:5: error: cannot find 'CFunctionReturningAForwardDeclaredProtocol' in scope
_ = CFunctionReturningAForwardDeclaredProtocol()
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/home/nuria/Git/swift-project/swift/scratch/test.h:70:1: warning: function 'CFunctionTakingAForwardDeclaredProtocolAsAParameter' not imported
void CFunctionTakingAForwardDeclaredProtocolAsAParameter(id<ForwardDeclaredProtocol> param1);
^
/home/nuria/Git/swift-project/swift/scratch/test.h:70:58: note: parameter 'param1' not imported
void CFunctionTakingAForwardDeclaredProtocolAsAParameter(id<ForwardDeclaredProtocol> param1);
^
/home/nuria/Git/swift-project/swift/scratch/test.h:70:58: note: protocol 'ForwardDeclaredProtocol' is incomplete
void CFunctionTakingAForwardDeclaredProtocolAsAParameter(id<ForwardDeclaredProtocol> param1);
^
/home/nuria/Git/swift-project/swift/scratch/test.h:46:1: note: protocol 'ForwardDeclaredProtocol' forward declared here
@protocol ForwardDeclaredProtocol;
^
test.swift:34:1: error: cannot find 'CFunctionTakingAForwardDeclaredProtocolAsAParameter' in scope
CFunctionTakingAForwardDeclaredProtocolAsAParameter(nil as Any?)
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Hopefully, this example is illustrative. Note that the "unreferenced" declarations are not diagnosed, and all diagnostics end in the existing fatal "declaration does not exist / not found in scope" error.
Action
If there are any reasons to keep these usage triggered diagnostics behind a flag, I would appreciate any input or feedback.
Asides
Flag Naming
These diagnostics are new and untested enough to warrant the experimental label, but at a certain point it feels odd to have a feature labelled experimental enabled by default. Perhaps, a name change is in order.