Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 15 additions & 2 deletions Funzo.SourceGenerators/Funzo.SourceGenerators.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,26 @@
<PackageLicenseFile>LICENSE</PackageLicenseFile>
<EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules>
<Version></Version>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
</PropertyGroup>

<ItemGroup>
<None Include="$(OutputPath)\$(AssemblyName).dll" Pack="true" PackagePath="analyzers/dotnet/cs" Visible="false" />
<ItemGroup>
<PackageReference Include="Sorse" Version="0.3.3-beta" PrivateAssets="all" GeneratePathProperty="true" />
</ItemGroup>

<PropertyGroup>
<GetTargetPathDependsOn>$(GetTargetPathDependsOn);GetDependencyTargetPaths</GetTargetPathDependsOn>
</PropertyGroup>

<Target Name="GetDependencyTargetPaths">
<ItemGroup>
<TargetPathWithTargetPlatformMoniker Include="$(PKGSorse)\lib\netstandard2.0\Sorse.dll" IncludeRuntimeDependency="false" />
<None Include="$(OutputPath)\$(AssemblyName).dll" Pack="true" PackagePath="analyzers/dotnet/cs" Visible="false" />
<None Include="$(PkgSorse)\lib\netstandard2.0\*.dll" Pack="true" PackagePath="analyzers/dotnet/cs" Visible="false" />
</ItemGroup>
<Message Text="$(PkgSorse)" Importance="high" />
</Target>

<ItemGroup>
<None Include="..\LICENSE">
<Pack>True</Pack>
Expand Down
35 changes: 5 additions & 30 deletions Funzo.SourceGenerators/Generators/ResultSourceGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,9 @@
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using System;
using Sorse;
using System.Collections.Immutable;
using System.Linq;
using System.Text;

namespace Funzo.SourceGenerators.Generators;

Expand All @@ -21,40 +20,16 @@ internal class ResultSourceGenerator : GeneratorBase

var is1ArityResult = type.AttributeTypeArguments.Length == 1;


ResultGenerator generator = is1ArityResult
? new Result1AritySourceGenerator(type)
: new Result2AritySourceGenerator(type);

