Skip to content

Commit

Permalink
[Rgen] Provide the needed code to parse notification attrs. (#22075)
Browse files Browse the repository at this point in the history
  • Loading branch information
mandel-macaque authored Jan 31, 2025
1 parent 3ec6219 commit 0984d4d
Show file tree
Hide file tree
Showing 4 changed files with 216 additions and 2 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using System.Diagnostics.CodeAnalysis;
using Microsoft.CodeAnalysis;

namespace Microsoft.Macios.Transformer.Attributes;

readonly record struct NotificationData {

/// <summary>
/// Diff the constructor used in the bindings.
/// </summary>
internal enum ConstructorType {
NotificationType,
NotificationCenter,
All
}

public string? Type { get; init; }
public string? NotificationCenter { get; init; }

public NotificationData (string? data, ConstructorType constructorType)
{
if (constructorType == ConstructorType.NotificationType) {
Type = data;
} else {
NotificationCenter = data;
}
}

public NotificationData (string? type, string? notificationCenter)
{
Type = type;
NotificationCenter = notificationCenter;
}

public static bool TryParse (AttributeData attributeData,
[NotNullWhen (true)] out NotificationData? data)
{
data = null;
var count = attributeData.ConstructorArguments.Length;
ConstructorType constructorType = ConstructorType.NotificationType;
string? notificationType = null;
string? notificationCenter = null;

switch (count) {
case 0:
break;
case 1:
// we have to diff constructors that take a single parameter, either a string or a type
if (attributeData.ConstructorArguments [0].Value! is string notificationCenterValue) {
constructorType = ConstructorType.NotificationCenter;
notificationCenter = notificationCenterValue;
} else {
constructorType = ConstructorType.NotificationType;
notificationType = ((INamedTypeSymbol) attributeData.ConstructorArguments [0].Value!).ToDisplayString ();
}
break;
case 2:
constructorType = ConstructorType.All;
notificationType = ((INamedTypeSymbol) attributeData.ConstructorArguments [0].Value!).ToDisplayString ();
notificationCenter = (string?) attributeData.ConstructorArguments [1].Value!;
break;
default:
// 0 should not be an option..
return false;
}

if (attributeData.NamedArguments.Length == 0) {
data = constructorType switch {
ConstructorType.NotificationCenter => new (notificationCenter, ConstructorType.NotificationCenter),
ConstructorType.NotificationType => new (notificationType, ConstructorType.NotificationType),
_ => new (notificationType, notificationCenter)
};
return true;
}

foreach (var (argumentName, value) in attributeData.NamedArguments) {
switch (argumentName) {
case "Type":
notificationType = ((INamedTypeSymbol) value.Value!).ToDisplayString ();
break;
case "NotificationCenter":
notificationCenter = (string) value.Value!;
break;
default:
data = null;
return false;
}
}

data = new (notificationType, notificationCenter);
return true;
}

}
7 changes: 7 additions & 0 deletions src/rgen/Microsoft.Macios.Transformer/AttributesNames.cs
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,13 @@ static class AttributesNames {
[BindingFlag (AttributeTargets.Method)]
public const string NoMethodAttribute = "NoMethodAttribute";

/// <summary>
/// When applied, flags the [Flags] as a notification and generates the
/// code to strongly type the notification.
/// </summary>
[BindingAttribute(typeof(NotificationData), AttributeTargets.Property)]
public const string NotificationAttribute = "NotificationAttribute";

public const string NoTVAttribute = "NoTVAttribute";
public const string NoiOSAttribute = "NoiOSAttribute";

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,7 @@
// Licensed under the MIT License.

using System.Collections;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.Macios.Generator.Extensions;
using Microsoft.Macios.Transformer.Attributes;
using Xamarin.Tests;
using Xamarin.Utils;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using System.Collections;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.Macios.Transformer.Attributes;
using Xamarin.Tests;
using Xamarin.Utils;

namespace Microsoft.Macios.Transformer.Tests.Attributes;

public class NotificationDataTests : AttributeParsingTestClass {

class TestDataTryCreate : IEnumerable<object []> {
public IEnumerator<object []> GetEnumerator ()
{
const string path = "/some/random/path.cs";

// simple notification
const string simpleNotification = @"
using System;
using Foundation;
using ObjCRuntime;
using UIKit;
namespace Test;
[NoMacCatalyst]
[BaseType (typeof (NSView))]
partial interface NSSplitView {
[Notification]
[Field (""NSSplitViewWillResizeSubviewsNotification"")]
NSString NSSplitViewWillResizeSubviewsNotification { get; }
}
";
yield return [(Source: simpleNotification, Path: path), new NotificationData (null, null)];

// notification type
const string notificationWithType = @"
using System;
using Foundation;
using ObjCRuntime;
using UIKit;
namespace Test;
class MyNotification {}
[NoMacCatalyst]
[BaseType (typeof (NSView))]
partial interface NSSplitView {
[Notification (typeof (MyNotification))]
[Field (""NSSplitViewWillResizeSubviewsNotification"")]
NSString NSSplitViewWillResizeSubviewsNotification { get; }
}
";

yield return [(Source: notificationWithType, Path: path), new NotificationData ("Test.MyNotification", null)];

// notification center
const string notificationWithCenter = @"
using System;
using Foundation;
using ObjCRuntime;
using UIKit;
namespace Test;
class MyNotification {}
[NoMacCatalyst]
[BaseType (typeof (NSView))]
partial interface NSSplitView {
[Notification (""SharedWorkspace.NotificationCenter"")]
[Field (""NSSplitViewWillResizeSubviewsNotification"")]
NSString NSSplitViewWillResizeSubviewsNotification { get; }
}
";
yield return [(Source: notificationWithCenter, Path: path), new NotificationData (null, "SharedWorkspace.NotificationCenter")];

// both
const string notificationWithBoth = @"
using System;
using Foundation;
using ObjCRuntime;
using UIKit;
namespace Test;
class MyNotification {}
[NoMacCatalyst]
[BaseType (typeof (NSView))]
partial interface NSSplitView {
[Notification (typeof (MyNotification), ""SharedWorkspace.NotificationCenter"")]
[Field (""NSSplitViewWillResizeSubviewsNotification"")]
NSString NSSplitViewWillResizeSubviewsNotification { get; }
}
";
yield return [(Source: notificationWithBoth, Path: path), new NotificationData ("Test.MyNotification", "SharedWorkspace.NotificationCenter")];
}

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

[Theory]
[AllSupportedPlatformsClassData<TestDataTryCreate>]
void TryCreateTests (ApplePlatform platform, (string Source, string Path) source,
NotificationData expectedData)
=> AssertTryCreate<NotificationData, PropertyDeclarationSyntax> (platform, source, AttributesNames.NotificationAttribute,
expectedData, NotificationData.TryParse, lastOrDefault: true);
}

0 comments on commit 0984d4d

Please sign in to comment.