Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions src/rgen/Microsoft.Macios.Bindings.Analyzer/Resources.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 13 additions & 0 deletions src/rgen/Microsoft.Macios.Bindings.Analyzer/Resources.resx
Original file line number Diff line number Diff line change
Expand Up @@ -534,4 +534,17 @@
<value>Duplicated async name</value>
</data>

<!-- RBI0040 -->

<data name="RBI0040Description" xml:space="preserve">
<value>No result type or result type name was provided for an async method.</value>
</data>
<data name="RBI0040MessageFormat" xml:space="preserve">
<value>The method '{0}' was marked as async and has multiple parameters but does not provide a return type name, a nameless tuple will be generated for the async method</value>
<comment>{0} is the name of the async method.</comment>
</data>
<data name="RBI0040Title" xml:space="preserve">
<value>Not specified return type</value>
</data>

</root>
15 changes: 15 additions & 0 deletions src/rgen/Microsoft.Macios.Bindings.Analyzer/RgenDiagnostics.cs
Original file line number Diff line number Diff line change
Expand Up @@ -601,4 +601,19 @@ public static class RgenDiagnostics {
description: new LocalizableResourceString (nameof (Resources.RBI0039Description), Resources.ResourceManager,
typeof (Resources))
);

/// <summary>
/// Disgnostic descriptor for when a method marked as async has does not provide a return type or return type name.
/// </summary>
internal static readonly DiagnosticDescriptor RBI0040 = new (
"RBI0040",
new LocalizableResourceString (nameof (Resources.RBI0040Title), Resources.ResourceManager, typeof (Resources)),
new LocalizableResourceString (nameof (Resources.RBI0040MessageFormat), Resources.ResourceManager,
typeof (Resources)),
"Usage",
DiagnosticSeverity.Warning,
isEnabledByDefault: true,
description: new LocalizableResourceString (nameof (Resources.RBI0040Description), Resources.ResourceManager,
typeof (Resources))
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,23 @@ bool ValidAsyncMethods (Binding binding, RootContext context,
// add a new list with the current property
asyncMethodNames.Add (asyncMethodKey, [(currentMethod.Name, currentMethod.Location)]);
}

// add a extra validation, if current method has more than one parameter + the delegate and does
// not provide a return type, create a warning for the user to avoid the usage of a nameless
// tuple
if (currentMethod.Parameters.Length > 2 // more than one parameter + the delegate
&& currentMethod.ExportMethodData.ResultType.IsNullOrDefault // does not provide a return type
&& currentMethod.ExportMethodData.ResultTypeName is null // does not provide a return type name to generate
) {
// create warning to inform that a nameless tuple will be generated for the async method
// and that it is better to provide a return type name
builder.Add (Diagnostic.Create (
descriptor: RBI0040, // The method '{0}' was marked as async and has multiple parameters but does not provide a return type name, a nameless tuple will be generated for the async method
location: currentMethod.Location,
messageArgs: [
currentMethod.Name,
]));
}
}
} else {
// this is not an async method, but we need to check if it has a callback delegate parameter,
Expand Down Expand Up @@ -315,7 +332,7 @@ public ClassValidator ()

// validate async methods. This is a global strategy because it needs to look at all the methods in the binding
// are validated together so that async methods do not have the same names
AddGlobalStrategy ([RBI0035, RBI0036, RBI0037, RBI0038, RBI0039], ValidAsyncMethods);
AddGlobalStrategy ([RBI0035, RBI0036, RBI0037, RBI0038, RBI0039, RBI0040], ValidAsyncMethods);

// validate that strong delegates are not duplicated, this is only for weak properties
AddStrategy (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -818,6 +818,44 @@ public partial class TestClass{
DiagnosticSeverity.Warning,
"The method 'GetCount' was not marked as async but it can be"
];

// correct async method but we are missing the return type
yield return [
@"
#pragma warning disable APL0003

using System;
using System.Runtime.Versioning;
using AVFoundation;
using CoreGraphics;
using Foundation;
using ObjCBindings;
using ObjCRuntime;
using nfloat = System.Runtime.InteropServices.NFloat;

namespace TestNamespace;

[SupportedOSPlatform (""macos"")]
[SupportedOSPlatform (""ios"")]
[SupportedOSPlatform (""tvos"")]
[SupportedOSPlatform (""maccatalyst13.1"")]
[BindingType<Class>]
public partial class TestClass{

[SupportedOSPlatform (""ios"")]
[SupportedOSPlatform (""tvos"")]
[SupportedOSPlatform (""macos"")]
[SupportedOSPlatform (""maccatalyst13.1"")]
[Export<Method> (""count:"",
Flags = ObjCBindings.Method.Async,
MethodName = ""GetCountAsync"")]
public virtual partial void GetCount (int first, int second, Action callback);

}",
"RBI0040",
DiagnosticSeverity.Warning,
"The method 'GetCount' was marked as async and has multiple parameters but does not provide a return type name, a nameless tuple will be generated for the async method"
];
}

IEnumerator IEnumerable.GetEnumerator () => GetEnumerator ();
Expand Down Expand Up @@ -1044,6 +1082,80 @@ public partial class TestClass{
}",
"RBI0039",
];

// correct async method but with return type
yield return [
@"
#pragma warning disable APL0003

using System;
using System.Runtime.Versioning;
using AVFoundation;
using CoreGraphics;
using Foundation;
using ObjCBindings;
using ObjCRuntime;
using nfloat = System.Runtime.InteropServices.NFloat;

namespace TestNamespace;

[SupportedOSPlatform (""macos"")]
[SupportedOSPlatform (""ios"")]
[SupportedOSPlatform (""tvos"")]
[SupportedOSPlatform (""maccatalyst13.1"")]
[BindingType<Class>]
public partial class TestClass{

[SupportedOSPlatform (""ios"")]
[SupportedOSPlatform (""tvos"")]
[SupportedOSPlatform (""macos"")]
[SupportedOSPlatform (""maccatalyst13.1"")]
[Export<Method> (""count:"",
Flags = ObjCBindings.Method.Async,
ResultType = typeof ((int First, int Second)),
MethodName = ""GetCountAsync"")]
public virtual partial void GetCount (int first, int second, Action callback);

}",
"RBI0040",
];

// correct async method but with return type name
yield return [
@"
#pragma warning disable APL0003

using System;
using System.Runtime.Versioning;
using AVFoundation;
using CoreGraphics;
using Foundation;
using ObjCBindings;
using ObjCRuntime;
using nfloat = System.Runtime.InteropServices.NFloat;

namespace TestNamespace;

[SupportedOSPlatform (""macos"")]
[SupportedOSPlatform (""ios"")]
[SupportedOSPlatform (""tvos"")]
[SupportedOSPlatform (""maccatalyst13.1"")]
[BindingType<Class>]
public partial class TestClass{

[SupportedOSPlatform (""ios"")]
[SupportedOSPlatform (""tvos"")]
[SupportedOSPlatform (""macos"")]
[SupportedOSPlatform (""maccatalyst13.1"")]
[Export<Method> (""count:"",
Flags = ObjCBindings.Method.Async,
ResultTypeName = ""AsyncResult""
MethodName = ""GetCountAsync"")]
public virtual partial void GetCount (int first, int second, Action callback);

}",
"RBI0040",
];
}

IEnumerator IEnumerable.GetEnumerator () => GetEnumerator ();
Expand Down