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 @@ -430,4 +430,17 @@
<value>Wrong property declaration</value>
</data>

<!-- RBI0032 -->

<data name="RBI0032Description" xml:space="preserve">
<value>A weak delegate has to have a strong delegate name if it does not start with 'Weak'.</value>
</data>
<data name="RBI0032MessageFormat" xml:space="preserve">
<value>The weak delegate '{0}' is missing a strong delegate name, provide one or use the 'Weak' prefix</value>
<comment>{0} is the name of the property that does not start with 'Weak'.</comment>
</data>
<data name="RBI0032Title" xml:space="preserve">
<value>Wrong weak delegate</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 @@ -481,4 +481,19 @@ public static class RgenDiagnostics {
description: new LocalizableResourceString (nameof (Resources.RBI0031Description), Resources.ResourceManager,
typeof (Resources))
);

/// <summary>
/// Diagnostic descriptor for when a weak delegate does not start with "Weak".
/// </summary>
internal static readonly DiagnosticDescriptor RBI0032 = new (
"RBI0032",
new LocalizableResourceString (nameof (Resources.RBI0032Title), Resources.ResourceManager, typeof (Resources)),
new LocalizableResourceString (nameof (Resources.RBI0032MessageFormat), Resources.ResourceManager,
typeof (Resources)),
"Usage",
DiagnosticSeverity.Warning,
isEnabledByDefault: true,
description: new LocalizableResourceString (nameof (Resources.RBI0032Description), Resources.ResourceManager,
typeof (Resources))
);
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using System;
using System.Collections.Immutable;
using Microsoft.CodeAnalysis;
using Microsoft.Macios.Generator.Context;
Expand Down Expand Up @@ -70,6 +71,32 @@ internal static bool AccessorIsValid ((string PropertyName, Accessor Accessor) d
out diagnostics);
}

/// <summary>
/// Validates that a weak delegate property's name starts with "Weak" or that it has a corresponding strong delegate name defined.
/// </summary>
/// <param name="data">The property to validate.</param>
/// <param name="context">The root context for validation.</param>
/// <param name="diagnostic">When this method returns, contains a diagnostic if the property name is invalid; otherwise, an empty array.</param>
/// <param name="location">The code location to be used for the diagnostics.</param>
/// <returns><c>true</c> if the property is not a weak delegate or if its name is valid; otherwise, <c>false</c>.</returns>
internal static bool WeakPropertyNameStartsWithWeak (Property data, RootContext context,
out ImmutableArray<Diagnostic> diagnostic, Location? location)
{
diagnostic = ImmutableArray<Diagnostic>.Empty;
if (data.IsWeakDelegate
&& !data.Name.StartsWith ("Weak", StringComparison.Ordinal)
&& string.IsNullOrEmpty (data.ExportPropertyData.StrongDelegateName)) {
// if the property is weak, it must start with Weak
diagnostic = [Diagnostic.Create (
descriptor: RBI0032,
location: data.Location,
messageArgs: data.Name)];
return false;
}
// we do not care about non-weak properties
return true;
}

/// <summary>
/// Initializes a new instance of the <see cref="PropertyValidator"/> class.
/// </summary>
Expand All @@ -96,5 +123,10 @@ public PropertyValidator () : base (p => p.Location)
descriptor: [RBI0018, RBI0019, RBI0029],
validation: AccessorIsValid,
propertyName: "setter");

// if a property is weak ensure that it starts the name with Weak or set the strondelegatename in the
// export property data
AddGlobalStrategy (RBI0032, WeakPropertyNameStartsWithWeak);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -152,4 +152,12 @@ public void ValidateValidPropertyTests ()
[Fact]
public void ValidateValidFieldPropertyTests ()
=> testLogic.ValidateValidFieldPropertyTestsImpl ();

[Theory]
[InlineData ("MyProperty", false, null, 0)] // Not weak, should pass
[InlineData ("WeakMyProperty", true, null, 0)] // Weak, starts with "Weak", should pass
[InlineData ("MyProperty", true, "StrongDelegateName", 0)] // Weak, doesn't start with "Weak", but has StrongDelegateName, should pass
[InlineData ("MyProperty", true, null, 1)] // Weak, doesn't start with "Weak", no StrongDelegateName, should fail
public void WeakPropertyNameStartsWithWeakTests (string propertyName, bool isWeak, string? strongDelegateName, int expectedDiagnosticsCount)
=> testLogic.WeakPropertyNameStartsWithWeakTestsImpl (propertyName, isWeak, strongDelegateName, expectedDiagnosticsCount);
}
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,8 @@ Property CreateProperty (string name = "TestProperty",
string? getterSelector = null,
string? setterSelector = null,
bool hasGetter = true,
bool hasSetter = true)
bool hasSetter = true,
string? strongDelegateName = null)
{
var modifiers = ImmutableArray.CreateBuilder<SyntaxToken> ();
modifiers.Add (SyntaxFactory.Token (SyntaxKind.PublicKeyword));
Expand Down Expand Up @@ -103,7 +104,9 @@ Property CreateProperty (string name = "TestProperty",
accessors: accessors.ToImmutable ()
) {
ExportPropertyData = new ExportData<ObjCBindings.Property> (propertySelector,
argumentSemantic, propertyFlags)
argumentSemantic, propertyFlags) {
StrongDelegateName = strongDelegateName
}
};
}

Expand Down Expand Up @@ -318,5 +321,17 @@ public void ValidateValidFieldPropertyTestsImpl ()
var result = validator.ValidateAll (property, context);
Assert.Empty (result);
}
}

public void WeakPropertyNameStartsWithWeakTestsImpl (string propertyName, bool isWeak, string? strongDelegateName, int expectedDiagnosticsCount)
{
var flags = isWeak ? ObjCBindings.Property.WeakDelegate : ObjCBindings.Property.Default;
var property = CreateProperty (name: propertyName, propertyFlags: flags, strongDelegateName: strongDelegateName);
var result = validator.ValidateAll (property, context);
var totalDiagnostics = result.Values.Sum (x => x.Count);
Assert.Equal (expectedDiagnosticsCount, totalDiagnostics);
if (expectedDiagnosticsCount > 0) {
var diagnostic = result.Values.SelectMany (x => x).First ();
Assert.Equal ("RBI0032", diagnostic.Id);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -88,4 +88,12 @@ public void PropertyAccessorPresenceTests (bool hasGetter, bool hasSetter, int e
[InlineData (false, "invalid getter", "invalid setter:", 3)] // Not partial and multiple issues
public void CombinedValidationTests (bool isPartial, string? getterSelector, string? setterSelector, int expectedDiagnosticsCount)
=> testLogic.CombinedPropertyValidationTestsImpl (isPartial, getterSelector, setterSelector, expectedDiagnosticsCount);

[Theory]
[InlineData ("MyProperty", false, null, 0)] // Not weak, should pass
[InlineData ("WeakMyProperty", true, null, 0)] // Weak, starts with "Weak", should pass
[InlineData ("MyProperty", true, "StrongDelegateName", 0)] // Weak, doesn't start with "Weak", but has StrongDelegateName, should pass
[InlineData ("MyProperty", true, null, 1)] // Weak, doesn't start with "Weak", no StrongDelegateName, should fail
public void WeakPropertyNameStartsWithWeakTests (string propertyName, bool isWeak, string? strongDelegateName, int expectedDiagnosticsCount)
=> testLogic.WeakPropertyNameStartsWithWeakTestsImpl (propertyName, isWeak, strongDelegateName, expectedDiagnosticsCount);
}