Skip to content

Commit b8c48f8

Browse files
committed
Don't know why this doesn't work :(
1 parent f483741 commit b8c48f8

File tree

7 files changed

+632
-0
lines changed

7 files changed

+632
-0
lines changed

.nuke/build.schema.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,12 @@
1818
"type": "boolean",
1919
"description": "Indicates to continue a previously failed build attempt"
2020
},
21+
"Filter": {
22+
"type": "string"
23+
},
24+
"Framework": {
25+
"type": "string"
26+
},
2127
"GithubToken": {
2228
"type": "string"
2329
},

build/Build.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@ class Build : NukeBuild
4848
[Parameter] readonly string GithubToken;
4949
[Parameter] readonly string NuGetToken;
5050
[Parameter] readonly AbsolutePath PackagesDirectory = RootDirectory / "packages";
51+
[Parameter] readonly string Filter;
52+
[Parameter] readonly string Framework;
5153

5254
const string NugetOrgUrl = "https://api.nuget.org/v3/index.json";
5355
bool IsTag => GitHubActions.Instance?.GitHubRef?.StartsWith("refs/tags/") ?? false;
@@ -88,6 +90,8 @@ class Build : NukeBuild
8890
DotNetTest(s => s
8991
.SetProjectFile(Solution)
9092
.SetConfiguration(Configuration)
93+
.When(!string.IsNullOrEmpty(Filter), x => x.SetFilter(Filter))
94+
.When(!string.IsNullOrEmpty(Framework), x => x.SetFramework(Framework))
9195
.EnableNoBuild()
9296
.EnableNoRestore());
9397
});

