From 549556e230a300a971f319e3854d77c0ba21c2ff Mon Sep 17 00:00:00 2001 From: Velociraptor45 <30436688+Velociraptor45@users.noreply.github.com> Date: Sun, 27 Apr 2025 09:13:36 +0200 Subject: [PATCH 1/6] #31: Implement fields in class --- RefleCS/RefleCS.TestKit/Nodes/ClassBuilder.cs | 21 +++- RefleCS/RefleCS.TestKit/Nodes/FieldBuilder.cs | 40 +++++++ .../Converters/CsFileConverterTestData.cs | 20 ++-- .../Converters/RecordConverterTestData.cs | 24 ++-- RefleCS/RefleCS.Tests/CsFileCreatorTests.cs | 107 +++++++++++++++++- RefleCS/RefleCS.Tests/Nodes/ClassTests.cs | 88 ++++++++++++++ RefleCS/RefleCS.Tests/Nodes/CsFileTests.cs | 50 ++++---- RefleCS/RefleCS.Tests/Nodes/FieldTests.cs | 49 ++++++++ RefleCS/RefleCS.Tests/Nodes/NamespaceTests.cs | 4 +- RefleCS/RefleCS/Converters/ClassConverter.cs | 10 +- RefleCS/RefleCS/Converters/FieldConverter.cs | 63 +++++++++++ .../RefleCS/Converters/ModifierConverter.cs | 18 +++ .../RefleCS/Converters/PropertyConverter.cs | 2 +- RefleCS/RefleCS/Enums/FieldModifier.cs | 34 ++++++ RefleCS/RefleCS/Nodes/Class.cs | 44 ++++++- RefleCS/RefleCS/Nodes/Field.cs | 45 ++++++++ RefleCS/RefleCS/Nodes/FieldInitializer.cs | 7 ++ 17 files changed, 559 insertions(+), 67 deletions(-) create mode 100644 RefleCS/RefleCS.TestKit/Nodes/FieldBuilder.cs create mode 100644 RefleCS/RefleCS.Tests/Nodes/FieldTests.cs create mode 100644 RefleCS/RefleCS/Converters/FieldConverter.cs create mode 100644 RefleCS/RefleCS/Enums/FieldModifier.cs create mode 100644 RefleCS/RefleCS/Nodes/Field.cs create mode 100644 RefleCS/RefleCS/Nodes/FieldInitializer.cs diff --git a/RefleCS/RefleCS.TestKit/Nodes/ClassBuilder.cs b/RefleCS/RefleCS.TestKit/Nodes/ClassBuilder.cs index 997ef47..fc1f3a6 100644 --- a/RefleCS/RefleCS.TestKit/Nodes/ClassBuilder.cs +++ b/RefleCS/RefleCS.TestKit/Nodes/ClassBuilder.cs @@ -27,7 +27,7 @@ public ClassBuilder WithModifiers(IEnumerable modifiers) public ClassBuilder WithEmptyModifiers() { - return WithModifiers(Enumerable.Empty()); + return WithModifiers([]); } public ClassBuilder WithConstructors(IEnumerable constructors) @@ -38,7 +38,18 @@ public ClassBuilder WithConstructors(IEnumerable constructors) public ClassBuilder WithEmptyConstructors() { - return WithConstructors(Enumerable.Empty()); + return WithConstructors([]); + } + + public ClassBuilder WithFields(IEnumerable fields) + { + FillConstructorWith(nameof(fields), fields); + return this; + } + + public ClassBuilder WithEmptyFields() + { + return WithFields([]); } public ClassBuilder WithProperties(IEnumerable properties) @@ -49,7 +60,7 @@ public ClassBuilder WithProperties(IEnumerable properties) public ClassBuilder WithEmptyProperties() { - return WithProperties(Enumerable.Empty()); + return WithProperties([]); } public ClassBuilder WithMethods(IEnumerable methods) @@ -60,7 +71,7 @@ public ClassBuilder WithMethods(IEnumerable methods) public ClassBuilder WithEmptyMethods() { - return WithMethods(Enumerable.Empty()); + return WithMethods([]); } public ClassBuilder WithBaseTypes(IEnumerable baseTypes) @@ -71,6 +82,6 @@ public ClassBuilder WithBaseTypes(IEnumerable baseTypes) public ClassBuilder WithEmptyBaseTypes() { - return WithBaseTypes(Enumerable.Empty()); + return WithBaseTypes([]); } } \ No newline at end of file diff --git a/RefleCS/RefleCS.TestKit/Nodes/FieldBuilder.cs b/RefleCS/RefleCS.TestKit/Nodes/FieldBuilder.cs new file mode 100644 index 0000000..977ca98 --- /dev/null +++ b/RefleCS/RefleCS.TestKit/Nodes/FieldBuilder.cs @@ -0,0 +1,40 @@ +using RefleCS.Enums; +using RefleCS.Nodes; + +namespace RefleCS.TestKit.Nodes; +public class FieldBuilder : TestBuilderBase +{ + public FieldBuilder WithModifiers(IEnumerable modifiers) + { + FillConstructorWith(nameof(modifiers), modifiers); + return this; + } + + public FieldBuilder WithEmptyModifiers() + { + return WithModifiers(Enumerable.Empty()); + } + + public FieldBuilder WithTypeName(string typeName) + { + FillConstructorWith(nameof(typeName), typeName); + return this; + } + + public FieldBuilder WithName(string name) + { + FillConstructorWith(nameof(name), name); + return this; + } + + public FieldBuilder WithInitializer(FieldInitializer? initializer) + { + FillConstructorWith(nameof(initializer), initializer); + return this; + } + + public FieldBuilder WithoutInitializer() + { + return WithInitializer(null); + } +} \ No newline at end of file diff --git a/RefleCS/RefleCS.Tests/Converters/CsFileConverterTestData.cs b/RefleCS/RefleCS.Tests/Converters/CsFileConverterTestData.cs index f274c00..3a103d1 100644 --- a/RefleCS/RefleCS.Tests/Converters/CsFileConverterTestData.cs +++ b/RefleCS/RefleCS.Tests/Converters/CsFileConverterTestData.cs @@ -14,13 +14,13 @@ public IEnumerator GetEnumerator() private object[] WithRecord() { - return new object[] - { + return + [ new CsFile( new List { new("System") }, new Namespace( "MyApp", - Enumerable.Empty(), + [], new List { new( @@ -49,13 +49,13 @@ private object[] WithRecord() new List(), new List()) })) - }; + ]; } private object[] WithClass() { - return new object[] - { + return + [ new CsFile( new List { @@ -80,6 +80,10 @@ private object[] WithClass() new List { new("ids") }), new List { new("Console.Log(\"Hello, World!\");") }) }, + new List + { + new([FieldModifier.Public], "string", "_myString", new("\"Test\"")) + }, new List { new( @@ -110,8 +114,8 @@ private object[] WithClass() }, new List { new("MyBaseClass") }) }, - Enumerable.Empty())) - }; + [])) + ]; } IEnumerator IEnumerable.GetEnumerator() diff --git a/RefleCS/RefleCS.Tests/Converters/RecordConverterTestData.cs b/RefleCS/RefleCS.Tests/Converters/RecordConverterTestData.cs index 8f3323c..bf53186 100644 --- a/RefleCS/RefleCS.Tests/Converters/RecordConverterTestData.cs +++ b/RefleCS/RefleCS.Tests/Converters/RecordConverterTestData.cs @@ -16,8 +16,8 @@ public IEnumerator GetEnumerator() private static object[] OnlyParameters() { - return new object[] - { + return + [ new Record( new List { ClassModifier.Public }, "App", @@ -26,13 +26,13 @@ private static object[] OnlyParameters() new List(), new List(), new List()) - }; + ]; } private static object[] WithConstructor() { - return new object[] - { + return + [ new Record( new List { ClassModifier.Public }, "App", @@ -58,13 +58,13 @@ private static object[] WithConstructor() }, new List(), new List()) - }; + ]; } private static object[] WithMethods() { - return new object[] - { + return + [ new Record( new List { ClassModifier.Public }, "App", @@ -98,13 +98,13 @@ private static object[] WithMethods() new List { new("nextVal = Ids.First()") }) }, new List()) - }; + ]; } private static object[] WithBaseType() { - return new object[] - { + return + [ new Record( new List { ClassModifier.Public }, "App", @@ -116,7 +116,7 @@ private static object[] WithBaseType() { new("MyBaseApp") }) - }; + ]; } IEnumerator IEnumerable.GetEnumerator() diff --git a/RefleCS/RefleCS.Tests/CsFileCreatorTests.cs b/RefleCS/RefleCS.Tests/CsFileCreatorTests.cs index 58045d2..995eaab 100644 --- a/RefleCS/RefleCS.Tests/CsFileCreatorTests.cs +++ b/RefleCS/RefleCS.Tests/CsFileCreatorTests.cs @@ -68,11 +68,12 @@ public App(int? id) new("Id = id.Value;") }) }, + new List(), new List(), new List(), new List()) }, - Enumerable.Empty())); + [])); // Act var result = new CsFileHandler().FromCode(content); @@ -109,6 +110,7 @@ public class App new List { ClassModifier.Public }, "App", new List(), + new List(), new List { new( @@ -124,7 +126,97 @@ public class App new List(), new List()) }, - Enumerable.Empty())); + [])); + + // Act + var result = new CsFileHandler().FromCode(content); + + // Assert + result.Should().BeEquivalentTo(expectedResult); + } + + [Fact] + public void FromCode_WithFieldInitialized_ShouldReturnExpectedResult() + { + // Arrange + var content = @"using System; +using System.Linq; + +namespace MyApp; + +public class App +{ + private int _myInt = 1; +}"; + + var expectedResult = new CsFile( + new List + { + new("System"), + new("System.Linq") + }, + new Namespace( + "MyApp", + new List + { + new( + new List { ClassModifier.Public }, + "App", + new List(), + new List + { + new([FieldModifier.Private], "int", "_myInt", new("1")) + }, + new List(), + new List(), + new List()) + }, + [])); + + // Act + var result = new CsFileHandler().FromCode(content); + + // Assert + result.Should().BeEquivalentTo(expectedResult); + } + + [Fact] + public void FromCode_WithFieldUninitialized_ShouldReturnExpectedResult() + { + // Arrange + var content = @"using System; +using System.Linq; + +namespace MyApp; + +public class App +{ + private int _myInt; +}"; + + var expectedResult = new CsFile( + new List + { + new("System"), + new("System.Linq") + }, + new Namespace( + "MyApp", + new List + { + new( + new List { ClassModifier.Public }, + "App", + new List(), + new List + { + new([FieldModifier.Private], "int", "_myInt", null) + }, + new List(), + new List(), + new List()) + }, + [])); // Act var result = new CsFileHandler().FromCode(content); @@ -182,6 +274,7 @@ public sealed class App }, "App", new List(), + new List(), new List(), new List { @@ -207,7 +300,7 @@ public sealed class App }, new List()) }, - Enumerable.Empty())); + [])); // Act var result = new CsFileHandler().FromCode(content); @@ -256,6 +349,7 @@ void CheckIfTrue({modifiers} bool bl) }, "App", new List(), + new List(), new List(), new List { @@ -278,7 +372,7 @@ void CheckIfTrue({modifiers} bool bl) }, new List()) }, - Enumerable.Empty())); + [])); // Act var result = new CsFileHandler().FromCode(content); @@ -316,6 +410,7 @@ public sealed class App : AnotherApp, IImplement }, "App", new List(), + new List(), new List(), new List(), new List @@ -324,7 +419,7 @@ public sealed class App : AnotherApp, IImplement new("IImplement") }) }, - Enumerable.Empty())); + [])); // Act var result = new CsFileHandler().FromCode(content); @@ -357,7 +452,7 @@ public App(int id, string name) : this(id) }, new Namespace( "MyApp", - Enumerable.Empty(), + [], new List { new( diff --git a/RefleCS/RefleCS.Tests/Nodes/ClassTests.cs b/RefleCS/RefleCS.Tests/Nodes/ClassTests.cs index 2086f0e..1a13c8c 100644 --- a/RefleCS/RefleCS.Tests/Nodes/ClassTests.cs +++ b/RefleCS/RefleCS.Tests/Nodes/ClassTests.cs @@ -10,6 +10,94 @@ namespace RefleCS.Tests.Nodes; public class ClassTests { + public class AddField + { + private readonly AddFieldFixture _fixture = new(); + + [Fact] + public void AddField_ShouldAddField() + { + // Arrange + var sut = _fixture.CreateSut(); + _fixture.SetupField(); + var parameterCount = sut.Fields.Count; + + TestPropertyNotSetException.ThrowIfNull(_fixture.Field); + + // Act + var result = sut.AddField(_fixture.Field); + + // Assert + result.Should().Be(sut); + sut.Fields.Should().Contain(_fixture.Field); + sut.Fields.Should().HaveCount(parameterCount + 1); + } + + private class AddFieldFixture + { + private readonly ClassBuilder _builder = new(); + + public Field? Field { get; private set; } + + public void SetupField() + { + Field = new FieldBuilder().Create(); + } + + public Class CreateSut() + { + return _builder.Create(); + } + } + } + + public class RemoveField + { + private readonly RemoveFieldFixture _fixture = new(); + + [Fact] + public void RemoveField_ShouldRemoveField() + { + // Arrange + _fixture.SetupInitialFields(); + var sut = _fixture.CreateSut(); + _fixture.SetupFieldToRemove(sut); + var parameterCount = sut.Fields.Count; + + TestPropertyNotSetException.ThrowIfNull(_fixture.Field); + + // Act + var result = sut.RemoveField(_fixture.Field); + + // Assert + result.Should().Be(sut); + sut.Fields.Should().HaveCount(parameterCount - 1); + sut.Fields.Should().NotContain(_fixture.Field); + } + + private class RemoveFieldFixture + { + private readonly ClassBuilder _builder = new(); + + public Field? Field { get; private set; } + + public void SetupInitialFields() + { + _builder.WithFields(new FieldBuilder().CreateMany(3)); + } + + public void SetupFieldToRemove(Class cls) + { + Field = cls.Fields.ElementAt(1); + } + + public Class CreateSut() + { + return _builder.Create(); + } + } + } + public class AddProperty { private readonly AddPropertyFixture _fixture = new(); diff --git a/RefleCS/RefleCS.Tests/Nodes/CsFileTests.cs b/RefleCS/RefleCS.Tests/Nodes/CsFileTests.cs index a5ec5d8..0fc0ca6 100644 --- a/RefleCS/RefleCS.Tests/Nodes/CsFileTests.cs +++ b/RefleCS/RefleCS.Tests/Nodes/CsFileTests.cs @@ -59,11 +59,11 @@ private sealed class AddUsingFixture : CsFileFixture public void SetupExistingUsing() { - Usings = new List - { + Usings = + [ new("a"), - new("b"), - }; + new("b") + ]; ExistingUsing = new("a"); @@ -76,11 +76,11 @@ public void SetupExistingUsing() public void SetupNonExistingUsing() { - Usings = new List - { + Usings = + [ new("a"), - new("b"), - }; + new("b") + ]; NonExistingUsing = new("c"); @@ -147,11 +147,11 @@ private sealed class RemoveUsingFixture : CsFileFixture public void SetupExistingUsing() { - Usings = new List - { + Usings = + [ new("a"), - new("b"), - }; + new("b") + ]; ExistingUsing = new("a"); @@ -163,11 +163,11 @@ public void SetupExistingUsing() public void SetupNonExistingUsing() { - Usings = new List - { + Usings = + [ new("a"), - new("b"), - }; + new("b") + ]; NonExistingUsing = new("c"); @@ -228,13 +228,13 @@ private sealed class OrderUsingsAscFixture : CsFileFixture public void SetupUnorderedUsings() { - Usings = new List - { + Usings = + [ new("aa"), new("a"), new("1a"), - new("b"), - }; + new("b") + ]; ExpectedResult = new List { @@ -295,13 +295,13 @@ private sealed class OrderUsingsDescFixture : CsFileFixture public void SetupUnorderedUsings() { - Usings = new List - { + Usings = + [ new("aa"), new("a"), new("1a"), - new("b"), - }; + new("b") + ]; ExpectedResult = new List { @@ -329,7 +329,7 @@ public CsFile CreateSut() public void SetupNamespaceEmpty() { - _namespace = new Namespace("MyNamespace", new List(), Enumerable.Empty()); + _namespace = new Namespace("MyNamespace", new List(), []); } } } \ No newline at end of file diff --git a/RefleCS/RefleCS.Tests/Nodes/FieldTests.cs b/RefleCS/RefleCS.Tests/Nodes/FieldTests.cs new file mode 100644 index 0000000..8bb6797 --- /dev/null +++ b/RefleCS/RefleCS.Tests/Nodes/FieldTests.cs @@ -0,0 +1,49 @@ +using FluentAssertions; +using RefleCS.Enums; +using RefleCS.Nodes; + +namespace RefleCS.Tests.Nodes; + +public class FieldTests +{ + [Theory] + [InlineData(FieldModifier.Public)] + [InlineData(FieldModifier.Private)] + [InlineData(FieldModifier.Protected)] + [InlineData(FieldModifier.Internal)] + public void Ctor_WithInitializer_ShouldReturnExpectedResult(FieldModifier modifier) + { + // Arrange + var expectedFieldInitializer = new FieldInitializer("1"); + + // Act + var result = new Field( + [modifier], + "int", + "fieldName", + expectedFieldInitializer); + + // Assert + result.Modifiers.Should().BeEquivalentTo([modifier]); + result.TypeName.Should().Be("int"); + result.Name.Should().Be("fieldName"); + result.Initializer.Should().BeEquivalentTo(expectedFieldInitializer); + } + + [Fact] + public void Ctor_WithoutInitializer_ShouldReturnExpectedResult() + { + // Act + var result = new Field( + [FieldModifier.Public], + "int", + "fieldName", + null); + + // Assert + result.Modifiers.Should().BeEquivalentTo([FieldModifier.Public]); + result.TypeName.Should().Be("int"); + result.Name.Should().Be("fieldName"); + result.Initializer.Should().BeNull(); + } +} \ No newline at end of file diff --git a/RefleCS/RefleCS.Tests/Nodes/NamespaceTests.cs b/RefleCS/RefleCS.Tests/Nodes/NamespaceTests.cs index 03d804c..d1a5340 100644 --- a/RefleCS/RefleCS.Tests/Nodes/NamespaceTests.cs +++ b/RefleCS/RefleCS.Tests/Nodes/NamespaceTests.cs @@ -74,12 +74,12 @@ public void SetupNamespaceContainingClass() private abstract class NamespaceFixture { - protected readonly List Classes = new(); + protected readonly List Classes = []; protected string Name = string.Empty; public Namespace CreateSut() { - return new Namespace(Name, Classes, Enumerable.Empty()); + return new Namespace(Name, Classes, []); } } } \ No newline at end of file diff --git a/RefleCS/RefleCS/Converters/ClassConverter.cs b/RefleCS/RefleCS/Converters/ClassConverter.cs index 609d0ae..44942aa 100644 --- a/RefleCS/RefleCS/Converters/ClassConverter.cs +++ b/RefleCS/RefleCS/Converters/ClassConverter.cs @@ -6,6 +6,7 @@ namespace RefleCS.Converters; internal class ClassConverter { + private readonly FieldConverter _fieldConverter = new(); private readonly PropertyConverter _propertyConverter = new(); private readonly ConstructorConverter _constructorConverter = new(); private readonly ModifierConverter _modifierConverter = new(); @@ -14,6 +15,9 @@ internal class ClassConverter public Class ToClass(ClassDeclarationSyntax classDeclaration) { + var fieldDeclarations = classDeclaration.DescendantNodes().OfType(); + var fields = _fieldConverter.ToField(fieldDeclarations); + var propertyDeclarations = classDeclaration.DescendantNodes().OfType(); var properties = _propertyConverter.ToProperty(propertyDeclarations); @@ -26,10 +30,10 @@ public Class ToClass(ClassDeclarationSyntax classDeclaration) var methods = _methodConverter.ToMethod(methodDeclarations); var baseTypes = classDeclaration.BaseList is null - ? Enumerable.Empty() + ? [] : _baseTypeConverter.ToBaseType(classDeclaration.BaseList); - return new Class(modifiers, classDeclaration.Identifier.ToString(), ctors, properties, methods, baseTypes); + return new Class(modifiers, classDeclaration.Identifier.ToString(), ctors, fields, properties, methods, baseTypes); } public IEnumerable ToClass(IEnumerable classDeclarations) @@ -43,6 +47,7 @@ public IEnumerable ToClass(IEnumerable classDecla public ClassDeclarationSyntax ToNode(Class cls) { var ctors = _constructorConverter.ToNode(cls.Constructors); + var fields = _fieldConverter.ToNode(cls.Fields); var properties = _propertyConverter.ToNode(cls.Properties); var modifiers = _modifierConverter.ToNode(cls.Modifiers); var methods = _methodConverter.ToNode(cls.Methods); @@ -51,6 +56,7 @@ public ClassDeclarationSyntax ToNode(Class cls) return SyntaxFactory.ClassDeclaration(cls.Name) .AddBaseListTypes(baseTypes.ToArray()) .AddModifiers(modifiers.ToArray()) + .AddMembers(fields.ToArray()) .AddMembers(ctors.ToArray()) .AddMembers(properties.ToArray()) .AddMembers(methods.ToArray()); diff --git a/RefleCS/RefleCS/Converters/FieldConverter.cs b/RefleCS/RefleCS/Converters/FieldConverter.cs new file mode 100644 index 0000000..c88c776 --- /dev/null +++ b/RefleCS/RefleCS/Converters/FieldConverter.cs @@ -0,0 +1,63 @@ +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using RefleCS.Nodes; + +namespace RefleCS.Converters; + +internal class FieldConverter +{ + private readonly ModifierConverter _modifierConverter = new(); + + public Field ToField(FieldDeclarationSyntax field) + { + var typeName = field.Declaration.Type.ToString(); + var name = field.Declaration.Variables.First().Identifier.ToString(); + var modifiers = _modifierConverter.ToFieldModifier(field.Modifiers); + var initializerString = field.Declaration.Variables.First().Initializer?.Value.ToString(); + FieldInitializer? initializer = initializerString is null + ? null + : initializer = new FieldInitializer(initializerString); + + return new Field(modifiers, typeName, name, initializer); + } + + public IEnumerable ToField(IEnumerable fields) + { + foreach (var field in fields) + { + yield return ToField(field); + } + } + + public FieldDeclarationSyntax ToNode(Field field) + { + var modifiers = _modifierConverter.ToNode(field.Modifiers); + + var initializer = field.Initializer is null + ? null + : SyntaxFactory.EqualsValueClause( + //SyntaxFactory.Token(SyntaxKind.EqualsValueClause), + SyntaxFactory.ParseExpression(field.Initializer.Value)); + + var variable = SyntaxFactory.VariableDeclarator(field.TypeName) + .WithIdentifier(SyntaxFactory.ParseToken(field.Name)) + .WithInitializer(initializer); + + var variables = SyntaxFactory.VariableDeclaration(SyntaxFactory.ParseTypeName(field.TypeName)) + .WithVariables([variable]); + + var result = SyntaxFactory + .FieldDeclaration(variables) + .WithModifiers(modifiers); + + return result; + } + + public IEnumerable ToNode(IEnumerable fields) + { + foreach (var field in fields) + { + yield return ToNode(field); + } + } +} diff --git a/RefleCS/RefleCS/Converters/ModifierConverter.cs b/RefleCS/RefleCS/Converters/ModifierConverter.cs index 6942807..e19a578 100644 --- a/RefleCS/RefleCS/Converters/ModifierConverter.cs +++ b/RefleCS/RefleCS/Converters/ModifierConverter.cs @@ -46,6 +46,19 @@ public IEnumerable ToConstructorModifier(IEnumerable(modifier); + } + + public IEnumerable ToFieldModifier(IEnumerable modifiers) + { + foreach (var modifier in modifiers) + { + yield return ToFieldModifier(modifier); + } + } + public PropertyModifier ToPropertyModifier(SyntaxToken modifier) { return ToModifier(modifier); @@ -121,6 +134,11 @@ public SyntaxTokenList ToNode(IEnumerable modifiers) return ToNode(modifiers); } + public SyntaxTokenList ToNode(IEnumerable modifiers) + { + return ToNode(modifiers); + } + public SyntaxTokenList ToNode(IEnumerable modifiers) { return ToNode(modifiers); diff --git a/RefleCS/RefleCS/Converters/PropertyConverter.cs b/RefleCS/RefleCS/Converters/PropertyConverter.cs index 1c9412b..9a9c457 100644 --- a/RefleCS/RefleCS/Converters/PropertyConverter.cs +++ b/RefleCS/RefleCS/Converters/PropertyConverter.cs @@ -13,7 +13,7 @@ public Property ToProperty(PropertyDeclarationSyntax property) { var typeName = property.Type.ToString(); var accessors = property.AccessorList is null - ? Enumerable.Empty() + ? [] : property.AccessorList.Accessors .Select(accessor => _propertyAccessorConverter.ToPropertyAccessor(accessor)); diff --git a/RefleCS/RefleCS/Enums/FieldModifier.cs b/RefleCS/RefleCS/Enums/FieldModifier.cs new file mode 100644 index 0000000..61a85bc --- /dev/null +++ b/RefleCS/RefleCS/Enums/FieldModifier.cs @@ -0,0 +1,34 @@ +using Microsoft.CodeAnalysis.CSharp; +using RefleCS.Attributes; + +namespace RefleCS.Enums; + +/// +/// Represents a field modifier. +/// +public enum FieldModifier +{ + /// + /// Public field modifier. + /// + [SyntaxKind(SyntaxKind.PublicKeyword)] + Public, + + /// + /// Private field modifier. + /// + [SyntaxKind(SyntaxKind.PrivateKeyword)] + Private, + + /// + /// Protected field modifier. + /// + [SyntaxKind(SyntaxKind.ProtectedKeyword)] + Protected, + + /// + /// Internal field modifier. + /// + [SyntaxKind(SyntaxKind.InternalKeyword)] + Internal +} \ No newline at end of file diff --git a/RefleCS/RefleCS/Nodes/Class.cs b/RefleCS/RefleCS/Nodes/Class.cs index 0a63a5f..02f57b9 100644 --- a/RefleCS/RefleCS/Nodes/Class.cs +++ b/RefleCS/RefleCS/Nodes/Class.cs @@ -9,6 +9,7 @@ public class Class { private readonly List _methods; private readonly List _constructors; + private readonly List _fields; private readonly List _properties; private readonly List _baseTypes; private readonly List _modifiers; @@ -19,11 +20,12 @@ public class Class public Class(string name) { Name = name; - _methods = new List(); - _constructors = new List(); - _properties = new List(); - _baseTypes = new List(); - _modifiers = new List(); + _methods = []; + _constructors = []; + _properties = []; + _baseTypes = []; + _modifiers = []; + _fields = []; } /// @@ -31,11 +33,13 @@ public Class(string name) /// /// /// + /// /// /// /// public Class(IEnumerable modifiers, string name, IEnumerable constructors, - IEnumerable properties, IEnumerable methods, IEnumerable baseTypes) + IEnumerable fields, IEnumerable properties, IEnumerable methods, + IEnumerable baseTypes) { _modifiers = modifiers.ToList(); Name = name; @@ -43,6 +47,7 @@ public Class(IEnumerable modifiers, string name, IEnumerable @@ -65,6 +70,11 @@ public Class(IEnumerable modifiers, string name, IEnumerable public IReadOnlyCollection Constructors => _constructors; + /// + /// The fields in the class. + /// + public IReadOnlyCollection Fields => _fields; + /// /// The properties in the class. /// @@ -141,6 +151,28 @@ public Class RemoveMethod(Method method) return this; } + /// + /// Adds a field to the class. + /// + /// + /// + public Class AddField(Field field) + { + _fields.Add(field); + return this; + } + + /// + /// Removes a field from the class. + /// + /// + /// + public Class RemoveField(Field field) + { + _fields.Remove(field); + return this; + } + /// /// Adds a property to the class. /// diff --git a/RefleCS/RefleCS/Nodes/Field.cs b/RefleCS/RefleCS/Nodes/Field.cs new file mode 100644 index 0000000..d0acaca --- /dev/null +++ b/RefleCS/RefleCS/Nodes/Field.cs @@ -0,0 +1,45 @@ +using RefleCS.Enums; + +namespace RefleCS.Nodes; + +/// +/// Represents a field. +/// +public class Field +{ + private readonly List _modifiers; + + /// + /// + /// + /// + /// + /// + public Field(IEnumerable modifiers, string typeName, string name, FieldInitializer? initializer) + { + _modifiers = modifiers.ToList(); + TypeName = typeName; + Name = name; + Initializer = initializer; + } + + /// + /// The modifiers of the field. + /// + public IReadOnlyCollection Modifiers => _modifiers; + + /// + /// The type name of the field. + /// + public string TypeName { get; private set; } + + /// + /// The name of the field. + /// + public string Name { get; private set; } + + /// + /// The initializer of the field. null if not initialized. + /// + public FieldInitializer? Initializer { get; } +} diff --git a/RefleCS/RefleCS/Nodes/FieldInitializer.cs b/RefleCS/RefleCS/Nodes/FieldInitializer.cs new file mode 100644 index 0000000..9b6a3c5 --- /dev/null +++ b/RefleCS/RefleCS/Nodes/FieldInitializer.cs @@ -0,0 +1,7 @@ +namespace RefleCS.Nodes; + +/// +/// Represents an inline field initializer. +/// +/// +public record FieldInitializer(string Value); \ No newline at end of file From 5957b01e9d1425cc00200c7da38ed17c1e71f7d0 Mon Sep 17 00:00:00 2001 From: Velociraptor45 <30436688+Velociraptor45@users.noreply.github.com> Date: Sun, 27 Apr 2025 09:17:27 +0200 Subject: [PATCH 2/6] Upgrade to .net 9 --- RefleCS/RefleCS.TestKit/RefleCS.TestKit.csproj | 4 ++-- RefleCS/RefleCS.TestTools/RefleCS.TestTools.csproj | 2 +- RefleCS/RefleCS.Tests/RefleCS.Tests.csproj | 14 +++++++------- RefleCS/RefleCS/RefleCS.csproj | 8 +++----- 4 files changed, 13 insertions(+), 15 deletions(-) diff --git a/RefleCS/RefleCS.TestKit/RefleCS.TestKit.csproj b/RefleCS/RefleCS.TestKit/RefleCS.TestKit.csproj index 3cbd684..c3e58b7 100644 --- a/RefleCS/RefleCS.TestKit/RefleCS.TestKit.csproj +++ b/RefleCS/RefleCS.TestKit/RefleCS.TestKit.csproj @@ -1,14 +1,14 @@  - net8.0 + net9.0 enable enable - + diff --git a/RefleCS/RefleCS.TestTools/RefleCS.TestTools.csproj b/RefleCS/RefleCS.TestTools/RefleCS.TestTools.csproj index 036e69c..3081695 100644 --- a/RefleCS/RefleCS.TestTools/RefleCS.TestTools.csproj +++ b/RefleCS/RefleCS.TestTools/RefleCS.TestTools.csproj @@ -1,7 +1,7 @@  - net8.0 + net9.0 enable enable diff --git a/RefleCS/RefleCS.Tests/RefleCS.Tests.csproj b/RefleCS/RefleCS.Tests/RefleCS.Tests.csproj index 8395b6d..0b9cb16 100644 --- a/RefleCS/RefleCS.Tests/RefleCS.Tests.csproj +++ b/RefleCS/RefleCS.Tests/RefleCS.Tests.csproj @@ -1,7 +1,7 @@ - net8.0 + net9.0 enable enable @@ -9,19 +9,19 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive - - + + - - + + runtime; build; native; contentfiles; analyzers; buildtransitive all - + runtime; build; native; contentfiles; analyzers; buildtransitive all diff --git a/RefleCS/RefleCS/RefleCS.csproj b/RefleCS/RefleCS/RefleCS.csproj index 90a48fc..744ebd1 100644 --- a/RefleCS/RefleCS/RefleCS.csproj +++ b/RefleCS/RefleCS/RefleCS.csproj @@ -1,7 +1,7 @@  - net8.0 + net8.0;net9.0 enable enable RefleCS @@ -9,16 +9,14 @@ README.md False - 0.2.0 - 0.2.0 - + 0.3.0-alpha.1 https://github.com/Velociraptor45/RefleCS MIT True - + From 9e85098991c5a33e404e07c63d61199f1ac5b57d Mon Sep 17 00:00:00 2001 From: Velociraptor45 <30436688+Velociraptor45@users.noreply.github.com> Date: Sun, 27 Apr 2025 09:21:30 +0200 Subject: [PATCH 3/6] #31: Update documentation --- README.md | 1 + RefleCS/RefleCS.Tests/CsFileCreatorTests.cs | 167 +++++++++++--------- RefleCS/RefleCS/Nodes/FieldInitializer.cs | 2 +- 3 files changed, 91 insertions(+), 79 deletions(-) diff --git a/README.md b/README.md index 7d0d14f..eaf4186 100644 --- a/README.md +++ b/README.md @@ -31,6 +31,7 @@ var file = new CsFile( }, "App", new List(), + new List(), new List(), new List { diff --git a/RefleCS/RefleCS.Tests/CsFileCreatorTests.cs b/RefleCS/RefleCS.Tests/CsFileCreatorTests.cs index 995eaab..0cd0a8a 100644 --- a/RefleCS/RefleCS.Tests/CsFileCreatorTests.cs +++ b/RefleCS/RefleCS.Tests/CsFileCreatorTests.cs @@ -6,29 +6,26 @@ namespace RefleCS.Tests; public class CsFileCreatorTests { - private readonly CsFileHandler _sut; - - public CsFileCreatorTests() - { - _sut = new CsFileHandler(); - } + private readonly CsFileHandler _sut = new(); [Fact] public void FromCode_WithCtor_ShouldReturnExpectedResult() { // Arrange - var content = @"using System; -using System.Linq; + var content = """ + using System; + using System.Linq; -namespace MyApp; + namespace MyApp; -public sealed class App -{ - public App(int? id) - { - Id = id.Value; - } -}"; + public sealed class App + { + public App(int? id) + { + Id = id.Value; + } + } + """; var expectedResult = new CsFile( new List @@ -86,15 +83,17 @@ public App(int? id) public void FromCode_WithPrivateSetProperty_ShouldReturnExpectedResult() { // Arrange - var content = @"using System; -using System.Linq; + var content = """ + using System; + using System.Linq; -namespace MyApp; + namespace MyApp; -public class App -{ - public int MyProp { get; private set; } -}"; + public class App + { + public int MyProp { get; private set; } + } + """; var expectedResult = new CsFile( new List @@ -139,15 +138,17 @@ public class App public void FromCode_WithFieldInitialized_ShouldReturnExpectedResult() { // Arrange - var content = @"using System; -using System.Linq; + var content = """ + using System; + using System.Linq; -namespace MyApp; + namespace MyApp; -public class App -{ - private int _myInt = 1; -}"; + public class App + { + private int _myInt = 1; + } + """; var expectedResult = new CsFile( new List @@ -184,15 +185,17 @@ public class App public void FromCode_WithFieldUninitialized_ShouldReturnExpectedResult() { // Arrange - var content = @"using System; -using System.Linq; + var content = """ + using System; + using System.Linq; -namespace MyApp; + namespace MyApp; -public class App -{ - private int _myInt; -}"; + public class App + { + private int _myInt; + } + """; var expectedResult = new CsFile( new List @@ -241,21 +244,23 @@ public class App public void FromCode_WithMethod_ShouldReturnExpectedResult(string modifiers, MethodModifier[] expectedModifiers) { // Arrange - var content = @$"using System; - -namespace MyApp; - -public sealed class App -{{ - // another comment ??? - /* - * my other comment - */ - {modifiers} void CheckIfTrue(bool bl) - {{ - return bl; - }} -}}"; + var content = $$""" + using System; + + namespace MyApp; + + public sealed class App + { + // another comment ??? + /* + * my other comment + */ + {{modifiers}} void CheckIfTrue(bool bl) + { + return bl; + } + } + """; var expectedResult = new CsFile( new List @@ -320,17 +325,19 @@ public void FromCode_WithMethodParameter_ShouldReturnExpectedResult(string modif ParameterModifier[] expectedModifiers) { // Arrange - var content = @$"using System; + var content = $$""" + using System; -namespace MyApp; + namespace MyApp; -public sealed class App -{{ - void CheckIfTrue({modifiers} bool bl) - {{ - return bl; - }} -}}"; + public sealed class App + { + void CheckIfTrue({{modifiers}} bool bl) + { + return bl; + } + } + """; var expectedResult = new CsFile( new List @@ -385,13 +392,15 @@ void CheckIfTrue({modifiers} bool bl) public void FromCode_WithSuperclass_ShouldReturnExpectedResult() { // Arrange - var content = @"using System; + var content = """ + using System; -namespace MyApp; + namespace MyApp; -public sealed class App : AnotherApp, IImplement -{ -}"; + public sealed class App : AnotherApp, IImplement + { + } + """; var expectedResult = new CsFile( new List @@ -432,18 +441,20 @@ public sealed class App : AnotherApp, IImplement public void FromCode_WithRecord_ShouldReturnExpectedResult() { // Arrange - var content = @"using System; - -namespace MyApp; - -public record App(int Id) -{ - public App(int id, string name) : this(id) - { - } - - public string Name { get; } -}"; + var content = """ + using System; + + namespace MyApp; + + public record App(int Id) + { + public App(int id, string name) : this(id) + { + } + + public string Name { get; } + } + """; var expectedResult = new CsFile( new List diff --git a/RefleCS/RefleCS/Nodes/FieldInitializer.cs b/RefleCS/RefleCS/Nodes/FieldInitializer.cs index 9b6a3c5..c57df9a 100644 --- a/RefleCS/RefleCS/Nodes/FieldInitializer.cs +++ b/RefleCS/RefleCS/Nodes/FieldInitializer.cs @@ -3,5 +3,5 @@ /// /// Represents an inline field initializer. /// -/// +/// The full field initializer value. Must not contain the '=' public record FieldInitializer(string Value); \ No newline at end of file From f280b04dbeb22db3100da43721bb255328541117 Mon Sep 17 00:00:00 2001 From: Velociraptor45 <30436688+Velociraptor45@users.noreply.github.com> Date: Sun, 27 Apr 2025 09:32:36 +0200 Subject: [PATCH 4/6] Upgrade pipeline actions --- .github/workflows/build.yml | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index bb915b7..52aa202 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -11,19 +11,20 @@ jobs: name: Build runs-on: ubuntu-latest steps: - - name: Set up .net 8 - uses: actions/setup-dotnet@v3 + - name: Set up .net 9 + uses: actions/setup-dotnet@v4 with: - dotnet-version: '8.0.x' + dotnet-version: '9.0.x' - name: Set up JDK 21 - uses: actions/setup-java@v1 + uses: actions/setup-java@v4 with: - java-version: 21 - - uses: actions/checkout@v2 + java-version: '21' + distribution: 'oracle' + - uses: actions/checkout@v4 with: fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis - name: Cache SonarCloud packages - uses: actions/cache@v1 + uses: actions/cache@v4 with: path: ~\sonar\cache key: ${{ runner.os }}-sonar From cdbe52c2e39d585bca0a092aa46efdb4888ceee0 Mon Sep 17 00:00:00 2001 From: Velociraptor45 <30436688+Velociraptor45@users.noreply.github.com> Date: Sun, 27 Apr 2025 09:38:11 +0200 Subject: [PATCH 5/6] #31: Remove useless initialization --- RefleCS/RefleCS/Converters/FieldConverter.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RefleCS/RefleCS/Converters/FieldConverter.cs b/RefleCS/RefleCS/Converters/FieldConverter.cs index c88c776..0244eed 100644 --- a/RefleCS/RefleCS/Converters/FieldConverter.cs +++ b/RefleCS/RefleCS/Converters/FieldConverter.cs @@ -16,7 +16,7 @@ public Field ToField(FieldDeclarationSyntax field) var initializerString = field.Declaration.Variables.First().Initializer?.Value.ToString(); FieldInitializer? initializer = initializerString is null ? null - : initializer = new FieldInitializer(initializerString); + : new FieldInitializer(initializerString); return new Field(modifiers, typeName, name, initializer); } From eb9945ed9821f4cf6a95f989f672d433a1ffef6c Mon Sep 17 00:00:00 2001 From: Velociraptor45 <30436688+Velociraptor45@users.noreply.github.com> Date: Sun, 27 Apr 2025 09:47:40 +0200 Subject: [PATCH 6/6] #31: Add null initializer test --- .../Converters/CsFileConverterTestData.cs | 31 +++++++++++++++++++ RefleCS/RefleCS/Converters/FieldConverter.cs | 4 +-- 2 files changed, 32 insertions(+), 3 deletions(-) diff --git a/RefleCS/RefleCS.Tests/Converters/CsFileConverterTestData.cs b/RefleCS/RefleCS.Tests/Converters/CsFileConverterTestData.cs index 3a103d1..ea4f5dc 100644 --- a/RefleCS/RefleCS.Tests/Converters/CsFileConverterTestData.cs +++ b/RefleCS/RefleCS.Tests/Converters/CsFileConverterTestData.cs @@ -10,6 +10,7 @@ public IEnumerator GetEnumerator() { yield return WithRecord(); yield return WithClass(); + yield return WithClass_EmptyFieldInitializer(); } private object[] WithRecord() @@ -118,6 +119,36 @@ private object[] WithClass() ]; } + private object[] WithClass_EmptyFieldInitializer() + { + return + [ + new CsFile( + new List + { + new("System"), + new("System.Linq") + }, + new Namespace( + "MyApp", + new List + { + new( + new List { ClassModifier.Private }, + "MyClass", + new List(), + new List + { + new([FieldModifier.Public], "string", "_myString", null) + }, + new List(), + new List(), + new List { new("MyBaseClass") }) + }, + [])) + ]; + } + IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); diff --git a/RefleCS/RefleCS/Converters/FieldConverter.cs b/RefleCS/RefleCS/Converters/FieldConverter.cs index 0244eed..31a7794 100644 --- a/RefleCS/RefleCS/Converters/FieldConverter.cs +++ b/RefleCS/RefleCS/Converters/FieldConverter.cs @@ -35,9 +35,7 @@ public FieldDeclarationSyntax ToNode(Field field) var initializer = field.Initializer is null ? null - : SyntaxFactory.EqualsValueClause( - //SyntaxFactory.Token(SyntaxKind.EqualsValueClause), - SyntaxFactory.ParseExpression(field.Initializer.Value)); + : SyntaxFactory.EqualsValueClause(SyntaxFactory.ParseExpression(field.Initializer.Value)); var variable = SyntaxFactory.VariableDeclarator(field.TypeName) .WithIdentifier(SyntaxFactory.ParseToken(field.Name))