Skip to content

Commit 057eede

Browse files
committed
Merge pull request #743 from vweijsters/fix-625
Added WithSpan method to DiagnosticResult
2 parents cb11b4d + 1845e69 commit 057eede

25 files changed

Lines changed: 178 additions & 285 deletions

StyleCop.Analyzers/StyleCop.Analyzers.Test/DocumentationRules/SA1600UnitTests.cs

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -31,16 +31,10 @@ public async Task TestRegressionMethodGlobalNamespaceAsync(string code, int colu
3131
3232
{code}";
3333

34-
var expected = new[]
34+
DiagnosticResult[] expected =
3535
{
36-
new DiagnosticResult
37-
{
38-
Id = "CS0116",
39-
Severity = DiagnosticSeverity.Error,
40-
Locations = new[] { new DiagnosticResultLocation("Test0.cs", 4, column) },
41-
Message = "A namespace cannot directly contain members such as fields or methods"
42-
},
43-
this.CSharpDiagnostic().WithLocation(4, column)
36+
this.CSharpCompilerError("CS0116", "A namespace cannot directly contain members such as fields or methods").WithLocation(4, column),
37+
this.CSharpDiagnostic().WithLocation(4, column),
4438
};
4539

4640
await this.VerifyCSharpDiagnosticAsync(testCode, expected, CancellationToken.None).ConfigureAwait(false);

StyleCop.Analyzers/StyleCop.Analyzers.Test/DocumentationRules/SA1606UnitTests.cs

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -777,12 +777,9 @@ public string MethodName
777777
}
778778
";
779779

780-
var expected = new DiagnosticResult
780+
DiagnosticResult[] expected =
781781
{
782-
Id = "CS1002",
783-
Severity = DiagnosticSeverity.Error,
784-
Locations = new[] { new DiagnosticResultLocation("Test0.cs", 5, 29) },
785-
Message = "; expected"
782+
this.CSharpCompilerError("CS1002", "; expected").WithLocation(5, 29),
786783
};
787784

788785
await this.VerifyCSharpDiagnosticAsync(testCode, expected, CancellationToken.None).ConfigureAwait(false);

StyleCop.Analyzers/StyleCop.Analyzers.Test/Helpers/DiagnosticResult.cs

Lines changed: 59 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,18 @@ namespace TestHelper
55
{
66
using System;
77
using Microsoft.CodeAnalysis;
8+
using Microsoft.CodeAnalysis.Text;
89

910
/// <summary>
1011
/// Structure that stores information about a <see cref="Diagnostic"/> appearing in a source.
1112
/// </summary>
1213
public struct DiagnosticResult
1314
{
15+
private const string DefaultPath = "Test0.cs";
16+
1417
private static readonly object[] EmptyArguments = new object[0];
1518

16-
private DiagnosticResultLocation[] locations;
19+
private FileLinePositionSpan[] spans;
1720
private string message;
1821

1922
public DiagnosticResult(DiagnosticDescriptor descriptor)
@@ -24,21 +27,16 @@ public DiagnosticResult(DiagnosticDescriptor descriptor)
2427
this.MessageFormat = descriptor.MessageFormat;
2528
}
2629

27-
public DiagnosticResultLocation[] Locations
30+
public FileLinePositionSpan[] Spans
2831
{
2932
get
3033
{
31-
if (this.locations == null)
32-
{
33-
this.locations = new DiagnosticResultLocation[] { };
34-
}
35-
36-
return this.locations;
34+
return this.spans ?? (this.spans = new FileLinePositionSpan[] { });
3735
}
3836

3937
set
4038
{
41-
this.locations = value;
39+
this.spans = value;
4240
}
4341
}
4442

@@ -87,27 +85,11 @@ public object[] MessageArguments
8785
set;
8886
}
8987

90-
public string Path
91-
{
92-
get
93-
{
94-
return this.Locations.Length > 0 ? this.Locations[0].Path : string.Empty;
95-
}
96-
}
97-
98-
public int Line
88+
public bool HasLocation
9989
{
10090
get
10191
{
102-
return this.Locations.Length > 0 ? this.Locations[0].Line : -1;
103-
}
104-
}
105-
106-
public int Column
107-
{
108-
get
109-
{
110-
return this.Locations.Length > 0 ? this.Locations[0].Column : -1;
92+
return (this.spans != null) && (this.spans.Length > 0);
11193
}
11294
}
11395

@@ -127,27 +109,69 @@ public DiagnosticResult WithMessageFormat(LocalizableString messageFormat)
127109

