diff --git a/src/Makefile b/src/Makefile
index bb088101958..4c5b8157cad 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -456,6 +456,7 @@ $($(2)_DOTNET_BUILD_DIR)/$(4)/Microsoft.$(1)%dll $($(2)_DOTNET_BUILD_DIR)/$(4)/M
$(DOTNET_CSC) \
$(DOTNET_FLAGS) \
/analyzer:$(ROSLYN_GENERATOR) \
+ /generatedfilesout:$($(2)_DOTNET_BUILD_DIR)/generated-sources \
-unsafe \
-optimize \
$$(ARGS_$(1)) \
diff --git a/src/rgen/Microsoft.Macios.Binding.Common/Microsoft.Macios.Binding.Common.csproj b/src/rgen/Microsoft.Macios.Binding.Common/Microsoft.Macios.Binding.Common.csproj
new file mode 100644
index 00000000000..1698ed678a9
--- /dev/null
+++ b/src/rgen/Microsoft.Macios.Binding.Common/Microsoft.Macios.Binding.Common.csproj
@@ -0,0 +1,27 @@
+
+
+
+ net8.0
+ enable
+ enable
+
+
+
+
+ external\ApplePlatform.cs
+
+
+ external\SdkVersions.cs
+
+
+ external\TargetFramework.cs
+
+
+ external\Frameworks.cs
+
+
+ external\PlatformName.cs
+
+
+
+
diff --git a/src/rgen/Microsoft.Macios.Bindings.Analyzer.Sample/Microsoft.Macios.Bindings.Analyzer.Sample.csproj b/src/rgen/Microsoft.Macios.Bindings.Analyzer.Sample/Microsoft.Macios.Bindings.Analyzer.Sample.csproj
index a02eefcf4af..9a9e7f03974 100644
--- a/src/rgen/Microsoft.Macios.Bindings.Analyzer.Sample/Microsoft.Macios.Bindings.Analyzer.Sample.csproj
+++ b/src/rgen/Microsoft.Macios.Bindings.Analyzer.Sample/Microsoft.Macios.Bindings.Analyzer.Sample.csproj
@@ -7,6 +7,7 @@
+
@@ -15,12 +16,6 @@
external\BindginTypeAttribute.cs
-
- external\Attributes.cs
-
-
- external\PlatformName.cs
-
diff --git a/src/rgen/Microsoft.Macios.Bindings.Analyzer/AnalyzerReleases.Shipped.md b/src/rgen/Microsoft.Macios.Bindings.Analyzer/AnalyzerReleases.Shipped.md
index 5a165075fbc..0b2cd20c485 100644
--- a/src/rgen/Microsoft.Macios.Bindings.Analyzer/AnalyzerReleases.Shipped.md
+++ b/src/rgen/Microsoft.Macios.Bindings.Analyzer/AnalyzerReleases.Shipped.md
@@ -3,5 +3,4 @@
### New Rules
| Rule ID | Category | Severity | Notes |
-|---------|----------|----------|------------------------------------------------------|
-| RBI0001 | Usage | Error | Binding types should be declared as partial classes. |
\ No newline at end of file
+|---------|----------|----------|------------------------------------------------------|
\ No newline at end of file
diff --git a/src/rgen/Microsoft.Macios.Bindings.Analyzer/AnalyzerReleases.Unshipped.md b/src/rgen/Microsoft.Macios.Bindings.Analyzer/AnalyzerReleases.Unshipped.md
index 44f7c8f4ef7..20f6010e33e 100644
--- a/src/rgen/Microsoft.Macios.Bindings.Analyzer/AnalyzerReleases.Unshipped.md
+++ b/src/rgen/Microsoft.Macios.Bindings.Analyzer/AnalyzerReleases.Unshipped.md
@@ -1,4 +1,9 @@
### New Rules
-| Rule ID | Category | Severity | Notes |
-|---------|----------|----------|-------|
\ No newline at end of file
+| 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 FieldAttribute. |
+| RBI0003 | Usage | Error | Smart enum backing field cannot be an empty string. |
+| RBI0004 | Usage | Error | Non Apple framework bindings must provide a library name. |
+| RBI0005 | Usage | Warning | Do not provide the LibraryName for known Apple frameworks. |
diff --git a/src/rgen/Microsoft.Macios.Bindings.Analyzer/BindingTypeCodeFixProvider.cs b/src/rgen/Microsoft.Macios.Bindings.Analyzer/BindingTypeCodeFixProvider.cs
index 17b8a298360..036b08041c1 100644
--- a/src/rgen/Microsoft.Macios.Bindings.Analyzer/BindingTypeCodeFixProvider.cs
+++ b/src/rgen/Microsoft.Macios.Bindings.Analyzer/BindingTypeCodeFixProvider.cs
@@ -20,7 +20,7 @@ namespace Microsoft.Macios.Bindings.Analyzer;
public class BindingTypeCodeFixProvider : CodeFixProvider {
// Specify the diagnostic IDs of analyzers that are expected to be linked.
public sealed override ImmutableArray FixableDiagnosticIds { get; } =
- ImmutableArray.Create (BindingTypeSemanticAnalyzer.DiagnosticId);
+ ImmutableArray.Create (BindingTypeSemanticAnalyzer.RBI0001.Id);
// If you don't need the 'fix all' behaviour, return null.
public override FixAllProvider? GetFixAllProvider () => null;
diff --git a/src/rgen/Microsoft.Macios.Bindings.Analyzer/BindingTypeSemanticAnalyzer.cs b/src/rgen/Microsoft.Macios.Bindings.Analyzer/BindingTypeSemanticAnalyzer.cs
index fd5df71a0e8..2e535572d9f 100644
--- a/src/rgen/Microsoft.Macios.Bindings.Analyzer/BindingTypeSemanticAnalyzer.cs
+++ b/src/rgen/Microsoft.Macios.Bindings.Analyzer/BindingTypeSemanticAnalyzer.cs
@@ -4,6 +4,7 @@
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;
+using Microsoft.Macios.Bindings.Analyzer.Extensions;
namespace Microsoft.Macios.Bindings.Analyzer;
@@ -12,23 +13,20 @@ namespace Microsoft.Macios.Bindings.Analyzer;
/// pattern.
///
[DiagnosticAnalyzer (LanguageNames.CSharp)]
-public class BindingTypeSemanticAnalyzer : DiagnosticAnalyzer {
- internal const string DiagnosticId = "RBI0001";
- static readonly LocalizableString Title = new LocalizableResourceString (nameof (Resources.RBI0001Title),
- Resources.ResourceManager, typeof (Resources));
- static readonly LocalizableString MessageFormat =
- new LocalizableResourceString (nameof (Resources.RBI0001MessageFormat), Resources.ResourceManager,
- typeof (Resources));
- static readonly LocalizableString Description =
- new LocalizableResourceString (nameof (Resources.RBI0001Description), Resources.ResourceManager,
- typeof (Resources));
- const string Category = "Usage";
+public class BindingTypeSemanticAnalyzer : DiagnosticAnalyzer, IBindingTypeAnalyzer {
- static readonly DiagnosticDescriptor RBI0001 = new (DiagnosticId, Title, MessageFormat, Category,
- DiagnosticSeverity.Error, isEnabledByDefault: true, description: Description);
- public override ImmutableArray SupportedDiagnostics { get; } =
- ImmutableArray.Create (RBI0001);
+ 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)),
+ "Usage",
+ DiagnosticSeverity.Error,
+ isEnabledByDefault: true,
+ description: new LocalizableResourceString (nameof (Resources.RBI0001Description), Resources.ResourceManager, typeof (Resources))
+ );
+
+ public override ImmutableArray SupportedDiagnostics { get; } = [RBI0001];
public override void Initialize (AnalysisContext context)
{
@@ -38,35 +36,17 @@ public override void Initialize (AnalysisContext context)
}
void AnalysisContext (SyntaxNodeAnalysisContext context)
- {
- // only care about classes
- if (context.Node is not ClassDeclarationSyntax classDeclarationNode)
- return;
+ => this.AnalyzeBindingType (context);
- var classSymbol = context.SemanticModel.GetDeclaredSymbol (classDeclarationNode);
- if (classSymbol is null)
- return;
+ public ImmutableArray Analyze (PlatformName _, ClassDeclarationSyntax declarationNode, INamedTypeSymbol symbol)
+ {
+ if (declarationNode.Modifiers.Any (x => x.IsKind (SyntaxKind.PartialKeyword)))
+ return [];
- var boundAttributes = classSymbol.GetAttributes ();
- if (boundAttributes.Length == 0) {
- return;
- }
+ var diagnostic = Diagnostic.Create (RBI0001,
+ declarationNode.Identifier.GetLocation (), // point to where the 'class' keyword is used
+ symbol.ToDisplayString ());
+ return [diagnostic];
- // 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 "ObjCBindings.BindingTypeAttribute":
- // validate that the class is partial, else we need to report an error
- if (!classDeclarationNode.Modifiers.Any (x => x.IsKind (SyntaxKind.PartialKeyword))) {
- var diagnostic = Diagnostic.Create (RBI0001,
- classDeclarationNode.Identifier.GetLocation (), // point to where the 'class' keyword is used
- classSymbol.ToDisplayString ());
- context.ReportDiagnostic (diagnostic);
- }
- break;
- }
- }
}
}
diff --git a/src/rgen/Microsoft.Macios.Bindings.Analyzer/DiagnosticInfo.cs b/src/rgen/Microsoft.Macios.Bindings.Analyzer/DiagnosticInfo.cs
new file mode 100644
index 00000000000..95e9f8c3ab6
--- /dev/null
+++ b/src/rgen/Microsoft.Macios.Bindings.Analyzer/DiagnosticInfo.cs
@@ -0,0 +1,17 @@
+using Microsoft.CodeAnalysis;
+
+namespace Microsoft.Macios.Bindings.Analyzer;
+
+public struct DiagnosticInfo (
+ string id,
+ LocalizableResourceString title,
+ LocalizableResourceString messageFormat,
+ LocalizableResourceString description,
+ string category) {
+
+ public string Id { get; } = id;
+ public LocalizableResourceString Title { get; } = title;
+ public LocalizableResourceString MessageFormat { get; } = messageFormat;
+ public LocalizableResourceString Description { get; } = description;
+ public string Category { get; } = category;
+}
diff --git a/src/rgen/Microsoft.Macios.Bindings.Analyzer/Extensions/IBindingTypeAnalyzerExtensions.cs b/src/rgen/Microsoft.Macios.Bindings.Analyzer/Extensions/IBindingTypeAnalyzerExtensions.cs
new file mode 100644
index 00000000000..a08eb31c692
--- /dev/null
+++ b/src/rgen/Microsoft.Macios.Bindings.Analyzer/Extensions/IBindingTypeAnalyzerExtensions.cs
@@ -0,0 +1,42 @@
+using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Microsoft.CodeAnalysis.Diagnostics;
+using Microsoft.Macios.Generator;
+using Microsoft.Macios.Generator.Extensions;
+using Diagnostic = Microsoft.CodeAnalysis.Diagnostic;
+
+namespace Microsoft.Macios.Bindings.Analyzer.Extensions;
+
+public static class BindingTypeAnalyzerExtensions {
+ public static void AnalyzeBindingType (this IBindingTypeAnalyzer self, SyntaxNodeAnalysisContext context) where T : BaseTypeDeclarationSyntax
+ {
+ // calculate the current compilation platform name
+ if (context.Node is not T declarationNode)
+ return;
+
+ var declaredSymbol = context.SemanticModel.GetDeclaredSymbol (declarationNode);
+ if (declaredSymbol is null)
+ return;
+
+ var boundAttributes = declaredSymbol.GetAttributes ();
+ if (boundAttributes.Length == 0) {
+ // do nothing since our generator only cares about declared types with the BindingType attribute
+ return;
+ }
+
+ // 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;
+ }
+ }
+ }
+}
diff --git a/src/rgen/Microsoft.Macios.Bindings.Analyzer/Extensions/PlatformNameExtensions.cs b/src/rgen/Microsoft.Macios.Bindings.Analyzer/Extensions/PlatformNameExtensions.cs
new file mode 100644
index 00000000000..2564975dfbe
--- /dev/null
+++ b/src/rgen/Microsoft.Macios.Bindings.Analyzer/Extensions/PlatformNameExtensions.cs
@@ -0,0 +1,16 @@
+using Xamarin.Utils;
+
+namespace Microsoft.Macios.Bindings.Analyzer.Extensions;
+
+public static class PlatformNameExtensions {
+
+ public static ApplePlatform ToApplePlatform (this PlatformName platformName)
+ => platformName switch {
+ PlatformName.iOS => ApplePlatform.iOS,
+ PlatformName.MacCatalyst => ApplePlatform.MacCatalyst,
+ PlatformName.MacOSX => ApplePlatform.MacOSX,
+ PlatformName.TvOS => ApplePlatform.TVOS,
+ _ => ApplePlatform.None,
+ };
+
+}
diff --git a/src/rgen/Microsoft.Macios.Bindings.Analyzer/IBindingTypeAnalyzer.cs b/src/rgen/Microsoft.Macios.Bindings.Analyzer/IBindingTypeAnalyzer.cs
new file mode 100644
index 00000000000..42e7759bf5f
--- /dev/null
+++ b/src/rgen/Microsoft.Macios.Bindings.Analyzer/IBindingTypeAnalyzer.cs
@@ -0,0 +1,12 @@
+using System.Collections.Immutable;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+
+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);
+}
diff --git a/src/rgen/Microsoft.Macios.Bindings.Analyzer/Microsoft.Macios.Bindings.Analyzer.csproj b/src/rgen/Microsoft.Macios.Bindings.Analyzer/Microsoft.Macios.Bindings.Analyzer.csproj
index 6f9e0902e43..3c9ecfb6775 100644
--- a/src/rgen/Microsoft.Macios.Bindings.Analyzer/Microsoft.Macios.Bindings.Analyzer.csproj
+++ b/src/rgen/Microsoft.Macios.Bindings.Analyzer/Microsoft.Macios.Bindings.Analyzer.csproj
@@ -14,12 +14,13 @@
-
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
-
-
+
+
+
@@ -43,4 +44,23 @@
+
+
+ external\AttributesNames.cs
+
+
+ external\FieldData.cs
+
+
+ Extensions\CompilationExtensions.cs
+
+
+ Extensions\TypeSymbolExtensions.cs
+
+
+
+
+
+
+
diff --git a/src/rgen/Microsoft.Macios.Bindings.Analyzer/Resources.Designer.cs b/src/rgen/Microsoft.Macios.Bindings.Analyzer/Resources.Designer.cs
index bfe0d3152b0..157059a7c12 100644
--- a/src/rgen/Microsoft.Macios.Bindings.Analyzer/Resources.Designer.cs
+++ b/src/rgen/Microsoft.Macios.Bindings.Analyzer/Resources.Designer.cs
@@ -45,51 +45,99 @@ internal static System.Globalization.CultureInfo Culture {
}
}
- internal static string AB0001Description {
+ internal static string RBI0001CodeFixTitle {
get {
- return ResourceManager.GetString("AB0001Description", resourceCulture);
+ return ResourceManager.GetString("RBI0001CodeFixTitle", resourceCulture);
}
}
- internal static string AB0001MessageFormat {
+ internal static string RBI0001Description {
get {
- return ResourceManager.GetString("AB0001MessageFormat", resourceCulture);
+ return ResourceManager.GetString("RBI0001Description", resourceCulture);
}
}
- internal static string AB0001Title {
+ internal static string RBI0001MessageFormat {
get {
- return ResourceManager.GetString("AB0001Title", resourceCulture);
+ return ResourceManager.GetString("RBI0001MessageFormat", resourceCulture);
}
}
- internal static string RBI0001CodeFixTitle {
+ internal static string RBI0001Title {
get {
- return ResourceManager.GetString("RBI0001CodeFixTitle", resourceCulture);
+ return ResourceManager.GetString("RBI0001Title", resourceCulture);
}
}
- internal static string RBI0001Description {
+ internal static string RBI0002Description {
get {
- return ResourceManager.GetString("RBI0001Description", resourceCulture);
+ return ResourceManager.GetString("RBI0002Description", resourceCulture);
}
}
- internal static string RBI0001MessageFormat {
+ internal static string RBI0002MessageFormat {
get {
- return ResourceManager.GetString("RBI0001MessageFormat", resourceCulture);
+ return ResourceManager.GetString("RBI0002MessageFormat", resourceCulture);
}
}
- internal static string RBI0001Title {
+ internal static string RBI0002Title {
get {
- return ResourceManager.GetString("RBI0001Title", resourceCulture);
+ return ResourceManager.GetString("RBI0002Title", resourceCulture);
+ }
+ }
+
+ internal static string RBI0003Description {
+ get {
+ return ResourceManager.GetString("RBI0003Description", resourceCulture);
+ }
+ }
+
+ internal static string RBI0003MessageFormat {
+ get {
+ return ResourceManager.GetString("RBI0003MessageFormat", resourceCulture);
+ }
+ }
+
+ internal static string RBI0003Title {
+ get {
+ return ResourceManager.GetString("RBI0003Title", resourceCulture);
+ }
+ }
+
+ internal static string RBI0004Description {
+ get {
+ return ResourceManager.GetString("RBI0004Description", resourceCulture);
+ }
+ }
+
+ internal static string RBI0004MessageFormat {
+ get {
+ return ResourceManager.GetString("RBI0004MessageFormat", resourceCulture);
+ }
+ }
+
+ internal static string RBI0004Title {
+ get {
+ return ResourceManager.GetString("RBI0004Title", resourceCulture);
+ }
+ }
+
+ internal static string RBI0005Description {
+ get {
+ return ResourceManager.GetString("RBI0005Description", resourceCulture);
+ }
+ }
+
+ internal static string RBI0005MessageFormat {
+ get {
+ return ResourceManager.GetString("RBI0005MessageFormat", resourceCulture);
}
}
- internal static string AB0002Description {
+ internal static string RBI0005Title {
get {
- return ResourceManager.GetString("AB0002Description", resourceCulture);
+ return ResourceManager.GetString("RBI0005Title", resourceCulture);
}
}
}
diff --git a/src/rgen/Microsoft.Macios.Bindings.Analyzer/Resources.resx b/src/rgen/Microsoft.Macios.Bindings.Analyzer/Resources.resx
index 2f228a310c3..6321521e9df 100644
--- a/src/rgen/Microsoft.Macios.Bindings.Analyzer/Resources.resx
+++ b/src/rgen/Microsoft.Macios.Bindings.Analyzer/Resources.resx
@@ -34,4 +34,56 @@
Binding type declaration must be partial
The title of the diagnostic.
+
+
+ In order for the code to be generated a smart enum value has to have a backing field.
+ An optional longer localizable description of the diagnostic.
+
+
+ The enum value '{0}' must be tagged with a FieldAttribute.
+ The format-able message the diagnostic displays.
+
+
+ Smart enum values must be tagged with an FieldAttribute
+ The title of the diagnostic.
+
+
+
+ In order for the code to be generated the backing filed of a smart enum value cannot be an empty string.
+ An optional longer localizable description of the diagnostic.
+
+
+ The enum value '{0}' backing field is an empty string.
+ The format-able message the diagnostic displays.
+
+
+ Smart enum backing field cannot be an empty string.
+ The title of the diagnostic.
+
+
+
+ Smart enum backing field for a non Apple framework must provide a library name.
+ An optional longer localizable description of the diagnostic.
+
+
+ The enum value '{0}' backing field must provide a library name.
+ The format-able message the diagnostic displays.
+
+
+ Non Apple framework bindings must provide a library name.
+ The title of the diagnostic.
+
+
+
+ Fields of known Apple frameworks should not provide a LibraryName.
+ An optional longer localizable description of the diagnostic.
+
+
+ The backing Field of '{0}' should not provide a LibraryName.
+ The format-able message the diagnostic displays.
+
+
+ Do not provide the LibraryName for known Apple frameworks.
+ The title of the diagnostic.
+
diff --git a/src/rgen/Microsoft.Macios.Bindings.Analyzer/SmartEnumSemanticAnalyzer.cs b/src/rgen/Microsoft.Macios.Bindings.Analyzer/SmartEnumSemanticAnalyzer.cs
new file mode 100644
index 00000000000..7899c23ce2b
--- /dev/null
+++ b/src/rgen/Microsoft.Macios.Bindings.Analyzer/SmartEnumSemanticAnalyzer.cs
@@ -0,0 +1,142 @@
+using System.Collections.Immutable;
+using System.Linq;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Microsoft.CodeAnalysis.Diagnostics;
+using Microsoft.Macios.Bindings.Analyzer.Extensions;
+using Microsoft.Macios.Generator;
+using Microsoft.Macios.Generator.Attributes;
+using Microsoft.Macios.Generator.Extensions;
+
+namespace Microsoft.Macios.Bindings.Analyzer;
+
+
+///
+/// Analyzer to ensure that all enum values in an SmartEnum contains a Field attribute.
+///
+[DiagnosticAnalyzer (LanguageNames.CSharp)]
+public class SmartEnumSemanticAnalyzer : 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, typeof (Resources)),
+ "Usage",
+ DiagnosticSeverity.Error,
+ isEnabledByDefault: true,
+ description: new LocalizableResourceString (nameof (Resources.RBI0002Description), Resources.ResourceManager, typeof (Resources))
+ );
+
+ // All Field symbols cannot be empty or white space
+ 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))
+ );
+
+ // If not an apple framework, we should provide the library path
+ 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))
+ );
+
+ // if apple framework, the library path should be empty
+ 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.Warning,
+ isEnabledByDefault: true,
+ description: new LocalizableResourceString (nameof (Resources.RBI0005Description), Resources.ResourceManager, typeof (Resources))
+ );
+
+ public override ImmutableArray SupportedDiagnostics { get; } = [RBI0002, RBI0003, RBI0004, RBI0005];
+
+ public override void Initialize (AnalysisContext context)
+ {
+ context.ConfigureGeneratedCodeAnalysis (GeneratedCodeAnalysisFlags.None);
+ context.EnableConcurrentExecution ();
+ context.RegisterSyntaxNodeAction (AnalysisContext, SyntaxKind.EnumDeclaration);
+ }
+
+ void AnalysisContext (SyntaxNodeAnalysisContext context)
+ => this.AnalyzeBindingType (context);
+
+ public ImmutableArray Analyze (PlatformName platformName, EnumDeclarationSyntax declarationNode, INamedTypeSymbol symbol)
+ {
+ // we want to ensure several things:
+ // 1. All enum values are marked with a Field attribute
+ // 2. All Field attributes have a symbol name
+ // 3. If the Field attribute is not from a known apple library, the library name is set
+ // 4. If the Field attribute is from a known apple library the lib should be null
+
+ // based on the platform decide if we are dealing with a known apple framework, we want all, not just the
+ // ones that are part of the simulator
+ var appleFrameworks = Frameworks.GetFrameworks (platformName.ToApplePlatform (), false);
+ var isAppleFramework = appleFrameworks.Find (symbol.ContainingNamespace.Name) is not null;
+
+ // bucket with all the diagnostics we have found
+ var bucket = ImmutableArray.CreateBuilder ();
+
+ var members = symbol.GetMembers ().OfType ().ToArray ();
+ foreach (var fieldSymbol in members) {
+ var attributes = fieldSymbol.GetAttributeData ();
+ if (attributes.Count == 0) {
+ // 1. All enum values are marked with a Field attribute, therefore add a diagnostic
+ bucket.Add (Diagnostic.Create (RBI0002,
+ fieldSymbol.Locations.First (),
+ fieldSymbol.ToDisplayString ()));
+ continue;
+ }
+
+ // Get all the FieldAttribute, parse it and add the data to the result
+ if (attributes.TryGetValue (AttributesNames.FieldAttribute, out var fieldAttrData)) {
+ var fieldSyntax = fieldAttrData.ApplicationSyntaxReference?.GetSyntax ();
+ if (fieldSyntax is null) {
+ // if we cant get the syntax reference, we have a bug
+ continue;
+ }
+
+ if (FieldData.TryParse (fieldSyntax, fieldAttrData, out var fieldData)) {
+ // only provide diagnostics if we managed to parse the FieldData, else we have a bug in the
+ // analyzer
+ if (string.IsNullOrWhiteSpace (fieldData.SymbolName)) {
+ // 2. All Field attributes have a symbol name, therefore add a diagnostic
+ bucket.Add (Diagnostic.Create (RBI0003,
+ fieldSyntax.GetLocation (),
+ fieldSymbol.ToDisplayString ()));
+ }
+
+ if (!isAppleFramework) {
+ if (string.IsNullOrWhiteSpace (fieldData.LibraryName)) {
+ bucket.Add (Diagnostic.Create (RBI0004,
+ fieldSyntax.GetLocation (),
+ fieldSymbol.ToDisplayString ()));
+ }
+ } else {
+ if (fieldData.LibraryName is not null) {
+ bucket.Add (Diagnostic.Create (RBI0005,
+ fieldSyntax.GetLocation (),
+ fieldSymbol.ToDisplayString ()));
+ }
+ }
+ } else {
+ // report but msg
+ }
+ }
+ }
+
+ return bucket.ToImmutable ();
+ }
+}
diff --git a/src/rgen/Microsoft.Macios.Generator.Sample/Microsoft.Macios.Generator.Sample.csproj b/src/rgen/Microsoft.Macios.Generator.Sample/Microsoft.Macios.Generator.Sample.csproj
index e10b90a1b8f..02fe20736ef 100644
--- a/src/rgen/Microsoft.Macios.Generator.Sample/Microsoft.Macios.Generator.Sample.csproj
+++ b/src/rgen/Microsoft.Macios.Generator.Sample/Microsoft.Macios.Generator.Sample.csproj
@@ -8,6 +8,7 @@
+
@@ -18,9 +19,6 @@
external\Attributes.cs
-
- external\PlatformName.cs
-
diff --git a/src/rgen/Microsoft.Macios.Generator/Attributes/FieldData.cs b/src/rgen/Microsoft.Macios.Generator/Attributes/FieldData.cs
new file mode 100644
index 00000000000..933cc0f082c
--- /dev/null
+++ b/src/rgen/Microsoft.Macios.Generator/Attributes/FieldData.cs
@@ -0,0 +1,51 @@
+using System.Diagnostics.CodeAnalysis;
+using Microsoft.CodeAnalysis;
+
+namespace Microsoft.Macios.Generator.Attributes;
+
+record FieldData {
+ public string SymbolName { get; }
+ public string? LibraryName { get; private set; }
+
+ FieldData (string symbolName, string? libraryName = null)
+ {
+ SymbolName = symbolName;
+ LibraryName = libraryName;
+ }
+
+ public static bool TryParse (SyntaxNode attributeSyntax, AttributeData attributeData,
+ [NotNullWhen (true)] out FieldData? data)
+ {
+ data = default;
+
+ var count = attributeData.ConstructorArguments.Length;
+ switch (count) {
+ case 1:
+ data = new((string) attributeData.ConstructorArguments [0].Value!);
+ break;
+ case 2:
+ data = new((string) attributeData.ConstructorArguments [0].Value!,
+ (string) attributeData.ConstructorArguments [1].Value!);
+ break;
+ default:
+ // 0 should not be an option..
+ return false;
+ }
+
+ if (attributeData.NamedArguments.Length == 0)
+ return true;
+
+ // LibraryName can be a param value
+ foreach (var (name, value) in attributeData.NamedArguments) {
+ switch (name) {
+ case "LibraryName":
+ data.LibraryName = (string) value.Value!;
+ break;
+ default:
+ data = null;
+ return false;
+ }
+ }
+ return true;
+ }
+}
diff --git a/src/rgen/Microsoft.Macios.Generator/AttributesNames.cs b/src/rgen/Microsoft.Macios.Generator/AttributesNames.cs
index 62ac3b632b0..ca2766bb79e 100644
--- a/src/rgen/Microsoft.Macios.Generator/AttributesNames.cs
+++ b/src/rgen/Microsoft.Macios.Generator/AttributesNames.cs
@@ -5,5 +5,6 @@ namespace Microsoft.Macios.Generator;
///
public static class AttributesNames {
- public static readonly string BindingAttribute = "ObjCBindings.BindingTypeAttribute";
+ public const string BindingAttribute = "ObjCBindings.BindingTypeAttribute";
+ public const string FieldAttribute = "Foundation.FieldAttribute";
}
diff --git a/src/rgen/Microsoft.Macios.Generator/BindingSourceGeneratorGenerator.cs b/src/rgen/Microsoft.Macios.Generator/BindingSourceGeneratorGenerator.cs
index 8b7d54e8533..834202e71a3 100644
--- a/src/rgen/Microsoft.Macios.Generator/BindingSourceGeneratorGenerator.cs
+++ b/src/rgen/Microsoft.Macios.Generator/BindingSourceGeneratorGenerator.cs
@@ -1,3 +1,4 @@
+using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
@@ -92,7 +93,9 @@ static void AddPipeline (IncrementalGeneratorInitializationContext context) w
///
/// Root syntax tree of the base type declaration.
/// String builder that will be used for the generated code.
- static void CollectUsingStatements (SyntaxTree tree, TabbedStringBuilder sb)
+ /// The context of the code being generated. Used to keep track of the current added statemnts
+ static void CollectUsingStatements (SyntaxTree tree, TabbedStringBuilder sb, ICodeEmitter emitter)
+ where T : BaseTypeDeclarationSyntax
{
// collect all using from the syntax tree, add them to a hash to make sure that we don't have duplicates
// and add those usings that we do know we need for bindings.
@@ -103,7 +106,14 @@ static void CollectUsingStatements (SyntaxTree tree, TabbedStringBuilder sb)
var usingDirectivesToKeep = new HashSet (usingDirectives) {
// add the using statements that we know we need and print them to the sb
};
- foreach (var ns in usingDirectivesToKeep) {
+
+ // add those using statements needed by the emitter
+ foreach (var ns in emitter.UsingStatements) {
+ usingDirectivesToKeep.Add (ns);
+ }
+
+ // add them sorted so that we have testeable generated code
+ foreach (var ns in usingDirectivesToKeep.OrderBy (s => s)) {
if (string.IsNullOrEmpty (ns))
continue;
sb.AppendLine ($"using {ns};");
@@ -138,17 +148,19 @@ static void GenerateCode (SourceProductionContext context, Compilation compil
sb.AppendLine ("#nullable enable");
sb.AppendLine ();
- CollectUsingStatements (baseTypeDeclarationSyntax.SyntaxTree, sb);
-
// delegate semantic model and syntax tree analysis to the emitter who will generate the code and knows
// best
if (ContextFactory.TryCreate (rootContext, semanticModel, namedTypeSymbol, baseTypeDeclarationSyntax, out var symbolBindingContext)
&& EmitterFactory.TryCreate (symbolBindingContext, sb, out var emitter)) {
- if (emitter.TryEmit (out var diagnostics)) {
- // only add file when we do generate code
- var code = sb.ToString ();
- context.AddSource ($"{emitter.SymbolName}.g.cs", SourceText.From (code, Encoding.UTF8));
+
+ CollectUsingStatements (baseTypeDeclarationSyntax.SyntaxTree, sb, emitter);
+
+ if (emitter.TryEmit(out var diagnostics)) {
+ // only add file when we do generate code
+ var code = sb.ToString ();
+ context.AddSource ($"{symbolBindingContext.Namespace}/{emitter.SymbolName}.g.cs",
+ SourceText.From (code, Encoding.UTF8));
} else {
// add to the diagnostics and continue to the next possible candidate
foreach (Diagnostic diagnostic in diagnostics) {
diff --git a/src/rgen/Microsoft.Macios.Generator/Context/ISymbolBindingContext.cs b/src/rgen/Microsoft.Macios.Generator/Context/ISymbolBindingContext.cs
index a9bc1f3c242..bcfb1b0bafa 100644
--- a/src/rgen/Microsoft.Macios.Generator/Context/ISymbolBindingContext.cs
+++ b/src/rgen/Microsoft.Macios.Generator/Context/ISymbolBindingContext.cs
@@ -12,8 +12,8 @@ interface ISymbolBindingContext where T : BaseTypeDeclarationSyntax {
T DeclarationSyntax { get; }
string Namespace { get; }
string SymbolName { get; }
- RootBindingContext RootBindingContext { get; init; }
- SemanticModel SemanticModel { get; init; }
- INamedTypeSymbol Symbol { get; init; }
+ RootBindingContext RootBindingContext { get; }
+ SemanticModel SemanticModel { get; }
+ INamedTypeSymbol Symbol { get; }
bool IsStatic { get; }
}
diff --git a/src/rgen/Microsoft.Macios.Generator/Context/RootBindingContext.cs b/src/rgen/Microsoft.Macios.Generator/Context/RootBindingContext.cs
index 64159cb0657..35df7c2036f 100644
--- a/src/rgen/Microsoft.Macios.Generator/Context/RootBindingContext.cs
+++ b/src/rgen/Microsoft.Macios.Generator/Context/RootBindingContext.cs
@@ -2,6 +2,7 @@
using System.Diagnostics.CodeAnalysis;
using System.IO;
using Microsoft.CodeAnalysis;
+using Microsoft.Macios.Generator.Extensions;
namespace Microsoft.Macios.Generator.Context;
@@ -22,26 +23,58 @@ class RootBindingContext {
public RootBindingContext (Compilation compilation)
{
Compilation = compilation;
- CurrentPlatform = PlatformName.None;
- // use the reference assembly to determine what platform we are binding
- foreach (var referencedAssemblyName in compilation.ReferencedAssemblyNames) {
- switch (referencedAssemblyName.Name) {
- case "Microsoft.iOS":
- CurrentPlatform = PlatformName.iOS;
- break;
- case "Microsoft.MacCatalyst":
- CurrentPlatform = PlatformName.MacCatalyst;
- break;
- case "Microsoft.macOS":
- CurrentPlatform = PlatformName.MacOSX;
- break;
- case "Microsoft.tvOS":
- CurrentPlatform = PlatformName.TvOS;
- break;
- default:
- CurrentPlatform = PlatformName.None;
- break;
+ CurrentPlatform = compilation.GetCurrentPlatform ();
+ }
+
+ // TODO: clean code coming from the old generator
+ public bool TryComputeLibraryName (string? attributeLibraryName, string typeNamespace,
+ [NotNullWhen (true)] out string? libraryName,
+ out string? libraryPath)
+ {
+ libraryPath = null;
+
+ if (!string.IsNullOrEmpty (attributeLibraryName)) {
+ // Remapped
+ libraryName = attributeLibraryName;
+ if (libraryName [0] == '+') {
+ switch (libraryName) {
+ case "+CoreImage":
+ CurrentPlatform.TryGetCoreImageMap (out libraryName);
+ break;
+ case "+CoreServices":
+ CurrentPlatform.TryGetCoreServicesMap (out libraryName);
+ break;
+ case "+PDFKit":
+ libraryName = "PdfKit";
+ CurrentPlatform.TryGetPDFKitMap (out libraryPath);
+ break;
+ }
+ } else {
+ // we get something in LibraryName from FieldAttribute so we assume
+ // it is a path to a library, so we save the path and change library name
+ // to a valid identifier if needed
+ libraryPath = libraryName;
+ // without extension makes more sense, but we can't change it since it breaks compat
+ if (BindThirdPartyLibrary) {
+ libraryName = Path.GetFileName (libraryName);
+ } else {
+ libraryName = Path.GetFileNameWithoutExtension (libraryName);
+ }
+
+ if (libraryName.Contains ('.'))
+ libraryName = libraryName.Replace (".", string.Empty);
}
+ } else if (BindThirdPartyLibrary) {
+ // User should provide a LibraryName
+ libraryName = null;
+ return false;
+ } else {
+ libraryName = typeNamespace;
}
+
+ if (libraryName is not null && !_libraries.ContainsKey (libraryName))
+ _libraries.Add (libraryName, libraryPath);
+
+ return true;
}
}
diff --git a/src/rgen/Microsoft.Macios.Generator/Context/SymbolBindingContext.cs b/src/rgen/Microsoft.Macios.Generator/Context/SymbolBindingContext.cs
index b3d0c24cfbb..79e03a7d730 100644
--- a/src/rgen/Microsoft.Macios.Generator/Context/SymbolBindingContext.cs
+++ b/src/rgen/Microsoft.Macios.Generator/Context/SymbolBindingContext.cs
@@ -1,3 +1,4 @@
+using System.Collections.Generic;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;
diff --git a/src/rgen/Microsoft.Macios.Generator/Emitters/ClassEmitter.cs b/src/rgen/Microsoft.Macios.Generator/Emitters/ClassEmitter.cs
index 0d56a01a1c1..bef0cc6e4a5 100644
--- a/src/rgen/Microsoft.Macios.Generator/Emitters/ClassEmitter.cs
+++ b/src/rgen/Microsoft.Macios.Generator/Emitters/ClassEmitter.cs
@@ -1,3 +1,4 @@
+using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics.CodeAnalysis;
using Microsoft.CodeAnalysis;
@@ -8,6 +9,8 @@ namespace Microsoft.Macios.Generator.Emitters;
class ClassEmitter (ClassBindingContext context, TabbedStringBuilder builder) : ICodeEmitter {
public string SymbolName => context.SymbolName;
+ public IEnumerable UsingStatements => [];
+
public bool TryEmit ([NotNullWhen (false)] out ImmutableArray? diagnostics)
{
diff --git a/src/rgen/Microsoft.Macios.Generator/Emitters/EnumEmitter.cs b/src/rgen/Microsoft.Macios.Generator/Emitters/EnumEmitter.cs
index b946986eac9..91a91047d78 100644
--- a/src/rgen/Microsoft.Macios.Generator/Emitters/EnumEmitter.cs
+++ b/src/rgen/Microsoft.Macios.Generator/Emitters/EnumEmitter.cs
@@ -1,8 +1,11 @@
+using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics.CodeAnalysis;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Microsoft.Macios.Generator.Attributes;
using Microsoft.Macios.Generator.Context;
+using Microsoft.Macios.Generator.Extensions;
namespace Microsoft.Macios.Generator.Emitters;
@@ -10,10 +13,132 @@ class EnumEmitter (ISymbolBindingContext context, TabbedS
: ICodeEmitter {
public string SymbolName => $"{context.SymbolName}Extensions";
+ public IEnumerable UsingStatements => ["System"];
- public bool TryEmit ([NotNullWhen (false)] out ImmutableArray? diagnostics)
+ void Emit (TabbedStringBuilder classBlock, (IFieldSymbol Symbol, FieldData FieldData) enumField, int index)
{
- diagnostics = null;
- return true;
+ var typeNamespace = enumField.Symbol.ContainingType.ContainingNamespace.Name;
+ if (!context.RootBindingContext.TryComputeLibraryName (enumField.FieldData.LibraryName, typeNamespace,
+ out string? libraryName, out string? libraryPath)) {
+ return;
+ }
+
+ classBlock.AppendLine ($"[Field (\"{enumField.FieldData.SymbolName}\", \"{libraryPath ?? libraryName}\")]");
+
+ using (var propertyBlock = classBlock.CreateBlock ($"internal unsafe static IntPtr {enumField.FieldData.SymbolName}", true))
+ using (var getterBlock = propertyBlock.CreateBlock ("get", true)) {
+ getterBlock.AppendLine ($"fixed (IntPtr *storage = &values [{index}])");
+ var lib = (libraryPath is null) ? $"Libraries.{libraryName}.Handle" : $"\"{libraryPath}\"";
+ getterBlock.AppendLine (
+ $"\treturn Dlfcn.CachePointer ({lib}, \"{enumField.FieldData.SymbolName}\", storage);");
+ }
+ }
+
+ void Emit (TabbedStringBuilder classBlock, ImmutableArray<(IFieldSymbol Symbol, FieldData FieldData)> fields)
+ {
+ for (var index = 0; index < fields.Length; index++) {
+ var field = fields [index];
+ classBlock.AppendLine ();
+ Emit (classBlock, field, index);
+ }
+ }
+
+ void Emit (TabbedStringBuilder classBlock, INamedTypeSymbol enumSymbol,
+ ImmutableArray<(IFieldSymbol Symbol, FieldData FieldData)>? members)
+ {
+ if (members is null)
+ return;
+
+ // smart enum require 4 diff methods to be able to retrieve the values
+
+ // Get constant
+ using (var getConstantBlock = classBlock.CreateBlock ($"public static NSString? GetConstant (this {enumSymbol.Name} self)", true)) {
+ getConstantBlock.AppendLine ("IntPtr ptr = IntPtr.Zero;");
+ using (var switchBlock = getConstantBlock.CreateBlock ("switch ((int) self)", true)) {
+ for (var index = 0; index < members.Value.Length; index++) {
+ var (_, fieldData) = members.Value [index];
+ switchBlock.AppendLine ($"case {index}: // {fieldData.SymbolName}");
+ switchBlock.AppendLine ($"\tptr = {fieldData.SymbolName};");
+ switchBlock.AppendLine ("\tbreak;");
+ }
+ }
+
+ getConstantBlock.AppendLine ("return (NSString?) Runtime.GetNSObject (ptr);");
+ }
+
+ classBlock.AppendLine ();
+ // Get value
+ using (var getValueBlock = classBlock.CreateBlock ($"public static {enumSymbol.Name} GetValue (NSString constant)", true)) {
+ getValueBlock.AppendLine ("if (constant is null)");
+ getValueBlock.AppendLine ("\tthrow new ArgumentNullException (nameof (constant));");
+ foreach ((IFieldSymbol? fieldSymbol, FieldData? fieldData) in members) {
+ getValueBlock.AppendLine ($"if (constant.IsEqualTo ({fieldData.SymbolName}))");
+ getValueBlock.AppendLine ($"\treturn {enumSymbol.Name}.{fieldSymbol.Name};");
+ }
+
+ getValueBlock.AppendLine (
+ "throw new NotSupportedException ($\"{constant} has no associated enum value on this platform.\");");
+ }
+
+ classBlock.AppendLine ();
+ // To ConstantArray
+ classBlock.AppendRaw (
+@$"internal static NSString?[]? ToConstantArray (this {enumSymbol.Name}[]? values)
+{{
+ if (values is null)
+ return null;
+ var rv = new global::System.Collections.Generic.List ();
+ for (var i = 0; i < values.Length; i++) {{
+ var value = values [i];
+ rv.Add (value.GetConstant ());
+ }}
+ return rv.ToArray ();
+}}");
+ classBlock.AppendLine ();
+ classBlock.AppendLine ();
+ // ToEnumArray
+ classBlock.AppendRaw (
+@$"internal static {enumSymbol.Name}[]? ToEnumArray (this NSString[]? values)
+{{
+ if (values is null)
+ return null;
+ var rv = new global::System.Collections.Generic.List<{enumSymbol.Name}> ();
+ for (var i = 0; i < values.Length; i++) {{
+ var value = values [i];
+ rv.Add (GetValue (value));
+ }}
+ return rv.ToArray ();
+}}");
}
+
+ public bool TryEmit ([NotNullWhen (false)] out ImmutableArray? diagnostics)
+ {
+ diagnostics = null;
+ if (!context.Symbol.TryGetEnumFields (out var members,
+ out diagnostics) || members.Value.Length == 0) {
+ diagnostics = new ImmutableArray ();
+ return false;
+ }
+ // in the old generator we had to copy over the enum, in this new approach the only code
+ // we need to create is the extension class for the enum that is backed by fields
+ builder.AppendLine ();
+ builder.AppendLine ($"namespace {context.Namespace};");
+ builder.AppendLine ();
+
+ builder.AppendGeneratedCodeAttribute ();
+ using (var classBlock = builder.CreateBlock ($"static public partial class {SymbolName}", true)) {
+ classBlock.AppendLine ();
+ classBlock.AppendLine ($"static IntPtr[] values = new IntPtr [{members.Value.Length}];");
+ // foreach member in the enum we need to create a field that holds the value, the property emitter
+ // will take care of generating the property. Do not order by name to keep the order of the enum
+ Emit (classBlock, members.Value);
+ classBlock.AppendLine ();
+
+ // emit the extension methods that will be used to get the values from the enum
+ Emit (classBlock, context.Symbol, members);
+ classBlock.AppendLine ();
+ }
+
+ return true;
+ }
}
diff --git a/src/rgen/Microsoft.Macios.Generator/Emitters/ICodeEmitter.cs b/src/rgen/Microsoft.Macios.Generator/Emitters/ICodeEmitter.cs
index d78584e82bb..6ae7b9b10f0 100644
--- a/src/rgen/Microsoft.Macios.Generator/Emitters/ICodeEmitter.cs
+++ b/src/rgen/Microsoft.Macios.Generator/Emitters/ICodeEmitter.cs
@@ -1,3 +1,4 @@
+using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics.CodeAnalysis;
using Microsoft.CodeAnalysis;
@@ -10,4 +11,5 @@ namespace Microsoft.Macios.Generator.Emitters;
interface ICodeEmitter {
public string SymbolName { get; }
bool TryEmit ([NotNullWhen (false)] out ImmutableArray? diagnostics);
+ IEnumerable UsingStatements { get; }
}
diff --git a/src/rgen/Microsoft.Macios.Generator/Emitters/InterfaceEmitter.cs b/src/rgen/Microsoft.Macios.Generator/Emitters/InterfaceEmitter.cs
index 23f77d0c4d7..65329736ad2 100644
--- a/src/rgen/Microsoft.Macios.Generator/Emitters/InterfaceEmitter.cs
+++ b/src/rgen/Microsoft.Macios.Generator/Emitters/InterfaceEmitter.cs
@@ -1,3 +1,4 @@
+using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics.CodeAnalysis;
using Microsoft.CodeAnalysis;
@@ -8,6 +9,7 @@ namespace Microsoft.Macios.Generator.Emitters;
class InterfaceEmitter (ISymbolBindingContext context, TabbedStringBuilder builder) : ICodeEmitter {
public string SymbolName { get; } = string.Empty;
+ public IEnumerable UsingStatements => [];
public bool TryEmit ([NotNullWhen (false)] out ImmutableArray? diagnostics)
{
diagnostics = null;
diff --git a/src/rgen/Microsoft.Macios.Generator/Extensions/CompilationExtensions.cs b/src/rgen/Microsoft.Macios.Generator/Extensions/CompilationExtensions.cs
new file mode 100644
index 00000000000..bcf6edeb23a
--- /dev/null
+++ b/src/rgen/Microsoft.Macios.Generator/Extensions/CompilationExtensions.cs
@@ -0,0 +1,26 @@
+using System;
+using Microsoft.CodeAnalysis;
+
+namespace Microsoft.Macios.Generator.Extensions;
+
+public static class CompilationExtensions {
+
+ public static PlatformName GetCurrentPlatform (this Compilation self)
+ {
+ // use the reference assembly to determine what platform we are binding
+ foreach (var referenceAssembly in self.ReferencedAssemblyNames) {
+ switch (referenceAssembly.Name) {
+ case "Microsoft.iOS":
+ return PlatformName.iOS;
+ case "Microsoft.MacCatalyst":
+ return PlatformName.MacCatalyst;
+ case "Microsoft.macOS":
+ return PlatformName.MacOSX;
+ case "Microsoft.tvOS":
+ return PlatformName.TvOS;
+ }
+ }
+
+ return PlatformName.None;
+ }
+}
diff --git a/src/rgen/Microsoft.Macios.Generator/Extensions/NamedTypeSymbolExtensions.cs b/src/rgen/Microsoft.Macios.Generator/Extensions/NamedTypeSymbolExtensions.cs
new file mode 100644
index 00000000000..e24f022b59f
--- /dev/null
+++ b/src/rgen/Microsoft.Macios.Generator/Extensions/NamedTypeSymbolExtensions.cs
@@ -0,0 +1,47 @@
+using System.Collections.Immutable;
+using System.Diagnostics.CodeAnalysis;
+using System.Linq;
+using Microsoft.CodeAnalysis;
+using Microsoft.Macios.Generator.Attributes;
+
+namespace Microsoft.Macios.Generator.Extensions;
+
+static class NamedTypeSymbolExtensions {
+ public static bool TryGetEnumFields (this INamedTypeSymbol enumSymbol,
+ [NotNullWhen (true)]
+ out ImmutableArray<(IFieldSymbol Symbol, FieldData FieldData)>? fields,
+ [NotNullWhen (false)] out ImmutableArray? diagnostics)
+ {
+ fields = null;
+ diagnostics = null;
+
+ // because we are dealing with an enum, we need to get all the fields from the symbol but we need to
+ // keep the order in which they are defined in the source code.
+
+ var fieldBucket =
+ ImmutableArray.CreateBuilder<(IFieldSymbol Symbol, FieldData FieldData)> ();
+
+ var members = enumSymbol.GetMembers ().OfType ().ToArray ();
+ foreach (var fieldSymbol in members) {
+ var attributes = fieldSymbol.GetAttributeData ();
+ if (attributes.Count == 0)
+ continue;
+
+ // Get all the FieldAttribute, parse it and add the data to the result
+ if (attributes.TryGetValue (AttributesNames.FieldAttribute, out var fieldAttrData)) {
+ var fieldSyntax = fieldAttrData.ApplicationSyntaxReference?.GetSyntax ();
+ if (fieldSyntax is null)
+ continue;
+
+ if (FieldData.TryParse (fieldSyntax, fieldAttrData, out var fieldData)) {
+ fieldBucket.Add ((Symbol: fieldSymbol, FieldData: fieldData));
+ } else {
+ // TODO: diagnostics
+ }
+ }
+ }
+
+ fields = fieldBucket.ToImmutable ();
+ return true;
+ }
+}
diff --git a/src/rgen/Microsoft.Macios.Generator/Extensions/TypeSymbolExtensions.cs b/src/rgen/Microsoft.Macios.Generator/Extensions/TypeSymbolExtensions.cs
new file mode 100644
index 00000000000..24c048e4d4d
--- /dev/null
+++ b/src/rgen/Microsoft.Macios.Generator/Extensions/TypeSymbolExtensions.cs
@@ -0,0 +1,48 @@
+using System.Collections.Generic;
+using Microsoft.CodeAnalysis;
+
+namespace Microsoft.Macios.Generator.Extensions;
+
+static class TypeSymbolExtensions {
+ public static bool IsSmartEnum (this ITypeSymbol symbol)
+ {
+ var boundAttributes = symbol.GetAttributes ();
+ if (boundAttributes.Length == 0) {
+ return false;
+ }
+
+ // do not use LINQ here, we need to check if the attribute is present
+ foreach (var attributeData in boundAttributes) {
+ if (attributeData.AttributeClass?.ToDisplayString () == AttributesNames.BindingAttribute)
+ return true;
+ }
+ return false;
+ }
+
+ public static string GetSmartEnumType (this ITypeSymbol symbol)
+ {
+ // TODO: look into the backing type of the smart enum
+ return "NSString";
+ }
+
+ public static Dictionary GetAttributeData (this ISymbol symbol)
+ {
+ var boundAttributes = symbol.GetAttributes ();
+ if (boundAttributes.Length == 0) {
+ // return an empty dictionary if there are no attributes
+ return new();
+ }
+
+ var attributes = new Dictionary ();
+ foreach (var attributeData in boundAttributes) {
+ var attrName = attributeData.AttributeClass?.ToDisplayString ();
+ if (string.IsNullOrEmpty (attrName))
+ continue;
+ if (!attributes.TryAdd (attrName, attributeData)) {
+ // TODO: diagnostics
+ }
+ }
+
+ return attributes;
+ }
+}
diff --git a/src/rgen/Microsoft.Macios.Generator/Microsoft.Macios.Generator.csproj b/src/rgen/Microsoft.Macios.Generator/Microsoft.Macios.Generator.csproj
index 7da28f54684..d6dc11ca906 100644
--- a/src/rgen/Microsoft.Macios.Generator/Microsoft.Macios.Generator.csproj
+++ b/src/rgen/Microsoft.Macios.Generator/Microsoft.Macios.Generator.csproj
@@ -14,12 +14,12 @@
-
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
-
-
+
+
@@ -27,15 +27,15 @@
<_Parameter1>Microsoft.Macios.Generator.Tests
-
-
- external\PlatformName.cs
-
external\PlatformNameExtensions.cs
+
+
+
+
diff --git a/src/rgen/rgen.sln b/src/rgen/rgen.sln
index 68499203954..701dc87108a 100644
--- a/src/rgen/rgen.sln
+++ b/src/rgen/rgen.sln
@@ -12,6 +12,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Macios.Generator.
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Macios.Bindings.Analyzer.Tests", "..\..\tests\rgen\Microsoft.Macios.Bindings.Analyzer.Tests\Microsoft.Macios.Bindings.Analyzer.Tests.csproj", "{1AC4A248-CC98-4392-8690-4E2CAF6E194B}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Macios.Binding.Common", "Microsoft.Macios.Binding.Common\Microsoft.Macios.Binding.Common.csproj", "{536758BC-2A88-4B79-ABB1-6B39494A5FE6}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -42,5 +44,9 @@ Global
{1AC4A248-CC98-4392-8690-4E2CAF6E194B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1AC4A248-CC98-4392-8690-4E2CAF6E194B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1AC4A248-CC98-4392-8690-4E2CAF6E194B}.Release|Any CPU.Build.0 = Release|Any CPU
+ {536758BC-2A88-4B79-ABB1-6B39494A5FE6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {536758BC-2A88-4B79-ABB1-6B39494A5FE6}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {536758BC-2A88-4B79-ABB1-6B39494A5FE6}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {536758BC-2A88-4B79-ABB1-6B39494A5FE6}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal
diff --git a/tests/rgen/Microsoft.Macios.Bindings.Analyzer.Tests/BindingTypeSemanticAnalyzerTests.cs b/tests/rgen/Microsoft.Macios.Bindings.Analyzer.Tests/BindingTypeSemanticAnalyzerTests.cs
index 67c71504329..2800e2632da 100644
--- a/tests/rgen/Microsoft.Macios.Bindings.Analyzer.Tests/BindingTypeSemanticAnalyzerTests.cs
+++ b/tests/rgen/Microsoft.Macios.Bindings.Analyzer.Tests/BindingTypeSemanticAnalyzerTests.cs
@@ -29,10 +29,10 @@ public class Examples {
var compilation = CreateCompilation (nameof (CompareGeneratedCode), platform, inputText);
var diagnostics = await RunAnalyzer (new BindingTypeSemanticAnalyzer (), compilation);
var analyzerDiagnotics = diagnostics
- .Where (d => d.Id == BindingTypeSemanticAnalyzer.DiagnosticId).ToArray ();
+ .Where (d => d.Id == BindingTypeSemanticAnalyzer.RBI0001.Id).ToArray ();
Assert.Single (analyzerDiagnotics);
// verify the diagnostic message
- VerifyDiagnosticMessage (analyzerDiagnotics [0], BindingTypeSemanticAnalyzer.DiagnosticId,
+ VerifyDiagnosticMessage (analyzerDiagnotics [0], BindingTypeSemanticAnalyzer.RBI0001.Id,
DiagnosticSeverity.Error, "The binding type 'Test.Examples' must declared as a partial class");
}
}
diff --git a/tests/rgen/Microsoft.Macios.Bindings.Analyzer.Tests/SmartEnumSemanticAnalyzerTests.cs b/tests/rgen/Microsoft.Macios.Bindings.Analyzer.Tests/SmartEnumSemanticAnalyzerTests.cs
new file mode 100644
index 00000000000..d234d8e4c12
--- /dev/null
+++ b/tests/rgen/Microsoft.Macios.Bindings.Analyzer.Tests/SmartEnumSemanticAnalyzerTests.cs
@@ -0,0 +1,255 @@
+using System.Linq;
+using System.Threading.Tasks;
+using Microsoft.CodeAnalysis;
+using Xamarin.Tests;
+using Xamarin.Utils;
+using Xunit;
+
+namespace Microsoft.Macios.Bindings.Analyzer.Tests;
+
+public class SmartEnumSemanticAnalyzerTests : BaseGeneratorWithAnalyzerTestClass {
+
+ [Theory]
+ [PlatformInlineData (ApplePlatform.iOS)]
+ [PlatformInlineData (ApplePlatform.TVOS)]
+ [PlatformInlineData (ApplePlatform.MacOSX)]
+ [PlatformInlineData (ApplePlatform.MacCatalyst)]
+ public async Task SmartEnumMustHaveFieldAttribute (ApplePlatform platform)
+ {
+ const string inputText = @"
+using Foundation;
+using ObjCRuntime;
+using ObjCBindings;
+
+namespace AVFoundation;
+
+[BindingType]
+public enum AVCaptureSystemPressureExampleLevel {
+ [Field (""AVCaptureSystemPressureLevelNominal"")]
+ Nominal,
+
+ [Field (""AVCaptureSystemPressureLevelFair"")]
+ Fair,
+
+ [Field (""AVCaptureSystemPressureLevelSerious"")]
+ Serious,
+
+ [Field (""AVCaptureSystemPressureLevelCritical"")]
+ Critical,
+
+ // missing field attribute, should be an error
+ Shutdown,
+}
+";
+
+ var compilation = CreateCompilation (nameof (SmartEnumSemanticAnalyzerTests), platform, inputText);
+ var diagnostics = await RunAnalyzer (new SmartEnumSemanticAnalyzer (), compilation);
+ var analyzerDiagnotics = diagnostics
+ .Where (d => d.Id == SmartEnumSemanticAnalyzer.RBI0002.Id).ToArray ();
+ Assert.Single (analyzerDiagnotics);
+ // verify the diagnostic message
+ VerifyDiagnosticMessage (analyzerDiagnotics [0], SmartEnumSemanticAnalyzer.RBI0002.Id,
+ DiagnosticSeverity.Error, "The enum value 'AVFoundation.AVCaptureSystemPressureExampleLevel.Shutdown' must be tagged with a FieldAttribute.");
+ }
+
+ [Theory]
+ [PlatformInlineData (ApplePlatform.iOS)]
+ [PlatformInlineData (ApplePlatform.TVOS)]
+ [PlatformInlineData (ApplePlatform.MacOSX)]
+ [PlatformInlineData (ApplePlatform.MacCatalyst)]
+ public async Task SmartEnumSymbolMustBeCorrect (ApplePlatform platform)
+ {
+ const string inputText = @"
+using Foundation;
+using ObjCRuntime;
+using ObjCBindings;
+
+namespace AVFoundation;
+
+[BindingType]
+public enum AVCaptureSystemPressureExampleLevel {
+ [Field (""AVCaptureSystemPressureLevelNominal"")]
+ Nominal,
+
+ [Field (""AVCaptureSystemPressureLevelFair"")]
+ Fair,
+
+ [Field (""AVCaptureSystemPressureLevelSerious"")]
+ Serious,
+
+ [Field (""AVCaptureSystemPressureLevelCritical"")]
+ Critical,
+
+ // empty field, this should be an error
+ [Field ("" "")]
+ Shutdown,
+}";
+
+ var compilation = CreateCompilation (nameof (SmartEnumSemanticAnalyzerTests), platform, inputText);
+ var diagnostics = await RunAnalyzer (new SmartEnumSemanticAnalyzer (), compilation);
+ var analyzerDiagnotics = diagnostics
+ .Where (d => d.Id == SmartEnumSemanticAnalyzer.RBI0003.Id).ToArray ();
+ Assert.Single (analyzerDiagnotics);
+ VerifyDiagnosticMessage (analyzerDiagnotics [0], SmartEnumSemanticAnalyzer.RBI0003.Id,
+ DiagnosticSeverity.Error, "The enum value 'AVFoundation.AVCaptureSystemPressureExampleLevel.Shutdown' backing field is an empty string.");
+ }
+
+ const string AppleFrameworkLib = @"
+using Foundation;
+using ObjCRuntime;
+using ObjCBindings;
+
+namespace AVFoundation;
+
+[BindingType]
+public enum AVCaptureSystemPressureExampleLevel {
+ [Field (""AVCaptureSystemPressureLevelNominal"")]
+ Nominal,
+
+ [Field (""AVCaptureSystemPressureLevelFair"")]
+ Fair,
+
+ [Field (""AVCaptureSystemPressureLevelSerious"")]
+ Serious,
+
+ [Field (""AVCaptureSystemPressureLevelCritical"")]
+ Critical,
+
+ // do not do this with apple frameworks
+ [Field (""AVCaptureSystemPressureLevelShutdown"", ""/path/to/not/needed/lib"")]
+ Shutdown,
+}";
+
+ const string AppleFrameworkLibNamedParameter = @"
+using Foundation;
+using ObjCRuntime;
+using ObjCBindings;
+
+namespace AVFoundation;
+
+[BindingType]
+public enum AVCaptureSystemPressureExampleLevel {
+ [Field (""AVCaptureSystemPressureLevelNominal"")]
+ Nominal,
+
+ [Field (""AVCaptureSystemPressureLevelFair"")]
+ Fair,
+
+ [Field (""AVCaptureSystemPressureLevelSerious"")]
+ Serious,
+
+ [Field (""AVCaptureSystemPressureLevelCritical"")]
+ Critical,
+
+ // do not do this with apple frameworks
+ [Field (""AVCaptureSystemPressureLevelShutdown"", LibraryName = ""/path/to/not/needed/lib"")]
+ Shutdown,
+}";
+
+ [Theory]
+ [PlatformInlineData (ApplePlatform.iOS, AppleFrameworkLib)]
+ [PlatformInlineData (ApplePlatform.iOS, AppleFrameworkLibNamedParameter)]
+ [PlatformInlineData (ApplePlatform.TVOS, AppleFrameworkLib)]
+ [PlatformInlineData (ApplePlatform.TVOS, AppleFrameworkLibNamedParameter)]
+ [PlatformInlineData (ApplePlatform.MacOSX, AppleFrameworkLib)]
+ [PlatformInlineData (ApplePlatform.MacOSX, AppleFrameworkLibNamedParameter)]
+ [PlatformInlineData (ApplePlatform.MacCatalyst, AppleFrameworkLib)]
+ [PlatformInlineData (ApplePlatform.MacCatalyst, AppleFrameworkLibNamedParameter)]
+ public async Task SmartEnumAppleFrameworkNotLibrary (ApplePlatform platform, string inputText)
+ {
+ var compilation = CreateCompilation (nameof (SmartEnumSemanticAnalyzerTests), platform, inputText);
+ var diagnostics = await RunAnalyzer (new SmartEnumSemanticAnalyzer(), compilation);
+
+ var analyzerDiagnotics = diagnostics
+ .Where (d => d.Id == SmartEnumSemanticAnalyzer.RBI0005.Id).ToArray ();
+ Assert.Single (analyzerDiagnotics);
+ VerifyDiagnosticMessage (analyzerDiagnotics [0], SmartEnumSemanticAnalyzer.RBI0005.Id,
+ DiagnosticSeverity.Warning, "The backing Field of 'AVFoundation.AVCaptureSystemPressureExampleLevel.Shutdown' should not provide a LibraryName.");
+ }
+
+ const string CustomLibraryMissingLibraryName = @"
+using System;
+using System.Runtime.Versioning;
+using Foundation;
+using ObjCRuntime;
+using ObjCBindings;
+
+namespace CustomLibrary;
+
+[BindingType]
+public enum CustomLibraryEnum {
+ [Field (""None"", ""/path/to/customlibrary.framework"")]
+ None,
+ [Field (""Medium"", ""/path/to/customlibrary.framework"")]
+ Medium,
+ // missing lib, this is an error
+ [Field (""High"")]
+ High,
+}
+";
+ const string CustomLibraryEmptyLibraryName = @"
+using System;
+using System.Runtime.Versioning;
+using Foundation;
+using ObjCRuntime;
+using ObjCBindings;
+
+namespace CustomLibrary;
+
+[BindingType]
+public enum CustomLibraryEnum {
+ [Field (""None"", ""/path/to/customlibrary.framework"")]
+ None,
+ [Field (""Medium"", ""/path/to/customlibrary.framework"")]
+ Medium,
+ // empty lib, this is an error
+ [Field (""High"", "" "")]
+ High,
+}
+";
+
+ const string CustomLibraryEmptyLibraryNameParameter = @"
+using System;
+using System.Runtime.Versioning;
+using Foundation;
+using ObjCRuntime;
+using ObjCBindings;
+
+namespace CustomLibrary;
+
+[BindingType]
+public enum CustomLibraryEnum {
+ [Field (""None"", ""/path/to/customlibrary.framework"")]
+ None,
+ [Field (""Medium"", ""/path/to/customlibrary.framework"")]
+ Medium,
+ // empty lib, this is an error
+ [Field (""High"", LibraryName = "" "")]
+ High,
+}
+";
+
+ [Theory]
+ [PlatformInlineData (ApplePlatform.iOS, CustomLibraryMissingLibraryName)]
+ [PlatformInlineData (ApplePlatform.iOS, CustomLibraryEmptyLibraryName)]
+ [PlatformInlineData (ApplePlatform.iOS, CustomLibraryEmptyLibraryNameParameter)]
+ [PlatformInlineData (ApplePlatform.TVOS, CustomLibraryMissingLibraryName)]
+ [PlatformInlineData (ApplePlatform.TVOS, CustomLibraryEmptyLibraryName)]
+ [PlatformInlineData (ApplePlatform.TVOS, CustomLibraryEmptyLibraryNameParameter)]
+ [PlatformInlineData (ApplePlatform.MacOSX, CustomLibraryMissingLibraryName)]
+ [PlatformInlineData (ApplePlatform.MacOSX, CustomLibraryEmptyLibraryName)]
+ [PlatformInlineData (ApplePlatform.MacOSX, CustomLibraryEmptyLibraryNameParameter)]
+ [PlatformInlineData (ApplePlatform.MacCatalyst, CustomLibraryMissingLibraryName)]
+ [PlatformInlineData (ApplePlatform.MacCatalyst, CustomLibraryEmptyLibraryName)]
+ [PlatformInlineData (ApplePlatform.MacCatalyst, CustomLibraryEmptyLibraryNameParameter)]
+ public async Task SmartEnumThirdPartyLibrary (ApplePlatform platform, string inputText)
+ {
+ var compilation = CreateCompilation (nameof (SmartEnumSemanticAnalyzerTests), platform, inputText);
+ var diagnostics = await RunAnalyzer (new SmartEnumSemanticAnalyzer(), compilation);
+ var analyzerDiagnotics = diagnostics
+ .Where (d => d.Id == SmartEnumSemanticAnalyzer.RBI0004.Id).ToArray ();
+ Assert.Single (analyzerDiagnotics);
+ VerifyDiagnosticMessage (analyzerDiagnotics [0], SmartEnumSemanticAnalyzer.RBI0004.Id,
+ DiagnosticSeverity.Error, "The enum value 'CustomLibrary.CustomLibraryEnum.High' backing field must provide a library name.");
+ }
+}
diff --git a/tests/rgen/Microsoft.Macios.Generator.Tests/BaseGeneratorTestClass.cs b/tests/rgen/Microsoft.Macios.Generator.Tests/BaseGeneratorTestClass.cs
index 1ed459e25cc..a183d4206fc 100644
--- a/tests/rgen/Microsoft.Macios.Generator.Tests/BaseGeneratorTestClass.cs
+++ b/tests/rgen/Microsoft.Macios.Generator.Tests/BaseGeneratorTestClass.cs
@@ -16,22 +16,31 @@ namespace Microsoft.Macios.Generator.Tests;
///
public class BaseGeneratorTestClass {
protected BindingSourceGeneratorGenerator GeneratorGenerator;
- protected CSharpGeneratorDriver _driver;
+ protected CSharpGeneratorDriver Driver;
+
+ // list of the defines for each platform, this is passed to the parser to ensure that
+ // we are testing the platforms as if they were being compiled.
+ readonly Dictionary platformDefines = new() {
+ { ApplePlatform.iOS, new [] { "__IOS__" } },
+ { ApplePlatform.TVOS, new [] { "__TVOS__" } },
+ { ApplePlatform.MacOSX, new [] { "__MACOS__" } },
+ { ApplePlatform.MacCatalyst, new [] { "__MACCATALYST__" } },
+ };
public BaseGeneratorTestClass ()
{
GeneratorGenerator = new BindingSourceGeneratorGenerator ();
- _driver = CSharpGeneratorDriver.Create (GeneratorGenerator);
+ Driver = CSharpGeneratorDriver.Create (GeneratorGenerator);
}
protected Compilation RunGeneratorsAndUpdateCompilation (Compilation compilation, out ImmutableArray diagnostics)
{
- _driver.RunGeneratorsAndUpdateCompilation (compilation, out var updatedCompilation, out diagnostics);
+ Driver.RunGeneratorsAndUpdateCompilation (compilation, out var updatedCompilation, out diagnostics);
return updatedCompilation;
}
protected GeneratorDriverRunResult RunGenerators (Compilation compilation)
- => _driver.RunGenerators (compilation).GetRunResult ();
+ => Driver.RunGenerators (compilation).GetRunResult ();
protected Compilation CreateCompilation (string name, ApplePlatform platform, params string [] sources)
{
@@ -46,8 +55,13 @@ protected Compilation CreateCompilation (string name, ApplePlatform platform, pa
} else {
throw new InvalidOperationException ($"Could not find platform dll for {platform}");
}
- var trees = sources.Select (s => CSharpSyntaxTree.ParseText (s));
- var options = new CSharpCompilationOptions (OutputKind.NetModule);
+
+ var parseOptions = new CSharpParseOptions (LanguageVersion.Latest, DocumentationMode.None, preprocessorSymbols: platformDefines[platform]);;
+ var trees = sources.Select (s => CSharpSyntaxTree.ParseText (s, parseOptions));
+
+ var options = new CSharpCompilationOptions (OutputKind.NetModule)
+ .WithAllowUnsafe (true);
+
return CSharpCompilation.Create (name, trees, references, options);
}
diff --git a/tests/rgen/Microsoft.Macios.Generator.Tests/BindingSourceGeneratorGeneratorTests.cs b/tests/rgen/Microsoft.Macios.Generator.Tests/BindingSourceGeneratorGeneratorTests.cs
index fd8738e7d3e..99279577131 100644
--- a/tests/rgen/Microsoft.Macios.Generator.Tests/BindingSourceGeneratorGeneratorTests.cs
+++ b/tests/rgen/Microsoft.Macios.Generator.Tests/BindingSourceGeneratorGeneratorTests.cs
@@ -23,9 +23,9 @@ public partial class AVAudioPcmBuffer : AVAudioBuffer {
#nullable enable
-using System;
using Foundation;
using ObjCBindings;
+using System;
namespace TestNamespace
{
@@ -48,7 +48,7 @@ public void CorrectUsingImports (ApplePlatform platform, string input, string ex
platform, usingImportInput);
// Run generators and retrieve all results.
- var runResult = _driver.RunGenerators (compilation).GetRunResult ();
+ var runResult = Driver.RunGenerators (compilation).GetRunResult ();
Assert.Empty (runResult.Diagnostics);
// ensure that we do have all the needed attributes present
diff --git a/tests/rgen/Microsoft.Macios.Generator.Tests/Microsoft.Macios.Generator.Tests.csproj b/tests/rgen/Microsoft.Macios.Generator.Tests/Microsoft.Macios.Generator.Tests.csproj
index 417b2c63d19..066aa4b25bd 100644
--- a/tests/rgen/Microsoft.Macios.Generator.Tests/Microsoft.Macios.Generator.Tests.csproj
+++ b/tests/rgen/Microsoft.Macios.Generator.Tests/Microsoft.Macios.Generator.Tests.csproj
@@ -20,6 +20,7 @@
+
@@ -36,24 +37,20 @@
external\ExecutionHelper.cs
-
- external\ApplePlatform.cs
-
-
- external\TargetFramework.cs
-
external\StringUtils.cs
external\Execution.cs
-
- external\SdkVersions.cs
-
external\Cache.cs
+
+
+
+
+
diff --git a/tests/rgen/Microsoft.Macios.Generator.Tests/SmartEnum/Data/AVCaptureDeviceTypeEnum.cs b/tests/rgen/Microsoft.Macios.Generator.Tests/SmartEnum/Data/AVCaptureDeviceTypeEnum.cs
new file mode 100644
index 00000000000..ba947b2b9f4
--- /dev/null
+++ b/tests/rgen/Microsoft.Macios.Generator.Tests/SmartEnum/Data/AVCaptureDeviceTypeEnum.cs
@@ -0,0 +1,43 @@
+using System;
+using Foundation;
+using ObjCRuntime;
+using ObjCBindings;
+
+namespace AVFoundation;
+
+[BindingType]
+public enum AVCaptureDeviceType {
+
+ [Field ("AVCaptureDeviceTypeBuiltInMicrophone")]
+ BuiltInMicrophone,
+
+ [Field ("AVCaptureDeviceTypeBuiltInWideAngleCamera")]
+ BuiltInWideAngleCamera,
+
+ [Field ("AVCaptureDeviceTypeBuiltInTelephotoCamera")]
+ BuiltInTelephotoCamera,
+
+ [Field ("AVCaptureDeviceTypeBuiltInDuoCamera")]
+ BuiltInDuoCamera,
+
+ [Field ("AVCaptureDeviceTypeBuiltInDualCamera")]
+ BuiltInDualCamera,
+
+ [Field ("AVCaptureDeviceTypeBuiltInTrueDepthCamera")]
+ BuiltInTrueDepthCamera,
+
+ [Field ("AVCaptureDeviceTypeBuiltInUltraWideCamera")]
+ BuiltInUltraWideCamera,
+
+ [Field ("AVCaptureDeviceTypeBuiltInTripleCamera")]
+ BuiltInTripleCamera,
+
+ [Field ("AVCaptureDeviceTypeBuiltInDualWideCamera")]
+ BuiltInDualWideCamera,
+
+ [Field ("AVCaptureDeviceTypeExternalUnknown")]
+ ExternalUnknown,
+
+ [Field ("AVCaptureDeviceTypeBuiltInLiDARDepthCamera")]
+ BuiltInLiDarDepthCamera,
+}
diff --git a/tests/rgen/Microsoft.Macios.Generator.Tests/SmartEnum/Data/AVCaptureSystemPressureLevel.cs b/tests/rgen/Microsoft.Macios.Generator.Tests/SmartEnum/Data/AVCaptureSystemPressureLevel.cs
new file mode 100644
index 00000000000..e16bf4334c5
--- /dev/null
+++ b/tests/rgen/Microsoft.Macios.Generator.Tests/SmartEnum/Data/AVCaptureSystemPressureLevel.cs
@@ -0,0 +1,23 @@
+using Foundation;
+using ObjCRuntime;
+using ObjCBindings;
+
+namespace AVFoundation;
+
+[BindingType]
+public enum AVCaptureSystemPressureLevel {
+ [Field ("AVCaptureSystemPressureLevelNominal")]
+ Nominal,
+
+ [Field ("AVCaptureSystemPressureLevelFair")]
+ Fair,
+
+ [Field ("AVCaptureSystemPressureLevelSerious")]
+ Serious,
+
+ [Field ("AVCaptureSystemPressureLevelCritical")]
+ Critical,
+
+ [Field ("AVCaptureSystemPressureLevelShutdown")]
+ Shutdown,
+}
diff --git a/tests/rgen/Microsoft.Macios.Generator.Tests/SmartEnum/Data/AVMediaCharacteristics.cs b/tests/rgen/Microsoft.Macios.Generator.Tests/SmartEnum/Data/AVMediaCharacteristics.cs
new file mode 100644
index 00000000000..82fe0c10b16
--- /dev/null
+++ b/tests/rgen/Microsoft.Macios.Generator.Tests/SmartEnum/Data/AVMediaCharacteristics.cs
@@ -0,0 +1,74 @@
+using System;
+using System.Runtime.Versioning;
+using Foundation;
+using ObjCRuntime;
+using ObjCBindings;
+
+namespace AVFoundation;
+
+[BindingType]
+[SupportedOSPlatform ("maccatalyst13.1")]
+enum AVMediaCharacteristics {
+ [Field ("AVMediaCharacteristicVisual")]
+ Visual = 0,
+
+ [Field ("AVMediaCharacteristicAudible")]
+ Audible = 1,
+
+ [Field ("AVMediaCharacteristicLegible")]
+ Legible = 2,
+
+ [Field ("AVMediaCharacteristicFrameBased")]
+ FrameBased = 3,
+
+ [Field ("AVMediaCharacteristicUsesWideGamutColorSpace")]
+ UsesWideGamutColorSpace = 4,
+
+ [Field ("AVMediaCharacteristicIsMainProgramContent")]
+ IsMainProgramContent = 5,
+
+ [Field ("AVMediaCharacteristicIsAuxiliaryContent")]
+ IsAuxiliaryContent = 6,
+
+ [Field ("AVMediaCharacteristicContainsOnlyForcedSubtitles")]
+ ContainsOnlyForcedSubtitles = 7,
+
+ [Field ("AVMediaCharacteristicTranscribesSpokenDialogForAccessibility")]
+ TranscribesSpokenDialogForAccessibility = 8,
+
+ [Field ("AVMediaCharacteristicDescribesMusicAndSoundForAccessibility")]
+ DescribesMusicAndSoundForAccessibility = 9,
+
+ [Field ("AVMediaCharacteristicDescribesVideoForAccessibility")]
+ DescribesVideoForAccessibility = 10,
+
+#if !__MACOS__
+ [Field ("AVMediaCharacteristicEasyToRead")]
+ EasyToRead = 11,
+#endif
+
+ [Field ("AVMediaCharacteristicLanguageTranslation")]
+ LanguageTranslation = 12,
+
+ [Field ("AVMediaCharacteristicDubbedTranslation")]
+ DubbedTranslation = 13,
+
+ [Field ("AVMediaCharacteristicVoiceOverTranslation")]
+ VoiceOverTranslation = 14,
+
+ [SupportedOSPlatform ("ios13.0")]
+ [SupportedOSPlatform ("tvos13.0")]
+ [Field ("AVMediaCharacteristicIsOriginalContent")]
+ IsOriginalContent = 15,
+
+ [SupportedOSPlatform ("ios14.0")]
+ [SupportedOSPlatform ("tvos14.0")]
+ [SupportedOSPlatform ("maccatalyst14.0")]
+ [Field ("AVMediaCharacteristicContainsHDRVideo")]
+ ContainsHdrVideo = 16,
+
+ [SupportedOSPlatform ("ios13.0")]
+ [SupportedOSPlatform ("tvos13.0")]
+ [Field ("AVMediaCharacteristicContainsAlphaChannel")]
+ ContainsAlphaChannel = 17,
+}
diff --git a/tests/rgen/Microsoft.Macios.Generator.Tests/SmartEnum/Data/CustomLibraryEnum.cs b/tests/rgen/Microsoft.Macios.Generator.Tests/SmartEnum/Data/CustomLibraryEnum.cs
new file mode 100644
index 00000000000..7bbcf15fff3
--- /dev/null
+++ b/tests/rgen/Microsoft.Macios.Generator.Tests/SmartEnum/Data/CustomLibraryEnum.cs
@@ -0,0 +1,17 @@
+using System;
+using System.Runtime.Versioning;
+using Foundation;
+using ObjCRuntime;
+using ObjCBindings;
+
+namespace CustomLibrary;
+
+[BindingType]
+public enum CustomLibraryEnum {
+ [Field ("None", "/path/to/customlibrary.framework")]
+ None,
+ [Field ("Medium", "/path/to/customlibrary.framework")]
+ Medium,
+ [Field ("High", "/path/to/customlibrary.framework")]
+ High,
+}
diff --git a/tests/rgen/Microsoft.Macios.Generator.Tests/SmartEnum/Data/ExpectedAVCaptureDeviceTypeEnum.cs b/tests/rgen/Microsoft.Macios.Generator.Tests/SmartEnum/Data/ExpectedAVCaptureDeviceTypeEnum.cs
new file mode 100644
index 00000000000..c748e4e3671
--- /dev/null
+++ b/tests/rgen/Microsoft.Macios.Generator.Tests/SmartEnum/Data/ExpectedAVCaptureDeviceTypeEnum.cs
@@ -0,0 +1,222 @@
+//
+
+#nullable enable
+
+using Foundation;
+using ObjCBindings;
+using ObjCRuntime;
+using System;
+
+namespace AVFoundation;
+
+[BindingImpl (BindingImplOptions.GeneratedCode | BindingImplOptions.Optimizable)]
+static public partial class AVCaptureDeviceTypeExtensions
+{
+
+ static IntPtr[] values = new IntPtr [11];
+
+ [Field ("AVCaptureDeviceTypeBuiltInMicrophone", "AVFoundation")]
+ internal unsafe static IntPtr AVCaptureDeviceTypeBuiltInMicrophone
+ {
+ get
+ {
+ fixed (IntPtr *storage = &values [0])
+ return Dlfcn.CachePointer (Libraries.AVFoundation.Handle, "AVCaptureDeviceTypeBuiltInMicrophone", storage);
+ }
+ }
+
+ [Field ("AVCaptureDeviceTypeBuiltInWideAngleCamera", "AVFoundation")]
+ internal unsafe static IntPtr AVCaptureDeviceTypeBuiltInWideAngleCamera
+ {
+ get
+ {
+ fixed (IntPtr *storage = &values [1])
+ return Dlfcn.CachePointer (Libraries.AVFoundation.Handle, "AVCaptureDeviceTypeBuiltInWideAngleCamera", storage);
+ }
+ }
+
+ [Field ("AVCaptureDeviceTypeBuiltInTelephotoCamera", "AVFoundation")]
+ internal unsafe static IntPtr AVCaptureDeviceTypeBuiltInTelephotoCamera
+ {
+ get
+ {
+ fixed (IntPtr *storage = &values [2])
+ return Dlfcn.CachePointer (Libraries.AVFoundation.Handle, "AVCaptureDeviceTypeBuiltInTelephotoCamera", storage);
+ }
+ }
+
+ [Field ("AVCaptureDeviceTypeBuiltInDuoCamera", "AVFoundation")]
+ internal unsafe static IntPtr AVCaptureDeviceTypeBuiltInDuoCamera
+ {
+ get
+ {
+ fixed (IntPtr *storage = &values [3])
+ return Dlfcn.CachePointer (Libraries.AVFoundation.Handle, "AVCaptureDeviceTypeBuiltInDuoCamera", storage);
+ }
+ }
+
+ [Field ("AVCaptureDeviceTypeBuiltInDualCamera", "AVFoundation")]
+ internal unsafe static IntPtr AVCaptureDeviceTypeBuiltInDualCamera
+ {
+ get
+ {
+ fixed (IntPtr *storage = &values [4])
+ return Dlfcn.CachePointer (Libraries.AVFoundation.Handle, "AVCaptureDeviceTypeBuiltInDualCamera", storage);
+ }
+ }
+
+ [Field ("AVCaptureDeviceTypeBuiltInTrueDepthCamera", "AVFoundation")]
+ internal unsafe static IntPtr AVCaptureDeviceTypeBuiltInTrueDepthCamera
+ {
+ get
+ {
+ fixed (IntPtr *storage = &values [5])
+ return Dlfcn.CachePointer (Libraries.AVFoundation.Handle, "AVCaptureDeviceTypeBuiltInTrueDepthCamera", storage);
+ }
+ }
+
+ [Field ("AVCaptureDeviceTypeBuiltInUltraWideCamera", "AVFoundation")]
+ internal unsafe static IntPtr AVCaptureDeviceTypeBuiltInUltraWideCamera
+ {
+ get
+ {
+ fixed (IntPtr *storage = &values [6])
+ return Dlfcn.CachePointer (Libraries.AVFoundation.Handle, "AVCaptureDeviceTypeBuiltInUltraWideCamera", storage);
+ }
+ }
+
+ [Field ("AVCaptureDeviceTypeBuiltInTripleCamera", "AVFoundation")]
+ internal unsafe static IntPtr AVCaptureDeviceTypeBuiltInTripleCamera
+ {
+ get
+ {
+ fixed (IntPtr *storage = &values [7])
+ return Dlfcn.CachePointer (Libraries.AVFoundation.Handle, "AVCaptureDeviceTypeBuiltInTripleCamera", storage);
+ }
+ }
+
+ [Field ("AVCaptureDeviceTypeBuiltInDualWideCamera", "AVFoundation")]
+ internal unsafe static IntPtr AVCaptureDeviceTypeBuiltInDualWideCamera
+ {
+ get
+ {
+ fixed (IntPtr *storage = &values [8])
+ return Dlfcn.CachePointer (Libraries.AVFoundation.Handle, "AVCaptureDeviceTypeBuiltInDualWideCamera", storage);
+ }
+ }
+
+ [Field ("AVCaptureDeviceTypeExternalUnknown", "AVFoundation")]
+ internal unsafe static IntPtr AVCaptureDeviceTypeExternalUnknown
+ {
+ get
+ {
+ fixed (IntPtr *storage = &values [9])
+ return Dlfcn.CachePointer (Libraries.AVFoundation.Handle, "AVCaptureDeviceTypeExternalUnknown", storage);
+ }
+ }
+
+ [Field ("AVCaptureDeviceTypeBuiltInLiDARDepthCamera", "AVFoundation")]
+ internal unsafe static IntPtr AVCaptureDeviceTypeBuiltInLiDARDepthCamera
+ {
+ get
+ {
+ fixed (IntPtr *storage = &values [10])
+ return Dlfcn.CachePointer (Libraries.AVFoundation.Handle, "AVCaptureDeviceTypeBuiltInLiDARDepthCamera", storage);
+ }
+ }
+
+ public static NSString? GetConstant (this AVCaptureDeviceType self)
+ {
+ IntPtr ptr = IntPtr.Zero;
+ switch ((int) self)
+ {
+ case 0: // AVCaptureDeviceTypeBuiltInMicrophone
+ ptr = AVCaptureDeviceTypeBuiltInMicrophone;
+ break;
+ case 1: // AVCaptureDeviceTypeBuiltInWideAngleCamera
+ ptr = AVCaptureDeviceTypeBuiltInWideAngleCamera;
+ break;
+ case 2: // AVCaptureDeviceTypeBuiltInTelephotoCamera
+ ptr = AVCaptureDeviceTypeBuiltInTelephotoCamera;
+ break;
+ case 3: // AVCaptureDeviceTypeBuiltInDuoCamera
+ ptr = AVCaptureDeviceTypeBuiltInDuoCamera;
+ break;
+ case 4: // AVCaptureDeviceTypeBuiltInDualCamera
+ ptr = AVCaptureDeviceTypeBuiltInDualCamera;
+ break;
+ case 5: // AVCaptureDeviceTypeBuiltInTrueDepthCamera
+ ptr = AVCaptureDeviceTypeBuiltInTrueDepthCamera;
+ break;
+ case 6: // AVCaptureDeviceTypeBuiltInUltraWideCamera
+ ptr = AVCaptureDeviceTypeBuiltInUltraWideCamera;
+ break;
+ case 7: // AVCaptureDeviceTypeBuiltInTripleCamera
+ ptr = AVCaptureDeviceTypeBuiltInTripleCamera;
+ break;
+ case 8: // AVCaptureDeviceTypeBuiltInDualWideCamera
+ ptr = AVCaptureDeviceTypeBuiltInDualWideCamera;
+ break;
+ case 9: // AVCaptureDeviceTypeExternalUnknown
+ ptr = AVCaptureDeviceTypeExternalUnknown;
+ break;
+ case 10: // AVCaptureDeviceTypeBuiltInLiDARDepthCamera
+ ptr = AVCaptureDeviceTypeBuiltInLiDARDepthCamera;
+ break;
+ }
+ return (NSString?) Runtime.GetNSObject (ptr);
+ }
+
+ public static AVCaptureDeviceType GetValue (NSString constant)
+ {
+ if (constant is null)
+ throw new ArgumentNullException (nameof (constant));
+ if (constant.IsEqualTo (AVCaptureDeviceTypeBuiltInMicrophone))
+ return AVCaptureDeviceType.BuiltInMicrophone;
+ if (constant.IsEqualTo (AVCaptureDeviceTypeBuiltInWideAngleCamera))
+ return AVCaptureDeviceType.BuiltInWideAngleCamera;
+ if (constant.IsEqualTo (AVCaptureDeviceTypeBuiltInTelephotoCamera))
+ return AVCaptureDeviceType.BuiltInTelephotoCamera;
+ if (constant.IsEqualTo (AVCaptureDeviceTypeBuiltInDuoCamera))
+ return AVCaptureDeviceType.BuiltInDuoCamera;
+ if (constant.IsEqualTo (AVCaptureDeviceTypeBuiltInDualCamera))
+ return AVCaptureDeviceType.BuiltInDualCamera;
+ if (constant.IsEqualTo (AVCaptureDeviceTypeBuiltInTrueDepthCamera))
+ return AVCaptureDeviceType.BuiltInTrueDepthCamera;
+ if (constant.IsEqualTo (AVCaptureDeviceTypeBuiltInUltraWideCamera))
+ return AVCaptureDeviceType.BuiltInUltraWideCamera;
+ if (constant.IsEqualTo (AVCaptureDeviceTypeBuiltInTripleCamera))
+ return AVCaptureDeviceType.BuiltInTripleCamera;
+ if (constant.IsEqualTo (AVCaptureDeviceTypeBuiltInDualWideCamera))
+ return AVCaptureDeviceType.BuiltInDualWideCamera;
+ if (constant.IsEqualTo (AVCaptureDeviceTypeExternalUnknown))
+ return AVCaptureDeviceType.ExternalUnknown;
+ if (constant.IsEqualTo (AVCaptureDeviceTypeBuiltInLiDARDepthCamera))
+ return AVCaptureDeviceType.BuiltInLiDarDepthCamera;
+ throw new NotSupportedException ($"{constant} has no associated enum value on this platform.");
+ }
+
+ internal static NSString?[]? ToConstantArray (this AVCaptureDeviceType[]? values)
+ {
+ if (values is null)
+ return null;
+ var rv = new global::System.Collections.Generic.List ();
+ for (var i = 0; i < values.Length; i++) {
+ var value = values [i];
+ rv.Add (value.GetConstant ());
+ }
+ return rv.ToArray ();
+ }
+
+ internal static AVCaptureDeviceType[]? ToEnumArray (this NSString[]? values)
+ {
+ if (values is null)
+ return null;
+ var rv = new global::System.Collections.Generic.List ();
+ for (var i = 0; i < values.Length; i++) {
+ var value = values [i];
+ rv.Add (GetValue (value));
+ }
+ return rv.ToArray ();
+ }
+}
diff --git a/tests/rgen/Microsoft.Macios.Generator.Tests/SmartEnum/Data/ExpectedAVCaptureSystemPressureLevel.cs b/tests/rgen/Microsoft.Macios.Generator.Tests/SmartEnum/Data/ExpectedAVCaptureSystemPressureLevel.cs
new file mode 100644
index 00000000000..83444b481be
--- /dev/null
+++ b/tests/rgen/Microsoft.Macios.Generator.Tests/SmartEnum/Data/ExpectedAVCaptureSystemPressureLevel.cs
@@ -0,0 +1,132 @@
+//
+
+#nullable enable
+
+using Foundation;
+using ObjCBindings;
+using ObjCRuntime;
+using System;
+
+namespace AVFoundation;
+
+[BindingImpl (BindingImplOptions.GeneratedCode | BindingImplOptions.Optimizable)]
+static public partial class AVCaptureSystemPressureLevelExtensions
+{
+
+ static IntPtr[] values = new IntPtr [5];
+
+ [Field ("AVCaptureSystemPressureLevelNominal", "AVFoundation")]
+ internal unsafe static IntPtr AVCaptureSystemPressureLevelNominal
+ {
+ get
+ {
+ fixed (IntPtr *storage = &values [0])
+ return Dlfcn.CachePointer (Libraries.AVFoundation.Handle, "AVCaptureSystemPressureLevelNominal", storage);
+ }
+ }
+
+ [Field ("AVCaptureSystemPressureLevelFair", "AVFoundation")]
+ internal unsafe static IntPtr AVCaptureSystemPressureLevelFair
+ {
+ get
+ {
+ fixed (IntPtr *storage = &values [1])
+ return Dlfcn.CachePointer (Libraries.AVFoundation.Handle, "AVCaptureSystemPressureLevelFair", storage);
+ }
+ }
+
+ [Field ("AVCaptureSystemPressureLevelSerious", "AVFoundation")]
+ internal unsafe static IntPtr AVCaptureSystemPressureLevelSerious
+ {
+ get
+ {
+ fixed (IntPtr *storage = &values [2])
+ return Dlfcn.CachePointer (Libraries.AVFoundation.Handle, "AVCaptureSystemPressureLevelSerious", storage);
+ }
+ }
+
+ [Field ("AVCaptureSystemPressureLevelCritical", "AVFoundation")]
+ internal unsafe static IntPtr AVCaptureSystemPressureLevelCritical
+ {
+ get
+ {
+ fixed (IntPtr *storage = &values [3])
+ return Dlfcn.CachePointer (Libraries.AVFoundation.Handle, "AVCaptureSystemPressureLevelCritical", storage);
+ }
+ }
+
+ [Field ("AVCaptureSystemPressureLevelShutdown", "AVFoundation")]
+ internal unsafe static IntPtr AVCaptureSystemPressureLevelShutdown
+ {
+ get
+ {
+ fixed (IntPtr *storage = &values [4])
+ return Dlfcn.CachePointer (Libraries.AVFoundation.Handle, "AVCaptureSystemPressureLevelShutdown", storage);
+ }
+ }
+
+ public static NSString? GetConstant (this AVCaptureSystemPressureLevel self)
+ {
+ IntPtr ptr = IntPtr.Zero;
+ switch ((int) self)
+ {
+ case 0: // AVCaptureSystemPressureLevelNominal
+ ptr = AVCaptureSystemPressureLevelNominal;
+ break;
+ case 1: // AVCaptureSystemPressureLevelFair
+ ptr = AVCaptureSystemPressureLevelFair;
+ break;
+ case 2: // AVCaptureSystemPressureLevelSerious
+ ptr = AVCaptureSystemPressureLevelSerious;
+ break;
+ case 3: // AVCaptureSystemPressureLevelCritical
+ ptr = AVCaptureSystemPressureLevelCritical;
+ break;
+ case 4: // AVCaptureSystemPressureLevelShutdown
+ ptr = AVCaptureSystemPressureLevelShutdown;
+ break;
+ }
+ return (NSString?) Runtime.GetNSObject (ptr);
+ }
+
+ public static AVCaptureSystemPressureLevel GetValue (NSString constant)
+ {
+ if (constant is null)
+ throw new ArgumentNullException (nameof (constant));
+ if (constant.IsEqualTo (AVCaptureSystemPressureLevelNominal))
+ return AVCaptureSystemPressureLevel.Nominal;
+ if (constant.IsEqualTo (AVCaptureSystemPressureLevelFair))
+ return AVCaptureSystemPressureLevel.Fair;
+ if (constant.IsEqualTo (AVCaptureSystemPressureLevelSerious))
+ return AVCaptureSystemPressureLevel.Serious;
+ if (constant.IsEqualTo (AVCaptureSystemPressureLevelCritical))
+ return AVCaptureSystemPressureLevel.Critical;
+ if (constant.IsEqualTo (AVCaptureSystemPressureLevelShutdown))
+ return AVCaptureSystemPressureLevel.Shutdown;
+ throw new NotSupportedException ($"{constant} has no associated enum value on this platform.");
+ }
+
+ internal static NSString?[]? ToConstantArray (this AVCaptureSystemPressureLevel[]? values)
+ {
+ if (values is null)
+ return null;
+ var rv = new global::System.Collections.Generic.List ();
+ for (var i = 0; i < values.Length; i++) {
+ var value = values [i];
+ rv.Add (value.GetConstant ());
+ }
+ return rv.ToArray ();
+ }
+
+ internal static AVCaptureSystemPressureLevel[]? ToEnumArray (this NSString[]? values)
+ {
+ if (values is null)
+ return null;
+ var rv = new global::System.Collections.Generic.List ();
+ for (var i = 0; i < values.Length; i++) {
+ var value = values [i];
+ rv.Add (GetValue (value));
+ }
+ return rv.ToArray ();
+ }
+}
diff --git a/tests/rgen/Microsoft.Macios.Generator.Tests/SmartEnum/Data/ExpectedCustomLibraryEnum.cs b/tests/rgen/Microsoft.Macios.Generator.Tests/SmartEnum/Data/ExpectedCustomLibraryEnum.cs
new file mode 100644
index 00000000000..1dc530f0b0c
--- /dev/null
+++ b/tests/rgen/Microsoft.Macios.Generator.Tests/SmartEnum/Data/ExpectedCustomLibraryEnum.cs
@@ -0,0 +1,103 @@
+//
+
+#nullable enable
+
+using Foundation;
+using ObjCBindings;
+using ObjCRuntime;
+using System;
+using System.Runtime.Versioning;
+
+namespace CustomLibrary;
+
+[BindingImpl (BindingImplOptions.GeneratedCode | BindingImplOptions.Optimizable)]
+static public partial class CustomLibraryEnumExtensions
+{
+
+ static IntPtr[] values = new IntPtr [3];
+
+ [Field ("None", "/path/to/customlibrary.framework")]
+ internal unsafe static IntPtr None
+ {
+ get
+ {
+ fixed (IntPtr *storage = &values [0])
+ return Dlfcn.CachePointer ("/path/to/customlibrary.framework", "None", storage);
+ }
+ }
+
+ [Field ("Medium", "/path/to/customlibrary.framework")]
+ internal unsafe static IntPtr Medium
+ {
+ get
+ {
+ fixed (IntPtr *storage = &values [1])
+ return Dlfcn.CachePointer ("/path/to/customlibrary.framework", "Medium", storage);
+ }
+ }
+
+ [Field ("High", "/path/to/customlibrary.framework")]
+ internal unsafe static IntPtr High
+ {
+ get
+ {
+ fixed (IntPtr *storage = &values [2])
+ return Dlfcn.CachePointer ("/path/to/customlibrary.framework", "High", storage);
+ }
+ }
+
+ public static NSString? GetConstant (this CustomLibraryEnum self)
+ {
+ IntPtr ptr = IntPtr.Zero;
+ switch ((int) self)
+ {
+ case 0: // None
+ ptr = None;
+ break;
+ case 1: // Medium
+ ptr = Medium;
+ break;
+ case 2: // High
+ ptr = High;
+ break;
+ }
+ return (NSString?) Runtime.GetNSObject (ptr);
+ }
+
+ public static CustomLibraryEnum GetValue (NSString constant)
+ {
+ if (constant is null)
+ throw new ArgumentNullException (nameof (constant));
+ if (constant.IsEqualTo (None))
+ return CustomLibraryEnum.None;
+ if (constant.IsEqualTo (Medium))
+ return CustomLibraryEnum.Medium;
+ if (constant.IsEqualTo (High))
+ return CustomLibraryEnum.High;
+ throw new NotSupportedException ($"{constant} has no associated enum value on this platform.");
+ }
+
+ internal static NSString?[]? ToConstantArray (this CustomLibraryEnum[]? values)
+ {
+ if (values is null)
+ return null;
+ var rv = new global::System.Collections.Generic.List ();
+ for (var i = 0; i < values.Length; i++) {
+ var value = values [i];
+ rv.Add (value.GetConstant ());
+ }
+ return rv.ToArray ();
+ }
+
+ internal static CustomLibraryEnum[]? ToEnumArray (this NSString[]? values)
+ {
+ if (values is null)
+ return null;
+ var rv = new global::System.Collections.Generic.List ();
+ for (var i = 0; i < values.Length; i++) {
+ var value = values [i];
+ rv.Add (GetValue (value));
+ }
+ return rv.ToArray ();
+ }
+}
diff --git a/tests/rgen/Microsoft.Macios.Generator.Tests/SmartEnum/Data/ExpectedMacOSAVMediaCharacteristics.cs b/tests/rgen/Microsoft.Macios.Generator.Tests/SmartEnum/Data/ExpectedMacOSAVMediaCharacteristics.cs
new file mode 100644
index 00000000000..3ea93bd507d
--- /dev/null
+++ b/tests/rgen/Microsoft.Macios.Generator.Tests/SmartEnum/Data/ExpectedMacOSAVMediaCharacteristics.cs
@@ -0,0 +1,313 @@
+//
+
+#nullable enable
+
+using Foundation;
+using ObjCBindings;
+using ObjCRuntime;
+using System;
+using System.Runtime.Versioning;
+
+namespace AVFoundation;
+
+[BindingImpl (BindingImplOptions.GeneratedCode | BindingImplOptions.Optimizable)]
+static public partial class AVMediaCharacteristicsExtensions
+{
+
+ static IntPtr[] values = new IntPtr [17];
+
+ [Field ("AVMediaCharacteristicVisual", "AVFoundation")]
+ internal unsafe static IntPtr AVMediaCharacteristicVisual
+ {
+ get
+ {
+ fixed (IntPtr *storage = &values [0])
+ return Dlfcn.CachePointer (Libraries.AVFoundation.Handle, "AVMediaCharacteristicVisual", storage);
+ }
+ }
+
+ [Field ("AVMediaCharacteristicAudible", "AVFoundation")]
+ internal unsafe static IntPtr AVMediaCharacteristicAudible
+ {
+ get
+ {
+ fixed (IntPtr *storage = &values [1])
+ return Dlfcn.CachePointer (Libraries.AVFoundation.Handle, "AVMediaCharacteristicAudible", storage);
+ }
+ }
+
+ [Field ("AVMediaCharacteristicLegible", "AVFoundation")]
+ internal unsafe static IntPtr AVMediaCharacteristicLegible
+ {
+ get
+ {
+ fixed (IntPtr *storage = &values [2])
+ return Dlfcn.CachePointer (Libraries.AVFoundation.Handle, "AVMediaCharacteristicLegible", storage);
+ }
+ }
+
+ [Field ("AVMediaCharacteristicFrameBased", "AVFoundation")]
+ internal unsafe static IntPtr AVMediaCharacteristicFrameBased
+ {
+ get
+ {
+ fixed (IntPtr *storage = &values [3])
+ return Dlfcn.CachePointer (Libraries.AVFoundation.Handle, "AVMediaCharacteristicFrameBased", storage);
+ }
+ }
+
+ [Field ("AVMediaCharacteristicUsesWideGamutColorSpace", "AVFoundation")]
+ internal unsafe static IntPtr AVMediaCharacteristicUsesWideGamutColorSpace
+ {
+ get
+ {
+ fixed (IntPtr *storage = &values [4])
+ return Dlfcn.CachePointer (Libraries.AVFoundation.Handle, "AVMediaCharacteristicUsesWideGamutColorSpace", storage);
+ }
+ }
+
+ [Field ("AVMediaCharacteristicIsMainProgramContent", "AVFoundation")]
+ internal unsafe static IntPtr AVMediaCharacteristicIsMainProgramContent
+ {
+ get
+ {
+ fixed (IntPtr *storage = &values [5])
+ return Dlfcn.CachePointer (Libraries.AVFoundation.Handle, "AVMediaCharacteristicIsMainProgramContent", storage);
+ }
+ }
+
+ [Field ("AVMediaCharacteristicIsAuxiliaryContent", "AVFoundation")]
+ internal unsafe static IntPtr AVMediaCharacteristicIsAuxiliaryContent
+ {
+ get
+ {
+ fixed (IntPtr *storage = &values [6])
+ return Dlfcn.CachePointer (Libraries.AVFoundation.Handle, "AVMediaCharacteristicIsAuxiliaryContent", storage);
+ }
+ }
+
+ [Field ("AVMediaCharacteristicContainsOnlyForcedSubtitles", "AVFoundation")]
+ internal unsafe static IntPtr AVMediaCharacteristicContainsOnlyForcedSubtitles
+ {
+ get
+ {
+ fixed (IntPtr *storage = &values [7])
+ return Dlfcn.CachePointer (Libraries.AVFoundation.Handle, "AVMediaCharacteristicContainsOnlyForcedSubtitles", storage);
+ }
+ }
+
+ [Field ("AVMediaCharacteristicTranscribesSpokenDialogForAccessibility", "AVFoundation")]
+ internal unsafe static IntPtr AVMediaCharacteristicTranscribesSpokenDialogForAccessibility
+ {
+ get
+ {
+ fixed (IntPtr *storage = &values [8])
+ return Dlfcn.CachePointer (Libraries.AVFoundation.Handle, "AVMediaCharacteristicTranscribesSpokenDialogForAccessibility", storage);
+ }
+ }
+
+ [Field ("AVMediaCharacteristicDescribesMusicAndSoundForAccessibility", "AVFoundation")]
+ internal unsafe static IntPtr AVMediaCharacteristicDescribesMusicAndSoundForAccessibility
+ {
+ get
+ {
+ fixed (IntPtr *storage = &values [9])
+ return Dlfcn.CachePointer (Libraries.AVFoundation.Handle, "AVMediaCharacteristicDescribesMusicAndSoundForAccessibility", storage);
+ }
+ }
+
+ [Field ("AVMediaCharacteristicDescribesVideoForAccessibility", "AVFoundation")]
+ internal unsafe static IntPtr AVMediaCharacteristicDescribesVideoForAccessibility
+ {
+ get
+ {
+ fixed (IntPtr *storage = &values [10])
+ return Dlfcn.CachePointer (Libraries.AVFoundation.Handle, "AVMediaCharacteristicDescribesVideoForAccessibility", storage);
+ }
+ }
+
+ [Field ("AVMediaCharacteristicLanguageTranslation", "AVFoundation")]
+ internal unsafe static IntPtr AVMediaCharacteristicLanguageTranslation
+ {
+ get
+ {
+ fixed (IntPtr *storage = &values [11])
+ return Dlfcn.CachePointer (Libraries.AVFoundation.Handle, "AVMediaCharacteristicLanguageTranslation", storage);
+ }
+ }
+
+ [Field ("AVMediaCharacteristicDubbedTranslation", "AVFoundation")]
+ internal unsafe static IntPtr AVMediaCharacteristicDubbedTranslation
+ {
+ get
+ {
+ fixed (IntPtr *storage = &values [12])
+ return Dlfcn.CachePointer (Libraries.AVFoundation.Handle, "AVMediaCharacteristicDubbedTranslation", storage);
+ }
+ }
+
+ [Field ("AVMediaCharacteristicVoiceOverTranslation", "AVFoundation")]
+ internal unsafe static IntPtr AVMediaCharacteristicVoiceOverTranslation
+ {
+ get
+ {
+ fixed (IntPtr *storage = &values [13])
+ return Dlfcn.CachePointer (Libraries.AVFoundation.Handle, "AVMediaCharacteristicVoiceOverTranslation", storage);
+ }
+ }
+
+ [Field ("AVMediaCharacteristicIsOriginalContent", "AVFoundation")]
+ internal unsafe static IntPtr AVMediaCharacteristicIsOriginalContent
+ {
+ get
+ {
+ fixed (IntPtr *storage = &values [14])
+ return Dlfcn.CachePointer (Libraries.AVFoundation.Handle, "AVMediaCharacteristicIsOriginalContent", storage);
+ }
+ }
+
+ [Field ("AVMediaCharacteristicContainsHDRVideo", "AVFoundation")]
+ internal unsafe static IntPtr AVMediaCharacteristicContainsHDRVideo
+ {
+ get
+ {
+ fixed (IntPtr *storage = &values [15])
+ return Dlfcn.CachePointer (Libraries.AVFoundation.Handle, "AVMediaCharacteristicContainsHDRVideo", storage);
+ }
+ }
+
+ [Field ("AVMediaCharacteristicContainsAlphaChannel", "AVFoundation")]
+ internal unsafe static IntPtr AVMediaCharacteristicContainsAlphaChannel
+ {
+ get
+ {
+ fixed (IntPtr *storage = &values [16])
+ return Dlfcn.CachePointer (Libraries.AVFoundation.Handle, "AVMediaCharacteristicContainsAlphaChannel", storage);
+ }
+ }
+
+ public static NSString? GetConstant (this AVMediaCharacteristics self)
+ {
+ IntPtr ptr = IntPtr.Zero;
+ switch ((int) self)
+ {
+ case 0: // AVMediaCharacteristicVisual
+ ptr = AVMediaCharacteristicVisual;
+ break;
+ case 1: // AVMediaCharacteristicAudible
+ ptr = AVMediaCharacteristicAudible;
+ break;
+ case 2: // AVMediaCharacteristicLegible
+ ptr = AVMediaCharacteristicLegible;
+ break;
+ case 3: // AVMediaCharacteristicFrameBased
+ ptr = AVMediaCharacteristicFrameBased;
+ break;
+ case 4: // AVMediaCharacteristicUsesWideGamutColorSpace
+ ptr = AVMediaCharacteristicUsesWideGamutColorSpace;
+ break;
+ case 5: // AVMediaCharacteristicIsMainProgramContent
+ ptr = AVMediaCharacteristicIsMainProgramContent;
+ break;
+ case 6: // AVMediaCharacteristicIsAuxiliaryContent
+ ptr = AVMediaCharacteristicIsAuxiliaryContent;
+ break;
+ case 7: // AVMediaCharacteristicContainsOnlyForcedSubtitles
+ ptr = AVMediaCharacteristicContainsOnlyForcedSubtitles;
+ break;
+ case 8: // AVMediaCharacteristicTranscribesSpokenDialogForAccessibility
+ ptr = AVMediaCharacteristicTranscribesSpokenDialogForAccessibility;
+ break;
+ case 9: // AVMediaCharacteristicDescribesMusicAndSoundForAccessibility
+ ptr = AVMediaCharacteristicDescribesMusicAndSoundForAccessibility;
+ break;
+ case 10: // AVMediaCharacteristicDescribesVideoForAccessibility
+ ptr = AVMediaCharacteristicDescribesVideoForAccessibility;
+ break;
+ case 11: // AVMediaCharacteristicLanguageTranslation
+ ptr = AVMediaCharacteristicLanguageTranslation;
+ break;
+ case 12: // AVMediaCharacteristicDubbedTranslation
+ ptr = AVMediaCharacteristicDubbedTranslation;
+ break;
+ case 13: // AVMediaCharacteristicVoiceOverTranslation
+ ptr = AVMediaCharacteristicVoiceOverTranslation;
+ break;
+ case 14: // AVMediaCharacteristicIsOriginalContent
+ ptr = AVMediaCharacteristicIsOriginalContent;
+ break;
+ case 15: // AVMediaCharacteristicContainsHDRVideo
+ ptr = AVMediaCharacteristicContainsHDRVideo;
+ break;
+ case 16: // AVMediaCharacteristicContainsAlphaChannel
+ ptr = AVMediaCharacteristicContainsAlphaChannel;
+ break;
+ }
+ return (NSString?) Runtime.GetNSObject (ptr);
+ }
+
+ public static AVMediaCharacteristics GetValue (NSString constant)
+ {
+ if (constant is null)
+ throw new ArgumentNullException (nameof (constant));
+ if (constant.IsEqualTo (AVMediaCharacteristicVisual))
+ return AVMediaCharacteristics.Visual;
+ if (constant.IsEqualTo (AVMediaCharacteristicAudible))
+ return AVMediaCharacteristics.Audible;
+ if (constant.IsEqualTo (AVMediaCharacteristicLegible))
+ return AVMediaCharacteristics.Legible;
+ if (constant.IsEqualTo (AVMediaCharacteristicFrameBased))
+ return AVMediaCharacteristics.FrameBased;
+ if (constant.IsEqualTo (AVMediaCharacteristicUsesWideGamutColorSpace))
+ return AVMediaCharacteristics.UsesWideGamutColorSpace;
+ if (constant.IsEqualTo (AVMediaCharacteristicIsMainProgramContent))
+ return AVMediaCharacteristics.IsMainProgramContent;
+ if (constant.IsEqualTo (AVMediaCharacteristicIsAuxiliaryContent))
+ return AVMediaCharacteristics.IsAuxiliaryContent;
+ if (constant.IsEqualTo (AVMediaCharacteristicContainsOnlyForcedSubtitles))
+ return AVMediaCharacteristics.ContainsOnlyForcedSubtitles;
+ if (constant.IsEqualTo (AVMediaCharacteristicTranscribesSpokenDialogForAccessibility))
+ return AVMediaCharacteristics.TranscribesSpokenDialogForAccessibility;
+ if (constant.IsEqualTo (AVMediaCharacteristicDescribesMusicAndSoundForAccessibility))
+ return AVMediaCharacteristics.DescribesMusicAndSoundForAccessibility;
+ if (constant.IsEqualTo (AVMediaCharacteristicDescribesVideoForAccessibility))
+ return AVMediaCharacteristics.DescribesVideoForAccessibility;
+ if (constant.IsEqualTo (AVMediaCharacteristicLanguageTranslation))
+ return AVMediaCharacteristics.LanguageTranslation;
+ if (constant.IsEqualTo (AVMediaCharacteristicDubbedTranslation))
+ return AVMediaCharacteristics.DubbedTranslation;
+ if (constant.IsEqualTo (AVMediaCharacteristicVoiceOverTranslation))
+ return AVMediaCharacteristics.VoiceOverTranslation;
+ if (constant.IsEqualTo (AVMediaCharacteristicIsOriginalContent))
+ return AVMediaCharacteristics.IsOriginalContent;
+ if (constant.IsEqualTo (AVMediaCharacteristicContainsHDRVideo))
+ return AVMediaCharacteristics.ContainsHdrVideo;
+ if (constant.IsEqualTo (AVMediaCharacteristicContainsAlphaChannel))
+ return AVMediaCharacteristics.ContainsAlphaChannel;
+ throw new NotSupportedException ($"{constant} has no associated enum value on this platform.");
+ }
+
+ internal static NSString?[]? ToConstantArray (this AVMediaCharacteristics[]? values)
+ {
+ if (values is null)
+ return null;
+ var rv = new global::System.Collections.Generic.List ();
+ for (var i = 0; i < values.Length; i++) {
+ var value = values [i];
+ rv.Add (value.GetConstant ());
+ }
+ return rv.ToArray ();
+ }
+
+ internal static AVMediaCharacteristics[]? ToEnumArray (this NSString[]? values)
+ {
+ if (values is null)
+ return null;
+ var rv = new global::System.Collections.Generic.List ();
+ for (var i = 0; i < values.Length; i++) {
+ var value = values [i];
+ rv.Add (GetValue (value));
+ }
+ return rv.ToArray ();
+ }
+}
diff --git a/tests/rgen/Microsoft.Macios.Generator.Tests/SmartEnum/Data/ExpectediOSAVMediaCharacteristics.cs b/tests/rgen/Microsoft.Macios.Generator.Tests/SmartEnum/Data/ExpectediOSAVMediaCharacteristics.cs
new file mode 100644
index 00000000000..0d121407e33
--- /dev/null
+++ b/tests/rgen/Microsoft.Macios.Generator.Tests/SmartEnum/Data/ExpectediOSAVMediaCharacteristics.cs
@@ -0,0 +1,328 @@
+//
+
+#nullable enable
+
+using Foundation;
+using ObjCBindings;
+using ObjCRuntime;
+using System;
+using System.Runtime.Versioning;
+
+namespace AVFoundation;
+
+[BindingImpl (BindingImplOptions.GeneratedCode | BindingImplOptions.Optimizable)]
+static public partial class AVMediaCharacteristicsExtensions
+{
+
+ static IntPtr[] values = new IntPtr [18];
+
+ [Field ("AVMediaCharacteristicVisual", "AVFoundation")]
+ internal unsafe static IntPtr AVMediaCharacteristicVisual
+ {
+ get
+ {
+ fixed (IntPtr *storage = &values [0])
+ return Dlfcn.CachePointer (Libraries.AVFoundation.Handle, "AVMediaCharacteristicVisual", storage);
+ }
+ }
+
+ [Field ("AVMediaCharacteristicAudible", "AVFoundation")]
+ internal unsafe static IntPtr AVMediaCharacteristicAudible
+ {
+ get
+ {
+ fixed (IntPtr *storage = &values [1])
+ return Dlfcn.CachePointer (Libraries.AVFoundation.Handle, "AVMediaCharacteristicAudible", storage);
+ }
+ }
+
+ [Field ("AVMediaCharacteristicLegible", "AVFoundation")]
+ internal unsafe static IntPtr AVMediaCharacteristicLegible
+ {
+ get
+ {
+ fixed (IntPtr *storage = &values [2])
+ return Dlfcn.CachePointer (Libraries.AVFoundation.Handle, "AVMediaCharacteristicLegible", storage);
+ }
+ }
+
+ [Field ("AVMediaCharacteristicFrameBased", "AVFoundation")]
+ internal unsafe static IntPtr AVMediaCharacteristicFrameBased
+ {
+ get
+ {
+ fixed (IntPtr *storage = &values [3])
+ return Dlfcn.CachePointer (Libraries.AVFoundation.Handle, "AVMediaCharacteristicFrameBased", storage);
+ }
+ }
+
+ [Field ("AVMediaCharacteristicUsesWideGamutColorSpace", "AVFoundation")]
+ internal unsafe static IntPtr AVMediaCharacteristicUsesWideGamutColorSpace
+ {
+ get
+ {
+ fixed (IntPtr *storage = &values [4])
+ return Dlfcn.CachePointer (Libraries.AVFoundation.Handle, "AVMediaCharacteristicUsesWideGamutColorSpace", storage);
+ }
+ }
+
+ [Field ("AVMediaCharacteristicIsMainProgramContent", "AVFoundation")]
+ internal unsafe static IntPtr AVMediaCharacteristicIsMainProgramContent
+ {
+ get
+ {
+ fixed (IntPtr *storage = &values [5])
+ return Dlfcn.CachePointer (Libraries.AVFoundation.Handle, "AVMediaCharacteristicIsMainProgramContent", storage);
+ }
+ }
+
+ [Field ("AVMediaCharacteristicIsAuxiliaryContent", "AVFoundation")]
+ internal unsafe static IntPtr AVMediaCharacteristicIsAuxiliaryContent
+ {
+ get
+ {
+ fixed (IntPtr *storage = &values [6])
+ return Dlfcn.CachePointer (Libraries.AVFoundation.Handle, "AVMediaCharacteristicIsAuxiliaryContent", storage);
+ }
+ }
+
+ [Field ("AVMediaCharacteristicContainsOnlyForcedSubtitles", "AVFoundation")]
+ internal unsafe static IntPtr AVMediaCharacteristicContainsOnlyForcedSubtitles
+ {
+ get
+ {
+ fixed (IntPtr *storage = &values [7])
+ return Dlfcn.CachePointer (Libraries.AVFoundation.Handle, "AVMediaCharacteristicContainsOnlyForcedSubtitles", storage);
+ }
+ }
+
+ [Field ("AVMediaCharacteristicTranscribesSpokenDialogForAccessibility", "AVFoundation")]
+ internal unsafe static IntPtr AVMediaCharacteristicTranscribesSpokenDialogForAccessibility
+ {
+ get
+ {
+ fixed (IntPtr *storage = &values [8])
+ return Dlfcn.CachePointer (Libraries.AVFoundation.Handle, "AVMediaCharacteristicTranscribesSpokenDialogForAccessibility", storage);
+ }
+ }
+
+ [Field ("AVMediaCharacteristicDescribesMusicAndSoundForAccessibility", "AVFoundation")]
+ internal unsafe static IntPtr AVMediaCharacteristicDescribesMusicAndSoundForAccessibility
+ {
+ get
+ {
+ fixed (IntPtr *storage = &values [9])
+ return Dlfcn.CachePointer (Libraries.AVFoundation.Handle, "AVMediaCharacteristicDescribesMusicAndSoundForAccessibility", storage);
+ }
+ }
+
+ [Field ("AVMediaCharacteristicDescribesVideoForAccessibility", "AVFoundation")]
+ internal unsafe static IntPtr AVMediaCharacteristicDescribesVideoForAccessibility
+ {
+ get
+ {
+ fixed (IntPtr *storage = &values [10])
+ return Dlfcn.CachePointer (Libraries.AVFoundation.Handle, "AVMediaCharacteristicDescribesVideoForAccessibility", storage);
+ }
+ }
+
+ [Field ("AVMediaCharacteristicEasyToRead", "AVFoundation")]
+ internal unsafe static IntPtr AVMediaCharacteristicEasyToRead
+ {
+ get
+ {
+ fixed (IntPtr *storage = &values [11])
+ return Dlfcn.CachePointer (Libraries.AVFoundation.Handle, "AVMediaCharacteristicEasyToRead", storage);
+ }
+ }
+
+ [Field ("AVMediaCharacteristicLanguageTranslation", "AVFoundation")]
+ internal unsafe static IntPtr AVMediaCharacteristicLanguageTranslation
+ {
+ get
+ {
+ fixed (IntPtr *storage = &values [12])
+ return Dlfcn.CachePointer (Libraries.AVFoundation.Handle, "AVMediaCharacteristicLanguageTranslation", storage);
+ }
+ }
+
+ [Field ("AVMediaCharacteristicDubbedTranslation", "AVFoundation")]
+ internal unsafe static IntPtr AVMediaCharacteristicDubbedTranslation
+ {
+ get
+ {
+ fixed (IntPtr *storage = &values [13])
+ return Dlfcn.CachePointer (Libraries.AVFoundation.Handle, "AVMediaCharacteristicDubbedTranslation", storage);
+ }
+ }
+
+ [Field ("AVMediaCharacteristicVoiceOverTranslation", "AVFoundation")]
+ internal unsafe static IntPtr AVMediaCharacteristicVoiceOverTranslation
+ {
+ get
+ {
+ fixed (IntPtr *storage = &values [14])
+ return Dlfcn.CachePointer (Libraries.AVFoundation.Handle, "AVMediaCharacteristicVoiceOverTranslation", storage);
+ }
+ }
+
+ [Field ("AVMediaCharacteristicIsOriginalContent", "AVFoundation")]
+ internal unsafe static IntPtr AVMediaCharacteristicIsOriginalContent
+ {
+ get
+ {
+ fixed (IntPtr *storage = &values [15])
+ return Dlfcn.CachePointer (Libraries.AVFoundation.Handle, "AVMediaCharacteristicIsOriginalContent", storage);
+ }
+ }
+
+ [Field ("AVMediaCharacteristicContainsHDRVideo", "AVFoundation")]
+ internal unsafe static IntPtr AVMediaCharacteristicContainsHDRVideo
+ {
+ get
+ {
+ fixed (IntPtr *storage = &values [16])
+ return Dlfcn.CachePointer (Libraries.AVFoundation.Handle, "AVMediaCharacteristicContainsHDRVideo", storage);
+ }
+ }
+
+ [Field ("AVMediaCharacteristicContainsAlphaChannel", "AVFoundation")]
+ internal unsafe static IntPtr AVMediaCharacteristicContainsAlphaChannel
+ {
+ get
+ {
+ fixed (IntPtr *storage = &values [17])
+ return Dlfcn.CachePointer (Libraries.AVFoundation.Handle, "AVMediaCharacteristicContainsAlphaChannel", storage);
+ }
+ }
+
+ public static NSString? GetConstant (this AVMediaCharacteristics self)
+ {
+ IntPtr ptr = IntPtr.Zero;
+ switch ((int) self)
+ {
+ case 0: // AVMediaCharacteristicVisual
+ ptr = AVMediaCharacteristicVisual;
+ break;
+ case 1: // AVMediaCharacteristicAudible
+ ptr = AVMediaCharacteristicAudible;
+ break;
+ case 2: // AVMediaCharacteristicLegible
+ ptr = AVMediaCharacteristicLegible;
+ break;
+ case 3: // AVMediaCharacteristicFrameBased
+ ptr = AVMediaCharacteristicFrameBased;
+ break;
+ case 4: // AVMediaCharacteristicUsesWideGamutColorSpace
+ ptr = AVMediaCharacteristicUsesWideGamutColorSpace;
+ break;
+ case 5: // AVMediaCharacteristicIsMainProgramContent
+ ptr = AVMediaCharacteristicIsMainProgramContent;
+ break;
+ case 6: // AVMediaCharacteristicIsAuxiliaryContent
+ ptr = AVMediaCharacteristicIsAuxiliaryContent;
+ break;
+ case 7: // AVMediaCharacteristicContainsOnlyForcedSubtitles
+ ptr = AVMediaCharacteristicContainsOnlyForcedSubtitles;
+ break;
+ case 8: // AVMediaCharacteristicTranscribesSpokenDialogForAccessibility
+ ptr = AVMediaCharacteristicTranscribesSpokenDialogForAccessibility;
+ break;
+ case 9: // AVMediaCharacteristicDescribesMusicAndSoundForAccessibility
+ ptr = AVMediaCharacteristicDescribesMusicAndSoundForAccessibility;
+ break;
+ case 10: // AVMediaCharacteristicDescribesVideoForAccessibility
+ ptr = AVMediaCharacteristicDescribesVideoForAccessibility;
+ break;
+ case 11: // AVMediaCharacteristicEasyToRead
+ ptr = AVMediaCharacteristicEasyToRead;
+ break;
+ case 12: // AVMediaCharacteristicLanguageTranslation
+ ptr = AVMediaCharacteristicLanguageTranslation;
+ break;
+ case 13: // AVMediaCharacteristicDubbedTranslation
+ ptr = AVMediaCharacteristicDubbedTranslation;
+ break;
+ case 14: // AVMediaCharacteristicVoiceOverTranslation
+ ptr = AVMediaCharacteristicVoiceOverTranslation;
+ break;
+ case 15: // AVMediaCharacteristicIsOriginalContent
+ ptr = AVMediaCharacteristicIsOriginalContent;
+ break;
+ case 16: // AVMediaCharacteristicContainsHDRVideo
+ ptr = AVMediaCharacteristicContainsHDRVideo;
+ break;
+ case 17: // AVMediaCharacteristicContainsAlphaChannel
+ ptr = AVMediaCharacteristicContainsAlphaChannel;
+ break;
+ }
+ return (NSString?) Runtime.GetNSObject (ptr);
+ }
+
+ public static AVMediaCharacteristics GetValue (NSString constant)
+ {
+ if (constant is null)
+ throw new ArgumentNullException (nameof (constant));
+ if (constant.IsEqualTo (AVMediaCharacteristicVisual))
+ return AVMediaCharacteristics.Visual;
+ if (constant.IsEqualTo (AVMediaCharacteristicAudible))
+ return AVMediaCharacteristics.Audible;
+ if (constant.IsEqualTo (AVMediaCharacteristicLegible))
+ return AVMediaCharacteristics.Legible;
+ if (constant.IsEqualTo (AVMediaCharacteristicFrameBased))
+ return AVMediaCharacteristics.FrameBased;
+ if (constant.IsEqualTo (AVMediaCharacteristicUsesWideGamutColorSpace))
+ return AVMediaCharacteristics.UsesWideGamutColorSpace;
+ if (constant.IsEqualTo (AVMediaCharacteristicIsMainProgramContent))
+ return AVMediaCharacteristics.IsMainProgramContent;
+ if (constant.IsEqualTo (AVMediaCharacteristicIsAuxiliaryContent))
+ return AVMediaCharacteristics.IsAuxiliaryContent;
+ if (constant.IsEqualTo (AVMediaCharacteristicContainsOnlyForcedSubtitles))
+ return AVMediaCharacteristics.ContainsOnlyForcedSubtitles;
+ if (constant.IsEqualTo (AVMediaCharacteristicTranscribesSpokenDialogForAccessibility))
+ return AVMediaCharacteristics.TranscribesSpokenDialogForAccessibility;
+ if (constant.IsEqualTo (AVMediaCharacteristicDescribesMusicAndSoundForAccessibility))
+ return AVMediaCharacteristics.DescribesMusicAndSoundForAccessibility;
+ if (constant.IsEqualTo (AVMediaCharacteristicDescribesVideoForAccessibility))
+ return AVMediaCharacteristics.DescribesVideoForAccessibility;
+ if (constant.IsEqualTo (AVMediaCharacteristicEasyToRead))
+ return AVMediaCharacteristics.EasyToRead;
+ if (constant.IsEqualTo (AVMediaCharacteristicLanguageTranslation))
+ return AVMediaCharacteristics.LanguageTranslation;
+ if (constant.IsEqualTo (AVMediaCharacteristicDubbedTranslation))
+ return AVMediaCharacteristics.DubbedTranslation;
+ if (constant.IsEqualTo (AVMediaCharacteristicVoiceOverTranslation))
+ return AVMediaCharacteristics.VoiceOverTranslation;
+ if (constant.IsEqualTo (AVMediaCharacteristicIsOriginalContent))
+ return AVMediaCharacteristics.IsOriginalContent;
+ if (constant.IsEqualTo (AVMediaCharacteristicContainsHDRVideo))
+ return AVMediaCharacteristics.ContainsHdrVideo;
+ if (constant.IsEqualTo (AVMediaCharacteristicContainsAlphaChannel))
+ return AVMediaCharacteristics.ContainsAlphaChannel;
+ throw new NotSupportedException ($"{constant} has no associated enum value on this platform.");
+ }
+
+ internal static NSString?[]? ToConstantArray (this AVMediaCharacteristics[]? values)
+ {
+ if (values is null)
+ return null;
+ var rv = new global::System.Collections.Generic.List ();
+ for (var i = 0; i < values.Length; i++) {
+ var value = values [i];
+ rv.Add (value.GetConstant ());
+ }
+ return rv.ToArray ();
+ }
+
+ internal static AVMediaCharacteristics[]? ToEnumArray (this NSString[]? values)
+ {
+ if (values is null)
+ return null;
+ var rv = new global::System.Collections.Generic.List ();
+ for (var i = 0; i < values.Length; i++) {
+ var value = values [i];
+ rv.Add (GetValue (value));
+ }
+ return rv.ToArray ();
+ }
+}
diff --git a/tests/rgen/Microsoft.Macios.Generator.Tests/SmartEnum/SmartEnumTests.cs b/tests/rgen/Microsoft.Macios.Generator.Tests/SmartEnum/SmartEnumTests.cs
new file mode 100644
index 00000000000..25320b39e49
--- /dev/null
+++ b/tests/rgen/Microsoft.Macios.Generator.Tests/SmartEnum/SmartEnumTests.cs
@@ -0,0 +1,46 @@
+using System.Collections;
+using System.Collections.Generic;
+using Xunit;
+using Xamarin.Utils;
+
+namespace Microsoft.Macios.Generator.Tests.SmartEnum;
+
+
+///
+/// Test all the field generation code.
+///
+public class SmartEnumTests : BaseGeneratorTestClass
+{
+ public class TestDataGenerator : BaseTestDataGenerator, IEnumerable