var sb = new StringBuilder();
sb.AppendLine($@"// <auto-generated />

namespace {type.Symbol.ContainingNamespace.ToDisplayString()}
{{");
var sorse = WithSorse.CreateNamespaceScope(type.Symbol.ContainingNamespace.ToDisplayString(), []);

// Add all needed upper classes
foreach (var containingType in type.ContainerClasses)
{
var isStatic = type.Symbol.ContainingType!.IsStatic ? "static " : "";

sb.AppendLine($"{isStatic}partial class {containingType.Name} {{");
}
sorse.AddClassWithInnerClasses(type, generator.GenerateResult);

sb.AppendLine($@"
{generator.ClassDefinition}
{{
{generator.OkConstructor}
{generator.OkStaticHelper}
{generator.OkImplicitConverter}
{generator.ErrConstructor}
{generator.ErrStaticHelper}
{generator.ErrImplicitConverter}
}}
}}");

// Close all the braces opened for parent classes
sb.AppendLine(new string('}', type.ContainerClasses.Count));

var src = sb.ToString();
var src = sorse.GetSource();

return src;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,43 +1,38 @@
using Funzo.SourceGenerators.Helpers;
using Microsoft.CodeAnalysis;
using System.Text;
using Sorse.BuilderInterfaces;

namespace Funzo.SourceGenerators.Generators.Results;

internal class Result1AritySourceGenerator : ResultGenerator
{
internal Result1AritySourceGenerator(MarkedType symbolWithAttribute) : base(symbolWithAttribute)
{
}

internal override string ClassDefinition => $@"partial class {ClassName} : global::Funzo.ResultBase<{ClassName},{ErrDisplayName}>, global::Funzo.IResultBase <{ClassName},{ErrDisplayName}>";

internal override string OkConstructor => @$"protected {ClassName}() : base() {{}}";

internal override string ErrConstructor => $@"protected {ClassName}({ErrDisplayName} _) : base(_) {{}}";

internal override string OkStaticHelper => $@"public static {ClassName} Ok() => new();";

internal override string ErrStaticHelper => $@"public static {ClassName} Err({ErrDisplayName} err) => new(err);";

internal override string OkImplicitConverter => string.Empty;
protected override void GenerateResultInner(IClassBuilder builder)
{
builder.Inherits($"global::Funzo.ResultBase<{ClassName}, {ErrDisplayName}>")
.Implements($"global::Funzo.IResultBase<{ClassName}, {ErrDisplayName}>")
.WithConstructor(c => c.WithBaseCall([]))
.WithConstructor(c => c.WithArguments([new(new(ErrType), "x")]).WithBaseCall(["x"]))
.WithMethod(ClassName, "Ok", m => m.Static().WithBody(" => new();"))
.WithMethod(ClassName, "Err", m => m.Static().WithArguments([new(new(ErrType), "x")]).WithBody(" => new(x);"))
.WithImplicitConversionOperatorFrom(new(ErrType), " => new(x);");

AddConversionsForErrUnions(builder);
}

internal override string ErrImplicitConverter
private void AddConversionsForErrUnions(IClassBuilder builder)
{
get
foreach (var type in GetTypesNeedingImplicitConversions(ErrType, ResultParameterType.Err))
{
var implicitConversions = new StringBuilder();
implicitConversions.AppendLine($@"public static implicit operator {ClassName}({ErrDisplayName} _) => new {ClassName}(_);");

if (TryGetImplicitConvertersForUnionType(ErrType, ResultParameterType.Err, out var converters))
{
implicitConversions.AppendLine(converters);
}

return implicitConversions.ToString();
builder.WithImplicitConversionOperatorFrom(new(type), $" => new(x);");
}
}

private string ErrDisplayName => ErrType.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat);

private ITypeSymbol ErrType => TypeArguments[0];

}
Original file line number Diff line number Diff line change
@@ -1,55 +1,52 @@
using Funzo.SourceGenerators.Helpers;
using Microsoft.CodeAnalysis;
using Sorse;
using Sorse.BuilderInterfaces;
using System.Collections.Immutable;
using System.Linq;
using System.Text;

namespace Funzo.SourceGenerators.Generators.Results;

internal class Result2AritySourceGenerator : ResultGenerator
{
internal Result2AritySourceGenerator(MarkedType symbolWithAttribute) : base(symbolWithAttribute)
{
}

internal override string ClassDefinition => $@"partial class {ClassName} : global::Funzo.ResultBase<{ClassName}, {OkDisplayName},{ErrDisplayName}>, global::Funzo.IResultBase<{ClassName}, {OkDisplayName},{ErrDisplayName}>";

internal override string OkConstructor => @$"protected {ClassName}({OkDisplayName} _) : base(_) {{}}";

internal override string ErrConstructor => $@"protected {ClassName}({ErrDisplayName} _) : base(_) {{}}";

internal override string OkStaticHelper => $@"public static {ClassName} Ok({OkDisplayName} ok) => new(ok);";

internal override string ErrStaticHelper => $@"public static {ClassName} Err({ErrDisplayName} err) => new(err);";
protected override void GenerateResultInner(IClassBuilder builder)
{
builder.Inherits($"global::Funzo.ResultBase<{ClassName}, {OkDisplayName}, {ErrDisplayName}>")
.Implements($"global::Funzo.IResultBase<{ClassName}, {OkDisplayName}, {ErrDisplayName}>")
.WithConstructor(c => c.WithAccessModifier(AccessModifier.Protected)
.WithArguments([new(new(OkType), "_")]).WithBaseCall(["_"]))
.WithConstructor(c => c.WithAccessModifier(AccessModifier.Protected)
.WithArguments([new(new(ErrType), "_")]).WithBaseCall(["_"]))
.WithMethod(ClassName, "Ok", m => m.Static().WithArguments([new(new(OkType), "ok")]).WithBody(" => new(ok);"))
.WithMethod(ClassName, "Err", m => m.Static().WithArguments([new(new(ErrType), "err")]).WithBody(" => new(err);"))
.WithImplicitConversionOperatorFrom(new(OkType), " => new(x);")
.WithImplicitConversionOperatorFrom(new(ErrType), " => new(x);");

AddConversionsForUnions(builder);
}

internal override string OkImplicitConverter
private void AddConversionsForUnions(IClassBuilder builder)
{
get
if (HasCollidingParameters())
{
var implicitConversions = new StringBuilder();
implicitConversions.AppendLine($@"public static implicit operator {ClassName}({OkDisplayName} _) => new {ClassName}(_);");
return;
}

if (!HasCollidingParameters() && TryGetImplicitConvertersForUnionType(OkType, ResultParameterType.Ok, out var converters))
{
implicitConversions.AppendLine(converters);
}
var okUnions = GetTypesNeedingImplicitConversions(OkType, ResultParameterType.Ok);
var errUnions = GetTypesNeedingImplicitConversions(ErrType, ResultParameterType.Err);

return implicitConversions.ToString();
foreach (var ok in okUnions)
{
builder.WithImplicitConversionOperatorFrom(new(ok), " => new(x);");
}
}

internal override string ErrImplicitConverter
{
get
foreach (var err in errUnions)
{
var implicitConversions = new StringBuilder();
implicitConversions.AppendLine($@"public static implicit operator {ClassName}({ErrDisplayName} _) => new {ClassName}(_);");

if (!HasCollidingParameters() && TryGetImplicitConvertersForUnionType(ErrType, ResultParameterType.Err, out var converters))
{
implicitConversions.AppendLine(converters);
}

return implicitConversions.ToString();
builder.WithImplicitConversionOperatorFrom(new(err), " => new(x);");
}
}

Expand Down
33 changes: 11 additions & 22 deletions Funzo.SourceGenerators/Generators/Results/ResultGenerator.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
using Funzo.SourceGenerators.Helpers;
using Microsoft.CodeAnalysis;
using Sorse.BuilderInterfaces;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Text;

namespace Funzo.SourceGenerators.Generators.Results;

Expand All @@ -17,14 +17,14 @@ protected ResultGenerator(MarkedType symbolWithAttribute)
_symbolWithAttribute = symbolWithAttribute;
}

internal abstract string ClassDefinition { get; }
internal abstract string OkConstructor { get; }
internal abstract string ErrConstructor { get; }
internal abstract string OkStaticHelper { get; }
internal abstract string ErrStaticHelper { get; }
internal abstract string OkImplicitConverter { get; }
internal abstract string ErrImplicitConverter { get; }
internal void GenerateResult(IClassBuilder builder)
{
builder.Partial();

GenerateResultInner(builder);
}

protected abstract void GenerateResultInner(IClassBuilder builder);
protected string ClassName => $"{ClassSymbol.Name}";

protected bool TryGetUnionTypes(ITypeSymbol type, out IEnumerable<ITypeSymbol> types)
Expand All @@ -43,24 +43,13 @@ protected bool TryGetUnionTypes(ITypeSymbol type, out IEnumerable<ITypeSymbol> t
}
}

