1+ using Microsoft . CodeAnalysis . Text ;
2+
13namespace EndpointHelpers ;
24
35using System . Collections . Generic ;
@@ -6,8 +8,6 @@ namespace EndpointHelpers;
68using System . Text ;
79using Microsoft . CodeAnalysis ;
810using Microsoft . CodeAnalysis . CSharp . Syntax ;
9- using Microsoft . CodeAnalysis . Text ;
10-
1111
1212[ Generator ]
1313public sealed class LinkGeneratorGenerator : IIncrementalGenerator
@@ -18,77 +18,46 @@ public sealed class LinkGeneratorGenerator : IIncrementalGenerator
1818 private const string UnifiedGenerateAttributeName = "GenerateEndpointHelpersAttribute" ;
1919 private const string NonActionAttributeName = "Microsoft.AspNetCore.Mvc.NonActionAttribute" ;
2020
21- //lang=cs
22- private const string AttributeSourceCode = $ """
23- // <auto-generated/>
24-
25- namespace { Namespace } ;
26-
27- [System.AttributeUsage(
28- System.AttributeTargets.Method |
29- System.AttributeTargets.Class |
30- System.AttributeTargets.Assembly)]
31- public sealed class { GenerateAttributeName } : System.Attribute;
32-
33- [System.AttributeUsage(System.AttributeTargets.Method)]
34- public sealed class { IgnoreAttributeName } : System.Attribute;
35- """ ;
36-
3721 public void Initialize ( IncrementalGeneratorInitializationContext context )
3822 {
39- context . RegisterPostInitializationOutput ( ctx =>
40- ctx . AddSource (
41- "Attributes.g.cs" ,
42- SourceText . From ( AttributeSourceCode , Encoding . UTF8 ) ) ) ;
43-
44- var provider = context . SyntaxProvider
45- . CreateSyntaxProvider (
46- static ( s , _ ) => s is ClassDeclarationSyntax or MethodDeclarationSyntax ,
47- static ( ctx , _ ) => GetTarget ( ctx ) )
23+ var generateTargets = context . SyntaxProvider
24+ . ForAttributeWithMetadataName (
25+ $ "{ Namespace } .{ GenerateAttributeName } ",
26+ static ( node , _ ) => node is ClassDeclarationSyntax or MethodDeclarationSyntax ,
27+ static ( ctx , _ ) => GetControllerMetadataName ( ctx . TargetSymbol ) )
4828 . Where ( static t => t is not null ) ;
4929
30+ var unifiedTargets = context . SyntaxProvider
31+ . ForAttributeWithMetadataName (
32+ $ "{ Namespace } .{ UnifiedGenerateAttributeName } ",
33+ static ( node , _ ) => node is ClassDeclarationSyntax or MethodDeclarationSyntax ,
34+ static ( ctx , _ ) => GetControllerMetadataName ( ctx . TargetSymbol ) )
35+ . Where ( static t => t is not null ) ;
36+
37+ var targets = generateTargets
38+ . Collect ( )
39+ . Combine ( unifiedTargets . Collect ( ) )
40+ . Select ( static ( x , _ ) => x . Left . AddRange ( x . Right ) ) ;
41+
5042 context . RegisterSourceOutput (
51- context . CompilationProvider . Combine ( provider . Collect ( ) ) ,
43+ context . CompilationProvider . Combine ( targets ) ,
5244 static ( ctx , t ) => Generate ( ctx , t . Left , t . Right ! ) ) ;
5345 }
5446
55- private static MemberDeclarationSyntax ? GetTarget ( GeneratorSyntaxContext context )
47+ private static string ? GetControllerMetadataName ( ISymbol symbol )
5648 {
57- if ( context . Node is ClassDeclarationSyntax cls )
58- {
59- foreach ( var list in cls . AttributeLists )
60- foreach ( var attr in list . Attributes )
61- {
62- if ( context . SemanticModel . GetSymbolInfo ( attr ) . Symbol is IMethodSymbol symbol &&
63- ( symbol . ContainingType . ToDisplayString ( ) ==
64- $ "{ Namespace } .{ GenerateAttributeName } " ||
65- symbol . ContainingType . ToDisplayString ( ) ==
66- $ "{ Namespace } .{ UnifiedGenerateAttributeName } ") )
67- return cls ;
68- }
69- }
70-
71- if ( context . Node is MethodDeclarationSyntax method )
49+ return symbol switch
7250 {
73- foreach ( var list in method . AttributeLists )
74- foreach ( var attr in list . Attributes )
75- {
76- if ( context . SemanticModel . GetSymbolInfo ( attr ) . Symbol is IMethodSymbol symbol &&
77- ( symbol . ContainingType . ToDisplayString ( ) ==
78- $ "{ Namespace } .{ GenerateAttributeName } " ||
79- symbol . ContainingType . ToDisplayString ( ) ==
80- $ "{ Namespace } .{ UnifiedGenerateAttributeName } ") )
81- return method ;
82- }
83- }
84-
85- return null ;
51+ INamedTypeSymbol type => GetMetadataName ( type ) ,
52+ IMethodSymbol method => method . ContainingType is null ? null : GetMetadataName ( method . ContainingType ) ,
53+ _ => null
54+ } ;
8655 }
8756
8857 private static void Generate (
8958 SourceProductionContext context ,
9059 Compilation compilation ,
91- ImmutableArray < MemberDeclarationSyntax > members )
60+ ImmutableArray < string ? > members )
9261 {
9362 var assemblyHasGenerate = compilation . Assembly . GetAttributes ( )
9463 . Any ( a => a . AttributeClass ? . ToDisplayString ( ) ==
@@ -113,18 +82,8 @@ private static void Generate(
11382 return ;
11483
11584 controllers = members
116- . Select ( m =>
117- {
118- if ( m is ClassDeclarationSyntax cls )
119- return compilation . GetSemanticModel ( cls . SyntaxTree )
120- . GetDeclaredSymbol ( cls ) as INamedTypeSymbol ;
121-
122- if ( m is MethodDeclarationSyntax method )
123- return ( compilation . GetSemanticModel ( method . SyntaxTree )
124- . GetDeclaredSymbol ( method ) as IMethodSymbol ) ? . ContainingType ;
125-
126- return null ;
127- } )
85+ . Distinct ( )
86+ . Select ( name => compilation . GetTypeByMetadataName ( name ! ) )
12887 . Where ( s => s is not null )
12988 . Distinct ( SymbolEqualityComparer . Default )
13089 . Cast < INamedTypeSymbol > ( )
@@ -268,4 +227,19 @@ private static IEnumerable<INamedTypeSymbol> GetAllTypes(INamespaceSymbol ns)
268227 foreach ( var type in GetAllTypes ( nested ) )
269228 yield return type ;
270229 }
230+
231+ private static string GetMetadataName ( INamedTypeSymbol type )
232+ {
233+ var name = type . MetadataName ;
234+ var current = type . ContainingType ;
235+
236+ while ( current is not null )
237+ {
238+ name = $ "{ current . MetadataName } +{ name } ";
239+ current = current . ContainingType ;
240+ }
241+
242+ var ns = type . ContainingNamespace ? . ToDisplayString ( ) ;
243+ return string . IsNullOrEmpty ( ns ) ? name : $ "{ ns } .{ name } ";
244+ }
271245}
0 commit comments