Skip to content

Commit b5008d7

Browse files
committed
Fixed Generator.
Fixed small bugs.
1 parent 6b9f870 commit b5008d7

File tree

6 files changed

+223
-150
lines changed

6 files changed

+223
-150
lines changed

Source/Expr.cs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1538,7 +1538,12 @@ static bool Modify(Func<Expression,bool> ff, MemberBinding b)
15381538
where T : Expression
15391539
{
15401540
foreach (var item in source)
1541-
return FindInternal(item, func);
1541+
{
1542+
var found = FindInternal(item, func);
1543+
if (found != null)
1544+
return found;
1545+
}
1546+
15421547
return null;
15431548
}
15441549

Source/Linq.Expressions.Deconstruct.csproj

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
<Description>Pattern matching support for System.Linq.Expressions.</Description>
1717
<PackageLicenseFile>LICENSE.TXT</PackageLicenseFile>
1818
<PackageReadmeFile>README.md</PackageReadmeFile>
19-
<Version>1.0.8.1</Version>
19+
<Version>1.0.9</Version>
2020

2121
<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
2222
<CompilerGeneratedFilesOutputPath>$(BaseIntermediateOutputPath)\GeneratedFiles</CompilerGeneratedFilesOutputPath>
@@ -26,11 +26,11 @@
2626
<Service Include="{508349b6-6b84-4df5-91f0-309beebad82d}" />
2727

2828
<None Include="..\LICENSE.TXT" Pack="true" PackagePath="\" />
29-
<None Include="..\README.md" Pack="true" PackagePath="\" />
29+
<None Include="..\README.md" Pack="true" PackagePath="\" />
3030

3131
<ProjectReference Include="..\SourceGenerators\SourceGenerators.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
3232

33-
<PackageReference Include="PolySharp" Version="1.14.1">
33+
<PackageReference Include="PolySharp" Version="1.15.0">
3434
<PrivateAssets>all</PrivateAssets>
3535
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
3636
</PackageReference>

SourceGenerators/SourceGenerators.csproj

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@
77
</PropertyGroup>
88

99
<ItemGroup>
10-
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.9.2" PrivateAssets="all" />
11-
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4" PrivateAssets="all" />
12-
<PackageReference Include="PolySharp" Version="1.14.1">
10+
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.13.0" PrivateAssets="all" />
11+
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.11.0" PrivateAssets="all" />
12+
<PackageReference Include="PolySharp" Version="1.15.0">
1313
<PrivateAssets>all</PrivateAssets>
1414
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
1515
</PackageReference>

SourceGenerators/ToExprGenerator.cs

Lines changed: 74 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -5,62 +5,90 @@
55
using System.Text;
66

77
using Microsoft.CodeAnalysis;
8-
using Microsoft.CodeAnalysis.CSharp;
98
using Microsoft.CodeAnalysis.CSharp.Syntax;
9+
using Microsoft.CodeAnalysis.Text;
10+
1011

