@@ -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 ;
0 commit comments