Skip to content

Commit 1124126

Browse files
committed
Avoid storing code fixes in the diagnostic property bags
1 parent 75c90a0 commit 1124126

File tree

5 files changed

+66
-71
lines changed

5 files changed

+66
-71
lines changed

src/CodeStyle/CSharp/Tests/FormattingAnalyzerTests.cs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,5 +165,34 @@ void MyMethod()
165165

166166
await Verify.VerifyCodeFixAsync(testCode, fixedCode);
167167
}
168+
169+
[Fact]
170+
public async Task TestIncrementalFixesFullLine()
171+
{
172+
var testCode = @"
173+
class MyClass
174+
{
175+
int Property1$${$$get;$$set;$$}
176+
int Property2$${$$get;$$}
177+
}
178+
";
179+
var fixedCode = @"
180+
class MyClass
181+
{
182+
int Property1 { get; set; }
183+
int Property2 { get; }
184+
}
185+
";
186+
187+
await new CSharpCodeFixTest<CSharpFormattingAnalyzer, FormattingCodeFixProvider, XUnitVerifier>
188+
{
189+
TestCode = testCode,
190+
FixedCode = fixedCode,
191+
192+
// Each application of a single code fix covers all diagnostics on the same line. In total, two lines
193+
// require changes so the number of incremental iterations is exactly 2.
194+
NumberOfIncrementalIterations = 2,
195+
}.RunAsync();
196+
}
168197
}
169198
}

src/CodeStyle/Core/CodeFixes/AbstractFormattingAnalyzerImpl.cs

Lines changed: 1 addition & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
22

33
using System;
4-
using System.Collections.Immutable;
54
using System.IO;
65
using Microsoft.CodeAnalysis.Diagnostics;
76
using Microsoft.CodeAnalysis.Formatting;
@@ -13,11 +12,6 @@ namespace Microsoft.CodeAnalysis.CodeStyle
1312
{
1413
internal abstract class AbstractFormattingAnalyzerImpl
1514
{
16-
public static readonly string ReplaceTextKey = nameof(ReplaceTextKey);
17-
18-
public static readonly ImmutableDictionary<string, string> RemoveTextProperties =
19-
ImmutableDictionary.Create<string, string>().Add(ReplaceTextKey, "");
20-
2115
private readonly DiagnosticDescriptor _descriptor;
2216

2317
protected AbstractFormattingAnalyzerImpl(DiagnosticDescriptor descriptor)
@@ -87,23 +81,13 @@ private void AnalyzeSyntaxTree(SyntaxTreeAnalysisContext context, Workspace work
8781
throw new InvalidOperationException("This program location is thought to be unreachable.");
8882
}
8983

90-
ImmutableDictionary<string, string> properties;
91-
if (change.NewText.Length == 0)
92-
{
93-
properties = RemoveTextProperties;
94-
}
95-
else
96-
{
97-
properties = ImmutableDictionary.Create<string, string>().Add(ReplaceTextKey, change.NewText);
98-
}
99-
10084
var location = Location.Create(tree, change.Span);
10185
context.ReportDiagnostic(DiagnosticHelper.Create(
10286
_descriptor,
10387
location,
10488
ReportDiagnostic.Default,
10589
additionalLocations: null,
106-
properties));
90+
properties: null));
10791
}
10892
}
10993
}

src/CodeStyle/Core/CodeFixes/FormattingCodeFixProvider.cs

Lines changed: 19 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
22

3-
using System.Collections.Generic;
43
using System.Collections.Immutable;
54
using System.Composition;
65
using System.Threading;
@@ -26,34 +25,34 @@ public override FixAllProvider GetFixAllProvider()
2625

2726
public override Task RegisterCodeFixesAsync(CodeFixContext context)
2827
{
29-
context.RegisterCodeFix(
30-
CodeAction.Create(
31-
CodeStyleFixesResources.Formatting_analyzer_code_fix,
32-
c => FixOneAsync(context.Document, context.Diagnostics, c),
33-
nameof(FormattingCodeFixProvider)),
34-
context.Diagnostics);
28+
foreach (var diagnostic in context.Diagnostics)
29+
{
30+
context.RegisterCodeFix(
31+
CodeAction.Create(
32+
CodeStyleFixesResources.Formatting_analyzer_code_fix,
33+
c => FixOneAsync(context.Document, diagnostic, c),
34+
nameof(FormattingCodeFixProvider)),
35+
diagnostic);
36+
}
3537

3638
return Task.CompletedTask;
3739
}
3840

39-
protected async Task<Document> FixOneAsync(Document document, ImmutableArray<Diagnostic> diagnostics, CancellationToken cancellationToken)
41+
protected async Task<Document> FixOneAsync(Document document, Diagnostic diagnostic, CancellationToken cancellationToken)
4042
{
4143
var tree = await document.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false);
42-
var text = await document.GetTextAsync(cancellationToken).ConfigureAwait(false);
43-
var changes = new List<TextChange>();
44-
foreach (var diagnostic in diagnostics)
45-
{
46-
if (!diagnostic.Properties.TryGetValue(AbstractFormattingAnalyzerImpl.ReplaceTextKey, out var replacement))
47-
{
48-
continue;
49-
}
5044

51-
changes.Add(new TextChange(diagnostic.Location.SourceSpan, replacement));
52-
}
45+
// The span to format is the full line(s) containing the diagnostic
46+
var text = await document.GetTextAsync(cancellationToken).ConfigureAwait(false);
47+
var diagnosticSpan = diagnostic.Location.SourceSpan;
48+
var diagnosticLinePositionSpan = text.Lines.GetLinePositionSpan(diagnosticSpan);
49+
var spanToFormat = TextSpan.FromBounds(
50+
text.Lines[diagnosticLinePositionSpan.Start.Line].Start,
51+
text.Lines[diagnosticLinePositionSpan.End.Line].End);
5352

