-
Notifications
You must be signed in to change notification settings - Fork 406
XP: Prototype source generator #1500
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| <Project> | ||
| <!-- Disable Directory.Build.props in repo root. --> | ||
| </Project> |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| <Project> | ||
| <PropertyGroup> | ||
| <!-- Disable Directory.Packages.props in repo root. --> | ||
| <ManagePackageVersionsCentrally>false</ManagePackageVersionsCentrally> | ||
| </PropertyGroup> | ||
| </Project> |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,34 @@ | ||
| | ||
| Microsoft Visual Studio Solution File, Format Version 12.00 | ||
| Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnitsNet.Xp.SrcGenDemo", "UnitsNet.Xp.SrcGenDemo\UnitsNet.Xp.SrcGenDemo.csproj", "{B30706F0-AA00-41BD-84AA-6E2A96DCAEB5}" | ||
| EndProject | ||
| Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnitsNet.Xp.SrcGen", "UnitsNet.Xp.SrcGen\UnitsNet.Xp.SrcGen.csproj", "{D635D0D1-D745-43A7-A04A-75995801753E}" | ||
| EndProject | ||
| Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "_Files", "_Files", "{8775A1CE-645B-45F2-82B2-1C94D56FA134}" | ||
| ProjectSection(SolutionItems) = preProject | ||
| Directory.Build.props = Directory.Build.props | ||
| Directory.Packages.props = Directory.Packages.props | ||
| EndProjectSection | ||
| EndProject | ||
| Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnitsNet.Xp.SrcGenDummySource", "UnitsNet.Xp.SrcGenDummySource\UnitsNet.Xp.SrcGenDummySource.csproj", "{8DBEADB1-CDEF-4841-9AAA-C4D36C6C541A}" | ||
| EndProject | ||
| Global | ||
| GlobalSection(SolutionConfigurationPlatforms) = preSolution | ||
| Debug|Any CPU = Debug|Any CPU | ||
| Release|Any CPU = Release|Any CPU | ||
| EndGlobalSection | ||
| GlobalSection(ProjectConfigurationPlatforms) = postSolution | ||
| {B30706F0-AA00-41BD-84AA-6E2A96DCAEB5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | ||
| {B30706F0-AA00-41BD-84AA-6E2A96DCAEB5}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||
| {B30706F0-AA00-41BD-84AA-6E2A96DCAEB5}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||
| {B30706F0-AA00-41BD-84AA-6E2A96DCAEB5}.Release|Any CPU.Build.0 = Release|Any CPU | ||
| {D635D0D1-D745-43A7-A04A-75995801753E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | ||
| {D635D0D1-D745-43A7-A04A-75995801753E}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||
| {D635D0D1-D745-43A7-A04A-75995801753E}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||
| {D635D0D1-D745-43A7-A04A-75995801753E}.Release|Any CPU.Build.0 = Release|Any CPU | ||
| {8DBEADB1-CDEF-4841-9AAA-C4D36C6C541A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | ||
| {8DBEADB1-CDEF-4841-9AAA-C4D36C6C541A}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||
| {8DBEADB1-CDEF-4841-9AAA-C4D36C6C541A}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||
| {8DBEADB1-CDEF-4841-9AAA-C4D36C6C541A}.Release|Any CPU.Build.0 = Release|Any CPU | ||
| EndGlobalSection | ||
| EndGlobal |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| // using System.ComponentModel; | ||
| // | ||
| // // ReSharper disable once CheckNamespace | ||
| // namespace System.Runtime.CompilerServices; | ||
| // | ||
| // /// <summary> | ||
| // /// Workaround for netstandard2.0 and record types and init properties: https://stackoverflow.com/a/62656145/134761 | ||
| // /// </summary> | ||
| // [EditorBrowsable(EditorBrowsableState.Never)] | ||
| // internal class IsExternalInit{} |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,134 @@ | ||
| // Licensed under MIT No Attribution, see LICENSE file at the root. | ||
| // Copyright 2013 Andreas Gullberg Larsen ([email protected]). Maintained at https://github.com/angularsen/UnitsNet. | ||
|
|
||
| using System.Collections.Generic; | ||
| using System.Linq; | ||
| using Microsoft.CodeAnalysis; | ||
| using Microsoft.CodeAnalysis.CSharp.Syntax; | ||
|
|
||
| namespace UnitsNet.Xp.SrcGen | ||
| { | ||
| [Generator] | ||
| public class QuantitySourceGenerator : ISourceGenerator | ||
| { | ||
| private const string SrcQuantitySource = | ||
| """ | ||
| namespace UnitsNetSrcGen | ||
| { | ||
| public interface ISrcQuantity<TUnitEnum> where TUnitEnum : System.Enum | ||
| { | ||
| double Value { get; } | ||
| TUnitEnum Unit { get; } | ||
| } | ||
| } | ||
| """; | ||
|
|
||
| private const string LengthSource = """ | ||
| namespace UnitsNetSrcGen | ||
| { | ||
| public enum LengthUnit | ||
| { | ||
| Centimeter, | ||
| Meter, | ||
| } | ||
|
|
||
| public struct Length : ISrcQuantity<LengthUnit> | ||
| { | ||
| public Length(double value, LengthUnit unit) | ||
| { | ||
| Value = value; | ||
| Unit = unit; | ||
| } | ||
|
|
||
| public required double Value { get; init; } | ||
| public required LengthUnit Unit { get; init; } | ||
| } | ||
| } | ||
| """; | ||
|
|
||
| private const string MassSource = """ | ||
| namespace UnitsNetSrcGen | ||
| { | ||
| public enum MassUnit | ||
| { | ||
| Gram, | ||
| Kilogram, | ||
| } | ||
|
|
||
| public struct Mass : ISrcQuantity<MassUnit> | ||
| { | ||
| public Mass(double value, MassUnit unit) | ||
| { | ||
| Value = value; | ||
| Unit = unit; | ||
| } | ||
|
|
||
| public required double Value { get; init; } | ||
| public required MassUnit Unit { get; init; } | ||
| } | ||
| } | ||
| """; | ||
|
|
||
| private const string AttributeSource = | ||
| """ | ||
| [System.AttributeUsage(System.AttributeTargets.Assembly, AllowMultiple = false)] | ||
| internal sealed class UnitsNetSrcGenInitAttribute : System.Attribute | ||
| { | ||
| public string[] QuantityNames { get; } | ||
|
|
||
| public UnitsNetSrcGenInitAttribute(string quantityNames) | ||
| => (QuantityNames) = (quantityNames.Split(',').Select(str => str.Trim()).ToArray()); | ||
| } | ||
| """; | ||
|
|
||
| public void Execute(GeneratorExecutionContext context) | ||
| { | ||
| SyntaxReceiver rx = (SyntaxReceiver)context.SyntaxContextReceiver!; | ||
| foreach (string quantityName in rx.QuantityNames) | ||
| { | ||
| string source = quantityName switch | ||
| { | ||
| "Length" => LengthSource, | ||
| "Mass" => MassSource, | ||
| _ => "", | ||
| }; | ||
|
|
||
| if (source != "") | ||
| { | ||
| context.AddSource($"UnitsNetSrc_{quantityName}.g.cs", source); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| public void Initialize(GeneratorInitializationContext context) | ||
| { | ||
| context.RegisterForPostInitialization((pi) => | ||
| { | ||
| pi.AddSource("UnitsNetSrcGenInit.g.cs", AttributeSource); | ||
| pi.AddSource("UnitsNetSrc_ISrcQuantity.g.cs", SrcQuantitySource); | ||
| }); | ||
| context.RegisterForSyntaxNotifications(() => new SyntaxReceiver()); | ||
| } | ||
|
|
||
| private class SyntaxReceiver : ISyntaxContextReceiver | ||
| { | ||
| public List<string> QuantityNames = new(); | ||
|
|
||
| public void OnVisitSyntaxNode(GeneratorSyntaxContext context) | ||
| { | ||
| // find all valid mustache attributes | ||
| if (context.Node is AttributeSyntax attrib | ||
| && context.SemanticModel.GetTypeInfo(attrib).Type?.ToDisplayString() == "UnitsNetSrcGenInitAttribute") | ||
| { | ||
| // string[] quantityNames = (string[])context.SemanticModel.GetConstantValue(attrib.ArgumentList.Arguments[0].Expression).Value; | ||
| IList<string> quantityNames = ((string)context.SemanticModel.GetConstantValue(attrib.ArgumentList!.Arguments[0].Expression).Value).Split(',').Select(str => str.Trim()).ToList(); | ||
|
|
||
| // string template = context.SemanticModel.GetConstantValue(attrib.ArgumentList.Arguments[1].Expression).ToString(); | ||
| // string hash = context.SemanticModel.GetConstantValue(attrib.ArgumentList.Arguments[2].Expression).ToString(); | ||
|
|
||
| QuantityNames.AddRange(quantityNames); | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,18 @@ | ||
| <Project Sdk="Microsoft.NET.Sdk"> | ||
|
|
||
| <PropertyGroup> | ||
| <TargetFramework>netstandard2.0</TargetFramework> | ||
| <LangVersion>latest</LangVersion> | ||
|
|
||
| <!-- RS1036: Specify analyzer banned API enforcement setting: A project containing analyzers or source generators should specify the property '<EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules>'. --> | ||
| <EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules> | ||
| <LangVersion>latest</LangVersion> | ||
| </PropertyGroup> | ||
|
|
||
| <ItemGroup> | ||
| <PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.10.0" PrivateAssets="all"/> | ||
| <PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4" PrivateAssets="all"/> | ||
| <PackageReference Include="Microsoft.CSharp" Version="4.7.0" /> | ||
| </ItemGroup> | ||
|
|
||
| </Project> |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| [assembly: UnitsNetSrcGenInit("Length,Mass")] |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| using UnitsNetSrcGen; | ||
|
|
||
| var l = new Length {Value = 10, Unit = LengthUnit.Centimeter}; | ||
| var m = new Mass { Value = 20, Unit = MassUnit.Kilogram }; | ||
|
|
||
| Console.WriteLine($"Length: {l.Value} {l.Unit}"); | ||
| Console.WriteLine($"Mass: {m.Value} {m.Unit}"); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,23 @@ | ||
| <Project Sdk="Microsoft.NET.Sdk"> | ||
|
|
||
| <PropertyGroup> | ||
| <OutputType>Exe</OutputType> | ||
| <TargetFramework>net8.0</TargetFramework> | ||
| <ImplicitUsings>enable</ImplicitUsings> | ||
| <Nullable>enable</Nullable> | ||
|
|
||
| <!-- Persist the source generator (and other) files to disk --> | ||
| <EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles> | ||
| <!-- 👇 The "base" path for the source generators --> | ||
| <GeneratedFolder>Generated</GeneratedFolder> | ||
| <!-- 👇 Write the output for each target framework to a different sub-folder --> | ||
| <CompilerGeneratedFilesOutputPath>$(GeneratedFolder)\$(TargetFramework)</CompilerGeneratedFilesOutputPath> | ||
| </PropertyGroup> | ||
|
|
||
| <ItemGroup> | ||
| <ProjectReference Include="../UnitsNet.Xp.SrcGen/UnitsNet.Xp.SrcGen.csproj" | ||
| OutputItemType="Analyzer" | ||
| ReferenceOutputAssembly="false"/> | ||
| </ItemGroup> | ||
|
|
||
| </Project> |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,52 @@ | ||
| | ||
| /* | ||
| * Dummy sources for copy & paste into c# source generator strings in UnitsNet.Xp.SrcGen/QuantitySourceGenerator.cs. | ||
| */ | ||
| // ReSharper disable once CheckNamespace | ||
| namespace UnitsNet.Xp.SrcGen; | ||
|
|
||
| /// <summary> | ||
| /// Represents a quantity. | ||
| /// </summary> | ||
| public interface ISrcQuantity<TUnitEnum> where TUnitEnum : System.Enum | ||
| { | ||
| double Value { get; } | ||
| TUnitEnum Unit { get; } | ||
| } | ||
|
|
||
| public enum LengthUnit | ||
| { | ||
| Centimeter, | ||
| Meter, | ||
| } | ||
|
|
||
| public struct Length : ISrcQuantity<LengthUnit> | ||
| { | ||
| public Length(double value, LengthUnit unit) | ||
| { | ||
| Value = value; | ||
| Unit = unit; | ||
| } | ||
|
|
||
| public required double Value { get; init; } | ||
| public required LengthUnit Unit { get; init; } | ||
| } | ||
|
|
||
| public enum MassUnit | ||
| { | ||
| Gram, | ||
| Kilogram, | ||
| } | ||
|
|
||
| public struct Mass : ISrcQuantity<MassUnit> | ||
| { | ||
| public Mass(double value, MassUnit unit) | ||
| { | ||
| Value = value; | ||
| Unit = unit; | ||
| } | ||
|
|
||
| public required double Value { get; init; } | ||
| public required MassUnit Unit { get; init; } | ||
| } | ||
|
|
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| <Project Sdk="Microsoft.NET.Sdk"> | ||
|
|
||
| <PropertyGroup> | ||
| <TargetFramework>net8.0</TargetFramework> | ||
| <ImplicitUsings>disable</ImplicitUsings> | ||
| <Nullable>enable</Nullable> | ||
| </PropertyGroup> | ||
|
|
||
| </Project> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ISourceGeneratorhas been deprecated in favor ofIIncrementalGenerator