Support for filescoped namespaces

Closes #7
Support for compilation units
Closes #8
This commit is contained in:
2024-04-11 10:52:51 +02:00
parent f9279b8910
commit 64f573a51f
7 changed files with 152 additions and 38 deletions
+29 -29
View File
@@ -7,44 +7,44 @@ namespace Sybil.IntegrationTests;
[TestClass]
public sealed class FlowTests
{
private const string ExpectedNamespace =
@"[InternalsVisibleTo(""FlowTests"")]
namespace TestNamespace
private const string ExpectedSyntax =
@"using System;
[InternalsVisibleTo(""FlowTests"")]
namespace TestNamespace;
[SomeAttribute(SomeIntProperty = 1, SomeStringProperty = ""Hello"", SomeEnumProperty = MyEnum.Value, SomeTypeProperty = typeof(String), SomeNullProperty = null)]
public sealed class TestClass : BaseTestClass
{
using System;
[SomeAttribute(SomeIntProperty = 1, SomeStringProperty = ""Hello"", SomeEnumProperty = MyEnum.Value, SomeTypeProperty = typeof(String), SomeNullProperty = null)]
public sealed class TestClass : BaseTestClass
[SomeAttribute5(null)]
TestClass(string fieldString) : base(fieldString)
{
[SomeAttribute5(null)]
TestClass(string fieldString) : base(fieldString)
{
this.fieldString = fieldString ?? throw new ArgumentNullException();
this.PropertyString = string.Empty;
}
this.fieldString = fieldString ?? throw new ArgumentNullException();
this.PropertyString = string.Empty;
}
private string someTypeString = $""{typeof(string)}"";
[SomeAttribute2]
private string fieldString;
[SomeAttribute3(typeof(String))]
public string PropertyString { private set; get; }
private string someTypeString = $""{typeof(string)}"";
[SomeAttribute2]
private string fieldString;
[SomeAttribute3(typeof(String))]
public string PropertyString { private set; get; }
[SomeAttribute4(0.5F)]
public string GetFieldString()
{
this.fieldString = 0;
return this.fieldString;
}
[SomeAttribute4(0.5F)]
public string GetFieldString()
{
this.fieldString = 0;
return this.fieldString;
}
}";
[TestMethod]
public void NewNamespace_GeneratesExpected()
{
var namespaceSyntax = SyntaxBuilder.CreateNamespace("TestNamespace")
var compilationUnitSyntax = SyntaxBuilder.CreateCompilationUnit()
.WithUsing("System")
.WithNamespace(
SyntaxBuilder.CreateFileScopedNamespace("TestNamespace")
.WithAttribute(SyntaxBuilder.CreateAttribute("InternalsVisibleTo")
.WithArgument("FlowTests"))
.WithUsing("System")
.WithClass(
SyntaxBuilder.CreateClass("TestClass")
.WithBaseClass("BaseTestClass")
@@ -88,10 +88,10 @@ namespace TestNamespace
.WithModifier("public")
.WithAttribute(SyntaxBuilder.CreateAttribute("SomeAttribute4")
.WithArgument(0.5f))
.WithBody("this.fieldString = 0;\r\nreturn this.fieldString;")))
.WithBody("this.fieldString = 0;\r\nreturn this.fieldString;"))))
.Build();
var namespaceString = namespaceSyntax.ToFullString();
namespaceString.Should().Be(ExpectedNamespace);
var compilationUnit = compilationUnitSyntax.ToFullString();
compilationUnit.Should().Be(ExpectedSyntax);
}
}
+44
View File
@@ -0,0 +1,44 @@
using FluentAssertions;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
namespace Sybil.Tests
{
[TestClass]
public class CompilationUnitTests
{
private const string UsingSystemWindows = "using System.Windows;";
private readonly CompilationUnitBuilder compilationUnitBuilder = new();
[TestMethod]
public void WithUsing_NullUsing_ThrowsArgumentNullException()
{
var action = () =>
{
this.compilationUnitBuilder.WithUsing(null);
};
action.Should().Throw<ArgumentNullException>();
}
[TestMethod]
public void WithNamespace_NullNamespaceBuilder_ThrowsArgumentNullException()
{
var action = () =>
{
this.compilationUnitBuilder.WithNamespace(null);
};
action.Should().Throw<ArgumentNullException>();
}
[TestMethod]
public void WithUsing_ReturnsExpected()
{
var result = this.compilationUnitBuilder.WithUsing("System.Windows").Build().ToFullString();
result.Should().Be(UsingSystemWindows);
}
}
}
+24 -2
View File
@@ -15,6 +15,9 @@ public class NamespaceBuilderTests
{
using System;
}";
private const string FileScopedNamespaceWithUsing =
@"namespace Test;
using System;";
private readonly NamespaceBuilder builder;
@@ -65,21 +68,40 @@ public class NamespaceBuilderTests
}
[TestMethod]
public void Build_ReturnsNamespaceDeclarationSyntax()
public void Build_ReturnsFileScopedNamespaceDeclarationSyntax()
{
var syntax = this.builder.Build();
syntax.Should().NotBeNull().And.Subject.Should().BeOfType<FileScopedNamespaceDeclarationSyntax>();
}
[TestMethod]
public void Build_ReturnsNamespaceDeclarationSyntax()
{
var syntax = new NamespaceBuilder(Namespace, false).Build();
syntax.Should().NotBeNull().And.Subject.Should().BeOfType<NamespaceDeclarationSyntax>();
}
[TestMethod]
public void WithUsing_ReturnsExpectedString()
public void FileScoped_WithUsing_ReturnsExpectedString()
{
var result = this.builder
.WithUsing(Using)
.Build()
.ToFullString();
result.Should().Be(FileScopedNamespaceWithUsing);
}
[TestMethod]
public void Enclosed_WithUsing_ReturnsExpectedString()
{
var result = new NamespaceBuilder(Namespace, false)
.WithUsing(Using)
.Build()
.ToFullString();
result.Should().Be(NamespaceWithUsing);
}
}
+44
View File
@@ -0,0 +1,44 @@
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using System;
using System.Collections.Generic;
using System.Linq;
namespace Sybil
{
public sealed class CompilationUnitBuilder : IBuilder<CompilationUnitSyntax>
{
private readonly List<NamespaceBuilder> namespaceBuilders = new List<NamespaceBuilder>();
private CompilationUnitSyntax CompilationUnitSyntax { get; set; }
internal CompilationUnitBuilder()
{
this.CompilationUnitSyntax = SyntaxFactory.CompilationUnit();
}
public CompilationUnitBuilder WithUsing(string usingName)
{
if (string.IsNullOrWhiteSpace(usingName))
{
throw new ArgumentNullException(nameof(usingName));
}
this.CompilationUnitSyntax = this.CompilationUnitSyntax.AddUsings(SyntaxFactory.UsingDirective(SyntaxFactory.IdentifierName(usingName)));
return this;
}
public CompilationUnitBuilder WithNamespace(NamespaceBuilder namespaceBuilder)
{
this.namespaceBuilders.Add(namespaceBuilder ?? throw new ArgumentNullException(nameof(namespaceBuilder)));
return this;
}
public CompilationUnitSyntax Build()
{
this.CompilationUnitSyntax = this.CompilationUnitSyntax.AddMembers(this.namespaceBuilders.Select(n => n.Build()).ToArray());
return this.CompilationUnitSyntax.NormalizeWhitespace();
}
}
}
+7 -5
View File
@@ -7,18 +7,20 @@ using System.Linq;
namespace Sybil
{
public sealed class NamespaceBuilder : IBuilder<NamespaceDeclarationSyntax>
public sealed class NamespaceBuilder : IBuilder<BaseNamespaceDeclarationSyntax>
{
private readonly List<AttributeBuilder> attributeBuilders = new List<AttributeBuilder>();
private readonly List<ClassBuilder> classBuilders = new List<ClassBuilder>();
private NamespaceDeclarationSyntax NamespaceDeclaration { get; set; }
private BaseNamespaceDeclarationSyntax NamespaceDeclaration { get; set; }
internal NamespaceBuilder(
string @namespace)
string @namespace, bool fileScoped = true)
{
_ = string.IsNullOrWhiteSpace(@namespace) ? throw new ArgumentNullException(nameof(@namespace)) : @namespace;
this.NamespaceDeclaration = SyntaxFactory.NamespaceDeclaration(SyntaxFactory.ParseName(@namespace));
this.NamespaceDeclaration = fileScoped ?
(BaseNamespaceDeclarationSyntax)SyntaxFactory.FileScopedNamespaceDeclaration(SyntaxFactory.ParseName(@namespace)) :
(BaseNamespaceDeclarationSyntax)SyntaxFactory.NamespaceDeclaration(SyntaxFactory.ParseName(@namespace));
}
public NamespaceBuilder WithUsing(string usingName)
@@ -48,7 +50,7 @@ namespace Sybil
return this;
}
public NamespaceDeclarationSyntax Build()
public BaseNamespaceDeclarationSyntax Build()
{
if (this.attributeBuilders.Count > 0)
{
+1 -1
View File
@@ -2,7 +2,7 @@
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<Version>0.5.0</Version>
<Version>0.6.0</Version>
</PropertyGroup>
<PropertyGroup>
+3 -1
View File
@@ -4,7 +4,8 @@ namespace Sybil
{
public static class SyntaxBuilder
{
public static NamespaceBuilder CreateNamespace(string @namespace) => new NamespaceBuilder(@namespace);
public static NamespaceBuilder CreateNamespace(string @namespace) => new NamespaceBuilder(@namespace, false);
public static NamespaceBuilder CreateFileScopedNamespace(string @namespace) => new NamespaceBuilder(@namespace, true);
public static ClassBuilder CreateClass(string className) => new ClassBuilder(className);
public static ConstructorBuilder CreateConstructor(string className) => new ConstructorBuilder(className);
public static BaseConstructorBuilder CreateBaseConstructor() => new BaseConstructorBuilder();
@@ -14,5 +15,6 @@ 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 CompilationUnitBuilder CreateCompilationUnit() => new CompilationUnitBuilder();
}
}