Skip to content

Commit 4ec134a

Browse files
committed
Port pull request #1961 to stabilization
Fix SA1127 code fix indenting when additional preceding trivia is present (cherry picked from commit 2a502fd)
1 parent d1a6ffb commit 4ec134a

3 files changed

Lines changed: 87 additions & 11 deletions

File tree

StyleCop.Analyzers/StyleCop.Analyzers.CodeFixes/ReadabilityRules/SA1127CodeFixProvider.cs

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ namespace StyleCop.Analyzers.ReadabilityRules
66
using System.Collections.Generic;
77
using System.Collections.Immutable;
88
using System.Composition;
9+
using System.Linq;
910
using System.Threading;
1011
using System.Threading.Tasks;
1112
using Helpers;
@@ -88,18 +89,11 @@ private static async Task<Document> GetTransformedDocumentAsync(Document documen
8889

8990
private static string GetParentIndentation(SyntaxToken token)
9091
{
91-
var parentLine = token.Parent.Parent;
92-
var parentIndentation = string.Empty;
93-
var parentTrivia = parentLine.GetLeadingTrivia();
94-
foreach (var trivia in parentTrivia)
95-
{
96-
if (trivia.IsKind(SyntaxKind.WhitespaceTrivia))
97-
{
98-
parentIndentation += trivia.ToString();
99-
}
100-
}
92+
var parentTrivia = token.Parent.Parent.GetLeadingTrivia();
10193

102-
return parentIndentation;
94+
return parentTrivia
95+
.LastOrDefault(SyntaxKind.WhitespaceTrivia)
96+
.ToString();
10397
}
10498

10599
// This function will remove any unnecessary whitespace or end-of-line trivia from a token.

StyleCop.Analyzers/StyleCop.Analyzers.Test/ReadabilityRules/SA1127UnitTests.cs

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,66 @@ private void Method<T>(
128128
await this.VerifyCSharpFixAsync(testCode, fixedCode, cancellationToken: CancellationToken.None).ConfigureAwait(false);
129129
}
130130

131+
/// <summary>
132+
/// This is a regression test for DotNetAnalyzers/StyleCopAnalyzers#1652:
133+
/// https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/1652
134+
/// </summary>
135+
/// <returns>A <see cref="Task"/> representing the asynchronous unit test.</returns>
136+
[Fact]
137+
public async Task TestViolationWithMethodDeclarationAndXmlCommentsAsync()
138+
{
139+
var testCode = $@"
140+
class Foo
141+
{{
142+
/// <summary>Foo</summary>
143+
/// <typeparam name=""T"">The type.</typeparam>
144+
private void Method<T>() where T : class {{ }}
145+
}}";
146+
var fixedCode = $@"
147+
class Foo
148+
{{
149+
/// <summary>Foo</summary>
150+
/// <typeparam name=""T"">The type.</typeparam>
151+
private void Method<T>()
152+
where T : class
153+
{{ }}
154+
}}";
155+
var expected = this.CSharpDiagnostic().WithLocation(6, 30);
156+
await this.VerifyCSharpDiagnosticAsync(testCode, expected, CancellationToken.None).ConfigureAwait(false);
157+
await this.VerifyCSharpDiagnosticAsync(fixedCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
158+
await this.VerifyCSharpFixAsync(testCode, fixedCode, cancellationToken: CancellationToken.None).ConfigureAwait(false);
159+
}
160+
161+
/// <summary>
162+
/// This is a regression test for DotNetAnalyzers/StyleCopAnalyzers#1652:
163+
/// https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/1652
164+
/// </summary>
165+
/// <returns>A <see cref="Task"/> representing the asynchronous unit test.</returns>
166+
[Fact]
167+
public async Task TestViolationWithMethodDeclarationRegionDirectiveAsync()
168+
{
169+
var testCode = $@"
170+
class Foo
171+
{{
172+
#region Test
173+
private void Method<T>() where T : class {{ }}
174+
#endregion
175+
}}";
176+
var fixedCode = $@"
177+
class Foo
178+
{{
179+
#region Test
180+
private void Method<T>()
181+
where T : class
182+
{{ }}
183+
#endregion
184+
}}";
185+
var expected = this.CSharpDiagnostic().WithLocation(5, 30);
186+
await this.VerifyCSharpDiagnosticAsync(testCode, expected, CancellationToken.None).ConfigureAwait(false);
187+
await this.VerifyCSharpDiagnosticAsync(fixedCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
188+
await this.VerifyCSharpFixAsync(testCode, fixedCode, cancellationToken: CancellationToken.None).ConfigureAwait(false);
189+
}
190+
131191
[Fact]
132192
public async Task TestViolationWithExpressionBodiedMethodDeclarationAsync()
133193
{

StyleCop.Analyzers/StyleCop.Analyzers/LinqHelpers/SyntaxTriviaListEnumerable.cs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ namespace System.Linq
55
{
66
using Collections.Generic;
77
using Microsoft.CodeAnalysis;
8+
using Microsoft.CodeAnalysis.CSharp;
89

910
/// <summary>
1011
/// This class supports a subset of LINQ operations on <see cref="SyntaxTriviaList"/> without requiring boxing of
@@ -93,5 +94,26 @@ internal static bool All(this SyntaxTriviaList list, Func<SyntaxTrivia, bool> pr
9394

9495
return true;
9596
}
97+
98+
/// <summary>
99+
/// Returns the last trivia of a specified kind in a trivia list.
100+
/// </summary>
101+
/// <param name="list">The trivia list.</param>
102+
/// <param name="kind">The syntax kind.</param>
103+
/// <returns>The last <see cref="SyntaxTrivia"/> in <paramref name="list"/> with the specified
104+
/// <paramref name="kind"/>; otherwise, a default <see cref="SyntaxTrivia"/> instance if no matching trivia was
105+
/// found.</returns>
106+
internal static SyntaxTrivia LastOrDefault(this SyntaxTriviaList list, SyntaxKind kind)
107+
{
108+
foreach (var trivia in list.Reverse())
109+
{
110+
if (trivia.IsKind(kind))
111+
{
112+
return trivia;
113+
}
114+
}
115+
116+
return default(SyntaxTrivia);
117+
}
96118
}
97119
}

0 commit comments

Comments
 (0)