diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 88fa693..b25f3c7 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -13,17 +13,17 @@ jobs:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v3
- - name: Setup .NET
- uses: actions/setup-dotnet@v3
- with:
- dotnet-version: 6.0.x
- - name: Restore dependencies
- run: dotnet restore
- working-directory: src
- - name: Build
- run: dotnet build --no-restore --maxcpucount:1
- working-directory: src
- - name: Test
- run: dotnet test --no-build --verbosity normal
- working-directory: src
+ - uses: actions/checkout@v3
+ - name: Setup .NET
+ uses: actions/setup-dotnet@v3
+ with:
+ dotnet-version: 6.0.x
+ - name: Restore dependencies
+ run: dotnet restore
+ working-directory: src
+ - name: Build
+ run: dotnet build --no-restore --maxcpucount:1
+ working-directory: src
+ - name: Test
+ run: dotnet test --no-build --verbosity normal
+ working-directory: src
\ No newline at end of file
diff --git a/README.md b/README.md
index f663127..9ba9f60 100644
--- a/README.md
+++ b/README.md
@@ -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:
@@ -440,6 +440,65 @@ private void BornOn(DateOnly dateOfBirth)
```
+### Documentation comments
+
+Documentation comments for fluent API members can be added using four slashes (////) followed by XML tags prefixed with `fluent`, e.g.:
+
+```cs
+////
+//// Sets the student's name.
+////
+//// The student's name.
+//// A builder for setting the student's age.
+[FluentMember(0)]
+public string Name { get; private set; }
+```
+
+Using four slashes instead of three prevents the IDE from interpreting these comments as standard XML documentation for the member.
+
+All `fluent`-prefixed tags are copied to the generated builder method and automatically transformed, e.g., `//// ` becomes `/// ` in the generated code.
+
+For compounds, add the documentation comments to the first member of the compound only to avoid duplication:
+
+```cs
+////
+//// Sets the student's name.
+////
+//// The student's first name.
+//// The student's last name.
+//// A builder for setting the student's age.
+[FluentMember(0, "Named", 0)]
+public string FirstName { get; private set; }
+
+[FluentMember(0, "Named", 1)]
+public string LastName { get; private set; }
+```
+
+If multiple methods are generated for a member, you can target a specific method by adding the `method` XML attribute to the documentation tags:
+
+```cs
+////
+//// Sets the student's current semester.
+////
+//// The student's current semester.
+//// A builder for setting the student's city.
+////
+////
+//// Sets the student's semester to 0.
+////
+//// A builder for setting the student's city.
+[FluentMember(2, "InSemester")]
+[FluentDefault("WhoStartsUniversity")]
+public int Semester { get; private set; } = 0;
+```
+
+To simplify adding documentation comments, a code action is available to generate the boilerplate for the selected member:
+
+
+
+For reference, you can view the documented version of the `Student` class in [DocumentedStudent.cs](src/M31.FluentApi.Tests/CodeGeneration/TestClasses/DocumentedStudentClass/DocumentedStudent.cs). The corresponding generated code is located in [DocumentedStudent.g.cs](src/M31.FluentApi.Tests/CodeGeneration/TestClasses/DocumentedStudentClass/CreateDocumentedStudent.g.cs)
+
+
### Lambda pattern
Instances of Fluent API classes can be created and passed into methods of other classes using the lambda pattern. For example, given a `University` class that needs to be augmented with an `AddStudent` method, the following code demonstrates the lambda pattern:
diff --git a/media/create-doc-comments-action.png b/media/create-doc-comments-action.png
new file mode 100644
index 0000000..4170cbd
Binary files /dev/null and b/media/create-doc-comments-action.png differ
diff --git a/src/ExampleProject/DocumentedStudent.cs b/src/ExampleProject/DocumentedStudent.cs
new file mode 100644
index 0000000..709a169
--- /dev/null
+++ b/src/ExampleProject/DocumentedStudent.cs
@@ -0,0 +1,117 @@
+// Non-nullable member is uninitialized
+#pragma warning disable CS8618
+// ReSharper disable All
+
+using M31.FluentApi.Attributes;
+
+namespace ExampleProject;
+
+[FluentApi]
+public class DocumentedStudent
+{
+ ////
+ //// Sets the student's name.
+ ////
+ //// The student's first name.
+ //// The student's last name.
+ //// A builder for setting the student's age.
+ [FluentMember(0, "Named", 0)]
+ public string FirstName { get; private set; }
+
+ [FluentMember(0, "Named", 1)]
+ public string LastName { get; private set; }
+
+ ////
+ //// Sets the student's age.
+ ////
+ //// The student's age.
+ //// A builder for setting the student's semester.
+ [FluentMember(1, "OfAge")]
+ public int Age { get; private set; }
+
+ ////
+ //// Sets the student's age based on their date of birth.
+ ////
+ //// The student's date of birth.
+ //// A builder for setting the student's semester.
+ [FluentMethod(1)]
+ private void BornOn(DateOnly dateOfBirth)
+ {
+ DateOnly today = DateOnly.FromDateTime(DateTime.Today);
+ int age = today.Year - dateOfBirth.Year;
+ if (dateOfBirth > today.AddYears(-age)) age--;
+ Age = age;
+ }
+
+ ////
+ //// Sets the student's current semester.
+ ////
+ //// The student's current semester.
+ //// A builder for setting the student's city.
+ ////
+ ////
+ //// Sets the student's semester to 0.
+ ////
+ //// A builder for setting the student's city.
+ [FluentMember(2, "InSemester")]
+ [FluentDefault("WhoStartsUniversity")]
+ public int Semester { get; private set; } = 0;
+
+ ////
+ //// Sets the student's city.
+ ////
+ //// The student's city.
+ //// A builder for setting whether the student is happy.
+ ////
+ ////
+ //// Sets the student's city to Boston.
+ ////
+ //// A builder for setting whether the student is happy.
+ ////
+ ////
+ //// Sets the student's city to null.
+ ////
+ //// A builder for setting whether the student is happy.
+ [FluentMember(3, "LivingIn")]
+ [FluentDefault("LivingInBoston")]
+ [FluentNullable("InUnknownCity")]
+ public string? City { get; private set; } = "Boston";
+
+ ////
+ //// Sets the property.
+ ////
+ //// Indicates whether the student is happy.
+ //// A builder for setting the student's friends.
+ ////
+ ////
+ //// Sets the property to false.
+ ////
+ //// A builder for setting the student's friends.
+ ////
+ ////
+ //// Sets the property to null.
+ ////
+ //// A builder for setting the student's friends.
+ [FluentPredicate(4, "WhoIsHappy", "WhoIsSad")]
+ [FluentNullable("WithUnknownMood")]
+ public bool? IsHappy { get; private set; }
+
+ ////
+ //// Sets the student's friends.
+ ////
+ //// The student's friends.
+ //// The .
+ ////
+ ////
+ //// Sets a single friend.
+ ////
+ //// The student's friend.
+ //// The .
+ ////
+ ////
+ //// Sets the student's friends to an empty collection.
+ ////
+ //// The .
+ [FluentCollection(5, "Friend", "WhoseFriendsAre", "WhoseFriendIs", "WhoHasNoFriends")]
+ public IReadOnlyCollection Friends { get; private set; }
+}
\ No newline at end of file
diff --git a/src/ExampleProject/ExampleProject.csproj b/src/ExampleProject/ExampleProject.csproj
index e73b4a0..ef23671 100644
--- a/src/ExampleProject/ExampleProject.csproj
+++ b/src/ExampleProject/ExampleProject.csproj
@@ -1,16 +1,16 @@
-
- Exe
- net6.0
- enable
- enable
-
+
+ Exe
+ net6.0
+ enable
+ enable
+
-
-
-
-
+
+
+
+
$(BaseIntermediateOutputPath)Generated
diff --git a/src/ExampleProject/HashCode.cs b/src/ExampleProject/HashCode.cs
index 501ad92..5d58dd4 100644
--- a/src/ExampleProject/HashCode.cs
+++ b/src/ExampleProject/HashCode.cs
@@ -82,7 +82,6 @@ public void AddSequence(IEnumerable items)
{
hash = hash * 23 + item?.GetHashCode() ?? 0;
}
-
}
}
diff --git a/src/ExampleProject/Program.cs b/src/ExampleProject/Program.cs
index 50ddc6e..1b08632 100644
--- a/src/ExampleProject/Program.cs
+++ b/src/ExampleProject/Program.cs
@@ -14,6 +14,17 @@
Console.WriteLine(JsonSerializer.Serialize(student1));
Console.WriteLine(JsonSerializer.Serialize(student2));
+// DocumentedStudent (generated code includes `summary`, `param` and `returns` XML comments)
+//
+
+DocumentedStudent documentedStudent1 = CreateDocumentedStudent.Named("Alice", "King").OfAge(22).WhoStartsUniversity()
+ .LivingIn("New York").WhoIsHappy().WhoseFriendsAre("Bob", "Carol", "Eve");
+DocumentedStudent documentedStudent2 = CreateDocumentedStudent.Named("Bob", "Bishop").BornOn(new DateOnly(2002, 8, 3)).InSemester(2)
+ .LivingInBoston().WithUnknownMood().WhoseFriendIs("Alice");
+
+Console.WriteLine(JsonSerializer.Serialize(documentedStudent1));
+Console.WriteLine(JsonSerializer.Serialize(documentedStudent2));
+
// ExchangeStudent (inherited from Student)
//
diff --git a/src/M31.FluentApi.Generator/CodeBuilding/CommentedMethodSignature.cs b/src/M31.FluentApi.Generator/CodeBuilding/CommentedMethodSignature.cs
new file mode 100644
index 0000000..19968c6
--- /dev/null
+++ b/src/M31.FluentApi.Generator/CodeBuilding/CommentedMethodSignature.cs
@@ -0,0 +1,25 @@
+namespace M31.FluentApi.Generator.CodeBuilding;
+
+internal class CommentedMethodSignature : ICode
+{
+ internal MethodSignature MethodSignature { get; }
+ internal MethodComments MethodComments { get; }
+
+ internal CommentedMethodSignature(MethodSignature methodSignature, MethodComments methodComments)
+ {
+ MethodSignature = methodSignature;
+ MethodComments = methodComments;
+ }
+
+ internal CommentedMethodSignature TransformSignature(Func transform)
+ {
+ return new CommentedMethodSignature(transform(MethodSignature), MethodComments);
+ }
+
+ public CodeBuilder AppendCode(CodeBuilder codeBuilder)
+ {
+ return codeBuilder
+ .Append(MethodComments)
+ .Append(MethodSignature);
+ }
+}
\ No newline at end of file
diff --git a/src/M31.FluentApi.Generator/CodeBuilding/Interface.cs b/src/M31.FluentApi.Generator/CodeBuilding/Interface.cs
index 31296dc..5b916e3 100644
--- a/src/M31.FluentApi.Generator/CodeBuilding/Interface.cs
+++ b/src/M31.FluentApi.Generator/CodeBuilding/Interface.cs
@@ -4,25 +4,25 @@ namespace M31.FluentApi.Generator.CodeBuilding;
internal class Interface : ICode
{
- private readonly List methodSignatures;
+ private readonly List methodSignatures;
private readonly List baseInterfaces;
internal Interface(string accessModifier, string name)
{
AccessModifier = accessModifier;
Name = name;
- methodSignatures = new List();
+ methodSignatures = new List();
baseInterfaces = new List();
}
internal string AccessModifier { get; }
internal string Name { get; }
- internal IReadOnlyCollection MethodSignatures => methodSignatures;
+ internal IReadOnlyCollection MethodSignatures => methodSignatures;
internal IReadOnlyCollection BaseInterfaces => baseInterfaces;
- internal void AddMethodSignature(MethodSignature methodSignature)
+ internal void AddMethodSignature(CommentedMethodSignature methodSignature)
{
- if (!methodSignature.IsSignatureForInterface)
+ if (!methodSignature.MethodSignature.IsSignatureForInterface)
{
throw new ArgumentException("Expected a stand-alone method signature.");
}
diff --git a/src/M31.FluentApi.Generator/CodeBuilding/Method.cs b/src/M31.FluentApi.Generator/CodeBuilding/Method.cs
index 119bf73..4bc2c5f 100644
--- a/src/M31.FluentApi.Generator/CodeBuilding/Method.cs
+++ b/src/M31.FluentApi.Generator/CodeBuilding/Method.cs
@@ -4,13 +4,27 @@ internal class Method : ICode
{
internal Method(MethodSignature methodSignature)
{
+ MethodComments = new MethodComments();
MethodSignature = methodSignature;
MethodBody = new MethodBody();
}
+ internal Method(MethodComments methodComments, MethodSignature methodSignature, MethodBody methodBody)
+ {
+ MethodComments = methodComments;
+ MethodSignature = methodSignature;
+ MethodBody = methodBody;
+ }
+
+ internal MethodComments MethodComments { get; }
internal MethodSignature MethodSignature { get; }
internal MethodBody MethodBody { get; }
+ internal void AddCommentLine(string commentLine)
+ {
+ MethodComments.AddLine(commentLine);
+ }
+
internal void AppendBodyLine(string line)
{
MethodBody.AppendLine(line);
@@ -19,6 +33,7 @@ internal void AppendBodyLine(string line)
public CodeBuilder AppendCode(CodeBuilder codeBuilder)
{
return codeBuilder
+ .Append(MethodComments)
.Append(MethodSignature)
.Append(MethodBody);
}
diff --git a/src/M31.FluentApi.Generator/CodeBuilding/MethodComments.cs b/src/M31.FluentApi.Generator/CodeBuilding/MethodComments.cs
new file mode 100644
index 0000000..09c4829
--- /dev/null
+++ b/src/M31.FluentApi.Generator/CodeBuilding/MethodComments.cs
@@ -0,0 +1,30 @@
+namespace M31.FluentApi.Generator.CodeBuilding;
+
+internal class MethodComments : ICode
+{
+ private readonly List comments;
+
+ internal MethodComments()
+ {
+ comments = new List();
+ }
+
+ internal MethodComments(List comments)
+ {
+ this.comments = comments;
+ }
+
+ internal IReadOnlyCollection Comments => comments;
+ internal bool Any => Comments.Count > 0;
+
+ internal void AddLine(string commentLine)
+ {
+ comments.Add(commentLine);
+ }
+
+ public CodeBuilder AppendCode(CodeBuilder codeBuilder)
+ {
+ return codeBuilder
+ .AppendLines(comments);
+ }
+}
\ No newline at end of file
diff --git a/src/M31.FluentApi.Generator/CodeGeneration/CodeBoardActors/BuilderMethodsGeneration/BuilderGenerator.cs b/src/M31.FluentApi.Generator/CodeGeneration/CodeBoardActors/BuilderMethodsGeneration/BuilderGenerator.cs
index 0800ff7..0ef6b32 100644
--- a/src/M31.FluentApi.Generator/CodeGeneration/CodeBoardActors/BuilderMethodsGeneration/BuilderGenerator.cs
+++ b/src/M31.FluentApi.Generator/CodeGeneration/CodeBoardActors/BuilderMethodsGeneration/BuilderGenerator.cs
@@ -7,10 +7,10 @@ internal class BuilderGenerator : ICodeBoardActor
{
public void Modify(CodeBoard codeBoard)
{
- BuilderMethods builderMethods =
- BuilderMethodsCreator.CreateBuilderMethods(codeBoard.Forks, codeBoard.CancellationToken);
+ BuilderStepMethods builderStepMethods =
+ BuilderStepMethodsCreator.CreateBuilderMethods(codeBoard.Forks, codeBoard.CancellationToken);
- foreach (BuilderStepMethod staticMethod in builderMethods.StaticMethods)
+ foreach (BuilderStepMethod staticMethod in builderStepMethods.StaticMethods)
{
if (codeBoard.CancellationToken.IsCancellationRequested)
{
@@ -21,10 +21,10 @@ public void Modify(CodeBoard codeBoard)
codeBoard.BuilderClass.AddMethod(method);
}
- List interfaces = new List(builderMethods.Interfaces.Count);
- interfaces.Add(CreateInitialStepInterface(builderMethods, codeBoard));
+ List interfaces = new List(builderStepMethods.Interfaces.Count);
+ interfaces.Add(CreateInitialStepInterface(builderStepMethods, codeBoard));
- foreach (BuilderInterface builderInterface in builderMethods.Interfaces)
+ foreach (BuilderInterface builderInterface in builderStepMethods.Interfaces)
{
if (codeBoard.CancellationToken.IsCancellationRequested)
{
@@ -37,8 +37,11 @@ public void Modify(CodeBoard codeBoard)
foreach (InterfaceBuilderMethod interfaceMethod in builderInterface.Methods)
{
Method method = CreateMethod(interfaceMethod, codeBoard);
- codeBoard.BuilderClass.AddMethod(method);
- @interface.AddMethodSignature(method.MethodSignature.ToSignatureForInterface());
+ codeBoard.BuilderClass.AddMethod(CreateMethodWithInheritedDocs(method));
+ CommentedMethodSignature methodSignature = new CommentedMethodSignature(
+ method.MethodSignature.ToSignatureForInterface(),
+ method.MethodComments);
+ @interface.AddMethodSignature(methodSignature);
}
@interface.AddBaseInterfaces(builderInterface.BaseInterfaces);
@@ -52,6 +55,16 @@ public void Modify(CodeBoard codeBoard)
AddInterfaceDefinitionsToBuilderClass(interfaces, codeBoard.BuilderClass);
}
+ private Method CreateMethodWithInheritedDocs(Method method)
+ {
+ return new Method(
+ new MethodComments(method.MethodComments.Any
+ ? new List() { "/// " }
+ : new List()),
+ method.MethodSignature,
+ method.MethodBody);
+ }
+
private Method CreateMethod(BuilderStepMethod builderStepMethod, CodeBoard codeBoard)
{
ReservedVariableNames reservedVariableNames = codeBoard.ReservedVariableNames.NewLocalScope();
@@ -64,9 +77,9 @@ private Method CreateMethod(BuilderStepMethod builderStepMethod, CodeBoard codeB
return method;
}
- private Interface CreateInitialStepInterface(BuilderMethods builderMethods, CodeBoard codeBoard)
+ private Interface CreateInitialStepInterface(BuilderStepMethods builderStepMethods, CodeBoard codeBoard)
{
- string? firstInterfaceName = builderMethods.Interfaces.FirstOrDefault()?.InterfaceName;
+ string? firstInterfaceName = builderStepMethods.Interfaces.FirstOrDefault()?.InterfaceName;
Interface initialStepInterface =
new Interface(codeBoard.Info.DefaultAccessModifier, codeBoard.Info.InitialStepInterfaceName);
diff --git a/src/M31.FluentApi.Generator/CodeGeneration/CodeBoardActors/BuilderMethodsGeneration/BuilderStepMethod.cs b/src/M31.FluentApi.Generator/CodeGeneration/CodeBoardActors/BuilderMethodsGeneration/BuilderStepMethod.cs
index 6a3ca8f..076647c 100644
--- a/src/M31.FluentApi.Generator/CodeGeneration/CodeBoardActors/BuilderMethodsGeneration/BuilderStepMethod.cs
+++ b/src/M31.FluentApi.Generator/CodeGeneration/CodeBoardActors/BuilderMethodsGeneration/BuilderStepMethod.cs
@@ -17,7 +17,13 @@ protected BuilderStepMethod(BuilderMethod builderMethod)
protected Method CreateMethod(string defaultReturnType, params string[] modifiers)
{
MethodSignature methodSignature = CreateMethodSignature(defaultReturnType, null, modifiers);
- return new Method(methodSignature);
+ Method method = new Method(methodSignature);
+ foreach (string comment in Comments.GetLines())
+ {
+ method.AddCommentLine(comment);
+ }
+
+ return method;
}
protected Method CreateInterfaceMethod(
@@ -26,7 +32,13 @@ protected Method CreateInterfaceMethod(
params string[] modifiers)
{
MethodSignature methodSignature = CreateMethodSignature(defaultReturnType, interfaceName, modifiers);
- return new Method(methodSignature);
+ Method method = new Method(methodSignature);
+ foreach (string comment in Comments.GetLines())
+ {
+ method.AddCommentLine(comment);
+ }
+
+ return method;
}
private MethodSignature CreateMethodSignature(
diff --git a/src/M31.FluentApi.Generator/CodeGeneration/CodeBoardActors/BuilderMethodsGeneration/BuilderMethods.cs b/src/M31.FluentApi.Generator/CodeGeneration/CodeBoardActors/BuilderMethodsGeneration/BuilderStepMethods.cs
similarity index 96%
rename from src/M31.FluentApi.Generator/CodeGeneration/CodeBoardActors/BuilderMethodsGeneration/BuilderMethods.cs
rename to src/M31.FluentApi.Generator/CodeGeneration/CodeBoardActors/BuilderMethodsGeneration/BuilderStepMethods.cs
index 402b0c5..478541b 100644
--- a/src/M31.FluentApi.Generator/CodeGeneration/CodeBoardActors/BuilderMethodsGeneration/BuilderMethods.cs
+++ b/src/M31.FluentApi.Generator/CodeGeneration/CodeBoardActors/BuilderMethodsGeneration/BuilderStepMethods.cs
@@ -2,9 +2,9 @@
namespace M31.FluentApi.Generator.CodeGeneration.CodeBoardActors.BuilderMethodsGeneration;
-internal class BuilderMethods
+internal class BuilderStepMethods
{
- public BuilderMethods(
+ internal BuilderStepMethods(
IReadOnlyCollection staticMethods,
IReadOnlyCollection interfaces)
{
diff --git a/src/M31.FluentApi.Generator/CodeGeneration/CodeBoardActors/BuilderMethodsGeneration/BuilderMethodsCreator.cs b/src/M31.FluentApi.Generator/CodeGeneration/CodeBoardActors/BuilderMethodsGeneration/BuilderStepMethodsCreator.cs
similarity index 88%
rename from src/M31.FluentApi.Generator/CodeGeneration/CodeBoardActors/BuilderMethodsGeneration/BuilderMethodsCreator.cs
rename to src/M31.FluentApi.Generator/CodeGeneration/CodeBoardActors/BuilderMethodsGeneration/BuilderStepMethodsCreator.cs
index ddfee87..46be7dc 100644
--- a/src/M31.FluentApi.Generator/CodeGeneration/CodeBoardActors/BuilderMethodsGeneration/BuilderMethodsCreator.cs
+++ b/src/M31.FluentApi.Generator/CodeGeneration/CodeBoardActors/BuilderMethodsGeneration/BuilderStepMethodsCreator.cs
@@ -4,12 +4,14 @@
namespace M31.FluentApi.Generator.CodeGeneration.CodeBoardActors.BuilderMethodsGeneration;
-internal class BuilderMethodsCreator
+internal class BuilderStepMethodsCreator
{
- internal static BuilderMethods CreateBuilderMethods(IReadOnlyList forks, CancellationToken cancellationToken)
+ internal static BuilderStepMethods CreateBuilderMethods(IReadOnlyList forks,
+ CancellationToken cancellationToken)
{
- return forks.Count == 0 ? EmptyBuilderMethods()
- : new BuilderMethodsCreator(forks, cancellationToken).Create();
+ return forks.Count == 0
+ ? EmptyBuilderMethods()
+ : new BuilderStepMethodsCreator(forks, cancellationToken).Create();
}
private readonly IReadOnlyList forks;
@@ -17,7 +19,7 @@ internal static BuilderMethods CreateBuilderMethods(IReadOnlyList forks, C
private readonly ListDictionary interfaceNameToBuilderMethods;
private readonly Dictionary builderStepToFork;
- private BuilderMethodsCreator(IReadOnlyList forks, CancellationToken cancellationToken)
+ private BuilderStepMethodsCreator(IReadOnlyList forks, CancellationToken cancellationToken)
{
this.forks = forks;
this.cancellationToken = cancellationToken;
@@ -25,12 +27,12 @@ private BuilderMethodsCreator(IReadOnlyList forks, CancellationToken cance
builderStepToFork = forks.ToDictionary(f => f.BuilderStep);
}
- private static BuilderMethods EmptyBuilderMethods()
+ private static BuilderStepMethods EmptyBuilderMethods()
{
- return new BuilderMethods(Array.Empty(), Array.Empty());
+ return new BuilderStepMethods(Array.Empty(), Array.Empty());
}
- private BuilderMethods Create()
+ private BuilderStepMethods Create()
{
if (cancellationToken.IsCancellationRequested)
{
@@ -45,7 +47,7 @@ private BuilderMethods Create()
}
IReadOnlyCollection interfaces =
- BuilderMethods.CreateInterfaces(interfaceMethods, cancellationToken);
+ BuilderStepMethods.CreateInterfaces(interfaceMethods, cancellationToken);
if (cancellationToken.IsCancellationRequested)
{
@@ -66,7 +68,7 @@ private BuilderMethods Create()
return EmptyBuilderMethods();
}
- return new BuilderMethods(staticMethods, interfaces);
+ return new BuilderStepMethods(staticMethods, interfaces);
}
private IEnumerable CreateInterfaceBuilderMethods(Fork fork)
diff --git a/src/M31.FluentApi.Generator/CodeGeneration/CodeBoardActors/Commons/BuilderMethod.cs b/src/M31.FluentApi.Generator/CodeGeneration/CodeBoardActors/Commons/BuilderMethod.cs
index 4c1fa65..6ec41ec 100644
--- a/src/M31.FluentApi.Generator/CodeGeneration/CodeBoardActors/Commons/BuilderMethod.cs
+++ b/src/M31.FluentApi.Generator/CodeGeneration/CodeBoardActors/Commons/BuilderMethod.cs
@@ -1,4 +1,5 @@
using M31.FluentApi.Generator.CodeBuilding;
+using M31.FluentApi.Generator.CodeGeneration.CodeBoardElements.FluentApiComments;
using M31.FluentApi.Generator.SourceGenerators.Generics;
namespace M31.FluentApi.Generator.CodeGeneration.CodeBoardActors.Commons;
@@ -10,19 +11,22 @@ internal class BuilderMethod
internal IReadOnlyCollection Parameters { get; }
internal string? ReturnTypeToRespect { get; }
internal BuildBodyCode BuildBodyCode { get; }
+ internal Comments Comments { get; }
internal BuilderMethod(
string methodName,
GenericInfo? genericInfo,
IReadOnlyCollection parameters,
string? returnTypeToRespect,
- BuildBodyCode buildBodyCode)
+ BuildBodyCode buildBodyCode,
+ Comments comments)
{
MethodName = methodName;
GenericInfo = genericInfo;
Parameters = parameters;
ReturnTypeToRespect = returnTypeToRespect;
BuildBodyCode = buildBodyCode;
+ Comments = comments;
}
internal BuilderMethod(BuilderMethod builderMethod)
@@ -32,5 +36,6 @@ internal BuilderMethod(BuilderMethod builderMethod)
Parameters = builderMethod.Parameters;
ReturnTypeToRespect = builderMethod.ReturnTypeToRespect;
BuildBodyCode = builderMethod.BuildBodyCode;
+ Comments = builderMethod.Comments;
}
}
\ No newline at end of file
diff --git a/src/M31.FluentApi.Generator/CodeGeneration/CodeBoardActors/Commons/BuilderMethodFactory.cs b/src/M31.FluentApi.Generator/CodeGeneration/CodeBoardActors/Commons/BuilderMethodFactory.cs
index be71dbf..0f76129 100644
--- a/src/M31.FluentApi.Generator/CodeGeneration/CodeBoardActors/Commons/BuilderMethodFactory.cs
+++ b/src/M31.FluentApi.Generator/CodeGeneration/CodeBoardActors/Commons/BuilderMethodFactory.cs
@@ -1,20 +1,27 @@
using M31.FluentApi.Generator.CodeBuilding;
using M31.FluentApi.Generator.CodeGeneration.CodeBoardElements;
+using M31.FluentApi.Generator.CodeGeneration.CodeBoardElements.FluentApiComments;
namespace M31.FluentApi.Generator.CodeGeneration.CodeBoardActors.Commons;
internal class BuilderMethodFactory
{
private readonly InnerBodyCreationDelegates innerBodyCreationDelegates;
+ private readonly TransformedComments transformedComments;
- internal BuilderMethodFactory(InnerBodyCreationDelegates innerBodyCreationDelegates)
+ internal BuilderMethodFactory(InnerBodyCreationDelegates innerBodyCreationDelegates,
+ TransformedComments transformedComments)
{
this.innerBodyCreationDelegates = innerBodyCreationDelegates;
+ this.transformedComments = transformedComments;
}
- internal BuilderMethod CreateBuilderMethod(string methodName)
+ internal BuilderMethod CreateEmptyBuilderMethod(MemberSymbolInfo memberInfo, string methodName)
{
- return new BuilderMethod(methodName, null, new List(), null, (_, _, _) => new List());
+ MemberCommentKey key = new MemberCommentKey(memberInfo.Name, methodName);
+ Comments comments = transformedComments.GetMemberComments(key, Array.Empty());
+ return new BuilderMethod(methodName, null, new List(), null, (_, _, _) => new List(),
+ comments);
}
internal BuilderMethod CreateBuilderMethod(string methodName, ComputeValueCode computeValue)
@@ -35,7 +42,9 @@ List BuildBodyCode(
};
}
- return new BuilderMethod(methodName, null, parameters, null, BuildBodyCode);
+ MemberCommentKey key = new MemberCommentKey(computeValue.TargetMember, methodName);
+ Comments comments = transformedComments.GetMemberComments(key, parameters.Select(p => p.Name).ToArray());
+ return new BuilderMethod(methodName, null, parameters, null, BuildBodyCode, comments);
}
internal BuilderMethod CreateBuilderMethod(string methodName, List computeValues)
@@ -53,7 +62,22 @@ List BuildBodyCode(
.ToList();
}
- return new BuilderMethod(methodName, null, parameters, null, BuildBodyCode);
+ Comments comments =
+ GetCompoundComments(methodName, parameters, computeValues.Select(v => v.TargetMember).ToArray());
+ return new BuilderMethod(methodName, null, parameters, null, BuildBodyCode, comments);
+ }
+
+ private Comments GetCompoundComments(
+ string methodName,
+ List parameters,
+ IReadOnlyCollection memberNames)
+ {
+ string[] parameterNames = parameters.Select(p => p.Name).ToArray();
+
+ return new Comments(memberNames
+ .SelectMany(n =>
+ transformedComments.GetMemberComments(new MemberCommentKey(n, methodName), parameterNames).List)
+ .ToArray());
}
internal BuilderMethod CreateBuilderMethod(
@@ -81,11 +105,14 @@ List BuildBodyCode(
.BuildCode(instancePrefix, parameters, reservedVariableNames, returnType);
}
+ Comments comments = transformedComments.GetMethodComments(methodSymbolInfo);
+
return new BuilderMethod(
methodName,
methodSymbolInfo.GenericInfo,
parameters,
returnTypeToRespect,
- BuildBodyCode);
+ BuildBodyCode,
+ comments);
}
}
\ No newline at end of file
diff --git a/src/M31.FluentApi.Generator/CodeGeneration/CodeBoardActors/FluentApiComments/CommentsGenerator.cs b/src/M31.FluentApi.Generator/CodeGeneration/CodeBoardActors/FluentApiComments/CommentsGenerator.cs
new file mode 100644
index 0000000..bc249e6
--- /dev/null
+++ b/src/M31.FluentApi.Generator/CodeGeneration/CodeBoardActors/FluentApiComments/CommentsGenerator.cs
@@ -0,0 +1,71 @@
+using M31.FluentApi.Generator.CodeGeneration.CodeBoardElements;
+using M31.FluentApi.Generator.CodeGeneration.CodeBoardElements.FluentApiComments;
+using M31.FluentApi.Generator.SourceGenerators;
+
+namespace M31.FluentApi.Generator.CodeGeneration.CodeBoardActors.FluentApiComments;
+
+internal class CommentsGenerator : ICodeBoardActor
+{
+ public void Modify(CodeBoard codeBoard)
+ {
+ foreach (FluentApiInfo fluentApiInfo in codeBoard.FluentApiInfos)
+ {
+ if (codeBoard.CancellationRequested)
+ {
+ return;
+ }
+
+ switch (fluentApiInfo.SymbolInfo)
+ {
+ case MemberSymbolInfo memberInfo:
+ HandleMemberSymbolInfo(memberInfo, fluentApiInfo, codeBoard);
+ break;
+
+ case MethodSymbolInfo methodSymbolInfo:
+ HandleMethodSymbolInfo(methodSymbolInfo, codeBoard);
+ break;
+
+ default:
+ throw new ArgumentException($"Unknown symbol info type: {fluentApiInfo.SymbolInfo.GetType()}");
+ }
+ }
+ }
+
+ private void HandleMemberSymbolInfo(MemberSymbolInfo memberInfo, FluentApiInfo fluentApiInfo, CodeBoard codeBoard)
+ {
+ string? singleMethodName = TryGetSingleMethodName(fluentApiInfo);
+ IGrouping[] groups = GroupByMethodName(memberInfo.Comments, singleMethodName);
+
+ foreach (var group in groups)
+ {
+ MemberCommentKey key = new MemberCommentKey(memberInfo.Name, group.Key);
+ Comments comments = new Comments(group.ToArray());
+ Comments transformedComments = CommentsTransformer.TransformComments(comments);
+ codeBoard.TransformedComments.AssignMemberComments(key, transformedComments);
+ }
+ }
+
+ private static string? TryGetSingleMethodName(FluentApiInfo fluentApiInfo)
+ {
+ string[] fluentMethodNames = fluentApiInfo.AttributeInfo.FluentMethodNames
+ .Concat(fluentApiInfo.OrthogonalAttributeInfos.SelectMany(o => o.FluentMethodNames)).Distinct().ToArray();
+ return fluentMethodNames.Length != 1 ? null : fluentMethodNames[0];
+ }
+
+ private IGrouping[] GroupByMethodName(Comments transformedComments, string? fallbackMethodName)
+ {
+ return transformedComments.List.GroupBy(GetMethodName).Where(g => g.Key != string.Empty).ToArray();
+
+ string GetMethodName(Comment comment)
+ {
+ return comment.Attributes.FirstOrDefault(a => a.Key == "method")?.Value ??
+ fallbackMethodName ?? string.Empty;
+ }
+ }
+
+ private void HandleMethodSymbolInfo(MethodSymbolInfo methodInfo, CodeBoard codeBoard)
+ {
+ Comments transformedComments = CommentsTransformer.TransformComments(methodInfo.Comments);
+ codeBoard.TransformedComments.AssignMethodComments(methodInfo, transformedComments);
+ }
+}
\ No newline at end of file
diff --git a/src/M31.FluentApi.Generator/CodeGeneration/CodeBoardActors/FluentApiComments/CommentsTransformer.cs b/src/M31.FluentApi.Generator/CodeGeneration/CodeBoardActors/FluentApiComments/CommentsTransformer.cs
new file mode 100644
index 0000000..4adc898
--- /dev/null
+++ b/src/M31.FluentApi.Generator/CodeGeneration/CodeBoardActors/FluentApiComments/CommentsTransformer.cs
@@ -0,0 +1,40 @@
+using M31.FluentApi.Generator.CodeGeneration.CodeBoardElements.FluentApiComments;
+using M31.FluentApi.Generator.Commons;
+
+namespace M31.FluentApi.Generator.CodeGeneration.CodeBoardActors.FluentApiComments;
+
+internal static class CommentsTransformer
+{
+ internal static Comments TransformComments(Comments comments)
+ {
+ return new Comments(comments.List.Select(TransformComment).OfType().ToArray());
+ }
+
+ internal static Comment? TransformComment(Comment comment)
+ {
+ string? transformedTag = TransformTag(comment.Tag);
+ if (transformedTag == null)
+ {
+ return null;
+ }
+
+ IReadOnlyList transformedAttributes = TransformAttributes(comment.Attributes);
+ return new Comment(transformedTag, transformedAttributes, comment.Content);
+ }
+
+ private static string? TransformTag(string tag)
+ {
+ if (!tag.StartsWith("fluent"))
+ {
+ return null;
+ }
+
+ return tag.Substring("fluent".Length).FirstCharToLower();
+ }
+
+ private static IReadOnlyList TransformAttributes(IReadOnlyList attributes)
+ {
+ CommentAttribute? firstMethodAttribute = attributes.FirstOrDefault(a => a.Key == "method");
+ return firstMethodAttribute == null ? attributes : attributes.Where(a => a != firstMethodAttribute).ToArray();
+ }
+}
\ No newline at end of file
diff --git a/src/M31.FluentApi.Generator/CodeGeneration/CodeBoardActors/MethodCreation/BuilderMethodCreator.cs b/src/M31.FluentApi.Generator/CodeGeneration/CodeBoardActors/MethodCreation/BuilderMethodsCreator.cs
similarity index 94%
rename from src/M31.FluentApi.Generator/CodeGeneration/CodeBoardActors/MethodCreation/BuilderMethodCreator.cs
rename to src/M31.FluentApi.Generator/CodeGeneration/CodeBoardActors/MethodCreation/BuilderMethodsCreator.cs
index 2bdd68e..21d73a2 100644
--- a/src/M31.FluentApi.Generator/CodeGeneration/CodeBoardActors/MethodCreation/BuilderMethodCreator.cs
+++ b/src/M31.FluentApi.Generator/CodeGeneration/CodeBoardActors/MethodCreation/BuilderMethodsCreator.cs
@@ -7,12 +7,12 @@
namespace M31.FluentApi.Generator.CodeGeneration.CodeBoardActors.MethodCreation;
-internal class BuilderMethodCreator : IBuilderMethodCreator
+internal class BuilderMethodsCreator : IBuilderMethodCreator
{
private readonly FluentApiInfoGroup group;
private readonly CodeBoard codeBoard;
- internal BuilderMethodCreator(FluentApiInfoGroup group, CodeBoard codeBoard)
+ internal BuilderMethodsCreator(FluentApiInfoGroup group, CodeBoard codeBoard)
{
this.group = group;
this.codeBoard = codeBoard;
@@ -22,6 +22,13 @@ internal BuilderMethodCreator(FluentApiInfoGroup group, CodeBoard codeBoard)
internal IReadOnlyCollection FluentApiInfos => group.FluentApiInfos;
public BuilderMethods CreateBuilderMethods(MethodCreator methodCreator)
+ {
+ BuilderMethods builderMethods = CreateBuilderMethodsInternal(methodCreator);
+ codeBoard.GroupsToMethods[group] = builderMethods;
+ return builderMethods;
+ }
+
+ private BuilderMethods CreateBuilderMethodsInternal(MethodCreator methodCreator)
{
if (FluentApiInfos.Count == 0)
{
diff --git a/src/M31.FluentApi.Generator/CodeGeneration/CodeBoardActors/MethodCreation/Collections/CollectionMethodCreator.cs b/src/M31.FluentApi.Generator/CodeGeneration/CodeBoardActors/MethodCreation/Collections/CollectionMethodCreator.cs
index b317922..ab6a8ec 100644
--- a/src/M31.FluentApi.Generator/CodeGeneration/CodeBoardActors/MethodCreation/Collections/CollectionMethodCreator.cs
+++ b/src/M31.FluentApi.Generator/CodeGeneration/CodeBoardActors/MethodCreation/Collections/CollectionMethodCreator.cs
@@ -25,8 +25,9 @@ internal CollectionMethodCreator(
internal BuilderMethod? CreateWithItemsMethod(MethodCreator methodCreator)
{
- return !ShouldCreateWithItemsMethod() ? null :
- methodCreator.CreateMethod(symbolInfo, collectionAttributeInfo.WithItems);
+ return !ShouldCreateWithItemsMethod()
+ ? null
+ : methodCreator.CreateMethod(symbolInfo, collectionAttributeInfo.WithItems);
}
private bool ShouldCreateWithItemsMethod()
diff --git a/src/M31.FluentApi.Generator/CodeGeneration/CodeBoardActors/MethodCreation/DefaultMethod.cs b/src/M31.FluentApi.Generator/CodeGeneration/CodeBoardActors/MethodCreation/DefaultMethod.cs
index c7a8093..61151a3 100644
--- a/src/M31.FluentApi.Generator/CodeGeneration/CodeBoardActors/MethodCreation/DefaultMethod.cs
+++ b/src/M31.FluentApi.Generator/CodeGeneration/CodeBoardActors/MethodCreation/DefaultMethod.cs
@@ -16,6 +16,8 @@ internal DefaultMethod(MemberSymbolInfo symbolInfo, FluentDefaultAttributeInfo d
public BuilderMethods CreateBuilderMethods(MethodCreator methodCreator)
{
- return new BuilderMethods(methodCreator.CreateMethodThatDoesNothing(DefaultAttributeInfo.Method));
+ return new BuilderMethods(methodCreator.CreateMethodThatDoesNothing(
+ SymbolInfo,
+ DefaultAttributeInfo.Method));
}
}
\ No newline at end of file
diff --git a/src/M31.FluentApi.Generator/CodeGeneration/CodeBoardActors/MethodCreation/FluentMethods.cs b/src/M31.FluentApi.Generator/CodeGeneration/CodeBoardActors/MethodCreation/FluentMethods.cs
index 109d34b..2dcf218 100644
--- a/src/M31.FluentApi.Generator/CodeGeneration/CodeBoardActors/MethodCreation/FluentMethods.cs
+++ b/src/M31.FluentApi.Generator/CodeGeneration/CodeBoardActors/MethodCreation/FluentMethods.cs
@@ -25,7 +25,7 @@ public BuilderMethods CreateBuilderMethods(MethodCreator methodCreator)
BuilderMethod builderMethod =
methodCreator.BuilderMethodFactory.CreateBuilderMethod(
SymbolInfo,
- MethodAttributeInfo.FluentMethodName,
+ MethodAttributeInfo.Method,
ReturnAttributeInfo != null);
return new BuilderMethods(builderMethod);
}
diff --git a/src/M31.FluentApi.Generator/CodeGeneration/CodeBoardActors/MethodCreation/Forks/ForkCreator.cs b/src/M31.FluentApi.Generator/CodeGeneration/CodeBoardActors/MethodCreation/Forks/ForkCreator.cs
index 2c06f78..d3ec021 100644
--- a/src/M31.FluentApi.Generator/CodeGeneration/CodeBoardActors/MethodCreation/Forks/ForkCreator.cs
+++ b/src/M31.FluentApi.Generator/CodeGeneration/CodeBoardActors/MethodCreation/Forks/ForkCreator.cs
@@ -15,7 +15,8 @@ internal ForkCreator()
public void Modify(CodeBoard codeBoard)
{
- BuilderMethodFactory builderMethodFactory = new BuilderMethodFactory(codeBoard.InnerBodyCreationDelegates);
+ BuilderMethodFactory builderMethodFactory =
+ new BuilderMethodFactory(codeBoard.InnerBodyCreationDelegates, codeBoard.TransformedComments);
MethodCreator methodCreator = new MethodCreator(builderMethodFactory);
CreateForks(methodCreator, codeBoard);
codeBoard.Forks = GetForks();
@@ -32,8 +33,8 @@ private void CreateForks(
return;
}
- BuilderMethodCreator builderMethodCreator = new BuilderMethodCreator(group, codeBoard);
- BuilderMethods builderMethods = builderMethodCreator.CreateBuilderMethods(methodCreator);
+ BuilderMethodsCreator builderMethodsCreator = new BuilderMethodsCreator(group, codeBoard);
+ BuilderMethods builderMethods = builderMethodsCreator.CreateBuilderMethods(methodCreator);
foreach (string @using in builderMethods.RequiredUsings)
{
diff --git a/src/M31.FluentApi.Generator/CodeGeneration/CodeBoardActors/MethodCreation/LambdaMethod.cs b/src/M31.FluentApi.Generator/CodeGeneration/CodeBoardActors/MethodCreation/LambdaMethod.cs
index 3946116..ffff921 100644
--- a/src/M31.FluentApi.Generator/CodeGeneration/CodeBoardActors/MethodCreation/LambdaMethod.cs
+++ b/src/M31.FluentApi.Generator/CodeGeneration/CodeBoardActors/MethodCreation/LambdaMethod.cs
@@ -22,7 +22,7 @@ internal LambdaMethod(
public BuilderMethods CreateBuilderMethods(MethodCreator methodCreator)
{
BuilderMethod memberBuilderMethod =
- methodCreator.CreateMethod(SymbolInfo, LambdaAttributeInfo.FluentMethodName);
+ methodCreator.CreateMethod(SymbolInfo, LambdaAttributeInfo.Method);
BuilderMethod lambdaBuilderMethod = CreateLambdaBuilderMethod(
methodCreator, LambdaAttributeInfo.Method, SymbolInfo, LambdaAttributeInfo.BuilderInfo);
diff --git a/src/M31.FluentApi.Generator/CodeGeneration/CodeBoardActors/MethodCreation/MemberMethod.cs b/src/M31.FluentApi.Generator/CodeGeneration/CodeBoardActors/MethodCreation/MemberMethod.cs
index c7644bd..8e0dcd1 100644
--- a/src/M31.FluentApi.Generator/CodeGeneration/CodeBoardActors/MethodCreation/MemberMethod.cs
+++ b/src/M31.FluentApi.Generator/CodeGeneration/CodeBoardActors/MethodCreation/MemberMethod.cs
@@ -20,7 +20,7 @@ public BuilderMethods CreateBuilderMethods(MethodCreator methodCreator)
List builderMethods = new List();
HashSet requiredUsings = new HashSet();
- BuilderMethod builderMethod = methodCreator.CreateMethod(SymbolInfo, MemberAttributeInfo.FluentMethodName);
+ BuilderMethod builderMethod = methodCreator.CreateMethod(SymbolInfo, MemberAttributeInfo.Method);
builderMethods.Add(builderMethod);
if (MemberAttributeInfo.LambdaBuilderInfo != null)
diff --git a/src/M31.FluentApi.Generator/CodeGeneration/CodeBoardActors/MethodCreation/MethodCreator.cs b/src/M31.FluentApi.Generator/CodeGeneration/CodeBoardActors/MethodCreation/MethodCreator.cs
index fb68027..9424891 100644
--- a/src/M31.FluentApi.Generator/CodeGeneration/CodeBoardActors/MethodCreation/MethodCreator.cs
+++ b/src/M31.FluentApi.Generator/CodeGeneration/CodeBoardActors/MethodCreation/MethodCreator.cs
@@ -16,7 +16,8 @@ internal MethodCreator(BuilderMethodFactory builderMethodFactory)
internal BuilderMethod CreateMethod(MemberSymbolInfo symbolInfo, string methodName)
{
return BuilderMethodFactory
- .CreateBuilderMethod(methodName, ComputeValueCode.Create(symbolInfo.Name, GetStandardParameter(symbolInfo)));
+ .CreateBuilderMethod(methodName,
+ ComputeValueCode.Create(symbolInfo.Name, GetStandardParameter(symbolInfo)));
}
internal BuilderMethod CreateMethodWithDefaultValue(
@@ -42,13 +43,14 @@ internal BuilderMethod CreateMethodWithComputedValue(
Func buildCodeWithParameter)
{
return BuilderMethodFactory
- .CreateBuilderMethod(methodName, ComputeValueCode.Create(symbolInfo.Name, parameter, buildCodeWithParameter));
+ .CreateBuilderMethod(methodName,
+ ComputeValueCode.Create(symbolInfo.Name, parameter, buildCodeWithParameter));
}
- internal BuilderMethod CreateMethodThatDoesNothing(string methodName)
+ internal BuilderMethod CreateMethodThatDoesNothing(MemberSymbolInfo memberSymbolInfo, string methodName)
{
return BuilderMethodFactory
- .CreateBuilderMethod(methodName);
+ .CreateEmptyBuilderMethod(memberSymbolInfo, methodName);
}
private Parameter GetStandardParameter(MemberSymbolInfo symbolInfo, string? defaultValue = null)
diff --git a/src/M31.FluentApi.Generator/CodeGeneration/CodeBoardElements/CodeBoard.cs b/src/M31.FluentApi.Generator/CodeGeneration/CodeBoardElements/CodeBoard.cs
index e3b05ce..2710b08 100644
--- a/src/M31.FluentApi.Generator/CodeGeneration/CodeBoardElements/CodeBoard.cs
+++ b/src/M31.FluentApi.Generator/CodeGeneration/CodeBoardElements/CodeBoard.cs
@@ -1,6 +1,8 @@
using M31.FluentApi.Generator.CodeBuilding;
using M31.FluentApi.Generator.CodeGeneration.CodeBoardActors.Commons;
+using M31.FluentApi.Generator.CodeGeneration.CodeBoardActors.MethodCreation;
using M31.FluentApi.Generator.CodeGeneration.CodeBoardActors.MethodCreation.Forks;
+using M31.FluentApi.Generator.CodeGeneration.CodeBoardElements.FluentApiComments;
using M31.FluentApi.Generator.SourceAnalyzers;
using M31.FluentApi.Generator.SourceGenerators;
using M31.FluentApi.Generator.SourceGenerators.AttributeElements;
@@ -31,6 +33,8 @@ private CodeBoard(
Constructor = null;
StaticConstructor = null;
InnerBodyCreationDelegates = new InnerBodyCreationDelegates();
+ TransformedComments = new TransformedComments();
+ GroupsToMethods = new Dictionary();
BuilderMethodToAttributeData = new Dictionary();
Forks = new List();
ReservedVariableNames = new ReservedVariableNames();
@@ -47,6 +51,8 @@ private CodeBoard(
internal Method? Constructor { get; set; }
internal Method? StaticConstructor { get; set; }
internal InnerBodyCreationDelegates InnerBodyCreationDelegates { get; }
+ internal TransformedComments TransformedComments { get; }
+ internal Dictionary GroupsToMethods { get; }
internal Dictionary BuilderMethodToAttributeData { get; }
internal IReadOnlyList Forks { get; set; }
internal ReservedVariableNames ReservedVariableNames { get; }
diff --git a/src/M31.FluentApi.Generator/CodeGeneration/CodeBoardElements/FluentApiComments/Comment.cs b/src/M31.FluentApi.Generator/CodeGeneration/CodeBoardElements/FluentApiComments/Comment.cs
new file mode 100644
index 0000000..ace85c7
--- /dev/null
+++ b/src/M31.FluentApi.Generator/CodeGeneration/CodeBoardElements/FluentApiComments/Comment.cs
@@ -0,0 +1,45 @@
+using M31.FluentApi.Generator.Commons;
+
+namespace M31.FluentApi.Generator.CodeGeneration.CodeBoardElements.FluentApiComments;
+
+internal class Comment
+{
+ internal string Tag { get; }
+ internal IReadOnlyList Attributes { get; }
+ internal string Content { get; }
+
+ internal Comment(string tag, IReadOnlyList attributes, string content)
+ {
+ Tag = tag;
+ Attributes = attributes;
+ Content = content;
+ }
+
+ internal string GetLine()
+ {
+ return $"/// <{Tag}{string.Join(string.Empty, Attributes.Select(a => $" {a}"))}>{Content}{Tag}>";
+ }
+
+ protected bool Equals(Comment other)
+ {
+ return Tag == other.Tag &&
+ Attributes.SequenceEqual(other.Attributes) &&
+ Content == other.Content;
+ }
+
+ public override bool Equals(object? obj)
+ {
+ if (ReferenceEquals(null, obj)) return false;
+ if (ReferenceEquals(this, obj)) return true;
+ if (obj.GetType() != this.GetType()) return false;
+ return Equals((Comment)obj);
+ }
+
+ public override int GetHashCode()
+ {
+ return new HashCode()
+ .Add(Tag)
+ .AddSequence(Attributes)
+ .Add(Content);
+ }
+}
\ No newline at end of file
diff --git a/src/M31.FluentApi.Generator/CodeGeneration/CodeBoardElements/FluentApiComments/CommentAttribute.cs b/src/M31.FluentApi.Generator/CodeGeneration/CodeBoardElements/FluentApiComments/CommentAttribute.cs
new file mode 100644
index 0000000..d1dcfbd
--- /dev/null
+++ b/src/M31.FluentApi.Generator/CodeGeneration/CodeBoardElements/FluentApiComments/CommentAttribute.cs
@@ -0,0 +1,39 @@
+using M31.FluentApi.Generator.Commons;
+
+namespace M31.FluentApi.Generator.CodeGeneration.CodeBoardElements.FluentApiComments;
+
+internal class CommentAttribute
+{
+ internal CommentAttribute(string key, string value)
+ {
+ Key = key;
+ Value = value;
+ }
+
+ internal string Key { get; }
+ internal string Value { get; }
+
+ protected bool Equals(CommentAttribute other)
+ {
+ return Key == other.Key &&
+ Value == other.Value;
+ }
+
+ public override bool Equals(object? obj)
+ {
+ if (ReferenceEquals(null, obj)) return false;
+ if (ReferenceEquals(this, obj)) return true;
+ if (obj.GetType() != this.GetType()) return false;
+ return Equals((CommentAttribute)obj);
+ }
+
+ public override int GetHashCode()
+ {
+ return new HashCode().Add(Key, Value);
+ }
+
+ public override string ToString()
+ {
+ return @$"{Key}=""{Value}""";
+ }
+}
\ No newline at end of file
diff --git a/src/M31.FluentApi.Generator/CodeGeneration/CodeBoardElements/FluentApiComments/Comments.cs b/src/M31.FluentApi.Generator/CodeGeneration/CodeBoardElements/FluentApiComments/Comments.cs
new file mode 100644
index 0000000..44dabcd
--- /dev/null
+++ b/src/M31.FluentApi.Generator/CodeGeneration/CodeBoardElements/FluentApiComments/Comments.cs
@@ -0,0 +1,37 @@
+using M31.FluentApi.Generator.Commons;
+
+namespace M31.FluentApi.Generator.CodeGeneration.CodeBoardElements.FluentApiComments;
+
+internal class Comments
+{
+ internal Comments(IReadOnlyList comments)
+ {
+ List = comments;
+ }
+
+ public IReadOnlyList List { get; }
+ internal bool Any => List.Count > 0;
+
+ internal List GetLines()
+ {
+ return List.Select(c => c.GetLine()).ToList();
+ }
+
+ protected bool Equals(Comments other)
+ {
+ return List.SequenceEqual(other.List);
+ }
+
+ public override bool Equals(object? obj)
+ {
+ if (ReferenceEquals(null, obj)) return false;
+ if (ReferenceEquals(this, obj)) return true;
+ if (obj.GetType() != this.GetType()) return false;
+ return Equals((Comments)obj);
+ }
+
+ public override int GetHashCode()
+ {
+ return new HashCode().AddSequence(List);
+ }
+}
\ No newline at end of file
diff --git a/src/M31.FluentApi.Generator/CodeGeneration/CodeBoardElements/FluentApiComments/FluentCommentsParser.cs b/src/M31.FluentApi.Generator/CodeGeneration/CodeBoardElements/FluentApiComments/FluentCommentsParser.cs
new file mode 100644
index 0000000..a7e7489
--- /dev/null
+++ b/src/M31.FluentApi.Generator/CodeGeneration/CodeBoardElements/FluentApiComments/FluentCommentsParser.cs
@@ -0,0 +1,42 @@
+using System.Text.RegularExpressions;
+
+namespace M31.FluentApi.Generator.CodeGeneration.CodeBoardElements.FluentApiComments;
+
+internal static class FluentCommentsParser
+{
+ private static readonly Regex commentRegex =
+ new Regex(@"<(?fluent\w+)(\s+(?[^>]+))?>\s*(?.*?)\s*\k>",
+ RegexOptions.Compiled | RegexOptions.Singleline);
+
+ private static readonly Regex attributeRegex = new Regex(@"(?\w+)\s*=\s*""(?[^""]*)""",
+ RegexOptions.Compiled | RegexOptions.Singleline);
+
+ internal static Comments Parse(string? commentXml)
+ {
+ if (commentXml == null)
+ {
+ return new Comments(Array.Empty());
+ }
+
+ List comments = new List();
+ MatchCollection matches = commentRegex.Matches(commentXml);
+
+ foreach (Match match in matches)
+ {
+ string tag = match.Groups["tag"].Value;
+ string attributes = match.Groups["attrs"].Value;
+ string content = match.Groups["content"].Value;
+ Comment comment = new Comment(tag, ParseCommentAttributes(attributes), content);
+ comments.Add(comment);
+ }
+
+ return new Comments(comments);
+ }
+
+ private static IReadOnlyList ParseCommentAttributes(string commentAttributes)
+ {
+ MatchCollection matches = attributeRegex.Matches(commentAttributes);
+ return matches.Cast().Select(m => new CommentAttribute(m.Groups["key"].Value, m.Groups["value"].Value))
+ .ToArray();
+ }
+}
\ No newline at end of file
diff --git a/src/M31.FluentApi.Generator/CodeGeneration/CodeBoardElements/FluentApiComments/MemberCommentKey.cs b/src/M31.FluentApi.Generator/CodeGeneration/CodeBoardElements/FluentApiComments/MemberCommentKey.cs
new file mode 100644
index 0000000..ed0a0f5
--- /dev/null
+++ b/src/M31.FluentApi.Generator/CodeGeneration/CodeBoardElements/FluentApiComments/MemberCommentKey.cs
@@ -0,0 +1,18 @@
+namespace M31.FluentApi.Generator.CodeGeneration.CodeBoardElements.FluentApiComments;
+
+internal record MemberCommentKey
+{
+ internal MemberCommentKey(string memberName, string method)
+ {
+ MemberName = memberName;
+ Method = method;
+ }
+
+ internal string MemberName { get; }
+ internal string Method { get; }
+
+ public override string ToString()
+ {
+ return $"{MemberName}-{Method}";
+ }
+}
\ No newline at end of file
diff --git a/src/M31.FluentApi.Generator/CodeGeneration/CodeBoardElements/FluentApiComments/TransformedComments.cs b/src/M31.FluentApi.Generator/CodeGeneration/CodeBoardElements/FluentApiComments/TransformedComments.cs
new file mode 100644
index 0000000..504135c
--- /dev/null
+++ b/src/M31.FluentApi.Generator/CodeGeneration/CodeBoardElements/FluentApiComments/TransformedComments.cs
@@ -0,0 +1,66 @@
+namespace M31.FluentApi.Generator.CodeGeneration.CodeBoardElements.FluentApiComments;
+
+internal class TransformedComments
+{
+ private readonly Dictionary memberComments;
+ private readonly Dictionary methodComments;
+
+ internal TransformedComments()
+ {
+ memberComments = new Dictionary();
+ methodComments = new Dictionary();
+ }
+
+ internal void AssignMemberComments(MemberCommentKey memberCommentKey, Comments comments)
+ {
+ if (memberComments.ContainsKey(memberCommentKey))
+ {
+ throw new InvalidOperationException(
+ $"{nameof(Comments)} for key {memberCommentKey} has already been assigned.");
+ }
+
+ memberComments[memberCommentKey] = comments;
+ }
+
+ internal Comments GetMemberComments(MemberCommentKey memberCommentKey, string[] parameterNames)
+ {
+ if (memberComments.TryGetValue(memberCommentKey, out Comments comments))
+ {
+ return new Comments(comments.List.Where(IsRelevant).ToArray());
+ }
+
+ return new Comments(Array.Empty());
+
+ bool IsRelevant(Comment comment)
+ {
+ if (comment.Tag != "param")
+ {
+ return true;
+ }
+
+ string? parameterName = comment.Attributes.FirstOrDefault(a => a.Key == "name")?.Value;
+ return parameterName != null && parameterNames.Contains(parameterName, StringComparer.InvariantCulture);
+ }
+ }
+
+ internal void AssignMethodComments(MethodSymbolInfo methodSymbolInfo, Comments comments)
+ {
+ if (methodComments.ContainsKey(methodSymbolInfo))
+ {
+ throw new InvalidOperationException(
+ $"{nameof(Comments)} for method {methodSymbolInfo.Name} has already been assigned.");
+ }
+
+ methodComments[methodSymbolInfo] = comments;
+ }
+
+ internal Comments GetMethodComments(MethodSymbolInfo methodSymbolInfo)
+ {
+ if (methodComments.TryGetValue(methodSymbolInfo, out Comments comments))
+ {
+ return comments;
+ }
+
+ return new Comments(Array.Empty());
+ }
+}
\ 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 4902a9b..1ef3654 100644
--- a/src/M31.FluentApi.Generator/CodeGeneration/CodeBoardElements/FluentApiSymbolInfo.cs
+++ b/src/M31.FluentApi.Generator/CodeGeneration/CodeBoardElements/FluentApiSymbolInfo.cs
@@ -1,3 +1,4 @@
+using M31.FluentApi.Generator.CodeGeneration.CodeBoardElements.FluentApiComments;
using M31.FluentApi.Generator.Commons;
using Microsoft.CodeAnalysis;
@@ -9,13 +10,15 @@ internal FluentApiSymbolInfo(
string name,
string declaringClassNameWithTypeParameters,
Accessibility accessibility,
- bool requiresReflection)
+ bool requiresReflection,
+ Comments comments)
{
Name = name;
NameInCamelCase = Name.TrimStart('_').FirstCharToLower();
DeclaringClassNameWithTypeParameters = declaringClassNameWithTypeParameters;
Accessibility = accessibility;
RequiresReflection = requiresReflection;
+ Comments = comments;
}
internal string Name { get; }
@@ -23,13 +26,15 @@ internal FluentApiSymbolInfo(
internal string DeclaringClassNameWithTypeParameters { get; }
internal Accessibility Accessibility { get; }
internal bool RequiresReflection { get; }
+ internal Comments Comments { get; }
protected bool Equals(FluentApiSymbolInfo other)
{
return Name == other.Name &&
DeclaringClassNameWithTypeParameters == other.DeclaringClassNameWithTypeParameters &&
Accessibility == other.Accessibility &&
- RequiresReflection == other.RequiresReflection;
+ RequiresReflection == other.RequiresReflection &&
+ Comments.Equals(other.Comments);
}
public override bool Equals(object? obj)
@@ -42,6 +47,7 @@ public override bool Equals(object? obj)
public override int GetHashCode()
{
- return new HashCode().Add(Name, DeclaringClassNameWithTypeParameters, Accessibility, RequiresReflection);
+ return new HashCode()
+ .Add(Name, DeclaringClassNameWithTypeParameters, Accessibility, RequiresReflection, Comments);
}
}
\ No newline at end of file
diff --git a/src/M31.FluentApi.Generator/CodeGeneration/CodeBoardElements/InnerBodyCreationDelegates.cs b/src/M31.FluentApi.Generator/CodeGeneration/CodeBoardElements/InnerBodyCreationDelegates.cs
index 3feeb62..c8b8425 100644
--- a/src/M31.FluentApi.Generator/CodeGeneration/CodeBoardElements/InnerBodyCreationDelegates.cs
+++ b/src/M31.FluentApi.Generator/CodeGeneration/CodeBoardElements/InnerBodyCreationDelegates.cs
@@ -17,7 +17,7 @@ internal void AssignSetMemberCode(string memberName, SetMemberCode setMemberCode
{
if (memberToSetMemberCode.ContainsKey(memberName))
{
- throw new GenerationException(
+ throw new InvalidOperationException(
$"{nameof(SetMemberCode)} for member {memberName} has already been assigned.");
}
@@ -33,7 +33,7 @@ internal void AssignCallMethodCode(MethodSymbolInfo methodSymbolInfo, CallMethod
{
if (methodToCallMethodCode.ContainsKey(methodSymbolInfo))
{
- throw new GenerationException(
+ throw new InvalidOperationException(
$"{nameof(CallMethodCode)} for method {methodSymbolInfo.Name} has already been assigned.");
}
diff --git a/src/M31.FluentApi.Generator/CodeGeneration/CodeBoardElements/MemberSymbolInfo.cs b/src/M31.FluentApi.Generator/CodeGeneration/CodeBoardElements/MemberSymbolInfo.cs
index 8fc7fa3..cd19d0e 100644
--- a/src/M31.FluentApi.Generator/CodeGeneration/CodeBoardElements/MemberSymbolInfo.cs
+++ b/src/M31.FluentApi.Generator/CodeGeneration/CodeBoardElements/MemberSymbolInfo.cs
@@ -1,3 +1,4 @@
+using M31.FluentApi.Generator.CodeGeneration.CodeBoardElements.FluentApiComments;
using M31.FluentApi.Generator.Commons;
using M31.FluentApi.Generator.SourceGenerators.Collections;
using Microsoft.CodeAnalysis;
@@ -15,8 +16,9 @@ internal MemberSymbolInfo(
string typeForCodeGeneration,
bool isNullable,
bool isProperty,
- CollectionType? collectionType)
- : base(name, declaringClassNameWithTypeParameters, accessibility, requiresReflection)
+ CollectionType? collectionType,
+ Comments comments)
+ : base(name, declaringClassNameWithTypeParameters, accessibility, requiresReflection, comments)
{
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 8a534d4..fd10609 100644
--- a/src/M31.FluentApi.Generator/CodeGeneration/CodeBoardElements/MethodSymbolInfo.cs
+++ b/src/M31.FluentApi.Generator/CodeGeneration/CodeBoardElements/MethodSymbolInfo.cs
@@ -1,3 +1,4 @@
+using M31.FluentApi.Generator.CodeGeneration.CodeBoardElements.FluentApiComments;
using M31.FluentApi.Generator.Commons;
using M31.FluentApi.Generator.SourceGenerators.Generics;
using Microsoft.CodeAnalysis;
@@ -13,8 +14,9 @@ internal MethodSymbolInfo(
bool requiresReflection,
GenericInfo? genericInfo,
IReadOnlyCollection parameterInfos,
- string returnType)
- : base(name, declaringClassNameWithTypeParameters, accessibility, requiresReflection)
+ string returnType,
+ Comments comments)
+ : base(name, declaringClassNameWithTypeParameters, accessibility, requiresReflection, comments)
{
GenericInfo = genericInfo;
ParameterInfos = parameterInfos;
diff --git a/src/M31.FluentApi.Generator/CodeGeneration/CodeGenerator.cs b/src/M31.FluentApi.Generator/CodeGeneration/CodeGenerator.cs
index 51b7e22..3a3544d 100644
--- a/src/M31.FluentApi.Generator/CodeGeneration/CodeGenerator.cs
+++ b/src/M31.FluentApi.Generator/CodeGeneration/CodeGenerator.cs
@@ -1,16 +1,58 @@
using M31.FluentApi.Generator.CodeGeneration.CodeBoardActors;
using M31.FluentApi.Generator.CodeGeneration.CodeBoardActors.BuilderMethodsGeneration;
using M31.FluentApi.Generator.CodeGeneration.CodeBoardActors.DuplicateMethodsChecking;
+using M31.FluentApi.Generator.CodeGeneration.CodeBoardActors.FluentApiComments;
using M31.FluentApi.Generator.CodeGeneration.CodeBoardActors.InnerBodyGeneration;
using M31.FluentApi.Generator.CodeGeneration.CodeBoardActors.MethodCreation.Forks;
using M31.FluentApi.Generator.CodeGeneration.CodeBoardElements;
using M31.FluentApi.Generator.SourceGenerators;
+using BuilderMethods = M31.FluentApi.Generator.CodeGeneration.CodeBoardActors.MethodCreation.BuilderMethods;
namespace M31.FluentApi.Generator.CodeGeneration;
internal static class CodeGenerator
{
internal static CodeGeneratorResult GenerateCode(FluentApiClassInfo classInfo, CancellationToken cancellationToken)
+ {
+ BuilderAndTargetInfo builderAndTargetInfo = CreateBuilderAndTargetInfo(classInfo);
+ CodeBoard codeBoard = CreateCodeBoard(classInfo, cancellationToken, builderAndTargetInfo);
+
+ List actors = new List()
+ {
+ new CommentsGenerator(),
+ new EntityFieldGenerator(),
+ new ConstructorGenerator(),
+ new InnerBodyCreator(),
+ new ForkCreator(),
+ new DuplicateMethodsChecker(),
+ new InitialStepMethodGenerator(),
+ new BuilderGenerator(),
+ };
+
+ foreach (ICodeBoardActor actor in actors)
+ {
+ if (cancellationToken.IsCancellationRequested || codeBoard.HasErrors)
+ {
+ break;
+ }
+
+ actor.Modify(codeBoard);
+ }
+
+ if (cancellationToken.IsCancellationRequested)
+ {
+ return CodeGeneratorResult.Cancelled();
+ }
+
+ if (codeBoard.HasErrors)
+ {
+ return CodeGeneratorResult.WithErrors(codeBoard.Diagnostics);
+ }
+
+ return new CodeGeneratorResult(codeBoard.CodeFile.ToString(), codeBoard.Diagnostics);
+ }
+
+ private static BuilderAndTargetInfo CreateBuilderAndTargetInfo(FluentApiClassInfo classInfo)
{
BuilderAndTargetInfo builderAndTargetInfo =
new BuilderAndTargetInfo(
@@ -21,7 +63,12 @@ internal static CodeGeneratorResult GenerateCode(FluentApiClassInfo classInfo, C
classInfo.IsInternal,
classInfo.ConstructorInfo,
classInfo.BuilderClassName);
+ return builderAndTargetInfo;
+ }
+ private static CodeBoard CreateCodeBoard(FluentApiClassInfo classInfo, CancellationToken cancellationToken,
+ BuilderAndTargetInfo builderAndTargetInfo)
+ {
CodeBoard codeBoard = CodeBoard.Create(
builderAndTargetInfo,
classInfo.FluentApiInfos,
@@ -29,6 +76,15 @@ internal static CodeGeneratorResult GenerateCode(FluentApiClassInfo classInfo, C
classInfo.UsingStatements,
classInfo.NewLineString,
cancellationToken);
+ return codeBoard;
+ }
+
+ internal static Dictionary GenerateBuilderMethods(
+ FluentApiClassInfo classInfo,
+ CancellationToken cancellationToken)
+ {
+ BuilderAndTargetInfo builderAndTargetInfo = CreateBuilderAndTargetInfo(classInfo);
+ CodeBoard codeBoard = CreateCodeBoard(classInfo, cancellationToken, builderAndTargetInfo);
List actors = new List()
{
@@ -36,9 +92,6 @@ internal static CodeGeneratorResult GenerateCode(FluentApiClassInfo classInfo, C
new ConstructorGenerator(),
new InnerBodyCreator(),
new ForkCreator(),
- new DuplicateMethodsChecker(),
- new InitialStepMethodGenerator(),
- new BuilderGenerator(),
};
foreach (ICodeBoardActor actor in actors)
@@ -53,14 +106,14 @@ internal static CodeGeneratorResult GenerateCode(FluentApiClassInfo classInfo, C
if (cancellationToken.IsCancellationRequested)
{
- return CodeGeneratorResult.Cancelled();
+ return new Dictionary();
}
if (codeBoard.HasErrors)
{
- return CodeGeneratorResult.WithErrors(codeBoard.Diagnostics);
+ return new Dictionary();
}
- return new CodeGeneratorResult(codeBoard.CodeFile.ToString(), codeBoard.Diagnostics);
+ return codeBoard.GroupsToMethods;
}
}
\ No newline at end of file
diff --git a/src/M31.FluentApi.Generator/Commons/GenerationException.cs b/src/M31.FluentApi.Generator/Commons/GenerationException.cs
index 879d0a4..bb97a9e 100644
--- a/src/M31.FluentApi.Generator/Commons/GenerationException.cs
+++ b/src/M31.FluentApi.Generator/Commons/GenerationException.cs
@@ -3,7 +3,7 @@ namespace M31.FluentApi.Generator.Commons;
internal class GenerationException : Exception
{
///
- /// Initializes a new instance of the class. Throw this exception only for errors
+ /// Initializes a new instance of the class. Throw this exception only for usage errors
/// that should not occur because they were already handled with diagnostics. Therefore, if this exception is
/// actually thrown, the diagnostics need to be improved.
///
diff --git a/src/M31.FluentApi.Generator/Commons/HashCode.cs b/src/M31.FluentApi.Generator/Commons/HashCode.cs
index 55559dd..883b64f 100644
--- a/src/M31.FluentApi.Generator/Commons/HashCode.cs
+++ b/src/M31.FluentApi.Generator/Commons/HashCode.cs
@@ -77,7 +77,6 @@ internal HashCode AddSequence(IEnumerable items)
{
hash = hash * 23 + item?.GetHashCode() ?? 0;
}
-
}
return this;
diff --git a/src/M31.FluentApi.Generator/Commons/StringBuilderExtensions.cs b/src/M31.FluentApi.Generator/Commons/StringBuilderExtensions.cs
index ed2ea5e..8c49458 100644
--- a/src/M31.FluentApi.Generator/Commons/StringBuilderExtensions.cs
+++ b/src/M31.FluentApi.Generator/Commons/StringBuilderExtensions.cs
@@ -4,13 +4,13 @@ namespace M31.FluentApi.Generator.Commons;
internal static class StringBuilderExtensions
{
- internal static StringBuilder Append(this StringBuilder stringBuilder, string? value, bool condition)
- {
- if (condition)
- {
- stringBuilder.Append(value);
- }
+ internal static StringBuilder Append(this StringBuilder stringBuilder, string? value, bool condition)
+ {
+ if (condition)
+ {
+ stringBuilder.Append(value);
+ }
- return stringBuilder;
- }
+ return stringBuilder;
+ }
}
\ No newline at end of file
diff --git a/src/M31.FluentApi.Generator/M31.FluentApi.Generator.csproj b/src/M31.FluentApi.Generator/M31.FluentApi.Generator.csproj
index 228681a..e8c74d4 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.10.0
+ 1.11.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/SourceAnalyzers/FluentApiComments/FluentApiCommentsProvider.cs b/src/M31.FluentApi.Generator/SourceAnalyzers/FluentApiComments/FluentApiCommentsProvider.cs
new file mode 100644
index 0000000..45167f6
--- /dev/null
+++ b/src/M31.FluentApi.Generator/SourceAnalyzers/FluentApiComments/FluentApiCommentsProvider.cs
@@ -0,0 +1,174 @@
+using System.Composition;
+using M31.FluentApi.Generator.CodeGeneration;
+using M31.FluentApi.Generator.CodeGeneration.CodeBoardActors.MethodCreation;
+using M31.FluentApi.Generator.Commons;
+using M31.FluentApi.Generator.SourceGenerators;
+using M31.FluentApi.Generator.SourceGenerators.AttributeElements;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CodeActions;
+using Microsoft.CodeAnalysis.CodeRefactorings;
+using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+
+namespace M31.FluentApi.Generator.SourceAnalyzers.FluentApiComments;
+
+[ExportCodeRefactoringProvider(LanguageNames.CSharp, Name = nameof(FluentApiCommentsProvider))]
+[Shared]
+internal class FluentApiCommentsProvider : CodeRefactoringProvider
+{
+ public override async Task ComputeRefactoringsAsync(CodeRefactoringContext context)
+ {
+ Document document = context.Document;
+
+ SyntaxNode? root = await document.GetSyntaxRootAsync(context.CancellationToken);
+ if (root == null)
+ {
+ return;
+ }
+
+ SyntaxNode node = root.FindNode(context.Span);
+
+ if (node is not MemberDeclarationSyntax memberSyntax)
+ {
+ return;
+ }
+
+ if (!memberSyntax.Parent.IsClassStructOrRecordSyntax(out TypeDeclarationSyntax typeSyntax))
+ {
+ return;
+ }
+
+ if (context.CancellationToken.IsCancellationRequested)
+ {
+ return;
+ }
+
+ if (!typeSyntax.AttributeLists
+ .SelectMany(list => list.Attributes)
+ .Any(attr => attr.IsFluentApiAttributeSyntax()))
+ {
+ return;
+ }
+
+ SemanticModel? semanticModel = await document.GetSemanticModelAsync(context.CancellationToken);
+ if (semanticModel == null)
+ {
+ return;
+ }
+
+ ISymbol? memberSymbol = semanticModel.GetDeclaredSymbol(memberSyntax, context.CancellationToken);
+ if (memberSymbol == null)
+ {
+ return;
+ }
+
+ if (context.CancellationToken.IsCancellationRequested)
+ {
+ return;
+ }
+
+ AttributeDataExtended[] attributeDataExtended =
+ memberSymbol.GetAttributes()
+ .Select(AttributeDataExtended.Create)
+ .OfType()
+ .ToArray();
+
+ if (!attributeDataExtended.Any(a => Attributes.IsMainAttribute(a.FullName)))
+ {
+ return;
+ }
+
+ CodeAction action = CodeAction.Create("Create fluent API documentation comments",
+ c => AddFluentSummaryAsync(
+ memberSyntax, memberSymbol, context.Document, root, semanticModel, typeSyntax, c),
+ nameof(FluentApiCommentsProvider));
+
+ context.RegisterRefactoring(action);
+ }
+
+ private Task AddFluentSummaryAsync(
+ MemberDeclarationSyntax memberSyntax,
+ ISymbol memberSymbol,
+ Document document,
+ SyntaxNode root,
+ SemanticModel semanticModel,
+ TypeDeclarationSyntax typeSyntax,
+ CancellationToken cancellationToken)
+ {
+ ClassInfoResult classInfoResult =
+ ClassInfoFactory.CreateFluentApiClassInfo(
+ semanticModel,
+ typeSyntax,
+ SourceGenerator.GeneratorConfig,
+ cancellationToken);
+
+ if (classInfoResult.ClassInfo == null)
+ {
+ return Task.FromResult(document);
+ }
+
+ FluentApiInfoGroup? group = classInfoResult.ClassInfo.AdditionalInfo.FluentApiInfoGroups.FirstOrDefault(
+ g => g.FluentApiInfos.Any(i => i.SymbolInfo.Name == memberSymbol.Name));
+
+ if (group == null)
+ {
+ return Task.FromResult(document);
+ }
+
+ if (group.IsCompoundGroup && group.FluentApiInfos.First().SymbolInfo.Name != memberSymbol.Name)
+ {
+ memberSymbol = group.FluentApiInfos.First().AdditionalInfo.Symbol;
+ MemberDeclarationSyntax? firstMemberSyntax =
+ memberSymbol.DeclaringSyntaxReferences.FirstOrDefault()?.GetSyntax() as MemberDeclarationSyntax;
+ if (firstMemberSyntax == null)
+ {
+ return Task.FromResult(document);
+ }
+
+ memberSyntax = firstMemberSyntax;
+ }
+
+ Dictionary groupToMethods =
+ CodeGenerator.GenerateBuilderMethods(classInfoResult.ClassInfo, cancellationToken);
+ BuilderMethods builderMethods = groupToMethods[group];
+ List commentsTemplate = MethodsToCommentsTemplate.CreateCommentsTemplate(builderMethods);
+
+ if (commentsTemplate.Count <= 2)
+ {
+ return Task.FromResult(document);
+ }
+
+ SyntaxTriviaList leadingTrivia = memberSyntax.GetLeadingTrivia();
+ string padding = GetPadding(leadingTrivia);
+ string nl = classInfoResult.ClassInfo.NewLineString;
+
+ commentsTemplate[0] = $"{commentsTemplate[0]}{nl}";
+ for (int i = 1; i < commentsTemplate.Count - 1; i++)
+ {
+ commentsTemplate[i] = $"{padding}{commentsTemplate[i]}{nl}";
+ }
+
+ commentsTemplate[commentsTemplate.Count - 1] =
+ $"{padding}{commentsTemplate[commentsTemplate.Count - 1]}{nl}{padding}";
+
+ SyntaxTriviaList xmlComment =
+ SyntaxFactory.TriviaList(commentsTemplate.Select(SyntaxFactory.Comment).ToArray());
+
+ MemberDeclarationSyntax newMemberSyntax = memberSyntax.WithLeadingTrivia(leadingTrivia.AddRange(xmlComment));
+
+ SyntaxNode newRoot = root.ReplaceNode(memberSyntax, newMemberSyntax);
+ return Task.FromResult(document.WithSyntaxRoot(newRoot));
+ }
+
+ private static string GetPadding(SyntaxTriviaList leadingTrivia)
+ {
+ const string fallback = " ";
+ SyntaxTrivia? first = leadingTrivia.FirstOrDefault();
+ if (first == null || first.Value.Kind() != SyntaxKind.WhitespaceTrivia)
+ {
+ return fallback;
+ }
+
+ return first.Value.ToString();
+ }
+}
\ No newline at end of file
diff --git a/src/M31.FluentApi.Generator/SourceAnalyzers/FluentApiComments/MethodsToCommentsTemplate.cs b/src/M31.FluentApi.Generator/SourceAnalyzers/FluentApiComments/MethodsToCommentsTemplate.cs
new file mode 100644
index 0000000..d5c8680
--- /dev/null
+++ b/src/M31.FluentApi.Generator/SourceAnalyzers/FluentApiComments/MethodsToCommentsTemplate.cs
@@ -0,0 +1,91 @@
+using M31.FluentApi.Generator.CodeGeneration.CodeBoardActors.Commons;
+using M31.FluentApi.Generator.CodeGeneration.CodeBoardActors.MethodCreation;
+
+namespace M31.FluentApi.Generator.SourceAnalyzers.FluentApiComments;
+
+internal class MethodsToCommentsTemplate
+{
+ private readonly List comments;
+
+ private MethodsToCommentsTemplate()
+ {
+ comments = new List();
+ }
+
+ internal static List CreateCommentsTemplate(BuilderMethods builderMethods)
+ {
+ if (builderMethods.Methods.Count == 0)
+ {
+ return new List();
+ }
+
+ MethodsToCommentsTemplate instance = new MethodsToCommentsTemplate();
+
+ IGrouping[] groups = builderMethods.Methods.GroupBy(m => m.MethodName).ToArray();
+
+ if (groups.Length == 1)
+ {
+ instance.CreateCommentsTemplateWithoutMethodNames(groups[0].ToArray());
+ }
+ else
+ {
+ foreach (IGrouping group in groups)
+ {
+ instance.CreateCommentsTemplateWithMethodNames(group.ToArray());
+ }
+ }
+
+ return instance.comments;
+ }
+
+ private void CreateCommentsTemplateWithoutMethodNames(BuilderMethod[] sameNameBuilderMethods)
+ {
+ if (comments.Count != 0)
+ {
+ comments.Add("////");
+ }
+
+ comments.Add("//// ");
+ comments.Add("//// ...");
+ comments.Add("//// ");
+
+ foreach (string parameterName in GetDistinctParameterNames(sameNameBuilderMethods))
+ {
+ comments.Add($"//// ...");
+ }
+
+ if (sameNameBuilderMethods.Any(b => b.ReturnTypeToRespect != "void"))
+ {
+ comments.Add("//// ...");
+ }
+ }
+
+ private void CreateCommentsTemplateWithMethodNames(BuilderMethod[] sameNameBuilderMethods)
+ {
+ string method = sameNameBuilderMethods[0].MethodName;
+
+ if (comments.Count != 0)
+ {
+ comments.Add("////");
+ }
+
+ comments.Add($"//// ");
+ comments.Add("//// ...");
+ comments.Add("//// ");
+
+ foreach (string parameterName in GetDistinctParameterNames(sameNameBuilderMethods))
+ {
+ comments.Add($"//// ...");
+ }
+
+ if (sameNameBuilderMethods.Any(b => b.ReturnTypeToRespect != "void"))
+ {
+ comments.Add($"//// ...");
+ }
+ }
+
+ private static IEnumerable GetDistinctParameterNames(BuilderMethod[] builderMethods)
+ {
+ return builderMethods.SelectMany(m => m.Parameters).Select(p => p.Name).Distinct();
+ }
+}
\ No newline at end of file
diff --git a/src/M31.FluentApi.Generator/SourceAnalyzers/FluentApiDiagnostics.cs b/src/M31.FluentApi.Generator/SourceAnalyzers/FluentApiDiagnostics.cs
index 4572acf..9d0b5c0 100644
--- a/src/M31.FluentApi.Generator/SourceAnalyzers/FluentApiDiagnostics.cs
+++ b/src/M31.FluentApi.Generator/SourceAnalyzers/FluentApiDiagnostics.cs
@@ -360,9 +360,9 @@ internal static class FluentLambdaMemberWithoutFluentApi
{
internal static readonly DiagnosticDescriptor Descriptor = new DiagnosticDescriptor(
id: "M31FA022",
- title: "Fluent lambda member without Fluent API",
+ title: "Fluent lambda member without fluent API",
messageFormat: "FluentLambda can not be used on a member of type '{0}'. " +
- "Type '{0}' does not have a Fluent API.",
+ "Type '{0}' does not have a fluent API.",
category: "M31.Usage",
defaultSeverity: DiagnosticSeverity.Error,
isEnabledByDefault: true);
diff --git a/src/M31.FluentApi.Generator/SourceGenerators/AttributeElements/AttributeDataExtended.cs b/src/M31.FluentApi.Generator/SourceGenerators/AttributeElements/AttributeDataExtended.cs
index bc6b31c..9d9c1f2 100644
--- a/src/M31.FluentApi.Generator/SourceGenerators/AttributeElements/AttributeDataExtended.cs
+++ b/src/M31.FluentApi.Generator/SourceGenerators/AttributeElements/AttributeDataExtended.cs
@@ -4,7 +4,7 @@ namespace M31.FluentApi.Generator.SourceGenerators.AttributeElements;
internal class AttributeDataExtended
{
- internal AttributeDataExtended(AttributeData attributeData, string fullName, string shortName)
+ private AttributeDataExtended(AttributeData attributeData, string fullName, string shortName)
{
AttributeData = attributeData;
FullName = fullName;
diff --git a/src/M31.FluentApi.Generator/SourceGenerators/AttributeInfo/AttributeInfoBase.cs b/src/M31.FluentApi.Generator/SourceGenerators/AttributeInfo/AttributeInfoBase.cs
index 8b31708..a68a948 100644
--- a/src/M31.FluentApi.Generator/SourceGenerators/AttributeInfo/AttributeInfoBase.cs
+++ b/src/M31.FluentApi.Generator/SourceGenerators/AttributeInfo/AttributeInfoBase.cs
@@ -8,5 +8,5 @@ protected AttributeInfoBase(int builderStep)
}
internal int BuilderStep { get; }
- internal abstract string FluentMethodName { get; }
+ internal abstract IReadOnlyList FluentMethodNames { get; }
}
\ No newline at end of file
diff --git a/src/M31.FluentApi.Generator/SourceGenerators/AttributeInfo/FluentCollectionAttributeInfo.cs b/src/M31.FluentApi.Generator/SourceGenerators/AttributeInfo/FluentCollectionAttributeInfo.cs
index 85816ad..b0ef0d5 100644
--- a/src/M31.FluentApi.Generator/SourceGenerators/AttributeInfo/FluentCollectionAttributeInfo.cs
+++ b/src/M31.FluentApi.Generator/SourceGenerators/AttributeInfo/FluentCollectionAttributeInfo.cs
@@ -29,7 +29,9 @@ private FluentCollectionAttributeInfo(
internal string? WithItem { get; }
internal string? WithZeroItems { get; }
internal LambdaBuilderInfo? LambdaBuilderInfo { get; }
- internal override string FluentMethodName => WithItems;
+
+ internal override IReadOnlyList FluentMethodNames =>
+ new string?[] { WithItems, WithItem, WithZeroItems }.OfType().ToArray();
internal static FluentCollectionAttributeInfo Create(
AttributeData attributeData,
diff --git a/src/M31.FluentApi.Generator/SourceGenerators/AttributeInfo/FluentDefaultAttributeInfo.cs b/src/M31.FluentApi.Generator/SourceGenerators/AttributeInfo/FluentDefaultAttributeInfo.cs
index 2ffa8dc..59e9e59 100644
--- a/src/M31.FluentApi.Generator/SourceGenerators/AttributeInfo/FluentDefaultAttributeInfo.cs
+++ b/src/M31.FluentApi.Generator/SourceGenerators/AttributeInfo/FluentDefaultAttributeInfo.cs
@@ -12,10 +12,7 @@ private FluentDefaultAttributeInfo(string method)
internal string Method { get; }
- internal override IReadOnlyCollection MethodNames()
- {
- return new string[] { Method };
- }
+ internal override IReadOnlyList FluentMethodNames => new string[] { Method };
internal static FluentDefaultAttributeInfo Create(AttributeData attributeData, string memberName)
{
diff --git a/src/M31.FluentApi.Generator/SourceGenerators/AttributeInfo/FluentLambdaAttributeInfo.cs b/src/M31.FluentApi.Generator/SourceGenerators/AttributeInfo/FluentLambdaAttributeInfo.cs
index 03398d6..e810909 100644
--- a/src/M31.FluentApi.Generator/SourceGenerators/AttributeInfo/FluentLambdaAttributeInfo.cs
+++ b/src/M31.FluentApi.Generator/SourceGenerators/AttributeInfo/FluentLambdaAttributeInfo.cs
@@ -14,7 +14,7 @@ private FluentLambdaAttributeInfo(int builderStep, string method, LambdaBuilderI
internal string Method { get; }
internal LambdaBuilderInfo BuilderInfo { get; }
- internal override string FluentMethodName => Method;
+ internal override IReadOnlyList FluentMethodNames => new string[] { Method };
internal static FluentLambdaAttributeInfo Create(
AttributeData attributeData,
diff --git a/src/M31.FluentApi.Generator/SourceGenerators/AttributeInfo/FluentMemberAttributeInfo.cs b/src/M31.FluentApi.Generator/SourceGenerators/AttributeInfo/FluentMemberAttributeInfo.cs
index ac8bf61..0b9fa8c 100644
--- a/src/M31.FluentApi.Generator/SourceGenerators/AttributeInfo/FluentMemberAttributeInfo.cs
+++ b/src/M31.FluentApi.Generator/SourceGenerators/AttributeInfo/FluentMemberAttributeInfo.cs
@@ -19,7 +19,7 @@ private FluentMemberAttributeInfo(
internal string Method { get; }
internal int ParameterPosition { get; }
- internal override string FluentMethodName => Method;
+ internal override IReadOnlyList FluentMethodNames => new string[] { Method };
internal LambdaBuilderInfo? LambdaBuilderInfo { get; }
internal static FluentMemberAttributeInfo Create(
diff --git a/src/M31.FluentApi.Generator/SourceGenerators/AttributeInfo/FluentMethodAttributeInfo.cs b/src/M31.FluentApi.Generator/SourceGenerators/AttributeInfo/FluentMethodAttributeInfo.cs
index 0e6d561..b2d76fd 100644
--- a/src/M31.FluentApi.Generator/SourceGenerators/AttributeInfo/FluentMethodAttributeInfo.cs
+++ b/src/M31.FluentApi.Generator/SourceGenerators/AttributeInfo/FluentMethodAttributeInfo.cs
@@ -12,7 +12,7 @@ private FluentMethodAttributeInfo(int builderStep, string method)
}
internal string Method { get; }
- internal override string FluentMethodName => Method;
+ internal override IReadOnlyList FluentMethodNames => new string[] { Method };
internal static FluentMethodAttributeInfo Create(AttributeData attributeData, string memberName)
{
diff --git a/src/M31.FluentApi.Generator/SourceGenerators/AttributeInfo/FluentNullableAttributeInfo.cs b/src/M31.FluentApi.Generator/SourceGenerators/AttributeInfo/FluentNullableAttributeInfo.cs
index 1c6d1eb..a0a1343 100644
--- a/src/M31.FluentApi.Generator/SourceGenerators/AttributeInfo/FluentNullableAttributeInfo.cs
+++ b/src/M31.FluentApi.Generator/SourceGenerators/AttributeInfo/FluentNullableAttributeInfo.cs
@@ -11,11 +11,7 @@ private FluentNullableAttributeInfo(string method)
}
internal string Method { get; }
-
- internal override IReadOnlyCollection MethodNames()
- {
- return new string[] { Method };
- }
+ internal override IReadOnlyList FluentMethodNames => new string[] { Method };
internal static FluentNullableAttributeInfo Create(AttributeData attributeData, string memberName)
{
diff --git a/src/M31.FluentApi.Generator/SourceGenerators/AttributeInfo/FluentPredicateAttributeInfo.cs b/src/M31.FluentApi.Generator/SourceGenerators/AttributeInfo/FluentPredicateAttributeInfo.cs
index f84a0cd..1d89ab3 100644
--- a/src/M31.FluentApi.Generator/SourceGenerators/AttributeInfo/FluentPredicateAttributeInfo.cs
+++ b/src/M31.FluentApi.Generator/SourceGenerators/AttributeInfo/FluentPredicateAttributeInfo.cs
@@ -14,11 +14,12 @@ private FluentPredicateAttributeInfo(int builderStep, string method, string nega
internal string Method { get; }
internal string NegatedMethod { get; }
- internal override string FluentMethodName => Method;
+ internal override IReadOnlyList FluentMethodNames => new string[] { Method, NegatedMethod };
internal static FluentPredicateAttributeInfo Create(AttributeData attributeData, string memberName)
{
- (int builderStep, string method, string negatedMethod) = attributeData.GetConstructorArguments();
+ (int builderStep, string method, string negatedMethod) =
+ attributeData.GetConstructorArguments();
method = NameCreator.CreateName(method, memberName);
negatedMethod = NameCreator.CreateName(negatedMethod, memberName);
return new FluentPredicateAttributeInfo(builderStep, method, negatedMethod);
diff --git a/src/M31.FluentApi.Generator/SourceGenerators/AttributeInfo/OrthogonalAttributeInfoBase.cs b/src/M31.FluentApi.Generator/SourceGenerators/AttributeInfo/OrthogonalAttributeInfoBase.cs
index 91eaf2e..4e46706 100644
--- a/src/M31.FluentApi.Generator/SourceGenerators/AttributeInfo/OrthogonalAttributeInfoBase.cs
+++ b/src/M31.FluentApi.Generator/SourceGenerators/AttributeInfo/OrthogonalAttributeInfoBase.cs
@@ -2,5 +2,5 @@ namespace M31.FluentApi.Generator.SourceGenerators.AttributeInfo;
internal abstract record OrthogonalAttributeInfoBase
{
- internal abstract IReadOnlyCollection MethodNames();
+ internal abstract IReadOnlyList FluentMethodNames { get; }
}
\ No newline at end of file
diff --git a/src/M31.FluentApi.Generator/SourceGenerators/ClassInfoFactory.cs b/src/M31.FluentApi.Generator/SourceGenerators/ClassInfoFactory.cs
index 829d953..7137f75 100644
--- a/src/M31.FluentApi.Generator/SourceGenerators/ClassInfoFactory.cs
+++ b/src/M31.FluentApi.Generator/SourceGenerators/ClassInfoFactory.cs
@@ -108,7 +108,8 @@ private ClassInfoResult CreateFluentApiClassInfoInternal(
return null;
}
- FluentApiInfo? fluentApiInfo = TryCreateFluentApiInfo(member, declaringClassNameWithGenericParameters);
+ FluentApiInfo? fluentApiInfo = TryCreateFluentApiInfo(
+ member, declaringClassNameWithGenericParameters, cancellationToken);
if (fluentApiInfo != null)
{
@@ -201,7 +202,8 @@ with the fewest parameters that is explicitly declared. */
constructors[0].DeclaredAccessibility != Accessibility.Public);
}
- private FluentApiInfo? TryCreateFluentApiInfo(ISymbol symbol, string declaringClassNameWithTypeParameters)
+ private FluentApiInfo? TryCreateFluentApiInfo(
+ ISymbol symbol, string declaringClassNameWithTypeParameters, CancellationToken cancellationToken)
{
AttributeDataExtractor extractor = new AttributeDataExtractor(report);
FluentApiAttributeData? attributeData = extractor.GetAttributeData(symbol);
@@ -218,7 +220,8 @@ with the fewest parameters that is explicitly declared. */
}
FluentApiInfoCreator fluentApiInfoCreator = new FluentApiInfoCreator(report);
- return fluentApiInfoCreator.Create(symbol, attributeData, declaringClassNameWithTypeParameters);
+ return fluentApiInfoCreator.Create(
+ symbol, attributeData, declaringClassNameWithTypeParameters, cancellationToken);
}
public static string AugmentTypeNameWithGenericParameters(string typeName, GenericInfo? genericInfo)
diff --git a/src/M31.FluentApi.Generator/SourceGenerators/FluentApiInfo.cs b/src/M31.FluentApi.Generator/SourceGenerators/FluentApiInfo.cs
index 7267b5e..dda0b71 100644
--- a/src/M31.FluentApi.Generator/SourceGenerators/FluentApiInfo.cs
+++ b/src/M31.FluentApi.Generator/SourceGenerators/FluentApiInfo.cs
@@ -31,7 +31,6 @@ internal FluentApiInfo(
internal IReadOnlyCollection OrthogonalAttributeInfos { get; }
internal IReadOnlyCollection ControlAttributeInfos { get; }
internal FluentApiAdditionalInfo AdditionalInfo { get; }
- internal string FluentMethodName => AttributeInfo.FluentMethodName;
protected bool Equals(FluentApiInfo other)
{
diff --git a/src/M31.FluentApi.Generator/SourceGenerators/FluentApiInfoCreator.cs b/src/M31.FluentApi.Generator/SourceGenerators/FluentApiInfoCreator.cs
index 9cc3d4b..272cf3f 100644
--- a/src/M31.FluentApi.Generator/SourceGenerators/FluentApiInfoCreator.cs
+++ b/src/M31.FluentApi.Generator/SourceGenerators/FluentApiInfoCreator.cs
@@ -20,12 +20,18 @@ internal FluentApiInfoCreator(ClassInfoReport classInfoReport)
internal FluentApiInfo? Create(
ISymbol symbol,
FluentApiAttributeData attributeData,
- string declaringClassNameWithTypeParameters)
+ string declaringClassNameWithTypeParameters,
+ CancellationToken cancellationToken)
{
+ if (cancellationToken.IsCancellationRequested)
+ {
+ return null;
+ }
+
FluentApiSymbolInfo symbolInfo = SymbolInfoCreator.Create(symbol, declaringClassNameWithTypeParameters);
AttributeInfoBase? attributeInfo = CreateAttributeInfo(attributeData.MainAttributeData, symbol, symbolInfo);
- if (attributeInfo == null)
+ if (attributeInfo == null || cancellationToken.IsCancellationRequested)
{
return null;
}
@@ -35,11 +41,21 @@ internal FluentApiInfoCreator(ClassInfoReport classInfoReport)
.Select(data => (data, CreateOrthogonalAttributeInfo(data, symbol.Name)))
.ToArray();
+ if (cancellationToken.IsCancellationRequested)
+ {
+ return null;
+ }
+
(AttributeDataExtended data, ControlAttributeInfoBase info)[] controlDataAndInfos =
attributeData.ControlAttributeData
.Select(data => (data, CreateControlAttributeInfo(data)))
.ToArray();
+ if (cancellationToken.IsCancellationRequested)
+ {
+ return null;
+ }
+
FluentReturnAttributeInfo? fluentReturnAttributeInfo = controlDataAndInfos.Select(d => d.info)
.OfType().FirstOrDefault();
diff --git a/src/M31.FluentApi.Generator/SourceGenerators/FluentApiInfoGroup.cs b/src/M31.FluentApi.Generator/SourceGenerators/FluentApiInfoGroup.cs
index b73b858..e508e47 100644
--- a/src/M31.FluentApi.Generator/SourceGenerators/FluentApiInfoGroup.cs
+++ b/src/M31.FluentApi.Generator/SourceGenerators/FluentApiInfoGroup.cs
@@ -8,7 +8,7 @@ internal FluentApiInfoGroup(
bool isSkippable,
string fluentMethodName,
Type attributeInfoType,
- IReadOnlyCollection fluentApiInfos)
+ IReadOnlyList fluentApiInfos)
{
BuilderStep = builderStep;
NextBuilderStep = nextBuilderStep;
@@ -23,6 +23,6 @@ internal FluentApiInfoGroup(
internal bool IsSkippable { get; }
internal string FluentMethodName { get; }
internal Type AttributeInfoType { get; }
- internal IReadOnlyCollection FluentApiInfos { get; }
+ internal IReadOnlyList FluentApiInfos { get; }
internal bool IsCompoundGroup => FluentApiInfos.Count > 1;
}
\ No newline at end of file
diff --git a/src/M31.FluentApi.Generator/SourceGenerators/FluentApiInfoGroupCreator.cs b/src/M31.FluentApi.Generator/SourceGenerators/FluentApiInfoGroupCreator.cs
index 4f35b5f..99810be 100644
--- a/src/M31.FluentApi.Generator/SourceGenerators/FluentApiInfoGroupCreator.cs
+++ b/src/M31.FluentApi.Generator/SourceGenerators/FluentApiInfoGroupCreator.cs
@@ -30,11 +30,12 @@ private IReadOnlyCollection CreateGroups(IReadOnlyCollection
// Group FluentApiInfos that have the same builder step, the same fluent method name, and represent
// FluentMembers (compounds).
(int builderStep, string fluentMethodName, Type type, FluentApiInfo[] infoArray)[] grouping = infos
- .GroupBy(i => (i.AttributeInfo.BuilderStep, i.FluentMethodName, i.AttributeInfo.GetType()))
+ .GroupBy(
+ i => (i.AttributeInfo.BuilderStep, i.AttributeInfo.FluentMethodNames[0], i.AttributeInfo.GetType()))
.SelectMany(g =>
g.First().AttributeInfo.GetType() == typeof(FluentMemberAttributeInfo)
- ? new[] { (g.Key.BuilderStep, g.Key.FluentMethodName, g.Key.GetType(), g.ToArray()) }
- : g.Select(g2 => (g.Key.BuilderStep, g.Key.FluentMethodName, g.Key.GetType(), new[] { g2 })))
+ ? new[] { (g.Key.BuilderStep, g.Key.Item2, g.Key.Item3, g.ToArray()) }
+ : g.Select(g2 => (g.Key.BuilderStep, g.Key.Item2, g.Key.Item3, new[] { g2 })))
.OrderBy(g => g.BuilderStep)
.ToArray();
@@ -66,7 +67,8 @@ private IReadOnlyCollection CreateGroups(IReadOnlyCollection
nextBuilderStep,
groupIsSkippable,
group.fluentMethodName,
- group.type, group.infoArray));
+ group.type,
+ group.infoArray));
}
return infoGroups;
diff --git a/src/M31.FluentApi.Generator/SourceGenerators/Generics/GenericInfo.cs b/src/M31.FluentApi.Generator/SourceGenerators/Generics/GenericInfo.cs
index 6c97a46..ed26fa6 100644
--- a/src/M31.FluentApi.Generator/SourceGenerators/Generics/GenericInfo.cs
+++ b/src/M31.FluentApi.Generator/SourceGenerators/Generics/GenericInfo.cs
@@ -25,6 +25,7 @@ internal static GenericInfo Create(IEnumerable typeParamet
internal IReadOnlyCollection Parameters { get; }
internal int ParameterCount => Parameters.Count;
internal IEnumerable ParameterStrings => Parameters.Select(p => p.ParameterName);
+
internal string ParameterListInAngleBrackets =>
Parameters.Count == 0 ? string.Empty : $"<{string.Join(", ", ParameterStrings)}>";
diff --git a/src/M31.FluentApi.Generator/SourceGenerators/SymbolInfoCreator.cs b/src/M31.FluentApi.Generator/SourceGenerators/SymbolInfoCreator.cs
index 36a689c..0e41c91 100644
--- a/src/M31.FluentApi.Generator/SourceGenerators/SymbolInfoCreator.cs
+++ b/src/M31.FluentApi.Generator/SourceGenerators/SymbolInfoCreator.cs
@@ -1,9 +1,12 @@
+using System.Text.RegularExpressions;
using M31.FluentApi.Generator.CodeBuilding;
using M31.FluentApi.Generator.CodeGeneration.CodeBoardElements;
+using M31.FluentApi.Generator.CodeGeneration.CodeBoardElements.FluentApiComments;
using M31.FluentApi.Generator.Commons;
using M31.FluentApi.Generator.SourceGenerators.Collections;
using M31.FluentApi.Generator.SourceGenerators.Generics;
using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp;
namespace M31.FluentApi.Generator.SourceGenerators;
@@ -39,7 +42,8 @@ private static MemberSymbolInfo CreateMemberSymbolInfo(
CodeTypeExtractor.GetTypeForCodeGeneration(fieldSymbol.Type),
fieldSymbol.NullableAnnotation == NullableAnnotation.Annotated,
false,
- CollectionInference.InferCollectionType(fieldSymbol.Type));
+ CollectionInference.InferCollectionType(fieldSymbol.Type),
+ GetFluentSymbolComments(fieldSymbol));
}
private static MemberSymbolInfo CreateMemberSymbolInfo(
@@ -55,7 +59,8 @@ private static MemberSymbolInfo CreateMemberSymbolInfo(
CodeTypeExtractor.GetTypeForCodeGeneration(propertySymbol.Type),
propertySymbol.NullableAnnotation == NullableAnnotation.Annotated,
true,
- CollectionInference.InferCollectionType(propertySymbol.Type));
+ CollectionInference.InferCollectionType(propertySymbol.Type),
+ GetFluentSymbolComments(propertySymbol));
}
private static MethodSymbolInfo CreateMethodSymbolInfo(
@@ -78,7 +83,8 @@ private static MethodSymbolInfo CreateMethodSymbolInfo(
RequiresReflection(methodSymbol),
genericInfo,
parameterInfos,
- CodeTypeExtractor.GetTypeForCodeGeneration(methodSymbol.ReturnType));
+ CodeTypeExtractor.GetTypeForCodeGeneration(methodSymbol.ReturnType),
+ GetFluentSymbolComments(methodSymbol));
}
private static GenericInfo? GetGenericInfo(IMethodSymbol methodSymbol)
@@ -193,4 +199,36 @@ private static ParameterKinds GetParameterKinds(IParameterSymbol parameterSymbol
return parameterKinds;
}
+
+ private static readonly Regex fluentApiCommentStart = new Regex(@"^\s*////(?!/)", RegexOptions.Compiled);
+ private static Comments GetFluentSymbolComments(ISymbol symbol)
+ {
+ SyntaxReference? syntaxRef = symbol.DeclaringSyntaxReferences.FirstOrDefault();
+ if (syntaxRef == null)
+ {
+ return new Comments(Array.Empty());
+ }
+
+ SyntaxNode syntaxNode = syntaxRef.GetSyntax();
+ SyntaxTriviaList leadingTrivia = syntaxNode.GetLeadingTrivia();
+
+ List commentLines = new List();
+
+ foreach (SyntaxTrivia syntaxTrivia in leadingTrivia)
+ {
+ if (!syntaxTrivia.IsKind(SyntaxKind.SingleLineCommentTrivia))
+ {
+ continue;
+ }
+
+ string str = syntaxTrivia.ToString();
+ if (fluentApiCommentStart.IsMatch(str))
+ {
+ commentLines.Add(str.TrimStart('/', ' '));
+ }
+ }
+
+ string comments = string.Join(Environment.NewLine, commentLines);
+ return FluentCommentsParser.Parse(comments);
+ }
}
\ No newline at end of file
diff --git a/src/M31.FluentApi.Storybook/01_Basics.cs b/src/M31.FluentApi.Storybook/01_Basics.cs
index e5ad1f2..43ef494 100644
--- a/src/M31.FluentApi.Storybook/01_Basics.cs
+++ b/src/M31.FluentApi.Storybook/01_Basics.cs
@@ -311,12 +311,12 @@ public static void UseTheGeneratedFluentApi()
namespace NestedFluentApis
{
/* Lastly, I would like to demonstrate the effect of applying the FluentMember attribute to a member whose type has
- its own Fluent API. In this scenario, an additional builder method that accepts a lambda expression is generated.
- In the example below, the Student class has an Address property, and the Address class has its own Fluent API.
+ its own fluent API. In this scenario, an additional builder method that accepts a lambda expression is generated.
+ In the example below, the Student class has an Address property, and the Address class has its own fluent API.
The advantage of the lambda method is that the user does not have to figure out the builder name of the Address
class when creating a student.
Similarly, additional lambda methods are generated if the FluentCollection attribute is applied to a collection
- whose element type has its own Fluent API. The employee class below is modeled with several addresses that can be
+ whose element type has its own fluent API. The employee class below is modeled with several addresses that can be
conveniently set using lambda methods.
In the next chapter, you will learn how to create non-linear paths with control attributes.
diff --git a/src/M31.FluentApi.Storybook/M31.FluentApi.Storybook.csproj b/src/M31.FluentApi.Storybook/M31.FluentApi.Storybook.csproj
index c1b1f28..33b1729 100644
--- a/src/M31.FluentApi.Storybook/M31.FluentApi.Storybook.csproj
+++ b/src/M31.FluentApi.Storybook/M31.FluentApi.Storybook.csproj
@@ -1,17 +1,17 @@
-
- Exe
- net6.0
- enable
- enable
- false
- $(BaseIntermediateOutputPath)Generated
- true
-
+
+ Exe
+ net6.0
+ enable
+ enable
+ false
+ $(BaseIntermediateOutputPath)Generated
+ true
+
-
-
-
-
-
+
+
+
+
+
\ No newline at end of file
diff --git a/src/M31.FluentApi.Tests/AnalyzerAndCodeFixes/DiagnosticsDuringGenerationTests.cs b/src/M31.FluentApi.Tests/AnalyzerAndCodeFixes/DiagnosticsDuringGenerationTests.cs
index 5aad575..4a29203 100644
--- a/src/M31.FluentApi.Tests/AnalyzerAndCodeFixes/DiagnosticsDuringGenerationTests.cs
+++ b/src/M31.FluentApi.Tests/AnalyzerAndCodeFixes/DiagnosticsDuringGenerationTests.cs
@@ -86,7 +86,7 @@ public void CanDetectReservedMethod1()
ExpectedDiagnostic expectedDiagnostic2 = new ExpectedDiagnostic(
ReservedMethodName.Descriptor,
"InitialStep",
- (18, 6));
+ (17, 6));
RunGeneratorAndCheckDiagnostics("ReservedMethodClass1", "Student", expectedDiagnostic1, expectedDiagnostic2);
}
diff --git a/src/M31.FluentApi.Tests/AnalyzerAndCodeFixes/FluentApiCommentsProviderTests.cs b/src/M31.FluentApi.Tests/AnalyzerAndCodeFixes/FluentApiCommentsProviderTests.cs
new file mode 100644
index 0000000..af0c6ad
--- /dev/null
+++ b/src/M31.FluentApi.Tests/AnalyzerAndCodeFixes/FluentApiCommentsProviderTests.cs
@@ -0,0 +1,55 @@
+using System;
+using System.IO;
+using System.Threading.Tasks;
+using M31.FluentApi.Attributes;
+using M31.FluentApi.Generator.SourceAnalyzers.FluentApiComments;
+using Microsoft.CodeAnalysis.CSharp.Testing;
+using Microsoft.CodeAnalysis.Testing.Verifiers;
+using Xunit;
+using M31.FluentApi.Tests.AnalyzerAndCodeFixes.Helpers;
+using Microsoft.CodeAnalysis.Testing;
+using static M31.FluentApi.Tests.AnalyzerAndCodeFixes.Helpers.TestSourceCodeReader;
+
+namespace M31.FluentApi.Tests.AnalyzerAndCodeFixes;
+
+public class FluentApiCommentsProviderTests
+{
+ [Theory]
+ [InlineData("CommentedClass", "Student", "FirstName", "FirstName")]
+ [InlineData("CommentedClass", "Student", "LastNa", "LastName")]
+ [InlineData("CommentedClass", "Student", "int Age ", "Age")]
+ [InlineData("CommentedClass", "Student", " BornOn", "BornOn")]
+ [InlineData("CommentedClass", "Student", "int Sem", "Semester")]
+ [InlineData("CommentedClass", "Student", " City ", "City")]
+ [InlineData("CommentedClass", "Student", "IsHappy ", "IsHappy")]
+ [InlineData("CommentedClass", "Student", " Friends", "Friends")]
+ [InlineData("LambdaCollectionClass", "Student", "PhoneNumbers", "PhoneNumbers")]
+ [InlineData("VoidMethodClass", "Student", "Study", "Study")]
+ [InlineData("VoidMethodClass", "Student", "Sleep", "Sleep")]
+ public async Task CanProvideFluentApiComments(
+ string commentTestClass, string @class, string selectedSpan, string member)
+ {
+ SourceWithFix source = ReadSource(Path.Combine("FluentApiComments", commentTestClass), @class,
+ $"Student.{member}.txt");
+ var test = new CSharpCodeRefactoringTest
+ {
+ TestCode = source.Source.SelectSpan(selectedSpan),
+ FixedCode = source.FixedSource!,
+#if NET6_0
+ ReferenceAssemblies = new ReferenceAssemblies(
+ "net6.0",
+ new PackageIdentity("Microsoft.NETCore.App.Ref", "6.0.0"),
+ Path.Combine("ref", "net6.0")),
+#else
+ throw new NotSupportedException();
+#endif
+
+ TestState =
+ {
+ AdditionalReferences = { typeof(FluentApiAttribute).Assembly }
+ }
+ };
+
+ await test.RunAsync();
+ }
+}
\ No newline at end of file
diff --git a/src/M31.FluentApi.Tests/AnalyzerAndCodeFixes/Helpers/AnalyzerAndCodeFixVerifier.cs b/src/M31.FluentApi.Tests/AnalyzerAndCodeFixes/Helpers/AnalyzerAndCodeFixVerifier.cs
index cc8c7d9..2c9db80 100644
--- a/src/M31.FluentApi.Tests/AnalyzerAndCodeFixes/Helpers/AnalyzerAndCodeFixVerifier.cs
+++ b/src/M31.FluentApi.Tests/AnalyzerAndCodeFixes/Helpers/AnalyzerAndCodeFixVerifier.cs
@@ -1,3 +1,4 @@
+using System;
using System.Collections.Generic;
using System.IO;
using System.Threading;
@@ -62,7 +63,7 @@ internal CodeFixTest(
new PackageIdentity("Microsoft.NETCore.App.Ref", "6.0.0"),
Path.Combine("ref", "net6.0"));
#else
- ReferenceAssemblies = ReferenceAssemblies.Net.Net50;
+ throw new NotSupportedException();
#endif
TestState.AdditionalReferences.Add(typeof(FluentApiAttribute).Assembly);
diff --git a/src/M31.FluentApi.Tests/AnalyzerAndCodeFixes/Helpers/SourceExtensions.cs b/src/M31.FluentApi.Tests/AnalyzerAndCodeFixes/Helpers/SourceExtensions.cs
new file mode 100644
index 0000000..d5417dc
--- /dev/null
+++ b/src/M31.FluentApi.Tests/AnalyzerAndCodeFixes/Helpers/SourceExtensions.cs
@@ -0,0 +1,27 @@
+using System;
+using System.Text.RegularExpressions;
+
+namespace M31.FluentApi.Tests.AnalyzerAndCodeFixes.Helpers;
+
+internal static class SourceExtensions
+{
+ internal static string SelectSpan(this string source, string span)
+ {
+ MatchCollection matches = Regex.Matches(source, Regex.Escape(span));
+ if (matches.Count != 1)
+ {
+ throw new InvalidOperationException(
+ $"Span '{span}' not found or found multiple times in the source code.");
+ }
+
+ string replacement = $"[|{span}|]";
+ return ReplaceMatch(source, matches[0], replacement);
+
+ static string ReplaceMatch(string source, Match match, string replacement)
+ {
+ return source[..match.Index] +
+ replacement +
+ source[(match.Index + match.Length)..];
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/M31.FluentApi.Tests/AnalyzerAndCodeFixes/Helpers/TestSourceCodeReader.cs b/src/M31.FluentApi.Tests/AnalyzerAndCodeFixes/Helpers/TestSourceCodeReader.cs
index f0703e0..59cb0d5 100644
--- a/src/M31.FluentApi.Tests/AnalyzerAndCodeFixes/Helpers/TestSourceCodeReader.cs
+++ b/src/M31.FluentApi.Tests/AnalyzerAndCodeFixes/Helpers/TestSourceCodeReader.cs
@@ -4,10 +4,11 @@ namespace M31.FluentApi.Tests.AnalyzerAndCodeFixes.Helpers;
internal static class TestSourceCodeReader
{
- internal static SourceWithFix ReadSource(string testClassFolder, string @class)
+ internal static SourceWithFix ReadSource(string testClassFolder, string @class, string? @fixed = null)
{
+ @fixed ??= $"{@class}.fixed.txt";
string source = ReadTestClassCode(testClassFolder, $"{@class}.cs");
- string fixedSource = TryReadTestClassCode(testClassFolder, $"{@class}.fixed.txt") ?? source;
+ string fixedSource = TryReadTestClassCode(testClassFolder, @fixed) ?? source;
return new SourceWithFix(source, fixedSource);
}
diff --git a/src/M31.FluentApi.Tests/AnalyzerAndCodeFixes/TestClasses/ConflictingControlAttributesClass3/Student.cs b/src/M31.FluentApi.Tests/AnalyzerAndCodeFixes/TestClasses/ConflictingControlAttributesClass3/Student.cs
index 7e9adc9..4009507 100644
--- a/src/M31.FluentApi.Tests/AnalyzerAndCodeFixes/TestClasses/ConflictingControlAttributesClass3/Student.cs
+++ b/src/M31.FluentApi.Tests/AnalyzerAndCodeFixes/TestClasses/ConflictingControlAttributesClass3/Student.cs
@@ -17,6 +17,5 @@ public class Student
[FluentReturn]
public void Method1()
{
-
}
}
\ No newline at end of file
diff --git a/src/M31.FluentApi.Tests/AnalyzerAndCodeFixes/TestClasses/FluentApiComments/CommentedClass/Student.Age.txt b/src/M31.FluentApi.Tests/AnalyzerAndCodeFixes/TestClasses/FluentApiComments/CommentedClass/Student.Age.txt
new file mode 100644
index 0000000..55b69b3
--- /dev/null
+++ b/src/M31.FluentApi.Tests/AnalyzerAndCodeFixes/TestClasses/FluentApiComments/CommentedClass/Student.Age.txt
@@ -0,0 +1,52 @@
+// Non-nullable member is uninitialized
+#pragma warning disable CS8618
+// ReSharper disable All
+
+using System;
+using System.Collections.Generic;
+using M31.FluentApi.Attributes;
+
+namespace M31.FluentApi.Tests.AnalyzerAndCodeFixes.TestClasses.FluentApiComments.CommentedClass;
+
+[FluentApi]
+public class Student
+{
+ [FluentMember(0, "Named", 0)]
+ public string FirstName { get; private set; }
+
+ [FluentMember(0, "Named", 1)]
+ public string LastName { get; private set; }
+
+ ////
+ //// ...
+ ////
+ //// ...
+ //// ...
+ [FluentMember(1, "OfAge")]
+ public int Age { get; private set; }
+
+ [FluentMethod(1)]
+ private void BornOn(DateOnly dateOfBirth)
+ {
+ DateOnly today = DateOnly.FromDateTime(DateTime.Today);
+ int age = today.Year - dateOfBirth.Year;
+ if (dateOfBirth > today.AddYears(-age)) age--;
+ Age = age;
+ }
+
+ [FluentMember(2, "InSemester")]
+ [FluentDefault("WhoStartsUniversity")]
+ public int Semester { get; private set; } = 0;
+
+ [FluentMember(3, "LivingIn")]
+ [FluentDefault("LivingInBoston")]
+ [FluentNullable("InUnknownCity")]
+ public string? City { get; private set; } = "Boston";
+
+ [FluentPredicate(4, "WhoIsHappy", "WhoIsSad")]
+ [FluentNullable("WithUnknownMood")]
+ public bool? IsHappy { get; private set; }
+
+ [FluentCollection(5, "Friend", "WhoseFriendsAre", "WhoseFriendIs", "WhoHasNoFriends")]
+ public IReadOnlyCollection Friends { get; private set; }
+}
\ No newline at end of file
diff --git a/src/M31.FluentApi.Tests/AnalyzerAndCodeFixes/TestClasses/FluentApiComments/CommentedClass/Student.BornOn.txt b/src/M31.FluentApi.Tests/AnalyzerAndCodeFixes/TestClasses/FluentApiComments/CommentedClass/Student.BornOn.txt
new file mode 100644
index 0000000..1a8ab98
--- /dev/null
+++ b/src/M31.FluentApi.Tests/AnalyzerAndCodeFixes/TestClasses/FluentApiComments/CommentedClass/Student.BornOn.txt
@@ -0,0 +1,52 @@
+// Non-nullable member is uninitialized
+#pragma warning disable CS8618
+// ReSharper disable All
+
+using System;
+using System.Collections.Generic;
+using M31.FluentApi.Attributes;
+
+namespace M31.FluentApi.Tests.AnalyzerAndCodeFixes.TestClasses.FluentApiComments.CommentedClass;
+
+[FluentApi]
+public class Student
+{
+ [FluentMember(0, "Named", 0)]
+ public string FirstName { get; private set; }
+
+ [FluentMember(0, "Named", 1)]
+ public string LastName { get; private set; }
+
+ [FluentMember(1, "OfAge")]
+ public int Age { get; private set; }
+
+ ////
+ //// ...
+ ////
+ //// ...
+ //// ...
+ [FluentMethod(1)]
+ private void BornOn(DateOnly dateOfBirth)
+ {
+ DateOnly today = DateOnly.FromDateTime(DateTime.Today);
+ int age = today.Year - dateOfBirth.Year;
+ if (dateOfBirth > today.AddYears(-age)) age--;
+ Age = age;
+ }
+
+ [FluentMember(2, "InSemester")]
+ [FluentDefault("WhoStartsUniversity")]
+ public int Semester { get; private set; } = 0;
+
+ [FluentMember(3, "LivingIn")]
+ [FluentDefault("LivingInBoston")]
+ [FluentNullable("InUnknownCity")]
+ public string? City { get; private set; } = "Boston";
+
+ [FluentPredicate(4, "WhoIsHappy", "WhoIsSad")]
+ [FluentNullable("WithUnknownMood")]
+ public bool? IsHappy { get; private set; }
+
+ [FluentCollection(5, "Friend", "WhoseFriendsAre", "WhoseFriendIs", "WhoHasNoFriends")]
+ public IReadOnlyCollection Friends { get; private set; }
+}
\ No newline at end of file
diff --git a/src/M31.FluentApi.Tests/AnalyzerAndCodeFixes/TestClasses/FluentApiComments/CommentedClass/Student.City.txt b/src/M31.FluentApi.Tests/AnalyzerAndCodeFixes/TestClasses/FluentApiComments/CommentedClass/Student.City.txt
new file mode 100644
index 0000000..b33dcb6
--- /dev/null
+++ b/src/M31.FluentApi.Tests/AnalyzerAndCodeFixes/TestClasses/FluentApiComments/CommentedClass/Student.City.txt
@@ -0,0 +1,62 @@
+// Non-nullable member is uninitialized
+#pragma warning disable CS8618
+// ReSharper disable All
+
+using System;
+using System.Collections.Generic;
+using M31.FluentApi.Attributes;
+
+namespace M31.FluentApi.Tests.AnalyzerAndCodeFixes.TestClasses.FluentApiComments.CommentedClass;
+
+[FluentApi]
+public class Student
+{
+ [FluentMember(0, "Named", 0)]
+ public string FirstName { get; private set; }
+
+ [FluentMember(0, "Named", 1)]
+ public string LastName { get; private set; }
+
+ [FluentMember(1, "OfAge")]
+ public int Age { get; private set; }
+
+ [FluentMethod(1)]
+ private void BornOn(DateOnly dateOfBirth)
+ {
+ DateOnly today = DateOnly.FromDateTime(DateTime.Today);
+ int age = today.Year - dateOfBirth.Year;
+ if (dateOfBirth > today.AddYears(-age)) age--;
+ Age = age;
+ }
+
+ [FluentMember(2, "InSemester")]
+ [FluentDefault("WhoStartsUniversity")]
+ public int Semester { get; private set; } = 0;
+
+ ////
+ //// ...
+ ////
+ //// ...
+ //// ...
+ ////
+ ////
+ //// ...
+ ////
+ //// ...
+ ////
+ ////
+ //// ...
+ ////
+ //// ...
+ [FluentMember(3, "LivingIn")]
+ [FluentDefault("LivingInBoston")]
+ [FluentNullable("InUnknownCity")]
+ public string? City { get; private set; } = "Boston";
+
+ [FluentPredicate(4, "WhoIsHappy", "WhoIsSad")]
+ [FluentNullable("WithUnknownMood")]
+ public bool? IsHappy { get; private set; }
+
+ [FluentCollection(5, "Friend", "WhoseFriendsAre", "WhoseFriendIs", "WhoHasNoFriends")]
+ public IReadOnlyCollection Friends { get; private set; }
+}
\ No newline at end of file
diff --git a/src/M31.FluentApi.Tests/AnalyzerAndCodeFixes/TestClasses/FluentApiComments/CommentedClass/Student.FirstName.txt b/src/M31.FluentApi.Tests/AnalyzerAndCodeFixes/TestClasses/FluentApiComments/CommentedClass/Student.FirstName.txt
new file mode 100644
index 0000000..3d7ff18
--- /dev/null
+++ b/src/M31.FluentApi.Tests/AnalyzerAndCodeFixes/TestClasses/FluentApiComments/CommentedClass/Student.FirstName.txt
@@ -0,0 +1,53 @@
+// Non-nullable member is uninitialized
+#pragma warning disable CS8618
+// ReSharper disable All
+
+using System;
+using System.Collections.Generic;
+using M31.FluentApi.Attributes;
+
+namespace M31.FluentApi.Tests.AnalyzerAndCodeFixes.TestClasses.FluentApiComments.CommentedClass;
+
+[FluentApi]
+public class Student
+{
+ ////
+ //// ...
+ ////
+ //// ...
+ //// ...
+ //// ...
+ [FluentMember(0, "Named", 0)]
+ public string FirstName { get; private set; }
+
+ [FluentMember(0, "Named", 1)]
+ public string LastName { get; private set; }
+
+ [FluentMember(1, "OfAge")]
+ public int Age { get; private set; }
+
+ [FluentMethod(1)]
+ private void BornOn(DateOnly dateOfBirth)
+ {
+ DateOnly today = DateOnly.FromDateTime(DateTime.Today);
+ int age = today.Year - dateOfBirth.Year;
+ if (dateOfBirth > today.AddYears(-age)) age--;
+ Age = age;
+ }
+
+ [FluentMember(2, "InSemester")]
+ [FluentDefault("WhoStartsUniversity")]
+ public int Semester { get; private set; } = 0;
+
+ [FluentMember(3, "LivingIn")]
+ [FluentDefault("LivingInBoston")]
+ [FluentNullable("InUnknownCity")]
+ public string? City { get; private set; } = "Boston";
+
+ [FluentPredicate(4, "WhoIsHappy", "WhoIsSad")]
+ [FluentNullable("WithUnknownMood")]
+ public bool? IsHappy { get; private set; }
+
+ [FluentCollection(5, "Friend", "WhoseFriendsAre", "WhoseFriendIs", "WhoHasNoFriends")]
+ public IReadOnlyCollection Friends { get; private set; }
+}
\ No newline at end of file
diff --git a/src/M31.FluentApi.Tests/AnalyzerAndCodeFixes/TestClasses/FluentApiComments/CommentedClass/Student.Friends.txt b/src/M31.FluentApi.Tests/AnalyzerAndCodeFixes/TestClasses/FluentApiComments/CommentedClass/Student.Friends.txt
new file mode 100644
index 0000000..6ae0dcc
--- /dev/null
+++ b/src/M31.FluentApi.Tests/AnalyzerAndCodeFixes/TestClasses/FluentApiComments/CommentedClass/Student.Friends.txt
@@ -0,0 +1,63 @@
+// Non-nullable member is uninitialized
+#pragma warning disable CS8618
+// ReSharper disable All
+
+using System;
+using System.Collections.Generic;
+using M31.FluentApi.Attributes;
+
+namespace M31.FluentApi.Tests.AnalyzerAndCodeFixes.TestClasses.FluentApiComments.CommentedClass;
+
+[FluentApi]
+public class Student
+{
+ [FluentMember(0, "Named", 0)]
+ public string FirstName { get; private set; }
+
+ [FluentMember(0, "Named", 1)]
+ public string LastName { get; private set; }
+
+ [FluentMember(1, "OfAge")]
+ public int Age { get; private set; }
+
+ [FluentMethod(1)]
+ private void BornOn(DateOnly dateOfBirth)
+ {
+ DateOnly today = DateOnly.FromDateTime(DateTime.Today);
+ int age = today.Year - dateOfBirth.Year;
+ if (dateOfBirth > today.AddYears(-age)) age--;
+ Age = age;
+ }
+
+ [FluentMember(2, "InSemester")]
+ [FluentDefault("WhoStartsUniversity")]
+ public int Semester { get; private set; } = 0;
+
+ [FluentMember(3, "LivingIn")]
+ [FluentDefault("LivingInBoston")]
+ [FluentNullable("InUnknownCity")]
+ public string? City { get; private set; } = "Boston";
+
+ [FluentPredicate(4, "WhoIsHappy", "WhoIsSad")]
+ [FluentNullable("WithUnknownMood")]
+ public bool? IsHappy { get; private set; }
+
+ ////
+ //// ...
+ ////
+ //// ...
+ //// ...
+ ////
+ ////
+ //// ...
+ ////
+ //// ...
+ //// ...
+ ////
+ ////
+ //// ...
+ ////
+ //// ...
+ [FluentCollection(5, "Friend", "WhoseFriendsAre", "WhoseFriendIs", "WhoHasNoFriends")]
+ public IReadOnlyCollection Friends { get; private set; }
+}
\ No newline at end of file
diff --git a/src/M31.FluentApi.Tests/AnalyzerAndCodeFixes/TestClasses/FluentApiComments/CommentedClass/Student.IsHappy.txt b/src/M31.FluentApi.Tests/AnalyzerAndCodeFixes/TestClasses/FluentApiComments/CommentedClass/Student.IsHappy.txt
new file mode 100644
index 0000000..7a753fe
--- /dev/null
+++ b/src/M31.FluentApi.Tests/AnalyzerAndCodeFixes/TestClasses/FluentApiComments/CommentedClass/Student.IsHappy.txt
@@ -0,0 +1,62 @@
+// Non-nullable member is uninitialized
+#pragma warning disable CS8618
+// ReSharper disable All
+
+using System;
+using System.Collections.Generic;
+using M31.FluentApi.Attributes;
+
+namespace M31.FluentApi.Tests.AnalyzerAndCodeFixes.TestClasses.FluentApiComments.CommentedClass;
+
+[FluentApi]
+public class Student
+{
+ [FluentMember(0, "Named", 0)]
+ public string FirstName { get; private set; }
+
+ [FluentMember(0, "Named", 1)]
+ public string LastName { get; private set; }
+
+ [FluentMember(1, "OfAge")]
+ public int Age { get; private set; }
+
+ [FluentMethod(1)]
+ private void BornOn(DateOnly dateOfBirth)
+ {
+ DateOnly today = DateOnly.FromDateTime(DateTime.Today);
+ int age = today.Year - dateOfBirth.Year;
+ if (dateOfBirth > today.AddYears(-age)) age--;
+ Age = age;
+ }
+
+ [FluentMember(2, "InSemester")]
+ [FluentDefault("WhoStartsUniversity")]
+ public int Semester { get; private set; } = 0;
+
+ [FluentMember(3, "LivingIn")]
+ [FluentDefault("LivingInBoston")]
+ [FluentNullable("InUnknownCity")]
+ public string? City { get; private set; } = "Boston";
+
+ ////
+ //// ...
+ ////
+ //// ...
+ //// ...
+ ////
+ ////
+ //// ...
+ ////
+ //// ...
+ ////
+ ////
+ //// ...
+ ////
+ //// ...
+ [FluentPredicate(4, "WhoIsHappy", "WhoIsSad")]
+ [FluentNullable("WithUnknownMood")]
+ public bool? IsHappy { get; private set; }
+
+ [FluentCollection(5, "Friend", "WhoseFriendsAre", "WhoseFriendIs", "WhoHasNoFriends")]
+ public IReadOnlyCollection Friends { get; private set; }
+}
\ No newline at end of file
diff --git a/src/M31.FluentApi.Tests/AnalyzerAndCodeFixes/TestClasses/FluentApiComments/CommentedClass/Student.LastName.txt b/src/M31.FluentApi.Tests/AnalyzerAndCodeFixes/TestClasses/FluentApiComments/CommentedClass/Student.LastName.txt
new file mode 100644
index 0000000..3d7ff18
--- /dev/null
+++ b/src/M31.FluentApi.Tests/AnalyzerAndCodeFixes/TestClasses/FluentApiComments/CommentedClass/Student.LastName.txt
@@ -0,0 +1,53 @@
+// Non-nullable member is uninitialized
+#pragma warning disable CS8618
+// ReSharper disable All
+
+using System;
+using System.Collections.Generic;
+using M31.FluentApi.Attributes;
+
+namespace M31.FluentApi.Tests.AnalyzerAndCodeFixes.TestClasses.FluentApiComments.CommentedClass;
+
+[FluentApi]
+public class Student
+{
+ ////
+ //// ...
+ ////
+ //// ...
+ //// ...
+ //// ...
+ [FluentMember(0, "Named", 0)]
+ public string FirstName { get; private set; }
+
+ [FluentMember(0, "Named", 1)]
+ public string LastName { get; private set; }
+
+ [FluentMember(1, "OfAge")]
+ public int Age { get; private set; }
+
+ [FluentMethod(1)]
+ private void BornOn(DateOnly dateOfBirth)
+ {
+ DateOnly today = DateOnly.FromDateTime(DateTime.Today);
+ int age = today.Year - dateOfBirth.Year;
+ if (dateOfBirth > today.AddYears(-age)) age--;
+ Age = age;
+ }
+
+ [FluentMember(2, "InSemester")]
+ [FluentDefault("WhoStartsUniversity")]
+ public int Semester { get; private set; } = 0;
+
+ [FluentMember(3, "LivingIn")]
+ [FluentDefault("LivingInBoston")]
+ [FluentNullable("InUnknownCity")]
+ public string? City { get; private set; } = "Boston";
+
+ [FluentPredicate(4, "WhoIsHappy", "WhoIsSad")]
+ [FluentNullable("WithUnknownMood")]
+ public bool? IsHappy { get; private set; }
+
+ [FluentCollection(5, "Friend", "WhoseFriendsAre", "WhoseFriendIs", "WhoHasNoFriends")]
+ public IReadOnlyCollection Friends { get; private set; }
+}
\ No newline at end of file
diff --git a/src/M31.FluentApi.Tests/AnalyzerAndCodeFixes/TestClasses/FluentApiComments/CommentedClass/Student.Semester.txt b/src/M31.FluentApi.Tests/AnalyzerAndCodeFixes/TestClasses/FluentApiComments/CommentedClass/Student.Semester.txt
new file mode 100644
index 0000000..957f36c
--- /dev/null
+++ b/src/M31.FluentApi.Tests/AnalyzerAndCodeFixes/TestClasses/FluentApiComments/CommentedClass/Student.Semester.txt
@@ -0,0 +1,57 @@
+// Non-nullable member is uninitialized
+#pragma warning disable CS8618
+// ReSharper disable All
+
+using System;
+using System.Collections.Generic;
+using M31.FluentApi.Attributes;
+
+namespace M31.FluentApi.Tests.AnalyzerAndCodeFixes.TestClasses.FluentApiComments.CommentedClass;
+
+[FluentApi]
+public class Student
+{
+ [FluentMember(0, "Named", 0)]
+ public string FirstName { get; private set; }
+
+ [FluentMember(0, "Named", 1)]
+ public string LastName { get; private set; }
+
+ [FluentMember(1, "OfAge")]
+ public int Age { get; private set; }
+
+ [FluentMethod(1)]
+ private void BornOn(DateOnly dateOfBirth)
+ {
+ DateOnly today = DateOnly.FromDateTime(DateTime.Today);
+ int age = today.Year - dateOfBirth.Year;
+ if (dateOfBirth > today.AddYears(-age)) age--;
+ Age = age;
+ }
+
+ ////
+ //// ...
+ ////
+ //// ...
+ //// ...
+ ////
+ ////
+ //// ...
+ ////
+ //// ...
+ [FluentMember(2, "InSemester")]
+ [FluentDefault("WhoStartsUniversity")]
+ public int Semester { get; private set; } = 0;
+
+ [FluentMember(3, "LivingIn")]
+ [FluentDefault("LivingInBoston")]
+ [FluentNullable("InUnknownCity")]
+ public string? City { get; private set; } = "Boston";
+
+ [FluentPredicate(4, "WhoIsHappy", "WhoIsSad")]
+ [FluentNullable("WithUnknownMood")]
+ public bool? IsHappy { get; private set; }
+
+ [FluentCollection(5, "Friend", "WhoseFriendsAre", "WhoseFriendIs", "WhoHasNoFriends")]
+ public IReadOnlyCollection Friends { get; private set; }
+}
\ No newline at end of file
diff --git a/src/M31.FluentApi.Tests/AnalyzerAndCodeFixes/TestClasses/FluentApiComments/CommentedClass/Student.cs b/src/M31.FluentApi.Tests/AnalyzerAndCodeFixes/TestClasses/FluentApiComments/CommentedClass/Student.cs
new file mode 100644
index 0000000..a84a7ee
--- /dev/null
+++ b/src/M31.FluentApi.Tests/AnalyzerAndCodeFixes/TestClasses/FluentApiComments/CommentedClass/Student.cs
@@ -0,0 +1,47 @@
+// Non-nullable member is uninitialized
+#pragma warning disable CS8618
+// ReSharper disable All
+
+using System;
+using System.Collections.Generic;
+using M31.FluentApi.Attributes;
+
+namespace M31.FluentApi.Tests.AnalyzerAndCodeFixes.TestClasses.FluentApiComments.CommentedClass;
+
+[FluentApi]
+public class Student
+{
+ [FluentMember(0, "Named", 0)]
+ public string FirstName { get; private set; }
+
+ [FluentMember(0, "Named", 1)]
+ public string LastName { get; private set; }
+
+ [FluentMember(1, "OfAge")]
+ public int Age { get; private set; }
+
+ [FluentMethod(1)]
+ private void BornOn(DateOnly dateOfBirth)
+ {
+ DateOnly today = DateOnly.FromDateTime(DateTime.Today);
+ int age = today.Year - dateOfBirth.Year;
+ if (dateOfBirth > today.AddYears(-age)) age--;
+ Age = age;
+ }
+
+ [FluentMember(2, "InSemester")]
+ [FluentDefault("WhoStartsUniversity")]
+ public int Semester { get; private set; } = 0;
+
+ [FluentMember(3, "LivingIn")]
+ [FluentDefault("LivingInBoston")]
+ [FluentNullable("InUnknownCity")]
+ public string? City { get; private set; } = "Boston";
+
+ [FluentPredicate(4, "WhoIsHappy", "WhoIsSad")]
+ [FluentNullable("WithUnknownMood")]
+ public bool? IsHappy { get; private set; }
+
+ [FluentCollection(5, "Friend", "WhoseFriendsAre", "WhoseFriendIs", "WhoHasNoFriends")]
+ public IReadOnlyCollection Friends { get; private set; }
+}
\ No newline at end of file
diff --git a/src/M31.FluentApi.Tests/AnalyzerAndCodeFixes/TestClasses/FluentApiComments/LambdaCollectionClass/Student.PhoneNumbers.txt b/src/M31.FluentApi.Tests/AnalyzerAndCodeFixes/TestClasses/FluentApiComments/LambdaCollectionClass/Student.PhoneNumbers.txt
new file mode 100644
index 0000000..9081ebb
--- /dev/null
+++ b/src/M31.FluentApi.Tests/AnalyzerAndCodeFixes/TestClasses/FluentApiComments/LambdaCollectionClass/Student.PhoneNumbers.txt
@@ -0,0 +1,45 @@
+// Non-nullable member is uninitialized
+#pragma warning disable CS8618
+// ReSharper disable All
+
+using System.Collections.Generic;
+using M31.FluentApi.Attributes;
+
+namespace M31.FluentApi.Tests.AnalyzerAndCodeFixes.TestClasses.FluentApiComments.LambdaCollectionClass;
+
+[FluentApi]
+public class Student
+{
+ [FluentMember(0)] public string Name { get; set; }
+
+ ////
+ //// ...
+ ////
+ //// ...
+ //// ...
+ //// ...
+ ////
+ ////
+ //// ...
+ ////
+ //// ...
+ //// ...
+ //// ...
+ ////
+ ////
+ //// ...
+ ////
+ //// ...
+ [FluentCollection(1, "PhoneNumber")]
+ public IReadOnlyCollection PhoneNumbers { get; set; }
+}
+
+[FluentApi]
+public class Phone
+{
+ [FluentMember(0)]
+ public string Number { get; set; }
+
+ [FluentMember(1)]
+ public string Usage { get; set; }
+}
\ No newline at end of file
diff --git a/src/M31.FluentApi.Tests/AnalyzerAndCodeFixes/TestClasses/FluentApiComments/LambdaCollectionClass/Student.cs b/src/M31.FluentApi.Tests/AnalyzerAndCodeFixes/TestClasses/FluentApiComments/LambdaCollectionClass/Student.cs
new file mode 100644
index 0000000..064b3e9
--- /dev/null
+++ b/src/M31.FluentApi.Tests/AnalyzerAndCodeFixes/TestClasses/FluentApiComments/LambdaCollectionClass/Student.cs
@@ -0,0 +1,27 @@
+// Non-nullable member is uninitialized
+#pragma warning disable CS8618
+// ReSharper disable All
+
+using System.Collections.Generic;
+using M31.FluentApi.Attributes;
+
+namespace M31.FluentApi.Tests.AnalyzerAndCodeFixes.TestClasses.FluentApiComments.LambdaCollectionClass;
+
+[FluentApi]
+public class Student
+{
+ [FluentMember(0)] public string Name { get; set; }
+
+ [FluentCollection(1, "PhoneNumber")]
+ public IReadOnlyCollection PhoneNumbers { get; set; }
+}
+
+[FluentApi]
+public class Phone
+{
+ [FluentMember(0)]
+ public string Number { get; set; }
+
+ [FluentMember(1)]
+ public string Usage { get; set; }
+}
\ No newline at end of file
diff --git a/src/M31.FluentApi.Tests/AnalyzerAndCodeFixes/TestClasses/FluentApiComments/VoidMethodClass/Student.Sleep.txt b/src/M31.FluentApi.Tests/AnalyzerAndCodeFixes/TestClasses/FluentApiComments/VoidMethodClass/Student.Sleep.txt
new file mode 100644
index 0000000..fdcac4e
--- /dev/null
+++ b/src/M31.FluentApi.Tests/AnalyzerAndCodeFixes/TestClasses/FluentApiComments/VoidMethodClass/Student.Sleep.txt
@@ -0,0 +1,30 @@
+// Non-nullable member is uninitialized
+#pragma warning disable CS8618
+// ReSharper disable All
+
+using System;
+using System.Collections.Generic;
+using M31.FluentApi.Attributes;
+
+namespace M31.FluentApi.Tests.AnalyzerAndCodeFixes.TestClasses.FluentApiComments.VoidMethodClass;
+
+[FluentApi]
+public class Student
+{
+ [FluentMember(0)]
+ public string Name { get; private set; }
+
+ [FluentMethod(1)]
+ public void Study()
+ {
+ }
+
+ ////
+ //// ...
+ ////
+ [FluentMethod(2)]
+ [FluentReturn]
+ public void Sleep()
+ {
+ }
+}
\ No newline at end of file
diff --git a/src/M31.FluentApi.Tests/AnalyzerAndCodeFixes/TestClasses/FluentApiComments/VoidMethodClass/Student.Study.txt b/src/M31.FluentApi.Tests/AnalyzerAndCodeFixes/TestClasses/FluentApiComments/VoidMethodClass/Student.Study.txt
new file mode 100644
index 0000000..8dd6a97
--- /dev/null
+++ b/src/M31.FluentApi.Tests/AnalyzerAndCodeFixes/TestClasses/FluentApiComments/VoidMethodClass/Student.Study.txt
@@ -0,0 +1,31 @@
+// Non-nullable member is uninitialized
+#pragma warning disable CS8618
+// ReSharper disable All
+
+using System;
+using System.Collections.Generic;
+using M31.FluentApi.Attributes;
+
+namespace M31.FluentApi.Tests.AnalyzerAndCodeFixes.TestClasses.FluentApiComments.VoidMethodClass;
+
+[FluentApi]
+public class Student
+{
+ [FluentMember(0)]
+ public string Name { get; private set; }
+
+ ////
+ //// ...
+ ////
+ //// ...
+ [FluentMethod(1)]
+ public void Study()
+ {
+ }
+
+ [FluentMethod(2)]
+ [FluentReturn]
+ public void Sleep()
+ {
+ }
+}
\ No newline at end of file
diff --git a/src/M31.FluentApi.Tests/AnalyzerAndCodeFixes/TestClasses/FluentApiComments/VoidMethodClass/Student.cs b/src/M31.FluentApi.Tests/AnalyzerAndCodeFixes/TestClasses/FluentApiComments/VoidMethodClass/Student.cs
new file mode 100644
index 0000000..5e590e2
--- /dev/null
+++ b/src/M31.FluentApi.Tests/AnalyzerAndCodeFixes/TestClasses/FluentApiComments/VoidMethodClass/Student.cs
@@ -0,0 +1,27 @@
+// Non-nullable member is uninitialized
+#pragma warning disable CS8618
+// ReSharper disable All
+
+using System;
+using System.Collections.Generic;
+using M31.FluentApi.Attributes;
+
+namespace M31.FluentApi.Tests.AnalyzerAndCodeFixes.TestClasses.FluentApiComments.VoidMethodClass;
+
+[FluentApi]
+public class Student
+{
+ [FluentMember(0)]
+ public string Name { get; private set; }
+
+ [FluentMethod(1)]
+ public void Study()
+ {
+ }
+
+ [FluentMethod(2)]
+ [FluentReturn]
+ public void Sleep()
+ {
+ }
+}
\ No newline at end of file
diff --git a/src/M31.FluentApi.Tests/AnalyzerAndCodeFixes/TestClasses/ReservedMethodClass1/Student.cs b/src/M31.FluentApi.Tests/AnalyzerAndCodeFixes/TestClasses/ReservedMethodClass1/Student.cs
index df7318e..95d8d6b 100644
--- a/src/M31.FluentApi.Tests/AnalyzerAndCodeFixes/TestClasses/ReservedMethodClass1/Student.cs
+++ b/src/M31.FluentApi.Tests/AnalyzerAndCodeFixes/TestClasses/ReservedMethodClass1/Student.cs
@@ -12,12 +12,10 @@ public class Student
[FluentMethod(0)]
public void InitialStep()
{
-
}
[FluentMethod(1, "InitialStep")]
public void Method1()
{
-
}
}
\ No newline at end of file
diff --git a/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/FluentApiComments/CommentedCompoundClass/CreateStudent.expected.txt b/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/FluentApiComments/CommentedCompoundClass/CreateStudent.expected.txt
new file mode 100644
index 0000000..a52bae8
--- /dev/null
+++ b/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/FluentApiComments/CommentedCompoundClass/CreateStudent.expected.txt
@@ -0,0 +1,73 @@
+//
+// 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
+
+namespace M31.FluentApi.Tests.CodeGeneration.TestClasses.Abstract.FluentApiComments.CommentedCompoundClass;
+
+public class CreateStudent :
+ CreateStudent.ICreateStudent,
+ CreateStudent.IWithName,
+ CreateStudent.IStudies
+{
+ private readonly Student student;
+
+ private CreateStudent()
+ {
+ student = new Student();
+ }
+
+ public static ICreateStudent InitialStep()
+ {
+ return new CreateStudent();
+ }
+
+ /// Sets the student's name.
+ /// The student's first name.
+ /// The student's last name.
+ public static IStudies WithName(string firstName, string lastName)
+ {
+ CreateStudent createStudent = new CreateStudent();
+ createStudent.student.FirstName = firstName;
+ createStudent.student.LastName = lastName;
+ return createStudent;
+ }
+
+ ///
+ IStudies IWithName.WithName(string firstName, string lastName)
+ {
+ student.FirstName = firstName;
+ student.LastName = lastName;
+ return this;
+ }
+
+ ///
+ Student IStudies.Studies(string courseOfStudy, int semester)
+ {
+ student.CourseOfStudy = courseOfStudy;
+ student.Semester = semester;
+ return student;
+ }
+
+ public interface ICreateStudent : IWithName
+ {
+ }
+
+ public interface IWithName
+ {
+ /// Sets the student's name.
+ /// The student's first name.
+ /// The student's last name.
+ IStudies WithName(string firstName, string lastName);
+ }
+
+ public interface IStudies
+ {
+ /// Sets the student's course of study and current semester.
+ /// The student's course of study.
+ /// The student's current semester.
+ Student Studies(string courseOfStudy, int semester);
+ }
+}
\ No newline at end of file
diff --git a/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/FluentApiComments/CommentedCompoundClass/CreateStudent.g.cs b/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/FluentApiComments/CommentedCompoundClass/CreateStudent.g.cs
new file mode 100644
index 0000000..a52bae8
--- /dev/null
+++ b/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/FluentApiComments/CommentedCompoundClass/CreateStudent.g.cs
@@ -0,0 +1,73 @@
+//
+// 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
+
+namespace M31.FluentApi.Tests.CodeGeneration.TestClasses.Abstract.FluentApiComments.CommentedCompoundClass;
+
+public class CreateStudent :
+ CreateStudent.ICreateStudent,
+ CreateStudent.IWithName,
+ CreateStudent.IStudies
+{
+ private readonly Student student;
+
+ private CreateStudent()
+ {
+ student = new Student();
+ }
+
+ public static ICreateStudent InitialStep()
+ {
+ return new CreateStudent();
+ }
+
+ /// Sets the student's name.
+ /// The student's first name.
+ /// The student's last name.
+ public static IStudies WithName(string firstName, string lastName)
+ {
+ CreateStudent createStudent = new CreateStudent();
+ createStudent.student.FirstName = firstName;
+ createStudent.student.LastName = lastName;
+ return createStudent;
+ }
+
+ ///
+ IStudies IWithName.WithName(string firstName, string lastName)
+ {
+ student.FirstName = firstName;
+ student.LastName = lastName;
+ return this;
+ }
+
+ ///
+ Student IStudies.Studies(string courseOfStudy, int semester)
+ {
+ student.CourseOfStudy = courseOfStudy;
+ student.Semester = semester;
+ return student;
+ }
+
+ public interface ICreateStudent : IWithName
+ {
+ }
+
+ public interface IWithName
+ {
+ /// Sets the student's name.
+ /// The student's first name.
+ /// The student's last name.
+ IStudies WithName(string firstName, string lastName);
+ }
+
+ public interface IStudies
+ {
+ /// Sets the student's course of study and current semester.
+ /// The student's course of study.
+ /// The student's current semester.
+ Student Studies(string courseOfStudy, int semester);
+ }
+}
\ No newline at end of file
diff --git a/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/FluentApiComments/CommentedCompoundClass/Student.cs b/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/FluentApiComments/CommentedCompoundClass/Student.cs
new file mode 100644
index 0000000..c91bcca
--- /dev/null
+++ b/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/FluentApiComments/CommentedCompoundClass/Student.cs
@@ -0,0 +1,33 @@
+// Non-nullable member is uninitialized
+#pragma warning disable CS8618
+// ReSharper disable All
+
+using M31.FluentApi.Attributes;
+
+namespace M31.FluentApi.Tests.CodeGeneration.TestClasses.Abstract.FluentApiComments.CommentedCompoundClass;
+
+[FluentApi]
+public class Student
+{
+ ////
+ //// Sets the student's name.
+ ////
+ //// The student's first name.
+ [FluentMember(0, "WithName")]
+ public string FirstName { get; set; }
+
+ //// The student's last name.
+ [FluentMember(0, "WithName")]
+ public string LastName { get; set; }
+
+ ////
+ //// Sets the student's course of study and current semester.
+ ////
+ //// The student's course of study.
+ [FluentMember(1, "Studies")]
+ public string CourseOfStudy { get; set; }
+
+ //// The student's current semester.
+ [FluentMember(1, "Studies")]
+ public int Semester { get; set; }
+}
\ No newline at end of file
diff --git a/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/FluentApiComments/CommentedLambdaCollectionClass/CreatePhone.g.cs b/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/FluentApiComments/CommentedLambdaCollectionClass/CreatePhone.g.cs
new file mode 100644
index 0000000..49e1704
--- /dev/null
+++ b/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/FluentApiComments/CommentedLambdaCollectionClass/CreatePhone.g.cs
@@ -0,0 +1,59 @@
+//
+// 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
+
+namespace M31.FluentApi.Tests.CodeGeneration.TestClasses.Abstract.FluentApiComments.CommentedLambdaCollectionClass;
+
+public class CreatePhone :
+ CreatePhone.ICreatePhone,
+ CreatePhone.IWithNumber,
+ CreatePhone.IWithUsage
+{
+ private readonly Phone phone;
+
+ private CreatePhone()
+ {
+ phone = new Phone();
+ }
+
+ public static ICreatePhone InitialStep()
+ {
+ return new CreatePhone();
+ }
+
+ public static IWithUsage WithNumber(string number)
+ {
+ CreatePhone createPhone = new CreatePhone();
+ createPhone.phone.Number = number;
+ return createPhone;
+ }
+
+ IWithUsage IWithNumber.WithNumber(string number)
+ {
+ phone.Number = number;
+ return this;
+ }
+
+ Phone IWithUsage.WithUsage(string usage)
+ {
+ phone.Usage = usage;
+ return phone;
+ }
+
+ public interface ICreatePhone : IWithNumber
+ {
+ }
+
+ public interface IWithNumber
+ {
+ IWithUsage WithNumber(string number);
+ }
+
+ public interface IWithUsage
+ {
+ Phone WithUsage(string usage);
+ }
+}
\ No newline at end of file
diff --git a/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/FluentApiComments/CommentedLambdaCollectionClass/CreateStudent.expected.txt b/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/FluentApiComments/CommentedLambdaCollectionClass/CreateStudent.expected.txt
new file mode 100644
index 0000000..0215330
--- /dev/null
+++ b/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/FluentApiComments/CommentedLambdaCollectionClass/CreateStudent.expected.txt
@@ -0,0 +1,120 @@
+//
+// 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.Collections.Generic;
+using System.Linq;
+using System;
+
+namespace M31.FluentApi.Tests.CodeGeneration.TestClasses.Abstract.FluentApiComments.CommentedLambdaCollectionClass;
+
+public class CreateStudent :
+ CreateStudent.ICreateStudent,
+ CreateStudent.IWithName,
+ CreateStudent.IWithPhoneNumbers
+{
+ private readonly Student student;
+
+ private CreateStudent()
+ {
+ student = new Student();
+ }
+
+ public static ICreateStudent InitialStep()
+ {
+ return new CreateStudent();
+ }
+
+ public static IWithPhoneNumbers WithName(string name)
+ {
+ CreateStudent createStudent = new CreateStudent();
+ createStudent.student.Name = name;
+ return createStudent;
+ }
+
+ IWithPhoneNumbers IWithName.WithName(string name)
+ {
+ student.Name = name;
+ return this;
+ }
+
+ ///
+ Student IWithPhoneNumbers.WithPhoneNumbers(System.Collections.Generic.IReadOnlyCollection phoneNumbers)
+ {
+ student.PhoneNumbers = phoneNumbers;
+ return student;
+ }
+
+ ///
+ Student IWithPhoneNumbers.WithPhoneNumbers(params M31.FluentApi.Tests.CodeGeneration.TestClasses.Abstract.FluentApiComments.CommentedLambdaCollectionClass.Phone[] phoneNumbers)
+ {
+ student.PhoneNumbers = phoneNumbers;
+ return student;
+ }
+
+ ///
+ Student IWithPhoneNumbers.WithPhoneNumbers(params Func[] createPhoneNumbers)
+ {
+ student.PhoneNumbers = createPhoneNumbers.Select(createPhoneNumber => createPhoneNumber(M31.FluentApi.Tests.CodeGeneration.TestClasses.Abstract.FluentApiComments.CommentedLambdaCollectionClass.CreatePhone.InitialStep())).ToArray();
+ return student;
+ }
+
+ ///
+ Student IWithPhoneNumbers.WithPhoneNumber(M31.FluentApi.Tests.CodeGeneration.TestClasses.Abstract.FluentApiComments.CommentedLambdaCollectionClass.Phone phoneNumber)
+ {
+ student.PhoneNumbers = new M31.FluentApi.Tests.CodeGeneration.TestClasses.Abstract.FluentApiComments.CommentedLambdaCollectionClass.Phone[1]{ phoneNumber };
+ return student;
+ }
+
+ ///
+ Student IWithPhoneNumbers.WithPhoneNumber(Func createPhoneNumber)
+ {
+ student.PhoneNumbers = new M31.FluentApi.Tests.CodeGeneration.TestClasses.Abstract.FluentApiComments.CommentedLambdaCollectionClass.Phone[1]{ createPhoneNumber(M31.FluentApi.Tests.CodeGeneration.TestClasses.Abstract.FluentApiComments.CommentedLambdaCollectionClass.CreatePhone.InitialStep()) };
+ return student;
+ }
+
+ ///
+ Student IWithPhoneNumbers.WithZeroPhoneNumbers()
+ {
+ student.PhoneNumbers = new M31.FluentApi.Tests.CodeGeneration.TestClasses.Abstract.FluentApiComments.CommentedLambdaCollectionClass.Phone[0];
+ return student;
+ }
+
+ public interface ICreateStudent : IWithName
+ {
+ }
+
+ public interface IWithName
+ {
+ IWithPhoneNumbers WithName(string name);
+ }
+
+ public interface IWithPhoneNumbers
+ {
+ /// Sets the student's phone numbers.
+ /// The student's phone numbers.
+ Student WithPhoneNumbers(System.Collections.Generic.IReadOnlyCollection phoneNumbers);
+
+ /// Sets the student's phone numbers.
+ /// The student's phone numbers.
+ Student WithPhoneNumbers(params M31.FluentApi.Tests.CodeGeneration.TestClasses.Abstract.FluentApiComments.CommentedLambdaCollectionClass.Phone[] phoneNumbers);
+
+ /// Sets the student's phone numbers.
+ /// Functions for creating the student's phone numbers.
+ Student WithPhoneNumbers(params Func[] createPhoneNumbers);
+
+ /// Sets the student's phone number.
+ /// The student's phone number.
+ Student WithPhoneNumber(M31.FluentApi.Tests.CodeGeneration.TestClasses.Abstract.FluentApiComments.CommentedLambdaCollectionClass.Phone phoneNumber);
+
+ /// Sets the student's phone number.
+ /// A function for creating the student's phone number.
+ Student WithPhoneNumber(Func createPhoneNumber);
+
+ /// Specifies that the student has no phone numbers.
+ Student WithZeroPhoneNumbers();
+ }
+}
\ No newline at end of file
diff --git a/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/FluentApiComments/CommentedLambdaCollectionClass/CreateStudent.g.cs b/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/FluentApiComments/CommentedLambdaCollectionClass/CreateStudent.g.cs
new file mode 100644
index 0000000..0215330
--- /dev/null
+++ b/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/FluentApiComments/CommentedLambdaCollectionClass/CreateStudent.g.cs
@@ -0,0 +1,120 @@
+//
+// 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.Collections.Generic;
+using System.Linq;
+using System;
+
+namespace M31.FluentApi.Tests.CodeGeneration.TestClasses.Abstract.FluentApiComments.CommentedLambdaCollectionClass;
+
+public class CreateStudent :
+ CreateStudent.ICreateStudent,
+ CreateStudent.IWithName,
+ CreateStudent.IWithPhoneNumbers
+{
+ private readonly Student student;
+
+ private CreateStudent()
+ {
+ student = new Student();
+ }
+
+ public static ICreateStudent InitialStep()
+ {
+ return new CreateStudent();
+ }
+
+ public static IWithPhoneNumbers WithName(string name)
+ {
+ CreateStudent createStudent = new CreateStudent();
+ createStudent.student.Name = name;
+ return createStudent;
+ }
+
+ IWithPhoneNumbers IWithName.WithName(string name)
+ {
+ student.Name = name;
+ return this;
+ }
+
+ ///
+ Student IWithPhoneNumbers.WithPhoneNumbers(System.Collections.Generic.IReadOnlyCollection phoneNumbers)
+ {
+ student.PhoneNumbers = phoneNumbers;
+ return student;
+ }
+
+ ///
+ Student IWithPhoneNumbers.WithPhoneNumbers(params M31.FluentApi.Tests.CodeGeneration.TestClasses.Abstract.FluentApiComments.CommentedLambdaCollectionClass.Phone[] phoneNumbers)
+ {
+ student.PhoneNumbers = phoneNumbers;
+ return student;
+ }
+
+ ///
+ Student IWithPhoneNumbers.WithPhoneNumbers(params Func[] createPhoneNumbers)
+ {
+ student.PhoneNumbers = createPhoneNumbers.Select(createPhoneNumber => createPhoneNumber(M31.FluentApi.Tests.CodeGeneration.TestClasses.Abstract.FluentApiComments.CommentedLambdaCollectionClass.CreatePhone.InitialStep())).ToArray();
+ return student;
+ }
+
+ ///
+ Student IWithPhoneNumbers.WithPhoneNumber(M31.FluentApi.Tests.CodeGeneration.TestClasses.Abstract.FluentApiComments.CommentedLambdaCollectionClass.Phone phoneNumber)
+ {
+ student.PhoneNumbers = new M31.FluentApi.Tests.CodeGeneration.TestClasses.Abstract.FluentApiComments.CommentedLambdaCollectionClass.Phone[1]{ phoneNumber };
+ return student;
+ }
+
+ ///
+ Student IWithPhoneNumbers.WithPhoneNumber(Func createPhoneNumber)
+ {
+ student.PhoneNumbers = new M31.FluentApi.Tests.CodeGeneration.TestClasses.Abstract.FluentApiComments.CommentedLambdaCollectionClass.Phone[1]{ createPhoneNumber(M31.FluentApi.Tests.CodeGeneration.TestClasses.Abstract.FluentApiComments.CommentedLambdaCollectionClass.CreatePhone.InitialStep()) };
+ return student;
+ }
+
+ ///
+ Student IWithPhoneNumbers.WithZeroPhoneNumbers()
+ {
+ student.PhoneNumbers = new M31.FluentApi.Tests.CodeGeneration.TestClasses.Abstract.FluentApiComments.CommentedLambdaCollectionClass.Phone[0];
+ return student;
+ }
+
+ public interface ICreateStudent : IWithName
+ {
+ }
+
+ public interface IWithName
+ {
+ IWithPhoneNumbers WithName(string name);
+ }
+
+ public interface IWithPhoneNumbers
+ {
+ /// Sets the student's phone numbers.
+ /// The student's phone numbers.
+ Student WithPhoneNumbers(System.Collections.Generic.IReadOnlyCollection phoneNumbers);
+
+ /// Sets the student's phone numbers.
+ /// The student's phone numbers.
+ Student WithPhoneNumbers(params M31.FluentApi.Tests.CodeGeneration.TestClasses.Abstract.FluentApiComments.CommentedLambdaCollectionClass.Phone[] phoneNumbers);
+
+ /// Sets the student's phone numbers.
+ /// Functions for creating the student's phone numbers.
+ Student WithPhoneNumbers(params Func[] createPhoneNumbers);
+
+ /// Sets the student's phone number.
+ /// The student's phone number.
+ Student WithPhoneNumber(M31.FluentApi.Tests.CodeGeneration.TestClasses.Abstract.FluentApiComments.CommentedLambdaCollectionClass.Phone phoneNumber);
+
+ /// Sets the student's phone number.
+ /// A function for creating the student's phone number.
+ Student WithPhoneNumber(Func createPhoneNumber);
+
+ /// Specifies that the student has no phone numbers.
+ Student WithZeroPhoneNumbers();
+ }
+}
\ No newline at end of file
diff --git a/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/FluentApiComments/CommentedLambdaCollectionClass/Phone.cs b/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/FluentApiComments/CommentedLambdaCollectionClass/Phone.cs
new file mode 100644
index 0000000..f7752a5
--- /dev/null
+++ b/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/FluentApiComments/CommentedLambdaCollectionClass/Phone.cs
@@ -0,0 +1,18 @@
+// Non-nullable member is uninitialized
+#pragma warning disable CS8618
+// ReSharper disable all
+
+using M31.FluentApi.Attributes;
+
+namespace M31.FluentApi.Tests.CodeGeneration.TestClasses.Abstract.FluentApiComments.
+ CommentedLambdaCollectionClass;
+
+[FluentApi]
+public class Phone
+{
+ [FluentMember(0)]
+ public string Number { get; set; }
+
+ [FluentMember(1)]
+ public string Usage { get; set; }
+}
\ No newline at end of file
diff --git a/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/FluentApiComments/CommentedLambdaCollectionClass/Student.cs b/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/FluentApiComments/CommentedLambdaCollectionClass/Student.cs
new file mode 100644
index 0000000..bb01569
--- /dev/null
+++ b/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/FluentApiComments/CommentedLambdaCollectionClass/Student.cs
@@ -0,0 +1,34 @@
+// Non-nullable member is uninitialized
+#pragma warning disable CS8618
+// ReSharper disable All
+
+using System.Collections.Generic;
+using M31.FluentApi.Attributes;
+
+namespace M31.FluentApi.Tests.CodeGeneration.TestClasses.Abstract.FluentApiComments.
+ CommentedLambdaCollectionClass;
+
+[FluentApi]
+public class Student
+{
+ [FluentMember(0)]
+ public string Name { get; set; }
+
+ ////
+ //// Sets the student's phone numbers.
+ ////
+ //// The student's phone numbers.
+ //// Functions for creating the student's phone numbers.
+ ////
+ ////
+ //// Sets the student's phone number.
+ ////
+ //// The student's phone number.
+ //// A function for creating the student's phone number.
+ ////
+ ////
+ //// Specifies that the student has no phone numbers.
+ ////
+ [FluentCollection(1, "PhoneNumber")]
+ public IReadOnlyCollection PhoneNumbers { get; set; }
+}
\ No newline at end of file
diff --git a/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/FluentApiComments/CommentedMethodsClass/CreateStudent.expected.txt b/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/FluentApiComments/CommentedMethodsClass/CreateStudent.expected.txt
new file mode 100644
index 0000000..2525ae9
--- /dev/null
+++ b/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/FluentApiComments/CommentedMethodsClass/CreateStudent.expected.txt
@@ -0,0 +1,102 @@
+//
+// 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 System.Reflection;
+
+namespace M31.FluentApi.Tests.CodeGeneration.TestClasses.Abstract.FluentApiComments.CommentedMethodsClass;
+
+public class CreateStudent :
+ CreateStudent.ICreateStudent,
+ CreateStudent.IWithName,
+ CreateStudent.IOfAgeBornOn
+{
+ private readonly Student student;
+ private static readonly MethodInfo withNameMethodInfo;
+ private static readonly PropertyInfo agePropertyInfo;
+ private static readonly MethodInfo bornOnMethodInfo;
+
+ static CreateStudent()
+ {
+ withNameMethodInfo = typeof(Student).GetMethod(
+ "WithName",
+ 0,
+ BindingFlags.Instance | BindingFlags.NonPublic,
+ null,
+ new Type[] { typeof(string), typeof(string) },
+ null)!;
+ agePropertyInfo = typeof(Student).GetProperty("Age", BindingFlags.Instance | BindingFlags.Public)!;
+ bornOnMethodInfo = typeof(Student).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();
+ }
+
+ /// Sets the student's first and last name.
+ /// The student's first name.
+ /// The student's last name.
+ public static IOfAgeBornOn WithName(string firstName, string lastName)
+ {
+ CreateStudent createStudent = new CreateStudent();
+ CreateStudent.withNameMethodInfo.Invoke(createStudent.student, new object?[] { firstName, lastName });
+ return createStudent;
+ }
+
+ ///
+ IOfAgeBornOn IWithName.WithName(string firstName, string lastName)
+ {
+ CreateStudent.withNameMethodInfo.Invoke(student, new object?[] { firstName, lastName });
+ return this;
+ }
+
+ Student IOfAgeBornOn.OfAge(int age)
+ {
+ CreateStudent.agePropertyInfo.SetValue(student, age);
+ return student;
+ }
+
+ ///
+ Student IOfAgeBornOn.BornOn(System.DateOnly dateOfBirth)
+ {
+ CreateStudent.bornOnMethodInfo.Invoke(student, new object?[] { dateOfBirth });
+ return student;
+ }
+
+ public interface ICreateStudent : IWithName
+ {
+ }
+
+ public interface IWithName
+ {
+ /// Sets the student's first and last name.
+ /// The student's first name.
+ /// The student's last name.
+ IOfAgeBornOn WithName(string firstName, string lastName);
+ }
+
+ public interface IOfAgeBornOn
+ {
+ Student OfAge(int age);
+
+ /// Calculates and sets the student's age based on the provided date of birth.
+ /// The student's date of birth.
+ Student BornOn(System.DateOnly dateOfBirth);
+ }
+}
\ No newline at end of file
diff --git a/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/FluentApiComments/CommentedMethodsClass/CreateStudent.g.cs b/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/FluentApiComments/CommentedMethodsClass/CreateStudent.g.cs
new file mode 100644
index 0000000..2525ae9
--- /dev/null
+++ b/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/FluentApiComments/CommentedMethodsClass/CreateStudent.g.cs
@@ -0,0 +1,102 @@
+//
+// 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 System.Reflection;
+
+namespace M31.FluentApi.Tests.CodeGeneration.TestClasses.Abstract.FluentApiComments.CommentedMethodsClass;
+
+public class CreateStudent :
+ CreateStudent.ICreateStudent,
+ CreateStudent.IWithName,
+ CreateStudent.IOfAgeBornOn
+{
+ private readonly Student student;
+ private static readonly MethodInfo withNameMethodInfo;
+ private static readonly PropertyInfo agePropertyInfo;
+ private static readonly MethodInfo bornOnMethodInfo;
+
+ static CreateStudent()
+ {
+ withNameMethodInfo = typeof(Student).GetMethod(
+ "WithName",
+ 0,
+ BindingFlags.Instance | BindingFlags.NonPublic,
+ null,
+ new Type[] { typeof(string), typeof(string) },
+ null)!;
+ agePropertyInfo = typeof(Student).GetProperty("Age", BindingFlags.Instance | BindingFlags.Public)!;
+ bornOnMethodInfo = typeof(Student).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();
+ }
+
+ /// Sets the student's first and last name.
+ /// The student's first name.
+ /// The student's last name.
+ public static IOfAgeBornOn WithName(string firstName, string lastName)
+ {
+ CreateStudent createStudent = new CreateStudent();
+ CreateStudent.withNameMethodInfo.Invoke(createStudent.student, new object?[] { firstName, lastName });
+ return createStudent;
+ }
+
+ ///
+ IOfAgeBornOn IWithName.WithName(string firstName, string lastName)
+ {
+ CreateStudent.withNameMethodInfo.Invoke(student, new object?[] { firstName, lastName });
+ return this;
+ }
+
+ Student IOfAgeBornOn.OfAge(int age)
+ {
+ CreateStudent.agePropertyInfo.SetValue(student, age);
+ return student;
+ }
+
+ ///
+ Student IOfAgeBornOn.BornOn(System.DateOnly dateOfBirth)
+ {
+ CreateStudent.bornOnMethodInfo.Invoke(student, new object?[] { dateOfBirth });
+ return student;
+ }
+
+ public interface ICreateStudent : IWithName
+ {
+ }
+
+ public interface IWithName
+ {
+ /// Sets the student's first and last name.
+ /// The student's first name.
+ /// The student's last name.
+ IOfAgeBornOn WithName(string firstName, string lastName);
+ }
+
+ public interface IOfAgeBornOn
+ {
+ Student OfAge(int age);
+
+ /// Calculates and sets the student's age based on the provided date of birth.
+ /// The student's date of birth.
+ Student BornOn(System.DateOnly dateOfBirth);
+ }
+}
\ No newline at end of file
diff --git a/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/FluentApiComments/CommentedMethodsClass/Student.cs b/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/FluentApiComments/CommentedMethodsClass/Student.cs
new file mode 100644
index 0000000..2f64848
--- /dev/null
+++ b/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/FluentApiComments/CommentedMethodsClass/Student.cs
@@ -0,0 +1,48 @@
+// 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.FluentApiComments.CommentedMethodsClass;
+
+[FluentApi]
+public class Student
+{
+ public string FirstName { get; private set; }
+ public string LastName { get; private set; }
+
+ ////
+ //// This summary will not be taken into account.
+ ////
+ //// This parameter documentation will not be taken into account.
+ //// This parameter documentation will not be taken into account.
+ ////
+ //// Sets the student's first and last name.
+ ////
+ //// The student's first name.
+ //// The student's last name.
+ [FluentMethod(0)]
+ private void WithName(string firstName, string lastName)
+ {
+ FirstName = firstName;
+ LastName = lastName;
+ }
+
+ [FluentMember(1, "OfAge")]
+ public int Age { get; private set; }
+
+ ////
+ //// Calculates and sets the student's age based on the provided date of birth.
+ ////
+ //// The student's date of birth.
+ [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/FluentApiComments/CommentedPropertiesClass/CreateStudent.expected.txt b/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/FluentApiComments/CommentedPropertiesClass/CreateStudent.expected.txt
new file mode 100644
index 0000000..40f765d
--- /dev/null
+++ b/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/FluentApiComments/CommentedPropertiesClass/CreateStudent.expected.txt
@@ -0,0 +1,124 @@
+//
+// 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
+
+namespace M31.FluentApi.Tests.CodeGeneration.TestClasses.Abstract.FluentApiComments.CommentedPropertiesClass;
+
+public class CreateStudent :
+ CreateStudent.ICreateStudent,
+ CreateStudent.IWithGivenNameWithFirstName,
+ CreateStudent.IWithLastName,
+ CreateStudent.IOfAge,
+ CreateStudent.IInSemester,
+ CreateStudent.ILivingIn
+{
+ private readonly Student student;
+
+ private CreateStudent()
+ {
+ student = new Student();
+ }
+
+ public static ICreateStudent InitialStep()
+ {
+ return new CreateStudent();
+ }
+
+ public static IWithLastName WithGivenName(string givenName)
+ {
+ CreateStudent createStudent = new CreateStudent();
+ createStudent.student.GivenName = givenName;
+ return createStudent;
+ }
+
+ /// Sets the student's first name.
+ /// The student's first name.
+ public static IWithLastName WithFirstName(string firstName)
+ {
+ CreateStudent createStudent = new CreateStudent();
+ createStudent.student.FirstName = firstName;
+ return createStudent;
+ }
+
+ IWithLastName IWithGivenNameWithFirstName.WithGivenName(string givenName)
+ {
+ student.GivenName = givenName;
+ return this;
+ }
+
+ ///
+ IWithLastName IWithGivenNameWithFirstName.WithFirstName(string firstName)
+ {
+ student.FirstName = firstName;
+ return this;
+ }
+
+ ///
+ IOfAge IWithLastName.WithLastName(string lastName)
+ {
+ student.LastName = lastName;
+ return this;
+ }
+
+ ///
+ IInSemester IOfAge.OfAge(int age)
+ {
+ student.Age = age;
+ return this;
+ }
+
+ ///
+ ILivingIn IInSemester.InSemester(int semester)
+ {
+ student.Semester = semester;
+ return this;
+ }
+
+ Student ILivingIn.LivingIn(string city)
+ {
+ student.City = city;
+ return student;
+ }
+
+ public interface ICreateStudent : IWithGivenNameWithFirstName
+ {
+ }
+
+ public interface IWithGivenNameWithFirstName
+ {
+ IWithLastName WithGivenName(string givenName);
+
+ /// Sets the student's first name.
+ /// The student's first name.
+ IWithLastName WithFirstName(string firstName);
+ }
+
+ public interface IWithLastName
+ {
+ /// Sets the student's last name.
+ /// The student's last name.
+ IOfAge WithLastName(string lastName);
+ }
+
+ public interface IOfAge
+ {
+ /// Sets the student's age.
+ /// The student's age.
+ IInSemester OfAge(int age);
+ }
+
+ public interface IInSemester
+ {
+ /// Sets the student's semester.
+ /// The student's semester.
+ ILivingIn InSemester(int semester);
+ }
+
+ public interface ILivingIn
+ {
+ Student LivingIn(string city);
+ }
+}
\ No newline at end of file
diff --git a/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/FluentApiComments/CommentedPropertiesClass/CreateStudent.g.cs b/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/FluentApiComments/CommentedPropertiesClass/CreateStudent.g.cs
new file mode 100644
index 0000000..40f765d
--- /dev/null
+++ b/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/FluentApiComments/CommentedPropertiesClass/CreateStudent.g.cs
@@ -0,0 +1,124 @@
+//
+// 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
+
+namespace M31.FluentApi.Tests.CodeGeneration.TestClasses.Abstract.FluentApiComments.CommentedPropertiesClass;
+
+public class CreateStudent :
+ CreateStudent.ICreateStudent,
+ CreateStudent.IWithGivenNameWithFirstName,
+ CreateStudent.IWithLastName,
+ CreateStudent.IOfAge,
+ CreateStudent.IInSemester,
+ CreateStudent.ILivingIn
+{
+ private readonly Student student;
+
+ private CreateStudent()
+ {
+ student = new Student();
+ }
+
+ public static ICreateStudent InitialStep()
+ {
+ return new CreateStudent();
+ }
+
+ public static IWithLastName WithGivenName(string givenName)
+ {
+ CreateStudent createStudent = new CreateStudent();
+ createStudent.student.GivenName = givenName;
+ return createStudent;
+ }
+
+ /// Sets the student's first name.
+ /// The student's first name.
+ public static IWithLastName WithFirstName(string firstName)
+ {
+ CreateStudent createStudent = new CreateStudent();
+ createStudent.student.FirstName = firstName;
+ return createStudent;
+ }
+
+ IWithLastName IWithGivenNameWithFirstName.WithGivenName(string givenName)
+ {
+ student.GivenName = givenName;
+ return this;
+ }
+
+ ///
+ IWithLastName IWithGivenNameWithFirstName.WithFirstName(string firstName)
+ {
+ student.FirstName = firstName;
+ return this;
+ }
+
+ ///
+ IOfAge IWithLastName.WithLastName(string lastName)
+ {
+ student.LastName = lastName;
+ return this;
+ }
+
+ ///
+ IInSemester IOfAge.OfAge(int age)
+ {
+ student.Age = age;
+ return this;
+ }
+
+ ///
+ ILivingIn IInSemester.InSemester(int semester)
+ {
+ student.Semester = semester;
+ return this;
+ }
+
+ Student ILivingIn.LivingIn(string city)
+ {
+ student.City = city;
+ return student;
+ }
+
+ public interface ICreateStudent : IWithGivenNameWithFirstName
+ {
+ }
+
+ public interface IWithGivenNameWithFirstName
+ {
+ IWithLastName WithGivenName(string givenName);
+
+ /// Sets the student's first name.
+ /// The student's first name.
+ IWithLastName WithFirstName(string firstName);
+ }
+
+ public interface IWithLastName
+ {
+ /// Sets the student's last name.
+ /// The student's last name.
+ IOfAge WithLastName(string lastName);
+ }
+
+ public interface IOfAge
+ {
+ /// Sets the student's age.
+ /// The student's age.
+ IInSemester OfAge(int age);
+ }
+
+ public interface IInSemester
+ {
+ /// Sets the student's semester.
+ /// The student's semester.
+ ILivingIn InSemester(int semester);
+ }
+
+ public interface ILivingIn
+ {
+ Student LivingIn(string city);
+ }
+}
\ No newline at end of file
diff --git a/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/FluentApiComments/CommentedPropertiesClass/Student.cs b/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/FluentApiComments/CommentedPropertiesClass/Student.cs
new file mode 100644
index 0000000..043831c
--- /dev/null
+++ b/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/FluentApiComments/CommentedPropertiesClass/Student.cs
@@ -0,0 +1,54 @@
+// Non-nullable member is uninitialized
+#pragma warning disable CS8618
+// ReSharper disable All
+// ReSharper disable InvalidXmlDocComment
+
+using M31.FluentApi.Attributes;
+
+namespace M31.FluentApi.Tests.CodeGeneration.TestClasses.Abstract.FluentApiComments.CommentedPropertiesClass;
+
+[FluentApi]
+public class Student
+{
+ ////
+ //// This summary will not be taken into account.
+ ////
+ //// This parameter will not be taken into account.
+ [FluentMember(0)]
+ public string GivenName { get; set; }
+
+ ////
+ //// Sets the student's first name.
+ ////
+ //// The student's first name.
+ [FluentMember(0)]
+ public string FirstName { get; set; }
+
+ ////
+ //// Sets the student's last name.
+ ////
+ //// The student's last name.
+ [FluentMember(1)]
+ public string LastName { get; set; }
+
+ ////
+ //// Sets the student's age.
+ ////
+ //// The student's age.
+ [FluentMember(2, "OfAge")]
+ public int Age { get; set; }
+
+ ////
+ //// Sets the student's semester.
+ ////
+ //// The student's semester.
+ [FluentMember(3, "InSemester")]
+ public int Semester { get; set; }
+
+ ////
+ //// This summary will not be taken into account.
+ ////
+ //// This parameter will not be taken into account.
+ [FluentMember(4, "LivingIn")]
+ public string City { get; set; }
+}
\ No newline at end of file
diff --git a/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/FluentApiComments/CommentedPropertiesClassAdvanced/CreateStudent.expected.txt b/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/FluentApiComments/CommentedPropertiesClassAdvanced/CreateStudent.expected.txt
new file mode 100644
index 0000000..43cc102
--- /dev/null
+++ b/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/FluentApiComments/CommentedPropertiesClassAdvanced/CreateStudent.expected.txt
@@ -0,0 +1,132 @@
+//
+// 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.Reflection;
+
+namespace M31.FluentApi.Tests.CodeGeneration.TestClasses.Abstract.FluentApiComments.CommentedPropertiesClassAdvanced;
+
+public class CreateStudent :
+ CreateStudent.ICreateStudent,
+ CreateStudent.IWithName,
+ CreateStudent.IOfAge,
+ CreateStudent.ILivingIn,
+ CreateStudent.IWhoIsHappy
+{
+ private readonly Student student;
+ private static readonly PropertyInfo isHappyPropertyInfo;
+
+ static CreateStudent()
+ {
+ isHappyPropertyInfo = typeof(Student).GetProperty("IsHappy", BindingFlags.Instance | BindingFlags.Public)!;
+ }
+
+ private CreateStudent()
+ {
+ student = new Student();
+ }
+
+ public static ICreateStudent InitialStep()
+ {
+ return new CreateStudent();
+ }
+
+ public static IOfAge WithName(string name)
+ {
+ CreateStudent createStudent = new CreateStudent();
+ createStudent.student.Name = name;
+ return createStudent;
+ }
+
+ IOfAge IWithName.WithName(string name)
+ {
+ student.Name = name;
+ return this;
+ }
+
+ ///
+ ILivingIn IOfAge.OfAge(int age)
+ {
+ student.Age = age;
+ return this;
+ }
+
+ ///
+ IWhoIsHappy ILivingIn.LivingIn(string? city)
+ {
+ student.City = city;
+ return this;
+ }
+
+ ///
+ IWhoIsHappy ILivingIn.LivingInBoston()
+ {
+ return this;
+ }
+
+ ///
+ IWhoIsHappy ILivingIn.InUnknownCity()
+ {
+ student.City = null;
+ return this;
+ }
+
+ Student IWhoIsHappy.WhoIsHappy(bool? isHappy)
+ {
+ CreateStudent.isHappyPropertyInfo.SetValue(student, isHappy);
+ return student;
+ }
+
+ Student IWhoIsHappy.WhoIsSad()
+ {
+ CreateStudent.isHappyPropertyInfo.SetValue(student, false);
+ return student;
+ }
+
+ Student IWhoIsHappy.WithUnknownMood()
+ {
+ CreateStudent.isHappyPropertyInfo.SetValue(student, null);
+ return student;
+ }
+
+ public interface ICreateStudent : IWithName
+ {
+ }
+
+ public interface IWithName
+ {
+ IOfAge WithName(string name);
+ }
+
+ public interface IOfAge
+ {
+ /// Sets the students's age.
+ /// The student's age.
+ ILivingIn OfAge(int age);
+ }
+
+ public interface ILivingIn
+ {
+ /// Sets the student's city.
+ /// The student's city.
+ IWhoIsHappy LivingIn(string? city);
+
+ /// Set's the student's city to Boston.
+ IWhoIsHappy LivingInBoston();
+
+ /// Set's the student's city to null.
+ IWhoIsHappy InUnknownCity();
+ }
+
+ public interface IWhoIsHappy
+ {
+ Student WhoIsHappy(bool? isHappy = true);
+
+ Student WhoIsSad();
+
+ Student WithUnknownMood();
+ }
+}
\ No newline at end of file
diff --git a/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/FluentApiComments/CommentedPropertiesClassAdvanced/CreateStudent.g.cs b/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/FluentApiComments/CommentedPropertiesClassAdvanced/CreateStudent.g.cs
new file mode 100644
index 0000000..43cc102
--- /dev/null
+++ b/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/FluentApiComments/CommentedPropertiesClassAdvanced/CreateStudent.g.cs
@@ -0,0 +1,132 @@
+//
+// 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.Reflection;
+
+namespace M31.FluentApi.Tests.CodeGeneration.TestClasses.Abstract.FluentApiComments.CommentedPropertiesClassAdvanced;
+
+public class CreateStudent :
+ CreateStudent.ICreateStudent,
+ CreateStudent.IWithName,
+ CreateStudent.IOfAge,
+ CreateStudent.ILivingIn,
+ CreateStudent.IWhoIsHappy
+{
+ private readonly Student student;
+ private static readonly PropertyInfo isHappyPropertyInfo;
+
+ static CreateStudent()
+ {
+ isHappyPropertyInfo = typeof(Student).GetProperty("IsHappy", BindingFlags.Instance | BindingFlags.Public)!;
+ }
+
+ private CreateStudent()
+ {
+ student = new Student();
+ }
+
+ public static ICreateStudent InitialStep()
+ {
+ return new CreateStudent();
+ }
+
+ public static IOfAge WithName(string name)
+ {
+ CreateStudent createStudent = new CreateStudent();
+ createStudent.student.Name = name;
+ return createStudent;
+ }
+
+ IOfAge IWithName.WithName(string name)
+ {
+ student.Name = name;
+ return this;
+ }
+
+ ///
+ ILivingIn IOfAge.OfAge(int age)
+ {
+ student.Age = age;
+ return this;
+ }
+
+ ///
+ IWhoIsHappy ILivingIn.LivingIn(string? city)
+ {
+ student.City = city;
+ return this;
+ }
+
+ ///
+ IWhoIsHappy ILivingIn.LivingInBoston()
+ {
+ return this;
+ }
+
+ ///
+ IWhoIsHappy ILivingIn.InUnknownCity()
+ {
+ student.City = null;
+ return this;
+ }
+
+ Student IWhoIsHappy.WhoIsHappy(bool? isHappy)
+ {
+ CreateStudent.isHappyPropertyInfo.SetValue(student, isHappy);
+ return student;
+ }
+
+ Student IWhoIsHappy.WhoIsSad()
+ {
+ CreateStudent.isHappyPropertyInfo.SetValue(student, false);
+ return student;
+ }
+
+ Student IWhoIsHappy.WithUnknownMood()
+ {
+ CreateStudent.isHappyPropertyInfo.SetValue(student, null);
+ return student;
+ }
+
+ public interface ICreateStudent : IWithName
+ {
+ }
+
+ public interface IWithName
+ {
+ IOfAge WithName(string name);
+ }
+
+ public interface IOfAge
+ {
+ /// Sets the students's age.
+ /// The student's age.
+ ILivingIn OfAge(int age);
+ }
+
+ public interface ILivingIn
+ {
+ /// Sets the student's city.
+ /// The student's city.
+ IWhoIsHappy LivingIn(string? city);
+
+ /// Set's the student's city to Boston.
+ IWhoIsHappy LivingInBoston();
+
+ /// Set's the student's city to null.
+ IWhoIsHappy InUnknownCity();
+ }
+
+ public interface IWhoIsHappy
+ {
+ Student WhoIsHappy(bool? isHappy = true);
+
+ Student WhoIsSad();
+
+ Student WithUnknownMood();
+ }
+}
\ No newline at end of file
diff --git a/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/FluentApiComments/CommentedPropertiesClassAdvanced/Student.cs b/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/FluentApiComments/CommentedPropertiesClassAdvanced/Student.cs
new file mode 100644
index 0000000..e3f8143
--- /dev/null
+++ b/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/FluentApiComments/CommentedPropertiesClassAdvanced/Student.cs
@@ -0,0 +1,43 @@
+// Non-nullable member is uninitialized
+#pragma warning disable CS8618
+// ReSharper disable All
+
+using M31.FluentApi.Attributes;
+
+namespace M31.FluentApi.Tests.CodeGeneration.TestClasses.Abstract.FluentApiComments.
+ CommentedPropertiesClassAdvanced;
+
+[FluentApi]
+public class Student
+{
+ [FluentMember(0)]
+ public string Name { get; set; }
+
+ ////
+ //// Sets the students's age.
+ ////
+ //// The student's age.
+ [FluentMember(1, "OfAge")]
+ public int Age { get; set; }
+
+ ////
+ //// Sets the student's city.
+ ////
+ //// The student's city.
+ ////
+ ////
+ //// Set's the student's city to Boston.
+ ////
+ ////
+ ////
+ //// Set's the student's city to null.
+ ////
+ [FluentMember(2, "LivingIn")]
+ [FluentDefault("LivingInBoston")]
+ [FluentNullable("InUnknownCity")]
+ public string? City { get; set; } = "Boston";
+
+ [FluentPredicate(3, "WhoIsHappy", "WhoIsSad")]
+ [FluentNullable("WithUnknownMood")]
+ public bool? IsHappy { get; private set; }
+}
\ No newline at end of file
diff --git a/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/FluentApiComments/IncompletelyCommentedPropertyClass/CreateStudent.expected.txt b/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/FluentApiComments/IncompletelyCommentedPropertyClass/CreateStudent.expected.txt
new file mode 100644
index 0000000..1c8abe3
--- /dev/null
+++ b/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/FluentApiComments/IncompletelyCommentedPropertyClass/CreateStudent.expected.txt
@@ -0,0 +1,75 @@
+//
+// 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
+
+namespace M31.FluentApi.Tests.CodeGeneration.TestClasses.Abstract.FluentApiComments.IncompletelyCommentedPropertyClass;
+
+public class CreateStudent :
+ CreateStudent.ICreateStudent,
+ CreateStudent.ILivingIn
+{
+ private readonly Student student;
+
+ private CreateStudent()
+ {
+ student = new Student();
+ }
+
+ public static ICreateStudent InitialStep()
+ {
+ return new CreateStudent();
+ }
+
+ public static Student LivingIn(string? city)
+ {
+ CreateStudent createStudent = new CreateStudent();
+ createStudent.student.City = city;
+ return createStudent.student;
+ }
+
+ public static Student LivingInBoston()
+ {
+ CreateStudent createStudent = new CreateStudent();
+ return createStudent.student;
+ }
+
+ public static Student InUnknownCity()
+ {
+ CreateStudent createStudent = new CreateStudent();
+ createStudent.student.City = null;
+ return createStudent.student;
+ }
+
+ Student ILivingIn.LivingIn(string? city)
+ {
+ student.City = city;
+ return student;
+ }
+
+ Student ILivingIn.LivingInBoston()
+ {
+ return student;
+ }
+
+ Student ILivingIn.InUnknownCity()
+ {
+ student.City = null;
+ return student;
+ }
+
+ public interface ICreateStudent : ILivingIn
+ {
+ }
+
+ public interface ILivingIn
+ {
+ Student LivingIn(string? city);
+
+ Student LivingInBoston();
+
+ Student InUnknownCity();
+ }
+}
\ No newline at end of file
diff --git a/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/FluentApiComments/IncompletelyCommentedPropertyClass/CreateStudent.g.cs b/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/FluentApiComments/IncompletelyCommentedPropertyClass/CreateStudent.g.cs
new file mode 100644
index 0000000..1c8abe3
--- /dev/null
+++ b/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/FluentApiComments/IncompletelyCommentedPropertyClass/CreateStudent.g.cs
@@ -0,0 +1,75 @@
+//
+// 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
+
+namespace M31.FluentApi.Tests.CodeGeneration.TestClasses.Abstract.FluentApiComments.IncompletelyCommentedPropertyClass;
+
+public class CreateStudent :
+ CreateStudent.ICreateStudent,
+ CreateStudent.ILivingIn
+{
+ private readonly Student student;
+
+ private CreateStudent()
+ {
+ student = new Student();
+ }
+
+ public static ICreateStudent InitialStep()
+ {
+ return new CreateStudent();
+ }
+
+ public static Student LivingIn(string? city)
+ {
+ CreateStudent createStudent = new CreateStudent();
+ createStudent.student.City = city;
+ return createStudent.student;
+ }
+
+ public static Student LivingInBoston()
+ {
+ CreateStudent createStudent = new CreateStudent();
+ return createStudent.student;
+ }
+
+ public static Student InUnknownCity()
+ {
+ CreateStudent createStudent = new CreateStudent();
+ createStudent.student.City = null;
+ return createStudent.student;
+ }
+
+ Student ILivingIn.LivingIn(string? city)
+ {
+ student.City = city;
+ return student;
+ }
+
+ Student ILivingIn.LivingInBoston()
+ {
+ return student;
+ }
+
+ Student ILivingIn.InUnknownCity()
+ {
+ student.City = null;
+ return student;
+ }
+
+ public interface ICreateStudent : ILivingIn
+ {
+ }
+
+ public interface ILivingIn
+ {
+ Student LivingIn(string? city);
+
+ Student LivingInBoston();
+
+ Student InUnknownCity();
+ }
+}
\ No newline at end of file
diff --git a/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/FluentApiComments/IncompletelyCommentedPropertyClass/Student.cs b/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/FluentApiComments/IncompletelyCommentedPropertyClass/Student.cs
new file mode 100644
index 0000000..c657c1e
--- /dev/null
+++ b/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/FluentApiComments/IncompletelyCommentedPropertyClass/Student.cs
@@ -0,0 +1,29 @@
+// Non-nullable member is uninitialized
+#pragma warning disable CS8618
+// ReSharper disable All
+
+using M31.FluentApi.Attributes;
+
+namespace M31.FluentApi.Tests.CodeGeneration.TestClasses.Abstract.FluentApiComments.
+ IncompletelyCommentedPropertyClass;
+
+[FluentApi]
+public class Student
+{
+ ////
+ //// Sets the student's city.
+ ////
+ //// The student's city.
+ ////
+ ////
+ //// Set's the student's city to Boston.
+ ////
+ ////
+ ////
+ //// Set's the student's city to null.
+ ////
+ [FluentMember(0, "LivingIn")]
+ [FluentDefault("LivingInBoston")]
+ [FluentNullable("InUnknownCity")]
+ public string? City { get; set; } = "Boston";
+}
\ No newline at end of file
diff --git a/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/FluentApiComments/RedundantCommentCompoundClass/CreateStudent.expected.txt b/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/FluentApiComments/RedundantCommentCompoundClass/CreateStudent.expected.txt
new file mode 100644
index 0000000..a609f51
--- /dev/null
+++ b/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/FluentApiComments/RedundantCommentCompoundClass/CreateStudent.expected.txt
@@ -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
+
+namespace M31.FluentApi.Tests.CodeGeneration.TestClasses.Abstract.FluentApiComments.RedundantCommentCompoundClass;
+
+public class CreateStudent :
+ CreateStudent.ICreateStudent,
+ CreateStudent.IWithName
+{
+ private readonly Student student;
+
+ private CreateStudent()
+ {
+ student = new Student();
+ }
+
+ public static ICreateStudent InitialStep()
+ {
+ return new CreateStudent();
+ }
+
+ /// Sets the student's name.
+ /// The student's first name.
+ /// The student's last name.
+ /// Sets the student's name.
+ /// The student's first name.
+ /// The student's last name.
+ public static Student WithName(string firstName, string lastName)
+ {
+ CreateStudent createStudent = new CreateStudent();
+ createStudent.student.FirstName = firstName;
+ createStudent.student.LastName = lastName;
+ return createStudent.student;
+ }
+
+ ///
+ Student IWithName.WithName(string firstName, string lastName)
+ {
+ student.FirstName = firstName;
+ student.LastName = lastName;
+ return student;
+ }
+
+ public interface ICreateStudent : IWithName
+ {
+ }
+
+ public interface IWithName
+ {
+ /// Sets the student's name.
+ /// The student's first name.
+ /// The student's last name.
+ /// Sets the student's name.
+ /// The student's first name.
+ /// The student's last name.
+ Student WithName(string firstName, string lastName);
+ }
+}
\ No newline at end of file
diff --git a/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/FluentApiComments/RedundantCommentCompoundClass/CreateStudent.g.cs b/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/FluentApiComments/RedundantCommentCompoundClass/CreateStudent.g.cs
new file mode 100644
index 0000000..a609f51
--- /dev/null
+++ b/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/FluentApiComments/RedundantCommentCompoundClass/CreateStudent.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
+
+namespace M31.FluentApi.Tests.CodeGeneration.TestClasses.Abstract.FluentApiComments.RedundantCommentCompoundClass;
+
+public class CreateStudent :
+ CreateStudent.ICreateStudent,
+ CreateStudent.IWithName
+{
+ private readonly Student student;
+
+ private CreateStudent()
+ {
+ student = new Student();
+ }
+
+ public static ICreateStudent InitialStep()
+ {
+ return new CreateStudent();
+ }
+
+ /// Sets the student's name.
+ /// The student's first name.
+ /// The student's last name.
+ /// Sets the student's name.
+ /// The student's first name.
+ /// The student's last name.
+ public static Student WithName(string firstName, string lastName)
+ {
+ CreateStudent createStudent = new CreateStudent();
+ createStudent.student.FirstName = firstName;
+ createStudent.student.LastName = lastName;
+ return createStudent.student;
+ }
+
+ ///
+ Student IWithName.WithName(string firstName, string lastName)
+ {
+ student.FirstName = firstName;
+ student.LastName = lastName;
+ return student;
+ }
+
+ public interface ICreateStudent : IWithName
+ {
+ }
+
+ public interface IWithName
+ {
+ /// Sets the student's name.
+ /// The student's first name.
+ /// The student's last name.
+ /// Sets the student's name.
+ /// The student's first name.
+ /// The student's last name.
+ Student WithName(string firstName, string lastName);
+ }
+}
\ No newline at end of file
diff --git a/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/FluentApiComments/RedundantCommentCompoundClass/Student.cs b/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/FluentApiComments/RedundantCommentCompoundClass/Student.cs
new file mode 100644
index 0000000..41ea6f3
--- /dev/null
+++ b/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/FluentApiComments/RedundantCommentCompoundClass/Student.cs
@@ -0,0 +1,27 @@
+// Non-nullable member is uninitialized
+#pragma warning disable CS8618
+// ReSharper disable All
+
+using M31.FluentApi.Attributes;
+
+namespace M31.FluentApi.Tests.CodeGeneration.TestClasses.Abstract.FluentApiComments.RedundantCommentCompoundClass;
+
+[FluentApi]
+public class Student
+{
+ ////
+ //// Sets the student's name.
+ ////
+ //// The student's first name.
+ //// The student's last name.
+ [FluentMember(0, "WithName")]
+ public string FirstName { get; set; }
+
+ ////
+ //// Sets the student's name.
+ ////
+ //// The student's first name.
+ //// The student's last name.
+ [FluentMember(0, "WithName")]
+ public string LastName { get; set; }
+}
\ No newline at end of file
diff --git a/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/FluentApiComments/WronglyCommentedClass/CreateStudent.expected.txt b/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/FluentApiComments/WronglyCommentedClass/CreateStudent.expected.txt
new file mode 100644
index 0000000..fe90a50
--- /dev/null
+++ b/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/FluentApiComments/WronglyCommentedClass/CreateStudent.expected.txt
@@ -0,0 +1,47 @@
+//
+// 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
+
+namespace M31.FluentApi.Tests.CodeGeneration.TestClasses.Abstract.FluentApiComments.WronglyCommentedClass;
+
+public class CreateStudent :
+ CreateStudent.ICreateStudent,
+ CreateStudent.IWithName
+{
+ private readonly Student student;
+
+ private CreateStudent()
+ {
+ student = new Student();
+ }
+
+ public static ICreateStudent InitialStep()
+ {
+ return new CreateStudent();
+ }
+
+ public static Student WithName(string name)
+ {
+ CreateStudent createStudent = new CreateStudent();
+ createStudent.student.Name = name;
+ return createStudent.student;
+ }
+
+ Student IWithName.WithName(string name)
+ {
+ student.Name = name;
+ return student;
+ }
+
+ public interface ICreateStudent : IWithName
+ {
+ }
+
+ public interface IWithName
+ {
+ Student WithName(string name);
+ }
+}
\ No newline at end of file
diff --git a/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/FluentApiComments/WronglyCommentedClass/CreateStudent.g.cs b/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/FluentApiComments/WronglyCommentedClass/CreateStudent.g.cs
new file mode 100644
index 0000000..fe90a50
--- /dev/null
+++ b/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/FluentApiComments/WronglyCommentedClass/CreateStudent.g.cs
@@ -0,0 +1,47 @@
+//
+// 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
+
+namespace M31.FluentApi.Tests.CodeGeneration.TestClasses.Abstract.FluentApiComments.WronglyCommentedClass;
+
+public class CreateStudent :
+ CreateStudent.ICreateStudent,
+ CreateStudent.IWithName
+{
+ private readonly Student student;
+
+ private CreateStudent()
+ {
+ student = new Student();
+ }
+
+ public static ICreateStudent InitialStep()
+ {
+ return new CreateStudent();
+ }
+
+ public static Student WithName(string name)
+ {
+ CreateStudent createStudent = new CreateStudent();
+ createStudent.student.Name = name;
+ return createStudent.student;
+ }
+
+ Student IWithName.WithName(string name)
+ {
+ student.Name = name;
+ return student;
+ }
+
+ public interface ICreateStudent : IWithName
+ {
+ }
+
+ public interface IWithName
+ {
+ Student WithName(string name);
+ }
+}
\ No newline at end of file
diff --git a/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/FluentApiComments/WronglyCommentedClass/Student.cs b/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/FluentApiComments/WronglyCommentedClass/Student.cs
new file mode 100644
index 0000000..be17027
--- /dev/null
+++ b/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/FluentApiComments/WronglyCommentedClass/Student.cs
@@ -0,0 +1,19 @@
+// Non-nullable member is uninitialized
+#pragma warning disable CS8618
+// ReSharper disable All
+
+using M31.FluentApi.Attributes;
+
+namespace M31.FluentApi.Tests.CodeGeneration.TestClasses.Abstract.FluentApiComments.
+ WronglyCommentedClass;
+
+[FluentApi]
+public class Student
+{
+ //
+ // Sets the student's name.
+ //
+ // The student's name.
+ [FluentMember(0)]
+ public string Name { get; set; }
+}
\ No newline at end of file
diff --git a/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/FluentApiComments/WronglyCommentedClass2/CreateStudent.expected.txt b/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/FluentApiComments/WronglyCommentedClass2/CreateStudent.expected.txt
new file mode 100644
index 0000000..d7e758b
--- /dev/null
+++ b/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/FluentApiComments/WronglyCommentedClass2/CreateStudent.expected.txt
@@ -0,0 +1,47 @@
+//
+// 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
+
+namespace M31.FluentApi.Tests.CodeGeneration.TestClasses.Abstract.FluentApiComments.WronglyCommentedClass2;
+
+public class CreateStudent :
+ CreateStudent.ICreateStudent,
+ CreateStudent.IWithName
+{
+ private readonly Student student;
+
+ private CreateStudent()
+ {
+ student = new Student();
+ }
+
+ public static ICreateStudent InitialStep()
+ {
+ return new CreateStudent();
+ }
+
+ public static Student WithName(string name)
+ {
+ CreateStudent createStudent = new CreateStudent();
+ createStudent.student.Name = name;
+ return createStudent.student;
+ }
+
+ Student IWithName.WithName(string name)
+ {
+ student.Name = name;
+ return student;
+ }
+
+ public interface ICreateStudent : IWithName
+ {
+ }
+
+ public interface IWithName
+ {
+ Student WithName(string name);
+ }
+}
\ No newline at end of file
diff --git a/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/FluentApiComments/WronglyCommentedClass2/CreateStudent.g.cs b/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/FluentApiComments/WronglyCommentedClass2/CreateStudent.g.cs
new file mode 100644
index 0000000..d7e758b
--- /dev/null
+++ b/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/FluentApiComments/WronglyCommentedClass2/CreateStudent.g.cs
@@ -0,0 +1,47 @@
+//
+// 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
+
+namespace M31.FluentApi.Tests.CodeGeneration.TestClasses.Abstract.FluentApiComments.WronglyCommentedClass2;
+
+public class CreateStudent :
+ CreateStudent.ICreateStudent,
+ CreateStudent.IWithName
+{
+ private readonly Student student;
+
+ private CreateStudent()
+ {
+ student = new Student();
+ }
+
+ public static ICreateStudent InitialStep()
+ {
+ return new CreateStudent();
+ }
+
+ public static Student WithName(string name)
+ {
+ CreateStudent createStudent = new CreateStudent();
+ createStudent.student.Name = name;
+ return createStudent.student;
+ }
+
+ Student IWithName.WithName(string name)
+ {
+ student.Name = name;
+ return student;
+ }
+
+ public interface ICreateStudent : IWithName
+ {
+ }
+
+ public interface IWithName
+ {
+ Student WithName(string name);
+ }
+}
\ No newline at end of file
diff --git a/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/FluentApiComments/WronglyCommentedClass2/Student.cs b/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/FluentApiComments/WronglyCommentedClass2/Student.cs
new file mode 100644
index 0000000..d4235ee
--- /dev/null
+++ b/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/FluentApiComments/WronglyCommentedClass2/Student.cs
@@ -0,0 +1,19 @@
+// Non-nullable member is uninitialized
+#pragma warning disable CS8618
+// ReSharper disable All
+
+using M31.FluentApi.Attributes;
+
+namespace M31.FluentApi.Tests.CodeGeneration.TestClasses.Abstract.FluentApiComments.
+ WronglyCommentedClass2;
+
+[FluentApi]
+public class Student
+{
+ /////
+ ///// Sets the student's name.
+ /////
+ ///// The student's name.
+ [FluentMember(0)]
+ public string Name { get; set; }
+}
\ No newline at end of file
diff --git a/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/FluentMethodClass/Student.cs b/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/FluentMethodClass/Student.cs
index c43b41b..e6b65a4 100644
--- a/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/FluentMethodClass/Student.cs
+++ b/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/FluentMethodClass/Student.cs
@@ -11,7 +11,7 @@ namespace M31.FluentApi.Tests.CodeGeneration.TestClasses.Abstract.FluentMethodCl
public class Student
{
public string Name { get; set; }
- public DateOnly DateOfBirth{ get; set; }
+ public DateOnly DateOfBirth { get; set; }
public int Semester { get; set; }
[FluentMethod(0)]
diff --git a/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/FluentMethodDefaultValuesClass/Student.cs b/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/FluentMethodDefaultValuesClass/Student.cs
index f725b43..59158c8 100644
--- a/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/FluentMethodDefaultValuesClass/Student.cs
+++ b/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/FluentMethodDefaultValuesClass/Student.cs
@@ -12,7 +12,7 @@ public class Student
{
public string FirstName { get; set; }
public string? LastName { get; set; }
- public DateOnly DateOfBirth{ get; set; }
+ public DateOnly DateOfBirth { get; set; }
public DateOnly EnrollmentDate { get; set; }
public int Semester { get; set; }
public int? NumberOfPassedExams { get; set; }
diff --git a/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/FluentNullableClass/Student.cs b/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/FluentNullableClass/Student.cs
index 5fe4ce3..41b7a53 100644
--- a/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/FluentNullableClass/Student.cs
+++ b/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/FluentNullableClass/Student.cs
@@ -14,5 +14,5 @@ public class Student
[FluentMember(1, "BornOn")]
[FluentNullable()]
- public DateOnly? DateOfBirth{ get; set; }
+ public DateOnly? DateOfBirth { get; set; }
}
\ No newline at end of file
diff --git a/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/FullyQualifiedTypeClass/Student.cs b/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/FullyQualifiedTypeClass/Student.cs
index 63a1865..3d59998 100644
--- a/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/FullyQualifiedTypeClass/Student.cs
+++ b/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/FullyQualifiedTypeClass/Student.cs
@@ -10,7 +10,7 @@ namespace M31.FluentApi.Tests.CodeGeneration.TestClasses.Abstract.FullyQualified
public class Student
{
[FluentMember(0, "BornOn")]
- public System.DateOnly DateOfBirth{ get; set; }
+ public System.DateOnly DateOfBirth { get; set; }
[FluentCollection(0, "Friend")]
public System.Collections.Generic.List Friends { get; set; }
diff --git a/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/GenericClassPrivateDefaultConstructor/Student.cs b/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/GenericClassPrivateDefaultConstructor/Student.cs
index 210ff24..2de2079 100644
--- a/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/GenericClassPrivateDefaultConstructor/Student.cs
+++ b/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/GenericClassPrivateDefaultConstructor/Student.cs
@@ -11,7 +11,6 @@ public class Student
{
private Student()
{
-
}
[FluentMember(0)]
diff --git a/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/GetPrivateInitPropertyClass/Student.cs b/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/GetPrivateInitPropertyClass/Student.cs
index f8ba4d3..21a2e61 100644
--- a/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/GetPrivateInitPropertyClass/Student.cs
+++ b/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/GetPrivateInitPropertyClass/Student.cs
@@ -1,4 +1,5 @@
using M31.FluentApi.Attributes;
+
// ReSharper disable All
namespace M31.FluentApi.Tests.CodeGeneration.TestClasses.Abstract.GetPrivateInitPropertyClass;
diff --git a/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/GetPrivateSetPropertyClass/Student.cs b/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/GetPrivateSetPropertyClass/Student.cs
index f4f1cb1..46e12c3 100644
--- a/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/GetPrivateSetPropertyClass/Student.cs
+++ b/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/GetPrivateSetPropertyClass/Student.cs
@@ -1,4 +1,5 @@
using M31.FluentApi.Attributes;
+
// ReSharper disable All
namespace M31.FluentApi.Tests.CodeGeneration.TestClasses.Abstract.GetPrivateSetPropertyClass;
diff --git a/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/InheritedClass/Person.cs b/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/InheritedClass/Person.cs
index 9138a12..1da3b09 100644
--- a/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/InheritedClass/Person.cs
+++ b/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/InheritedClass/Person.cs
@@ -14,5 +14,5 @@ public class Person
public string Name { get; set; }
[FluentMember(1, "BornOn")]
- public DateOnly DateOfBirth{ get; set; }
+ public DateOnly DateOfBirth { get; set; }
}
\ 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
index 8fb6b94..ac03eca 100644
--- a/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/InheritedClassProtectedMembers/Person.cs
+++ b/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/InheritedClassProtectedMembers/Person.cs
@@ -14,5 +14,5 @@ public class Person
protected string Name { get; set; }
[FluentMember(1, "BornOn")]
- protected DateOnly DateOfBirth{ get; set; }
+ protected DateOnly DateOfBirth { get; set; }
}
\ 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
index e5206b2..5214e85 100644
--- a/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/InheritedClassProtectedSetters/Person.cs
+++ b/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/InheritedClassProtectedSetters/Person.cs
@@ -14,5 +14,5 @@ public class Person
public string Name { get; protected set; }
[FluentMember(1, "BornOn")]
- public DateOnly DateOfBirth{ get; protected set; }
+ public DateOnly DateOfBirth { get; protected set; }
}
\ No newline at end of file
diff --git a/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/InternalClass/Student.cs b/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/InternalClass/Student.cs
index 799f25e..4442adc 100644
--- a/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/InternalClass/Student.cs
+++ b/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/InternalClass/Student.cs
@@ -14,7 +14,7 @@ internal class Student
public string Name { get; set; }
[FluentMember(1, "BornOn")]
- public DateOnly DateOfBirth{ get; set; }
+ public DateOnly DateOfBirth { get; set; }
[FluentMember(2, "InSemester")]
public int Semester { get; set; }
diff --git a/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/PrivateConstructorClass/Student.cs b/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/PrivateConstructorClass/Student.cs
index c488029..532a5d1 100644
--- a/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/PrivateConstructorClass/Student.cs
+++ b/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/PrivateConstructorClass/Student.cs
@@ -9,7 +9,6 @@ public class Student
{
private Student()
{
-
}
[FluentMember(0, "InSemester")]
diff --git a/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/ThreeMemberClass/Student.cs b/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/ThreeMemberClass/Student.cs
index 2504510..5133152 100644
--- a/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/ThreeMemberClass/Student.cs
+++ b/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/ThreeMemberClass/Student.cs
@@ -14,7 +14,7 @@ public class Student
public string Name { get; set; }
[FluentMember(1, "BornOn")]
- public DateOnly DateOfBirth{ get; set; }
+ public DateOnly DateOfBirth { get; set; }
[FluentMember(2, "InSemester")]
public int Semester { get; set; }
diff --git a/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/ThreeMemberRecord/Student.cs b/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/ThreeMemberRecord/Student.cs
index fa00dc1..a50bcae 100644
--- a/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/ThreeMemberRecord/Student.cs
+++ b/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/ThreeMemberRecord/Student.cs
@@ -14,7 +14,7 @@ public record Student
public string Name { get; set; }
[FluentMember(1, "BornOn")]
- public DateOnly DateOfBirth{ get; set; }
+ public DateOnly DateOfBirth { get; set; }
[FluentMember(2, "InSemester")]
public int Semester { get; set; }
diff --git a/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/ThreeMemberRecordStruct/Student.cs b/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/ThreeMemberRecordStruct/Student.cs
index eee6a54..1051c4e 100644
--- a/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/ThreeMemberRecordStruct/Student.cs
+++ b/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/ThreeMemberRecordStruct/Student.cs
@@ -14,7 +14,7 @@ public record struct Student
public string Name { get; set; }
[FluentMember(1, "BornOn")]
- public DateOnly DateOfBirth{ get; set; }
+ public DateOnly DateOfBirth { get; set; }
[FluentMember(2, "InSemester")]
public int Semester { get; set; }
diff --git a/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/ThreeMemberStruct/Student.cs b/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/ThreeMemberStruct/Student.cs
index b3d6e0c..e83488f 100644
--- a/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/ThreeMemberStruct/Student.cs
+++ b/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/ThreeMemberStruct/Student.cs
@@ -14,7 +14,7 @@ public struct Student
public string Name { get; set; }
[FluentMember(1, "BornOn")]
- public DateOnly DateOfBirth{ get; set; }
+ public DateOnly DateOfBirth { get; set; }
[FluentMember(2, "InSemester")]
public int Semester { get; set; }
diff --git a/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/ThreePrivateMembersClass/Student.cs b/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/ThreePrivateMembersClass/Student.cs
index d5d0ddc..34f298c 100644
--- a/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/ThreePrivateMembersClass/Student.cs
+++ b/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/ThreePrivateMembersClass/Student.cs
@@ -14,7 +14,7 @@ public class Student
public string Name { get; private set; }
[FluentMember(1, "BornOn")]
- public DateOnly DateOfBirth{ get; private set; }
+ public DateOnly DateOfBirth { get; private set; }
[FluentMember(2, "InSemester")]
public int Semester { get; private set; }
diff --git a/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/TryBreakFluentApiClass1/Student.cs b/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/TryBreakFluentApiClass1/Student.cs
index 1d87f73..dd94bfd 100644
--- a/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/TryBreakFluentApiClass1/Student.cs
+++ b/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/TryBreakFluentApiClass1/Student.cs
@@ -12,6 +12,5 @@ public class Student
[FluentMethod(0)]
private void SomeMethod(string someMethodMethodInfo)
{
-
}
}
\ No newline at end of file
diff --git a/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/TwoMemberClass/Student.cs b/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/TwoMemberClass/Student.cs
index 165d426..0f8b07c 100644
--- a/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/TwoMemberClass/Student.cs
+++ b/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/Abstract/TwoMemberClass/Student.cs
@@ -14,5 +14,5 @@ public class Student
public string Name { get; set; }
[FluentMember(1, "BornOn")]
- public DateOnly DateOfBirth{ get; set; }
+ public DateOnly DateOfBirth { get; set; }
}
\ No newline at end of file
diff --git a/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/DocumentedStudentClass/CreateDocumentedStudent.expected.txt b/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/DocumentedStudentClass/CreateDocumentedStudent.expected.txt
new file mode 100644
index 0000000..9753a24
--- /dev/null
+++ b/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/DocumentedStudentClass/CreateDocumentedStudent.expected.txt
@@ -0,0 +1,268 @@
+//
+// 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 System.Collections.Generic;
+using System.Reflection;
+
+namespace M31.FluentApi.Tests.CodeGeneration.TestClasses.DocumentedStudentClass;
+
+public class CreateDocumentedStudent :
+ CreateDocumentedStudent.ICreateDocumentedStudent,
+ CreateDocumentedStudent.INamed,
+ CreateDocumentedStudent.IOfAgeBornOn,
+ CreateDocumentedStudent.IInSemester,
+ CreateDocumentedStudent.ILivingIn,
+ CreateDocumentedStudent.IWhoIsHappy,
+ CreateDocumentedStudent.IWhoseFriendsAre
+{
+ private readonly DocumentedStudent documentedStudent;
+ private static readonly PropertyInfo firstNamePropertyInfo;
+ private static readonly PropertyInfo lastNamePropertyInfo;
+ private static readonly PropertyInfo agePropertyInfo;
+ private static readonly MethodInfo bornOnMethodInfo;
+ private static readonly PropertyInfo semesterPropertyInfo;
+ private static readonly PropertyInfo cityPropertyInfo;
+ private static readonly PropertyInfo isHappyPropertyInfo;
+ private static readonly PropertyInfo friendsPropertyInfo;
+
+ static CreateDocumentedStudent()
+ {
+ firstNamePropertyInfo = typeof(DocumentedStudent).GetProperty("FirstName", BindingFlags.Instance | BindingFlags.Public)!;
+ lastNamePropertyInfo = typeof(DocumentedStudent).GetProperty("LastName", BindingFlags.Instance | BindingFlags.Public)!;
+ agePropertyInfo = typeof(DocumentedStudent).GetProperty("Age", BindingFlags.Instance | BindingFlags.Public)!;
+ bornOnMethodInfo = typeof(DocumentedStudent).GetMethod(
+ "BornOn",
+ 0,
+ BindingFlags.Instance | BindingFlags.NonPublic,
+ null,
+ new Type[] { typeof(System.DateOnly) },
+ null)!;
+ semesterPropertyInfo = typeof(DocumentedStudent).GetProperty("Semester", BindingFlags.Instance | BindingFlags.Public)!;
+ cityPropertyInfo = typeof(DocumentedStudent).GetProperty("City", BindingFlags.Instance | BindingFlags.Public)!;
+ isHappyPropertyInfo = typeof(DocumentedStudent).GetProperty("IsHappy", BindingFlags.Instance | BindingFlags.Public)!;
+ friendsPropertyInfo = typeof(DocumentedStudent).GetProperty("Friends", BindingFlags.Instance | BindingFlags.Public)!;
+ }
+
+ private CreateDocumentedStudent()
+ {
+ documentedStudent = new DocumentedStudent();
+ }
+
+ public static ICreateDocumentedStudent InitialStep()
+ {
+ return new CreateDocumentedStudent();
+ }
+
+ /// Sets the student's name.
+ /// The student's first name.
+ /// The student's last name.
+ /// A builder for setting the student's age.
+ public static IOfAgeBornOn Named(string firstName, string lastName)
+ {
+ CreateDocumentedStudent createDocumentedStudent = new CreateDocumentedStudent();
+ CreateDocumentedStudent.firstNamePropertyInfo.SetValue(createDocumentedStudent.documentedStudent, firstName);
+ CreateDocumentedStudent.lastNamePropertyInfo.SetValue(createDocumentedStudent.documentedStudent, lastName);
+ return createDocumentedStudent;
+ }
+
+ ///
+ IOfAgeBornOn INamed.Named(string firstName, string lastName)
+ {
+ CreateDocumentedStudent.firstNamePropertyInfo.SetValue(documentedStudent, firstName);
+ CreateDocumentedStudent.lastNamePropertyInfo.SetValue(documentedStudent, lastName);
+ return this;
+ }
+
+ ///
+ IInSemester IOfAgeBornOn.OfAge(int age)
+ {
+ CreateDocumentedStudent.agePropertyInfo.SetValue(documentedStudent, age);
+ return this;
+ }
+
+ ///
+ IInSemester IOfAgeBornOn.BornOn(System.DateOnly dateOfBirth)
+ {
+ CreateDocumentedStudent.bornOnMethodInfo.Invoke(documentedStudent, new object?[] { dateOfBirth });
+ return this;
+ }
+
+ ///
+ ILivingIn IInSemester.InSemester(int semester)
+ {
+ CreateDocumentedStudent.semesterPropertyInfo.SetValue(documentedStudent, semester);
+ return this;
+ }
+
+ ///
+ ILivingIn IInSemester.WhoStartsUniversity()
+ {
+ return this;
+ }
+
+ ///
+ IWhoIsHappy ILivingIn.LivingIn(string? city)
+ {
+ CreateDocumentedStudent.cityPropertyInfo.SetValue(documentedStudent, city);
+ return this;
+ }
+
+ ///
+ IWhoIsHappy ILivingIn.LivingInBoston()
+ {
+ return this;
+ }
+
+ ///
+ IWhoIsHappy ILivingIn.InUnknownCity()
+ {
+ CreateDocumentedStudent.cityPropertyInfo.SetValue(documentedStudent, null);
+ return this;
+ }
+
+ ///
+ IWhoseFriendsAre IWhoIsHappy.WhoIsHappy(bool? isHappy)
+ {
+ CreateDocumentedStudent.isHappyPropertyInfo.SetValue(documentedStudent, isHappy);
+ return this;
+ }
+
+ ///
+ IWhoseFriendsAre IWhoIsHappy.WhoIsSad()
+ {
+ CreateDocumentedStudent.isHappyPropertyInfo.SetValue(documentedStudent, false);
+ return this;
+ }
+
+ ///
+ IWhoseFriendsAre IWhoIsHappy.WithUnknownMood()
+ {
+ CreateDocumentedStudent.isHappyPropertyInfo.SetValue(documentedStudent, null);
+ return this;
+ }
+
+ ///
+ DocumentedStudent IWhoseFriendsAre.WhoseFriendsAre(System.Collections.Generic.IReadOnlyCollection friends)
+ {
+ CreateDocumentedStudent.friendsPropertyInfo.SetValue(documentedStudent, friends);
+ return documentedStudent;
+ }
+
+ ///
+ DocumentedStudent IWhoseFriendsAre.WhoseFriendsAre(params string[] friends)
+ {
+ CreateDocumentedStudent.friendsPropertyInfo.SetValue(documentedStudent, friends);
+ return documentedStudent;
+ }
+
+ ///
+ DocumentedStudent IWhoseFriendsAre.WhoseFriendIs(string friend)
+ {
+ CreateDocumentedStudent.friendsPropertyInfo.SetValue(documentedStudent, new string[1]{ friend });
+ return documentedStudent;
+ }
+
+ ///
+ DocumentedStudent IWhoseFriendsAre.WhoHasNoFriends()
+ {
+ CreateDocumentedStudent.friendsPropertyInfo.SetValue(documentedStudent, new string[0]);
+ return documentedStudent;
+ }
+
+ public interface ICreateDocumentedStudent : INamed
+ {
+ }
+
+ public interface INamed
+ {
+ /// Sets the student's name.
+ /// The student's first name.
+ /// The student's last name.
+ /// A builder for setting the student's age.
+ IOfAgeBornOn Named(string firstName, string lastName);
+ }
+
+ public interface IOfAgeBornOn
+ {
+ /// Sets the student's age.
+ /// The student's age.
+ /// A builder for setting the student's semester.
+ IInSemester OfAge(int age);
+
+ /// Sets the student's age based on their date of birth.
+ /// The student's date of birth.
+ /// A builder for setting the student's semester.
+ IInSemester BornOn(System.DateOnly dateOfBirth);
+ }
+
+ public interface IInSemester
+ {
+ /// Sets the student's current semester.
+ /// The student's current semester.
+ /// A builder for setting the student's city.
+ ILivingIn InSemester(int semester);
+
+ /// Sets the student's semester to 0.
+ /// A builder for setting the student's city.
+ ILivingIn WhoStartsUniversity();
+ }
+
+ public interface ILivingIn
+ {
+ /// Sets the student's city.
+ /// The student's city.
+ /// A builder for setting whether the student is happy.
+ IWhoIsHappy LivingIn(string? city);
+
+ /// Sets the student's city to Boston.
+ /// A builder for setting whether the student is happy.
+ IWhoIsHappy LivingInBoston();
+
+ /// Sets the student's city to null.
+ /// A builder for setting whether the student is happy.
+ IWhoIsHappy InUnknownCity();
+ }
+
+ public interface IWhoIsHappy
+ {
+ /// Sets the property.
+ /// Indicates whether the student is happy.
+ /// A builder for setting the student's friends.
+ IWhoseFriendsAre WhoIsHappy(bool? isHappy = true);
+
+ /// Sets the property to false.
+ /// A builder for setting the student's friends.
+ IWhoseFriendsAre WhoIsSad();
+
+ /// Sets the property to null.
+ /// A builder for setting the student's friends.
+ IWhoseFriendsAre WithUnknownMood();
+ }
+
+ public interface IWhoseFriendsAre
+ {
+ /// Sets the student's friends.
+ /// The student's friends.
+ /// The .
+ DocumentedStudent WhoseFriendsAre(System.Collections.Generic.IReadOnlyCollection friends);
+
+ /// Sets the student's friends.
+ /// The student's friends.
+ /// The .
+ DocumentedStudent WhoseFriendsAre(params string[] friends);
+
+ /// Sets a single friend.
+ /// The student's friend.
+ /// The .
+ DocumentedStudent WhoseFriendIs(string friend);
+
+ /// Sets the student's friends to an empty collection.
+ /// The .
+ DocumentedStudent WhoHasNoFriends();
+ }
+}
\ No newline at end of file
diff --git a/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/DocumentedStudentClass/CreateDocumentedStudent.g.cs b/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/DocumentedStudentClass/CreateDocumentedStudent.g.cs
new file mode 100644
index 0000000..9753a24
--- /dev/null
+++ b/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/DocumentedStudentClass/CreateDocumentedStudent.g.cs
@@ -0,0 +1,268 @@
+//
+// 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 System.Collections.Generic;
+using System.Reflection;
+
+namespace M31.FluentApi.Tests.CodeGeneration.TestClasses.DocumentedStudentClass;
+
+public class CreateDocumentedStudent :
+ CreateDocumentedStudent.ICreateDocumentedStudent,
+ CreateDocumentedStudent.INamed,
+ CreateDocumentedStudent.IOfAgeBornOn,
+ CreateDocumentedStudent.IInSemester,
+ CreateDocumentedStudent.ILivingIn,
+ CreateDocumentedStudent.IWhoIsHappy,
+ CreateDocumentedStudent.IWhoseFriendsAre
+{
+ private readonly DocumentedStudent documentedStudent;
+ private static readonly PropertyInfo firstNamePropertyInfo;
+ private static readonly PropertyInfo lastNamePropertyInfo;
+ private static readonly PropertyInfo agePropertyInfo;
+ private static readonly MethodInfo bornOnMethodInfo;
+ private static readonly PropertyInfo semesterPropertyInfo;
+ private static readonly PropertyInfo cityPropertyInfo;
+ private static readonly PropertyInfo isHappyPropertyInfo;
+ private static readonly PropertyInfo friendsPropertyInfo;
+
+ static CreateDocumentedStudent()
+ {
+ firstNamePropertyInfo = typeof(DocumentedStudent).GetProperty("FirstName", BindingFlags.Instance | BindingFlags.Public)!;
+ lastNamePropertyInfo = typeof(DocumentedStudent).GetProperty("LastName", BindingFlags.Instance | BindingFlags.Public)!;
+ agePropertyInfo = typeof(DocumentedStudent).GetProperty("Age", BindingFlags.Instance | BindingFlags.Public)!;
+ bornOnMethodInfo = typeof(DocumentedStudent).GetMethod(
+ "BornOn",
+ 0,
+ BindingFlags.Instance | BindingFlags.NonPublic,
+ null,
+ new Type[] { typeof(System.DateOnly) },
+ null)!;
+ semesterPropertyInfo = typeof(DocumentedStudent).GetProperty("Semester", BindingFlags.Instance | BindingFlags.Public)!;
+ cityPropertyInfo = typeof(DocumentedStudent).GetProperty("City", BindingFlags.Instance | BindingFlags.Public)!;
+ isHappyPropertyInfo = typeof(DocumentedStudent).GetProperty("IsHappy", BindingFlags.Instance | BindingFlags.Public)!;
+ friendsPropertyInfo = typeof(DocumentedStudent).GetProperty("Friends", BindingFlags.Instance | BindingFlags.Public)!;
+ }
+
+ private CreateDocumentedStudent()
+ {
+ documentedStudent = new DocumentedStudent();
+ }
+
+ public static ICreateDocumentedStudent InitialStep()
+ {
+ return new CreateDocumentedStudent();
+ }
+
+ /// Sets the student's name.
+ /// The student's first name.
+ /// The student's last name.
+ /// A builder for setting the student's age.
+ public static IOfAgeBornOn Named(string firstName, string lastName)
+ {
+ CreateDocumentedStudent createDocumentedStudent = new CreateDocumentedStudent();
+ CreateDocumentedStudent.firstNamePropertyInfo.SetValue(createDocumentedStudent.documentedStudent, firstName);
+ CreateDocumentedStudent.lastNamePropertyInfo.SetValue(createDocumentedStudent.documentedStudent, lastName);
+ return createDocumentedStudent;
+ }
+
+ ///
+ IOfAgeBornOn INamed.Named(string firstName, string lastName)
+ {
+ CreateDocumentedStudent.firstNamePropertyInfo.SetValue(documentedStudent, firstName);
+ CreateDocumentedStudent.lastNamePropertyInfo.SetValue(documentedStudent, lastName);
+ return this;
+ }
+
+ ///
+ IInSemester IOfAgeBornOn.OfAge(int age)
+ {
+ CreateDocumentedStudent.agePropertyInfo.SetValue(documentedStudent, age);
+ return this;
+ }
+
+ ///
+ IInSemester IOfAgeBornOn.BornOn(System.DateOnly dateOfBirth)
+ {
+ CreateDocumentedStudent.bornOnMethodInfo.Invoke(documentedStudent, new object?[] { dateOfBirth });
+ return this;
+ }
+
+ ///
+ ILivingIn IInSemester.InSemester(int semester)
+ {
+ CreateDocumentedStudent.semesterPropertyInfo.SetValue(documentedStudent, semester);
+ return this;
+ }
+
+ ///
+ ILivingIn IInSemester.WhoStartsUniversity()
+ {
+ return this;
+ }
+
+ ///
+ IWhoIsHappy ILivingIn.LivingIn(string? city)
+ {
+ CreateDocumentedStudent.cityPropertyInfo.SetValue(documentedStudent, city);
+ return this;
+ }
+
+ ///
+ IWhoIsHappy ILivingIn.LivingInBoston()
+ {
+ return this;
+ }
+
+ ///
+ IWhoIsHappy ILivingIn.InUnknownCity()
+ {
+ CreateDocumentedStudent.cityPropertyInfo.SetValue(documentedStudent, null);
+ return this;
+ }
+
+ ///
+ IWhoseFriendsAre IWhoIsHappy.WhoIsHappy(bool? isHappy)
+ {
+ CreateDocumentedStudent.isHappyPropertyInfo.SetValue(documentedStudent, isHappy);
+ return this;
+ }
+
+ ///
+ IWhoseFriendsAre IWhoIsHappy.WhoIsSad()
+ {
+ CreateDocumentedStudent.isHappyPropertyInfo.SetValue(documentedStudent, false);
+ return this;
+ }
+
+ ///
+ IWhoseFriendsAre IWhoIsHappy.WithUnknownMood()
+ {
+ CreateDocumentedStudent.isHappyPropertyInfo.SetValue(documentedStudent, null);
+ return this;
+ }
+
+ ///
+ DocumentedStudent IWhoseFriendsAre.WhoseFriendsAre(System.Collections.Generic.IReadOnlyCollection friends)
+ {
+ CreateDocumentedStudent.friendsPropertyInfo.SetValue(documentedStudent, friends);
+ return documentedStudent;
+ }
+
+ ///
+ DocumentedStudent IWhoseFriendsAre.WhoseFriendsAre(params string[] friends)
+ {
+ CreateDocumentedStudent.friendsPropertyInfo.SetValue(documentedStudent, friends);
+ return documentedStudent;
+ }
+
+ ///
+ DocumentedStudent IWhoseFriendsAre.WhoseFriendIs(string friend)
+ {
+ CreateDocumentedStudent.friendsPropertyInfo.SetValue(documentedStudent, new string[1]{ friend });
+ return documentedStudent;
+ }
+
+ ///
+ DocumentedStudent IWhoseFriendsAre.WhoHasNoFriends()
+ {
+ CreateDocumentedStudent.friendsPropertyInfo.SetValue(documentedStudent, new string[0]);
+ return documentedStudent;
+ }
+
+ public interface ICreateDocumentedStudent : INamed
+ {
+ }
+
+ public interface INamed
+ {
+ /// Sets the student's name.
+ /// The student's first name.
+ /// The student's last name.
+ /// A builder for setting the student's age.
+ IOfAgeBornOn Named(string firstName, string lastName);
+ }
+
+ public interface IOfAgeBornOn
+ {
+ /// Sets the student's age.
+ /// The student's age.
+ /// A builder for setting the student's semester.
+ IInSemester OfAge(int age);
+
+ /// Sets the student's age based on their date of birth.
+ /// The student's date of birth.
+ /// A builder for setting the student's semester.
+ IInSemester BornOn(System.DateOnly dateOfBirth);
+ }
+
+ public interface IInSemester
+ {
+ /// Sets the student's current semester.
+ /// The student's current semester.
+ /// A builder for setting the student's city.
+ ILivingIn InSemester(int semester);
+
+ /// Sets the student's semester to 0.
+ /// A builder for setting the student's city.
+ ILivingIn WhoStartsUniversity();
+ }
+
+ public interface ILivingIn
+ {
+ /// Sets the student's city.
+ /// The student's city.
+ /// A builder for setting whether the student is happy.
+ IWhoIsHappy LivingIn(string? city);
+
+ /// Sets the student's city to Boston.
+ /// A builder for setting whether the student is happy.
+ IWhoIsHappy LivingInBoston();
+
+ /// Sets the student's city to null.
+ /// A builder for setting whether the student is happy.
+ IWhoIsHappy InUnknownCity();
+ }
+
+ public interface IWhoIsHappy
+ {
+ /// Sets the property.
+ /// Indicates whether the student is happy.
+ /// A builder for setting the student's friends.
+ IWhoseFriendsAre WhoIsHappy(bool? isHappy = true);
+
+ /// Sets the property to false.
+ /// A builder for setting the student's friends.
+ IWhoseFriendsAre WhoIsSad();
+
+ /// Sets the property to null.
+ /// A builder for setting the student's friends.
+ IWhoseFriendsAre WithUnknownMood();
+ }
+
+ public interface IWhoseFriendsAre
+ {
+ /// Sets the student's friends.
+ /// The student's friends.
+ /// The .
+ DocumentedStudent WhoseFriendsAre(System.Collections.Generic.IReadOnlyCollection friends);
+
+ /// Sets the student's friends.
+ /// The student's friends.
+ /// The .
+ DocumentedStudent WhoseFriendsAre(params string[] friends);
+
+ /// Sets a single friend.
+ /// The student's friend.
+ /// The .
+ DocumentedStudent WhoseFriendIs(string friend);
+
+ /// Sets the student's friends to an empty collection.
+ /// The .
+ DocumentedStudent WhoHasNoFriends();
+ }
+}
\ No newline at end of file
diff --git a/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/DocumentedStudentClass/DocumentedStudent.cs b/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/DocumentedStudentClass/DocumentedStudent.cs
new file mode 100644
index 0000000..113bde8
--- /dev/null
+++ b/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/DocumentedStudentClass/DocumentedStudent.cs
@@ -0,0 +1,119 @@
+// Non-nullable member is uninitialized
+#pragma warning disable CS8618
+// ReSharper disable All
+
+using System;
+using System.Collections.Generic;
+using M31.FluentApi.Attributes;
+
+namespace M31.FluentApi.Tests.CodeGeneration.TestClasses.DocumentedStudentClass;
+
+[FluentApi]
+public class DocumentedStudent
+{
+ ////
+ //// Sets the student's name.
+ ////
+ //// The student's first name.
+ //// The student's last name.
+ //// A builder for setting the student's age.
+ [FluentMember(0, "Named", 0)]
+ public string FirstName { get; private set; }
+
+ [FluentMember(0, "Named", 1)]
+ public string LastName { get; private set; }
+
+ ////
+ //// Sets the student's age.
+ ////
+ //// The student's age.
+ //// A builder for setting the student's semester.
+ [FluentMember(1, "OfAge")]
+ public int Age { get; private set; }
+
+ ////
+ //// Sets the student's age based on their date of birth.
+ ////
+ //// The student's date of birth.
+ //// A builder for setting the student's semester.
+ [FluentMethod(1)]
+ private void BornOn(DateOnly dateOfBirth)
+ {
+ DateOnly today = DateOnly.FromDateTime(DateTime.Today);
+ int age = today.Year - dateOfBirth.Year;
+ if (dateOfBirth > today.AddYears(-age)) age--;
+ Age = age;
+ }
+
+ ////
+ //// Sets the student's current semester.
+ ////
+ //// The student's current semester.
+ //// A builder for setting the student's city.
+ ////
+ ////
+ //// Sets the student's semester to 0.
+ ////
+ //// A builder for setting the student's city.
+ [FluentMember(2, "InSemester")]
+ [FluentDefault("WhoStartsUniversity")]
+ public int Semester { get; private set; } = 0;
+
+ ////
+ //// Sets the student's city.
+ ////
+ //// The student's city.
+ //// A builder for setting whether the student is happy.
+ ////
+ ////
+ //// Sets the student's city to Boston.
+ ////
+ //// A builder for setting whether the student is happy.
+ ////
+ ////
+ //// Sets the student's city to null.
+ ////
+ //// A builder for setting whether the student is happy.
+ [FluentMember(3, "LivingIn")]
+ [FluentDefault("LivingInBoston")]
+ [FluentNullable("InUnknownCity")]
+ public string? City { get; private set; } = "Boston";
+
+ ////
+ //// Sets the property.
+ ////
+ //// Indicates whether the student is happy.
+ //// A builder for setting the student's friends.
+ ////
+ ////
+ //// Sets the property to false.
+ ////
+ //// A builder for setting the student's friends.
+ ////
+ ////
+ //// Sets the property to null.
+ ////
+ //// A builder for setting the student's friends.
+ [FluentPredicate(4, "WhoIsHappy", "WhoIsSad")]
+ [FluentNullable("WithUnknownMood")]
+ public bool? IsHappy { get; private set; }
+
+ ////
+ //// Sets the student's friends.
+ ////
+ //// The student's friends.
+ //// The .
+ ////
+ ////
+ //// Sets a single friend.
+ ////
+ //// The student's friend.
+ //// The .
+ ////
+ ////
+ //// Sets the student's friends to an empty collection.
+ ////
+ //// The .
+ [FluentCollection(5, "Friend", "WhoseFriendsAre", "WhoseFriendIs", "WhoHasNoFriends")]
+ public IReadOnlyCollection Friends { get; private set; }
+}
\ No newline at end of file
diff --git a/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/PersonClass/Person.cs b/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/PersonClass/Person.cs
index 21713e3..2f39d3a 100644
--- a/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/PersonClass/Person.cs
+++ b/src/M31.FluentApi.Tests/CodeGeneration/TestClasses/PersonClass/Person.cs
@@ -38,24 +38,24 @@ private void WhoLivesAtAddress()
{
}
- [FluentMethod(4)]
- private void WithHouseNumber(string houseNumber)
- {
- HouseNumber = houseNumber;
- }
-
- [FluentMethod(5)]
- private void WithStreet(string street)
- {
- Street = street;
- }
-
- [FluentMethod(6)]
- [FluentBreak]
- private void InCity(string city)
- {
- City = city;
- }
+ [FluentMethod(4)]
+ private void WithHouseNumber(string houseNumber)
+ {
+ HouseNumber = houseNumber;
+ }
+
+ [FluentMethod(5)]
+ private void WithStreet(string street)
+ {
+ Street = street;
+ }
+
+ [FluentMethod(6)]
+ [FluentBreak]
+ private void InCity(string city)
+ {
+ City = city;
+ }
[FluentMethod(3)]
[FluentContinueWith(7)]
@@ -64,9 +64,9 @@ private void WhoIsADigitalNomad()
IsDigitalNomad = true;
}
- [FluentMethod(7)]
- private void LivingInCity(string city)
- {
- City = city;
- }
+ [FluentMethod(7)]
+ private void LivingInCity(string city)
+ {
+ City = city;
+ }
}
\ 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 9ad6a26..00a65b8 100644
--- a/src/M31.FluentApi.Tests/CodeGeneration/TestDataProvider.cs
+++ b/src/M31.FluentApi.Tests/CodeGeneration/TestDataProvider.cs
@@ -21,6 +21,15 @@ internal class TestDataProvider : IEnumerable
public FluentBreakAttribute()
{
-
}
}
\ No newline at end of file
diff --git a/src/M31.FluentApi/Attributes/FluentContinueWithAttribute.cs b/src/M31.FluentApi/Attributes/FluentContinueWithAttribute.cs
index 34fd1ed..188dfa3 100644
--- a/src/M31.FluentApi/Attributes/FluentContinueWithAttribute.cs
+++ b/src/M31.FluentApi/Attributes/FluentContinueWithAttribute.cs
@@ -14,6 +14,5 @@ public class FluentContinueWithAttribute : Attribute
/// The builder step to continue with.
public FluentContinueWithAttribute(int builderStep)
{
-
}
}
\ No newline at end of file
diff --git a/src/M31.FluentApi/Attributes/FluentDefaultAttribute.cs b/src/M31.FluentApi/Attributes/FluentDefaultAttribute.cs
index 2bf29d1..c8bb50a 100644
--- a/src/M31.FluentApi/Attributes/FluentDefaultAttribute.cs
+++ b/src/M31.FluentApi/Attributes/FluentDefaultAttribute.cs
@@ -16,6 +16,5 @@ public class FluentDefaultAttribute : Attribute
///
public FluentDefaultAttribute(string method = "WithDefault{Name}")
{
-
}
}
\ No newline at end of file
diff --git a/src/M31.FluentApi/Attributes/FluentLambdaAttribute.cs b/src/M31.FluentApi/Attributes/FluentLambdaAttribute.cs
index 6961f32..1f99c06 100644
--- a/src/M31.FluentApi/Attributes/FluentLambdaAttribute.cs
+++ b/src/M31.FluentApi/Attributes/FluentLambdaAttribute.cs
@@ -4,10 +4,10 @@ namespace M31.FluentApi.Attributes;
///
/// Generates a builder method that accepts a lambda expression for creating the target field or property with its
-/// Fluent API.
+/// fluent API.
///
[Obsolete(
- "Use FluentMember instead. Lambda methods are generated by default if the decorated member has a Fluent API.",
+ "Use FluentMember instead. Lambda methods are generated by default if the decorated member has a fluent API.",
false)]
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
public class FluentLambdaAttribute : Attribute
diff --git a/src/M31.FluentApi/Attributes/FluentMethodAttribute.cs b/src/M31.FluentApi/Attributes/FluentMethodAttribute.cs
index e1a53af..d280cfd 100644
--- a/src/M31.FluentApi/Attributes/FluentMethodAttribute.cs
+++ b/src/M31.FluentApi/Attributes/FluentMethodAttribute.cs
@@ -16,6 +16,5 @@ public class FluentMethodAttribute : Attribute
///
public FluentMethodAttribute(int builderStep, string method = "{Name}")
{
-
}
}
\ No newline at end of file
diff --git a/src/M31.FluentApi/Attributes/FluentNullableAttribute.cs b/src/M31.FluentApi/Attributes/FluentNullableAttribute.cs
index e44bd3d..ea9b6a2 100644
--- a/src/M31.FluentApi/Attributes/FluentNullableAttribute.cs
+++ b/src/M31.FluentApi/Attributes/FluentNullableAttribute.cs
@@ -15,6 +15,5 @@ public class FluentNullableAttribute : Attribute
/// 'null'.
public FluentNullableAttribute(string method = "Without{Name}")
{
-
}
}
\ No newline at end of file
diff --git a/src/M31.FluentApi/Attributes/FluentReturnAttribute.cs b/src/M31.FluentApi/Attributes/FluentReturnAttribute.cs
index 0b63a20..abdf3ce 100644
--- a/src/M31.FluentApi/Attributes/FluentReturnAttribute.cs
+++ b/src/M31.FluentApi/Attributes/FluentReturnAttribute.cs
@@ -11,6 +11,5 @@ public class FluentReturnAttribute : Attribute
///
public FluentReturnAttribute()
{
-
}
}
\ No newline at end of file
diff --git a/src/M31.FluentApi/Attributes/FluentSkippableAttribute.cs b/src/M31.FluentApi/Attributes/FluentSkippableAttribute.cs
index 51bef63..259ce77 100644
--- a/src/M31.FluentApi/Attributes/FluentSkippableAttribute.cs
+++ b/src/M31.FluentApi/Attributes/FluentSkippableAttribute.cs
@@ -11,6 +11,5 @@ public class FluentSkippableAttribute : Attribute
///
public FluentSkippableAttribute()
{
-
}
}
\ No newline at end of file
diff --git a/src/M31.FluentApi/M31.FluentApi.csproj b/src/M31.FluentApi/M31.FluentApi.csproj
index e522469..95052a9 100644
--- a/src/M31.FluentApi/M31.FluentApi.csproj
+++ b/src/M31.FluentApi/M31.FluentApi.csproj
@@ -7,7 +7,7 @@
enable
true
true
- 1.10.0
+ 1.11.0
Kevin Schaal
Generate fluent builders in C#.
fluentapi fluentbuilder fluentinterface fluentdesign fluent codegeneration