1112
namespace SourceGenerators
1213
{
1314
[Generator]
14-
public class ToExprGenerator : ISourceGenerator
15+
public class ToExprGenerator : IIncrementalGenerator
1516
{
16-
public void Initialize(GeneratorInitializationContext context)
17+
public void Initialize(IncrementalGeneratorInitializationContext context)
1718
{
18-
#if DEBUG1
19+
#if DEBUG1
1920
if (!Debugger.IsAttached)
2021
{
2122
Debugger.Launch();
2223
}
23-
#endif
24+
#endif
2425

25-
context.RegisterForPostInitialization(i => i.AddSource("ToExprAttribute.g.cs",
26-
"""
27-
// <auto-generated/>
28-
#pragma warning disable
29-
#nullable enable annotations
26+
// Generate the attribute definition
27+
context.RegisterPostInitializationOutput(ctx =>
28+
{
29+
ctx.AddSource("ToExprAttribute.g.cs",
30+
"""
31+
// <auto-generated/>
32+
#pragma warning disable
33+
#nullable enable annotations
3034
31-
using System;
35+
using System;
3236
33-
namespace Linq.Expressions.Deconstruct
34-
{
35-
[AttributeUsage(AttributeTargets.Field, Inherited = false, AllowMultiple = false)]
36-
[System.Diagnostics.Conditional("ToExprGenerator_DEBUG")]
37-
sealed class ToExprAttribute : Attribute
37+
namespace Linq.Expressions.Deconstruct
3838
{
39-
public string PropertyName { get; set; }
40-
public bool IsNullable { get; set; }
39+
[AttributeUsage(AttributeTargets.Field, Inherited = false, AllowMultiple = false)]
40+
[System.Diagnostics.Conditional("ToExprGenerator_DEBUG")]
41+
sealed class ToExprAttribute : Attribute
42+
{
43+
public string PropertyName { get; set; }
44+
public bool IsNullable { get; set; }
45+
}
4146
}
42-
}
43-
44-
"""));
47+
""");
48+
});
49+
50+
// Collect all fields with the target attribute
51+
//
52+
var fieldsWithAttribute = context.SyntaxProvider
53+
.CreateSyntaxProvider(
54+
static (node, _) => node is FieldDeclarationSyntax { AttributeLists.Count: > 0 },
55+
static (ctx, _) => GetFieldSymbolWithAttribute(ctx))
56+
.Where(static field => field is not null)
57+
.Select(static (field, _) => field!)
58+
.Collect();
59+
60+
// Main code generation step
61+
//
62+
context.RegisterSourceOutput(fieldsWithAttribute, (spc, fields) =>
63+
{
64+
var grouped = fields.GroupBy(f => f.ContainingType, SymbolEqualityComparer.Default);
4565

46-
context.RegisterForSyntaxNotifications(() => new SyntaxReceiver());
66+
foreach (var group in grouped)
67+
{
68+
var classSource = ProcessClass((INamedTypeSymbol)group.Key, group.ToList(), spc);
69+
spc.AddSource($"{group.Key.Name}.ToExpr.g.cs", SourceText.From(classSource, Encoding.UTF8));
70+
}
71+
});
4772
}
4873

49-
public void Execute(GeneratorExecutionContext context)
74+
static IFieldSymbol GetFieldSymbolWithAttribute(GeneratorSyntaxContext context)
5075
{
51-
if (context.SyntaxContextReceiver is not SyntaxReceiver receiver)
52-
return;
76+
if (context.Node is not FieldDeclarationSyntax fieldDeclaration)
77+
return null;
5378

54-
var attributeSymbol = context.Compilation.GetTypeByMetadataName("Linq.Expressions.Deconstruct.ToExprAttribute");
55-
56-
foreach (var group in receiver.Fields.GroupBy<IFieldSymbol,INamedTypeSymbol>(f => f.ContainingType, SymbolEqualityComparer.Default))
79+
foreach (var variable in fieldDeclaration.Declaration.Variables)
5780
{
58-
var classSource = ProcessClass(group.Key, group.ToList(), attributeSymbol, context);
59-
context.AddSource($"{group.Key.Name}.ToExpr.g.cs", classSource);
81+
if (context.SemanticModel.GetDeclaredSymbol(variable) is not IFieldSymbol symbol)
82+
continue;
83+
84+
if (symbol.GetAttributes().Any(attr => attr.AttributeClass?.ToDisplayString() == "Linq.Expressions.Deconstruct.ToExprAttribute"))
85+
return symbol;
6086
}
87+
88+
return null;
6189
}
6290

63-
string ProcessClass(INamedTypeSymbol classSymbol, List<IFieldSymbol> fields, ISymbol attributeSymbol, GeneratorExecutionContext context)
91+
static string ProcessClass(INamedTypeSymbol classSymbol, List<IFieldSymbol> fields, SourceProductionContext context)
6492
{
6593
// if (!classSymbol.ContainingSymbol.Equals(classSymbol.ContainingNamespace, SymbolEqualityComparer.Default))
6694
// {
@@ -77,8 +105,6 @@ string ProcessClass(INamedTypeSymbol classSymbol, List<IFieldSymbol> fields, ISy
77105
78106
using System;
79107
80-
#nullable enable
81-
82108
namespace {{namespaceName}}
83109
{
84110
partial class Expr
@@ -88,6 +114,8 @@ partial class {{classSymbol.Name}}
88114
89115
""");
90116

117+
var attributeSymbol = fields[0].GetAttributes().First(a => a.AttributeClass?.Name == "ToExprAttribute").AttributeClass!;
118+
91119
foreach (var fieldSymbol in fields)
92120
ProcessField(source, classSymbol, fieldSymbol, attributeSymbol);
93121

@@ -100,18 +128,18 @@ partial class {{classSymbol.Name}}
100128
return source.ToString();
101129
}
102130

103-
void ProcessField(StringBuilder source, INamedTypeSymbol classSymbol, IFieldSymbol fieldSymbol, ISymbol attributeSymbol)
131+
static void ProcessField(StringBuilder source, INamedTypeSymbol classSymbol, IFieldSymbol fieldSymbol, INamedTypeSymbol attributeSymbol)
104132
{
105133
var fieldName = fieldSymbol.Name;
106134
var fieldType = fieldSymbol.Type.ToDisplayString();
107135

108-
var attributeData = fieldSymbol.GetAttributes().Single(ad => ad.AttributeClass!.Equals(attributeSymbol, SymbolEqualityComparer.Default));
109-
var overridenName = attributeData.NamedArguments.SingleOrDefault(kvp => kvp.Key == "PropertyName").Value;
110-
var isNullable = attributeData.NamedArguments.SingleOrDefault(kvp => kvp.Key == "IsNullable"). Value;
136+
var attributeData = fieldSymbol.GetAttributes().Single(ad => SymbolEqualityComparer.Default.Equals(ad.AttributeClass, attributeSymbol));
137+
var overriddenName = attributeData.NamedArguments.FirstOrDefault(kvp => kvp.Key == "PropertyName").Value;
138+
var isNullable = attributeData.NamedArguments.FirstOrDefault(kvp => kvp.Key == "IsNullable").Value;
111139

112140
string propertyName;
113141

114-
if (overridenName.IsNull)
142+
if (overriddenName.IsNull)
115143
{
116144
propertyName = fieldName.TrimStart('_');
117145

@@ -120,26 +148,29 @@ void ProcessField(StringBuilder source, INamedTypeSymbol classSymbol, IFieldSymb
120148
}
121149
else
122150
{
123-
propertyName = overridenName.Value!.ToString();
151+
propertyName = overriddenName.Value?.ToString() ?? "";
124152
}
125153

154+
// Skip if name is invalid or clashes with other members
155+
//
126156
if (propertyName.Length == 0 || propertyName == fieldName || classSymbol.MemberNames.Contains(propertyName))
127157
{
128158
if (fieldSymbol.Locations[0] is { Kind: LocationKind.SourceFile } location)
129159
{
130-
var ls = location.GetMappedLineSpan();
131-
source
132-
.AppendLine($"#line ({ls.Span.Start.Line + 1},{ls.Span.Start.Character})-({ls.Span.End.Line + 1},{ls.Span.End.Character}) \"{ls.Path}\"")
133-
;
160+
var ls = location.GetLineSpan();
161+
source.AppendLine($"#line ({ls.StartLinePosition.Line + 1},{ls.StartLinePosition.Character})-({ls.EndLinePosition.Line + 1},{ls.EndLinePosition.Character}) \"{ls.Path}\"");
134162
}
135163

136164
source
137165
.AppendLine($"#error Generator failed on '{fieldName}'.")
138166
.AppendLine("#line default")
139167
;
168+
140169
return;
141170
}
142171

172+
// If IsNullable is false or unset, trim the trailing '?'
173+
//
143174
if (isNullable.IsNull || isNullable.Value is false)
144175
fieldType = fieldType.TrimEnd('?');
145176

@@ -149,25 +180,5 @@ void ProcessField(StringBuilder source, INamedTypeSymbol classSymbol, IFieldSymb
149180
150181
""");
151182
}
152-
153-
public class SyntaxReceiver : ISyntaxContextReceiver
154-
{
155-
public List<IFieldSymbol> Fields { get; } = new ();
156-
157-
public void OnVisitSyntaxNode(GeneratorSyntaxContext context)
158-
{
159-
if (context.Node is FieldDeclarationSyntax { AttributeLists: [_, ..] } field)
160-
{
161-
foreach (var variable in field.Declaration.Variables)
162-
{
163-
var fieldSymbol = context.SemanticModel.GetDeclaredSymbol(variable) as IFieldSymbol;
164-
var attributes = fieldSymbol!.GetAttributes();
165-
166-
if (attributes.Any(ad => ad.AttributeClass!.ToDisplayString() == "Linq.Expressions.Deconstruct.ToExprAttribute"))
167-
Fields.Add(fieldSymbol);
168-
}
169-
}
170-
}
171-
}
172183
}
173184
}

Tests/Linq.Expressions.Deconstruct.Tests.csproj

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,11 @@
77
</PropertyGroup>
88

99
<ItemGroup>
10-
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
11-
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.9.0" />
12-
<PackageReference Include="NUnit3TestAdapter" Version="4.5.0" />
13-
<PackageReference Include="NUnit" Version="4.1.0" />
14-
<PackageReference Include="PolySharp" Version="1.14.1">
10+
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
11+
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.13.0" />
12+
<PackageReference Include="NUnit3TestAdapter" Version="5.0.0" />
13+
<PackageReference Include="NUnit" Version="4.3.2" />
14+
<PackageReference Include="PolySharp" Version="1.15.0">
1515
<PrivateAssets>all</PrivateAssets>
1616
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
1717
</PackageReference>

0 commit comments

Comments
 (0)