diff --git a/src/rgen/Microsoft.Macios.Bindings.Analyzer/Resources.Designer.cs b/src/rgen/Microsoft.Macios.Bindings.Analyzer/Resources.Designer.cs index 5854f1a7568c..73a673b87d76 100644 --- a/src/rgen/Microsoft.Macios.Bindings.Analyzer/Resources.Designer.cs +++ b/src/rgen/Microsoft.Macios.Bindings.Analyzer/Resources.Designer.cs @@ -1352,5 +1352,194 @@ internal static string RBI0047Title { return ResourceManager.GetString("RBI0047Title", resourceCulture); } } + + /// + /// Looks up a localized string similar to A category name cannot be an empty string or have spaces.. + /// + internal static string RBI0048Description { + get { + return ResourceManager.GetString("RBI0048Description", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Category '{0}' name '{1}' is empty an empty string or has white spaces. + /// + internal static string RBI0048MessageFormat { + get { + return ResourceManager.GetString("RBI0048MessageFormat", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Category names cannot be a empty string or have white spaces. + /// + internal static string RBI0048Title { + get { + return ResourceManager.GetString("RBI0048Title", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to A category type must be a INativeObject.. + /// + internal static string RBI0049Description { + get { + return ResourceManager.GetString("RBI0049Description", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Category '{0}' type '{1}' does not implement INativeObject. + /// + internal static string RBI0049MessageFormat { + get { + return ResourceManager.GetString("RBI0049MessageFormat", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Category types must be INativeObjects. + /// + internal static string RBI0049Title { + get { + return ResourceManager.GetString("RBI0049Title", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to DefaultCtorVisibility is ignored in categories.. + /// + internal static string RBI0050Description { + get { + return ResourceManager.GetString("RBI0050Description", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Category '{0}' has DefaultCtorVisibility set to '{1}' but it will be ignored. + /// + internal static string RBI0050MessageFormat { + get { + return ResourceManager.GetString("RBI0050MessageFormat", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to DefaultCtorVisibility is ignored in categories. + /// + internal static string RBI0050Title { + get { + return ResourceManager.GetString("RBI0050Title", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to ErrorDomain is ignored in categories.. + /// + internal static string RBI0051Description { + get { + return ResourceManager.GetString("RBI0051Description", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Category '{0}' has ErrorDomain set to '{1}' but it will be ignored. + /// + internal static string RBI0051MessageFormat { + get { + return ResourceManager.GetString("RBI0051MessageFormat", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to ErrorDomain is ignored in categories. + /// + internal static string RBI0051Title { + get { + return ResourceManager.GetString("RBI0051Title", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to IntPtrCtorVisibility is ignored in categories.. + /// + internal static string RBI0052Description { + get { + return ResourceManager.GetString("RBI0052Description", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Category '{0}' has IntPtrCtorVisibility set to '{1}' but it will be ignored. + /// + internal static string RBI0052MessageFormat { + get { + return ResourceManager.GetString("RBI0052MessageFormat", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to IntPtrCtorVisibility is ignored in categories. + /// + internal static string RBI0052Title { + get { + return ResourceManager.GetString("RBI0052Title", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to ModelName is ignored in categories.. + /// + internal static string RBI0053Description { + get { + return ResourceManager.GetString("RBI0053Description", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Category '{0}' has ModelName set to '{1}' but it will be ignored. + /// + internal static string RBI0053MessageFormat { + get { + return ResourceManager.GetString("RBI0053MessageFormat", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to ModelName is ignored in categories. + /// + internal static string RBI0053Title { + get { + return ResourceManager.GetString("RBI0053Title", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to StringCtorVisibility is ignored in categories.. + /// + internal static string RBI0054Description { + get { + return ResourceManager.GetString("RBI0054Description", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Category '{0}' has StringCtorVisibility set to '{1}' but it will be ignored. + /// + internal static string RBI0054MessageFormat { + get { + return ResourceManager.GetString("RBI0054MessageFormat", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to StringCtorVisibility is ignored in categories. + /// + internal static string RBI0054Title { + get { + return ResourceManager.GetString("RBI0054Title", resourceCulture); + } + } } } diff --git a/src/rgen/Microsoft.Macios.Bindings.Analyzer/Resources.resx b/src/rgen/Microsoft.Macios.Bindings.Analyzer/Resources.resx index 55f3b4c605d1..50c56253458d 100644 --- a/src/rgen/Microsoft.Macios.Bindings.Analyzer/Resources.resx +++ b/src/rgen/Microsoft.Macios.Bindings.Analyzer/Resources.resx @@ -638,4 +638,95 @@ Properties are not supported on categories + + + + A category name cannot be an empty string or have spaces. + + + Category '{0}' name '{1}' is empty an empty string or has white spaces + {0} is the name of the category, {1} is what we found + + + Category names cannot be a empty string or have white spaces + + + + + + A category type must be a INativeObject. + + + Category '{0}' type '{1}' does not implement INativeObject + {0} is the name of the category, {1} is the category type + + + Category types must be INativeObjects + + + + + + DefaultCtorVisibility is ignored in categories. + + + Category '{0}' has DefaultCtorVisibility set to '{1}' but it will be ignored + {0} is the name of the category, {1} is the value + + + DefaultCtorVisibility is ignored in categories + + + + + + ErrorDomain is ignored in categories. + + + Category '{0}' has ErrorDomain set to '{1}' but it will be ignored + {0} is the name of the category, {1} is the value + + + ErrorDomain is ignored in categories + + + + + + IntPtrCtorVisibility is ignored in categories. + + + Category '{0}' has IntPtrCtorVisibility set to '{1}' but it will be ignored + {0} is the name of the category, {1} is the value + + + IntPtrCtorVisibility is ignored in categories + + + + + + ModelName is ignored in categories. + + + Category '{0}' has ModelName set to '{1}' but it will be ignored + {0} is the name of the category, {1} is the value + + + ModelName is ignored in categories + + + + + + StringCtorVisibility is ignored in categories. + + + Category '{0}' has StringCtorVisibility set to '{1}' but it will be ignored + {0} is the name of the category, {1} is the value + + + StringCtorVisibility is ignored in categories + + diff --git a/src/rgen/Microsoft.Macios.Bindings.Analyzer/RgenDiagnostics.cs b/src/rgen/Microsoft.Macios.Bindings.Analyzer/RgenDiagnostics.cs index a8c95d30851f..82b7d455a495 100644 --- a/src/rgen/Microsoft.Macios.Bindings.Analyzer/RgenDiagnostics.cs +++ b/src/rgen/Microsoft.Macios.Bindings.Analyzer/RgenDiagnostics.cs @@ -721,4 +721,109 @@ public static class RgenDiagnostics { description: new LocalizableResourceString (nameof (Resources.RBI0047Description), Resources.ResourceManager, typeof (Resources)) ); + + /// + /// Diagnostic descriptor for when a category name is incorrect. + /// + internal static readonly DiagnosticDescriptor RBI0048 = new ( + "RBI0048", + new LocalizableResourceString (nameof (Resources.RBI0048Title), Resources.ResourceManager, typeof (Resources)), + new LocalizableResourceString (nameof (Resources.RBI0048MessageFormat), Resources.ResourceManager, + typeof (Resources)), + "Usage", + DiagnosticSeverity.Error, + isEnabledByDefault: true, + description: new LocalizableResourceString (nameof (Resources.RBI0048Description), Resources.ResourceManager, + typeof (Resources)) + ); + + /// + /// Diagnostic descriptor for when a category type is not a INativeObject. + /// + internal static readonly DiagnosticDescriptor RBI0049 = new ( + "RBI0049", + new LocalizableResourceString (nameof (Resources.RBI0049Title), Resources.ResourceManager, typeof (Resources)), + new LocalizableResourceString (nameof (Resources.RBI0049MessageFormat), Resources.ResourceManager, + typeof (Resources)), + "Usage", + DiagnosticSeverity.Error, + isEnabledByDefault: true, + description: new LocalizableResourceString (nameof (Resources.RBI0049Description), Resources.ResourceManager, + typeof (Resources)) + ); + + /// + /// Diagnostic descriptor for when a category sets the default constructor visibility. + /// + internal static readonly DiagnosticDescriptor RBI0050 = new ( + "RBI0050", + new LocalizableResourceString (nameof (Resources.RBI0050Title), Resources.ResourceManager, typeof (Resources)), + new LocalizableResourceString (nameof (Resources.RBI0050MessageFormat), Resources.ResourceManager, + typeof (Resources)), + "Usage", + DiagnosticSeverity.Warning, + isEnabledByDefault: true, + description: new LocalizableResourceString (nameof (Resources.RBI0050Description), Resources.ResourceManager, + typeof (Resources)) + ); + + /// + /// Diagnostic descriptor for when a category sets the default constructor visibility. + /// + internal static readonly DiagnosticDescriptor RBI0051 = new ( + "RBI0051", + new LocalizableResourceString (nameof (Resources.RBI0051Title), Resources.ResourceManager, typeof (Resources)), + new LocalizableResourceString (nameof (Resources.RBI0051MessageFormat), Resources.ResourceManager, + typeof (Resources)), + "Usage", + DiagnosticSeverity.Warning, + isEnabledByDefault: true, + description: new LocalizableResourceString (nameof (Resources.RBI0051Description), Resources.ResourceManager, + typeof (Resources)) + ); + + /// + /// Diagnostic descriptor for when a category sets the IntPtr constructor visibility. + /// + internal static readonly DiagnosticDescriptor RBI0052 = new ( + "RBI0052", + new LocalizableResourceString (nameof (Resources.RBI0052Title), Resources.ResourceManager, typeof (Resources)), + new LocalizableResourceString (nameof (Resources.RBI0052MessageFormat), Resources.ResourceManager, + typeof (Resources)), + "Usage", + DiagnosticSeverity.Warning, + isEnabledByDefault: true, + description: new LocalizableResourceString (nameof (Resources.RBI0052Description), Resources.ResourceManager, + typeof (Resources)) + ); + + /// + /// Diagnostic descriptor for when a category sets a model name. + /// + internal static readonly DiagnosticDescriptor RBI0053 = new ( + "RBI0053", + new LocalizableResourceString (nameof (Resources.RBI0053Title), Resources.ResourceManager, typeof (Resources)), + new LocalizableResourceString (nameof (Resources.RBI0053MessageFormat), Resources.ResourceManager, + typeof (Resources)), + "Usage", + DiagnosticSeverity.Warning, + isEnabledByDefault: true, + description: new LocalizableResourceString (nameof (Resources.RBI0053Description), Resources.ResourceManager, + typeof (Resources)) + ); + + /// + /// Diagnostic descriptor for when a category sets a model name. + /// + internal static readonly DiagnosticDescriptor RBI0054 = new ( + "RBI0054", + new LocalizableResourceString (nameof (Resources.RBI0054Title), Resources.ResourceManager, typeof (Resources)), + new LocalizableResourceString (nameof (Resources.RBI0054MessageFormat), Resources.ResourceManager, + typeof (Resources)), + "Usage", + DiagnosticSeverity.Warning, + isEnabledByDefault: true, + description: new LocalizableResourceString (nameof (Resources.RBI0054Description), 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 48499e959a25..dfa66c27289e 100644 --- a/src/rgen/Microsoft.Macios.Bindings.Analyzer/Validators/CategoryValidator.cs +++ b/src/rgen/Microsoft.Macios.Bindings.Analyzer/Validators/CategoryValidator.cs @@ -2,6 +2,7 @@ // Licensed under the MIT License. using System.Collections.Immutable; +using System.Reflection; using Microsoft.CodeAnalysis; using Microsoft.Macios.Generator.Attributes; using Microsoft.Macios.Generator.Context; @@ -110,6 +111,92 @@ bool ValidMembers (Binding binding, RootContext context, return diagnostics.Length == 0; } + /// + /// Validates the Export attribute data for a category binding, ensuring proper naming conventions, + /// type constraints, and constructor visibility settings. + /// + /// The binding to validate. + /// The root context for validation. + /// When this method returns, contains diagnostics for any validation failures; otherwise, an empty array. + /// The code location to be used for the diagnostics. + /// true if the Export attribute data is valid; otherwise, false. + bool ValidateExportData (Binding binding, RootContext context, out ImmutableArray diagnostics, Location? location) + { + var data = (BindingTypeData) binding.BindingInfo; + var builder = ImmutableArray.CreateBuilder (); + + // validate the name if specified + if (data.Name is not null) { + // validate that we do not have any whitespaces in the name + if (string.IsNullOrWhiteSpace (data.Name) || data.Name.Contains (' ')) { + // the name is not valid + builder.Add (Diagnostic.Create ( + RBI0048, // Category '{0}' name '{1}' is empty an empty string or has white spaces + location, + binding.Name, + data.Name)); + } + } + // the category types must be a INativeObject or a NSObject + if (!data.CategoryType.IsINativeObject) { + // the type is not valid + builder.Add (Diagnostic.Create ( + RBI0049, // Category '{0}' type '{1}' does not implement INativeObject + location, + binding.Name, + data.CategoryType.FullyQualifiedName)); + } + // the default ctor visibility must be the default value, else throw a warning + if (data.DefaultCtorVisibility != MethodAttributes.Public) { + // warning for the user + builder.Add (Diagnostic.Create ( + RBI0050, // Category '{0}' has DefaultCtorVisibility set to '{1}' but it will be ignored + location, + binding.Name, + data.DefaultCtorVisibility.ToString ())); + } + + if (data.ErrorDomain is not null) { + // warning for the user + builder.Add (Diagnostic.Create ( + RBI0051, // Category '{0}' has set ErrorDomain to '{1}' but it will be ignored + location, + binding.Name, + data.ErrorDomain)); + } + + // intptr ctor visibility must be private scope since it will be ignored + if (data.IntPtrCtorVisibility != MethodAttributes.PrivateScope) { + // warning for the user + builder.Add (Diagnostic.Create ( + RBI0052, // The IntPtr constructor visibility for a category must be PrivateScope. + location, + binding.Name, + data.IntPtrCtorVisibility.ToString ())); + } + + if (data.ModelName is not null) { + // warning for the user + builder.Add (Diagnostic.Create ( + RBI0053, // Category '{0}' has set ModelName to '{1}' but it will be ignored + location, + binding.Name, + data.ModelName)); + } + + // string ctor visibility must be private scope since it will be ignored + if (data.StringCtorVisibility != MethodAttributes.PrivateScope) { + // warning for the user + builder.Add (Diagnostic.Create ( + RBI0054, // Category '{0}' has set StringCtorVisibility to '{1}' but it will be ignored + location, + binding.Name, + data.StringCtorVisibility.ToString ())); + } + + diagnostics = builder.ToImmutable (); + return diagnostics.Length == 0; + } /// /// Initializes a new instance of the class. @@ -120,6 +207,10 @@ public CategoryValidator () AddGlobalStrategy (RBI0001, IsPartial); // categories must be static AddGlobalStrategy (RBI0004, IsStatic); + // validate the export attr of the category + AddGlobalStrategy ( + descriptor: [RBI0048, RBI0049, RBI0050, RBI0051, RBI0052, RBI0053, RBI0054], + validation: ValidateExportData); // validate all methods in the category binding AddGlobalStrategy ([RBI0042, RBI0043, RBI0044], ValidMethods); // make sure that we do not have constructors, properties, fields or events diff --git a/src/rgen/Microsoft.Macios.Generator/Attributes/BindingTypeData.cs b/src/rgen/Microsoft.Macios.Generator/Attributes/BindingTypeData.cs index c24a12c58095..2e0c115bad5c 100644 --- a/src/rgen/Microsoft.Macios.Generator/Attributes/BindingTypeData.cs +++ b/src/rgen/Microsoft.Macios.Generator/Attributes/BindingTypeData.cs @@ -127,7 +127,7 @@ public override string ToString () /// /// Original name of the ObjC class or protocol. /// - public string? Name { get; } + public string? Name { get; init; } /// /// The domain of an error enumerator. This has to be used with the SmartEnum flag. @@ -237,7 +237,6 @@ public static bool TryParse (AttributeData attributeData, // category related data TypeInfo categoryType = TypeInfo.Default; // protocol related data - string? modelName = null; // check if we have a category type, we can do that by checking the type of the flag var isCategory = typeof (T) == typeof (ObjCBindings.Category); @@ -285,104 +284,102 @@ public static bool TryParse (AttributeData attributeData, return true; } - // the named types are different depending on the type of the flag, if we are dealing with a category or not. - if (isCategory && TryExtractCategoryNamedParameters (attributeData, out name, ref flags, out categoryType)) { - data = CreateCategoryBindingData (flags, categoryType); - return true; - } else if (isProtocol && TryExtractProtocolNamedParameters (attributeData, out name, ref flags, out modelName)) { - data = CreateProtocolBindingData (flags, name, modelName); - return true; - } else if (TryExtractClassNamedParameters (attributeData, out name, ref flags, out string? errorDomain, out string? libraryName, out MethodAttributes defaultCtorVisibility, out MethodAttributes intPtrCtorVisibility, out MethodAttributes stringCtorVisibility)) { - data = CreateClassBindingData (flags, name, errorDomain, libraryName, defaultCtorVisibility, intPtrCtorVisibility, stringCtorVisibility); + // get all the data from the attribute even when in certain use cases like categories and protocols we do no + // need all the data. Later the analyzer/validator will check if the data is correct and will report any issues. + if (TryExtractNamedParameters (attributeData, out name, ref flags, out string? modelName, out string? errorDomain, out string? libraryName, out categoryType, out MethodAttributes defaultCtorVisibility, out MethodAttributes intPtrCtorVisibility, out MethodAttributes stringCtorVisibility)) { + if (isCategory) { + data = flags is not null + ? new (categoryType, flags) { + Name = name, + ModelName = modelName, + ErrorDomain = errorDomain, + LibraryPath = libraryName, + DefaultCtorVisibility = defaultCtorVisibility, + IntPtrCtorVisibility = intPtrCtorVisibility, + StringCtorVisibility = stringCtorVisibility, + } + : new (categoryType) { + Name = name, + ModelName = modelName, + ErrorDomain = errorDomain, + LibraryPath = libraryName, + DefaultCtorVisibility = defaultCtorVisibility, + IntPtrCtorVisibility = intPtrCtorVisibility, + StringCtorVisibility = stringCtorVisibility, + }; + } else if (isProtocol) { + data = flags is not null ? + new (name, flags) { + ModelName = modelName, + ErrorDomain = errorDomain, + LibraryPath = libraryName, + DefaultCtorVisibility = defaultCtorVisibility, + IntPtrCtorVisibility = intPtrCtorVisibility, + StringCtorVisibility = stringCtorVisibility, + } + : new (name) { + ModelName = modelName, + ErrorDomain = errorDomain, + LibraryPath = libraryName, + DefaultCtorVisibility = defaultCtorVisibility, + IntPtrCtorVisibility = intPtrCtorVisibility, + StringCtorVisibility = stringCtorVisibility, + }; + } else { + data = flags is not null ? + new (name, flags) { + ModelName = modelName, + ErrorDomain = errorDomain, + LibraryPath = libraryName, + DefaultCtorVisibility = defaultCtorVisibility, + IntPtrCtorVisibility = intPtrCtorVisibility, + StringCtorVisibility = stringCtorVisibility, + } + : new (name) { + ModelName = modelName, + ErrorDomain = errorDomain, + LibraryPath = libraryName, + DefaultCtorVisibility = defaultCtorVisibility, + IntPtrCtorVisibility = intPtrCtorVisibility, + StringCtorVisibility = stringCtorVisibility, + }; + } return true; } return false; } - /// - /// Creates a new instance of for a class. - /// - /// The configuration flags. - /// The original name of the ObjC class or protocol. - /// The domain of an error enumerator. - /// The library name of an error/smart enum. - /// The visibility of the default constructor. - /// The visibility of the IntPtr constructor. - /// The visibility of the string constructor. - /// A new instance of . - static BindingTypeData CreateClassBindingData (T? flags, string? name, string? errorDomain, - string? libraryPath, MethodAttributes defaultCtorVisibility, MethodAttributes intPtrCtorVisibility, - MethodAttributes stringCtorVisibility) - { - return flags is not null - ? new (name, flags) { - ErrorDomain = errorDomain, - LibraryPath = libraryPath, - DefaultCtorVisibility = defaultCtorVisibility, - IntPtrCtorVisibility = intPtrCtorVisibility, - StringCtorVisibility = stringCtorVisibility, - } - : new (name) { - ErrorDomain = errorDomain, - LibraryPath = libraryPath, - DefaultCtorVisibility = defaultCtorVisibility, - IntPtrCtorVisibility = intPtrCtorVisibility, - StringCtorVisibility = stringCtorVisibility, - }; - } - - /// - /// Creates a new instance of for a protocol. - /// - /// The configuration flags. - /// The original name of the ObjC protocol. - /// The name of the model class for the protocol. - /// A new instance of . - static BindingTypeData CreateProtocolBindingData (T? flags, string? name, string? modelName) - { - return flags is not null - ? new (name, flags) { - ModelName = modelName, - } - : new (name) { - ModelName = modelName, - }; - } - - /// - /// Creates a new instance of for a category. - /// - /// The configuration flags. - /// The type that the category extends. - /// A new instance of . - static BindingTypeData CreateCategoryBindingData (T? flags, TypeInfo categoryType) - { - return flags is not null - ? new (categoryType, flags) - : new (categoryType); - } - /// /// Tries to extract the named parameters for a class from the attribute data. /// /// The attribute data to be parsed. /// The original name of the ObjC class or protocol. /// The configuration flags. + /// The name of the model. /// The domain of an error enumerator. /// The library name of an error/smart enum. + /// The category type that is being extended. /// The visibility of the default constructor. /// The visibility of the IntPtr constructor. /// The visibility of the string constructor. /// True if the data was parsed. - static bool TryExtractClassNamedParameters (AttributeData attributeData, - out string? name, ref T? flags, out string? errorDomain, out string? libraryPath, - out MethodAttributes defaultCtorVisibility, out MethodAttributes intPtrCtorVisibility, + static bool TryExtractNamedParameters (AttributeData attributeData, + out string? name, + ref T? flags, + out string? modelName, + out string? errorDomain, + out string? libraryPath, + out TypeInfo categoryType, + out MethodAttributes defaultCtorVisibility, + out MethodAttributes intPtrCtorVisibility, out MethodAttributes stringCtorVisibility) { name = null; + modelName = null; errorDomain = null; libraryPath = null; + categoryType = TypeInfo.Default; defaultCtorVisibility = MethodAttributes.PrivateScope; intPtrCtorVisibility = MethodAttributes.PrivateScope; stringCtorVisibility = MethodAttributes.PrivateScope; @@ -401,6 +398,12 @@ static bool TryExtractClassNamedParameters (AttributeData attributeData, case "LibraryPath": libraryPath = (string?) value.Value!; break; + case "ModelName": + modelName = (string?) value.Value!; + break; + case "CategoryType": + categoryType = new ((INamedTypeSymbol) value.Value!); + break; case "DefaultCtorVisibility": defaultCtorVisibility = (MethodAttributes) Convert.ToSingle ((int) value.Value!); break; @@ -418,68 +421,6 @@ static bool TryExtractClassNamedParameters (AttributeData attributeData, return true; } - /// - /// Tries to extract the named parameters for a category from the attribute data. - /// - /// The attribute data to be parsed. - /// The original name of the ObjC class or protocol. - /// The configuration flags. - /// The type that the category extends. - /// True if the data was parsed. - static bool TryExtractCategoryNamedParameters (AttributeData attributeData, out string? name, ref T? flags, out TypeInfo categoryType) - { - name = null; - categoryType = TypeInfo.Default; - foreach (var (paramName, value) in attributeData.NamedArguments) { - switch (paramName) { - case "Name": - name = (string?) value.Value!; - break; - case "Flags": - flags = (T) value.Value!; - break; - case "CategoryType": - categoryType = new ((INamedTypeSymbol) value.Value!); - break; - default: - return false; - } - } - - return true; - } - - /// - /// Tries to extract the named parameters for a protocol from the attribute data. - /// - /// The attribute data to be parsed. - /// The original name of the ObjC protocol. - /// The configuration flags. - /// The name of the model class for the protocol. - /// True if the data was parsed. - static bool TryExtractProtocolNamedParameters (AttributeData attributeData, out string? name, ref T? flags, out string? modelName) - { - name = null; - modelName = null; - foreach (var (paramName, value) in attributeData.NamedArguments) { - switch (paramName) { - case "Name": - name = (string?) value.Value!; - break; - case "Flags": - flags = (T) value.Value!; - break; - case "ModelName": - modelName = (string?) value.Value!; - break; - default: - return false; - } - } - - return true; - } - /// public bool Equals (BindingTypeData other) { diff --git a/tests/rgen/Microsoft.Macios.Bindings.Analyzer.Tests/CategoryAnalyzerTests.cs b/tests/rgen/Microsoft.Macios.Bindings.Analyzer.Tests/CategoryAnalyzerTests.cs index fa168375e1c0..32acbe5c0d91 100644 --- a/tests/rgen/Microsoft.Macios.Bindings.Analyzer.Tests/CategoryAnalyzerTests.cs +++ b/tests/rgen/Microsoft.Macios.Bindings.Analyzer.Tests/CategoryAnalyzerTests.cs @@ -244,6 +244,237 @@ public partial class TestClass{ DiagnosticSeverity.Error, "Category 'TestClass' has properties (found 1), but properties are not supported on categories" ]; + + // wrong name, is empty spaces + 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), Name = "" "")] +public partial static class TestClass{ +}", + "RBI0048", + DiagnosticSeverity.Error, + "Category 'TestClass' name ' ' is empty an empty string or has white spaces" + ]; + + // wrong name, contains spaces + 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), Name = ""Category Name "")] +public partial static class TestClass{ +}", + "RBI0048", + DiagnosticSeverity.Error, + "Category 'TestClass' name 'Category Name ' is empty an empty string or has white spaces" + ]; + + // not INativeObject + 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; + +public class MyClass {} + +[SupportedOSPlatform (""macos"")] +[SupportedOSPlatform (""ios"")] +[SupportedOSPlatform (""tvos"")] +[SupportedOSPlatform (""maccatalyst13.1"")] +[BindingType (typeof (MyClass))] +public partial static class TestClass{ +}", + "RBI0049", + DiagnosticSeverity.Error, + "Category 'TestClass' type 'TestNamespace.MyClass' does not implement INativeObject" + ]; + + // DefaultCtorVisibility is ignored + yield return [ + @" +#pragma warning disable APL0003 + +using System; +using System.Reflection; +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), DefaultCtorVisibility = MethodAttributes.PrivateScope)] +public partial static class TestClass{ +}", + "RBI0050", + DiagnosticSeverity.Warning, + "Category 'TestClass' has DefaultCtorVisibility set to 'PrivateScope' but it will be ignored" + ]; + + // IntPtrCtorVisibility is ignored + yield return [ + @" +#pragma warning disable APL0003 + +using System; +using System.Reflection; +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), IntPtrCtorVisibility = MethodAttributes.Public)] +public partial static class TestClass{ +}", + "RBI0052", + DiagnosticSeverity.Warning, + "Category 'TestClass' has IntPtrCtorVisibility set to 'Public' but it will be ignored" + ]; + + // StringCtorVisibility is ignored + yield return [ + @" +#pragma warning disable APL0003 + +using System; +using System.Reflection; +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), StringCtorVisibility = MethodAttributes.Public)] +public partial static class TestClass{ +}", + "RBI0054", + DiagnosticSeverity.Warning, + "Category 'TestClass' has StringCtorVisibility set to 'Public' but it will be ignored" + ]; + + // ErrorDomain is ignored + yield return [ + @" +#pragma warning disable APL0003 + +using System; +using System.Reflection; +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), ErrorDomain = ""MyErrorDomain"")] +public partial static class TestClass{ +}", + "RBI0051", + DiagnosticSeverity.Warning, + "Category 'TestClass' has ErrorDomain set to 'MyErrorDomain' but it will be ignored" + ]; + + // ModelName is ignored + yield return [ + @" +#pragma warning disable APL0003 + +using System; +using System.Reflection; +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), ModelName = ""MyModelName"")] +public partial static class TestClass{ +}", + "RBI0053", + DiagnosticSeverity.Warning, + "Category 'TestClass' has ModelName set to 'MyModelName' but it will be ignored" + ]; } IEnumerator IEnumerable.GetEnumerator () => GetEnumerator ();