Setup Type Parameters support

This commit is contained in:
2024-09-22 20:52:33 +02:00
parent bfdd2ce911
commit 7bac357a14
10 changed files with 285 additions and 4 deletions
+8 -2
View File
@@ -13,7 +13,7 @@ public sealed class FlowTests
[InternalsVisibleTo(""FlowTests"")]
namespace TestNamespace;
[SomeAttribute(SomeIntProperty = 1, SomeStringProperty = ""Hello"", SomeEnumProperty = MyEnum.Value, SomeTypeProperty = typeof(String), SomeNullProperty = null)]
public sealed class TestClass : BaseTestClass, IInterface
public sealed class TestClass<T> : BaseTestClass, IInterface where T : new(), class
{
[SomeAttribute5(null)]
TestClass(string fieldString) : base(fieldString)
@@ -29,7 +29,7 @@ public sealed class TestClass : BaseTestClass, IInterface
public string PropertyString { private set; get; }
[SomeAttribute4(0.5F)]
public string GetFieldString()
public string GetFieldString<T>()
{
this.fieldString = 0;
return this.fieldString;
@@ -49,6 +49,11 @@ public sealed class TestClass : BaseTestClass, IInterface
SyntaxBuilder.CreateClass("TestClass")
.WithBaseClass("BaseTestClass")
.WithInterface("IInterface")
.WithTypeParameter(SyntaxBuilder.CreateTypeParameter("T"))
.WithTypeParameterConstraint(
SyntaxBuilder.CreateTypeParameterConstraint("T")
.WithParameterlessConstructor()
.WithClass())
.WithAttribute(SyntaxBuilder.CreateAttribute("SomeAttribute")
.WithArgument("SomeIntProperty", 1)
.WithArgument("SomeStringProperty", "Hello")
@@ -87,6 +92,7 @@ public sealed class TestClass : BaseTestClass, IInterface
.WithMethod(
SyntaxBuilder.CreateMethod("string", "GetFieldString")
.WithModifier("public")
.WithTypeParameter(SyntaxBuilder.CreateTypeParameter("T"))
.WithAttribute(SyntaxBuilder.CreateAttribute("SomeAttribute4")
.WithArgument(0.5f))
.WithBody("this.fieldString = 0;\r\nreturn this.fieldString;"))))
@@ -0,0 +1,38 @@
using FluentAssertions;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
namespace Sybil.Tests;
[TestClass]
public class TypeParameterBuilderTests
{
private const string TypeName = "T";
private readonly TypeParameterBuilder builder;
public TypeParameterBuilderTests()
{
this.builder = new TypeParameterBuilder(TypeName);
}
[TestMethod]
public void Constructor_NullType_ThrowsArgumentNullException()
{
var action = () =>
{
_ = new TypeParameterBuilder(null);
};
action.Should().Throw<ArgumentNullException>();
}
[TestMethod]
public void Build_ReturnsPropertyDeclarationSyntax()
{
var syntax = this.builder.Build();
syntax.Should().NotBeNull().And.Subject.Should().BeOfType<TypeParameterSyntax>();
}
}
@@ -0,0 +1,91 @@
using FluentAssertions;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
namespace Sybil.Tests;
[TestClass]
public class TypeParameterConstraintBuilderTests
{
private const string TypeName = "T";
private const string ConstraintType = "TType";
private const string Expected = "where T : class, struct, new(), TType";
private readonly TypeParameterConstraintBuilder builder;
public TypeParameterConstraintBuilderTests()
{
this.builder = new TypeParameterConstraintBuilder(TypeName);
}
[TestMethod]
public void Constructor_NullType_ThrowsArgumentNullException()
{
var action = () =>
{
_ = new TypeParameterConstraintBuilder(null);
};
action.Should().Throw<ArgumentNullException>();
}
[TestMethod]
public void WithClass_ReturnsBuilder()
{
this.builder.WithClass().Should().BeOfType<TypeParameterConstraintBuilder>();
}
[TestMethod]
public void WithStruct_ReturnsBuilder()
{
this.builder.WithStruct().Should().BeOfType<TypeParameterConstraintBuilder>();
}
[TestMethod]
public void WithParameterlessConstructor_ReturnsBuilder()
{
this.builder.WithParameterlessConstructor().Should().BeOfType<TypeParameterConstraintBuilder>();
}
[TestMethod]
public void WithType_ReturnsBuilder()
{
this.builder.WithType(ConstraintType).Should().BeOfType<TypeParameterConstraintBuilder>();
}
[TestMethod]
public void WithType_NullType_ThrowsArgumentNullException()
{
var action = () =>
{
_ = this.builder.WithType(null);
};
action.Should().Throw<ArgumentNullException>();
}
[TestMethod]
public void Build_ReturnsPropertyDeclarationSyntax()
{
var syntax = this.builder.Build();
syntax.Should().NotBeNull().And.Subject.Should().BeOfType<TypeParameterConstraintClauseSyntax>();
}
[TestMethod]
public void Build_ReturnsExpectedString()
{
var syntax = this.builder
.WithClass()
.WithStruct()
.WithParameterlessConstructor()
.WithType(ConstraintType)
.Build();
var result = syntax.NormalizeWhitespace().ToFullString();
result.Should().Be(Expected);
}
}
+25
View File
@@ -16,6 +16,8 @@ namespace Sybil
private readonly List<FieldBuilder> Fields = new List<FieldBuilder>();
private readonly List<PropertyBuilder> Properties = new List<PropertyBuilder>();
private readonly List<MethodBuilder> Methods = new List<MethodBuilder>();
private readonly List<TypeParameterBuilder> TypeParameters = new List<TypeParameterBuilder>();
private readonly List<TypeParameterConstraintBuilder> TypeParameterConstraints = new List<TypeParameterConstraintBuilder>();
internal ClassBuilder(
string className)
@@ -98,6 +100,20 @@ namespace Sybil
return this;
}
public ClassBuilder WithTypeParameter(TypeParameterBuilder typeParameterBuilder)
{
this.TypeParameters.Add(typeParameterBuilder ?? throw new ArgumentNullException(nameof(typeParameterBuilder)));
return this;
}
public ClassBuilder WithTypeParameterConstraint(TypeParameterConstraintBuilder typeConstraintBuilder)
{
this.TypeParameterConstraints.Add(typeConstraintBuilder ?? throw new ArgumentNullException(nameof(typeConstraintBuilder)));
return this;
}
public ClassDeclarationSyntax Build()
{
if (this.Attributes.Count > 0)
@@ -105,6 +121,15 @@ namespace Sybil
this.ClassDeclaration = this.ClassDeclaration.AddAttributeLists(SyntaxFactory.AttributeList(SyntaxFactory.SeparatedList(this.Attributes.Select(p => p.Build()).ToArray())));
}
if (this.TypeParameters.Count > 0)
{
this.ClassDeclaration = this.ClassDeclaration.AddTypeParameterListParameters(this.TypeParameters.Select(t => t.Build()).ToArray());
if (this.TypeParameterConstraints.Count > 0)
{
this.ClassDeclaration = this.ClassDeclaration.AddConstraintClauses(this.TypeParameterConstraints.Select(t => t.Build()).ToArray());
}
}
return this.ClassDeclaration
.AddMembers(this.Constructors.Select(p => p.Build()).ToArray())
.AddMembers(this.Fields.Select(p => p.Build()).ToArray())
+25
View File
@@ -13,6 +13,8 @@ namespace Sybil
private readonly List<PropertyBuilder> Properties = new List<PropertyBuilder>();
private readonly List<MethodBuilder> Methods = new List<MethodBuilder>();
private readonly List<TypeParameterBuilder> TypeParameters = new List<TypeParameterBuilder>();
private readonly List<TypeParameterConstraintBuilder> TypeParameterConstraints = new List<TypeParameterConstraintBuilder>();
internal InterfaceBuilder(
string interfaceName)
@@ -54,8 +56,31 @@ namespace Sybil
return this;
}
public InterfaceBuilder WithTypeParameter(TypeParameterBuilder typeParameterBuilder)
{
this.TypeParameters.Add(typeParameterBuilder ?? throw new ArgumentNullException(nameof(typeParameterBuilder)));
return this;
}
public InterfaceBuilder WithTypeParameterConstraint(TypeParameterConstraintBuilder typeConstraintBuilder)
{
this.TypeParameterConstraints.Add(typeConstraintBuilder ?? throw new ArgumentNullException(nameof(typeConstraintBuilder)));
return this;
}
public InterfaceDeclarationSyntax Build()
{
if (this.TypeParameters.Count > 0)
{
this.InterfaceDeclaration = this.InterfaceDeclaration.AddTypeParameterListParameters(this.TypeParameters.Select(t => t.Build()).ToArray());
if (this.TypeParameterConstraints.Count > 0)
{
this.InterfaceDeclaration = this.InterfaceDeclaration.AddConstraintClauses(this.TypeParameterConstraints.Select(t => t.Build()).ToArray());
}
}
return this.InterfaceDeclaration
.AddMembers(this.Properties.Select(p => p.Build()).ToArray())
.AddMembers(this.Methods.Select(p => p.Build()).ToArray())
+26 -1
View File
@@ -14,6 +14,8 @@ namespace Sybil
private BlockSyntax BlockBody { get; set; }
private ArrowExpressionClauseSyntax ArrowExpression { get; set; }
private MethodDeclarationSyntax MethodDeclarationSyntax { get; set; }
private readonly List<TypeParameterBuilder> TypeParameters = new List<TypeParameterBuilder>();
private readonly List<TypeParameterConstraintBuilder> TypeParameterConstraints = new List<TypeParameterConstraintBuilder>();
internal MethodBuilder(
string returnType, string name)
@@ -115,6 +117,20 @@ namespace Sybil
return this;
}
public MethodBuilder WithTypeParameter(TypeParameterBuilder typeParameterBuilder)
{
this.TypeParameters.Add(typeParameterBuilder ?? throw new ArgumentNullException(nameof(typeParameterBuilder)));
return this;
}
public MethodBuilder WithTypeParameterConstraint(TypeParameterConstraintBuilder typeConstraintBuilder)
{
this.TypeParameterConstraints.Add(typeConstraintBuilder ?? throw new ArgumentNullException(nameof(typeConstraintBuilder)));
return this;
}
public MethodDeclarationSyntax Build()
{
if (this.attributeBuilders.Count > 0)
@@ -122,13 +138,22 @@ namespace Sybil
this.MethodDeclarationSyntax = this.MethodDeclarationSyntax.AddAttributeLists(SyntaxFactory.AttributeList(SyntaxFactory.SeparatedList(this.attributeBuilders.Select(p => p.Build()).ToArray())));
}
if (this.TypeParameters.Count > 0)
{
this.MethodDeclarationSyntax = this.MethodDeclarationSyntax.AddTypeParameterListParameters(this.TypeParameters.Select(t => t.Build()).ToArray());
if (this.TypeParameterConstraints.Count > 0)
{
this.MethodDeclarationSyntax = this.MethodDeclarationSyntax.AddConstraintClauses(this.TypeParameterConstraints.Select(t => t.Build()).ToArray());
}
}
if (this.BlockBody is null is false)
{
return this.MethodDeclarationSyntax
.WithBody(this.BlockBody)
.NormalizeWhitespace();
}
return this.MethodDeclarationSyntax
.WithExpressionBody(this.ArrowExpression)
.NormalizeWhitespace();
+1 -1
View File
@@ -2,7 +2,7 @@
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<Version>0.7.0</Version>
<Version>0.8.0</Version>
</PropertyGroup>
<PropertyGroup>
+2
View File
@@ -16,6 +16,8 @@ namespace Sybil
public static FieldBuilder CreateField(string fieldType, string fieldName) => new FieldBuilder(fieldType, fieldName);
public static MethodBuilder CreateMethod(string returnType, string methodName) => new MethodBuilder(returnType, methodName);
public static AttributeBuilder CreateAttribute(string attributeName) => new AttributeBuilder(attributeName);
public static TypeParameterBuilder CreateTypeParameter(string typeName) => new TypeParameterBuilder(typeName);
public static TypeParameterConstraintBuilder CreateTypeParameterConstraint(string typeName) => new TypeParameterConstraintBuilder(typeName);
public static CompilationUnitBuilder CreateCompilationUnit() => new CompilationUnitBuilder();
}
}
+23
View File
@@ -0,0 +1,23 @@
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using System;
namespace Sybil
{
public sealed class TypeParameterBuilder : IBuilder<TypeParameterSyntax>
{
private readonly string identifier;
internal TypeParameterBuilder(string identifier)
{
_ = identifier ?? throw new ArgumentNullException(nameof(identifier));
this.identifier = identifier;
}
public TypeParameterSyntax Build()
{
return SyntaxFactory.TypeParameter(this.identifier);
}
}
}
+46
View File
@@ -0,0 +1,46 @@
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using System;
namespace Sybil
{
public sealed class TypeParameterConstraintBuilder : IBuilder<TypeParameterConstraintClauseSyntax>
{
private TypeParameterConstraintClauseSyntax TypeParameterConstraintClauseSyntax { get; set; }
internal TypeParameterConstraintBuilder(string typeIdentifier)
{
_ = typeIdentifier ?? throw new ArgumentNullException(nameof(typeIdentifier));
this.TypeParameterConstraintClauseSyntax = SyntaxFactory.TypeParameterConstraintClause(typeIdentifier);
}
public TypeParameterConstraintBuilder WithClass()
{
this.TypeParameterConstraintClauseSyntax = this.TypeParameterConstraintClauseSyntax.AddConstraints(SyntaxFactory.ClassOrStructConstraint(SyntaxKind.ClassConstraint));
return this;
}
public TypeParameterConstraintBuilder WithStruct()
{
this.TypeParameterConstraintClauseSyntax = this.TypeParameterConstraintClauseSyntax.AddConstraints(SyntaxFactory.ClassOrStructConstraint(SyntaxKind.StructConstraint));
return this;
}
public TypeParameterConstraintBuilder WithParameterlessConstructor()
{
this.TypeParameterConstraintClauseSyntax = this.TypeParameterConstraintClauseSyntax.AddConstraints(SyntaxFactory.ConstructorConstraint());
return this;
}
public TypeParameterConstraintBuilder WithType(string typeIdentifier)
{
this.TypeParameterConstraintClauseSyntax = this.TypeParameterConstraintClauseSyntax.AddConstraints(SyntaxFactory.TypeConstraint(SyntaxFactory.ParseTypeName(typeIdentifier ?? throw new ArgumentNullException(nameof(typeIdentifier)))));
return this;
}
public TypeParameterConstraintClauseSyntax Build()
{
return this.TypeParameterConstraintClauseSyntax;
}
}
}