src/StronglyTypedIds/Parser.cs

Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ internal static class Parser
1212
{
1313
public const string StronglyTypedIdAttribute = "StronglyTypedIds.StronglyTypedIdAttribute";
1414
public const string StronglyTypedIdDefaultsAttribute = "StronglyTypedIds.StronglyTypedIdDefaultsAttribute";
15+
public const string StronglyTypedIdConvertersAttribute = "StronglyTypedIds.StronglyTypedIdConvertersAttribute";
16+
public const string StronglyTypedIdConvertersDefaultsAttribute = "StronglyTypedIds.StronglyTypedIdConvertersDefaultsAttribute";
1517

1618
public static Result<(StructToGenerate info, bool valid)> GetIdSemanticTarget(GeneratorAttributeSyntaxContext ctx, CancellationToken ct)
1719
{
@@ -151,6 +153,147 @@ internal static class Parser
151153
return new Result<(Defaults, bool)>((defaults, true), errors);
152154
}
153155

156+
public static Result<(ConverterToGenerate info, bool valid)> GetConvertersSemanticTarget(GeneratorAttributeSyntaxContext ctx, CancellationToken ct)
157+
{
158+
var structSymbol = ctx.TargetSymbol as INamedTypeSymbol;
159+
if (structSymbol is null)
160+
{
161+
return Result<ConverterToGenerate>.Fail();
162+
}
163+
164+
var structSyntax = (StructDeclarationSyntax)ctx.TargetNode;
165+
166+
var hasMisconfiguredInput = false;
167+
List<DiagnosticInfo>? diagnostics = null;
168+
string[]? templateNames = null;
169+
LocationInfo? attributeLocation = null;
170+
string? idName = null;
171+
172+
foreach (AttributeData attribute in structSymbol.GetAttributes())
173+
{
174+
if (!((attribute.AttributeClass?.Name == "StronglyTypedIdAttribute" ||
175+
attribute.AttributeClass?.Name == "StronglyTypedId") &&
176+
attribute.AttributeClass.ToDisplayString() == StronglyTypedIdAttribute))
177+
{
178+
// wrong attribute
179+
continue;
180+
}
181+
182+
// Can never have template
183+
(var result, (_, templateNames)) = GetConstructorValues(attribute);
184+
hasMisconfiguredInput |= result;
185+
186+
if (attribute.ApplicationSyntaxReference?.GetSyntax() is { } s)
187+
{
188+
attributeLocation = LocationInfo.CreateFrom(s);
189+
}
190+
191+
var typeParameter = attribute.AttributeClass.TypeArguments[0];
192+
idName = typeParameter.ToString();
193+
}
194+
195+
var hasPartialModifier = false;
196+
foreach (var modifier in structSyntax.Modifiers)
197+
{
198+
if (modifier.IsKind(SyntaxKind.PartialKeyword))
199+
{
200+
hasPartialModifier = true;
201+
break;
202+
}
203+
}
204+
205+
if (!hasPartialModifier)
206+
{
207+
diagnostics ??= new();
208+
diagnostics.Add(NotPartialDiagnostic.CreateInfo(structSyntax));
209+
}
210+
211+
var errors = diagnostics is null
212+
? EquatableArray<DiagnosticInfo>.Empty
213+
: new EquatableArray<DiagnosticInfo>(diagnostics.ToArray());
214+
215+
if (hasMisconfiguredInput || idName is null)
216+
{
217+
return new Result<(ConverterToGenerate, bool)>((default, false), errors);
218+
}
219+
220+
string nameSpace = GetNameSpace(structSyntax);
221+
ParentClass? parentClass = GetParentClasses(structSyntax);
222+
var name = structSymbol.Name;
223+
224+
var toGenerate = new ConverterToGenerate(
225+
name: name,
226+
nameSpace: nameSpace,
227+
idName: idName,
228+
templateNames: templateNames,
229+
templateLocation: attributeLocation!,
230+
parent: parentClass);
231+
232+
return new Result<(ConverterToGenerate, bool)>((toGenerate, true), errors);
233+
}
234+
235+
public static Result<(Defaults defaults, bool valid)> GetConverterDefaults(
236+
GeneratorAttributeSyntaxContext ctx, CancellationToken ct)
237+
{
238+
var assemblyAttributes = ctx.TargetSymbol.GetAttributes();
239+
if (assemblyAttributes.IsDefaultOrEmpty)
240+
{
241+
return Result<Defaults>.Fail();
242+
}
243+
244+
// We only return the first config that we find
245+
string[]? templateNames = null;
246+
LocationInfo? attributeLocation = null;
247+
List<DiagnosticInfo>? diagnostics = null;
248+
bool hasMisconfiguredInput = false;
249+
bool hasMultiple = false;
250+
251+
// if we have multiple attributes we still check them, so that we can add extra diagnostics if necessary
252+
// the "first" one found won't be flagged as a duplicate though.
253+
foreach (AttributeData attribute in assemblyAttributes)
254+
{
255+
if (!((attribute.AttributeClass?.Name == "StronglyTypedIdConvertersDefaultsAttribute" ||
256+
attribute.AttributeClass?.Name == "StronglyTypedIdConvertersDefaults") &&
257+
attribute.AttributeClass.ToDisplayString() == StronglyTypedIdConvertersDefaultsAttribute))
258+
{
259+
// wrong attribute
260+
continue;
261+
}
262+
263+
var syntax = attribute.ApplicationSyntaxReference?.GetSyntax();
264+
if (templateNames is not null || hasMisconfiguredInput)
265+
{
266+
hasMultiple = true;
267+
if (syntax is not null)
268+
{
269+
diagnostics ??= new();
270+
diagnostics.Add(MultipleAssemblyAttributeDiagnostic.CreateInfo(syntax));
271+
}
272+
}
273+
274+
(var result, (_, templateNames)) = GetConstructorValues(attribute);
275+
hasMisconfiguredInput |= result;
276+
277+
if (syntax is not null)
278+
{
279+
attributeLocation = LocationInfo.CreateFrom(syntax);
280+
}
281+
}
282+
283+
var errors = diagnostics is null
284+
? EquatableArray<DiagnosticInfo>.Empty
285+
: new EquatableArray<DiagnosticInfo>(diagnostics.ToArray());
286+
287+
if (hasMisconfiguredInput)
288+
{
289+
return new Result<(Defaults, bool)>((default, false), errors);
290+
}
291+
292+
var defaults = new Defaults(template: null, templateNames, attributeLocation!, hasMultiple);
293+
294+
return new Result<(Defaults, bool)>((defaults, true), errors);
295+
}
296+
154297
private static (bool HasMisconfiguredInput, (Template? Template, string[]? TemplateNames)) GetConstructorValues(AttributeData attribute)
155298
{
156299
(Template? Template, string? Name, string[]? Names)? results1 = null;

src/StronglyTypedIds/StronglyTypedIdGenerator.cs

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,14 +78,56 @@ public void Initialize(IncrementalGeneratorInitializationContext context)
7878
idsAndDiagnostics.SelectMany((x, _) => x.Errors),
7979
static (context, info) => context.ReportDiagnostic(info));
8080

81+
// Converter defaults
82+
IncrementalValuesProvider<Result<(Defaults defaults, bool valid)>> converterDefaultsAndDiagnostics = context.SyntaxProvider
83+
.ForAttributeWithMetadataName(
84+
Parser.StronglyTypedIdConvertersDefaultsAttribute,
85+
predicate: (node, _) => node is CompilationUnitSyntax,
86+
transform: Parser.GetConverterDefaults)
87+
.Where(static m => m is not null);
88+
89+
IncrementalValueProvider<(EquatableArray<(string Name, string Content)> Content, bool isValid, DiagnosticInfo? Diagnostic)> converterDefaultTemplateContent = converterDefaultsAndDiagnostics
90+
.Where(static x => x.Value.valid)
91+
.Select((result, _) => result.Value.defaults)
92+
.Collect()
93+
.Combine(templates)
94+
.Select(ProcessDefaults);
95+
96+
context.RegisterSourceOutput(
97+
converterDefaultsAndDiagnostics.SelectMany((x, _) => x.Errors),
98+
static (context, info) => context.ReportDiagnostic(info));
99+
100+
// Converters
101+
IncrementalValuesProvider<Result<(ConverterToGenerate info, bool valid)>> convertersAndDiagnostics = context.SyntaxProvider
102+
.ForAttributeWithMetadataName(
103+
Parser.StronglyTypedIdConvertersAttribute,
104+
predicate: (node, _) => node is StructDeclarationSyntax,
105+
transform: Parser.GetConvertersSemanticTarget)
106+
.Where(static m => m is not null);
107+
108+
IncrementalValuesProvider<ConverterToGenerate> converters = convertersAndDiagnostics
109+
.Where(static x => x.Value.valid)
110+
.Select((result, _) => result.Value.info);
111+
112+
context.RegisterSourceOutput(
113+
convertersAndDiagnostics.SelectMany((x, _) => x.Errors),
114+
static (context, info) => context.ReportDiagnostic(info));
115+
81116
// Combined
82117
var idsWithDefaultsAndTemplates = ids
83118
.Combine(templates)
84119
.Combine(idDefaultTemplateContent);
85120

121+
var convertersWithDefaultsAndTemplates = converters
122+
.Combine(templates)
123+
.Combine(converterDefaultTemplateContent);
124+
86125
// Output
87126
context.RegisterSourceOutput(idsWithDefaultsAndTemplates,
88127
static (spc, source) => GenerateIds(source.Left.Left, source.Left.Right, source.Right, spc));
128+
129+
context.RegisterSourceOutput(convertersWithDefaultsAndTemplates,
130+
static (spc, source) => GenerateConverters(source.Left.Left, source.Left.Right, source.Right, spc));
89131
}
90132

