Skip to content

Commit 9c207c4

Browse files
committed
Merge pull request #1851 from sharwell/fix-437
Update RenameToUpperCaseCodeFixProvider to support conflict resolution
2 parents c05d224 + 24ccd60 commit 9c207c4

6 files changed

Lines changed: 358 additions & 47 deletions

File tree

StyleCop.Analyzers/StyleCop.Analyzers.CodeFixes/NamingRules/RenameToUpperCaseCodeFixProvider.cs

Lines changed: 23 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@ namespace StyleCop.Analyzers.NamingRules
2626
[Shared]
2727
internal class RenameToUpperCaseCodeFixProvider : CodeFixProvider
2828
{
29+
/// <summary>
30+
/// During conflict resolution for fields, this suffix is tried before falling back to 1, 2, 3, etc...
31+
/// </summary>
2932
private const string Suffix = "Value";
3033

3134
/// <inheritdoc/>
@@ -52,8 +55,9 @@ public override async Task RegisterCodeFixesAsync(CodeFixContext context)
5255
foreach (var diagnostic in context.Diagnostics)
5356
{
5457
var token = root.FindToken(diagnostic.Location.SourceSpan.Start);
55-
var newName = char.ToUpper(token.ValueText[0]) + token.ValueText.Substring(1);
56-
var memberSyntax = this.GetParentTypeDeclaration(token);
58+
var baseName = char.ToUpper(token.ValueText[0]) + token.ValueText.Substring(1);
59+
var newName = baseName;
60+
var memberSyntax = GetParentTypeDeclaration(token);
5761

5862
if (memberSyntax is NamespaceDeclarationSyntax)
5963
{
@@ -62,9 +66,9 @@ public override async Task RegisterCodeFixesAsync(CodeFixContext context)
6266
{
6367
IdentifierNameSyntax identifierSyntax = (IdentifierNameSyntax)token.Parent;
6468

65-
var newIdentifierSyntac = identifierSyntax.WithIdentifier(SyntaxFactory.Identifier(newName));
69+
var newIdentifierSyntax = identifierSyntax.WithIdentifier(SyntaxFactory.Identifier(newName));
6670

67-
var newRoot = root.ReplaceNode(identifierSyntax, newIdentifierSyntac);
71+
var newRoot = root.ReplaceNode(identifierSyntax, newIdentifierSyntax);
6872
return Task.FromResult(context.Document.WithSyntaxRoot(newRoot));
6973
};
7074

@@ -79,65 +83,38 @@ public override async Task RegisterCodeFixesAsync(CodeFixContext context)
7983
{
8084
SemanticModel semanticModel = await document.GetSemanticModelAsync(context.CancellationToken).ConfigureAwait(false);
8185

82-
var typeDeclarationSymbol = semanticModel.GetDeclaredSymbol(memberSyntax);
83-
if (typeDeclarationSymbol == null)
86+
var declaredSymbol = semanticModel.GetDeclaredSymbol(memberSyntax);
87+
if (declaredSymbol == null)
8488
{
8589
continue;
8690
}
8791

88-
if (!this.IsValidNewMemberName(typeDeclarationSymbol, newName))
92+
bool usedSuffix = false;
93+
if (declaredSymbol.Kind == SymbolKind.Field && !RenameHelper.IsValidNewMemberName(semanticModel, declaredSymbol, newName))
8994
{
95+
usedSuffix = true;
9096
newName = newName + Suffix;
9197
}
9298

99+
int index = 0;
100+
while (!RenameHelper.IsValidNewMemberName(semanticModel, declaredSymbol, newName))
101+
{
102+
usedSuffix = false;
103+
index++;
104+
newName = baseName + index;
105+
}
106+
93107
context.RegisterCodeFix(
94108
CodeAction.Create(
95109
string.Format(NamingResources.RenameToCodeFix, newName),
96110
cancellationToken => RenameHelper.RenameSymbolAsync(document, root, token, newName, cancellationToken),
97-
nameof(RenameToUpperCaseCodeFixProvider) + "_" + diagnostic.Id),
111+
nameof(RenameToUpperCaseCodeFixProvider) + "_" + diagnostic.Id + "_" + usedSuffix + "_" + index),
98112
diagnostic);
99113
}
100114
}
101115
}
102116

103-
private bool IsValidNewMemberName(ISymbol typeSymbol, string name)
104-
{
105-
if (typeSymbol == null)
106-
{
107-
throw new ArgumentNullException(nameof(typeSymbol));
108-
}
109-
else if (typeSymbol.Name == name)
110-
{
111-
return false;
112-
}
113-
114-
var members = (typeSymbol as INamedTypeSymbol)?.GetMembers(name);
115-
if (members.HasValue && !members.Value.IsDefaultOrEmpty)
116-
{
117-
return false;
118-
}
119-
120-
var containingType = typeSymbol.ContainingSymbol as INamedTypeSymbol;
121-
if (containingType != null)
122-
{
123-
// The name can't be the same as the name of the containing type
124-
if (containingType.Name == name)
125-
{
126-
return false;
127-
}
128-
129-
// The name can't be the same as the name of an other member of the same type
130-
members = containingType.GetMembers(name);
131-
if (!members.Value.IsDefaultOrEmpty)
132-
{
133-
return false;
134-
}
135-
}
136-
137-
return true;
138-
}
139-
140-
private SyntaxNode GetParentTypeDeclaration(SyntaxToken token)
117+
private static SyntaxNode GetParentTypeDeclaration(SyntaxToken token)
141118
{
142119
SyntaxNode parent = token.Parent;
143120

StyleCop.Analyzers/StyleCop.Analyzers.Test/NamingRules/SA1300UnitTests.cs

Lines changed: 205 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,27 @@ public async Task TestLowerCaseClassAsync()
106106
await this.VerifyCSharpFixAsync(testCode, fixedCode).ConfigureAwait(false);
107107
}
108108

