44namespace StyleCop . Analyzers . Helpers
55{
66 using System . Collections . Immutable ;
7+ using System . Linq ;
78 using System . Threading ;
89 using System . Threading . Tasks ;
910 using Microsoft . CodeAnalysis ;
1011 using Microsoft . CodeAnalysis . CodeActions ;
12+ using Microsoft . CodeAnalysis . CSharp ;
13+ using Microsoft . CodeAnalysis . CSharp . Syntax ;
1114 using Microsoft . CodeAnalysis . Rename ;
1215
1316 internal static class RenameHelper
@@ -30,7 +33,7 @@ public static async Task<Solution> RenameSymbolAsync(Document document, SyntaxNo
3033 return newSolution ;
3134 }
3235
33- public static bool IsValidNewMemberName ( SemanticModel semanticModel , ISymbol symbol , string name )
36+ public static async Task < bool > IsValidNewMemberNameAsync ( SemanticModel semanticModel , ISymbol symbol , string name , CancellationToken cancellationToken )
3437 {
3538 if ( symbol . Kind == SymbolKind . NamedType )
3639 {
@@ -47,39 +50,204 @@ public static bool IsValidNewMemberName(SemanticModel semanticModel, ISymbol sym
4750 }
4851 }
4952
50- var containingSymbol = symbol . ContainingSymbol as INamespaceOrTypeSymbol ;
51- if ( containingSymbol == null )
53+ var containingSymbol = symbol . ContainingSymbol ;
54+
55+ var containingNamespaceOrTypeSymbol = containingSymbol as INamespaceOrTypeSymbol ;
56+ if ( containingNamespaceOrTypeSymbol != null )
5257 {
58+ if ( containingNamespaceOrTypeSymbol . Kind == SymbolKind . Namespace )
59+ {
60+ // Make sure to use the compilation namespace so interfaces in referenced assemblies are considered
61+ containingNamespaceOrTypeSymbol = semanticModel . Compilation . GetCompilationNamespace ( ( INamespaceSymbol ) containingNamespaceOrTypeSymbol ) ;
62+ }
63+ else if ( containingNamespaceOrTypeSymbol . Kind == SymbolKind . NamedType )
64+ {
65+ TypeKind typeKind = ( ( INamedTypeSymbol ) containingNamespaceOrTypeSymbol ) . TypeKind ;
66+
67+ // If the containing type is a class or struct, the name can't be the same as the name of the containing
68+ // type.
69+ if ( ( typeKind == TypeKind . Class || typeKind == TypeKind . Struct )
70+ && containingNamespaceOrTypeSymbol . Name == name )
71+ {
72+ return false ;
73+ }
74+ }
75+
76+ // The name can't be the same as the name of an other member of the same type. At this point no special
77+ // consideration is given to overloaded methods.
78+ ImmutableArray < ISymbol > siblings = containingNamespaceOrTypeSymbol . GetMembers ( name ) ;
79+ if ( ! siblings . IsDefaultOrEmpty )
80+ {
81+ return false ;
82+ }
83+
5384 return true ;
5485 }
55-
56- if ( containingSymbol . Kind == SymbolKind . Namespace )
86+ else if ( containingSymbol . Kind == SymbolKind . Method )
5787 {
58- // Make sure to use the compilation namespace so interfaces in referenced assemblies are considered
59- containingSymbol = semanticModel . Compilation . GetCompilationNamespace ( ( INamespaceSymbol ) containingSymbol ) ;
88+ IMethodSymbol methodSymbol = ( IMethodSymbol ) containingSymbol ;
89+ if ( methodSymbol . Parameters . Any ( i => i . Name == name )
90+ || methodSymbol . TypeParameters . Any ( i => i . Name == name ) )
91+ {
92+ return false ;
93+ }
94+
95+ IMethodSymbol outermostMethod = methodSymbol ;
96+ while ( outermostMethod . ContainingSymbol . Kind == SymbolKind . Method )
97+ {
98+ outermostMethod = ( IMethodSymbol ) outermostMethod . ContainingSymbol ;
99+ if ( outermostMethod . Parameters . Any ( i => i . Name == name )
100+ || outermostMethod . TypeParameters . Any ( i => i . Name == name ) )
101+ {
102+ return false ;
103+ }
104+ }
105+
106+ foreach ( var syntaxReference in outermostMethod . DeclaringSyntaxReferences )
107+ {
108+ SyntaxNode syntaxNode = await syntaxReference . GetSyntaxAsync ( cancellationToken ) . ConfigureAwait ( false ) ;
109+ LocalNameFinder localNameFinder = new LocalNameFinder ( name ) ;
110+ localNameFinder . Visit ( syntaxNode ) ;
111+ if ( localNameFinder . Found )
112+ {
113+ return false ;
114+ }
115+ }
116+
117+ return true ;
60118 }
61- else if ( containingSymbol . Kind == SymbolKind . NamedType )
119+ else
62120 {
63- TypeKind typeKind = ( ( INamedTypeSymbol ) containingSymbol ) . TypeKind ;
121+ return true ;
122+ }
123+ }
64124
65- // If the containing type is a class or struct, the name can't be the same as the name of the containing
66- // type.
67- if ( ( typeKind == TypeKind . Class || typeKind == TypeKind . Struct )
68- && containingSymbol . Name == name )
125+ public static SyntaxNode GetParentDeclaration ( SyntaxToken token )
126+ {
127+ SyntaxNode parent = token . Parent ;
128+
129+ while ( parent != null )
130+ {
131+ switch ( parent . Kind ( ) )
69132 {
70- return false ;
133+ case SyntaxKind . VariableDeclarator :
134+ case SyntaxKind . Parameter :
135+ case SyntaxKind . TypeParameter :
136+ case SyntaxKind . CatchDeclaration :
137+ case SyntaxKind . ExternAliasDirective :
138+ case SyntaxKind . QueryContinuation :
139+ case SyntaxKind . FromClause :
140+ case SyntaxKind . LetClause :
141+ case SyntaxKind . JoinClause :
142+ case SyntaxKind . JoinIntoClause :
143+ case SyntaxKind . ForEachStatement :
144+ case SyntaxKind . UsingDirective :
145+ case SyntaxKind . LabeledStatement :
146+ case SyntaxKind . AnonymousObjectMemberDeclarator :
147+ return parent ;
148+
149+ default :
150+ var declarationParent = parent as MemberDeclarationSyntax ;
151+ if ( declarationParent != null )
152+ {
153+ return declarationParent ;
154+ }
155+
156+ break ;
71157 }
158+
159+ parent = parent . Parent ;
160+ }
161+
162+ return null ;
163+ }
164+
165+ private class LocalNameFinder : CSharpSyntaxWalker
166+ {
167+ private readonly string name ;
168+
169+ public LocalNameFinder ( string name )
170+ {
171+ this . name = name ;
172+ }
173+
174+ public bool Found
175+ {
176+ get ;
177+ private set ;
72178 }
73179
74- // The name can't be the same as the name of an other member of the same type. At this point no special
75- // consideration is given to overloaded methods.
76- ImmutableArray < ISymbol > siblings = containingSymbol . GetMembers ( name ) ;
77- if ( ! siblings . IsDefaultOrEmpty )
180+ public override void VisitVariableDeclarator ( VariableDeclaratorSyntax node )
78181 {
79- return false ;
182+ this . Found |= node . Identifier . ValueText == this . name ;
183+ base . VisitVariableDeclarator ( node ) ;
80184 }
81185
82- return true ;
186+ public override void VisitParameter ( ParameterSyntax node )
187+ {
188+ this . Found |= node . Identifier . ValueText == this . name ;
189+ base . VisitParameter ( node ) ;
190+ }
191+
192+ public override void VisitTypeParameter ( TypeParameterSyntax node )
193+ {
194+ this . Found |= node . Identifier . ValueText == this . name ;
195+ base . VisitTypeParameter ( node ) ;
196+ }
197+
198+ public override void VisitCatchDeclaration ( CatchDeclarationSyntax node )
199+ {
200+ this . Found |= node . Identifier . ValueText == this . name ;
201+ base . VisitCatchDeclaration ( node ) ;
202+ }
203+
204+ public override void VisitQueryContinuation ( QueryContinuationSyntax node )
205+ {
206+ this . Found |= node . Identifier . ValueText == this . name ;
207+ base . VisitQueryContinuation ( node ) ;
208+ }
209+
210+ public override void VisitFromClause ( FromClauseSyntax node )
211+ {
212+ this . Found |= node . Identifier . ValueText == this . name ;
213+ base . VisitFromClause ( node ) ;
214+ }
215+
216+ public override void VisitLetClause ( LetClauseSyntax node )
217+ {
218+ this . Found |= node . Identifier . ValueText == this . name ;
219+ base . VisitLetClause ( node ) ;
220+ }
221+
222+ public override void VisitJoinClause ( JoinClauseSyntax node )
223+ {
224+ this . Found |= node . Identifier . ValueText == this . name ;
225+ base . VisitJoinClause ( node ) ;
226+ }
227+
228+ public override void VisitJoinIntoClause ( JoinIntoClauseSyntax node )
229+ {
230+ this . Found |= node . Identifier . ValueText == this . name ;
231+ base . VisitJoinIntoClause ( node ) ;
232+ }
233+
234+ public override void VisitForEachStatement ( ForEachStatementSyntax node )
235+ {
236+ this . Found |= node . Identifier . ValueText == this . name ;
237+ base . VisitForEachStatement ( node ) ;
238+ }
239+
240+ public override void VisitLabeledStatement ( LabeledStatementSyntax node )
241+ {
242+ this . Found |= node . Identifier . ValueText == this . name ;
243+ base . VisitLabeledStatement ( node ) ;
244+ }
245+
246+ public override void VisitAnonymousObjectMemberDeclarator ( AnonymousObjectMemberDeclaratorSyntax node )
247+ {
248+ this . Found |= node . NameEquals ? . Name ? . Identifier . ValueText == this . name ;
249+ base . VisitAnonymousObjectMemberDeclarator ( node ) ;
250+ }
83251 }
84252 }
85253}
0 commit comments