128110
public DiagnosticResult WithLocation(int line, int column)
129111
{
130-
return this.WithLocation("Test0.cs", line, column);
112+
return this.WithLocation(DefaultPath, line, column);
131113
}
132114

133115
public DiagnosticResult WithLocation(string path, int line, int column)
134116
{
135-
DiagnosticResult result = this;
136-
Array.Resize(ref result.locations, (result.locations?.Length ?? 0) + 1);
137-
result.locations[result.locations.Length - 1] = new DiagnosticResultLocation(path, line, column);
138-
return result;
117+
var linePosition = new LinePosition(line, column);
118+
119+
return this.AppendSpan(new FileLinePositionSpan(path, linePosition, linePosition));
120+
}
121+
122+
public DiagnosticResult WithSpan(int startLine, int startColumn, int endLine, int endColumn)
123+
{
124+
return this.WithSpan(DefaultPath, startLine, startColumn, endLine, endColumn);
125+
}
126+
127+
public DiagnosticResult WithSpan(string path, int startLine, int startColumn, int endLine, int endColumn)
128+
{
129+
return this.AppendSpan(new FileLinePositionSpan(path, new LinePosition(startLine, startColumn), new LinePosition(endLine, endColumn)));
139130
}
140131

141132
public DiagnosticResult WithLineOffset(int offset)
142133
{
143134
DiagnosticResult result = this;
144-
Array.Resize(ref result.locations, result.locations?.Length ?? 0);
145-
for (int i = 0; i < result.locations.Length; i++)
135+
Array.Resize(ref result.spans, result.spans?.Length ?? 0);
136+
for (int i = 0; i < result.spans.Length; i++)
146137
{
147-
result.locations[i].Line += offset;
138+
var newStartLinePosition = new LinePosition(result.spans[i].StartLinePosition.Line + offset, result.spans[i].StartLinePosition.Character);
139+
var newEndLinePosition = new LinePosition(result.spans[i].EndLinePosition.Line + offset, result.spans[i].EndLinePosition.Character);
140+
141+
result.spans[i] = new FileLinePositionSpan(result.spans[i].Path, newStartLinePosition, newEndLinePosition);
148142
}
149143

150144
return result;
151145
}
146+
147+
private DiagnosticResult AppendSpan(FileLinePositionSpan span)
148+
{
149+
FileLinePositionSpan[] newSpans;
150+
151+
if (this.spans != null)
152+
{
153+
newSpans = new FileLinePositionSpan[this.spans.Length + 1];
154+
Array.Copy(this.spans, newSpans, this.spans.Length);
155+
newSpans[this.spans.Length] = span;
156+
}
157+
else
158+
{
159+
newSpans = new FileLinePositionSpan[1]
160+
{
161+
span,
162+
};
163+
}
164+
165+
// clone the object, so that the fluent syntax will work on immutable objects.
166+
return new DiagnosticResult
167+
{
168+
Id = this.Id,
169+
Message = this.message,
170+
MessageFormat = this.MessageFormat,
171+
MessageArguments = this.MessageArguments,
172+
Severity = this.Severity,
173+
Spans = newSpans,
174+
};
175+
}
152176
}
153177
}

StyleCop.Analyzers/StyleCop.Analyzers.Test/Helpers/DiagnosticVerifier.Helper.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,16 @@ protected DiagnosticResult CSharpDiagnostic(DiagnosticDescriptor descriptor)
209209
return new DiagnosticResult(descriptor);
210210
}
211211

212+
protected DiagnosticResult CSharpCompilerError(string errorIdentifier, string message)
213+
{
214+
return new DiagnosticResult
215+
{
216+
Id = errorIdentifier,
217+
Severity = DiagnosticSeverity.Error,
218+
Message = message,
219+
};
220+
}
221+
212222
/// <summary>
213223
/// Create a project using the input strings as sources.
214224
/// </summary>

StyleCop.Analyzers/StyleCop.Analyzers.Test/LayoutRules/SA1504UnitTests.cs

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -291,13 +291,7 @@ public int Prop
291291
}";
292292
DiagnosticResult[] expected =
293293
{
294-
new DiagnosticResult()
295-
{
296-
Id = "CS0501",
297-
Message = "'Foo.Prop.get' must declare a body because it is not marked abstract, extern, or partial",
298-
Severity = DiagnosticSeverity.Error,
299-
Locations = new[] { new DiagnosticResultLocation("Test0.cs", 6, 9) }
300-
}
294+
this.CSharpCompilerError("CS0501", "'Foo.Prop.get' must declare a body because it is not marked abstract, extern, or partial").WithLocation(6, 9),
301295
};
302296

