Skip to content

Commit 1811707

Browse files
[Rgen] Add support to parse the strong dict attr in the transformer. (#22057)
1 parent e597395 commit 1811707

File tree

3 files changed

+164
-0
lines changed

3 files changed

+164
-0
lines changed
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT License.
3+
4+
using System.Diagnostics.CodeAnalysis;
5+
using Microsoft.CodeAnalysis;
6+
7+
namespace Microsoft.Macios.Transformer.Attributes;
8+
9+
readonly struct StrongDictionaryData : IEquatable<StrongDictionaryData> {
10+
11+
public string TypeWithKeys { get; }
12+
public string? Suffix { get; }
13+
14+
public StrongDictionaryData (string typeWithKeys)
15+
{
16+
TypeWithKeys = typeWithKeys;
17+
}
18+
19+
public StrongDictionaryData (string typeWithKeys, string? suffix)
20+
{
21+
TypeWithKeys = typeWithKeys;
22+
Suffix = suffix;
23+
}
24+
25+
public static bool TryParse (AttributeData attributeData,
26+
[NotNullWhen (true)] out StrongDictionaryData? data)
27+
{
28+
data = null;
29+
var count = attributeData.ConstructorArguments.Length;
30+
string typeWithKeys;
31+
string? suffix = null;
32+
33+
// custom marshal directive values
34+
35+
switch (count) {
36+
case 1:
37+
typeWithKeys = (string) attributeData.ConstructorArguments [0].Value!;
38+
break;
39+
default:
40+
// 0 should not be an option..
41+
return false;
42+
}
43+
44+
if (attributeData.NamedArguments.Length == 0) {
45+
data = new (typeWithKeys);
46+
return true;
47+
}
48+
49+
foreach (var (argumentName, value) in attributeData.NamedArguments) {
50+
switch (argumentName) {
51+
case "TypeWithKeys":
52+
typeWithKeys = (string?) value.Value!;
53+
break;
54+
case "Suffix":
55+
suffix = (string?) value.Value!;
56+
break;
57+
}
58+
}
59+
60+
data = new (typeWithKeys, suffix);
61+
return true;
62+
}
63+
64+
public bool Equals (StrongDictionaryData other)
65+
{
66+
if (TypeWithKeys != other.TypeWithKeys)
67+
return false;
68+
return Suffix == other.Suffix;
69+
}
70+
71+
/// <inheritdoc />
72+
public override bool Equals (object? obj)
73+
{
74+
return obj is StrongDictionaryData other && Equals (other);
75+
}
76+
77+
/// <inheritdoc />
78+
public override int GetHashCode ()
79+
=> HashCode.Combine (TypeWithKeys, Suffix);
80+
81+
public static bool operator == (StrongDictionaryData x, StrongDictionaryData y)
82+
{
83+
return x.Equals (y);
84+
}
85+
86+
public static bool operator != (StrongDictionaryData x, StrongDictionaryData y)
87+
{
88+
return !(x == y);
89+
}
90+
91+
public override string ToString ()
92+
=> $"{{ TypeWithKeys: '{TypeWithKeys}' Suffix: '{Suffix}' }}";
93+
}

src/rgen/Microsoft.Macios.Transformer/AttributesNames.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,12 @@ static class AttributesNames {
264264
/// </summary>
265265
[BindingFlag (AttributeTargets.Class)]
266266
public const string StaticAttribute = "StaticAttribute";
267+
268+
/// <summary>
269+
/// When this attribute is applied to an interface, it directs the generator to
270+
/// create a strongly typed DictionaryContainer for the specified fields.
271+
/// </summary>
272+
[BindingAttribute(typeof(StrongDictionaryData), AttributeTargets.Interface | AttributeTargets.Class | AttributeTargets.Property)]
267273
public const string StrongDictionaryAttribute = "StrongDictionaryAttribute";
268274

269275
/// <summary>
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT License.
3+
4+
using System.Collections;
5+
using Microsoft.CodeAnalysis.CSharp;
6+
using Microsoft.CodeAnalysis.CSharp.Syntax;
7+
using Microsoft.Macios.Generator.Extensions;
8+
using Microsoft.Macios.Transformer.Attributes;
9+
using Xamarin.Tests;
10+
using Xamarin.Utils;
11+
12+
namespace Microsoft.Macios.Transformer.Tests.Attributes;
13+
14+
public class StrongDictionaryDataTests : BaseTransformerTestClass {
15+
16+
class TestDataTryCreate : IEnumerable<object []> {
17+
public IEnumerator<object []> GetEnumerator ()
18+
{
19+
const string path = "/some/random/path.cs";
20+
21+
const string strongDictionary = @"
22+
using System;
23+
using Foundation;
24+
using ObjCRuntime;
25+
using UIKit;
26+
27+
namespace Test;
28+
29+
[StrongDictionary (""AVCapturePhotoSettingsThumbnailFormatKeys"")]
30+
interface AVCapturePhotoSettingsThumbnailFormat {
31+
NSString Codec { get; set; }
32+
NSNumber Width { get; set; }
33+
NSNumber Height { get; set; }
34+
}
35+
";
36+
37+
yield return [(Source: strongDictionary, Path: "/some/random/path.cs"), new StrongDictionaryData ("AVCapturePhotoSettingsThumbnailFormatKeys")];
38+
}
39+
40+
IEnumerator IEnumerable.GetEnumerator () => GetEnumerator ();
41+
}
42+
43+
[Theory]
44+
[AllSupportedPlatformsClassData<TestDataTryCreate>]
45+
void TryCreateTests (ApplePlatform platform, (string Source, string Path) source, StrongDictionaryData expectedData)
46+
{
47+
// create a compilation used to create the transformer
48+
var compilation = CreateCompilation (platform, sources: source);
49+
var syntaxTree = compilation.SyntaxTrees.ForSource (source);
50+
Assert.NotNull (syntaxTree);
51+
52+
var semanticModel = compilation.GetSemanticModel (syntaxTree);
53+
Assert.NotNull (semanticModel);
54+
55+
var declaration = syntaxTree.GetRoot ()
56+
.DescendantNodes ().OfType<BaseTypeDeclarationSyntax> ()
57+
.FirstOrDefault ();
58+
Assert.NotNull (declaration);
59+
60+
var symbol = semanticModel.GetDeclaredSymbol (declaration);
61+
Assert.NotNull (symbol);
62+
var attribute = symbol.GetAttribute<StrongDictionaryData> (AttributesNames.StrongDictionaryAttribute, StrongDictionaryData.TryParse);
63+
Assert.Equal (expectedData, attribute);
64+
}
65+
}

0 commit comments

Comments
 (0)