diff --git a/README.md b/README.md
index 140f71d..5cdb1ab 100644
--- a/README.md
+++ b/README.md
@@ -24,7 +24,7 @@ Accompanying blog post: [www.m31coding.com>blog>fluent-api](https://www.m31codin
- Optional (skippable) builder methods
- Forking and branching capabilities
- Support for returning arbitrary types
-- Support for generics and partial classes
+- Support for inheritance, generics, and partial classes
## Installing via NuGet
@@ -37,7 +37,7 @@ PM> Install-Package M31.FluentApi
A package reference will be added to your `csproj` file. Moreover, since this library provides code via source code generation, consumers of your project don't need the reference to `M31.FluentApi`. Therefore, it is recommended to use the `PrivateAssets` metadata tag:
```xml
-
+
```
If you would like to examine the generated code, you may emit it by adding the following lines to your `csproj` file:
diff --git a/src/ExampleProject/ExchangeStudent.cs b/src/ExampleProject/ExchangeStudent.cs
new file mode 100644
index 0000000..8b1efe2
--- /dev/null
+++ b/src/ExampleProject/ExchangeStudent.cs
@@ -0,0 +1,14 @@
+// Non-nullable member is uninitialized
+#pragma warning disable CS8618
+// ReSharper disable All
+
+using M31.FluentApi.Attributes;
+
+namespace ExampleProject;
+
+[FluentApi]
+public class ExchangeStudent : Student
+{
+ [FluentMember(6)]
+ public string HomeCountry { get; private set; }
+}
\ No newline at end of file
diff --git a/src/ExampleProject/Program.cs b/src/ExampleProject/Program.cs
index 645728c..5dc8388 100644
--- a/src/ExampleProject/Program.cs
+++ b/src/ExampleProject/Program.cs
@@ -12,6 +12,14 @@
Console.WriteLine(JsonSerializer.Serialize(student1));
Console.WriteLine(JsonSerializer.Serialize(student2));
+// ExchangeStudent (inherited from Student)
+//
+
+ExchangeStudent exchangeStudent = CreateExchangeStudent.Named("Bob", "Bishop").BornOn(new DateOnly(2002, 8, 3))
+ .InSemester(2).LivingInBoston().WithUnknownMood().WhoseFriendIs("Alice").WithHomeCountry("United States");
+
+Console.WriteLine(JsonSerializer.Serialize(exchangeStudent));
+
// Person
//
diff --git a/src/M31.FluentApi.Generator/CodeGeneration/CodeBoardActors/InnerBodyGeneration/InnerBodyForMemberGenerator.cs b/src/M31.FluentApi.Generator/CodeGeneration/CodeBoardActors/InnerBodyGeneration/InnerBodyForMemberGenerator.cs
index 68d0cf0..c1a7f25 100644
--- a/src/M31.FluentApi.Generator/CodeGeneration/CodeBoardActors/InnerBodyGeneration/InnerBodyForMemberGenerator.cs
+++ b/src/M31.FluentApi.Generator/CodeGeneration/CodeBoardActors/InnerBodyGeneration/InnerBodyForMemberGenerator.cs
@@ -43,7 +43,7 @@ protected override void InitializeInfoField(string fieldName, MemberSymbolInfo s
// semesterPropertyInfo = typeof(Student)
// .GetProperty("Semester", BindingFlags.Instance | BindingFlags.NonPublic););
string code = $"{fieldName} =" +
- $" typeof({CodeBoard.Info.FluentApiClassNameWithTypeParameters})" +
+ $" typeof({symbolInfo.DeclaringClassNameWithTypeParameters})" +
$".Get{SymbolType(symbolInfo)}(\"{symbolInfo.Name}\", " +
$"{InfoFieldBindingFlagsArgument(symbolInfo)})!;";
diff --git a/src/M31.FluentApi.Generator/CodeGeneration/CodeBoardActors/InnerBodyGeneration/InnerBodyForMethodGenerator.cs b/src/M31.FluentApi.Generator/CodeGeneration/CodeBoardActors/InnerBodyGeneration/InnerBodyForMethodGenerator.cs
index 2d14707..bad8118 100644
--- a/src/M31.FluentApi.Generator/CodeGeneration/CodeBoardActors/InnerBodyGeneration/InnerBodyForMethodGenerator.cs
+++ b/src/M31.FluentApi.Generator/CodeGeneration/CodeBoardActors/InnerBodyGeneration/InnerBodyForMethodGenerator.cs
@@ -194,7 +194,7 @@ protected override void InitializeInfoField(string fieldName, MethodSymbolInfo s
// Generic types are created via Type.MakeGenericMethodParameter(int position). In addition, a ref type is
// specified via MakeByRefType().
staticConstructor.AppendBodyLine($"{fieldName} = " +
- $"typeof({CodeBoard.Info.FluentApiClassNameWithTypeParameters}).GetMethod(");
+ $"typeof({symbolInfo.DeclaringClassNameWithTypeParameters}).GetMethod(");
staticConstructor.AppendBodyLine($"{indentation}\"{symbolInfo.Name}\",");
staticConstructor.AppendBodyLine($"{indentation}{GetGenericParameterCount(symbolInfo.GenericInfo)},");
staticConstructor.AppendBodyLine($"{indentation}{InfoFieldBindingFlagsArgument(symbolInfo)},");
diff --git a/src/M31.FluentApi.Generator/CodeGeneration/CodeBoardElements/BuilderAndTargetInfo.cs b/src/M31.FluentApi.Generator/CodeGeneration/CodeBoardElements/BuilderAndTargetInfo.cs
index 7db5996..fe9b12d 100644
--- a/src/M31.FluentApi.Generator/CodeGeneration/CodeBoardElements/BuilderAndTargetInfo.cs
+++ b/src/M31.FluentApi.Generator/CodeGeneration/CodeBoardElements/BuilderAndTargetInfo.cs
@@ -17,14 +17,16 @@ internal BuilderAndTargetInfo(
{
Namespace = @namespace;
FluentApiClassName = fluentApiClassName;
- FluentApiClassNameWithTypeParameters = WithTypeParameters(fluentApiClassName, genericInfo);
+ FluentApiClassNameWithTypeParameters =
+ ClassInfoFactory.AugmentTypeNameWithGenericParameters(fluentApiClassName, genericInfo);
GenericInfo = genericInfo;
FluentApiTypeIsStruct = fluentApiTypeIsStruct;
FluentApiTypeIsInternal = fluentApiTypeIsInternal;
DefaultAccessModifier = fluentApiTypeIsInternal ? "internal" : "public";
FluentApiTypeConstructorInfo = fluentApiTypeConstructorInfo;
BuilderClassName = builderClassName;
- BuilderClassNameWithTypeParameters = WithTypeParameters(builderClassName, genericInfo);
+ BuilderClassNameWithTypeParameters =
+ ClassInfoFactory.AugmentTypeNameWithGenericParameters(builderClassName, genericInfo);
BuilderInstanceName = builderClassName.FirstCharToLower();
ClassInstanceName = fluentApiClassName.FirstCharToLower();
InitialStepInterfaceName = $"I{builderClassName}";
@@ -43,10 +45,4 @@ internal BuilderAndTargetInfo(
internal string BuilderInstanceName { get; }
internal string ClassInstanceName { get; }
internal string InitialStepInterfaceName { get; }
-
- private static string WithTypeParameters(string typeName, GenericInfo? genericInfo)
- {
- string parameterListInAngleBrackets = genericInfo?.ParameterListInAngleBrackets ?? string.Empty;
- return $"{typeName}{parameterListInAngleBrackets}";
- }
}
\ No newline at end of file
diff --git a/src/M31.FluentApi.Generator/CodeGeneration/CodeBoardElements/FluentApiSymbolInfo.cs b/src/M31.FluentApi.Generator/CodeGeneration/CodeBoardElements/FluentApiSymbolInfo.cs
index 65d079c..4902a9b 100644
--- a/src/M31.FluentApi.Generator/CodeGeneration/CodeBoardElements/FluentApiSymbolInfo.cs
+++ b/src/M31.FluentApi.Generator/CodeGeneration/CodeBoardElements/FluentApiSymbolInfo.cs
@@ -5,22 +5,29 @@ namespace M31.FluentApi.Generator.CodeGeneration.CodeBoardElements;
internal abstract class FluentApiSymbolInfo
{
- internal FluentApiSymbolInfo(string name, Accessibility accessibility, bool requiresReflection)
+ internal FluentApiSymbolInfo(
+ string name,
+ string declaringClassNameWithTypeParameters,
+ Accessibility accessibility,
+ bool requiresReflection)
{
Name = name;
NameInCamelCase = Name.TrimStart('_').FirstCharToLower();
+ DeclaringClassNameWithTypeParameters = declaringClassNameWithTypeParameters;
Accessibility = accessibility;
RequiresReflection = requiresReflection;
}
internal string Name { get; }
internal string NameInCamelCase { get; }
+ internal string DeclaringClassNameWithTypeParameters { get; }
internal Accessibility Accessibility { get; }
internal bool RequiresReflection { get; }
protected bool Equals(FluentApiSymbolInfo other)
{
return Name == other.Name &&
+ DeclaringClassNameWithTypeParameters == other.DeclaringClassNameWithTypeParameters &&
Accessibility == other.Accessibility &&
RequiresReflection == other.RequiresReflection;
}
@@ -35,6 +42,6 @@ public override bool Equals(object? obj)
public override int GetHashCode()
{
- return new HashCode().Add(Name, Accessibility, RequiresReflection);
+ return new HashCode().Add(Name, DeclaringClassNameWithTypeParameters, Accessibility, RequiresReflection);
}
}
\ No newline at end of file
diff --git a/src/M31.FluentApi.Generator/CodeGeneration/CodeBoardElements/MemberSymbolInfo.cs b/src/M31.FluentApi.Generator/CodeGeneration/CodeBoardElements/MemberSymbolInfo.cs
index 8473ee0..8fc7fa3 100644
--- a/src/M31.FluentApi.Generator/CodeGeneration/CodeBoardElements/MemberSymbolInfo.cs
+++ b/src/M31.FluentApi.Generator/CodeGeneration/CodeBoardElements/MemberSymbolInfo.cs
@@ -9,13 +9,14 @@ internal class MemberSymbolInfo : FluentApiSymbolInfo
internal MemberSymbolInfo(
string name,
string type,
+ string declaringClassNameWithTypeParameters,
Accessibility accessibility,
bool requiresReflection,
string typeForCodeGeneration,
bool isNullable,
bool isProperty,
CollectionType? collectionType)
- : base(name, accessibility, requiresReflection)
+ : base(name, declaringClassNameWithTypeParameters, accessibility, requiresReflection)
{
Type = type;
TypeForCodeGeneration = typeForCodeGeneration;
diff --git a/src/M31.FluentApi.Generator/CodeGeneration/CodeBoardElements/MethodSymbolInfo.cs b/src/M31.FluentApi.Generator/CodeGeneration/CodeBoardElements/MethodSymbolInfo.cs
index cd1b6a0..8a534d4 100644
--- a/src/M31.FluentApi.Generator/CodeGeneration/CodeBoardElements/MethodSymbolInfo.cs
+++ b/src/M31.FluentApi.Generator/CodeGeneration/CodeBoardElements/MethodSymbolInfo.cs
@@ -8,12 +8,13 @@ internal class MethodSymbolInfo : FluentApiSymbolInfo
{
internal MethodSymbolInfo(
string name,
+ string declaringClassNameWithTypeParameters,
Accessibility accessibility,
bool requiresReflection,
GenericInfo? genericInfo,
IReadOnlyCollection parameterInfos,
string returnType)
- : base(name, accessibility, requiresReflection)
+ : base(name, declaringClassNameWithTypeParameters, accessibility, requiresReflection)
{
GenericInfo = genericInfo;
ParameterInfos = parameterInfos;
diff --git a/src/M31.FluentApi.Generator/M31.FluentApi.Generator.csproj b/src/M31.FluentApi.Generator/M31.FluentApi.Generator.csproj
index 35f958d..fe5b9ee 100644
--- a/src/M31.FluentApi.Generator/M31.FluentApi.Generator.csproj
+++ b/src/M31.FluentApi.Generator/M31.FluentApi.Generator.csproj
@@ -11,7 +11,7 @@
true
true
true
- 1.8.0
+ 1.9.0
Kevin Schaal
The generator package for M31.FluentAPI. Don't install this package explicitly, install M31.FluentAPI instead.
fluentapi fluentbuilder fluentinterface fluentdesign fluent codegeneration
diff --git a/src/M31.FluentApi.Generator/SourceGenerators/ClassInfoFactory.cs b/src/M31.FluentApi.Generator/SourceGenerators/ClassInfoFactory.cs
index 7cd340e..829d953 100644
--- a/src/M31.FluentApi.Generator/SourceGenerators/ClassInfoFactory.cs
+++ b/src/M31.FluentApi.Generator/SourceGenerators/ClassInfoFactory.cs
@@ -57,10 +57,7 @@ private ClassInfoResult CreateFluentApiClassInfoInternal(
bool isStruct = syntaxKind is SyntaxKind.StructDeclaration or SyntaxKind.RecordStructDeclaration;
FluentApiClassInfo? classInfo = CreateFluentApiClassInfo(
- typeData.Type,
- typeData.GenericInfo,
- typeData.AttributeData,
- typeData.UsingStatements,
+ typeData,
isStruct,
generatorConfig.NewLineString,
cancellationToken);
@@ -75,35 +72,48 @@ private ClassInfoResult CreateFluentApiClassInfoInternal(
}
private FluentApiClassInfo? CreateFluentApiClassInfo(
- INamedTypeSymbol type,
- GenericInfo? genericInfo,
- AttributeDataExtended attributeDataExtended,
- IReadOnlyCollection usingStatements,
+ TypeData typeData,
bool isStruct,
string newLineString,
CancellationToken cancellationToken)
{
- string className = type.Name;
- string? @namespace = type.ContainingNamespace.IsGlobalNamespace ? null : type.ContainingNamespace.ToString();
- bool isInternal = type.DeclaredAccessibility == Accessibility.Internal;
- ConstructorInfo? constructorInfo = TryGetConstructorInfo(type);
+ string className = typeData.Type.Name;
+ string? @namespace = typeData.Type.ContainingNamespace.IsGlobalNamespace
+ ? null
+ : typeData.Type.ContainingNamespace.ToString();
+ bool isInternal = typeData.Type.DeclaredAccessibility == Accessibility.Internal;
+ ConstructorInfo? constructorInfo = TryGetConstructorInfo(typeData.Type);
FluentApiAttributeInfo fluentApiAttributeInfo =
- FluentApiAttributeInfo.Create(attributeDataExtended.AttributeData, className);
+ FluentApiAttributeInfo.Create(typeData.AttributeDataExtended.AttributeData, className);
List infos = new List();
+ (ITypeSymbol declaringType, ISymbol[] members)[] allMembers =
+ GetMembersOfTypeAndBaseTypes(typeData.Type).ToArray();
- foreach (var member in type.GetMembers().Where(m => m.CanBeReferencedByName && m.Name != string.Empty))
+ foreach ((ITypeSymbol declaringType, ISymbol[] members) in allMembers)
{
- if (cancellationToken.IsCancellationRequested)
+ if (declaringType is not INamedTypeSymbol namedTypeSymbol)
{
- return null;
+ throw new GenerationException($"The type {declaringType.Name} is not a named type symbol.");
}
- FluentApiInfo? fluentApiInfo = TryCreateFluentApiInfo(member);
+ GenericInfo? genericInfo = GenericInfo.TryCreate(namedTypeSymbol);
+ string declaringClassNameWithGenericParameters =
+ AugmentTypeNameWithGenericParameters(namedTypeSymbol.Name, genericInfo);
- if (fluentApiInfo != null)
+ foreach (ISymbol member in members)
{
- infos.Add(fluentApiInfo);
+ if (cancellationToken.IsCancellationRequested)
+ {
+ return null;
+ }
+
+ FluentApiInfo? fluentApiInfo = TryCreateFluentApiInfo(member, declaringClassNameWithGenericParameters);
+
+ if (fluentApiInfo != null)
+ {
+ infos.Add(fluentApiInfo);
+ }
}
}
@@ -112,17 +122,43 @@ private ClassInfoResult CreateFluentApiClassInfoInternal(
return new FluentApiClassInfo(
className,
@namespace,
- genericInfo,
+ typeData.GenericInfo,
isStruct,
isInternal,
constructorInfo!,
fluentApiAttributeInfo.BuilderClassName,
newLineString,
infos,
- usingStatements,
+ typeData.UsingStatements,
new FluentApiClassAdditionalInfo(groups));
}
+ private static List<(ITypeSymbol declaringType, ISymbol[] members)> GetMembersOfTypeAndBaseTypes(
+ ITypeSymbol typeSymbol)
+ {
+ List<(ITypeSymbol declaringType, ISymbol[] members)> result =
+ new List<(ITypeSymbol declaringType, ISymbol[] members)>();
+
+ GetMembers(typeSymbol);
+ return result;
+
+ void GetMembers(ITypeSymbol currentTypeSymbol)
+ {
+ ISymbol[] members = currentTypeSymbol.GetMembers()
+ .Where(m => m.CanBeReferencedByName && m.Name != string.Empty).ToArray();
+
+ result.Add((currentTypeSymbol, members));
+
+ if (currentTypeSymbol.BaseType == null ||
+ currentTypeSymbol.BaseType.SpecialType == SpecialType.System_Object)
+ {
+ return;
+ }
+
+ GetMembers(currentTypeSymbol.BaseType);
+ }
+ }
+
private ConstructorInfo? TryGetConstructorInfo(INamedTypeSymbol type)
{
/* Look for the default constructor. If it is not present, take the constructor
@@ -165,7 +201,7 @@ with the fewest parameters that is explicitly declared. */
constructors[0].DeclaredAccessibility != Accessibility.Public);
}
- private FluentApiInfo? TryCreateFluentApiInfo(ISymbol symbol)
+ private FluentApiInfo? TryCreateFluentApiInfo(ISymbol symbol, string declaringClassNameWithTypeParameters)
{
AttributeDataExtractor extractor = new AttributeDataExtractor(report);
FluentApiAttributeData? attributeData = extractor.GetAttributeData(symbol);
@@ -182,6 +218,12 @@ with the fewest parameters that is explicitly declared. */
}
FluentApiInfoCreator fluentApiInfoCreator = new FluentApiInfoCreator(report);
- return fluentApiInfoCreator.Create(symbol, attributeData);
+ return fluentApiInfoCreator.Create(symbol, attributeData, declaringClassNameWithTypeParameters);
+ }
+
+ public static string AugmentTypeNameWithGenericParameters(string typeName, GenericInfo? genericInfo)
+ {
+ string parameterListInAngleBrackets = genericInfo?.ParameterListInAngleBrackets ?? string.Empty;
+ return $"{typeName}{parameterListInAngleBrackets}";
}
}
\ No newline at end of file
diff --git a/src/M31.FluentApi.Generator/SourceGenerators/FluentApiInfoCreator.cs b/src/M31.FluentApi.Generator/SourceGenerators/FluentApiInfoCreator.cs
index 884c5b1..9cc3d4b 100644
--- a/src/M31.FluentApi.Generator/SourceGenerators/FluentApiInfoCreator.cs
+++ b/src/M31.FluentApi.Generator/SourceGenerators/FluentApiInfoCreator.cs
@@ -17,9 +17,12 @@ internal FluentApiInfoCreator(ClassInfoReport classInfoReport)
this.classInfoReport = classInfoReport;
}
- internal FluentApiInfo? Create(ISymbol symbol, FluentApiAttributeData attributeData)
+ internal FluentApiInfo? Create(
+ ISymbol symbol,
+ FluentApiAttributeData attributeData,
+ string declaringClassNameWithTypeParameters)
{
- FluentApiSymbolInfo symbolInfo = SymbolInfoCreator.Create(symbol);
+ FluentApiSymbolInfo symbolInfo = SymbolInfoCreator.Create(symbol, declaringClassNameWithTypeParameters);
AttributeInfoBase? attributeInfo = CreateAttributeInfo(attributeData.MainAttributeData, symbol, symbolInfo);
if (attributeInfo == null)
diff --git a/src/M31.FluentApi.Generator/SourceGenerators/Generics/GenericInfo.cs b/src/M31.FluentApi.Generator/SourceGenerators/Generics/GenericInfo.cs
index f06f47a..6c97a46 100644
--- a/src/M31.FluentApi.Generator/SourceGenerators/Generics/GenericInfo.cs
+++ b/src/M31.FluentApi.Generator/SourceGenerators/Generics/GenericInfo.cs
@@ -10,6 +10,11 @@ private GenericInfo(IReadOnlyCollection parameters)
Parameters = parameters;
}
+ internal static GenericInfo? TryCreate(INamedTypeSymbol type)
+ {
+ return type.IsGenericType ? Create(type.TypeParameters) : null;
+ }
+
internal static GenericInfo Create(IEnumerable typeParameters)
{
GenericTypeParameter[] parameters =
diff --git a/src/M31.FluentApi.Generator/SourceGenerators/SymbolInfoCreator.cs b/src/M31.FluentApi.Generator/SourceGenerators/SymbolInfoCreator.cs
index 2644041..36a689c 100644
--- a/src/M31.FluentApi.Generator/SourceGenerators/SymbolInfoCreator.cs
+++ b/src/M31.FluentApi.Generator/SourceGenerators/SymbolInfoCreator.cs
@@ -9,22 +9,31 @@ namespace M31.FluentApi.Generator.SourceGenerators;
internal static class SymbolInfoCreator
{
- internal static FluentApiSymbolInfo Create(ISymbol symbol)
+ internal static FluentApiSymbolInfo Create(ISymbol symbol, string declaringClassNameWithTypeParameters)
{
return symbol switch
{
- IPropertySymbol propertySymbol => CreateMemberSymbolInfo(propertySymbol),
- IFieldSymbol fieldSymbol => CreateMemberSymbolInfo(fieldSymbol),
- IMethodSymbol methodSymbol => CreateMethodSymbolInfo(methodSymbol),
+ IPropertySymbol propertySymbol => CreateMemberSymbolInfo(
+ propertySymbol,
+ declaringClassNameWithTypeParameters),
+ IFieldSymbol fieldSymbol => CreateMemberSymbolInfo(
+ fieldSymbol,
+ declaringClassNameWithTypeParameters),
+ IMethodSymbol methodSymbol => CreateMethodSymbolInfo(
+ methodSymbol,
+ declaringClassNameWithTypeParameters),
_ => throw new ArgumentException($"Unexpected symbol type: {symbol.GetType()}."),
};
}
- private static MemberSymbolInfo CreateMemberSymbolInfo(IFieldSymbol fieldSymbol)
+ private static MemberSymbolInfo CreateMemberSymbolInfo(
+ IFieldSymbol fieldSymbol,
+ string declaringClassNameWithTypeParameters)
{
return new MemberSymbolInfo(
fieldSymbol.Name,
fieldSymbol.Type.ToString(),
+ declaringClassNameWithTypeParameters,
fieldSymbol.DeclaredAccessibility,
RequiresReflection(fieldSymbol),
CodeTypeExtractor.GetTypeForCodeGeneration(fieldSymbol.Type),
@@ -33,11 +42,14 @@ private static MemberSymbolInfo CreateMemberSymbolInfo(IFieldSymbol fieldSymbol)
CollectionInference.InferCollectionType(fieldSymbol.Type));
}
- private static MemberSymbolInfo CreateMemberSymbolInfo(IPropertySymbol propertySymbol)
+ private static MemberSymbolInfo CreateMemberSymbolInfo(
+ IPropertySymbol propertySymbol,
+ string declaringClassNameWithTypeParameters)
{
return new MemberSymbolInfo(
propertySymbol.Name,
propertySymbol.Type.ToString(),
+ declaringClassNameWithTypeParameters,
propertySymbol.DeclaredAccessibility,
RequiresReflection(propertySymbol),
CodeTypeExtractor.GetTypeForCodeGeneration(propertySymbol.Type),
@@ -46,7 +58,9 @@ private static MemberSymbolInfo CreateMemberSymbolInfo(IPropertySymbol propertyS
CollectionInference.InferCollectionType(propertySymbol.Type));
}
- private static MethodSymbolInfo CreateMethodSymbolInfo(IMethodSymbol methodSymbol)
+ private static MethodSymbolInfo CreateMethodSymbolInfo(
+ IMethodSymbol methodSymbol,
+ string declaringClassNameWithTypeParameters)
{
GenericInfo? genericInfo = GetGenericInfo(methodSymbol);
Dictionary typeParameterNameToTypeParameterPosition = genericInfo == null
@@ -59,6 +73,7 @@ private static MethodSymbolInfo CreateMethodSymbolInfo(IMethodSymbol methodSymbo
return new MethodSymbolInfo(
methodSymbol.Name,
+ declaringClassNameWithTypeParameters,
methodSymbol.DeclaredAccessibility,
RequiresReflection(methodSymbol),
genericInfo,
diff --git a/src/M31.FluentApi.Generator/SourceGenerators/TypeData.cs b/src/M31.FluentApi.Generator/SourceGenerators/TypeData.cs
index ba38bbf..be301ac 100644
--- a/src/M31.FluentApi.Generator/SourceGenerators/TypeData.cs
+++ b/src/M31.FluentApi.Generator/SourceGenerators/TypeData.cs
@@ -9,17 +9,17 @@ internal class TypeData
internal TypeData(
INamedTypeSymbol type,
GenericInfo? genericInfo,
- AttributeDataExtended attributeData,
+ AttributeDataExtended attributeDataExtended,
IReadOnlyCollection usingStatements)
{
Type = type;
GenericInfo = genericInfo;
- AttributeData = attributeData;
+ AttributeDataExtended = attributeDataExtended;
UsingStatements = usingStatements;
}
internal INamedTypeSymbol Type { get; }
internal GenericInfo? GenericInfo { get; }
- internal AttributeDataExtended AttributeData { get; }
+ internal AttributeDataExtended AttributeDataExtended { get; }
internal IReadOnlyCollection UsingStatements { get; }
}
\ No newline at end of file
diff --git a/src/M31.FluentApi.Generator/SourceGenerators/TypeDataCreator.cs b/src/M31.FluentApi.Generator/SourceGenerators/TypeDataCreator.cs
index bce3dab..384279f 100644
--- a/src/M31.FluentApi.Generator/SourceGenerators/TypeDataCreator.cs
+++ b/src/M31.FluentApi.Generator/SourceGenerators/TypeDataCreator.cs
@@ -27,7 +27,7 @@ internal TypeDataCreator(ClassInfoReport report)
return null;
}
- GenericInfo? genericInfo = GetGenericInfo(type);
+ GenericInfo? genericInfo = GenericInfo.TryCreate(type);
AttributeDataExtended[] attributeData = type.GetAttributes().Select(AttributeDataExtended.Create)
.OfType().Where(a => a.FullName == Attributes.FullNames.FluentApiAttribute)
@@ -72,9 +72,4 @@ internal TypeDataCreator(ClassInfoReport report)
return GetUsingStatements(syntaxNode.Parent);
}
-
- private GenericInfo? GetGenericInfo(INamedTypeSymbol type)
- {
- return type.IsGenericType ? GenericInfo.Create(type.TypeParameters) : null;
- }
}
\ No newline at end of file
diff --git a/src/M31.FluentApi.Tests/CodeGeneration/CodeGenerationExecutionTests.cs b/src/M31.FluentApi.Tests/CodeGeneration/CodeGenerationExecutionTests.cs
index 04770f8..f75069f 100644
--- a/src/M31.FluentApi.Tests/CodeGeneration/CodeGenerationExecutionTests.cs
+++ b/src/M31.FluentApi.Tests/CodeGeneration/CodeGenerationExecutionTests.cs
@@ -5,6 +5,7 @@
using System;
using System.Collections.Generic;
+using System.Reflection;
using M31.FluentApi.Tests.CodeGeneration.Helpers;
using Xunit;
using Xunit.Priority;
@@ -448,6 +449,83 @@ public void CanExecuteGenericOverloadedPrivateMethodClass()
}
}
+ [Fact, Priority(1)]
+ public void CanExecuteInheritedClass()
+ {
+ var student = TestClasses.Abstract.InheritedClass
+ .CreateStudent
+ .WithName("Alice")
+ .BornOn(new DateOnly(2002, 8, 3))
+ .InSemester(2);
+
+ Assert.Equal("Alice", student.Name);
+ Assert.Equal(new DateOnly(2002, 8, 3), student.DateOfBirth);
+ Assert.Equal(2, student.Semester);
+ }
+
+ [Fact, Priority(1)]
+ public void CanExecuteInheritedClassPrivateSetters()
+ {
+ var student = TestClasses.Abstract.InheritedClassPrivateSetters
+ .CreateStudent
+ .WithName("Alice")
+ .BornOn(new DateOnly(2002, 8, 3))
+ .InSemester(2);
+
+ Assert.Equal("Alice", student.Name);
+ Assert.Equal(22, student.Age);
+ Assert.Equal(2, student.Semester);
+ }
+
+ [Fact, Priority(1)]
+ public void CanExecuteInheritedClassProtectedMembers()
+ {
+ var student = TestClasses.Abstract.InheritedClassProtectedMembers
+ .CreateStudent
+ .WithName("Alice")
+ .BornOn(new DateOnly(2002, 8, 3))
+ .InSemester(2);
+
+ Assert.Equal("Alice", GetProperty("Name"));
+ Assert.Equal(new DateOnly(2002, 8, 3), GetProperty("DateOfBirth"));
+ Assert.Equal(2, GetProperty("Semester"));
+
+ object GetProperty(string propertyName)
+ {
+ return typeof(TestClasses.Abstract.InheritedClassProtectedMembers.Student)
+ .GetProperty(propertyName, BindingFlags.Instance | BindingFlags.NonPublic)?
+ .GetValue(student)!;
+ }
+ }
+
+ [Fact, Priority(1)]
+ public void CanExecuteInheritedClassProtectedSetters()
+ {
+ var student = TestClasses.Abstract.InheritedClassProtectedSetters
+ .CreateStudent
+ .WithName("Alice")
+ .BornOn(new DateOnly(2002, 8, 3))
+ .InSemester(2);
+
+ Assert.Equal("Alice", student.Name);
+ Assert.Equal(new DateOnly(2002, 8, 3), student.DateOfBirth);
+ Assert.Equal(2, student.Semester);
+ }
+
+ [Fact, Priority(1)]
+ public void CanExecuteInheritedRecord()
+ {
+ var student = TestClasses.Abstract.InheritedRecord
+ .CreateStudent
+ .WithName("Alice")
+ .BornOn(new DateOnly(2002, 8, 3))
+ .InSemester(2);
+
+ Assert.Equal("Alice", student.Name);
+ Assert.Equal(new DateOnly(2002, 8, 3), student.DateOfBirth);
+ Assert.Equal(2, student.Semester);
+ }
+
[Fact, Priority(1)]
public void CanExecutePartialClass()
{
diff --git a/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/InheritedClass/CreatePerson.g.cs b/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/InheritedClass/CreatePerson.g.cs
new file mode 100644
index 0000000..e0494f0
--- /dev/null
+++ b/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/InheritedClass/CreatePerson.g.cs
@@ -0,0 +1,62 @@
+//
+// This code was generated by the library M31.FluentAPI.
+// Changes to this file may cause incorrect behavior and will be lost if the code is regenerated.
+
+#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
+#nullable enable
+
+using System;
+using M31.FluentApi.Attributes;
+
+namespace M31.FluentApi.Tests.CodeGeneration.TestClasses.Abstract.InheritedClass;
+
+public class CreatePerson :
+ CreatePerson.ICreatePerson,
+ CreatePerson.IWithName,
+ CreatePerson.IBornOn
+{
+ private readonly Person person;
+
+ private CreatePerson()
+ {
+ person = new Person();
+ }
+
+ public static ICreatePerson InitialStep()
+ {
+ return new CreatePerson();
+ }
+
+ public static IBornOn WithName(string name)
+ {
+ CreatePerson createPerson = new CreatePerson();
+ createPerson.person.Name = name;
+ return createPerson;
+ }
+
+ IBornOn IWithName.WithName(string name)
+ {
+ person.Name = name;
+ return this;
+ }
+
+ Person IBornOn.BornOn(System.DateOnly dateOfBirth)
+ {
+ person.DateOfBirth = dateOfBirth;
+ return person;
+ }
+
+ public interface ICreatePerson : IWithName
+ {
+ }
+
+ public interface IWithName
+ {
+ IBornOn WithName(string name);
+ }
+
+ public interface IBornOn
+ {
+ Person BornOn(System.DateOnly dateOfBirth);
+ }
+}
\ No newline at end of file
diff --git a/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/InheritedClass/CreateStudent.expected.txt b/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/InheritedClass/CreateStudent.expected.txt
new file mode 100644
index 0000000..bccf1b4
--- /dev/null
+++ b/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/InheritedClass/CreateStudent.expected.txt
@@ -0,0 +1,74 @@
+//
+// This code was generated by the library M31.FluentAPI.
+// Changes to this file may cause incorrect behavior and will be lost if the code is regenerated.
+
+#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
+#nullable enable
+
+using System;
+using M31.FluentApi.Attributes;
+
+namespace M31.FluentApi.Tests.CodeGeneration.TestClasses.Abstract.InheritedClass;
+
+public class CreateStudent :
+ CreateStudent.ICreateStudent,
+ CreateStudent.IWithName,
+ CreateStudent.IBornOn,
+ CreateStudent.IInSemester
+{
+ private readonly Student student;
+
+ private CreateStudent()
+ {
+ student = new Student();
+ }
+
+ public static ICreateStudent InitialStep()
+ {
+ return new CreateStudent();
+ }
+
+ public static IBornOn WithName(string name)
+ {
+ CreateStudent createStudent = new CreateStudent();
+ createStudent.student.Name = name;
+ return createStudent;
+ }
+
+ IBornOn IWithName.WithName(string name)
+ {
+ student.Name = name;
+ return this;
+ }
+
+ IInSemester IBornOn.BornOn(System.DateOnly dateOfBirth)
+ {
+ student.DateOfBirth = dateOfBirth;
+ return this;
+ }
+
+ Student IInSemester.InSemester(int semester)
+ {
+ student.Semester = semester;
+ return student;
+ }
+
+ public interface ICreateStudent : IWithName
+ {
+ }
+
+ public interface IWithName
+ {
+ IBornOn WithName(string name);
+ }
+
+ public interface IBornOn
+ {
+ IInSemester BornOn(System.DateOnly dateOfBirth);
+ }
+
+ public interface IInSemester
+ {
+ Student InSemester(int semester);
+ }
+}
\ No newline at end of file
diff --git a/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/InheritedClass/CreateStudent.g.cs b/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/InheritedClass/CreateStudent.g.cs
new file mode 100644
index 0000000..bccf1b4
--- /dev/null
+++ b/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/InheritedClass/CreateStudent.g.cs
@@ -0,0 +1,74 @@
+//
+// This code was generated by the library M31.FluentAPI.
+// Changes to this file may cause incorrect behavior and will be lost if the code is regenerated.
+
+#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
+#nullable enable
+
+using System;
+using M31.FluentApi.Attributes;
+
+namespace M31.FluentApi.Tests.CodeGeneration.TestClasses.Abstract.InheritedClass;
+
+public class CreateStudent :
+ CreateStudent.ICreateStudent,
+ CreateStudent.IWithName,
+ CreateStudent.IBornOn,
+ CreateStudent.IInSemester
+{
+ private readonly Student student;
+
+ private CreateStudent()
+ {
+ student = new Student();
+ }
+
+ public static ICreateStudent InitialStep()
+ {
+ return new CreateStudent();
+ }
+
+ public static IBornOn WithName(string name)
+ {
+ CreateStudent createStudent = new CreateStudent();
+ createStudent.student.Name = name;
+ return createStudent;
+ }
+
+ IBornOn IWithName.WithName(string name)
+ {
+ student.Name = name;
+ return this;
+ }
+
+ IInSemester IBornOn.BornOn(System.DateOnly dateOfBirth)
+ {
+ student.DateOfBirth = dateOfBirth;
+ return this;
+ }
+
+ Student IInSemester.InSemester(int semester)
+ {
+ student.Semester = semester;
+ return student;
+ }
+
+ public interface ICreateStudent : IWithName
+ {
+ }
+
+ public interface IWithName
+ {
+ IBornOn WithName(string name);
+ }
+
+ public interface IBornOn
+ {
+ IInSemester BornOn(System.DateOnly dateOfBirth);
+ }
+
+ public interface IInSemester
+ {
+ Student InSemester(int semester);
+ }
+}
\ No newline at end of file
diff --git a/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/InheritedClass/Person.cs b/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/InheritedClass/Person.cs
new file mode 100644
index 0000000..9138a12
--- /dev/null
+++ b/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/InheritedClass/Person.cs
@@ -0,0 +1,18 @@
+// Non-nullable member is uninitialized
+#pragma warning disable CS8618
+// ReSharper disable All
+
+using System;
+using M31.FluentApi.Attributes;
+
+namespace M31.FluentApi.Tests.CodeGeneration.TestClasses.Abstract.InheritedClass;
+
+[FluentApi]
+public class Person
+{
+ [FluentMember(0, "WithName")]
+ public string Name { get; set; }
+
+ [FluentMember(1, "BornOn")]
+ public DateOnly DateOfBirth{ get; set; }
+}
\ No newline at end of file
diff --git a/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/InheritedClass/Student.cs b/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/InheritedClass/Student.cs
new file mode 100644
index 0000000..5635793
--- /dev/null
+++ b/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/InheritedClass/Student.cs
@@ -0,0 +1,15 @@
+// Non-nullable member is uninitialized
+#pragma warning disable CS8618
+// ReSharper disable All
+
+using System;
+using M31.FluentApi.Attributes;
+
+namespace M31.FluentApi.Tests.CodeGeneration.TestClasses.Abstract.InheritedClass;
+
+[FluentApi]
+public class Student : Person
+{
+ [FluentMember(2, "InSemester")]
+ public int Semester { get; set; }
+}
\ No newline at end of file
diff --git a/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/InheritedClassPrivateSetters/CreatePerson.g.cs b/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/InheritedClassPrivateSetters/CreatePerson.g.cs
new file mode 100644
index 0000000..f78dd93
--- /dev/null
+++ b/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/InheritedClassPrivateSetters/CreatePerson.g.cs
@@ -0,0 +1,87 @@
+//
+// This code was generated by the library M31.FluentAPI.
+// Changes to this file may cause incorrect behavior and will be lost if the code is regenerated.
+
+#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
+#nullable enable
+
+using System;
+using M31.FluentApi.Attributes;
+using System.Reflection;
+
+namespace M31.FluentApi.Tests.CodeGeneration.TestClasses.Abstract.InheritedClassPrivateSetters;
+
+public class CreatePerson :
+ CreatePerson.ICreatePerson,
+ CreatePerson.IWithName,
+ CreatePerson.IOfAgeBornOn
+{
+ private readonly Person person;
+ private static readonly PropertyInfo namePropertyInfo;
+ private static readonly PropertyInfo agePropertyInfo;
+ private static readonly MethodInfo bornOnMethodInfo;
+
+ static CreatePerson()
+ {
+ namePropertyInfo = typeof(Person).GetProperty("Name", BindingFlags.Instance | BindingFlags.Public)!;
+ agePropertyInfo = typeof(Person).GetProperty("Age", BindingFlags.Instance | BindingFlags.Public)!;
+ bornOnMethodInfo = typeof(Person).GetMethod(
+ "BornOn",
+ 0,
+ BindingFlags.Instance | BindingFlags.NonPublic,
+ null,
+ new Type[] { typeof(System.DateOnly) },
+ null)!;
+ }
+
+ private CreatePerson()
+ {
+ person = new Person();
+ }
+
+ public static ICreatePerson InitialStep()
+ {
+ return new CreatePerson();
+ }
+
+ public static IOfAgeBornOn WithName(string name)
+ {
+ CreatePerson createPerson = new CreatePerson();
+ CreatePerson.namePropertyInfo.SetValue(createPerson.person, name);
+ return createPerson;
+ }
+
+ IOfAgeBornOn IWithName.WithName(string name)
+ {
+ CreatePerson.namePropertyInfo.SetValue(person, name);
+ return this;
+ }
+
+ Person IOfAgeBornOn.OfAge(int age)
+ {
+ CreatePerson.agePropertyInfo.SetValue(person, age);
+ return person;
+ }
+
+ Person IOfAgeBornOn.BornOn(System.DateOnly dateOfBirth)
+ {
+ CreatePerson.bornOnMethodInfo.Invoke(person, new object?[] { dateOfBirth });
+ return person;
+ }
+
+ public interface ICreatePerson : IWithName
+ {
+ }
+
+ public interface IWithName
+ {
+ IOfAgeBornOn WithName(string name);
+ }
+
+ public interface IOfAgeBornOn
+ {
+ Person OfAge(int age);
+
+ Person BornOn(System.DateOnly dateOfBirth);
+ }
+}
\ No newline at end of file
diff --git a/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/InheritedClassPrivateSetters/CreateStudent.expected.txt b/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/InheritedClassPrivateSetters/CreateStudent.expected.txt
new file mode 100644
index 0000000..a4ca5b5
--- /dev/null
+++ b/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/InheritedClassPrivateSetters/CreateStudent.expected.txt
@@ -0,0 +1,101 @@
+//
+// This code was generated by the library M31.FluentAPI.
+// Changes to this file may cause incorrect behavior and will be lost if the code is regenerated.
+
+#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
+#nullable enable
+
+using System;
+using M31.FluentApi.Attributes;
+using System.Reflection;
+
+namespace M31.FluentApi.Tests.CodeGeneration.TestClasses.Abstract.InheritedClassPrivateSetters;
+
+public class CreateStudent :
+ CreateStudent.ICreateStudent,
+ CreateStudent.IWithName,
+ CreateStudent.IOfAgeBornOn,
+ CreateStudent.IInSemester
+{
+ private readonly Student student;
+ private static readonly PropertyInfo semesterPropertyInfo;
+ private static readonly PropertyInfo namePropertyInfo;
+ private static readonly PropertyInfo agePropertyInfo;
+ private static readonly MethodInfo bornOnMethodInfo;
+
+ static CreateStudent()
+ {
+ semesterPropertyInfo = typeof(Student).GetProperty("Semester", BindingFlags.Instance | BindingFlags.Public)!;
+ namePropertyInfo = typeof(Person).GetProperty("Name", BindingFlags.Instance | BindingFlags.Public)!;
+ agePropertyInfo = typeof(Person).GetProperty("Age", BindingFlags.Instance | BindingFlags.Public)!;
+ bornOnMethodInfo = typeof(Person).GetMethod(
+ "BornOn",
+ 0,
+ BindingFlags.Instance | BindingFlags.NonPublic,
+ null,
+ new Type[] { typeof(System.DateOnly) },
+ null)!;
+ }
+
+ private CreateStudent()
+ {
+ student = new Student();
+ }
+
+ public static ICreateStudent InitialStep()
+ {
+ return new CreateStudent();
+ }
+
+ public static IOfAgeBornOn WithName(string name)
+ {
+ CreateStudent createStudent = new CreateStudent();
+ CreateStudent.namePropertyInfo.SetValue(createStudent.student, name);
+ return createStudent;
+ }
+
+ IOfAgeBornOn IWithName.WithName(string name)
+ {
+ CreateStudent.namePropertyInfo.SetValue(student, name);
+ return this;
+ }
+
+ IInSemester IOfAgeBornOn.OfAge(int age)
+ {
+ CreateStudent.agePropertyInfo.SetValue(student, age);
+ return this;
+ }
+
+ IInSemester IOfAgeBornOn.BornOn(System.DateOnly dateOfBirth)
+ {
+ CreateStudent.bornOnMethodInfo.Invoke(student, new object?[] { dateOfBirth });
+ return this;
+ }
+
+ Student IInSemester.InSemester(int semester)
+ {
+ CreateStudent.semesterPropertyInfo.SetValue(student, semester);
+ return student;
+ }
+
+ public interface ICreateStudent : IWithName
+ {
+ }
+
+ public interface IWithName
+ {
+ IOfAgeBornOn WithName(string name);
+ }
+
+ public interface IOfAgeBornOn
+ {
+ IInSemester OfAge(int age);
+
+ IInSemester BornOn(System.DateOnly dateOfBirth);
+ }
+
+ public interface IInSemester
+ {
+ Student InSemester(int semester);
+ }
+}
\ No newline at end of file
diff --git a/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/InheritedClassPrivateSetters/CreateStudent.g.cs b/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/InheritedClassPrivateSetters/CreateStudent.g.cs
new file mode 100644
index 0000000..a4ca5b5
--- /dev/null
+++ b/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/InheritedClassPrivateSetters/CreateStudent.g.cs
@@ -0,0 +1,101 @@
+//
+// This code was generated by the library M31.FluentAPI.
+// Changes to this file may cause incorrect behavior and will be lost if the code is regenerated.
+
+#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
+#nullable enable
+
+using System;
+using M31.FluentApi.Attributes;
+using System.Reflection;
+
+namespace M31.FluentApi.Tests.CodeGeneration.TestClasses.Abstract.InheritedClassPrivateSetters;
+
+public class CreateStudent :
+ CreateStudent.ICreateStudent,
+ CreateStudent.IWithName,
+ CreateStudent.IOfAgeBornOn,
+ CreateStudent.IInSemester
+{
+ private readonly Student student;
+ private static readonly PropertyInfo semesterPropertyInfo;
+ private static readonly PropertyInfo namePropertyInfo;
+ private static readonly PropertyInfo agePropertyInfo;
+ private static readonly MethodInfo bornOnMethodInfo;
+
+ static CreateStudent()
+ {
+ semesterPropertyInfo = typeof(Student).GetProperty("Semester", BindingFlags.Instance | BindingFlags.Public)!;
+ namePropertyInfo = typeof(Person).GetProperty("Name", BindingFlags.Instance | BindingFlags.Public)!;
+ agePropertyInfo = typeof(Person).GetProperty("Age", BindingFlags.Instance | BindingFlags.Public)!;
+ bornOnMethodInfo = typeof(Person).GetMethod(
+ "BornOn",
+ 0,
+ BindingFlags.Instance | BindingFlags.NonPublic,
+ null,
+ new Type[] { typeof(System.DateOnly) },
+ null)!;
+ }
+
+ private CreateStudent()
+ {
+ student = new Student();
+ }
+
+ public static ICreateStudent InitialStep()
+ {
+ return new CreateStudent();
+ }
+
+ public static IOfAgeBornOn WithName(string name)
+ {
+ CreateStudent createStudent = new CreateStudent();
+ CreateStudent.namePropertyInfo.SetValue(createStudent.student, name);
+ return createStudent;
+ }
+
+ IOfAgeBornOn IWithName.WithName(string name)
+ {
+ CreateStudent.namePropertyInfo.SetValue(student, name);
+ return this;
+ }
+
+ IInSemester IOfAgeBornOn.OfAge(int age)
+ {
+ CreateStudent.agePropertyInfo.SetValue(student, age);
+ return this;
+ }
+
+ IInSemester IOfAgeBornOn.BornOn(System.DateOnly dateOfBirth)
+ {
+ CreateStudent.bornOnMethodInfo.Invoke(student, new object?[] { dateOfBirth });
+ return this;
+ }
+
+ Student IInSemester.InSemester(int semester)
+ {
+ CreateStudent.semesterPropertyInfo.SetValue(student, semester);
+ return student;
+ }
+
+ public interface ICreateStudent : IWithName
+ {
+ }
+
+ public interface IWithName
+ {
+ IOfAgeBornOn WithName(string name);
+ }
+
+ public interface IOfAgeBornOn
+ {
+ IInSemester OfAge(int age);
+
+ IInSemester BornOn(System.DateOnly dateOfBirth);
+ }
+
+ public interface IInSemester
+ {
+ Student InSemester(int semester);
+ }
+}
\ No newline at end of file
diff --git a/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/InheritedClassPrivateSetters/Person.cs b/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/InheritedClassPrivateSetters/Person.cs
new file mode 100644
index 0000000..195b03f
--- /dev/null
+++ b/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/InheritedClassPrivateSetters/Person.cs
@@ -0,0 +1,27 @@
+// Non-nullable member is uninitialized
+#pragma warning disable CS8618
+// ReSharper disable All
+
+using System;
+using M31.FluentApi.Attributes;
+
+namespace M31.FluentApi.Tests.CodeGeneration.TestClasses.Abstract.InheritedClassPrivateSetters;
+
+[FluentApi]
+public class Person
+{
+ [FluentMember(0, "WithName")]
+ public string Name { get; private set; }
+
+ [FluentMember(1, "OfAge")]
+ public int Age { get; private set; }
+
+ [FluentMethod(1)]
+ private void BornOn(DateOnly dateOfBirth)
+ {
+ DateOnly today = new DateOnly(2024, 9, 26);
+ int age = today.Year - dateOfBirth.Year;
+ if (dateOfBirth > today.AddYears(-age)) age--;
+ Age = age;
+ }
+}
\ No newline at end of file
diff --git a/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/InheritedClassPrivateSetters/Student.cs b/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/InheritedClassPrivateSetters/Student.cs
new file mode 100644
index 0000000..2d75afb
--- /dev/null
+++ b/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/InheritedClassPrivateSetters/Student.cs
@@ -0,0 +1,15 @@
+// Non-nullable member is uninitialized
+#pragma warning disable CS8618
+// ReSharper disable All
+
+using System;
+using M31.FluentApi.Attributes;
+
+namespace M31.FluentApi.Tests.CodeGeneration.TestClasses.Abstract.InheritedClassPrivateSetters;
+
+[FluentApi]
+public class Student : Person
+{
+ [FluentMember(2, "InSemester")]
+ public int Semester { get; private set; }
+}
\ No newline at end of file
diff --git a/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/InheritedClassProtectedMembers/CreatePerson.g.cs b/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/InheritedClassProtectedMembers/CreatePerson.g.cs
new file mode 100644
index 0000000..e429f72
--- /dev/null
+++ b/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/InheritedClassProtectedMembers/CreatePerson.g.cs
@@ -0,0 +1,71 @@
+//
+// This code was generated by the library M31.FluentAPI.
+// Changes to this file may cause incorrect behavior and will be lost if the code is regenerated.
+
+#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
+#nullable enable
+
+using System;
+using M31.FluentApi.Attributes;
+using System.Reflection;
+
+namespace M31.FluentApi.Tests.CodeGeneration.TestClasses.Abstract.InheritedClassProtectedMembers;
+
+public class CreatePerson :
+ CreatePerson.ICreatePerson,
+ CreatePerson.IWithName,
+ CreatePerson.IBornOn
+{
+ private readonly Person person;
+ private static readonly PropertyInfo namePropertyInfo;
+ private static readonly PropertyInfo dateOfBirthPropertyInfo;
+
+ static CreatePerson()
+ {
+ namePropertyInfo = typeof(Person).GetProperty("Name", BindingFlags.Instance | BindingFlags.NonPublic)!;
+ dateOfBirthPropertyInfo = typeof(Person).GetProperty("DateOfBirth", BindingFlags.Instance | BindingFlags.NonPublic)!;
+ }
+
+ private CreatePerson()
+ {
+ person = new Person();
+ }
+
+ public static ICreatePerson InitialStep()
+ {
+ return new CreatePerson();
+ }
+
+ public static IBornOn WithName(string name)
+ {
+ CreatePerson createPerson = new CreatePerson();
+ CreatePerson.namePropertyInfo.SetValue(createPerson.person, name);
+ return createPerson;
+ }
+
+ IBornOn IWithName.WithName(string name)
+ {
+ CreatePerson.namePropertyInfo.SetValue(person, name);
+ return this;
+ }
+
+ Person IBornOn.BornOn(System.DateOnly dateOfBirth)
+ {
+ CreatePerson.dateOfBirthPropertyInfo.SetValue(person, dateOfBirth);
+ return person;
+ }
+
+ public interface ICreatePerson : IWithName
+ {
+ }
+
+ public interface IWithName
+ {
+ IBornOn WithName(string name);
+ }
+
+ public interface IBornOn
+ {
+ Person BornOn(System.DateOnly dateOfBirth);
+ }
+}
\ No newline at end of file
diff --git a/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/InheritedClassProtectedMembers/CreateStudent.expected.txt b/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/InheritedClassProtectedMembers/CreateStudent.expected.txt
new file mode 100644
index 0000000..268142c
--- /dev/null
+++ b/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/InheritedClassProtectedMembers/CreateStudent.expected.txt
@@ -0,0 +1,85 @@
+//
+// This code was generated by the library M31.FluentAPI.
+// Changes to this file may cause incorrect behavior and will be lost if the code is regenerated.
+
+#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
+#nullable enable
+
+using System;
+using M31.FluentApi.Attributes;
+using System.Reflection;
+
+namespace M31.FluentApi.Tests.CodeGeneration.TestClasses.Abstract.InheritedClassProtectedMembers;
+
+public class CreateStudent :
+ CreateStudent.ICreateStudent,
+ CreateStudent.IWithName,
+ CreateStudent.IBornOn,
+ CreateStudent.IInSemester
+{
+ private readonly Student student;
+ private static readonly PropertyInfo semesterPropertyInfo;
+ private static readonly PropertyInfo namePropertyInfo;
+ private static readonly PropertyInfo dateOfBirthPropertyInfo;
+
+ static CreateStudent()
+ {
+ semesterPropertyInfo = typeof(Student).GetProperty("Semester", BindingFlags.Instance | BindingFlags.NonPublic)!;
+ namePropertyInfo = typeof(Person).GetProperty("Name", BindingFlags.Instance | BindingFlags.NonPublic)!;
+ dateOfBirthPropertyInfo = typeof(Person).GetProperty("DateOfBirth", BindingFlags.Instance | BindingFlags.NonPublic)!;
+ }
+
+ private CreateStudent()
+ {
+ student = new Student();
+ }
+
+ public static ICreateStudent InitialStep()
+ {
+ return new CreateStudent();
+ }
+
+ public static IBornOn WithName(string name)
+ {
+ CreateStudent createStudent = new CreateStudent();
+ CreateStudent.namePropertyInfo.SetValue(createStudent.student, name);
+ return createStudent;
+ }
+
+ IBornOn IWithName.WithName(string name)
+ {
+ CreateStudent.namePropertyInfo.SetValue(student, name);
+ return this;
+ }
+
+ IInSemester IBornOn.BornOn(System.DateOnly dateOfBirth)
+ {
+ CreateStudent.dateOfBirthPropertyInfo.SetValue(student, dateOfBirth);
+ return this;
+ }
+
+ Student IInSemester.InSemester(int semester)
+ {
+ CreateStudent.semesterPropertyInfo.SetValue(student, semester);
+ return student;
+ }
+
+ public interface ICreateStudent : IWithName
+ {
+ }
+
+ public interface IWithName
+ {
+ IBornOn WithName(string name);
+ }
+
+ public interface IBornOn
+ {
+ IInSemester BornOn(System.DateOnly dateOfBirth);
+ }
+
+ public interface IInSemester
+ {
+ Student InSemester(int semester);
+ }
+}
\ No newline at end of file
diff --git a/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/InheritedClassProtectedMembers/CreateStudent.g.cs b/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/InheritedClassProtectedMembers/CreateStudent.g.cs
new file mode 100644
index 0000000..268142c
--- /dev/null
+++ b/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/InheritedClassProtectedMembers/CreateStudent.g.cs
@@ -0,0 +1,85 @@
+//
+// This code was generated by the library M31.FluentAPI.
+// Changes to this file may cause incorrect behavior and will be lost if the code is regenerated.
+
+#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
+#nullable enable
+
+using System;
+using M31.FluentApi.Attributes;
+using System.Reflection;
+
+namespace M31.FluentApi.Tests.CodeGeneration.TestClasses.Abstract.InheritedClassProtectedMembers;
+
+public class CreateStudent :
+ CreateStudent.ICreateStudent,
+ CreateStudent.IWithName,
+ CreateStudent.IBornOn,
+ CreateStudent.IInSemester
+{
+ private readonly Student student;
+ private static readonly PropertyInfo semesterPropertyInfo;
+ private static readonly PropertyInfo namePropertyInfo;
+ private static readonly PropertyInfo dateOfBirthPropertyInfo;
+
+ static CreateStudent()
+ {
+ semesterPropertyInfo = typeof(Student).GetProperty("Semester", BindingFlags.Instance | BindingFlags.NonPublic)!;
+ namePropertyInfo = typeof(Person).GetProperty("Name", BindingFlags.Instance | BindingFlags.NonPublic)!;
+ dateOfBirthPropertyInfo = typeof(Person).GetProperty("DateOfBirth", BindingFlags.Instance | BindingFlags.NonPublic)!;
+ }
+
+ private CreateStudent()
+ {
+ student = new Student();
+ }
+
+ public static ICreateStudent InitialStep()
+ {
+ return new CreateStudent();
+ }
+
+ public static IBornOn WithName(string name)
+ {
+ CreateStudent createStudent = new CreateStudent();
+ CreateStudent.namePropertyInfo.SetValue(createStudent.student, name);
+ return createStudent;
+ }
+
+ IBornOn IWithName.WithName(string name)
+ {
+ CreateStudent.namePropertyInfo.SetValue(student, name);
+ return this;
+ }
+
+ IInSemester IBornOn.BornOn(System.DateOnly dateOfBirth)
+ {
+ CreateStudent.dateOfBirthPropertyInfo.SetValue(student, dateOfBirth);
+ return this;
+ }
+
+ Student IInSemester.InSemester(int semester)
+ {
+ CreateStudent.semesterPropertyInfo.SetValue(student, semester);
+ return student;
+ }
+
+ public interface ICreateStudent : IWithName
+ {
+ }
+
+ public interface IWithName
+ {
+ IBornOn WithName(string name);
+ }
+
+ public interface IBornOn
+ {
+ IInSemester BornOn(System.DateOnly dateOfBirth);
+ }
+
+ public interface IInSemester
+ {
+ Student InSemester(int semester);
+ }
+}
\ No newline at end of file
diff --git a/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/InheritedClassProtectedMembers/Person.cs b/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/InheritedClassProtectedMembers/Person.cs
new file mode 100644
index 0000000..8fb6b94
--- /dev/null
+++ b/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/InheritedClassProtectedMembers/Person.cs
@@ -0,0 +1,18 @@
+// Non-nullable member is uninitialized
+#pragma warning disable CS8618
+// ReSharper disable All
+
+using System;
+using M31.FluentApi.Attributes;
+
+namespace M31.FluentApi.Tests.CodeGeneration.TestClasses.Abstract.InheritedClassProtectedMembers;
+
+[FluentApi]
+public class Person
+{
+ [FluentMember(0, "WithName")]
+ protected string Name { get; set; }
+
+ [FluentMember(1, "BornOn")]
+ protected DateOnly DateOfBirth{ get; set; }
+}
\ No newline at end of file
diff --git a/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/InheritedClassProtectedMembers/Student.cs b/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/InheritedClassProtectedMembers/Student.cs
new file mode 100644
index 0000000..dbee594
--- /dev/null
+++ b/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/InheritedClassProtectedMembers/Student.cs
@@ -0,0 +1,15 @@
+// Non-nullable member is uninitialized
+#pragma warning disable CS8618
+// ReSharper disable All
+
+using System;
+using M31.FluentApi.Attributes;
+
+namespace M31.FluentApi.Tests.CodeGeneration.TestClasses.Abstract.InheritedClassProtectedMembers;
+
+[FluentApi]
+public class Student : Person
+{
+ [FluentMember(2, "InSemester")]
+ protected int Semester { get; set; }
+}
\ No newline at end of file
diff --git a/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/InheritedClassProtectedSetters/CreatePerson.g.cs b/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/InheritedClassProtectedSetters/CreatePerson.g.cs
new file mode 100644
index 0000000..ed0341a
--- /dev/null
+++ b/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/InheritedClassProtectedSetters/CreatePerson.g.cs
@@ -0,0 +1,71 @@
+//
+// This code was generated by the library M31.FluentAPI.
+// Changes to this file may cause incorrect behavior and will be lost if the code is regenerated.
+
+#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
+#nullable enable
+
+using System;
+using M31.FluentApi.Attributes;
+using System.Reflection;
+
+namespace M31.FluentApi.Tests.CodeGeneration.TestClasses.Abstract.InheritedClassProtectedSetters;
+
+public class CreatePerson :
+ CreatePerson.ICreatePerson,
+ CreatePerson.IWithName,
+ CreatePerson.IBornOn
+{
+ private readonly Person person;
+ private static readonly PropertyInfo namePropertyInfo;
+ private static readonly PropertyInfo dateOfBirthPropertyInfo;
+
+ static CreatePerson()
+ {
+ namePropertyInfo = typeof(Person).GetProperty("Name", BindingFlags.Instance | BindingFlags.Public)!;
+ dateOfBirthPropertyInfo = typeof(Person).GetProperty("DateOfBirth", BindingFlags.Instance | BindingFlags.Public)!;
+ }
+
+ private CreatePerson()
+ {
+ person = new Person();
+ }
+
+ public static ICreatePerson InitialStep()
+ {
+ return new CreatePerson();
+ }
+
+ public static IBornOn WithName(string name)
+ {
+ CreatePerson createPerson = new CreatePerson();
+ CreatePerson.namePropertyInfo.SetValue(createPerson.person, name);
+ return createPerson;
+ }
+
+ IBornOn IWithName.WithName(string name)
+ {
+ CreatePerson.namePropertyInfo.SetValue(person, name);
+ return this;
+ }
+
+ Person IBornOn.BornOn(System.DateOnly dateOfBirth)
+ {
+ CreatePerson.dateOfBirthPropertyInfo.SetValue(person, dateOfBirth);
+ return person;
+ }
+
+ public interface ICreatePerson : IWithName
+ {
+ }
+
+ public interface IWithName
+ {
+ IBornOn WithName(string name);
+ }
+
+ public interface IBornOn
+ {
+ Person BornOn(System.DateOnly dateOfBirth);
+ }
+}
\ No newline at end of file
diff --git a/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/InheritedClassProtectedSetters/CreateStudent.expected.txt b/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/InheritedClassProtectedSetters/CreateStudent.expected.txt
new file mode 100644
index 0000000..f48c704
--- /dev/null
+++ b/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/InheritedClassProtectedSetters/CreateStudent.expected.txt
@@ -0,0 +1,85 @@
+//
+// This code was generated by the library M31.FluentAPI.
+// Changes to this file may cause incorrect behavior and will be lost if the code is regenerated.
+
+#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
+#nullable enable
+
+using System;
+using M31.FluentApi.Attributes;
+using System.Reflection;
+
+namespace M31.FluentApi.Tests.CodeGeneration.TestClasses.Abstract.InheritedClassProtectedSetters;
+
+public class CreateStudent :
+ CreateStudent.ICreateStudent,
+ CreateStudent.IWithName,
+ CreateStudent.IBornOn,
+ CreateStudent.IInSemester
+{
+ private readonly Student student;
+ private static readonly PropertyInfo semesterPropertyInfo;
+ private static readonly PropertyInfo namePropertyInfo;
+ private static readonly PropertyInfo dateOfBirthPropertyInfo;
+
+ static CreateStudent()
+ {
+ semesterPropertyInfo = typeof(Student).GetProperty("Semester", BindingFlags.Instance | BindingFlags.Public)!;
+ namePropertyInfo = typeof(Person).GetProperty("Name", BindingFlags.Instance | BindingFlags.Public)!;
+ dateOfBirthPropertyInfo = typeof(Person).GetProperty("DateOfBirth", BindingFlags.Instance | BindingFlags.Public)!;
+ }
+
+ private CreateStudent()
+ {
+ student = new Student();
+ }
+
+ public static ICreateStudent InitialStep()
+ {
+ return new CreateStudent();
+ }
+
+ public static IBornOn WithName(string name)
+ {
+ CreateStudent createStudent = new CreateStudent();
+ CreateStudent.namePropertyInfo.SetValue(createStudent.student, name);
+ return createStudent;
+ }
+
+ IBornOn IWithName.WithName(string name)
+ {
+ CreateStudent.namePropertyInfo.SetValue(student, name);
+ return this;
+ }
+
+ IInSemester IBornOn.BornOn(System.DateOnly dateOfBirth)
+ {
+ CreateStudent.dateOfBirthPropertyInfo.SetValue(student, dateOfBirth);
+ return this;
+ }
+
+ Student IInSemester.InSemester(int semester)
+ {
+ CreateStudent.semesterPropertyInfo.SetValue(student, semester);
+ return student;
+ }
+
+ public interface ICreateStudent : IWithName
+ {
+ }
+
+ public interface IWithName
+ {
+ IBornOn WithName(string name);
+ }
+
+ public interface IBornOn
+ {
+ IInSemester BornOn(System.DateOnly dateOfBirth);
+ }
+
+ public interface IInSemester
+ {
+ Student InSemester(int semester);
+ }
+}
\ No newline at end of file
diff --git a/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/InheritedClassProtectedSetters/CreateStudent.g.cs b/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/InheritedClassProtectedSetters/CreateStudent.g.cs
new file mode 100644
index 0000000..f48c704
--- /dev/null
+++ b/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/InheritedClassProtectedSetters/CreateStudent.g.cs
@@ -0,0 +1,85 @@
+//
+// This code was generated by the library M31.FluentAPI.
+// Changes to this file may cause incorrect behavior and will be lost if the code is regenerated.
+
+#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
+#nullable enable
+
+using System;
+using M31.FluentApi.Attributes;
+using System.Reflection;
+
+namespace M31.FluentApi.Tests.CodeGeneration.TestClasses.Abstract.InheritedClassProtectedSetters;
+
+public class CreateStudent :
+ CreateStudent.ICreateStudent,
+ CreateStudent.IWithName,
+ CreateStudent.IBornOn,
+ CreateStudent.IInSemester
+{
+ private readonly Student student;
+ private static readonly PropertyInfo semesterPropertyInfo;
+ private static readonly PropertyInfo namePropertyInfo;
+ private static readonly PropertyInfo dateOfBirthPropertyInfo;
+
+ static CreateStudent()
+ {
+ semesterPropertyInfo = typeof(Student).GetProperty("Semester", BindingFlags.Instance | BindingFlags.Public)!;
+ namePropertyInfo = typeof(Person).GetProperty("Name", BindingFlags.Instance | BindingFlags.Public)!;
+ dateOfBirthPropertyInfo = typeof(Person).GetProperty("DateOfBirth", BindingFlags.Instance | BindingFlags.Public)!;
+ }
+
+ private CreateStudent()
+ {
+ student = new Student();
+ }
+
+ public static ICreateStudent InitialStep()
+ {
+ return new CreateStudent();
+ }
+
+ public static IBornOn WithName(string name)
+ {
+ CreateStudent createStudent = new CreateStudent();
+ CreateStudent.namePropertyInfo.SetValue(createStudent.student, name);
+ return createStudent;
+ }
+
+ IBornOn IWithName.WithName(string name)
+ {
+ CreateStudent.namePropertyInfo.SetValue(student, name);
+ return this;
+ }
+
+ IInSemester IBornOn.BornOn(System.DateOnly dateOfBirth)
+ {
+ CreateStudent.dateOfBirthPropertyInfo.SetValue(student, dateOfBirth);
+ return this;
+ }
+
+ Student IInSemester.InSemester(int semester)
+ {
+ CreateStudent.semesterPropertyInfo.SetValue(student, semester);
+ return student;
+ }
+
+ public interface ICreateStudent : IWithName
+ {
+ }
+
+ public interface IWithName
+ {
+ IBornOn WithName(string name);
+ }
+
+ public interface IBornOn
+ {
+ IInSemester BornOn(System.DateOnly dateOfBirth);
+ }
+
+ public interface IInSemester
+ {
+ Student InSemester(int semester);
+ }
+}
\ No newline at end of file
diff --git a/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/InheritedClassProtectedSetters/Person.cs b/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/InheritedClassProtectedSetters/Person.cs
new file mode 100644
index 0000000..e5206b2
--- /dev/null
+++ b/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/InheritedClassProtectedSetters/Person.cs
@@ -0,0 +1,18 @@
+// Non-nullable member is uninitialized
+#pragma warning disable CS8618
+// ReSharper disable All
+
+using System;
+using M31.FluentApi.Attributes;
+
+namespace M31.FluentApi.Tests.CodeGeneration.TestClasses.Abstract.InheritedClassProtectedSetters;
+
+[FluentApi]
+public class Person
+{
+ [FluentMember(0, "WithName")]
+ public string Name { get; protected set; }
+
+ [FluentMember(1, "BornOn")]
+ public DateOnly DateOfBirth{ get; protected set; }
+}
\ No newline at end of file
diff --git a/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/InheritedClassProtectedSetters/Student.cs b/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/InheritedClassProtectedSetters/Student.cs
new file mode 100644
index 0000000..b20c6b4
--- /dev/null
+++ b/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/InheritedClassProtectedSetters/Student.cs
@@ -0,0 +1,15 @@
+// Non-nullable member is uninitialized
+#pragma warning disable CS8618
+// ReSharper disable All
+
+using System;
+using M31.FluentApi.Attributes;
+
+namespace M31.FluentApi.Tests.CodeGeneration.TestClasses.Abstract.InheritedClassProtectedSetters;
+
+[FluentApi]
+public class Student : Person
+{
+ [FluentMember(2, "InSemester")]
+ public int Semester { get; protected set; }
+}
\ No newline at end of file
diff --git a/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/InheritedRecord/CreatePerson.g.cs b/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/InheritedRecord/CreatePerson.g.cs
new file mode 100644
index 0000000..168a7a5
--- /dev/null
+++ b/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/InheritedRecord/CreatePerson.g.cs
@@ -0,0 +1,71 @@
+//
+// This code was generated by the library M31.FluentAPI.
+// Changes to this file may cause incorrect behavior and will be lost if the code is regenerated.
+
+#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
+#nullable enable
+
+using System;
+using M31.FluentApi.Attributes;
+using System.Reflection;
+
+namespace M31.FluentApi.Tests.CodeGeneration.TestClasses.Abstract.InheritedRecord;
+
+public class CreatePerson :
+ CreatePerson.ICreatePerson,
+ CreatePerson.IWithName,
+ CreatePerson.IBornOn
+{
+ private readonly Person person;
+ private static readonly PropertyInfo namePropertyInfo;
+ private static readonly PropertyInfo dateOfBirthPropertyInfo;
+
+ static CreatePerson()
+ {
+ namePropertyInfo = typeof(Person).GetProperty("Name", BindingFlags.Instance | BindingFlags.Public)!;
+ dateOfBirthPropertyInfo = typeof(Person).GetProperty("DateOfBirth", BindingFlags.Instance | BindingFlags.Public)!;
+ }
+
+ private CreatePerson()
+ {
+ person = new Person(default!, default!);
+ }
+
+ public static ICreatePerson InitialStep()
+ {
+ return new CreatePerson();
+ }
+
+ public static IBornOn WithName(string name)
+ {
+ CreatePerson createPerson = new CreatePerson();
+ CreatePerson.namePropertyInfo.SetValue(createPerson.person, name);
+ return createPerson;
+ }
+
+ IBornOn IWithName.WithName(string name)
+ {
+ CreatePerson.namePropertyInfo.SetValue(person, name);
+ return this;
+ }
+
+ Person IBornOn.BornOn(System.DateOnly dateOfBirth)
+ {
+ CreatePerson.dateOfBirthPropertyInfo.SetValue(person, dateOfBirth);
+ return person;
+ }
+
+ public interface ICreatePerson : IWithName
+ {
+ }
+
+ public interface IWithName
+ {
+ IBornOn WithName(string name);
+ }
+
+ public interface IBornOn
+ {
+ Person BornOn(System.DateOnly dateOfBirth);
+ }
+}
\ No newline at end of file
diff --git a/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/InheritedRecord/CreateStudent.expected.txt b/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/InheritedRecord/CreateStudent.expected.txt
new file mode 100644
index 0000000..c3c4286
--- /dev/null
+++ b/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/InheritedRecord/CreateStudent.expected.txt
@@ -0,0 +1,85 @@
+//
+// This code was generated by the library M31.FluentAPI.
+// Changes to this file may cause incorrect behavior and will be lost if the code is regenerated.
+
+#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
+#nullable enable
+
+using System;
+using M31.FluentApi.Attributes;
+using System.Reflection;
+
+namespace M31.FluentApi.Tests.CodeGeneration.TestClasses.Abstract.InheritedRecord;
+
+public class CreateStudent :
+ CreateStudent.ICreateStudent,
+ CreateStudent.IWithName,
+ CreateStudent.IBornOn,
+ CreateStudent.IInSemester
+{
+ private readonly Student student;
+ private static readonly PropertyInfo semesterPropertyInfo;
+ private static readonly PropertyInfo namePropertyInfo;
+ private static readonly PropertyInfo dateOfBirthPropertyInfo;
+
+ static CreateStudent()
+ {
+ semesterPropertyInfo = typeof(Student).GetProperty("Semester", BindingFlags.Instance | BindingFlags.Public)!;
+ namePropertyInfo = typeof(Person).GetProperty("Name", BindingFlags.Instance | BindingFlags.Public)!;
+ dateOfBirthPropertyInfo = typeof(Person).GetProperty("DateOfBirth", BindingFlags.Instance | BindingFlags.Public)!;
+ }
+
+ private CreateStudent()
+ {
+ student = new Student(default!, default!, default!);
+ }
+
+ public static ICreateStudent InitialStep()
+ {
+ return new CreateStudent();
+ }
+
+ public static IBornOn WithName(string name)
+ {
+ CreateStudent createStudent = new CreateStudent();
+ CreateStudent.namePropertyInfo.SetValue(createStudent.student, name);
+ return createStudent;
+ }
+
+ IBornOn IWithName.WithName(string name)
+ {
+ CreateStudent.namePropertyInfo.SetValue(student, name);
+ return this;
+ }
+
+ IInSemester IBornOn.BornOn(System.DateOnly dateOfBirth)
+ {
+ CreateStudent.dateOfBirthPropertyInfo.SetValue(student, dateOfBirth);
+ return this;
+ }
+
+ Student IInSemester.InSemester(int semester)
+ {
+ CreateStudent.semesterPropertyInfo.SetValue(student, semester);
+ return student;
+ }
+
+ public interface ICreateStudent : IWithName
+ {
+ }
+
+ public interface IWithName
+ {
+ IBornOn WithName(string name);
+ }
+
+ public interface IBornOn
+ {
+ IInSemester BornOn(System.DateOnly dateOfBirth);
+ }
+
+ public interface IInSemester
+ {
+ Student InSemester(int semester);
+ }
+}
\ No newline at end of file
diff --git a/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/InheritedRecord/CreateStudent.g.cs b/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/InheritedRecord/CreateStudent.g.cs
new file mode 100644
index 0000000..c3c4286
--- /dev/null
+++ b/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/InheritedRecord/CreateStudent.g.cs
@@ -0,0 +1,85 @@
+//
+// This code was generated by the library M31.FluentAPI.
+// Changes to this file may cause incorrect behavior and will be lost if the code is regenerated.
+
+#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
+#nullable enable
+
+using System;
+using M31.FluentApi.Attributes;
+using System.Reflection;
+
+namespace M31.FluentApi.Tests.CodeGeneration.TestClasses.Abstract.InheritedRecord;
+
+public class CreateStudent :
+ CreateStudent.ICreateStudent,
+ CreateStudent.IWithName,
+ CreateStudent.IBornOn,
+ CreateStudent.IInSemester
+{
+ private readonly Student student;
+ private static readonly PropertyInfo semesterPropertyInfo;
+ private static readonly PropertyInfo namePropertyInfo;
+ private static readonly PropertyInfo dateOfBirthPropertyInfo;
+
+ static CreateStudent()
+ {
+ semesterPropertyInfo = typeof(Student).GetProperty("Semester", BindingFlags.Instance | BindingFlags.Public)!;
+ namePropertyInfo = typeof(Person).GetProperty("Name", BindingFlags.Instance | BindingFlags.Public)!;
+ dateOfBirthPropertyInfo = typeof(Person).GetProperty("DateOfBirth", BindingFlags.Instance | BindingFlags.Public)!;
+ }
+
+ private CreateStudent()
+ {
+ student = new Student(default!, default!, default!);
+ }
+
+ public static ICreateStudent InitialStep()
+ {
+ return new CreateStudent();
+ }
+
+ public static IBornOn WithName(string name)
+ {
+ CreateStudent createStudent = new CreateStudent();
+ CreateStudent.namePropertyInfo.SetValue(createStudent.student, name);
+ return createStudent;
+ }
+
+ IBornOn IWithName.WithName(string name)
+ {
+ CreateStudent.namePropertyInfo.SetValue(student, name);
+ return this;
+ }
+
+ IInSemester IBornOn.BornOn(System.DateOnly dateOfBirth)
+ {
+ CreateStudent.dateOfBirthPropertyInfo.SetValue(student, dateOfBirth);
+ return this;
+ }
+
+ Student IInSemester.InSemester(int semester)
+ {
+ CreateStudent.semesterPropertyInfo.SetValue(student, semester);
+ return student;
+ }
+
+ public interface ICreateStudent : IWithName
+ {
+ }
+
+ public interface IWithName
+ {
+ IBornOn WithName(string name);
+ }
+
+ public interface IBornOn
+ {
+ IInSemester BornOn(System.DateOnly dateOfBirth);
+ }
+
+ public interface IInSemester
+ {
+ Student InSemester(int semester);
+ }
+}
\ No newline at end of file
diff --git a/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/InheritedRecord/Person.cs b/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/InheritedRecord/Person.cs
new file mode 100644
index 0000000..e121273
--- /dev/null
+++ b/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/InheritedRecord/Person.cs
@@ -0,0 +1,13 @@
+// Non-nullable member is uninitialized
+#pragma warning disable CS8618
+// ReSharper disable All
+
+using System;
+using M31.FluentApi.Attributes;
+
+namespace M31.FluentApi.Tests.CodeGeneration.TestClasses.Abstract.InheritedRecord;
+
+[FluentApi]
+public record Person(
+ [property: FluentMember(0, "WithName")] string Name,
+ [property: FluentMember(1, "BornOn")] DateOnly DateOfBirth);
\ No newline at end of file
diff --git a/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/InheritedRecord/Student.cs b/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/InheritedRecord/Student.cs
new file mode 100644
index 0000000..f2286bd
--- /dev/null
+++ b/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/InheritedRecord/Student.cs
@@ -0,0 +1,15 @@
+// Non-nullable member is uninitialized
+#pragma warning disable CS8618
+// ReSharper disable All
+
+using System;
+using M31.FluentApi.Attributes;
+
+namespace M31.FluentApi.Tests.CodeGeneration.TestClasses.Abstract.InheritedRecord;
+
+[FluentApi]
+public record Student(
+ string Name,
+ DateOnly DateOfBirth,
+ [property: FluentMember(2, "InSemester")] int Semester)
+ : Person(Name, DateOfBirth);
\ No newline at end of file
diff --git a/src/M31.FluentApi.Tests/CodeGeneration/TestDataProvider.cs b/src/M31.FluentApi.Tests/CodeGeneration/TestDataProvider.cs
index cba4ff3..6a0b15f 100644
--- a/src/M31.FluentApi.Tests/CodeGeneration/TestDataProvider.cs
+++ b/src/M31.FluentApi.Tests/CodeGeneration/TestDataProvider.cs
@@ -57,6 +57,11 @@ internal class TestDataProvider : IEnumerable