Skip to content

Commit 3e69734

Browse files
authored
Support default values in fluent methods (#5)
* fix: address minor warnings * feat(FluentMethod): support default parameter values
1 parent 4b0cbe5 commit 3e69734

File tree

12 files changed

+309
-12
lines changed

12 files changed

+309
-12
lines changed

src/ExampleProject/ExampleProject.csproj

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,13 @@
88
</PropertyGroup>
99

1010
<ItemGroup>
11-
<PackageReference Include="M31.FluentApi" Version="1.0.0" PrivateAssets="all" />
11+
<ProjectReference Include="..\M31.FluentApi\M31.FluentApi.csproj"/>
12+
<ProjectReference Include="..\M31.FluentApi.Generator\M31.FluentApi.Generator.csproj" ReferenceOutputAssembly="false" OutputItemType="Analyzer"/>
1213
</ItemGroup>
1314

1415
<PropertyGroup>
1516
<CompilerGeneratedFilesOutputPath>$(BaseIntermediateOutputPath)Generated</CompilerGeneratedFilesOutputPath>
1617
<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
1718
</PropertyGroup>
1819

19-
</Project>
20+
</Project>

src/M31.FluentApi.Generator/CodeBuilding/CodeBuilder.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ internal CodeBuilder Append(string? code)
6060
if (code is not null)
6161
{
6262
InsertNewLines();
63-
stringBuilder.Append(code!);
63+
stringBuilder.Append(code);
6464
}
6565

6666
return this;

src/M31.FluentApi.Generator/CodeGeneration/CodeBoardActors/MethodCreation/Collections/CollectionMethodCreator.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ internal CollectionMethodCreator(
2828
: methodCreator.CreateMethod(symbolInfo, collectionAttributeInfo.WithItems);
2929
}
3030

31-
internal BuilderMethod? CreateWithItemsParamsMethod(MethodCreator methodCreator)
31+
internal BuilderMethod CreateWithItemsParamsMethod(MethodCreator methodCreator)
3232
{
3333
Parameter parameter = new Parameter($"params {genericTypeArgument}[]", symbolInfo.NameInCamelCase);
3434
return methodCreator.CreateMethodWithComputedValue(

src/M31.FluentApi.Generator/CodeGeneration/CodeBoardActors/MethodCreation/FluentMethods.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ internal FluentMethods(MethodSymbolInfo symbolInfo, FluentMethodAttributeInfo me
1919
public BuilderMethods CreateBuilderMethods(MethodCreator methodCreator)
2020
{
2121
List<Parameter> parameters = SymbolInfo.ParameterInfos
22-
.Select(i => new Parameter(i.TypeForCodeGeneration, i.ParameterName)).ToList();
22+
.Select(i => new Parameter(i.TypeForCodeGeneration, i.ParameterName, i.DefaultValue)).ToList();
2323

2424
BuilderMethod builderMethod =
2525
methodCreator.BuilderMethodFactory.CreateBuilderMethod(

src/M31.FluentApi.Generator/CodeGeneration/CodeBoardElements/ParameterSymbolInfo.cs

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,22 +4,29 @@ namespace M31.FluentApi.Generator.CodeGeneration.CodeBoardElements;
44

55
internal class ParameterSymbolInfo
66
{
7-
internal ParameterSymbolInfo(string parameterName, string typeForCodeGeneration, bool isNullable)
7+
internal ParameterSymbolInfo(
8+
string parameterName,
9+
string typeForCodeGeneration,
10+
bool isNullable,
11+
string? defaultValue)
812
{
913
ParameterName = parameterName;
1014
TypeForCodeGeneration = typeForCodeGeneration;
1115
IsNullable = isNullable;
16+
DefaultValue = defaultValue;
1217
}
1318

1419
internal string ParameterName { get; }
1520
internal string TypeForCodeGeneration { get; }
1621
internal bool IsNullable { get; }
22+
internal string? DefaultValue { get; }
1723

1824
protected bool Equals(ParameterSymbolInfo other)
1925
{
2026
return ParameterName == other.ParameterName &&
2127
TypeForCodeGeneration == other.TypeForCodeGeneration &&
22-
IsNullable == other.IsNullable;
28+
IsNullable == other.IsNullable &&
29+
DefaultValue == other.DefaultValue;
2330
}
2431

2532
public override bool Equals(object? obj)
@@ -32,6 +39,6 @@ public override bool Equals(object? obj)
3239

3340
public override int GetHashCode()
3441
{
35-
return new HashCode().Add(ParameterName, TypeForCodeGeneration, IsNullable);
42+
return new HashCode().Add(ParameterName, TypeForCodeGeneration, IsNullable, DefaultValue);
3643
}
3744
}

src/M31.FluentApi.Generator/Commons/HashCode.cs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,19 @@ internal HashCode Add<T1, T2, T3>(T1 value1, T2 value2, T3 value3)
4242
return this;
4343
}
4444

45+
internal HashCode Add<T1, T2, T3, T4>(T1 value1, T2 value2, T3 value3, T4 value4)
46+
{
47+
unchecked
48+
{
49+
hash = hash * 23 + value1?.GetHashCode() ?? 0;
50+
hash = hash * 23 + value2?.GetHashCode() ?? 0;
51+
hash = hash * 23 + value3?.GetHashCode() ?? 0;
52+
hash = hash * 23 + value4?.GetHashCode() ?? 0;
53+
}
54+
55+
return this;
56+
}
57+
4558
internal HashCode AddSequence<T>(IEnumerable<T> items)
4659
{
4760
unchecked

src/M31.FluentApi.Generator/SourceGenerators/SymbolInfoCreator.cs

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,37 @@ private static ParameterSymbolInfo CreateParameterSymbolInfo(IParameterSymbol pa
8787
return new ParameterSymbolInfo(
8888
parameterSymbol.Name,
8989
CodeTypeExtractor.GetTypeForCodeGeneration(parameterSymbol.Type),
90-
parameterSymbol.NullableAnnotation == NullableAnnotation.Annotated);
90+
parameterSymbol.NullableAnnotation == NullableAnnotation.Annotated,
91+
GetDefaultValueAsCode(parameterSymbol));
92+
}
93+
94+
private static string? GetDefaultValueAsCode(IParameterSymbol parameterSymbol)
95+
{
96+
if (!parameterSymbol.HasExplicitDefaultValue)
97+
{
98+
return null;
99+
}
100+
101+
if (parameterSymbol.ExplicitDefaultValue == null && parameterSymbol.Type.IsReferenceType)
102+
{
103+
return "null";
104+
}
105+
106+
if (parameterSymbol.ExplicitDefaultValue == null && parameterSymbol.Type.IsValueType)
107+
{
108+
return "default";
109+
}
110+
111+
if (parameterSymbol.ExplicitDefaultValue == null &&
112+
parameterSymbol.Type is { IsReferenceType: false, IsValueType: false }) // unconstrained type parameter
113+
{
114+
return "default";
115+
}
116+
117+
return parameterSymbol.ExplicitDefaultValue! switch
118+
{
119+
string s => $"\"{s}\"",
120+
{ } o => o.ToString(),
121+
};
91122
}
92123
}

src/M31.FluentApi.Tests/AnalyzerAndCodeFixes/DiagnosticsDuringGenerationTests.cs

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,6 @@
55
using Microsoft.CodeAnalysis;
66
using Xunit;
77
using static M31.FluentApi.Tests.AnalyzerAndCodeFixes.Helpers.TestSourceCodeReader;
8-
using Verifier = M31.FluentApi.Tests.AnalyzerAndCodeFixes.Helpers.AnalyzerAndCodeFixVerifier<
9-
M31.FluentApi.Generator.SourceAnalyzers.FluentApiAnalyzer,
10-
M31.FluentApi.Generator.SourceAnalyzers.FluentApiCodeFixProvider>;
118

129
namespace M31.FluentApi.Tests.AnalyzerAndCodeFixes;
1310

src/M31.FluentApi.Tests/CodeGeneration/Helpers/TestDataProvider.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ internal class TestDataProvider : IEnumerable<object[]>
1616
new object[] { "Abstract", "DefaultFluentMethodNameClass", "Student" },
1717
new object[] { "Abstract", "FluentDefaultMemberClass", "Student" },
1818
new object[] { "Abstract", "FluentMethodClass", "Student" },
19+
new object[] { "Abstract", "FluentMethodDefaultValuesClass", "Student" },
1920
new object[] { "Abstract", "FluentNullableClass", "Student" },
2021
new object[] { "Abstract", "FluentNullableNoNullableAnnotationClass", "Student" },
2122
new object[] { "Abstract", "FluentNullableNoNullableAnnotationPrivateSetClass", "Student" },
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
// <auto-generated/>
2+
// This code was generated by the library M31.FluentAPI.
3+
// Changes to this file may cause incorrect behavior and will be lost if the code is regenerated.
4+
5+
#nullable enable
6+
7+
using System;
8+
using M31.FluentApi.Attributes;
9+
10+
namespace M31.FluentApi.Tests.CodeGeneration.TestClasses.Abstract.FluentMethodDefaultValuesClass;
11+
12+
public class CreateStudent : CreateStudent.IWithLastName, CreateStudent.IBornOn, CreateStudent.IEnrolledIn, CreateStudent.IInSemester, CreateStudent.IWithNumberOfPassedExams, CreateStudent.IWithNumberOfFailedExams
13+
{
14+
private readonly Student student;
15+
16+
private CreateStudent()
17+
{
18+
student = new Student();
19+
}
20+
21+
public static IWithLastName WithFirstName(string firstName = "Alice")
22+
{
23+
CreateStudent createStudent = new CreateStudent();
24+
createStudent.student.WithFirstName(firstName);
25+
return createStudent;
26+
}
27+
28+
public IBornOn WithLastName(string? lastName = null)
29+
{
30+
student.WithLastName(lastName);
31+
return this;
32+
}
33+
34+
public IEnrolledIn BornOn(System.DateOnly date = default)
35+
{
36+
student.BornOn(date);
37+
return this;
38+
}
39+
40+
public IInSemester EnrolledIn(System.DateOnly date = default)
41+
{
42+
student.EnrolledIn(date);
43+
return this;
44+
}
45+
46+
public IWithNumberOfPassedExams InSemester(int semester = 3)
47+
{
48+
student.InSemester(semester);
49+
return this;
50+
}
51+
52+
public IWithNumberOfFailedExams WithNumberOfPassedExams(int? numberOfPassedExams = default)
53+
{
54+
student.WithNumberOfPassedExams(numberOfPassedExams);
55+
return this;
56+
}
57+
58+
public Student WithNumberOfFailedExams(int? numberOfFailedExams = default)
59+
{
60+
student.WithNumberOfFailedExams(numberOfFailedExams);
61+
return student;
62+
}
63+
64+
public interface IWithLastName
65+
{
66+
IBornOn WithLastName(string? lastName = null);
67+
}
68+
69+
public interface IBornOn
70+
{
71+
IEnrolledIn BornOn(System.DateOnly date = default);
72+
}
73+
74+
public interface IEnrolledIn
75+
{
76+
IInSemester EnrolledIn(System.DateOnly date = default);
77+
}
78+
79+
public interface IInSemester
80+
{
81+
IWithNumberOfPassedExams InSemester(int semester = 3);
82+
}
83+
84+
public interface IWithNumberOfPassedExams
85+
{
86+
IWithNumberOfFailedExams WithNumberOfPassedExams(int? numberOfPassedExams = default);
87+
}
88+
89+
public interface IWithNumberOfFailedExams
90+
{
91+
Student WithNumberOfFailedExams(int? numberOfFailedExams = default);
92+
}
93+
}

0 commit comments

Comments
 (0)