Skip to content

Commit 2c660a1

Browse files
NikolayPianikovNikolayPianikov
authored andcommitted
Support record with ctor
1 parent 288f21e commit 2c660a1

File tree

6 files changed

+223
-16
lines changed

6 files changed

+223
-16
lines changed

Immutype.Tests/Integration/Tests.cs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
namespace Immutype.Tests.Integration
66
{
7+
using System.Security.Cryptography.X509Certificates;
78
using Microsoft.CodeAnalysis;
89

910
public class Tests
@@ -47,6 +48,34 @@ public record Rec(int val);
4748
// Then
4849
output.ShouldBe(new [] { "Rec { val = 99 }" }, generatedCode);
4950
}
51+
52+
[Fact]
53+
public void ShouldCreateRecordWithCtor()
54+
{
55+
// Given
56+
const string statements = "System.Console.WriteLine(new Rec(33).WithVal(99).Val);";
57+
58+
// When
59+
var output = @"
60+
namespace Sample
61+
{
62+
using System;
63+
64+
[Immutype.TargetAttribute()]
65+
public record Rec
66+
{
67+
public Rec(int val)
68+
{
69+
Val = val;
70+
}
71+
72+
public int Val { get; }
73+
}
74+
}".Run(out var generatedCode, new RunOptions { Statements = statements });
75+
76+
// Then
77+
output.ShouldBe(new [] { "99" }, generatedCode);
78+
}
5079

5180
[Fact]
5281
public void ShouldCreateRecordWithTwoValues()

Immutype.UsageScenarios.Tests/README_TEMPLATE.md

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
- [Removing](#removing)
1010
- [Nullable collection](#nullable-collection)
1111
- [Set](#set)
12+
- [Record with constructor](#record-with-constructor)
1213

1314
### Sample scenario
1415

@@ -228,3 +229,55 @@ public class Set
228229

229230

230231

232+
### Record with constructor
233+
234+
235+
236+
``` CSharp
237+
[Immutype.Target]
238+
internal record Person
239+
{
240+
public Person(
241+
string name,
242+
int? age = default,
243+
ICollection<Person>? friends = default)
244+
{
245+
Name = name;
246+
Age = age;
247+
Friends = friends;
248+
}
249+
250+
public string Name { get; }
251+
252+
public int? Age { get; }
253+
254+
public ICollection<Person>? Friends { get; }
255+
256+
public void Deconstruct(
257+
out string name,
258+
out int? age,
259+
out ICollection<Person>? friends)
260+
{
261+
name = Name;
262+
age = Age;
263+
friends = Friends;
264+
}
265+
}
266+
267+
public class RecordWithConstructor
268+
{
269+
public void Run()
270+
{
271+
var john = new Person("John",15)
272+
.WithFriends(
273+
new Person("David").WithAge(16),
274+
new Person("James").WithAge(17)
275+
.WithFriends(new Person("Tyler").WithAge(16)));
276+
277+
john.Friends?.Count.ShouldBe(2);
278+
}
279+
}
280+
```
281+
282+
283+
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
// ReSharper disable ClassNeverInstantiated.Global
2+
// ReSharper disable MemberCanBePrivate.Global
3+
// ReSharper disable UnusedMember.Global
4+
// ReSharper disable RedundantNameQualifier
5+
// ReSharper disable CheckNamespace
6+
// ReSharper disable ConvertToPrimaryConstructor
7+
namespace Immutype.UsageScenarios.Tests.RecordWithConstructor
8+
{
9+
using System.Collections.Generic;
10+
using Shouldly;
11+
using Xunit;
12+
13+
// $visible=true
14+
// $tag=1 Basics
15+
// $priority=05
16+
// $description=Record with constructor
17+
// {
18+
[Immutype.Target]
19+
internal record Person
20+
{
21+
public Person(
22+
string name,
23+
int? age = default,
24+
ICollection<Person>? friends = default)
25+
{
26+
Name = name;
27+
Age = age;
28+
Friends = friends;
29+
}
30+
31+
public string Name { get; }
32+
33+
public int? Age { get; }
34+
35+
public ICollection<Person>? Friends { get; }
36+
37+
public void Deconstruct(
38+
out string name,
39+
out int? age,
40+
out ICollection<Person>? friends)
41+
{
42+
name = Name;
43+
age = Age;
44+
friends = Friends;
45+
}
46+
}
47+
48+
public class RecordWithConstructor
49+
{
50+
// }
51+
[Fact]
52+
// {
53+
public void Run()
54+
{
55+
var john = new Person("John",15)
56+
.WithFriends(
57+
new Person("David").WithAge(16),
58+
new Person("James").WithAge(17)
59+
.WithFriends(new Person("Tyler").WithAge(16)));
60+
61+
john.Friends?.Count.ShouldBe(2);
62+
}
63+
}
64+
// }
65+
}

Immutype/Core/SourceBuilder.cs

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -36,20 +36,22 @@ from source in Build(typeDeclarationSyntax, cancellationToken)
3636

3737
public IEnumerable<Source> Build(TypeDeclarationSyntax typeDeclarationSyntax, CancellationToken cancellationToken)
3838
{
39-
IReadOnlyList<ParameterSyntax>? parameters = typeDeclarationSyntax switch
39+
IReadOnlyList<ParameterSyntax>? parameters = default;
40+
if (typeDeclarationSyntax is RecordDeclarationSyntax recordDeclarationSyntax && recordDeclarationSyntax.ParameterList?.Parameters.Count != 0)
4041
{
41-
RecordDeclarationSyntax recordDeclarationSyntax => recordDeclarationSyntax.ParameterList?.Parameters,
42-
_ => (
43-
from ctor in typeDeclarationSyntax.Members.OfType<ConstructorDeclarationSyntax>()
44-
where !cancellationToken.IsCancellationRequested
45-
where ctor.Modifiers.Any(i => i.IsKind(SyntaxKind.PublicKeyword) || i.IsKind(SyntaxKind.InternalKeyword)) || !ctor.Modifiers.Any()
46-
orderby ctor.ParameterList.Parameters.Count descending
47-
select ctor)
48-
.FirstOrDefault()
49-
?.ParameterList
50-
.Parameters
51-
};
42+
parameters = recordDeclarationSyntax.ParameterList?.Parameters;
43+
}
5244

45+
parameters ??= (
46+
from ctor in typeDeclarationSyntax.Members.OfType<ConstructorDeclarationSyntax>()
47+
where !cancellationToken.IsCancellationRequested
48+
where ctor.ParameterList.Parameters.Count > 0 && ctor.Modifiers.Any(i => i.IsKind(SyntaxKind.PublicKeyword) || i.IsKind(SyntaxKind.InternalKeyword)) || !ctor.Modifiers.Any()
49+
orderby ctor.ParameterList.Parameters.Count descending
50+
select ctor)
51+
.FirstOrDefault()
52+
?.ParameterList
53+
.Parameters;
54+
5355
return parameters != default
5456
? _unitFactory.Create(typeDeclarationSyntax, parameters, cancellationToken)
5557
: Enumerable.Empty<Source>();

Immutype/Core/TypeSyntaxFilter.cs

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
namespace Immutype.Core
33
{
44
using System.Linq;
5+
using Microsoft.CodeAnalysis;
6+
using Microsoft.CodeAnalysis.CSharp;
57
using Microsoft.CodeAnalysis.CSharp.Syntax;
68

79
internal class TypeSyntaxFilter : ITypeSyntaxFilter
@@ -33,11 +35,14 @@ public bool IsAccepted(TypeDeclarationSyntax typeDeclarationSyntax)
3335
return false;
3436
}
3537

36-
return typeDeclarationSyntax switch
38+
if (typeDeclarationSyntax is RecordDeclarationSyntax { ParameterList.Parameters.Count: > 0 })
3739
{
38-
RecordDeclarationSyntax recordDeclarationSyntax => recordDeclarationSyntax.ParameterList is { Parameters.Count: > 0 },
39-
_ => typeDeclarationSyntax.Members.OfType<ConstructorDeclarationSyntax>().Any(i => i.ParameterList.Parameters.Count > 0)
40-
};
40+
return true;
41+
}
42+
43+
return typeDeclarationSyntax.Members
44+
.OfType<ConstructorDeclarationSyntax>()
45+
.Any(ctor => ctor.ParameterList.Parameters.Count > 0 && ctor.Modifiers.Any(i => i.IsKind(SyntaxKind.PublicKeyword) || i.IsKind(SyntaxKind.InternalKeyword)) || !ctor.Modifiers.Any());
4146
}
4247
}
4348
}

