1+ using System ;
2+ using System . Collections . Generic ;
3+ using System . Linq ;
4+ using System . Threading . Tasks ;
5+ using ICSharpCode . CodeConverter . Util ;
6+ using Microsoft . CodeAnalysis ;
7+ using Microsoft . CodeAnalysis . FindSymbols ;
8+ using Microsoft . CodeAnalysis . Rename ;
9+
10+ namespace ICSharpCode . CodeConverter . Shared
11+ {
12+ internal static class SymbolRenamer
13+ {
14+ public static IEnumerable < ( ISymbol Original , string NewName ) > GetSymbolsWithNewNames (
15+ IEnumerable < ISymbol > toRename , Func < string , bool > canUse , bool canKeepOne )
16+ {
17+ var symbolsWithNewNames = toRename . OrderByDescending ( x => x . DeclaredAccessibility ) . ThenByDescending ( x => x . Kind == SymbolKind . Parameter || x . Kind == SymbolKind . Property ) . Skip ( canKeepOne ? 1 : 0 ) . Select ( tr =>
18+ {
19+ string newName = NameGenerator . GenerateUniqueName ( GetBaseName ( tr ) , canUse ) ;
20+ return ( Original : tr , NewName : newName ) ;
21+ } ) ;
22+ return symbolsWithNewNames ;
23+ }
24+
25+ public static string GetName ( ISymbol m ) {
26+ if ( m . CanBeReferencedByName )
27+ return m . Name ;
28+ if ( m . ExplicitInterfaceImplementations ( ) . Any ( ) )
29+ return m . Name . Split ( '.' ) . Last ( ) ;
30+ return m . Name ;
31+ }
32+
33+ public static async Task < Project > PerformRenamesAsync ( Project project , IReadOnlyCollection < ( ISymbol Original , string NewName ) > symbolsWithNewNames )
34+ {
35+ var solution = project . Solution ;
36+ foreach ( var ( originalSymbol , newName ) in symbolsWithNewNames ) {
37+ project = solution . GetProject ( project . Id ) ;
38+ var compilation = await project . GetCompilationAsync ( ) ;
39+ ISymbol currentDeclaration = SymbolFinder . FindSimilarSymbols ( originalSymbol , compilation ) . FirstOrDefault ( ) ;
40+ if ( currentDeclaration == null )
41+ continue ; //Must have already renamed this symbol for a different reason
42+ solution = await Renamer . RenameSymbolAsync ( solution , currentDeclaration , newName , solution . Workspace . Options ) ;
43+ }
44+
45+ return solution . GetProject ( project . Id ) ;
46+ }
47+
48+ private static string GetBaseName ( ISymbol declaration )
49+ {
50+ string prefix = declaration . Kind . ToString ( ) . ToLowerInvariant ( ) [ 0 ] + "_" ;
51+ string name = GetName ( declaration ) ;
52+ return prefix + name . Substring ( 0 , 1 ) . ToUpperInvariant ( ) + name . Substring ( 1 ) ;
53+ }
54+
55+ public static IEnumerable < INamespaceOrTypeSymbol > GetNamespacesAndTypesInAssembly ( Project project , Compilation compilation )
56+ {
57+ return compilation . GlobalNamespace . FollowProperty ( ( INamespaceOrTypeSymbol n ) => n . GetMembers ( ) . OfType < INamespaceOrTypeSymbol > ( ) . Where ( s => s . IsDefinedInSource ( ) && s ? . ContainingAssembly ? . Name == project . AssemblyName ) ) ;
58+ }
59+
60+ public static IEnumerable < ( ISymbol Original , string NewName ) > GetSymbolsWithNewNames ( IReadOnlyCollection < ISymbol > symbolGroup , HashSet < string > names , bool caseSensitive )
61+ {
62+ var canRename = symbolGroup . Where ( s => s . IsDefinedInSource ( ) && s . CanBeReferencedByName ) . ToArray ( ) ;
63+ var specialSymbolUsingName = canRename . Length < symbolGroup . Count ;
64+ var methodSymbols = canRename . OfType < IMethodSymbol > ( ) . ToArray ( ) ;
65+ var canKeepOneNormalMemberName = ! specialSymbolUsingName && ! methodSymbols . Any ( ) ;
66+ symbolGroup = canRename . Except ( methodSymbols ) . ToArray ( ) ;
67+ ( ISymbol Original , string NewName ) [ ] methodsWithNewNames = GetMethodSymbolsWithNewNames ( methodSymbols . ToArray ( ) , names , specialSymbolUsingName , caseSensitive ) ;
68+ return GetSymbolsWithNewNames ( symbolGroup , names . Add , canKeepOneNormalMemberName ) . Concat ( methodsWithNewNames ) ;
69+ }
70+
71+ private static ( ISymbol Original , string NewName ) [ ] GetMethodSymbolsWithNewNames ( IMethodSymbol [ ] methodSymbols ,
72+ HashSet < string > names ,
73+ bool specialSymbolUsingName , bool caseSensitive )
74+ {
75+ var stringComparer = caseSensitive ? StringComparer . Ordinal : StringComparer . OrdinalIgnoreCase ;
76+ var methodsBySignature = methodSymbols
77+ . ToLookup ( m => m . GetUnqualifiedMethodSignature ( caseSensitive ) )
78+ . Where ( g => g . Count ( ) > 1 )
79+ . SelectMany ( clashingMethodGroup =>
80+ {
81+ var thisMethodGroupNames = new HashSet < string > ( stringComparer ) ;
82+ var symbolsWithNewNames = GetSymbolsWithNewNames ( clashingMethodGroup ,
83+ n => ! names . Contains ( n ) && thisMethodGroupNames . Add ( n ) ,
84+ ! specialSymbolUsingName ) . ToArray ( ) ;
85+ return symbolsWithNewNames ;
86+ } ) . ToArray ( ) ;
87+
88+ foreach ( var newMethodNames in methodsBySignature . Select ( m => m . NewName ) )
89+ {
90+ names . Add ( newMethodNames ) ;
91+ }
92+
93+ return methodsBySignature ;
94+ }
95+ }
96+ }
0 commit comments