Skip to content

Commit

Permalink
[Rgen] Use a roslyn code generator to help generate the flag parsing …
Browse files Browse the repository at this point in the history
…of all attributes.

The old Xamarin API used A LOT of attributes as flags to indicate
different generating methods. All this attributes do not have any data
and can be used as a feature flag.

For example, if we wanted to generated a static class we can do:
```csharp

[Static]
interface MyStaticClass {}
```
In this commit we are relaying in a custom roslyn code generator that
parses an attribute and generates the flag properties for our data
model. All the code is very repetitive and specially error prone. The
generator allows use just to focus on the name of the attribute and the
targets to which the attribute can be applied.

When you do the review you'll notice that the targets used in the
AttributeNames in the transformer do not match the ones in the
bgen/Attributes.cs file. The mismatch is because people back in the day
were careless and used the default flag, which is All. That is a
mistake, so I went throgh the trouble of using the correct target and
add a document string explaining the use of the flag and therefore the
reasoning of the updated target.

A sample of the generated code can be found
[here](https://gist.github.com/mandel-macaque/ed4277457a3afa6606af0f78ee9bf07e)
  • Loading branch information
mandel-macaque committed Jan 23, 2025
1 parent b50532b commit d4be53b
Show file tree
Hide file tree
Showing 11 changed files with 571 additions and 93 deletions.
1 change: 1 addition & 0 deletions src/rgen/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Generated
4 changes: 3 additions & 1 deletion src/rgen/Microsoft.Macios.Generator/DataModel/Parameter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using System;
using System.Collections.Immutable;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.InteropServices;
using System.Text;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;
Expand All @@ -13,7 +14,8 @@ namespace Microsoft.Macios.Generator.DataModel;
/// <summary>
/// Readonly structure that represents a change in a parameter.
/// </summary>
readonly struct Parameter : IEquatable<Parameter> {
[StructLayout (LayoutKind.Auto)]
readonly partial struct Parameter : IEquatable<Parameter> {
/// <summary>
/// Parameter position in the method.
/// </summary>
Expand Down
2 changes: 2 additions & 0 deletions src/rgen/Microsoft.Macios.Generator/DataModel/TypeInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using System;
using System.Collections.Immutable;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.InteropServices;
using System.Text;
using Microsoft.CodeAnalysis;
using Microsoft.Macios.Generator.Attributes;
Expand All @@ -13,6 +14,7 @@ namespace Microsoft.Macios.Generator.DataModel;
/// <summary>
/// Readonly structure that represents a change in a method return type.
/// </summary>
[StructLayout (LayoutKind.Auto)]
readonly partial struct TypeInfo : IEquatable<TypeInfo> {

public static TypeInfo Void = new ("void", SpecialType.System_Void) { Parents = ["System.ValueType", "object"], };
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using Microsoft.Macios.Generator.Availability;
using Xamarin.Utils;

namespace Microsoft.Macios.Generator;

partial class TabbedStringBuilder {

public TabbedStringBuilder AppendMemberAvailability (in SymbolAvailability allPlatformsAvailability)
{
foreach (var availability in allPlatformsAvailability.PlatformAvailabilities) {
var platformName = availability.Platform.AsString ().ToLower ();
if (availability.SupportedVersion is not null) {
var versionStr = (PlatformAvailability.IsDefaultVersion (availability.SupportedVersion))
? string.Empty
: availability.SupportedVersion.ToString ();
AppendLine ($"[SupportedOSPlatform (\"{platformName}{versionStr}\")]");
}

// loop over the unsupported versions of the platform
foreach (var (version, message) in availability.UnsupportedVersions) {
var versionStr = (PlatformAvailability.IsDefaultVersion (version)) ? string.Empty : version.ToString ();
if (message is null) {
AppendLine ($"[UnsupportedOSPlatform (\"{platformName}{versionStr}\")]");
} else {
AppendLine ($"[UnsupportedOSPlatform (\"{platformName}{versionStr}\", \"{message}\")]");
}
}

// loop over the obsolete versions of the platform
foreach (var (version, obsoleteInfo) in availability.ObsoletedVersions) {
var versionStr = (PlatformAvailability.IsDefaultVersion (version)) ? string.Empty : version.ToString ();

switch (obsoleteInfo) {
case (null, null):
AppendLine ($"[ObsoletedOSPlatform (\"{platformName}{versionStr}\")]");
break;
case (not null, null):
AppendLine ($"[ObsoletedOSPlatform (\"{platformName}{versionStr}\", \"{obsoleteInfo.Message}\")]");
break;
case (null, not null):
AppendLine ($"[ObsoletedOSPlatform (\"{platformName}{versionStr}\", Url=\"{obsoleteInfo.Url}\")]");
break;
case (not null, not null):
AppendLine (
$"[ObsoletedOSPlatform (\"{platformName}{versionStr}\", \"{obsoleteInfo.Message}\", Url=\"{obsoleteInfo.Url}\")]");
break;
}
}
}

return this;
}
}
53 changes: 3 additions & 50 deletions src/rgen/Microsoft.Macios.Generator/TabbedStringBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Text;
using Microsoft.Macios.Generator.Availability;
using Xamarin.Utils;

namespace Microsoft.Macios.Generator;

Expand Down Expand Up @@ -123,7 +121,7 @@ public TabbedStringBuilder AppendLine (ReadOnlySpan<char> span)

return this;
}

public TabbedStringBuilder Append (ref DefaultInterpolatedStringHandler handler)
{
WriteTabs ().Append (handler.ToStringAndClear ());
Expand All @@ -135,6 +133,7 @@ public TabbedStringBuilder AppendLine (ref DefaultInterpolatedStringHandler hand
WriteTabs ().Append (handler.ToStringAndClear ()).AppendLine ();
return this;
}


/// <summary>
/// Append a new raw literal by prepending the correct indentation.
Expand All @@ -157,7 +156,7 @@ public TabbedStringBuilder AppendRaw (string rawString)

return this;
}

/// <summary>
/// Append the generated code attribute to the current string builder. Added for convenience.
/// </summary>
Expand All @@ -176,52 +175,6 @@ public TabbedStringBuilder AppendGeneratedCodeAttribute (bool optimizable = true
return this;
}

public TabbedStringBuilder AppendMemberAvailability (in SymbolAvailability allPlatformsAvailability)
{
foreach (var availability in allPlatformsAvailability.PlatformAvailabilities) {
var platformName = availability.Platform.AsString ().ToLower ();
if (availability.SupportedVersion is not null) {
var versionStr = (PlatformAvailability.IsDefaultVersion (availability.SupportedVersion))
? string.Empty
: availability.SupportedVersion.ToString ();
AppendLine ($"[SupportedOSPlatform (\"{platformName}{versionStr}\")]");
}

// loop over the unsupported versions of the platform
foreach (var (version, message) in availability.UnsupportedVersions) {
var versionStr = (PlatformAvailability.IsDefaultVersion (version)) ? string.Empty : version.ToString ();
if (message is null) {
AppendLine ($"[UnsupportedOSPlatform (\"{platformName}{versionStr}\")]");
} else {
AppendLine ($"[UnsupportedOSPlatform (\"{platformName}{versionStr}\", \"{message}\")]");
}
}

// loop over the obsolete versions of the platform
foreach (var (version, obsoleteInfo) in availability.ObsoletedVersions) {
var versionStr = (PlatformAvailability.IsDefaultVersion (version)) ? string.Empty : version.ToString ();

switch (obsoleteInfo) {
case (null, null):
AppendLine ($"[ObsoletedOSPlatform (\"{platformName}{versionStr}\")]");
break;
case (not null, null):
AppendLine ($"[ObsoletedOSPlatform (\"{platformName}{versionStr}\", \"{obsoleteInfo.Message}\")]");
break;
case (null, not null):
AppendLine ($"[ObsoletedOSPlatform (\"{platformName}{versionStr}\", Url=\"{obsoleteInfo.Url}\")]");
break;
case (not null, not null):
AppendLine (
$"[ObsoletedOSPlatform (\"{platformName}{versionStr}\", \"{obsoleteInfo.Message}\", Url=\"{obsoleteInfo.Url}\")]");
break;
}
}
}

return this;
}

public TabbedStringBuilder AppendNotificationAdvice (in string className, in string notification)
{
string attr =
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net$(BundledNETCoreAppTargetFrameworkVersion)</TargetFramework>
<IsPackable>false</IsPackable>
<Nullable>enable</Nullable>
<LangVersion>latest</LangVersion>

<EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules>
<IsRoslynComponent>true</IsRoslynComponent>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>

<RootNamespace>Microsoft.Macios.Transformer.Generator</RootNamespace>
<PackageId>Microsoft.Macios.Transformer.Generator</PackageId>
<!-- There is a bug in the roslyn analyzer for roslyn analyzers.... -->
<NoWarn>RS2007;RS1041;APL0003</NoWarn>

</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.11">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.9.2"/>
</ItemGroup>

<ItemGroup>
<Compile Include="..\..\Microsoft.Macios.Generator\TabbedStringBuilder.cs">
<Link>TabbedStringBuilder.cs</Link>
</Compile>
</ItemGroup>


</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"$schema": "https://json.schemastore.org/launchsettings.json",
"profiles": {
"DebugRoslynSourceGenerator": {
"commandName": "DebugRoslynComponent",
"targetProject": "../Microsoft.Macios.Transformer.Generator.Sample/Microsoft.Macios.Transformer.Generator.Sample.csproj"
}
}
}
Loading

0 comments on commit d4be53b

Please sign in to comment.