22// The .NET Foundation licenses this file to you under the MIT license.
33// See the LICENSE file in the project root for more information.
44
5+ using System ;
56using System . Collections . Generic ;
67using System . Text ;
8+ using CommunityToolkit . Mvvm . SourceGenerators . Helpers ;
79using Microsoft . CodeAnalysis ;
810
911namespace CommunityToolkit . Mvvm . SourceGenerators . Extensions ;
@@ -20,27 +22,46 @@ internal static class INamedTypeSymbolExtensions
2022 /// <returns>The full metadata name for <paramref name="symbol"/> that is also a valid filename.</returns>
2123 public static string GetFullMetadataNameForFileName ( this INamedTypeSymbol symbol )
2224 {
23- static StringBuilder BuildFrom ( ISymbol ? symbol , StringBuilder builder )
25+ using ImmutableArrayBuilder < char > builder = ImmutableArrayBuilder < char > . Rent ( ) ;
26+
27+ static void BuildFrom ( ISymbol ? symbol , in ImmutableArrayBuilder < char > builder )
2428 {
25- return symbol switch
29+ switch ( symbol )
2630 {
27- INamespaceSymbol ns when ns . IsGlobalNamespace => builder ,
28- INamespaceSymbol ns when ns . ContainingNamespace is { IsGlobalNamespace : false }
29- => BuildFrom ( ns . ContainingNamespace , builder . Insert ( 0 , $ ".{ ns . MetadataName } ") ) ,
30- ITypeSymbol ts when ts . ContainingType is ISymbol pt
31- => BuildFrom ( pt , builder . Insert ( 0 , $ "+{ ts . MetadataName } ") ) ,
32- ITypeSymbol ts when ts . ContainingNamespace is ISymbol pn and not INamespaceSymbol { IsGlobalNamespace : true }
33- => BuildFrom ( pn , builder . Insert ( 0 , $ ".{ ts . MetadataName } ") ) ,
34- ISymbol => BuildFrom ( symbol . ContainingSymbol , builder . Insert ( 0 , symbol . MetadataName ) ) ,
35- _ => builder
36- } ;
31+ // Namespaces that are nested also append a leading '.'
32+ case INamespaceSymbol { ContainingNamespace . IsGlobalNamespace : false } :
33+ BuildFrom ( symbol . ContainingNamespace , in builder ) ;
34+ builder . Add ( '.' ) ;
35+ builder . AddRange ( symbol . MetadataName . AsSpan ( ) ) ;
36+ break ;
37+ // Other namespaces (ie. the one right before global) skip the leading '.'
38+ case INamespaceSymbol { IsGlobalNamespace : false } :
39+ builder . AddRange ( symbol . MetadataName . AsSpan ( ) ) ;
40+ break ;
41+ // Types with no namespace just have their metadata name directly written
42+ case ITypeSymbol { ContainingSymbol : INamespaceSymbol { IsGlobalNamespace : true } } :
43+ builder . AddRange ( symbol . MetadataName . AsSpan ( ) ) ;
44+ break ;
45+ // Types with a containing non-global namespace also append a leading '.'
46+ case ITypeSymbol { ContainingSymbol : INamespaceSymbol namespaceSymbol } :
47+ BuildFrom ( namespaceSymbol , in builder ) ;
48+ builder . Add ( '.' ) ;
49+ builder . AddRange ( symbol . MetadataName . AsSpan ( ) ) ;
50+ break ;
51+ // Nested types append a leading '+'
52+ case ITypeSymbol { ContainingSymbol : ITypeSymbol typeSymbol } :
53+ BuildFrom ( typeSymbol , in builder ) ;
54+ builder . Add ( '+' ) ;
55+ builder . AddRange ( symbol . MetadataName . AsSpan ( ) ) ;
56+ break ;
57+ default :
58+ break ;
59+ }
3760 }
3861
39- // Build the full metadata name by concatenating the metadata names of all symbols from the input
40- // one to the outermost namespace, if any. Additionally, the ` and + symbols need to be replaced
41- // to avoid errors when generating code. This is a known issue with source generators not accepting
42- // those characters at the moment, see: https://github.com/dotnet/roslyn/issues/58476.
43- return BuildFrom ( symbol , new StringBuilder ( 256 ) ) . Replace ( '`' , '-' ) . Replace ( '+' , '.' ) . ToString ( ) ;
62+ BuildFrom ( symbol , in builder ) ;
63+
64+ return builder . ToString ( ) ;
4465 }
4566
4667 /// <summary>
0 commit comments