Skip to content

Commit 026253a

Browse files
committed
Include SA1027 code fix behavior with the diagnostics
This change increases the overall flexibility of the SA1027 code fix to meet the demands of future applications.
1 parent c372210 commit 026253a

2 files changed

Lines changed: 50 additions & 22 deletions

File tree

StyleCop.Analyzers/StyleCop.Analyzers.CodeFixes/SpacingRules/SA1027CodeFixProvider.cs

Lines changed: 32 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -62,11 +62,17 @@ private static TextChange FixDiagnostic(IndentationSettings indentationSettings,
6262
TextSpan span = diagnostic.Location.SourceSpan;
6363

6464
TextLine startLine = sourceText.Lines.GetLineFromPosition(span.Start);
65-
bool useTabs = indentationSettings.UseTabs && span.Start == startLine.Start;
65+
66+
bool useTabs = false;
67+
string behavior;
68+
if (diagnostic.Properties.TryGetValue(SA1027TabsMustNotBeUsed.BehaviorKey, out behavior))
69+
{
70+
useTabs = behavior == SA1027TabsMustNotBeUsed.ConvertToTabsBehavior;
71+
}
72+
6673
string text = sourceText.ToString(TextSpan.FromBounds(startLine.Start, span.End));
6774
StringBuilder replacement = StringBuilderPool.Allocate();
6875
int spaceCount = 0;
69-
bool encounteredNonWhitespace = false;
7076
int column = 0;
7177
for (int i = 0; i < text.Length; i++)
7278
{
@@ -76,16 +82,18 @@ private static TextChange FixDiagnostic(IndentationSettings indentationSettings,
7682
var offsetWithinTabColumn = column % indentationSettings.TabSize;
7783
var tabWidth = indentationSettings.TabSize - offsetWithinTabColumn;
7884

79-
if (useTabs && !encounteredNonWhitespace)
80-
{
81-
// We already know indentation started at the beginning of the line
82-
replacement.Length = replacement.Length - spaceCount;
83-
replacement.Append('\t');
84-
spaceCount = 0;
85-
}
86-
else if (i >= span.Start - startLine.Start)
85+
if (i >= span.Start - startLine.Start)
8786
{
88-
replacement.Append(' ', tabWidth);
87+
if (useTabs)
88+
{
89+
replacement.Length = replacement.Length - spaceCount;
90+
replacement.Append('\t');
91+
spaceCount = 0;
92+
}
93+
else
94+
{
95+
replacement.Append(' ', tabWidth);
96+
}
8997
}
9098

9199
column += tabWidth;
@@ -98,25 +106,31 @@ private static TextChange FixDiagnostic(IndentationSettings indentationSettings,
98106
if (c == ' ')
99107
{
100108
spaceCount++;
101-
if (useTabs && !encounteredNonWhitespace && spaceCount == indentationSettings.TabSize)
109+
if (useTabs)
102110
{
103-
replacement.Length = replacement.Length - spaceCount;
104-
replacement.Append('\t');
105-
spaceCount = 0;
111+
// Note that we account for column not yet being incremented
112+
var offsetWithinTabColumn = (column + 1) % indentationSettings.TabSize;
113+
if (offsetWithinTabColumn == 0)
114+
{
115+
// We reached a tab stop.
116+
replacement.Length = replacement.Length - spaceCount;
117+
replacement.Append('\t');
118+
spaceCount = 0;
119+
}
106120
}
107121
}
108122
else
109123
{
110124
spaceCount = 0;
111-
encounteredNonWhitespace = true;
112125
}
113126
}
114127

115-
if (c == '\n')
128+
if (c == '\r' || c == '\n')
116129
{
130+
// Handle newlines. We can ignore CR/LF/CRLF issues because we are only tracking column position
131+
// in a line, and not the line numbers themselves.
117132
column = 0;
118133
spaceCount = 0;
119-
encounteredNonWhitespace = false;
120134
}
121135
else
122136
{

StyleCop.Analyzers/StyleCop.Analyzers/SpacingRules/SA1027TabsMustNotBeUsed.cs

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,10 @@ internal class SA1027TabsMustNotBeUsed : DiagnosticAnalyzer
3232
/// </summary>
3333
public const string DiagnosticId = "SA1027";
3434

35+
internal static readonly string BehaviorKey = "Behavior";
36+
internal static readonly string ConvertToTabsBehavior = "ConvertToTabs";
37+
internal static readonly string ConvertToSpacesBehavior = "ConvertToSpaces";
38+
3539
private static readonly LocalizableString Title = new LocalizableResourceString(nameof(SpacingResources.SA1027Title), SpacingResources.ResourceManager, typeof(SpacingResources));
3640
private static readonly LocalizableString MessageFormat = new LocalizableResourceString(nameof(SpacingResources.SA1027MessageFormat), SpacingResources.ResourceManager, typeof(SpacingResources));
3741
private static readonly LocalizableString Description = new LocalizableResourceString(nameof(SpacingResources.SA1027Description), SpacingResources.ResourceManager, typeof(SpacingResources));
@@ -43,6 +47,12 @@ internal class SA1027TabsMustNotBeUsed : DiagnosticAnalyzer
4347
private static readonly Action<CompilationStartAnalysisContext> CompilationStartAction = HandleCompilationStart;
4448
private static readonly Action<SyntaxTreeAnalysisContext, StyleCopSettings> SyntaxTreeAction = HandleSyntaxTree;
4549

50+
private static readonly ImmutableDictionary<string, string> ConvertToTabsProperties =
51+
ImmutableDictionary.Create<string, string>().SetItem(BehaviorKey, ConvertToTabsBehavior);
52+
53+
private static readonly ImmutableDictionary<string, string> ConvertToSpacesProperties =
54+
ImmutableDictionary.Create<string, string>().SetItem(BehaviorKey, ConvertToSpacesBehavior);
55+
4656
/// <inheritdoc/>
4757
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get; } =
4858
ImmutableArray.Create(Descriptor);
@@ -101,7 +111,8 @@ private static void HandleSyntaxTree(SyntaxTreeAnalysisContext context, StyleCop
101111
context.ReportDiagnostic(
102112
Diagnostic.Create(
103113
Descriptor,
104-
Location.Create(syntaxTree, TextSpan.FromBounds(violationStart, violationEnd))));
114+
Location.Create(syntaxTree, TextSpan.FromBounds(violationStart, violationEnd)),
115+
ConvertToTabsProperties));
105116
}
106117

107118
if (included.End > excluded.End)
@@ -130,7 +141,8 @@ private static void HandleSyntaxTree(SyntaxTreeAnalysisContext context, StyleCop
130141
context.ReportDiagnostic(
131142
Diagnostic.Create(
132143
Descriptor,
133-
Location.Create(syntaxTree, TextSpan.FromBounds(violationStart, violationEnd))));
144+
Location.Create(syntaxTree, TextSpan.FromBounds(violationStart, violationEnd)),
145+
ConvertToSpacesProperties));
134146
}
135147

136148
if (included.End > excluded.End)
@@ -148,15 +160,17 @@ private static void HandleSyntaxTree(SyntaxTreeAnalysisContext context, StyleCop
148160
context.ReportDiagnostic(
149161
Diagnostic.Create(
150162
Descriptor,
151-
Location.Create(syntaxTree, convertToTabsSpans[toTabsIndex])));
163+
Location.Create(syntaxTree, convertToTabsSpans[toTabsIndex]),
164+
ConvertToTabsProperties));
152165
}
153166

154167
for (; toSpacesIndex < convertToSpacesSpans.Length; toSpacesIndex++)
155168
{
156169
context.ReportDiagnostic(
157170
Diagnostic.Create(
158171
Descriptor,
159-
Location.Create(syntaxTree, convertToSpacesSpans[toSpacesIndex])));
172+
Location.Create(syntaxTree, convertToSpacesSpans[toSpacesIndex]),
173+
ConvertToSpacesProperties));
160174
}
161175
}
162176

0 commit comments

Comments
 (0)