54-
changes.Sort((left, right) => left.Span.Start.CompareTo(right.Span.Start));
5553

56-
return document.WithText(text.WithChanges(changes));
54+
var options = await document.GetOptionsAsync(cancellationToken).ConfigureAwait(false);
55+
return await Formatter.FormatAsync(document, spanToFormat, options, cancellationToken).ConfigureAwait(false);
5756
}
5857

5958
private class FixAll : DocumentBasedFixAllProvider

src/Features/Core/Portable/Formatting/FormattingCodeFixProvider.cs

Lines changed: 16 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
22

33
using System;
4-
using System.Collections.Generic;
54
using System.Collections.Immutable;
65
using System.Composition;
76
using System.Threading;
@@ -23,31 +22,31 @@ public override ImmutableArray<string> FixableDiagnosticIds
2322

2423
public override Task RegisterCodeFixesAsync(CodeFixContext context)
2524
{
26-
context.RegisterCodeFix(
27-
new MyCodeAction(c => FixOneAsync(context.Document, context.Diagnostics, c)),
28-
context.Diagnostics);
25+
foreach (var diagnostic in context.Diagnostics)
26+
{
27+
context.RegisterCodeFix(
28+
new MyCodeAction(c => FixOneAsync(context.Document, diagnostic, c)),
29+
diagnostic);
30+
}
2931

3032
return Task.CompletedTask;
3133
}
3234

33-
protected async Task<Document> FixOneAsync(Document document, ImmutableArray<Diagnostic> diagnostics, CancellationToken cancellationToken)
35+
protected async Task<Document> FixOneAsync(Document document, Diagnostic diagnostic, CancellationToken cancellationToken)
3436
{
3537
var tree = await document.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false);
36-
var text = await document.GetTextAsync(cancellationToken).ConfigureAwait(false);
37-
var changes = new List<TextChange>();
38-
foreach (var diagnostic in diagnostics)
39-
{
40-
if (!diagnostic.Properties.TryGetValue(FormattingDiagnosticAnalyzer.ReplaceTextKey, out var replacement))
41-
{
42-
continue;
43-
}
4438

45-
changes.Add(new TextChange(diagnostic.Location.SourceSpan, replacement));
46-
}
39+
// The span to format is the full line(s) containing the diagnostic
40+
var text = await document.GetTextAsync(cancellationToken).ConfigureAwait(false);
41+
var diagnosticSpan = diagnostic.Location.SourceSpan;
42+
var diagnosticLinePositionSpan = text.Lines.GetLinePositionSpan(diagnosticSpan);
43+
var spanToFormat = TextSpan.FromBounds(
44+
text.Lines[diagnosticLinePositionSpan.Start.Line].Start,
45+
text.Lines[diagnosticLinePositionSpan.End.Line].End);
4746

48-
changes.Sort((left, right) => left.Span.Start.CompareTo(right.Span.Start));
4947

50-
return document.WithText(text.WithChanges(changes));
48+
var options = await document.GetOptionsAsync(cancellationToken).ConfigureAwait(false);
49+
return await Formatter.FormatAsync(document, spanToFormat, options, cancellationToken).ConfigureAwait(false);
5150
}
5251

5352
protected override async Task FixAllAsync(Document document, ImmutableArray<Diagnostic> diagnostics, SyntaxEditor editor, CancellationToken cancellationToken)

src/Features/Core/Portable/Formatting/FormattingDiagnosticAnalyzer.cs

Lines changed: 1 addition & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
22

3-
using System.Collections.Immutable;
43
using Microsoft.CodeAnalysis.CodeStyle;
54
using Microsoft.CodeAnalysis.Diagnostics;
65
using Microsoft.CodeAnalysis.Text;
@@ -12,11 +11,6 @@ namespace Microsoft.CodeAnalysis.Formatting
1211
internal class FormattingDiagnosticAnalyzer
1312
: AbstractCodeStyleDiagnosticAnalyzer
1413
{
15-
public static readonly string ReplaceTextKey = nameof(ReplaceTextKey);
16-
17-
public static readonly ImmutableDictionary<string, string> RemoveTextProperties =
18-
ImmutableDictionary.Create<string, string>().Add(ReplaceTextKey, "");
19-
2014
public FormattingDiagnosticAnalyzer()
2115
: base(
2216
IDEDiagnosticIds.FormattingDiagnosticId,
@@ -91,23 +85,13 @@ private void AnalyzeSyntaxTree(SyntaxTreeAnalysisContext context)
9185
throw ExceptionUtilities.Unreachable;
9286
}
9387

94-
ImmutableDictionary<string, string> properties;
95-
if (change.NewText.Length == 0)
96-
{
97-
properties = RemoveTextProperties;
98-
}
99-
else
100-
{
101-
properties = ImmutableDictionary.Create<string, string>().Add(ReplaceTextKey, change.NewText);
102-
}
103-
10488
var location = Location.Create(tree, change.Span);
10589
context.ReportDiagnostic(DiagnosticHelper.Create(
10690
Descriptor,
10791
location,
10892
ReportDiagnostic.Default,
10993
additionalLocations: null,
110-
properties));
94+
properties: null));
11195
}
11296
}
11397
}

0 commit comments

Comments
 (0)