diff --git a/src/ObjCBindings/BindingTypeTag.cs b/src/ObjCBindings/BindingTypeTag.cs index ce9f34dd4f83..aeb3d3734955 100644 --- a/src/ObjCBindings/BindingTypeTag.cs +++ b/src/ObjCBindings/BindingTypeTag.cs @@ -45,4 +45,16 @@ public enum Category : Int64 { /// Default = 0, } + + /// + /// Flags to be used on strong dictionary bindings. + /// + [Flags] + [Experimental ("APL0003")] + public enum StrongDictionary : Int64 { + /// + /// Use the default values. + /// + Default = 0, + } } diff --git a/src/rgen/Microsoft.Macios.Bindings.Analyzer/AnalyzerReleases.Unshipped.md b/src/rgen/Microsoft.Macios.Bindings.Analyzer/AnalyzerReleases.Unshipped.md index c2fa06a3b9eb..c13c36ccb555 100644 --- a/src/rgen/Microsoft.Macios.Bindings.Analyzer/AnalyzerReleases.Unshipped.md +++ b/src/rgen/Microsoft.Macios.Bindings.Analyzer/AnalyzerReleases.Unshipped.md @@ -5,9 +5,15 @@ | Rule ID | Category | Severity | Notes | |---------|----------|----------|---------------------------------------------------------------------------| | RBI0001 | Usage | Error | Binding types should be declared as partial classes. | -| RBI0002 | Usage | Error | Smart enum values must be tagged with an Field attribute. | -| RBI0003 | Usage | Error | Smart enum backing field cannot appear more than once. | -| RBI0004 | Usage | Error | Smart enum backing field must represent a valid C# identifier to be used. | -| RBI0005 | Usage | Error | Non Apple framework bindings must provide a library name. | -| RBI0006 | Usage | Warning | Do not provide the LibraryName for known Apple frameworks. | -| RBI0007 | Usage | Error | Enum values must be tagged with Field. | +| RBI0002 | Usage | Error | BindingType must be on a class. | +| RBI0003 | Usage | Error | BindingType must be on a class. | +| RBI0004 | Usage | Error | BindingType must be on a static class. | +| RBI0005 | Usage | Error | BindingType must be on an interface. | +| RBI0006 | Usage | Error | BindingType must be on an enumerator. | +| RBI0007 | Usage | Error | BindingType must be on a class. | +| RBI0008 | Usage | Error | Smart enum values must be tagged with an Field attribute. | +| RBI0009 | Usage | Error | Smart enum backing field cannot appear more than once. | +| RBI0010 | Usage | Error | Smart enum backing field must represent a valid C# identifier to be used. | +| RBI0011 | Usage | Error | Non Apple framework bindings must provide a library name. | +| RBI0012 | Usage | Warning | Do not provide the LibraryName for known Apple frameworks. | +| RBI0013 | Usage | Error | Enum values must be tagged with Field. | diff --git a/src/rgen/Microsoft.Macios.Bindings.Analyzer/BindingTypeSemanticAnalyzer.cs b/src/rgen/Microsoft.Macios.Bindings.Analyzer/BindingTypeSemanticAnalyzer.cs index 482c409b7455..a6179bc06551 100644 --- a/src/rgen/Microsoft.Macios.Bindings.Analyzer/BindingTypeSemanticAnalyzer.cs +++ b/src/rgen/Microsoft.Macios.Bindings.Analyzer/BindingTypeSemanticAnalyzer.cs @@ -1,5 +1,8 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. + +using System; +using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; using Microsoft.CodeAnalysis; @@ -7,6 +10,7 @@ using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.Macios.Bindings.Analyzer.Extensions; +using Microsoft.Macios.Generator; namespace Microsoft.Macios.Bindings.Analyzer; @@ -15,39 +19,255 @@ namespace Microsoft.Macios.Bindings.Analyzer; /// pattern. /// [DiagnosticAnalyzer (LanguageNames.CSharp)] -public class BindingTypeSemanticAnalyzer : DiagnosticAnalyzer, IBindingTypeAnalyzer { - +public class BindingTypeSemanticAnalyzer : DiagnosticAnalyzer, IBindingTypeAnalyzer { + /// + /// All binding types should be partial. + /// internal static readonly DiagnosticDescriptor RBI0001 = new ( "RBI0001", new LocalizableResourceString (nameof (Resources.RBI0001Title), Resources.ResourceManager, typeof (Resources)), - new LocalizableResourceString (nameof (Resources.RBI0001MessageFormat), Resources.ResourceManager, typeof (Resources)), + new LocalizableResourceString (nameof (Resources.RBI0001MessageFormat), Resources.ResourceManager, + typeof (Resources)), + "Usage", + DiagnosticSeverity.Error, + isEnabledByDefault: true, + description: new LocalizableResourceString (nameof (Resources.RBI0001Description), Resources.ResourceManager, + typeof (Resources)) + ); + + /// + /// BindingType<Class> can only decorate partial classes. + /// + internal static readonly DiagnosticDescriptor RBI0002 = new ( + "RBI0002", + new LocalizableResourceString (nameof (Resources.RBI0002Title), Resources.ResourceManager, typeof (Resources)), + new LocalizableResourceString (nameof (Resources.RBI0002MessageFormat), Resources.ResourceManager, + typeof (Resources)), + "Usage", + DiagnosticSeverity.Error, + isEnabledByDefault: true, + description: new LocalizableResourceString (nameof (Resources.RBI0002Description), Resources.ResourceManager, + typeof (Resources)) + ); + + /// + /// BindingType<Category> can only decorate partial classes. + /// + internal static readonly DiagnosticDescriptor RBI0003 = new ( + "RBI0003", + new LocalizableResourceString (nameof (Resources.RBI0003Title), Resources.ResourceManager, typeof (Resources)), + new LocalizableResourceString (nameof (Resources.RBI0003MessageFormat), Resources.ResourceManager, + typeof (Resources)), + "Usage", + DiagnosticSeverity.Error, + isEnabledByDefault: true, + description: new LocalizableResourceString (nameof (Resources.RBI0003Description), Resources.ResourceManager, + typeof (Resources)) + ); + + /// + /// BindingType<Category> can only decorate static classes. + /// + internal static readonly DiagnosticDescriptor RBI0004 = new ( + "RBI0004", + new LocalizableResourceString (nameof (Resources.RBI0004Title), Resources.ResourceManager, typeof (Resources)), + new LocalizableResourceString (nameof (Resources.RBI0004MessageFormat), Resources.ResourceManager, + typeof (Resources)), + "Usage", + DiagnosticSeverity.Error, + isEnabledByDefault: true, + description: new LocalizableResourceString (nameof (Resources.RBI0004Description), Resources.ResourceManager, + typeof (Resources)) + ); + + /// + /// BindingType<Protocol> can only decorate interfaces. + /// + internal static readonly DiagnosticDescriptor RBI0005 = new ( + "RBI0005", + new LocalizableResourceString (nameof (Resources.RBI0005Title), Resources.ResourceManager, typeof (Resources)), + new LocalizableResourceString (nameof (Resources.RBI0005MessageFormat), Resources.ResourceManager, + typeof (Resources)), + "Usage", + DiagnosticSeverity.Error, + isEnabledByDefault: true, + description: new LocalizableResourceString (nameof (Resources.RBI0005Description), Resources.ResourceManager, + typeof (Resources)) + ); + + /// + /// BindingType can only decorate enumerators. + /// + internal static readonly DiagnosticDescriptor RBI0006 = new ( + "RBI0006", + new LocalizableResourceString (nameof (Resources.RBI0006Title), Resources.ResourceManager, typeof (Resources)), + new LocalizableResourceString (nameof (Resources.RBI0006MessageFormat), Resources.ResourceManager, + typeof (Resources)), + "Usage", + DiagnosticSeverity.Error, + isEnabledByDefault: true, + description: new LocalizableResourceString (nameof (Resources.RBI0006Description), Resources.ResourceManager, + typeof (Resources)) + ); + + internal static readonly DiagnosticDescriptor RBI0007 = new ( + "RBI0007", + new LocalizableResourceString (nameof (Resources.RBI0007Title), Resources.ResourceManager, typeof (Resources)), + new LocalizableResourceString (nameof (Resources.RBI0007MessageFormat), Resources.ResourceManager, + typeof (Resources)), "Usage", DiagnosticSeverity.Error, isEnabledByDefault: true, - description: new LocalizableResourceString (nameof (Resources.RBI0001Description), Resources.ResourceManager, typeof (Resources)) + description: new LocalizableResourceString (nameof (Resources.RBI0007Description), Resources.ResourceManager, + typeof (Resources)) ); - public override ImmutableArray SupportedDiagnostics { get; } = [RBI0001]; + public override ImmutableArray SupportedDiagnostics { get; } = [ + RBI0001, + RBI0002, + RBI0003, + RBI0004, + RBI0005, + RBI0006, + RBI0007, + ]; public override void Initialize (AnalysisContext context) { context.ConfigureGeneratedCodeAnalysis (GeneratedCodeAnalysisFlags.None); context.EnableConcurrentExecution (); - context.RegisterSyntaxNodeAction (AnalysisContext, SyntaxKind.ClassDeclaration); + context.RegisterSyntaxNodeAction (AnalysisContext, + SyntaxKind.ClassDeclaration, + SyntaxKind.InterfaceDeclaration, + SyntaxKind.EnumDeclaration); } void AnalysisContext (SyntaxNodeAnalysisContext context) => this.AnalyzeBindingType (context); - public ImmutableArray Analyze (PlatformName _, ClassDeclarationSyntax declarationNode, INamedTypeSymbol symbol) + static readonly HashSet attributes = new HashSet (AttributesNames.BindingTypes); + public IReadOnlySet AttributeNames => attributes; + + ImmutableArray ValidateClass (BaseTypeDeclarationSyntax declarationNode, INamedTypeSymbol symbol) + { + var bucket = ImmutableArray.CreateBuilder (); + if (declarationNode is ClassDeclarationSyntax classDeclarationSyntax) { + if (!classDeclarationSyntax.IsPartial ()) { + var partialDiagnostic = Diagnostic.Create ( + descriptor: RBI0001, // Binding types should be declared as partial classes. + location: declarationNode.Identifier.GetLocation (), + messageArgs: symbol.ToDisplayString ()); + bucket.Add (partialDiagnostic); + } + } else { + var notAClassDiagnostic = Diagnostic.Create ( + descriptor: RBI0002, // BindingType must be on a class + location: declarationNode.Identifier.GetLocation (), + messageArgs: symbol.ToDisplayString ()); + bucket.Add (notAClassDiagnostic); + } + + return bucket.ToImmutable (); + } + + ImmutableArray ValidateCategory (BaseTypeDeclarationSyntax declarationNode, INamedTypeSymbol symbol) { - if (declarationNode.Modifiers.Any (x => x.IsKind (SyntaxKind.PartialKeyword))) - return []; + var bucket = ImmutableArray.CreateBuilder (); + if (declarationNode is ClassDeclarationSyntax classDeclarationSyntax) { + if (!classDeclarationSyntax.IsPartial ()) { + var partialDiagnostic = Diagnostic.Create ( + descriptor: RBI0001, // Binding types should be declared as partial classes. + location: declarationNode.Identifier.GetLocation (), + messageArgs: symbol.ToDisplayString ()); + bucket.Add (partialDiagnostic); + } - var diagnostic = Diagnostic.Create (RBI0001, // Binding types should be declared as partial classes. - declarationNode.Identifier.GetLocation (), // point to where the 'class' keyword is used - symbol.ToDisplayString ()); - return [diagnostic]; + if (!classDeclarationSyntax.IsStatic ()) { + var partialDiagnostic = Diagnostic.Create ( + descriptor: RBI0004, // BindingType must be on a static class + location: declarationNode.Identifier.GetLocation (), + messageArgs: symbol.ToDisplayString ()); + bucket.Add (partialDiagnostic); + } + } else { + var notAClassDiagnostic = Diagnostic.Create ( + descriptor: RBI0003, // BindingType must be on a class + location: declarationNode.Identifier.GetLocation (), + messageArgs: symbol.ToDisplayString ()); + bucket.Add (notAClassDiagnostic); + } + return bucket.ToImmutable (); } + + ImmutableArray ValidateProtocol (BaseTypeDeclarationSyntax declarationNode, INamedTypeSymbol symbol) + { + var bucket = ImmutableArray.CreateBuilder (); + if (declarationNode is InterfaceDeclarationSyntax interfaceDeclarationSyntax) { + if (!interfaceDeclarationSyntax.IsPartial ()) { + var partialDiagnostic = Diagnostic.Create ( + descriptor: RBI0001, // Binding types should be declared as partial classes. + location: declarationNode.Identifier.GetLocation (), + messageArgs: symbol.ToDisplayString ()); + bucket.Add (partialDiagnostic); + } + } else { + var notAInterfaceDiagnostic = Diagnostic.Create ( + descriptor: RBI0005, // BindingType must be on an interface + location: declarationNode.Identifier.GetLocation (), + messageArgs: symbol.ToDisplayString ()); + bucket.Add (notAInterfaceDiagnostic); + } + + return bucket.ToImmutable (); + } + + ImmutableArray ValidateStrongDictionary (BaseTypeDeclarationSyntax declarationNode, + INamedTypeSymbol symbol) + { + var bucket = ImmutableArray.CreateBuilder (); + if (declarationNode is ClassDeclarationSyntax classDeclarationSyntax) { + if (!classDeclarationSyntax.IsPartial ()) { + var partialDiagnostic = Diagnostic.Create ( + descriptor: RBI0001, // Binding types should be declared as partial classes. + location: declarationNode.Identifier.GetLocation (), + messageArgs: symbol.ToDisplayString ()); + bucket.Add (partialDiagnostic); + } + } else { + var notAInterfaceDiagnostic = Diagnostic.Create ( + descriptor: RBI0007, // BindingType must be on a class + location: declarationNode.Identifier.GetLocation (), + messageArgs: symbol.ToDisplayString ()); + bucket.Add (notAInterfaceDiagnostic); + } + + return bucket.ToImmutable (); + } + + public ImmutableArray ValidateSmartEnum (BaseTypeDeclarationSyntax declarationNode, + INamedTypeSymbol symbol) + { + var bucket = ImmutableArray.CreateBuilder (); + if (declarationNode is not EnumDeclarationSyntax) { + var notAInterfaceDiagnostic = Diagnostic.Create ( + descriptor: RBI0006, // BindingType must be on an enumerator + location: declarationNode.Identifier.GetLocation (), + messageArgs: symbol.ToDisplayString ()); + bucket.Add (notAInterfaceDiagnostic); + } + + return bucket.ToImmutable (); + } + + public ImmutableArray Analyze (string matchedAttribute, PlatformName _, + BaseTypeDeclarationSyntax declarationNode, INamedTypeSymbol symbol) + => matchedAttribute switch { + AttributesNames.BindingClassAttribute => ValidateClass (declarationNode, symbol), + AttributesNames.BindingCategoryAttribute => ValidateCategory (declarationNode, symbol), + AttributesNames.BindingProtocolAttribute => ValidateProtocol (declarationNode, symbol), + AttributesNames.BindingAttribute => ValidateSmartEnum (declarationNode, symbol), + AttributesNames.BindingStrongDictionaryAttribute => ValidateStrongDictionary (declarationNode, symbol), + _ => throw new InvalidOperationException ($"Not recognized attribute {matchedAttribute}.") + }; } diff --git a/src/rgen/Microsoft.Macios.Bindings.Analyzer/Extensions/BaseTypeDeclarationSyntaxExtensions.cs b/src/rgen/Microsoft.Macios.Bindings.Analyzer/Extensions/BaseTypeDeclarationSyntaxExtensions.cs new file mode 100644 index 000000000000..cc8ce3dd9b0d --- /dev/null +++ b/src/rgen/Microsoft.Macios.Bindings.Analyzer/Extensions/BaseTypeDeclarationSyntaxExtensions.cs @@ -0,0 +1,28 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +using System.Linq; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; + +namespace Microsoft.Macios.Bindings.Analyzer.Extensions; + +public static class BaseTypeDeclarationSyntaxExtensions { + + /// + /// Returns if the base type declaration was declared as a partial one. + /// + /// The declaration under test. + /// True if the declaration is partial. + public static bool IsPartial (this BaseTypeDeclarationSyntax baseTypeDeclarationSyntax) + => baseTypeDeclarationSyntax.Modifiers.Any (x => x.IsKind (SyntaxKind.PartialKeyword)); + + /// + /// Returns if the based type declaration was declared as a static one. + /// + /// The declaration under test. + /// True if the declaration is static. + public static bool IsStatic (this BaseTypeDeclarationSyntax baseTypeDeclarationSyntax) + => baseTypeDeclarationSyntax.Modifiers.Any (x => x.IsKind (SyntaxKind.StaticKeyword)); + +} diff --git a/src/rgen/Microsoft.Macios.Bindings.Analyzer/Extensions/IBindingTypeAnalyzerExtensions.cs b/src/rgen/Microsoft.Macios.Bindings.Analyzer/Extensions/IBindingTypeAnalyzerExtensions.cs index 1079f5e45b37..55dd3924013f 100644 --- a/src/rgen/Microsoft.Macios.Bindings.Analyzer/Extensions/IBindingTypeAnalyzerExtensions.cs +++ b/src/rgen/Microsoft.Macios.Bindings.Analyzer/Extensions/IBindingTypeAnalyzerExtensions.cs @@ -26,19 +26,18 @@ public static void AnalyzeBindingType (this IBindingTypeAnalyzer self, Syn return; } - // the c# syntax is a a list of lists of attributes. That is why we need to iterate through the list of lists + // The c# syntax is a a list of lists of attributes. That is why we need to iterate through the list of lists foreach (var attributeData in boundAttributes) { // based on the type use the correct parser to retrieve the data var attributeType = attributeData.AttributeClass?.ToDisplayString (); - switch (attributeType) { - case AttributesNames.BindingAttribute: - // validate that the class is partial, else we need to report an error - var diagnostics = self.Analyze (context.Compilation.GetCurrentPlatform (), - declarationNode, declaredSymbol); - foreach (var diagnostic in diagnostics) - context.ReportDiagnostic (diagnostic); - break; - } + // ignore attrs whose name we cannot get, or we do not care about + if (attributeType is null || !self.AttributeNames.Contains (attributeType)) + continue; + + var diagnostics = self.Analyze (attributeType, context.Compilation.GetCurrentPlatform (), + declarationNode, declaredSymbol); + foreach (var diagnostic in diagnostics) + context.ReportDiagnostic (diagnostic); } } } diff --git a/src/rgen/Microsoft.Macios.Bindings.Analyzer/IBindingTypeAnalyzer.cs b/src/rgen/Microsoft.Macios.Bindings.Analyzer/IBindingTypeAnalyzer.cs index 4a16c6876bc9..8e2fad622545 100644 --- a/src/rgen/Microsoft.Macios.Bindings.Analyzer/IBindingTypeAnalyzer.cs +++ b/src/rgen/Microsoft.Macios.Bindings.Analyzer/IBindingTypeAnalyzer.cs @@ -1,5 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. + +using System.Collections.Generic; using System.Collections.Immutable; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; @@ -10,5 +12,7 @@ namespace Microsoft.Macios.Bindings.Analyzer; /// Interface to be implemented by those analyzer that will be looking at BindingTypes. /// public interface IBindingTypeAnalyzer where T : BaseTypeDeclarationSyntax { - ImmutableArray Analyze (PlatformName platformName, T declarationNode, INamedTypeSymbol symbol); + IReadOnlySet AttributeNames { get; } + + ImmutableArray Analyze (string matchedAttribute, PlatformName platformName, T declarationNode, INamedTypeSymbol symbol); } diff --git a/src/rgen/Microsoft.Macios.Bindings.Analyzer/Resources.Designer.cs b/src/rgen/Microsoft.Macios.Bindings.Analyzer/Resources.Designer.cs index e671b0176398..ae3a368d09cc 100644 --- a/src/rgen/Microsoft.Macios.Bindings.Analyzer/Resources.Designer.cs +++ b/src/rgen/Microsoft.Macios.Bindings.Analyzer/Resources.Designer.cs @@ -170,5 +170,113 @@ internal static string RBI0007Title { return ResourceManager.GetString("RBI0007Title", resourceCulture); } } + + internal static string RBI0008Description { + get { + return ResourceManager.GetString("RBI0008Description", resourceCulture); + } + } + + internal static string RBI0008MessageFormat { + get { + return ResourceManager.GetString("RBI0008MessageFormat", resourceCulture); + } + } + + internal static string RBI0008Title { + get { + return ResourceManager.GetString("RBI0008Title", resourceCulture); + } + } + + internal static string RBI0009Description { + get { + return ResourceManager.GetString("RBI0009Description", resourceCulture); + } + } + + internal static string RBI0009MessageFormat { + get { + return ResourceManager.GetString("RBI0009MessageFormat", resourceCulture); + } + } + + internal static string RBI0009Title { + get { + return ResourceManager.GetString("RBI0009Title", resourceCulture); + } + } + + internal static string RBI0010Description { + get { + return ResourceManager.GetString("RBI0010Description", resourceCulture); + } + } + + internal static string RBI0010MessageFormat { + get { + return ResourceManager.GetString("RBI0010MessageFormat", resourceCulture); + } + } + + internal static string RBI0010Title { + get { + return ResourceManager.GetString("RBI0010Title", resourceCulture); + } + } + + internal static string RBI0011Description { + get { + return ResourceManager.GetString("RBI0011Description", resourceCulture); + } + } + + internal static string RBI0011MessageFormat { + get { + return ResourceManager.GetString("RBI0011MessageFormat", resourceCulture); + } + } + + internal static string RBI0011Title { + get { + return ResourceManager.GetString("RBI0011Title", resourceCulture); + } + } + + internal static string RBI0012Description { + get { + return ResourceManager.GetString("RBI0012Description", resourceCulture); + } + } + + internal static string RBI0012MessageFormat { + get { + return ResourceManager.GetString("RBI0012MessageFormat", resourceCulture); + } + } + + internal static string RBI0012Title { + get { + return ResourceManager.GetString("RBI0012Title", resourceCulture); + } + } + + internal static string RBI0013Description { + get { + return ResourceManager.GetString("RBI0013Description", resourceCulture); + } + } + + internal static string RBI0013MessageFormat { + get { + return ResourceManager.GetString("RBI0013MessageFormat", resourceCulture); + } + } + + internal static string RBI0013Title { + get { + return ResourceManager.GetString("RBI0013Title", resourceCulture); + } + } } } diff --git a/src/rgen/Microsoft.Macios.Bindings.Analyzer/Resources.resx b/src/rgen/Microsoft.Macios.Bindings.Analyzer/Resources.resx index 7b695353ded7..d7139f18bd6b 100644 --- a/src/rgen/Microsoft.Macios.Bindings.Analyzer/Resources.resx +++ b/src/rgen/Microsoft.Macios.Bindings.Analyzer/Resources.resx @@ -18,80 +18,173 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + In order for the code to be generated all binding types have to be declared as partial classes. - The binding type '{0}' must declared as a partial class + The binding type '{0}' must be declared partial '{0}' is the name of the class. Binding type declaration must be partial + + - In order for the code to be generated a smart enum value has to have a backing field. + BindingType<Class> can only decorate partial classes. + BindingType<Class> can only be used to decorate a class but was found on '{0}' which is not a class + '{0}' is the name of type. + + + BindingType<Class> must be on a class + + + + + + BindingType<Category> can only decorate partial classes. + + + BindingType<Category> can only be used to decorate a class but was found on '{0}' which is not a class + '{0}' is the name of type. + + + BindingType<Category> must be on a class + + + + + + BindingType<Category> can only decorate static classes. + + + BindingType<Category> can only be used to decorate a static class but was found on '{0}' which is not static + '{0}' is the name of type. + + + BindingType<Category> must be on a static class + + + + + + BindingType<Protocol> can only decorate interfaces. + + + BindingType<Protocol> can only be used to decorate an interface but was found on '{0}' which is not an interface + '{0}' is the name of type. + + + BindingType<Protocol> must be on an interface + + + + + + BindingType can only decorate enumerators. + + + BindingType can only be used to decorate an enumerator but was found on '{0}' which is not an enumerator + '{0}' is the name of type. + + + BindingType must be on an enumerator + + + + + + BindingType<StrongDictionary> can only decorate classes. + + + BindingType<StrongDictionary> can only be used to decorate a class but was found on '{0}' which is not a class + '{0}' is the name of type. + + + BindingType<StrongDictionary> must be on a class + + + + + + In order for the code to be generated a smart enum value has to have a backing field. + + The enum value '{0}' must be tagged with a Field<EnumValue> attribute '{0}' is the name of the enumerator value. - + Smart enum values must be tagged with an Field<EnumValue> attribute - + + + Smart enum backing field cannot appear more than once. - + The backing field '{0}' for the enum value '{1}' is already in use for the enum value '{2}' '{0}' is the name of the enum value. '{1}' is the name of a native field. '{2}' is the previous enum value - + Smart enum backing field cannot appear more than once - + + + Smart enum backing field must be a valid identifier. - + The enum value '{0}' backing field '{1}' is not a valid identifier '{0}' is the name of the enum value. '{1}' is the name of a native field. - + Smart enum backing field must represent a valid C# identifier to be used - + + + Smart enum backing field for a non Apple framework must provide a library name. - + The field attribute for the enum value '{0}' must set the property 'LibraryName' '{0}' is the name of the enumerator value. - + Non Apple framework bindings must provide a library name - + + + Fields of known Apple frameworks should not provide a LibraryName. - + The Field attribute for the enum value '{0}' must not provide a value for 'LibraryName' '{0}' is the name of the native field. - + Do not provide the LibraryName for known Apple frameworks - + + + Wrong flags were used in the FieldAttribute when applying it on an enum value. - + Used attribute '{0}' on enum value '{1}' when 'ObjCBindings.FieldAttribute<ObjCBindings.EnumValue>' was expected '{0}' is the name of an attribute. '{1}' is the name of the enumerator value. - + Enum values must be tagged with Field<EnumValue> diff --git a/src/rgen/Microsoft.Macios.Bindings.Analyzer/SmartEnumsAnalyzer.cs b/src/rgen/Microsoft.Macios.Bindings.Analyzer/SmartEnumsAnalyzer.cs index 3413f74a7b44..815ab55fe2ce 100644 --- a/src/rgen/Microsoft.Macios.Bindings.Analyzer/SmartEnumsAnalyzer.cs +++ b/src/rgen/Microsoft.Macios.Bindings.Analyzer/SmartEnumsAnalyzer.cs @@ -23,96 +23,102 @@ public class SmartEnumsAnalyzer : DiagnosticAnalyzer, IBindingTypeAnalyzer /// All enum values must have a Field attribute /// - internal static readonly DiagnosticDescriptor RBI0002 = new ( - "RBI0002", - new LocalizableResourceString (nameof (Resources.RBI0002Title), Resources.ResourceManager, typeof (Resources)), - new LocalizableResourceString (nameof (Resources.RBI0002MessageFormat), Resources.ResourceManager, + internal static readonly DiagnosticDescriptor RBI0008 = new ( + "RBI0008", + new LocalizableResourceString (nameof (Resources.RBI0008Title), Resources.ResourceManager, typeof (Resources)), + new LocalizableResourceString (nameof (Resources.RBI0008MessageFormat), Resources.ResourceManager, typeof (Resources)), "Usage", DiagnosticSeverity.Error, isEnabledByDefault: true, - description: new LocalizableResourceString (nameof (Resources.RBI0002Description), Resources.ResourceManager, + description: new LocalizableResourceString (nameof (Resources.RBI0008Description), Resources.ResourceManager, typeof (Resources)) ); /// /// Do not allow duplicated backing fields /// - internal static readonly DiagnosticDescriptor RBI0003 = new ( - "RBI0003", - new LocalizableResourceString (nameof (Resources.RBI0003Title), Resources.ResourceManager, typeof (Resources)), - new LocalizableResourceString (nameof (Resources.RBI0003MessageFormat), Resources.ResourceManager, + internal static readonly DiagnosticDescriptor RBI0009 = new ( + "RBI0009", + new LocalizableResourceString (nameof (Resources.RBI0009Title), Resources.ResourceManager, typeof (Resources)), + new LocalizableResourceString (nameof (Resources.RBI0009MessageFormat), Resources.ResourceManager, typeof (Resources)), "Usage", DiagnosticSeverity.Error, isEnabledByDefault: true, - description: new LocalizableResourceString (nameof (Resources.RBI0003Description), Resources.ResourceManager, + description: new LocalizableResourceString (nameof (Resources.RBI0009Description), Resources.ResourceManager, typeof (Resources)) ); /// /// Fields must be a valid identifier /// - internal static readonly DiagnosticDescriptor RBI0004 = new ( - "RBI0004", - new LocalizableResourceString (nameof (Resources.RBI0004Title), Resources.ResourceManager, typeof (Resources)), - new LocalizableResourceString (nameof (Resources.RBI0004MessageFormat), Resources.ResourceManager, + internal static readonly DiagnosticDescriptor RBI0010 = new ( + "RBI0010", + new LocalizableResourceString (nameof (Resources.RBI0010Title), Resources.ResourceManager, typeof (Resources)), + new LocalizableResourceString (nameof (Resources.RBI0010MessageFormat), Resources.ResourceManager, typeof (Resources)), "Usage", DiagnosticSeverity.Error, isEnabledByDefault: true, - description: new LocalizableResourceString (nameof (Resources.RBI0004Description), Resources.ResourceManager, + description: new LocalizableResourceString (nameof (Resources.RBI0010Description), Resources.ResourceManager, typeof (Resources)) ); /// /// If not an apple framework, we should provide the library path /// - internal static readonly DiagnosticDescriptor RBI0005 = new ( - "RBI0005", - new LocalizableResourceString (nameof (Resources.RBI0005Title), Resources.ResourceManager, typeof (Resources)), - new LocalizableResourceString (nameof (Resources.RBI0005MessageFormat), Resources.ResourceManager, + internal static readonly DiagnosticDescriptor RBI0011 = new ( + "RBI0011", + new LocalizableResourceString (nameof (Resources.RBI0011Title), Resources.ResourceManager, typeof (Resources)), + new LocalizableResourceString (nameof (Resources.RBI0011MessageFormat), Resources.ResourceManager, typeof (Resources)), "Usage", DiagnosticSeverity.Error, isEnabledByDefault: true, - description: new LocalizableResourceString (nameof (Resources.RBI0005Description), Resources.ResourceManager, + description: new LocalizableResourceString (nameof (Resources.RBI0011Description), Resources.ResourceManager, typeof (Resources)) ); /// /// if apple framework, the library path should be empty /// - internal static readonly DiagnosticDescriptor RBI0006 = new ( - "RBI0006", - new LocalizableResourceString (nameof (Resources.RBI0006Title), Resources.ResourceManager, typeof (Resources)), - new LocalizableResourceString (nameof (Resources.RBI0006MessageFormat), Resources.ResourceManager, + internal static readonly DiagnosticDescriptor RBI0012 = new ( + "RBI0012", + new LocalizableResourceString (nameof (Resources.RBI0012Title), Resources.ResourceManager, typeof (Resources)), + new LocalizableResourceString (nameof (Resources.RBI0012MessageFormat), Resources.ResourceManager, typeof (Resources)), "Usage", DiagnosticSeverity.Warning, isEnabledByDefault: true, - description: new LocalizableResourceString (nameof (Resources.RBI0006Description), Resources.ResourceManager, + description: new LocalizableResourceString (nameof (Resources.RBI0012Description), Resources.ResourceManager, typeof (Resources)) ); /// /// User used the wrong flag for the attribute /// - internal static readonly DiagnosticDescriptor RBI0007 = new ( - "RBI0007", - new LocalizableResourceString (nameof (Resources.RBI0007Title), Resources.ResourceManager, typeof (Resources)), - new LocalizableResourceString (nameof (Resources.RBI0007MessageFormat), Resources.ResourceManager, + internal static readonly DiagnosticDescriptor RBI0013 = new ( + "RBI0013", + new LocalizableResourceString (nameof (Resources.RBI0013Title), Resources.ResourceManager, typeof (Resources)), + new LocalizableResourceString (nameof (Resources.RBI0013MessageFormat), Resources.ResourceManager, typeof (Resources)), "Usage", DiagnosticSeverity.Error, isEnabledByDefault: true, - description: new LocalizableResourceString (nameof (Resources.RBI0007Description), Resources.ResourceManager, + description: new LocalizableResourceString (nameof (Resources.RBI0013Description), Resources.ResourceManager, typeof (Resources)) ); - public override ImmutableArray SupportedDiagnostics { get; } = - [RBI0002, RBI0003, RBI0004, RBI0005, RBI0006, RBI0007]; + public override ImmutableArray SupportedDiagnostics { get; } = [ + RBI0008, + RBI0009, + RBI0010, + RBI0011, + RBI0012, + RBI0013, + ]; public override void Initialize (AnalysisContext context) { @@ -124,7 +130,10 @@ public override void Initialize (AnalysisContext context) void AnalysisContext (SyntaxNodeAnalysisContext context) => this.AnalyzeBindingType (context); - public ImmutableArray Analyze (PlatformName platformName, EnumDeclarationSyntax declarationNode, + static readonly HashSet attributes = [AttributesNames.BindingAttribute]; + public IReadOnlySet AttributeNames => attributes; + + public ImmutableArray Analyze (string matchedAttribute, PlatformName platformName, EnumDeclarationSyntax declarationNode, INamedTypeSymbol symbol) { // we want to ensure several things: @@ -162,7 +171,7 @@ public ImmutableArray Analyze (PlatformName platformName, EnumDeclar if (attributes.Count == 0) { // All enum values are marked with a Field attribute, therefore add a diagnostic bucket.Add (Diagnostic.Create ( - RBI0002, // Smart enum values must be tagged with an Field attribute. + RBI0008, // Smart enum values must be tagged with an Field attribute. fieldSymbol.Locations.First (), fieldSymbol.ToDisplayString ())); continue; @@ -183,7 +192,7 @@ public ImmutableArray Analyze (PlatformName platformName, EnumDeclar if (backingFields.TryGetValue (fieldData.Value.SymbolName, out var previousEnumValue)) { // All symbol names have to be unique bucket.Add (Diagnostic.Create ( - RBI0003, // The backing field '{0}' for the enum value '{1}' is already in use for the enum value '{2}' + RBI0009, // The backing field '{0}' for the enum value '{1}' is already in use for the enum value '{2}' fieldSyntax.GetLocation (), fieldSymbol.ToDisplayString (), fieldData.Value.SymbolName, fieldSymbol.ToDisplayString ().Trim (), previousEnumValue)); @@ -196,7 +205,7 @@ public ImmutableArray Analyze (PlatformName platformName, EnumDeclar // If the Field attribute is not from a known apple library, the library name is set if (string.IsNullOrWhiteSpace (fieldData.Value.LibraryName)) { bucket.Add (Diagnostic.Create ( - RBI0005, // Non Apple framework bindings must provide a library name. + RBI0011, // Non Apple framework bindings must provide a library name. fieldSyntax.GetLocation (), fieldSymbol.ToDisplayString ())); } @@ -204,7 +213,7 @@ public ImmutableArray Analyze (PlatformName platformName, EnumDeclar // If the Field attribute is from a known apple library, the lib should be null if (fieldData.Value.LibraryName is not null) { bucket.Add (Diagnostic.Create ( - RBI0006, // Do not provide the LibraryName for known Apple frameworks. + RBI0012, // Do not provide the LibraryName for known Apple frameworks. fieldSyntax.GetLocation (), fieldSymbol.ToDisplayString ())); } @@ -214,7 +223,7 @@ public ImmutableArray Analyze (PlatformName platformName, EnumDeclar switch (errorTuple.Error) { case FieldData.ParsingError.NotIdentifier: // Backing field is not a valid identifier. - bucket.Add (Diagnostic.Create (RBI0004, + bucket.Add (Diagnostic.Create (RBI0010, fieldSyntax .GetLocation (), // Smart enum backing field must represent a valid C# identifier to be used. fieldSymbol.ToDisplayString (), errorTuple.Value)); @@ -227,7 +236,7 @@ public ImmutableArray Analyze (PlatformName platformName, EnumDeclar .FirstOrDefault (s => s.StartsWith (AttributesNames.FieldAttribute)); if (fieldAttr is not null) { bucket.Add (Diagnostic.Create ( - RBI0007, // Enum values must be tagged with Field. + RBI0013, // Enum values must be tagged with Field. fieldSymbol.Locations.First (), fieldAttr, fieldSymbol.ToDisplayString ())); } diff --git a/src/rgen/Microsoft.Macios.Generator/AttributesNames.cs b/src/rgen/Microsoft.Macios.Generator/AttributesNames.cs index 2a59e148b1ae..436e45225e2f 100644 --- a/src/rgen/Microsoft.Macios.Generator/AttributesNames.cs +++ b/src/rgen/Microsoft.Macios.Generator/AttributesNames.cs @@ -13,6 +13,7 @@ static class AttributesNames { public const string BindingCategoryAttribute = "ObjCBindings.BindingTypeAttribute"; public const string BindingClassAttribute = "ObjCBindings.BindingTypeAttribute"; public const string BindingProtocolAttribute = "ObjCBindings.BindingTypeAttribute"; + public const string BindingStrongDictionaryAttribute = "ObjCBindings.BindingTypeAttribute"; public const string FieldAttribute = "ObjCBindings.FieldAttribute"; public const string EnumFieldAttribute = "ObjCBindings.FieldAttribute"; public const string ExportFieldAttribute = "ObjCBindings.ExportAttribute"; @@ -26,7 +27,8 @@ static class AttributesNames { BindingAttribute, BindingCategoryAttribute, BindingClassAttribute, - BindingProtocolAttribute + BindingProtocolAttribute, + BindingStrongDictionaryAttribute, ]; @@ -42,6 +44,9 @@ static class AttributesNames { if (type == typeof (ObjCBindings.Protocol)) { return BindingProtocolAttribute; } + if (type == typeof (ObjCBindings.StrongDictionary)) { + return BindingStrongDictionaryAttribute; + } return null; } diff --git a/tests/rgen/Microsoft.Macios.Bindings.Analyzer.Tests/BindingTypeSemanticAnalyzerTests.cs b/tests/rgen/Microsoft.Macios.Bindings.Analyzer.Tests/BindingTypeSemanticAnalyzerTests.cs index fd77bd9e9e7a..493566bc3d17 100644 --- a/tests/rgen/Microsoft.Macios.Bindings.Analyzer.Tests/BindingTypeSemanticAnalyzerTests.cs +++ b/tests/rgen/Microsoft.Macios.Bindings.Analyzer.Tests/BindingTypeSemanticAnalyzerTests.cs @@ -1,5 +1,8 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. + +using System.Collections; +using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Microsoft.CodeAnalysis; @@ -10,12 +13,115 @@ namespace Microsoft.Macios.Bindings.Analyzer.Tests; public class BindingTypeSemanticAnalyzerTests : BaseGeneratorWithAnalyzerTestClass { + class TestDataBindingTypeAnalyzerTests : IEnumerable { + public IEnumerator GetEnumerator () + { + const string nonPartialClassBinding = @" +using ObjCBindings; - [Theory] - [AllSupportedPlatforms] - public async Task BindingTypeMustBePartial (ApplePlatform platform) - { - const string inputText = @" +namespace Test { + [BindingType] + public class Examples { + } +} +"; + yield return [ + nonPartialClassBinding, + BindingTypeSemanticAnalyzer.RBI0001.Id, + "The binding type 'Test.Examples' must be declared partial" + ]; + + const string classBindingNotClas = @" +using ObjCBindings; + +namespace Test { + [BindingType] + public interface Examples { + } +} +"; + yield return [ + classBindingNotClas, + BindingTypeSemanticAnalyzer.RBI0002.Id, + "BindingType can only be used to decorate a class but was found on 'Test.Examples' which is not a class" + ]; + + const string nonPartialCategory = @" +using ObjCBindings; + +namespace Test { + [BindingType] + public class Examples { + } +} +"; + yield return [ + nonPartialCategory, + BindingTypeSemanticAnalyzer.RBI0001.Id, + "The binding type 'Test.Examples' must be declared partial" + ]; + + const string nonClassCategory = @" +using ObjCBindings; + +namespace Test { + [BindingType] + public interface Examples { + } +} +"; + yield return [ + nonClassCategory, + BindingTypeSemanticAnalyzer.RBI0003.Id, + "BindingType can only be used to decorate a class but was found on 'Test.Examples' which is not a class" + ]; + + const string nonStaticCategory = @" +using ObjCBindings; + +namespace Test { + [BindingType] + public partial class Examples { + } +} +"; + yield return [ + nonStaticCategory, + BindingTypeSemanticAnalyzer.RBI0004.Id, + "BindingType can only be used to decorate a static class but was found on 'Test.Examples' which is not static" + ]; + + const string nonPartialProtocol = @" +using ObjCBindings; + +namespace Test { + [BindingType] + public interface Examples { + } +} +"; + yield return [ + nonPartialProtocol, + BindingTypeSemanticAnalyzer.RBI0001.Id, + "The binding type 'Test.Examples' must be declared partial" + ]; + + const string nonInterfaceProtocol = @" +using ObjCBindings; + +namespace Test { + [BindingType] + public class Examples { + } +} +"; + yield return [ + nonInterfaceProtocol, + BindingTypeSemanticAnalyzer.RBI0005.Id, + "BindingType can only be used to decorate an interface but was found on 'Test.Examples' which is not an interface" + ]; + + const string nonSmartEnum = @" using ObjCBindings; namespace Test { @@ -24,14 +130,57 @@ public class Examples { } } "; + yield return [ + nonSmartEnum, + BindingTypeSemanticAnalyzer.RBI0006.Id, + "BindingType can only be used to decorate an enumerator but was found on 'Test.Examples' which is not an enumerator" + ]; + + const string nonPartialStrongDictionary = @" +using ObjCBindings; + +namespace Test { + [BindingType] + public class Examples { + } +} +"; + yield return [ + nonPartialStrongDictionary, + BindingTypeSemanticAnalyzer.RBI0001.Id, + "The binding type 'Test.Examples' must be declared partial" + ]; + + const string nonClassStrongDictionary = @" +using ObjCBindings; + +namespace Test { + [BindingType] + public interface Examples { + } +} +"; + yield return [ + nonClassStrongDictionary, + BindingTypeSemanticAnalyzer.RBI0007.Id, + "BindingType can only be used to decorate a class but was found on 'Test.Examples' which is not a class" + ]; + } + IEnumerator IEnumerable.GetEnumerator () => GetEnumerator (); + } + + [Theory] + [AllSupportedPlatformsClassData] + public async Task BindingTypeAnalyzerTests (ApplePlatform platform, string inputText, string diagnosticId, + string diagnosticMessage) + { var (compilation, _) = CreateCompilation (platform, sources: inputText); var diagnostics = await RunAnalyzer (new BindingTypeSemanticAnalyzer (), compilation); var analyzerDiagnotics = diagnostics - .Where (d => d.Id == BindingTypeSemanticAnalyzer.RBI0001.Id).ToArray (); + .Where (d => d.Id == diagnosticId).ToArray (); Assert.Single (analyzerDiagnotics); // verify the diagnostic message - VerifyDiagnosticMessage (analyzerDiagnotics [0], BindingTypeSemanticAnalyzer.RBI0001.Id, - DiagnosticSeverity.Error, "The binding type 'Test.Examples' must declared as a partial class"); + VerifyDiagnosticMessage (analyzerDiagnotics [0], diagnosticId, DiagnosticSeverity.Error, diagnosticMessage); } } diff --git a/tests/rgen/Microsoft.Macios.Bindings.Analyzer.Tests/SmartEnumsAnalyzerTests.cs b/tests/rgen/Microsoft.Macios.Bindings.Analyzer.Tests/SmartEnumsAnalyzerTests.cs index 6cd3eb14d3b0..ddb2b019b09d 100644 --- a/tests/rgen/Microsoft.Macios.Bindings.Analyzer.Tests/SmartEnumsAnalyzerTests.cs +++ b/tests/rgen/Microsoft.Macios.Bindings.Analyzer.Tests/SmartEnumsAnalyzerTests.cs @@ -43,10 +43,10 @@ public enum AVCaptureSystemPressureExampleLevel { var (compilation, _) = CreateCompilation (platform, sources: inputText); var diagnostics = await RunAnalyzer (new SmartEnumsAnalyzer (), compilation); var analyzerDiagnotics = diagnostics - .Where (d => d.Id == SmartEnumsAnalyzer.RBI0002.Id).ToArray (); + .Where (d => d.Id == SmartEnumsAnalyzer.RBI0008.Id).ToArray (); Assert.Single (analyzerDiagnotics); // verify the diagnostic message - VerifyDiagnosticMessage (analyzerDiagnotics [0], SmartEnumsAnalyzer.RBI0002.Id, + VerifyDiagnosticMessage (analyzerDiagnotics [0], SmartEnumsAnalyzer.RBI0008.Id, DiagnosticSeverity.Error, "The enum value 'AVFoundation.AVCaptureSystemPressureExampleLevel.Shutdown' must be tagged with a Field attribute"); } @@ -131,9 +131,9 @@ public async Task SmartEnumSymbolMustBeValidIdentifier (ApplePlatform platform, var (compilation, _) = CreateCompilation (platform, sources: inputText); var diagnostics = await RunAnalyzer (new SmartEnumsAnalyzer (), compilation); var analyzerDiagnotics = diagnostics - .Where (d => d.Id == SmartEnumsAnalyzer.RBI0004.Id).ToArray (); + .Where (d => d.Id == SmartEnumsAnalyzer.RBI0010.Id).ToArray (); Assert.Single (analyzerDiagnotics); - VerifyDiagnosticMessage (analyzerDiagnotics [0], SmartEnumsAnalyzer.RBI0004.Id, + VerifyDiagnosticMessage (analyzerDiagnotics [0], SmartEnumsAnalyzer.RBI0010.Id, DiagnosticSeverity.Error, $"The enum value 'AVFoundation.AVCaptureSystemPressureExampleLevel.Shutdown' backing field '{fieldValue}' is not a valid identifier"); } @@ -199,9 +199,9 @@ public async Task SmartEnumAppleFrameworkNotLibrary (ApplePlatform platform, str var diagnostics = await RunAnalyzer (new SmartEnumsAnalyzer (), compilation); var analyzerDiagnotics = diagnostics - .Where (d => d.Id == SmartEnumsAnalyzer.RBI0006.Id).ToArray (); + .Where (d => d.Id == SmartEnumsAnalyzer.RBI0012.Id).ToArray (); Assert.Single (analyzerDiagnotics); - VerifyDiagnosticMessage (analyzerDiagnotics [0], SmartEnumsAnalyzer.RBI0006.Id, + VerifyDiagnosticMessage (analyzerDiagnotics [0], SmartEnumsAnalyzer.RBI0012.Id, DiagnosticSeverity.Warning, "The Field attribute for the enum value 'AVFoundation.AVCaptureSystemPressureExampleLevel.Shutdown' must not provide a value for 'LibraryName'"); } @@ -278,9 +278,9 @@ public async Task SmartEnumThirdPartyLibrary (ApplePlatform platform, string inp var (compilation, _) = CreateCompilation (platform, sources: inputText); var diagnostics = await RunAnalyzer (new SmartEnumsAnalyzer (), compilation); var analyzerDiagnotics = diagnostics - .Where (d => d.Id == SmartEnumsAnalyzer.RBI0005.Id).ToArray (); + .Where (d => d.Id == SmartEnumsAnalyzer.RBI0011.Id).ToArray (); Assert.Single (analyzerDiagnotics); - VerifyDiagnosticMessage (analyzerDiagnotics [0], SmartEnumsAnalyzer.RBI0005.Id, + VerifyDiagnosticMessage (analyzerDiagnotics [0], SmartEnumsAnalyzer.RBI0011.Id, DiagnosticSeverity.Error, "The field attribute for the enum value 'CustomLibrary.CustomLibraryEnum.High' must set the property 'LibraryName'"); } @@ -308,7 +308,7 @@ public enum CustomLibraryEnum { var (compilation, _) = CreateCompilation (platform, sources: inputText); var diagnostics = await RunAnalyzer (new SmartEnumsAnalyzer (), compilation); var analyzerDiagnotics = diagnostics - .Where (d => d.Id == SmartEnumsAnalyzer.RBI0002.Id).ToArray (); + .Where (d => d.Id == SmartEnumsAnalyzer.RBI0008.Id).ToArray (); // we should have a diagnostic for each enum value Assert.Equal (3, analyzerDiagnotics.Length); } @@ -346,9 +346,9 @@ public enum AVCaptureSystemPressureExampleLevel { var (compilation, _) = CreateCompilation (platform, sources: inputText); var diagnostics = await RunAnalyzer (new SmartEnumsAnalyzer (), compilation); var analyzerDiagnotics = diagnostics - .Where (d => d.Id == SmartEnumsAnalyzer.RBI0003.Id).ToArray (); + .Where (d => d.Id == SmartEnumsAnalyzer.RBI0009.Id).ToArray (); Assert.Single (analyzerDiagnotics); - VerifyDiagnosticMessage (analyzerDiagnotics [0], SmartEnumsAnalyzer.RBI0003.Id, + VerifyDiagnosticMessage (analyzerDiagnotics [0], SmartEnumsAnalyzer.RBI0009.Id, DiagnosticSeverity.Error, "The backing field 'AVFoundation.AVCaptureSystemPressureExampleLevel.Fair' for the enum value 'AVCaptureSystemPressureLevelNominal' is already in use for the enum value 'AVFoundation.AVCaptureSystemPressureExampleLevel.Fair'"); } @@ -373,11 +373,11 @@ public enum AVCaptureSystemPressureExampleLevel { var (compilation, _) = CreateCompilation (platform, sources: inputText); var diagnostics = await RunAnalyzer (new SmartEnumsAnalyzer (), compilation); var analyzerDiagnotics = diagnostics - .Where (d => d.Id == SmartEnumsAnalyzer.RBI0007.Id).ToArray (); + .Where (d => d.Id == SmartEnumsAnalyzer.RBI0013.Id).ToArray (); // we should have a diagnostic for each enum value Assert.Single (analyzerDiagnotics); - VerifyDiagnosticMessage (analyzerDiagnotics [0], SmartEnumsAnalyzer.RBI0007.Id, + VerifyDiagnosticMessage (analyzerDiagnotics [0], SmartEnumsAnalyzer.RBI0013.Id, DiagnosticSeverity.Error, "Used attribute 'ObjCBindings.FieldAttribute' on enum value 'AVFoundation.AVCaptureSystemPressureExampleLevel.Shutdown' when 'ObjCBindings.FieldAttribute' was expected"); } diff --git a/tests/rgen/Microsoft.Macios.Generator.Tests/AttributesNamesTests.cs b/tests/rgen/Microsoft.Macios.Generator.Tests/AttributesNamesTests.cs index ce64599b0ba1..02fadc628298 100644 --- a/tests/rgen/Microsoft.Macios.Generator.Tests/AttributesNamesTests.cs +++ b/tests/rgen/Microsoft.Macios.Generator.Tests/AttributesNamesTests.cs @@ -27,6 +27,7 @@ public void GetFieldAttributeName (T @enum, string? expectedName) where T : E [InlineData (Category.Default, AttributesNames.BindingCategoryAttribute)] [InlineData (Class.Default, AttributesNames.BindingClassAttribute)] [InlineData (Protocol.Default, AttributesNames.BindingProtocolAttribute)] + [InlineData (StrongDictionary.Default, AttributesNames.BindingStrongDictionaryAttribute)] public void GetBindingTypeAttributeName (T @enum, string? expectedName) where T : Enum { Assert.NotNull (@enum);