303297
await this.VerifyCSharpDiagnosticAsync(testCode, expected, CancellationToken.None).ConfigureAwait(false);

StyleCop.Analyzers/StyleCop.Analyzers.Test/LayoutRules/SA1508UnitTests.cs

Lines changed: 4 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -851,34 +851,10 @@ public class TestClass /
851851

852852
DiagnosticResult[] expected =
853853
{
854-
new DiagnosticResult
855-
{
856-
Id = "CS1022",
857-
Severity = DiagnosticSeverity.Error,
858-
Locations = new[] { new DiagnosticResultLocation("Test0.cs", 3, 28) },
859-
Message = "Type or namespace definition, or end-of-file expected"
860-
},
861-
new DiagnosticResult
862-
{
863-
Id = "CS1513",
864-
Severity = DiagnosticSeverity.Error,
865-
Locations = new[] { new DiagnosticResultLocation("Test0.cs", 3, 28) },
866-
Message = "} expected"
867-
},
868-
new DiagnosticResult
869-
{
870-
Id = "CS1514",
871-
Severity = DiagnosticSeverity.Error,
872-
Locations = new[] { new DiagnosticResultLocation("Test0.cs", 3, 28) },
873-
Message = "{ expected"
874-
},
875-
new DiagnosticResult
876-
{
877-
Id = "CS1022",
878-
Severity = DiagnosticSeverity.Error,
879-
Locations = new[] { new DiagnosticResultLocation("Test0.cs", 6, 1) },
880-
Message = "Type or namespace definition, or end-of-file expected"
881-
}
854+
this.CSharpCompilerError("CS1022", "Type or namespace definition, or end-of-file expected").WithLocation(3, 28),
855+
this.CSharpCompilerError("CS1513", "} expected").WithLocation(3, 28),
856+
this.CSharpCompilerError("CS1514", "{ expected").WithLocation(3, 28),
857+
this.CSharpCompilerError("CS1022", "Type or namespace definition, or end-of-file expected").WithLocation(6, 1),
882858
};
883859

884860
await this.VerifyCSharpDiagnosticAsync(testCode, expected, CancellationToken.None).ConfigureAwait(false);

StyleCop.Analyzers/StyleCop.Analyzers.Test/MaintainabilityRules/DebugMessagesUnitTestsBase.cs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ namespace StyleCop.Analyzers.Test.MaintainabilityRules
99
using System.Threading;
1010
using System.Threading.Tasks;
1111
using Microsoft.CodeAnalysis;
12+
using Microsoft.CodeAnalysis.Text;
1213
using TestHelper;
1314
using Xunit;
1415

