Skip to content

Commit c757335

Browse files
committed
Merge pull request #1866 from vweijsters/fix-1861
SA1306 now ignores leading underscores.
2 parents f30b606 + 61f0098 commit c757335

3 files changed

Lines changed: 88 additions & 11 deletions

File tree

StyleCop.Analyzers/StyleCop.Analyzers.CodeFixes/NamingRules/RenameToLowerCaseCodeFixProvider.cs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ public override async Task RegisterCodeFixesAsync(CodeFixContext context)
5151
}
5252

5353
var originalName = token.ValueText;
54+
5455
var baseName = originalName.TrimStart('_');
5556
if (baseName.Length == 0)
5657
{
@@ -60,7 +61,7 @@ public override async Task RegisterCodeFixesAsync(CodeFixContext context)
6061

6162
baseName = char.ToLower(baseName[0]) + baseName.Substring(1);
6263
int underscoreCount = originalName.Length - baseName.Length;
63-
var newName = baseName;
64+
6465
var memberSyntax = RenameHelper.GetParentDeclaration(token);
6566

6667
SemanticModel semanticModel = await document.GetSemanticModelAsync(context.CancellationToken).ConfigureAwait(false);
@@ -71,11 +72,15 @@ public override async Task RegisterCodeFixesAsync(CodeFixContext context)
7172
continue;
7273
}
7374

75+
// preserve the underscores, but only for fields.
76+
var prefix = declaredSymbol.Kind == SymbolKind.Field ? originalName.Substring(0, underscoreCount) : string.Empty;
77+
var newName = prefix + baseName;
78+
7479
int index = 0;
7580
while (!await RenameHelper.IsValidNewMemberNameAsync(semanticModel, declaredSymbol, newName, context.CancellationToken).ConfigureAwait(false))
7681
{
7782
index++;
78-
newName = baseName + index;
83+
newName = prefix + baseName + index;
7984
}
8085

8186
context.RegisterCodeFix(

StyleCop.Analyzers/StyleCop.Analyzers.Test/NamingRules/SA1306UnitTests.cs

Lines changed: 67 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -135,12 +135,27 @@ public async Task TestThatDiagnosticIsReported_SingleFieldAsync(string modifiers
135135
string car;
136136
{0}
137137
string Dar;
138+
{0}
139+
string _ear;
140+
{0}
141+
string _Far;
142+
{0}
143+
string __gar;
144+
{0}
145+
string __Har;
146+
{0}
147+
string ___iar;
148+
{0}
149+
string ___Jar;
138150
}}";
139151

140152
DiagnosticResult[] expected =
141153
{
142154
this.CSharpDiagnostic().WithArguments("Bar").WithLocation(4, 8),
143-
this.CSharpDiagnostic().WithArguments("Dar").WithLocation(8, 8)
155+
this.CSharpDiagnostic().WithArguments("Dar").WithLocation(8, 8),
156+
this.CSharpDiagnostic().WithArguments("_Far").WithLocation(12, 8),
157+
this.CSharpDiagnostic().WithArguments("__Har").WithLocation(16, 8),
158+
this.CSharpDiagnostic().WithArguments("___Jar").WithLocation(20, 8),
144159
};
145160

146161
await this.VerifyCSharpDiagnosticAsync(string.Format(testCode, modifiers), expected, CancellationToken.None).ConfigureAwait(false);
@@ -153,6 +168,18 @@ public async Task TestThatDiagnosticIsReported_SingleFieldAsync(string modifiers
153168
string car;
154169
{0}
155170
string dar;
171+
{0}
172+
string _ear;
173+
{0}
174+
string _far;
175+
{0}
176+
string __gar;
177+
{0}
178+
string __har;
179+
{0}
180+
string ___iar;
181+
{0}
182+
string ___jar;
156183
}}";
157184

158185
await this.VerifyCSharpDiagnosticAsync(string.Format(fixedCode, modifiers), EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
@@ -193,28 +220,62 @@ public async Task TestThatDiagnosticIsReported_MultipleFieldsAsync(string modifi
193220
}
194221

195222
[Fact]
196-
public async Task TestFieldStartingWithAnUnderscoreAsync()
223+
public async Task TestFieldStartingWithLetterAsync()
197224
{
198-
// Makes sure SA1306 is not reported for fields starting with an underscore
199225
var testCode = @"public class Foo
200226
{
201-
public string _bar = ""baz"";
227+
public string bar = ""baz"";
202228
}";
203229

204230
await this.VerifyCSharpDiagnosticAsync(testCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
205231
}
206232

207233
[Fact]
208-
public async Task TestFieldStartingWithLetterAsync()
234+
public async Task TestFieldWithAllUnderscoresAsync()
209235
{
210236
var testCode = @"public class Foo
211237
{
212-
public string bar = ""baz"";
238+
private string _ = ""bar"";
239+
private string __ = ""baz"";
240+
private string ___ = ""qux"";
213241
}";
214242

215243
await this.VerifyCSharpDiagnosticAsync(testCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
216244
}
217245

246+
[Fact]
247+
public async Task TestFieldWithTrailingUnderscoreAsync()
248+
{
249+
var testCode = @"public class Foo
250+
{
251+
private string someVar_ = ""bar"";
252+
}";
253+
254+
await this.VerifyCSharpDiagnosticAsync(testCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
255+
}
256+
257+
[Fact]
258+
public async Task TestFieldWithCodefixRenameConflictAsync()
259+
{
260+
var testCode = @"public class Foo
261+
{
262+
private string _test = ""test1"";
263+
private string _Test = ""test2"";
264+
}";
265+
266+
var fixedTestCode = @"public class Foo
267+
{
268+
private string _test = ""test1"";
269+
private string _test1 = ""test2"";
270+
}";
271+
272+
var expected = this.CSharpDiagnostic().WithArguments("_Test").WithLocation(4, 20);
273+
274+
await this.VerifyCSharpDiagnosticAsync(testCode, expected, CancellationToken.None).ConfigureAwait(false);
275+
await this.VerifyCSharpDiagnosticAsync(fixedTestCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
276+
await this.VerifyCSharpFixAsync(testCode, fixedTestCode).ConfigureAwait(false);
277+
}
278+
218279
[Fact]
219280
public async Task TestFieldPlacedInsideNativeMethodsClassAsync()
220281
{

StyleCop.Analyzers/StyleCop.Analyzers/NamingRules/SA1306FieldNamesMustBeginWithLowerCaseLetter.cs

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -114,14 +114,25 @@ private static void HandleFieldDeclaration(SyntaxNodeAnalysisContext context)
114114
}
115115

116116
string name = identifier.ValueText;
117-
if (string.IsNullOrEmpty(name) || char.IsLower(name[0]))
117+
if (string.IsNullOrEmpty(name))
118118
{
119119
continue;
120120
}
121121

122-
if (name[0] == '_')
122+
var index = 0;
123+
while ((index < name.Length) && name[index] == '_')
124+
{
125+
index++;
126+
}
127+
128+
if (index == name.Length)
129+
{
130+
// ignore fields with all underscores
131+
continue;
132+
}
133+
134+
if (char.IsLower(name, index))
123135
{
124-
// `_foo` is handled by SA1309
125136
continue;
126137
}
127138

0 commit comments

Comments
 (0)