[registrar] Report a warning when the registrar export an abstract INativeObject type to Objective-C. (#6659)
* [registrar] Report a warning when the registrar export an abstract INativeObject type to Objective-C. Exporting abstract types to Objective-C can lead to problems when at runtime we're asked to create an instance of such a type (which we can't), so warn when this happens. This would have caught #6655, and the problems explained in #4969 as well. Since this may trigger for code that's currently working fine, I'm making it a warning instead of an error (which means adding some extra code to be able to easily report warnings from the generator code). * Don't assume a TypeReference can be successfully resolved every time.
This commit is contained in:
Родитель
14559e35bd
Коммит
15f0af7a1a
|
@ -583,6 +583,22 @@ complicated to get it right when doing it manually.
|
|||
|
||||
If this is not the case, please file a [bug report](https://github.com/xamarin/xamarin-macios/issues/new) with a test case.
|
||||
|
||||
### MM4178: The registrar found the abstract type '{type}' in the signature for '{member}'. Abstract types should not be used in the signature for a member exported to Objective-C.
|
||||
|
||||
This is a warning, indicating a potential problem where a method or property
|
||||
has a parameter or return type which is abstract. The potential problem occurs
|
||||
at runtime, when the Xamarin.Mac runtime may need to create an instance of
|
||||
such a type, which will fail if the type is abstract.
|
||||
|
||||
Possible solutions:
|
||||
|
||||
* Modify the signature in question to not use an abstract type.
|
||||
* Make the type not abstract.
|
||||
|
||||
If this is an API exposed by Xamarin, please file a new issue on
|
||||
[github](https://github.com/xamarin/xamarin-macios/issues/new), if it's a
|
||||
third-party binding, please contact the vendor.
|
||||
|
||||
## MM5xxx: GCC and toolchain
|
||||
|
||||
### MM51xx: compilation
|
||||
|
|
|
@ -2545,6 +2545,22 @@ If this is not the case, please file a [bug report](https://github.com/xamarin/x
|
|||
|
||||
The name of an Objective-C protocol can't contain certain characters which means that the `Adopts` attribute on the corresponding managed class can't have the `ProtocolType` parameter containing them. Please refer to the provided error message and fix accordingly.
|
||||
|
||||
### MT4178: The registrar found the abstract type '{type}' in the signature for '{member}'. Abstract types should not be used in the signature for a member exported to Objective-C.
|
||||
|
||||
This is a warning, indicating a potential problem where a method or property
|
||||
has a parameter or return type which is abstract. The potential problem occurs
|
||||
at runtime, when the Xamarin.iOS runtime may need to create an instance of
|
||||
such a type, which will fail if the type is abstract.
|
||||
|
||||
Possible solutions:
|
||||
|
||||
* Modify the signature in question to not use an abstract type.
|
||||
* Make the type not abstract.
|
||||
|
||||
If this is an API exposed by Xamarin, please file a new issue on
|
||||
[github](https://github.com/xamarin/xamarin-macios/issues/new), if it's a
|
||||
third-party binding, please contact the vendor.
|
||||
|
||||
# MT5xxx: GCC and toolchain error messages
|
||||
|
||||
### MT51xx: Compilation
|
||||
|
|
|
@ -524,18 +524,22 @@ namespace Registrar {
|
|||
return false;
|
||||
}
|
||||
|
||||
protected override Exception CreateException (int code, Exception innerException, MethodBase method, string message, params object[] args)
|
||||
protected override Exception CreateExceptionImpl (int code, bool error, Exception innerException, MethodBase method, string message, params object[] args)
|
||||
{
|
||||
// There doesn't seem to be a way to find the source code location
|
||||
// for the method using System.Reflection.
|
||||
return ErrorHelper.CreateError (code, innerException, message, args);
|
||||
if (error)
|
||||
return ErrorHelper.CreateError (code, innerException, message, args);
|
||||
return ErrorHelper.CreateWarning (code, innerException, message, args);
|
||||
}
|
||||
|
||||
protected override Exception CreateException (int code, Exception innerException, Type type, string message, params object [] args)
|
||||
protected override Exception CreateExceptionImpl (int code, bool error, Exception innerException, Type type, string message, params object [] args)
|
||||
{
|
||||
// There doesn't seem to be a way to find the source code location
|
||||
// for the method using System.Reflection.
|
||||
return ErrorHelper.CreateError (code, innerException, message, args);
|
||||
if (error)
|
||||
return ErrorHelper.CreateError (code, innerException, message, args);
|
||||
return ErrorHelper.CreateWarning (code, innerException, message, args);
|
||||
}
|
||||
|
||||
protected override string GetAssemblyQualifiedName (Type type)
|
||||
|
@ -635,6 +639,11 @@ namespace Registrar {
|
|||
return type.IsInterface;
|
||||
}
|
||||
|
||||
protected override bool IsAbstract (Type type)
|
||||
{
|
||||
return type.IsAbstract;
|
||||
}
|
||||
|
||||
protected override bool IsINativeObject (Type type)
|
||||
{
|
||||
return typeof (INativeObject).IsAssignableFrom (type);
|
||||
|
|
|
@ -278,6 +278,11 @@ namespace ObjCRuntime {
|
|||
return new ProductException (code, false, message, args);
|
||||
}
|
||||
|
||||
public static ProductException CreateWarning (int code, Exception innerException, string message, params object [] args)
|
||||
{
|
||||
return new ProductException (code, false, innerException, message, args);
|
||||
}
|
||||
|
||||
public static void Error (int code, Exception innerException, string message, params object[] args)
|
||||
{
|
||||
throw new ProductException (code, true, innerException, message, args);
|
||||
|
|
|
@ -1110,6 +1110,7 @@ namespace Registrar {
|
|||
protected abstract bool IsGenericType (TType type);
|
||||
protected abstract bool IsGenericMethod (TMethod method);
|
||||
protected abstract bool IsInterface (TType type);
|
||||
protected abstract bool IsAbstract (TType type);
|
||||
protected abstract TType GetGenericTypeDefinition (TType type);
|
||||
protected abstract bool VerifyIsConstrainedToNSObject (TType type, out TType constrained_type);
|
||||
protected abstract TType GetEnumUnderlyingType (TType type);
|
||||
|
@ -1120,8 +1121,8 @@ namespace Registrar {
|
|||
protected abstract bool IsSimulatorOrDesktop { get; }
|
||||
protected abstract bool Is64Bits { get; }
|
||||
protected abstract bool IsDualBuildImpl { get; }
|
||||
protected abstract Exception CreateException (int code, Exception innerException, TMethod method, string message, params object[] args);
|
||||
protected abstract Exception CreateException (int code, Exception innerException, TType type, string message, params object [] args);
|
||||
protected abstract Exception CreateExceptionImpl (int code, bool error, Exception innerException, TMethod method, string message, params object[] args);
|
||||
protected abstract Exception CreateExceptionImpl (int code, bool error, Exception innerException, TType type, string message, params object [] args);
|
||||
protected abstract string PlatformName { get; }
|
||||
public abstract TType FindType (TType relative, string @namespace, string name);
|
||||
protected abstract IEnumerable<TMethod> FindMethods (TType type, string name); // will return null if nothing was found
|
||||
|
@ -1343,43 +1344,78 @@ namespace Registrar {
|
|||
|
||||
protected Exception CreateException (int code, string message, params object[] args)
|
||||
{
|
||||
return CreateException (code, (TMethod)null, message, args);
|
||||
return CreateExceptionImpl (code, true, message, args);
|
||||
}
|
||||
|
||||
protected Exception CreateException (int code, TMethod method, string message, params object[] args)
|
||||
{
|
||||
return CreateException (code, null, method, message, args);
|
||||
return CreateExceptionImpl (code, true, method, message, args);
|
||||
}
|
||||
|
||||
protected Exception CreateException (int code, TProperty property, string message, params object[] args)
|
||||
{
|
||||
return CreateException (code, null, property, message, args);
|
||||
return CreateExceptionImpl (code, true, property, message, args);
|
||||
}
|
||||
|
||||
protected Exception CreateException (int code, TType type, string message, params object [] args)
|
||||
{
|
||||
return CreateException (code, null, type, message, args);
|
||||
return CreateExceptionImpl (code, true, type, message, args);
|
||||
}
|
||||
|
||||
protected Exception CreateException (int code, Exception innerException, TProperty property, string message, params object[] args)
|
||||
{
|
||||
if (property == null)
|
||||
return CreateException (code, innerException, (TMethod) null, message, args);
|
||||
var getter = GetGetMethod (property);
|
||||
if (getter != null)
|
||||
return CreateException (code, innerException, getter, message, args);
|
||||
return CreateException (code, innerException, GetSetMethod (property), message, args);
|
||||
return CreateExceptionImpl (code, true, innerException, property, message, args);
|
||||
}
|
||||
|
||||
Exception CreateException (int code, ObjCMember member, string message, params object[] args)
|
||||
Exception CreateExceptionImpl (int code, bool error, string message, params object [] args)
|
||||
{
|
||||
return CreateExceptionImpl (code, error, (TMethod) null, message, args);
|
||||
}
|
||||
|
||||
Exception CreateExceptionImpl (int code, bool error, TMethod method, string message, params object [] args)
|
||||
{
|
||||
return CreateExceptionImpl (code, error, null, method, message, args);
|
||||
}
|
||||
|
||||
Exception CreateExceptionImpl (int code, bool error, TProperty property, string message, params object [] args)
|
||||
{
|
||||
return CreateExceptionImpl (code, error, null, property, message, args);
|
||||
}
|
||||
|
||||
Exception CreateExceptionImpl (int code, bool error, TType type, string message, params object [] args)
|
||||
{
|
||||
return CreateExceptionImpl (code, error, null, type, message, args);
|
||||
}
|
||||
|
||||
Exception CreateExceptionImpl (int code, bool error, Exception innerException, TProperty property, string message, params object [] args)
|
||||
{
|
||||
if (property == null)
|
||||
return CreateExceptionImpl (code, error, innerException, (TMethod) null, message, args);
|
||||
var getter = GetGetMethod (property);
|
||||
if (getter != null)
|
||||
return CreateExceptionImpl (code, error, innerException, getter, message, args);
|
||||
return CreateExceptionImpl (code, error, innerException, GetSetMethod (property), message, args);
|
||||
}
|
||||
|
||||
Exception CreateException (int code, ObjCMember member, string message, params object [] args)
|
||||
{
|
||||
return CreateExceptionImpl (code, true, member, message, args);
|
||||
}
|
||||
|
||||
Exception CreateExceptionImpl (int code, bool error, ObjCMember member, string message, params object[] args)
|
||||
{
|
||||
var method = member as ObjCMethod;
|
||||
if (method != null)
|
||||
return CreateException (code, method.Method, message, args);
|
||||
return CreateExceptionImpl (code, error, method.Method, message, args);
|
||||
var property = member as ObjCProperty;
|
||||
if (property != null)
|
||||
return CreateException (code, property.Property, message, args);
|
||||
return CreateException (code, message, args);
|
||||
return CreateExceptionImpl (code, error, property.Property, message, args);
|
||||
return CreateExceptionImpl (code, error, message, args);
|
||||
}
|
||||
|
||||
Exception CreateWarning (int code, ObjCMember member, string message, params object [] args)
|
||||
{
|
||||
return CreateExceptionImpl (code, false, member, message, args);
|
||||
}
|
||||
|
||||
protected string GetDescriptiveMethodName (TMethod method)
|
||||
|
@ -2614,6 +2650,8 @@ namespace Registrar {
|
|||
return "#";
|
||||
|
||||
if (IsINativeObject (type)) {
|
||||
if (!IsGenericType (type) && !IsInterface (type) && !IsNSObject (type) && IsAbstract (type))
|
||||
ErrorHelper.Show (CreateWarning (4178, member, $"The registrar found the abstract type '{type.FullName}' in the signature for '{member.FullName}'. Abstract types should not be used in the signature for a member exported to Objective-C."));
|
||||
if (IsNSObject (type) && forProperty) {
|
||||
return "@\"" + GetExportedTypeName (type) + "\"";
|
||||
} else {
|
||||
|
|
|
@ -1794,6 +1794,55 @@ class CTP4 : CTP3 {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void MT4178 ()
|
||||
{
|
||||
using (var mtouch = new MTouchTool ()) {
|
||||
var code = @"
|
||||
using System;
|
||||
|
||||
using Foundation;
|
||||
using ObjCRuntime;
|
||||
|
||||
abstract class SomeNativeObject : INativeObject
|
||||
{
|
||||
public IntPtr Handle { get; set; }
|
||||
SomeNativeObject (IntPtr handle, bool owns) { }
|
||||
}
|
||||
|
||||
class C : NSObject {
|
||||
[Export (""M1:"")]
|
||||
public void M1 (SomeNativeObject obj)
|
||||
{
|
||||
}
|
||||
|
||||
[Export (""M2"")]
|
||||
public SomeNativeObject M2 ()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
[Export (""M3"")]
|
||||
public SomeNativeObject M3 {
|
||||
get;
|
||||
set;
|
||||
}
|
||||
static void Main () {}
|
||||
}
|
||||
";
|
||||
|
||||
mtouch.CreateTemporaryApp (code: code, extraArg: "-debug");
|
||||
mtouch.Registrar = MTouchRegistrar.Static;
|
||||
mtouch.Linker = MTouchLinker.DontLink;
|
||||
mtouch.AssertExecute (MTouchAction.BuildSim, "build");
|
||||
mtouch.AssertWarningCount (4);
|
||||
mtouch.AssertWarning (4178, $"The registrar found the abstract type 'SomeNativeObject' in the signature for 'C.get_M3'. Abstract types should not be used in the signature for a member exported to Objective-C.", "testApp.cs", 27);
|
||||
mtouch.AssertWarning (4178, $"The registrar found the abstract type 'SomeNativeObject' in the signature for 'C.set_M3'. Abstract types should not be used in the signature for a member exported to Objective-C.", "testApp.cs", 28);
|
||||
mtouch.AssertWarning (4178, $"The registrar found the abstract type 'SomeNativeObject' in the signature for 'C.M1'. Abstract types should not be used in the signature for a member exported to Objective-C.", "testApp.cs", 16);
|
||||
mtouch.AssertWarning (4178, $"The registrar found the abstract type 'SomeNativeObject' in the signature for 'C.M2'. Abstract types should not be used in the signature for a member exported to Objective-C.", "testApp.cs", 21);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -616,14 +616,14 @@ namespace Registrar {
|
|||
}
|
||||
}
|
||||
|
||||
protected override Exception CreateException (int code, Exception innerException, MethodDefinition method, string message, params object[] args)
|
||||
protected override Exception CreateExceptionImpl (int code, bool error, Exception innerException, MethodDefinition method, string message, params object[] args)
|
||||
{
|
||||
return ErrorHelper.CreateError (App, code, innerException, method, message, args);
|
||||
return ErrorHelper.Create (App, code, error, innerException, method, message, args);
|
||||
}
|
||||
|
||||
protected override Exception CreateException (int code, Exception innerException, TypeReference type, string message, params object [] args)
|
||||
protected override Exception CreateExceptionImpl (int code, bool error, Exception innerException, TypeReference type, string message, params object [] args)
|
||||
{
|
||||
return ErrorHelper.CreateError (App, code, innerException, type, message, args);
|
||||
return ErrorHelper.Create (App, code, error, innerException, type, message, args);
|
||||
}
|
||||
|
||||
protected override bool ContainsPlatformReference (AssemblyDefinition assembly)
|
||||
|
@ -907,6 +907,11 @@ namespace Registrar {
|
|||
return type.Resolve ().IsInterface;
|
||||
}
|
||||
|
||||
protected override bool IsAbstract (TypeReference type)
|
||||
{
|
||||
return type.Resolve ().IsAbstract;
|
||||
}
|
||||
|
||||
protected override TypeReference[] GetInterfaces (TypeReference type)
|
||||
{
|
||||
var td = type.Resolve ();
|
||||
|
|
|
@ -805,16 +805,16 @@ namespace Registrar {
|
|||
}
|
||||
}
|
||||
|
||||
protected override Exception CreateException (int code, Exception innerException, MethodDefinition method, string message, params object[] args)
|
||||
protected override Exception CreateExceptionImpl (int code, bool error, Exception innerException, MethodDefinition method, string message, params object[] args)
|
||||
{
|
||||
return ErrorHelper.CreateError (App, code, innerException, method, message, args);
|
||||
return ErrorHelper.Create (App, code, error, innerException, method, message, args);
|
||||
}
|
||||
|
||||
protected override Exception CreateException (int code, Exception innerException, TypeReference type, string message, params object [] args)
|
||||
protected override Exception CreateExceptionImpl (int code, bool error, Exception innerException, TypeReference type, string message, params object [] args)
|
||||
{
|
||||
return ErrorHelper.CreateError (App, code, innerException, type, message, args);
|
||||
return ErrorHelper.Create (App, code, error, innerException, type, message, args);
|
||||
}
|
||||
|
||||
|
||||
protected override bool ContainsPlatformReference (AssemblyDefinition assembly)
|
||||
{
|
||||
if (assembly.Name.Name == PlatformAssembly)
|
||||
|
@ -1099,7 +1099,16 @@ namespace Registrar {
|
|||
|
||||
protected override bool IsInterface (TypeReference type)
|
||||
{
|
||||
return type.Resolve ().IsInterface;
|
||||
if (type.IsArray)
|
||||
return false;
|
||||
return type.Resolve ()?.IsInterface == true;
|
||||
}
|
||||
|
||||
protected override bool IsAbstract (TypeReference type)
|
||||
{
|
||||
if (type.IsArray)
|
||||
return false;
|
||||
return type.Resolve ()?.IsAbstract == true;
|
||||
}
|
||||
|
||||
protected override TypeReference[] GetInterfaces (TypeReference type)
|
||||
|
|
Загрузка…
Ссылка в новой задаче