From 077dc0c6a75db2bdcfb757e161e160454862da83 Mon Sep 17 00:00:00 2001 From: Manuel de la Pena Saenz Date: Wed, 10 Sep 2025 17:26:45 -0400 Subject: [PATCH 1/4] [RGen] If a user declares a class as a category it cannot have properties, events or constructors If a user makes a category that contains constructors, properties or evetns we raise an error. --- .../Resources.Designer.cs | 81 +++++++++++++++++++ .../Resources.resx | 39 +++++++++ .../RgenDiagnostics.cs | 45 +++++++++++ .../Validators/CategoryValidator.cs | 47 +++++++++++ .../CategoryAnalyzerTests.cs | 67 +++++++++++++++ 5 files changed, 279 insertions(+) diff --git a/src/rgen/Microsoft.Macios.Bindings.Analyzer/Resources.Designer.cs b/src/rgen/Microsoft.Macios.Bindings.Analyzer/Resources.Designer.cs index db8da0cbd3cd..5854f1a7568c 100644 --- a/src/rgen/Microsoft.Macios.Bindings.Analyzer/Resources.Designer.cs +++ b/src/rgen/Microsoft.Macios.Bindings.Analyzer/Resources.Designer.cs @@ -1271,5 +1271,86 @@ internal static string RBI0044Title { return ResourceManager.GetString("RBI0044Title", resourceCulture); } } + + /// + /// Looks up a localized string similar to Categories do not support events.. + /// + internal static string RBI0045Description { + get { + return ResourceManager.GetString("RBI0045Description", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Category '{0}' has events (found {1}), but events are not supported on categories. + /// + internal static string RBI0045MessageFormat { + get { + return ResourceManager.GetString("RBI0045MessageFormat", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Events are not supported on categories. + /// + internal static string RBI0045Title { + get { + return ResourceManager.GetString("RBI0045Title", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Categories do not support constructors.. + /// + internal static string RBI0046Description { + get { + return ResourceManager.GetString("RBI0046Description", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Category '{0}' has constructors (found {1}), but constructors are not supported on categories. + /// + internal static string RBI0046MessageFormat { + get { + return ResourceManager.GetString("RBI0046MessageFormat", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Constructors are not supported on categories. + /// + internal static string RBI0046Title { + get { + return ResourceManager.GetString("RBI0046Title", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Categories do not support properties.. + /// + internal static string RBI0047Description { + get { + return ResourceManager.GetString("RBI0047Description", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Category '{0}' has properties (found {1}), but properties are not supported on categories. + /// + internal static string RBI0047MessageFormat { + get { + return ResourceManager.GetString("RBI0047MessageFormat", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Properties are not supported on categories. + /// + internal static string RBI0047Title { + get { + return ResourceManager.GetString("RBI0047Title", resourceCulture); + } + } } } diff --git a/src/rgen/Microsoft.Macios.Bindings.Analyzer/Resources.resx b/src/rgen/Microsoft.Macios.Bindings.Analyzer/Resources.resx index 6c5d4621edcd..55f3b4c605d1 100644 --- a/src/rgen/Microsoft.Macios.Bindings.Analyzer/Resources.resx +++ b/src/rgen/Microsoft.Macios.Bindings.Analyzer/Resources.resx @@ -599,4 +599,43 @@ Wrong 'this' parameter in category method + + + + Categories do not support events. + + + Category '{0}' has events (found {1}), but events are not supported on categories + {0} is the name of the category, {1} the number of events + + + Events are not supported on categories + + + + + + Categories do not support constructors. + + + Category '{0}' has constructors (found {1}), but constructors are not supported on categories + {0} is the name of the category, {1} the number of constructors + + + Constructors are not supported on categories + + + + + + Categories do not support properties. + + + Category '{0}' has properties (found {1}), but properties are not supported on categories + {0} is the name of the category, {1} the number of properties + + + Properties are not supported on categories + + diff --git a/src/rgen/Microsoft.Macios.Bindings.Analyzer/RgenDiagnostics.cs b/src/rgen/Microsoft.Macios.Bindings.Analyzer/RgenDiagnostics.cs index a286c132a77e..0b34ec95a617 100644 --- a/src/rgen/Microsoft.Macios.Bindings.Analyzer/RgenDiagnostics.cs +++ b/src/rgen/Microsoft.Macios.Bindings.Analyzer/RgenDiagnostics.cs @@ -676,4 +676,49 @@ public static class RgenDiagnostics { description: new LocalizableResourceString (nameof (Resources.RBI0044Description), Resources.ResourceManager, typeof (Resources)) ); + + /// + /// Disgnostic descriptor for when a category contains an event. + /// + internal static readonly DiagnosticDescriptor RBI0045 = new ( + "RBI0045", + new LocalizableResourceString (nameof (Resources.RBI0045Title), Resources.ResourceManager, typeof (Resources)), + new LocalizableResourceString (nameof (Resources.RBI0045MessageFormat), Resources.ResourceManager, + typeof (Resources)), + "Usage", + DiagnosticSeverity.Error, + isEnabledByDefault: true, + description: new LocalizableResourceString (nameof (Resources.RBI0045Description), Resources.ResourceManager, + typeof (Resources)) + ); + + /// + /// Disgnostic descriptor for when a category contains an constructor. + /// + internal static readonly DiagnosticDescriptor RBI0046 = new ( + "RBI0046", + new LocalizableResourceString (nameof (Resources.RBI0046Title), Resources.ResourceManager, typeof (Resources)), + new LocalizableResourceString (nameof (Resources.RBI0046MessageFormat), Resources.ResourceManager, + typeof (Resources)), + "Usage", + DiagnosticSeverity.Error, + isEnabledByDefault: true, + description: new LocalizableResourceString (nameof (Resources.RBI0046Description), Resources.ResourceManager, + typeof (Resources)) + ); + + /// + /// Disgnostic descriptor for when a category contains an constructor. + /// + internal static readonly DiagnosticDescriptor RBI0047 = new ( + "RBI0047", + new LocalizableResourceString (nameof (Resources.RBI0047Title), Resources.ResourceManager, typeof (Resources)), + new LocalizableResourceString (nameof (Resources.RBI0047MessageFormat), Resources.ResourceManager, + typeof (Resources)), + "Usage", + DiagnosticSeverity.Error, + isEnabledByDefault: true, + description: new LocalizableResourceString (nameof (Resources.RBI0047Description), Resources.ResourceManager, + typeof (Resources)) + ); } diff --git a/src/rgen/Microsoft.Macios.Bindings.Analyzer/Validators/CategoryValidator.cs b/src/rgen/Microsoft.Macios.Bindings.Analyzer/Validators/CategoryValidator.cs index f97d3509f87e..8940ce489e11 100644 --- a/src/rgen/Microsoft.Macios.Bindings.Analyzer/Validators/CategoryValidator.cs +++ b/src/rgen/Microsoft.Macios.Bindings.Analyzer/Validators/CategoryValidator.cs @@ -9,6 +9,8 @@ using Microsoft.Macios.Generator.DataModel; using ObjCBindings; using static Microsoft.Macios.Generator.RgenDiagnostics; +using Constructor = Microsoft.Macios.Generator.DataModel.Constructor; +using Property = Microsoft.Macios.Generator.DataModel.Property; namespace Microsoft.Macios.Bindings.Analyzer.Validators; @@ -70,6 +72,48 @@ bool ValidMethods (Binding binding, RootContext context, return diagnostics.Length == 0; } + /// + /// Validates that categories do not contain unsupported members such as events, constructors, or properties. + /// + /// The category binding to validate. + /// The root context for validation. + /// When this method returns, contains diagnostics for any unsupported members; otherwise, an empty array. + /// The code location to be used for the diagnostics. + /// true if no unsupported members are present; otherwise, false. + bool ValidMembers (Binding binding, RootContext context, + out ImmutableArray diagnostics, Location? location = null) + { + var builder = ImmutableArray.CreateBuilder (); + + if (binding.Events.Length > 0) { + builder.Add (Diagnostic.Create ( + RBI0045, // Categories cannot contain events. + location, + binding.Name, + binding.Events.Length)); + } + + if (binding.Constructors.Length > 0) { + builder.Add (Diagnostic.Create ( + RBI0046, // Categories cannot contain constructors. + location, + binding.Name, + binding.Constructors.Length)); + } + + if (binding.Properties.Length > 0) { + builder.Add (Diagnostic.Create ( + RBI0047, // Categories cannot contain properties. + location, + binding.Name, + binding.Properties.Length)); + } + + diagnostics = builder.ToImmutable (); + return diagnostics.Length == 0; + } + + /// /// Initializes a new instance of the class. /// @@ -81,5 +125,8 @@ public CategoryValidator () AddGlobalStrategy (RBI0004, IsStatic); // validate all methods in the category binding AddGlobalStrategy ([RBI0042, RBI0043, RBI0044], ValidMethods); + // make sure that we do not have constructors, properties, fields or events + AddGlobalStrategy ([RBI0045, RBI0046, RBI0047], ValidMembers); } + } diff --git a/tests/rgen/Microsoft.Macios.Bindings.Analyzer.Tests/CategoryAnalyzerTests.cs b/tests/rgen/Microsoft.Macios.Bindings.Analyzer.Tests/CategoryAnalyzerTests.cs index bafa3f91a0f2..e3f5e3a8040a 100644 --- a/tests/rgen/Microsoft.Macios.Bindings.Analyzer.Tests/CategoryAnalyzerTests.cs +++ b/tests/rgen/Microsoft.Macios.Bindings.Analyzer.Tests/CategoryAnalyzerTests.cs @@ -177,6 +177,73 @@ public static partial class TestClass{ DiagnosticSeverity.Error, "Extension method 'ValueForKey' in category 'TestClass' must have the first parameter type match the category's extended type 'Foundation.NSObject' found 'Foundation.NSValue'" ]; + + // category with constructors + 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 (typeof (NSObject))] +public partial class TestClass{ + + [Export (""initWithScheme:host:path:"")] + public TestClass (string scheme, string host, string path); + +}", + "RBI0046", + DiagnosticSeverity.Error, + "Category 'TestClass' has constructors (found 1), but constructors are not supported on categories" + ]; + + // category with properties + 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 (typeof (NSObject))] +public partial class TestClass{ + + [SupportedOSPlatform (""ios"")] + [SupportedOSPlatform (""tvos"")] + [SupportedOSPlatform (""macos"")] + [SupportedOSPlatform (""maccatalyst13.1"")] + [Export (""count"")] + public virtual partial nuint Count { get; set; } +}", + "RBI0047", + DiagnosticSeverity.Error, + "Category 'TestClass' has properties (found 1), but properties are not supported on categories" + ]; } IEnumerator IEnumerable.GetEnumerator () => GetEnumerator (); From 5ce27cc12c8ce339dbb4464e9bafa34f56768bce Mon Sep 17 00:00:00 2001 From: GitHub Actions Autoformatter Date: Wed, 10 Sep 2025 21:32:28 +0000 Subject: [PATCH 2/4] Auto-format source code --- .../Microsoft.Macios.Bindings.Analyzer/RgenDiagnostics.cs | 6 +++--- .../Validators/CategoryValidator.cs | 8 ++++---- .../CategoryAnalyzerTests.cs | 4 ++-- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/rgen/Microsoft.Macios.Bindings.Analyzer/RgenDiagnostics.cs b/src/rgen/Microsoft.Macios.Bindings.Analyzer/RgenDiagnostics.cs index 0b34ec95a617..0c6c40b4fe3d 100644 --- a/src/rgen/Microsoft.Macios.Bindings.Analyzer/RgenDiagnostics.cs +++ b/src/rgen/Microsoft.Macios.Bindings.Analyzer/RgenDiagnostics.cs @@ -676,7 +676,7 @@ public static class RgenDiagnostics { description: new LocalizableResourceString (nameof (Resources.RBI0044Description), Resources.ResourceManager, typeof (Resources)) ); - + /// /// Disgnostic descriptor for when a category contains an event. /// @@ -691,7 +691,7 @@ public static class RgenDiagnostics { description: new LocalizableResourceString (nameof (Resources.RBI0045Description), Resources.ResourceManager, typeof (Resources)) ); - + /// /// Disgnostic descriptor for when a category contains an constructor. /// @@ -706,7 +706,7 @@ public static class RgenDiagnostics { description: new LocalizableResourceString (nameof (Resources.RBI0046Description), Resources.ResourceManager, typeof (Resources)) ); - + /// /// Disgnostic descriptor for when a category contains an constructor. /// diff --git a/src/rgen/Microsoft.Macios.Bindings.Analyzer/Validators/CategoryValidator.cs b/src/rgen/Microsoft.Macios.Bindings.Analyzer/Validators/CategoryValidator.cs index 8940ce489e11..d0d917bf2522 100644 --- a/src/rgen/Microsoft.Macios.Bindings.Analyzer/Validators/CategoryValidator.cs +++ b/src/rgen/Microsoft.Macios.Bindings.Analyzer/Validators/CategoryValidator.cs @@ -84,7 +84,7 @@ bool ValidMembers (Binding binding, RootContext context, out ImmutableArray diagnostics, Location? location = null) { var builder = ImmutableArray.CreateBuilder (); - + if (binding.Events.Length > 0) { builder.Add (Diagnostic.Create ( RBI0045, // Categories cannot contain events. @@ -100,7 +100,7 @@ bool ValidMembers (Binding binding, RootContext context, binding.Name, binding.Constructors.Length)); } - + if (binding.Properties.Length > 0) { builder.Add (Diagnostic.Create ( RBI0047, // Categories cannot contain properties. @@ -108,11 +108,11 @@ bool ValidMembers (Binding binding, RootContext context, binding.Name, binding.Properties.Length)); } - + diagnostics = builder.ToImmutable (); return diagnostics.Length == 0; } - + /// /// Initializes a new instance of the class. diff --git a/tests/rgen/Microsoft.Macios.Bindings.Analyzer.Tests/CategoryAnalyzerTests.cs b/tests/rgen/Microsoft.Macios.Bindings.Analyzer.Tests/CategoryAnalyzerTests.cs index e3f5e3a8040a..fa168375e1c0 100644 --- a/tests/rgen/Microsoft.Macios.Bindings.Analyzer.Tests/CategoryAnalyzerTests.cs +++ b/tests/rgen/Microsoft.Macios.Bindings.Analyzer.Tests/CategoryAnalyzerTests.cs @@ -177,7 +177,7 @@ public static partial class TestClass{ DiagnosticSeverity.Error, "Extension method 'ValueForKey' in category 'TestClass' must have the first parameter type match the category's extended type 'Foundation.NSObject' found 'Foundation.NSValue'" ]; - + // category with constructors yield return [ @" @@ -209,7 +209,7 @@ public partial class TestClass{ DiagnosticSeverity.Error, "Category 'TestClass' has constructors (found 1), but constructors are not supported on categories" ]; - + // category with properties yield return [ @" From 2351cd927fbca699b2be435ec26138c5f53d1f10 Mon Sep 17 00:00:00 2001 From: Manuel de la Pena Saenz Date: Wed, 10 Sep 2025 17:37:11 -0400 Subject: [PATCH 3/4] Fix typo. --- .../RgenDiagnostics.cs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/rgen/Microsoft.Macios.Bindings.Analyzer/RgenDiagnostics.cs b/src/rgen/Microsoft.Macios.Bindings.Analyzer/RgenDiagnostics.cs index 0c6c40b4fe3d..a8c95d30851f 100644 --- a/src/rgen/Microsoft.Macios.Bindings.Analyzer/RgenDiagnostics.cs +++ b/src/rgen/Microsoft.Macios.Bindings.Analyzer/RgenDiagnostics.cs @@ -603,7 +603,7 @@ public static class RgenDiagnostics { ); /// - /// 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. /// internal static readonly DiagnosticDescriptor RBI0040 = new ( "RBI0040", @@ -618,7 +618,7 @@ public static class RgenDiagnostics { ); /// - /// Disgnostic descriptor for when a class inherits from UIView and is missing the initWithFrame: constructor. + /// Diagnostic descriptor for when a class inherits from UIView and is missing the initWithFrame: constructor. /// internal static readonly DiagnosticDescriptor RBI0041 = new ( "RBI0041", @@ -633,7 +633,7 @@ public static class RgenDiagnostics { ); /// - /// Disgnostic descriptor for when a category method is not partial + /// Diagnostic descriptor for when a category method is not partial /// internal static readonly DiagnosticDescriptor RBI0042 = new ( "RBI0042", @@ -648,7 +648,7 @@ public static class RgenDiagnostics { ); /// - /// Disgnostic descriptor for when a category method is not and extension + /// Diagnostic descriptor for when a category method is not and extension /// internal static readonly DiagnosticDescriptor RBI0043 = new ( "RBI0043", @@ -663,7 +663,7 @@ public static class RgenDiagnostics { ); /// - /// Disgnostic descriptor for when a category method is not and extension of the category's target type + /// Diagnostic descriptor for when a category method is not and extension of the category's target type /// internal static readonly DiagnosticDescriptor RBI0044 = new ( "RBI0044", @@ -678,7 +678,7 @@ public static class RgenDiagnostics { ); /// - /// Disgnostic descriptor for when a category contains an event. + /// Diagnostic descriptor for when a category contains an event. /// internal static readonly DiagnosticDescriptor RBI0045 = new ( "RBI0045", @@ -693,7 +693,7 @@ public static class RgenDiagnostics { ); /// - /// Disgnostic descriptor for when a category contains an constructor. + /// Diagnostic descriptor for when a category contains an constructor. /// internal static readonly DiagnosticDescriptor RBI0046 = new ( "RBI0046", @@ -708,7 +708,7 @@ public static class RgenDiagnostics { ); /// - /// Disgnostic descriptor for when a category contains an constructor. + /// Diagnostic descriptor for when a category contains an constructor. /// internal static readonly DiagnosticDescriptor RBI0047 = new ( "RBI0047", From e9cd70778bc8ae3ac593592cc759f5fe4916c4ee Mon Sep 17 00:00:00 2001 From: Manuel de la Pena Saenz Date: Wed, 10 Sep 2025 17:38:17 -0400 Subject: [PATCH 4/4] Remove not needed usings. --- .../Validators/CategoryValidator.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/rgen/Microsoft.Macios.Bindings.Analyzer/Validators/CategoryValidator.cs b/src/rgen/Microsoft.Macios.Bindings.Analyzer/Validators/CategoryValidator.cs index d0d917bf2522..48499e959a25 100644 --- a/src/rgen/Microsoft.Macios.Bindings.Analyzer/Validators/CategoryValidator.cs +++ b/src/rgen/Microsoft.Macios.Bindings.Analyzer/Validators/CategoryValidator.cs @@ -3,14 +3,11 @@ using System.Collections.Immutable; using Microsoft.CodeAnalysis; -using Microsoft.Macios.Generator; using Microsoft.Macios.Generator.Attributes; using Microsoft.Macios.Generator.Context; using Microsoft.Macios.Generator.DataModel; using ObjCBindings; using static Microsoft.Macios.Generator.RgenDiagnostics; -using Constructor = Microsoft.Macios.Generator.DataModel.Constructor; -using Property = Microsoft.Macios.Generator.DataModel.Property; namespace Microsoft.Macios.Bindings.Analyzer.Validators;