Skip to content
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.

15 changes: 14 additions & 1 deletion src/rgen/Microsoft.Macios.Bindings.Analyzer/Resources.resx
Original file line number Diff line number Diff line change
Expand Up @@ -540,11 +540,24 @@
<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>
<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>

<!-- RBI0040 -->

<data name="RBI0041Description" xml:space="preserve">
<value>Protocol inline constructor is hidden.</value>
</data>
<data name="RBI0041MessageFormat" xml:space="preserve">
<value>The class '{0}' contains a constructor with the selector '{1}' that hides a inline constructor from protocol '{2}'</value>
<comment>{0} is the name of the class, {1} is the selector, {2} is the name of the protocol</comment>
</data>
<data name="RBI0041Title" xml:space="preserve">
<value>Protocol constructor overlap:</value>
</data>

</root>
17 changes: 16 additions & 1 deletion src/rgen/Microsoft.Macios.Bindings.Analyzer/RgenDiagnostics.cs
Original file line number Diff line number Diff line change
Expand Up @@ -603,7 +603,7 @@ public static class RgenDiagnostics {
);

/// <summary>
/// Disgnostic descriptor for when a method marked as async has does not provide a return type or return type name.
/// Diagnostic 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",
Expand All @@ -616,4 +616,19 @@ public static class RgenDiagnostics {
description: new LocalizableResourceString (nameof (Resources.RBI0040Description), Resources.ResourceManager,
typeof (Resources))
);

/// <summary>
/// Diagnostic descriptor for when a class inherits from UIView and is missing the initWithFrame: constructor.
/// </summary>
internal static readonly DiagnosticDescriptor RBI0041 = new (
"RBI0041",
new LocalizableResourceString (nameof (Resources.RBI0041Title), Resources.ResourceManager, typeof (Resources)),
new LocalizableResourceString (nameof (Resources.RBI0041MessageFormat), Resources.ResourceManager,
typeof (Resources)),
"Usage",
DiagnosticSeverity.Warning,
isEnabledByDefault: true,
description: new LocalizableResourceString (nameof (Resources.RBI0041Description), Resources.ResourceManager,
typeof (Resources))
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,42 @@ bool ValidAsyncMethods (Binding binding, RootContext context,
return diagnostics.Length == 0;
}

bool ValidProtocolInlineConstructors (Binding binding, RootContext context,
out ImmutableArray<Diagnostic> diagnostics, Location? location = null)
{
diagnostics = [];
// ensure that if there are any constructors that are going to be inlined from the protocols that they
// do not conflict with the constructors that are already defined in the class. This is a warning, and we only
// are about those constructors that have the same selectors. The user can disable the warning if he really has
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// are about those constructors that have the same selectors. The user can disable the warning if he really has
// warn about those constructors that have the same selectors. The user can disable the warning if he really has

var builder = ImmutableArray.CreateBuilder<Diagnostic> ();
// get all the selectors from the constructors defined in the class as well as the protocol ones and find
// the duplicates
var constructorSelectorsSet = binding.Constructors.ToDictionary (x => x.Selector!, x => x);
var duplicates = binding.ProtocolConstructors
.Where (x => x.Selector is not null && constructorSelectorsSet.ContainsKey (x.Selector))
.Select (x => (Selector: x.Selector!, Constructor: x))
.ToArray ();
if (duplicates.Length > 0) {
// we have duplicates, create a warning for each of them
foreach (var (selector, protocolConstructor) in duplicates) {
// use the class constructor location
var constructorLocation = constructorSelectorsSet.TryGetValue (selector, out var constructor)
? constructor.Location : location;
var protocolName = protocolConstructor.IsProtocolConstructor ? protocolConstructor.ProtocolType : "unknown";
builder.Add (Diagnostic.Create (
descriptor: RBI0041, // The class '{0}' contains a constructor with the selector '{1}' that hides a inline constructor from a protocol
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you include the name/location of the protocol that brings in the conflicting constructor?

location: constructorLocation,
messageArgs: [
binding.Name,
selector,
protocolName
]));
}
}
diagnostics = builder.ToImmutable ();
return diagnostics.Length == 0;
}

/// <summary>
/// Initializes a new instance of the <see cref="ClassValidator"/> class.
/// </summary>
Expand All @@ -332,6 +368,9 @@ public ClassValidator ()
// validate that the selectors are not duplicated, this includes properties and methods
AddGlobalStrategy ([RBI0034], SelectorsAreUnique);

// validate that we have the required constructors for certain base classes like UIView
AddGlobalStrategy ([RBI0041], ValidProtocolInlineConstructors);

// 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, RBI0040], ValidAsyncMethods);
Expand Down
Loading
Loading