@@ -41,14 +42,15 @@ public async Task TestConstantMessage_Field_PassExpressionAsync()
4142
[Fact]
4243
public async Task TestConstantMessage_Field_PassWrongTypeAsync()
4344
{
45+
LinePosition linePosition = new LinePosition(4, 28);
4446
DiagnosticResult[] expected =
4547
{
4648
new DiagnosticResult
4749
{
4850
Id = "CS0029",
4951
Message = "Cannot implicitly convert type 'int' to 'string'",
5052
Severity = DiagnosticSeverity.Error,
51-
Locations = new[] { new DiagnosticResultLocation("Test0.cs", 4, 28) }
53+
Spans = new[] { new FileLinePositionSpan("Test0.cs", linePosition, linePosition) }
5254
}
5355
};
5456

@@ -70,14 +72,15 @@ public async Task TestConstantMessage_Local_PassExpressionAsync()
7072
[Fact]
7173
public async Task TestConstantMessage_Local_PassWrongTypeAsync()
7274
{
75+
LinePosition linePosition = new LinePosition(6, 32);
7376
DiagnosticResult[] expected =
7477
{
7578
new DiagnosticResult
7679
{
7780
Id = "CS0029",
7881
Message = "Cannot implicitly convert type 'int' to 'string'",
7982
Severity = DiagnosticSeverity.Error,
80-
Locations = new[] { new DiagnosticResultLocation("Test0.cs", 6, 32) }
83+
Spans = new[] { new FileLinePositionSpan("Test0.cs", linePosition, linePosition) }
8184
}
8285
};
8386

@@ -99,14 +102,15 @@ public async Task TestConstantMessage_Inline_PassExpressionAsync()
99102
[Fact]
100103
public async Task TestConstantMessage_Inline_PassWrongTypeAsync()
101104
{
105+
LinePosition linePosition = new LinePosition(6, 16 + this.MethodName.Length + this.InitialArguments.Sum(i => i.Length + ", ".Length));
102106
DiagnosticResult[] expected =
103107
{
104108
new DiagnosticResult
105109
{
106110
Id = "CS1503",
107111
Message = $"Argument {1 + this.InitialArguments.Count()}: cannot convert from 'int' to 'string'",
108112
Severity = DiagnosticSeverity.Error,
109-
Locations = new[] { new DiagnosticResultLocation("Test0.cs", 6, 16 + this.MethodName.Length + this.InitialArguments.Sum(i => i.Length + ", ".Length)) }
113+
Spans = new[] { new FileLinePositionSpan("Test0.cs", linePosition, linePosition) }
110114
}
111115
};
112116

StyleCop.Analyzers/StyleCop.Analyzers.Test/MaintainabilityRules/SA1404UnitTests.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ namespace StyleCop.Analyzers.Test.MaintainabilityRules
99
using Microsoft.CodeAnalysis;
1010
using Microsoft.CodeAnalysis.CodeFixes;
1111
using Microsoft.CodeAnalysis.Diagnostics;
12+
using Microsoft.CodeAnalysis.Text;
1213
using StyleCop.Analyzers.MaintainabilityRules;
1314
using TestHelper;
1415
using Xunit;
@@ -338,6 +339,7 @@ public void Bar()
338339
}
339340
}";
340341

342+
var expectedLinePosition = new LinePosition(4, 82);
341343
DiagnosticResult[] expected =
342344
{
343345
this.CSharpDiagnostic().WithLocation(4, 66),
@@ -346,7 +348,7 @@ public void Bar()
346348
Id = "CS0029",
347349
Message = "Cannot implicitly convert type 'int' to 'string'",
348350
Severity = DiagnosticSeverity.Error,
349-
Locations = new[] { new DiagnosticResultLocation("Test0.cs", 4, 82) }
351+
Spans = new[] { new FileLinePositionSpan("Test0.cs", expectedLinePosition, expectedLinePosition) }
350352
}
351353
};
352354
await this.VerifyCSharpDiagnosticAsync(testCode, expected, CancellationToken.None).ConfigureAwait(false);

StyleCop.Analyzers/StyleCop.Analyzers.Test/OrderingRules/SA1201UnitTests.cs

Lines changed: 19 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,12 @@ namespace StyleCop.Analyzers.Test.OrderingRules
66
using System.Collections.Generic;
77
using System.Threading;
88
using System.Threading.Tasks;
9+
910
using Microsoft.CodeAnalysis;
1011
using Microsoft.CodeAnalysis.CodeFixes;
1112
using Microsoft.CodeAnalysis.Diagnostics;
13+
using Microsoft.CodeAnalysis.Text;
14+
1215
using StyleCop.Analyzers.OrderingRules;
1316
using TestHelper;
1417
using Xunit;
@@ -270,22 +273,24 @@ public string
270273
";
271274

272275
// We don't care about the syntax errors.
276+
var expectedLinePosition1 = new LinePosition(5, 5);
277+
var expectedLinePosition2 = new LinePosition(6, 1);
273278
var expected = new[]
274279
{
275-
new DiagnosticResult
276-
{
277-
Id = "CS1585",
278-
Message = "Member modifier 'public' must precede the member type and name",
279-
Severity = DiagnosticSeverity.Error,
280-
Locations = new[] { new DiagnosticResultLocation("Test0.cs", 5, 5) }
281-
},
282-
new DiagnosticResult
283-
{
284-
Id = "CS1519",
285-
Message = "Invalid token '}' in class, struct, or interface member declaration",
286-
Severity = DiagnosticSeverity.Error,
287-
Locations = new[] { new DiagnosticResultLocation("Test0.cs", 6, 1) }
288-
}
280+
new DiagnosticResult
281+
{
282+
Id = "CS1585",
283+
Message = "Member modifier 'public' must precede the member type and name",
284+
Severity = DiagnosticSeverity.Error,
285+
Spans = new[] { new FileLinePositionSpan("Test0.cs", expectedLinePosition1, expectedLinePosition1) }
286+
},
287+
new DiagnosticResult
288+
{
289+
Id = "CS1519",
290+
Message = "Invalid token '}' in class, struct, or interface member declaration",
291+
Severity = DiagnosticSeverity.Error,
292+
Spans = new[] { new FileLinePositionSpan("Test0.cs", expectedLinePosition2, expectedLinePosition2) }
293+
}
289294
};
290295

291296
await this.VerifyCSharpDiagnosticAsync(testCode, expected, CancellationToken.None).ConfigureAwait(false);

0 commit comments

Comments
 (0)