91133
private static void GenerateIds(
@@ -132,6 +174,51 @@ private static void GenerateIds(
132174
}
133175
}
134176

177+
private static void GenerateConverters(
178+
ConverterToGenerate converterToGenerate,
179+
ImmutableArray<(string Path, string Name, string? Content)> templates,
180+
(EquatableArray<(string Name, string Content)>, bool IsValid, DiagnosticInfo? Diagnostic) defaults,
181+
SourceProductionContext context)
182+
{
183+
if (defaults.Diagnostic is { } diagnostic)
184+
{
185+
// report error with the default template
186+
context.ReportDiagnostic(diagnostic);
187+
}
188+
189+
if (!TryGetTemplateContent(selectedTemplate: null, converterToGenerate.TemplateNames, converterToGenerate.TemplateLocation, templates, defaults, in context, out var templateContents))
190+
{
191+
return;
192+
}
193+
194+
var addGeneratedCodeAttribute = true;
195+
196+
var sb = new StringBuilder();
197+
foreach (var (name, content) in templateContents.Distinct())
198+
{
199+
var result = SourceGenerationHelper.CreateId(
200+
converterToGenerate.NameSpace,
201+
converterToGenerate.Name,
202+
converterToGenerate.IdName,
203+
converterToGenerate.Parent,
204+
content,
205+
addDefaultAttributes: string.IsNullOrEmpty(name),
206+
addGeneratedCodeAttribute: addGeneratedCodeAttribute,
207+
sb);
208+
209+
addGeneratedCodeAttribute = false; // We can only add it once, so just add to the first rendering
210+
211+
var fileName = SourceGenerationHelper.CreateSourceName(
212+
sb,
213+
converterToGenerate.NameSpace,
214+
converterToGenerate.Parent,
215+
converterToGenerate.Name,
216+
name);
217+
218+
context.AddSource(fileName, SourceText.From(result, Encoding.UTF8));
219+
}
220+
}
221+
135222

136223
private static (EquatableArray<(string Name, string Content)>, bool, DiagnosticInfo?) ProcessDefaults((ImmutableArray<Defaults> Left, ImmutableArray<(string Path, string Name, string? Content)> Right) all, CancellationToken _)
137224
{

src/StronglyTypedIds/StructToGenerate.cs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,26 @@ public StructToGenerate(string name, string nameSpace, Template? template, strin
2424
public LocationInfo? TemplateLocation { get; }
2525
}
2626

27+
internal readonly record struct ConverterToGenerate
28+
{
29+
public ConverterToGenerate(string name, string nameSpace, string idName, string[]? templateNames, ParentClass? parent, LocationInfo templateLocation)
30+
{
31+
Name = name;
32+
NameSpace = nameSpace;
33+
IdName = idName;
34+
TemplateNames = templateNames is null ? EquatableArray<string>.Empty : new EquatableArray<string>(templateNames);
35+
Parent = parent;
36+
TemplateLocation = templateLocation;
37+
}
38+
39+
public string Name { get; }
40+
public string NameSpace { get; }
41+
public string IdName { get; }
42+
public EquatableArray<string> TemplateNames { get; }
43+
public ParentClass? Parent { get; }
44+
public LocationInfo? TemplateLocation { get; }
45+
}
46+
2747
internal sealed record Result<TValue>(TValue Value, EquatableArray<DiagnosticInfo> Errors)
2848
where TValue : IEquatable<TValue>?
2949
{

0 commit comments

Comments
 (0)