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