Skip to content

Commit

Permalink
[Rgen] Use the formatter pattern for the SmartEnum extension class de…
Browse files Browse the repository at this point in the history
…claration. (#21900)

Use the same pattern as with other declarations

---------

Co-authored-by: GitHub Actions Autoformatter <[email protected]>
Co-authored-by: Michael Cummings (MSFT) <[email protected]>
  • Loading branch information
3 people authored Jan 9, 2025
1 parent 544516d commit 605b1b7
Show file tree
Hide file tree
Showing 3 changed files with 129 additions and 2 deletions.
6 changes: 4 additions & 2 deletions src/rgen/Microsoft.Macios.Generator/Emitters/EnumEmitter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using Microsoft.CodeAnalysis;
using Microsoft.Macios.Generator.Context;
using Microsoft.Macios.Generator.DataModel;
using Microsoft.Macios.Generator.Formatters;

namespace Microsoft.Macios.Generator.Emitters;

Expand Down Expand Up @@ -139,8 +140,9 @@ public bool TryEmit (in BindingContext bindingContext, [NotNullWhen (false)] out

bindingContext.Builder.AppendMemberAvailability (bindingContext.Changes.SymbolAvailability);
bindingContext.Builder.AppendGeneratedCodeAttribute ();
var modifiers = $"{string.Join (' ', bindingContext.Changes.Modifiers)} ";
using (var classBlock = bindingContext.Builder.CreateBlock ($"{(string.IsNullOrWhiteSpace (modifiers) ? string.Empty : modifiers)}static partial class {GetSymbolName (bindingContext.Changes)}", true)) {
var extensionClassDeclaration =
bindingContext.Changes.ToSmartEnumExtensionDeclaration (GetSymbolName (bindingContext.Changes));
using (var classBlock = bindingContext.Builder.CreateBlock (extensionClassDeclaration.ToString (), true)) {
classBlock.AppendLine ();
classBlock.AppendLine ($"static IntPtr[] values = new IntPtr [{bindingContext.Changes.EnumMembers.Length}];");
// foreach member in the enum we need to create a field that holds the value, the property emitter
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
using System.Collections.Generic;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.Macios.Generator.DataModel;
using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;

namespace Microsoft.Macios.Generator.Formatters;

static class SmartEnumFormatter {
/// <summary>
/// Return the declaration of the extension class for a given smart enum using the provided class name.
/// </summary>
/// <param name="smartEnumChange">The smart enum code change.</param>
/// <param name="extensionClassName">The name to use for the extension class.</param>
/// <returns>The class declaration for the extension class for a smart enum change.</returns>
public static CompilationUnitSyntax ToSmartEnumExtensionDeclaration (this in CodeChanges smartEnumChange,
string extensionClassName)
{
// add to a set to make sure we do no duplicate them and make sure static and partial are present
var modifiers = new HashSet<SyntaxToken> (smartEnumChange.Modifiers) {
Token (SyntaxKind.StaticKeyword), Token (SyntaxKind.PartialKeyword),
};

var compilationUnit = CompilationUnit ().WithMembers (
SingletonList<MemberDeclarationSyntax> (
ClassDeclaration (extensionClassName)
.WithModifiers (TokenList (modifiers))
.WithOpenBraceToken (MissingToken (SyntaxKind.OpenBraceToken)) // do not add open/close brace
.WithCloseBraceToken (MissingToken (SyntaxKind.CloseBraceToken))))
.NormalizeWhitespace (); // no diff between dotnet and mono
return compilationUnit;
}

/// <summary>
/// Return the declaration of the extension class for a given smart enum using the provided class name.
/// </summary>
/// <param name="smartEnumChange">The smart enum code change.</param>
/// <param name="extensionClassName">The name to use for the extension class.</param>
/// <returns>The class declaration for the extension class for a smart enum change.</returns>
public static CompilationUnitSyntax? ToSmartEnumExtensionDeclaration (this in CodeChanges? smartEnumChange,
string extensionClassName)
=> smartEnumChange is null ? null : ToSmartEnumExtensionDeclaration (smartEnumChange.Value, extensionClassName);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.Macios.Generator.DataModel;
using Microsoft.Macios.Generator.Formatters;
using Xamarin.Tests;
using Xamarin.Utils;
using Xunit;

namespace Microsoft.Macios.Generator.Tests.Formatters;

public class SmartEnumFormatterTests : BaseGeneratorTestClass {

class TestDataToExtensionDeclaration : IEnumerable<object []> {
public IEnumerator<object []> GetEnumerator ()
{
const string publicSmartEnum = @"
using System;
using Foundation;
using ObjCBindings;
namespace AVFoundation;
[BindingType]
public enum AVCaptureDeviceType {
[Field<EnumValue> (""AVCaptureDeviceTypeBuiltInMicrophone"")]
BuiltInMicrophone,
[Field<EnumValue> (""AVCaptureDeviceTypeBuiltInWideAngleCamera"")]
BuiltInWideAngleCamera,
}
";
yield return [publicSmartEnum, "AVCaptureDeviceTypeExtensions", "public static partial class AVCaptureDeviceTypeExtensions"];

const string internalSmartEnum = @"
using System;
using Foundation;
using ObjCBindings;
namespace AVFoundation;
[BindingType]
internal enum AVCaptureDeviceType {
[Field<EnumValue> (""AVCaptureDeviceTypeBuiltInMicrophone"")]
BuiltInMicrophone,
[Field<EnumValue> (""AVCaptureDeviceTypeBuiltInWideAngleCamera"")]
BuiltInWideAngleCamera,
}
";

yield return [internalSmartEnum, "AVCaptureDeviceTypeExtensions", "internal static partial class AVCaptureDeviceTypeExtensions"];
}

IEnumerator IEnumerable.GetEnumerator () => GetEnumerator ();
}

[Theory]
[AllSupportedPlatformsClassData<TestDataToExtensionDeclaration>]
public void ToDeclarationTests (ApplePlatform platform, string inputText, string className, string expectedDeclaration)
{
var (compilation, syntaxTrees) = CreateCompilation (platform, sources: inputText);
Assert.Single (syntaxTrees);
var declaration = syntaxTrees [0].GetRoot ()
.DescendantNodes ()
.OfType<EnumDeclarationSyntax> ()
.FirstOrDefault ();
Assert.NotNull (declaration);
var semanticModel = compilation.GetSemanticModel (syntaxTrees [0]);
Assert.NotNull (semanticModel);
var changes = CodeChanges.FromDeclaration (declaration, semanticModel);
Assert.NotNull (changes);
var classDeclaration = changes.ToSmartEnumExtensionDeclaration (className);
Assert.NotNull (classDeclaration);
var str = classDeclaration.ToString ();
Assert.Equal (expectedDeclaration, classDeclaration.ToString ());
}
}

0 comments on commit 605b1b7

Please sign in to comment.