Skip to content

Commit 73afee8

Browse files
committed
Merge pull request #1880 from NikolayIT/Fix1878
Fix #1878 (SA1133CodeFixProvider crash with System.InvalidCastException)
2 parents 9876971 + 972c393 commit 73afee8

2 files changed

Lines changed: 118 additions & 2 deletions

File tree

StyleCop.Analyzers/StyleCop.Analyzers.CodeFixes/ReadabilityRules/SA1133CodeFixProvider.cs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,10 @@ namespace StyleCop.Analyzers.ReadabilityRules
1818
/// <summary>
1919
/// Implements a code fix for <see cref="SA1133DoNotCombineAttributes"/>.
2020
/// </summary>
21+
/// <remarks>
22+
/// The SA1133 code fix adds the new lines to make sure that it doesn't immediately introduces a SA1134 after code fixing,
23+
/// but it will not / should not attempt to fix any preexisting SA1134 cases.
24+
/// </remarks>
2125
[ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(SA1133CodeFixProvider))]
2226
[Shared]
2327
internal class SA1133CodeFixProvider : CodeFixProvider
@@ -51,8 +55,9 @@ public override Task RegisterCodeFixesAsync(CodeFixContext context)
5155
private static async Task<Document> GetTransformedDocumentAsync(Document document, Diagnostic diagnostic, CancellationToken cancellationToken)
5256
{
5357
var syntaxRoot = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
54-
var violatingAttribute = (AttributeSyntax)syntaxRoot.FindNode(diagnostic.Location.SourceSpan).Parent;
55-
var attributeList = (AttributeListSyntax)violatingAttribute.Parent;
58+
var nodeInSourceSpan = syntaxRoot.FindNode(diagnostic.Location.SourceSpan, getInnermostNodeForTie: true);
59+
AttributeListSyntax attributeList = nodeInSourceSpan.FirstAncestorOrSelf<AttributeListSyntax>();
60+
5661
var newAttributeLists = new List<AttributeListSyntax>();
5762

5863
var indentationOptions = IndentationOptions.FromDocument(document);

StyleCop.Analyzers/StyleCop.Analyzers.Test/ReadabilityRules/SA1133UnitTests.cs

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,117 @@ public void TestMethod()
165165
await this.VerifyCSharpFixAsync(testCode, fixedTestCode).ConfigureAwait(false);
166166
}
167167

168+
/// <summary>
169+
/// Verifies that a combination of attributes without parameters will produce the required diagnostics.
170+
/// </summary>
171+
/// <param name="before">The code part before the code fix.</param>
172+
/// <param name="after">The code part after the code fix.</param>
173+
/// <param name="line">The line on which the diagnostic is expected.</param>
174+
/// <param name="column">The column on which the diagnostic is expected.</param>
175+
/// <returns>A <see cref="Task"/> representing the asynchronous unit test.</returns>
176+
[Theory]
177+
[InlineData("[Foo]\r\n[Bar, Car]", "[Foo]\r\n[Bar]\r\n[Car]", 3, 7)]
178+
[InlineData("[Foo, Bar]\r\n[Car]", "[Foo]\r\n[Bar]\r\n[Car]", 2, 7)]
179+
[InlineData("[Foo]\r\n[Bar, Car]\r\n[Ear]", "[Foo]\r\n[Bar]\r\n[Car]\r\n[Ear]", 3, 7)]
180+
public async Task VerifyAttributeCombinationsWithoutParametersAreHandledCorrectlyAsync(string before, string after, int line, int column)
181+
{
182+
var testCode = @"using System;
183+
{0}
184+
public class TestClass
185+
{{
186+
}}
187+
188+
public class Foo : Attribute
189+
{{
190+
}}
191+
192+
public class Bar : Attribute
193+
{{
194+
}}
195+
196+
public class Car : Attribute
197+
{{
198+
}}
199+
200+
public class Ear : Attribute
201+
{{
202+
}}";
203+
var codeBefore = string.Format(testCode, before);
204+
var codeAfter = string.Format(testCode, after);
205+
206+
DiagnosticResult[] expected =
207+
{
208+
this.CSharpDiagnostic().WithLocation(line, column)
209+
};
210+
211+
await this.VerifyCSharpDiagnosticAsync(codeBefore, expected, CancellationToken.None).ConfigureAwait(false);
212+
await this.VerifyCSharpDiagnosticAsync(codeAfter, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
213+
await this.VerifyCSharpFixAsync(codeBefore, codeAfter).ConfigureAwait(false);
214+
}
215+
216+
/// <summary>
217+
/// Regression test for issue 1878 (SA1133CodeFixProvider crash), https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/1878
218+
/// Fixing exception "Unable to cast object of type 'Microsoft.CodeAnalysis.CSharp.Syntax.AttributeListSyntax' to type 'Microsoft.CodeAnalysis.CSharp.Syntax.AttributeSyntax'."
219+
/// </summary>
220+
/// <returns>A <see cref="Task"/> representing the asynchronous unit test.</returns>
221+
[Fact]
222+
public async Task TestRegressionIssue1878Async()
223+
{
224+
var testCode = @"namespace Stylecop_rc1_bug_repro
225+
{
226+
using System;
227+
228+
internal class Program
229+
{
230+
[Foo, Bar]
231+
private static void Main(string[] args)
232+
{
233+
}
234+
}
235+
236+
internal class FooAttribute : Attribute
237+
{
238+
}
239+
240+
internal class BarAttribute : Attribute
241+
{
242+
}
243+
}
244+
";
245+
246+
var fixedTestCode = @"namespace Stylecop_rc1_bug_repro
247+
{
248+
using System;
249+
250+
internal class Program
251+
{
252+
[Foo]
253+
[Bar]
254+
private static void Main(string[] args)
255+
{
256+
}
257+
}
258+
259+
internal class FooAttribute : Attribute
260+
{
261+
}
262+
263+
internal class BarAttribute : Attribute
264+
{
265+
}
266+
}
267+
";
268+
269+
DiagnosticResult[] expected =
270+
{
271+
this.CSharpDiagnostic().WithLocation(7, 15)
272+
};
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+
168279
/// <inheritdoc/>
169280
protected override CodeFixProvider GetCSharpCodeFixProvider()
170281
{

0 commit comments

Comments
 (0)