diff --git a/docs/website/mmp-errors.md b/docs/website/mmp-errors.md index 31797c6807..46581703ee 100644 --- a/docs/website/mmp-errors.md +++ b/docs/website/mmp-errors.md @@ -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 diff --git a/docs/website/mtouch-errors.md b/docs/website/mtouch-errors.md index dd2637214d..365e91aebf 100644 --- a/docs/website/mtouch-errors.md +++ b/docs/website/mtouch-errors.md @@ -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 diff --git a/src/ObjCRuntime/DynamicRegistrar.cs b/src/ObjCRuntime/DynamicRegistrar.cs index 203a795321..cbd783690a 100644 --- a/src/ObjCRuntime/DynamicRegistrar.cs +++ b/src/ObjCRuntime/DynamicRegistrar.cs @@ -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); diff --git a/src/ObjCRuntime/ErrorHelper.cs b/src/ObjCRuntime/ErrorHelper.cs index d3794baa8d..2b73157abf 100644 --- a/src/ObjCRuntime/ErrorHelper.cs +++ b/src/ObjCRuntime/ErrorHelper.cs @@ -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); diff --git a/src/ObjCRuntime/Registrar.cs b/src/ObjCRuntime/Registrar.cs index 50ac0a48ee..d91cf1180c 100644 --- a/src/ObjCRuntime/Registrar.cs +++ b/src/ObjCRuntime/Registrar.cs @@ -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 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 { diff --git a/tests/mtouch/RegistrarTest.cs b/tests/mtouch/RegistrarTest.cs index e281b21463..63168cc480 100644 --- a/tests/mtouch/RegistrarTest.cs +++ b/tests/mtouch/RegistrarTest.cs @@ -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); + } + } } } diff --git a/tools/common/ClassicStaticRegistrar.cs b/tools/common/ClassicStaticRegistrar.cs index ed9546682e..bb13b4ec24 100644 --- a/tools/common/ClassicStaticRegistrar.cs +++ b/tools/common/ClassicStaticRegistrar.cs @@ -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 (); diff --git a/tools/common/StaticRegistrar.cs b/tools/common/StaticRegistrar.cs index 6b9339be70..8b444790cb 100644 --- a/tools/common/StaticRegistrar.cs +++ b/tools/common/StaticRegistrar.cs @@ -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)