protected bool TryGetImplicitConvertersForUnionType(ITypeSymbol type, ResultParameterType parameterType, out string converters)
protected IEnumerable<ITypeSymbol> GetTypesNeedingImplicitConversions(ITypeSymbol type, ResultParameterType parameterType)
{
if (!TryGetUnionTypes(type, out var unionTypes))
{
converters = string.Empty;
return false;
}

var sb = new StringBuilder();

var ctor = parameterType is ResultParameterType.Ok ? "Ok" : "Err";

foreach (var unionType in unionTypes)
{
sb.AppendLine($@"public static implicit operator {ClassName}({unionType.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)} _) => {ctor}(_);");
return [];
}

converters = sb.ToString();
return true;
return unionTypes;
}
}
62 changes: 26 additions & 36 deletions Funzo.SourceGenerators/Generators/Unions/UnionGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Sorse;
using Sorse.BuilderInterfaces;
using System;
using System.Collections.Immutable;
using System.Linq;
using System.Text;

namespace Funzo.SourceGenerators.Generators.Unions;

Expand All @@ -24,47 +25,36 @@ internal class UnionGenerator : GeneratorBase
var className = $"{classSymbol.Name}";
var typeArguments = type.AttributeTypeArguments;

StringBuilder source = new($@"// <auto-generated />
var sorse = WithSorse.CreateNamespaceScope(classSymbol.ContainingNamespace.ToDisplayString(), []);

namespace {classSymbol.ContainingNamespace.ToDisplayString()}
{{");
sorse.AddClassWithInnerClasses(type, AddUnionClass(type));

// Add all needed upper classes
foreach (var containingType in type.ContainerClasses)
{
var isStatic = type.Symbol.ContainingType!.IsStatic ? "static " : "";

source.AppendLine($"{isStatic}partial class {containingType.Name} {{");
}

source.AppendLine($@" partial class {className} : global::{FunzoAttributeSources.AttributeNamespace}.Union{typeArguments.OpenGenericPart()}
{{");

foreach (var typeArgument in typeArguments)
{
source.Append($@"
public {classSymbol.Name}({typeArgument.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)} _) : base(_) {{}}
public static implicit operator {className}({typeArgument.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)} _) => new {className}(_);
");
}
var src = sorse.GetSource();

foreach (var prop in commonProperties)
{
source.Append($@"
public {prop.Type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)} {prop.Name} => Match({string.Join(",", Enumerable.Range(0, typeArguments.Length).Select(_ => $"x => x.{prop.Name}"))});
");
}

// Close all the braces opened for parent classes
source.AppendLine(new string('}', type.ContainerClasses.Count));

source.Append(@" }
}");

var src = source.ToString();
return src;
}

private Action<IClassBuilder> AddUnionClass(MarkedType type) =>
builder =>
{
var typeArguments = type.AttributeTypeArguments;
builder.Partial()
.Inherits($"global::{FunzoAttributeSources.AttributeNamespace}.Union{typeArguments.OpenGenericPart()}");

foreach (var typeArgument in typeArguments)
{
builder.WithConstructor(c => c.WithBaseCall(["_"]).WithArguments([new(new(typeArgument), "_")]));
builder.WithImplicitConversionOperatorFrom(new(typeArgument), " => new(x);");
}

var commonProperties = GetCommonProperties(type.AttributeTypeArguments);

foreach (var prop in commonProperties)
{
builder.WithProperty(new(prop.Type), prop.Name, p => p.WithComputedValue($" => Match({string.Join(",", Enumerable.Range(0, typeArguments.Length).Select(_ => $"x => x.{prop.Name}"))});"));
}
};

private bool HasErrors(SourceProductionContext context, MarkedType type)
{
var symbol = type.Symbol;
Expand Down
3 changes: 2 additions & 1 deletion Funzo.SourceGenerators/Helpers/ContainerClass.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
namespace Funzo.SourceGenerators.Helpers;

public sealed class ContainerClass(string name)
public sealed class ContainerClass(string name, bool isStatic)
{
public string Name { get; } = name;
public bool IsStatic { get; } = isStatic;
}
2 changes: 1 addition & 1 deletion Funzo.SourceGenerators/Helpers/MarkedType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ private static Stack<ContainerClass> GenerateContainerClassesHierarchy(INamedTyp

while (container is not null)
{
containers.Push(new(container.Name));
containers.Push(new(container.Name, container.IsStatic));
container = container.ContainingType;
}

Expand Down
Loading
Loading