README.md

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ _Immutype_ supports [IIncrementalGenerator](https://docs.microsoft.com/en-us/dot
9595
- [Removing](#removing)
9696
- [Nullable collection](#nullable-collection)
9797
- [Set](#set)
98+
- [Record with constructor](#record-with-constructor)
9899

99100
### Sample scenario
100101

@@ -314,3 +315,55 @@ public class Set
314315

315316

316317

318+
### Record with constructor
319+
320+
321+
322+
``` CSharp
323+
[Immutype.Target]
324+
internal record Person
325+
{
326+
public Person(
327+
string name,
328+
int? age = default,
329+
ICollection<Person>? friends = default)
330+
{
331+
Name = name;
332+
Age = age;
333+
Friends = friends;
334+
}
335+
336+
public string Name { get; }
337+
338+
public int? Age { get; }
339+
340+
public ICollection<Person>? Friends { get; }
341+
342+
public void Deconstruct(
343+
out string name,
344+
out int? age,
345+
out ICollection<Person>? friends)
346+
{
347+
name = Name;
348+
age = Age;
349+
friends = Friends;
350+
}
351+
}
352+
353+
public class RecordWithConstructor
354+
{
355+
public void Run()
356+
{
357+
var john = new Person("John",15)
358+
.WithFriends(
359+
new Person("David").WithAge(16),
360+
new Person("James").WithAge(17)
361+
.WithFriends(new Person("Tyler").WithAge(16)));
362+
363+
john.Friends?.Count.ShouldBe(2);
364+
}
365+
}
366+
```
367+
368+
369+

0 commit comments

Comments
 (0)