109+
[Fact]
110+
public async Task TestLowerCaseClassWithConflictAsync()
111+
{
112+
var testCode = @"public class test
113+
{
114+
}
115+
116+
public class Test { }";
117+
var fixedCode = @"public class Test1
118+
{
119+
}
120+
121+
public class Test { }";
122+
123+
DiagnosticResult expected = this.CSharpDiagnostic().WithArguments("test").WithLocation(1, 14);
124+
125+
await this.VerifyCSharpDiagnosticAsync(testCode, expected, CancellationToken.None).ConfigureAwait(false);
126+
await this.VerifyCSharpDiagnosticAsync(fixedCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
127+
await this.VerifyCSharpFixAsync(testCode, fixedCode).ConfigureAwait(false);
128+
}
129+
109130
[Fact]
110131
public async Task TestUpperCaseInterfaceAsync()
111132
{
@@ -159,6 +180,27 @@ public async Task TestLowerCaseStructAsync()
159180
await this.VerifyCSharpFixAsync(testCode, fixedCode).ConfigureAwait(false);
160181
}
161182

183+
[Fact]
184+
public async Task TestLowerCaseStructWithConflictAsync()
185+
{
186+
var testCode = @"public struct test
187+
{
188+
}
189+
190+
public class Test { }";
191+
var fixedCode = @"public struct Test1
192+
{
193+
}
194+
195+
public class Test { }";
196+
197+
DiagnosticResult expected = this.CSharpDiagnostic().WithArguments("test").WithLocation(1, 15);
198+
199+
await this.VerifyCSharpDiagnosticAsync(testCode, expected, CancellationToken.None).ConfigureAwait(false);
200+
await this.VerifyCSharpDiagnosticAsync(fixedCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
201+
await this.VerifyCSharpFixAsync(testCode, fixedCode).ConfigureAwait(false);
202+
}
203+
162204
[Fact]
163205
public async Task TestUpperCaseEnumAsync()
164206
{
@@ -189,6 +231,27 @@ public async Task TestLowerCaseEnumAsync()
189231
await this.VerifyCSharpFixAsync(testCode, fixedCode).ConfigureAwait(false);
190232
}
191233

234+
[Fact]
235+
public async Task TestLowerCaseEnumWithConflictAsync()
236+
{
237+
var testCode = @"public enum test
238+
{
239+
}
240+
241+
public class Test { }";
242+
var fixedCode = @"public enum Test1
243+
{
244+
}
245+
246+
public class Test { }";
247+
248+
DiagnosticResult expected = this.CSharpDiagnostic().WithArguments("test").WithLocation(1, 13);
249+
250+
await this.VerifyCSharpDiagnosticAsync(testCode, expected, CancellationToken.None).ConfigureAwait(false);
251+
await this.VerifyCSharpDiagnosticAsync(fixedCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
252+
await this.VerifyCSharpFixAsync(testCode, fixedCode).ConfigureAwait(false);
253+
}
254+
192255
[Fact]
193256
public async Task TestUpperCaseDelegateAsync()
194257
{
@@ -219,6 +282,29 @@ public async Task TestLowerCaseDelegateAsync()
219282
await this.VerifyCSharpFixAsync(testCode, fixedCode).ConfigureAwait(false);
220283
}
221284

285+
[Fact]
286+
public async Task TestLowerCaseDelegateWithConflictAsync()
287+
{
288+
var testCode = @"public class Test1
289+
{
290+
public delegate void test();
291+
292+
public int Test => 0;
293+
}";
294+
var fixedCode = @"public class Test1
295+
{
296+
public delegate void Test2();
297+
298+
public int Test => 0;
299+
}";
300+
301+
DiagnosticResult expected = this.CSharpDiagnostic().WithArguments("test").WithLocation(3, 22);
302+
303+
await this.VerifyCSharpDiagnosticAsync(testCode, expected, CancellationToken.None).ConfigureAwait(false);
304+
await this.VerifyCSharpDiagnosticAsync(fixedCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
305+
await this.VerifyCSharpFixAsync(testCode, fixedCode).ConfigureAwait(false);
306+
}
307+
222308
[Fact]
223309
public async Task TestUpperCaseEventAsync()
224310
{
@@ -285,6 +371,53 @@ public event Test TestEvent
285371
await this.VerifyCSharpFixAsync(testCode, fixedCode).ConfigureAwait(false);
286372
}
287373

374+
[Fact]
375+
public async Task TestLowerCaseEventWithConflictAsync()
376+
{
377+
var testCode = @"public class TestClass
378+
{
379+
public delegate void Test();
380+
Test _testEvent;
381+
public event Test testEvent
382+
{
383+
add
384+
{
385+
_testEvent += value;
386+
}
387+
remove
388+
{
389+
_testEvent -= value;
390+
}
391+
}
392+
393+
public int TestEvent => 0;
394+
}";
395+
var fixedCode = @"public class TestClass
396+
{
397+
public delegate void Test();
398+
Test _testEvent;
399+
public event Test TestEvent1
400+
{
401+
add
402+
{
403+
_testEvent += value;
404+
}
405+
remove
406+
{
407+
_testEvent -= value;
408+
}
409+
}
410+
411+
public int TestEvent => 0;
412+
}";
413+
414+
DiagnosticResult expected = this.CSharpDiagnostic().WithArguments("testEvent").WithLocation(5, 23);
415+
416+
await this.VerifyCSharpDiagnosticAsync(testCode, expected, CancellationToken.None).ConfigureAwait(false);
417+
await this.VerifyCSharpDiagnosticAsync(fixedCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
418+
await this.VerifyCSharpFixAsync(testCode, fixedCode).ConfigureAwait(false);
419+
}
420+
288421
[Fact]
289422
public async Task TestUpperCaseEventFieldAsync()
290423
{
@@ -318,6 +451,29 @@ public async Task TestLowerCaseEventFieldAsync()
318451
await this.VerifyCSharpFixAsync(testCode, fixedCode).ConfigureAwait(false);
319452
}
320453

454+
[Fact]
455+
public async Task TestLowerCaseEventFieldWithConflictAsync()
456+
{
457+
var testCode = @"public class TestClass
458+
{
459+
public delegate void Test();
460+
public event Test testEvent;
461+
public event Test TestEvent;
462+
}";
463+
var fixedCode = @"public class TestClass
464+
{
465+
public delegate void Test();
466+
public event Test TestEvent1;
467+
public event Test TestEvent;
468+
}";
469+
470+
DiagnosticResult expected = this.CSharpDiagnostic().WithArguments("testEvent").WithLocation(4, 23);
471+
472+
await this.VerifyCSharpDiagnosticAsync(testCode, expected, CancellationToken.None).ConfigureAwait(false);
473+
await this.VerifyCSharpDiagnosticAsync(fixedCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
474+
await this.VerifyCSharpFixAsync(testCode, fixedCode).ConfigureAwait(false);
475+
}
476+
321477
[Fact]
322478
public async Task TestUpperCaseMethodAsync()
323479
{
@@ -354,6 +510,34 @@ public void Test()
354510
await this.VerifyCSharpFixAsync(testCode, fixedCode).ConfigureAwait(false);
355511
}
356512

513+
[Fact]
514+
public async Task TestLowerCaseMethodWithConflictAsync()
515+
{
516+
// Conflict resolution does not attempt to examine overloaded methods.
517+
var testCode = @"public class TestClass
518+
{
519+
public void test()
520+
{
521+
}
522+
523+
public int Test(int value) => value;
524+
}";
525+
var fixedCode = @"public class TestClass
526+
{
527+
public void Test1()
528+
{
529+
}
530+
531+
public int Test(int value) => value;
532+
}";
533+
534+
DiagnosticResult expected = this.CSharpDiagnostic().WithArguments("test").WithLocation(3, 13);
535+
536+
await this.VerifyCSharpDiagnosticAsync(testCode, expected, CancellationToken.None).ConfigureAwait(false);
537+
await this.VerifyCSharpDiagnosticAsync(fixedCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
538+
await this.VerifyCSharpFixAsync(testCode, fixedCode).ConfigureAwait(false);
539+
}
540+
357541
[Fact]
358542
public async Task TestUpperCasePropertyAsync()
359543
{
@@ -384,6 +568,27 @@ public async Task TestLowerCasePropertyAsync()
384568
await this.VerifyCSharpFixAsync(testCode, fixedCode).ConfigureAwait(false);
385569
}
386570

571+
[Fact]
572+
public async Task TestLowerCasePropertyWithConflictAsync()
573+
{
574+
var testCode = @"public class TestClass
575+
{
576+
public string test { get; set; }
577+
public string Test => string.Empty;
578+
}";
579+
var fixedCode = @"public class TestClass
580+
{
581+
public string Test1 { get; set; }
582+
public string Test => string.Empty;
583+
}";
584+
585+
DiagnosticResult expected = this.CSharpDiagnostic().WithArguments("test").WithLocation(3, 15);
586+
587+
await this.VerifyCSharpDiagnosticAsync(testCode, expected, CancellationToken.None).ConfigureAwait(false);
588+
await this.VerifyCSharpDiagnosticAsync(fixedCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
589+
await this.VerifyCSharpFixAsync(testCode, fixedCode).ConfigureAwait(false);
590+
}
591+
387592
[Fact]
388593
public async Task TestUpperCasePublicFieldAsync()
389594
{

0 commit comments

Comments
 (0)