Skip to content
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ namespace StyleCop.Analyzers.ReadabilityRules
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using StyleCop.Analyzers.Helpers;
using StyleCop.Analyzers.Settings.ObjectModel;

/// <summary>
/// Implements a code fix for <see cref="SA1134AttributesMustNotShareLine"/>.
Expand All @@ -29,7 +30,7 @@ internal class SA1134CodeFixProvider : CodeFixProvider
/// <inheritdoc/>
public override FixAllProvider GetFixAllProvider()
{
return CustomFixAllProviders.BatchFixer;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

❓ Any idea what's making this not deterministic? We originally added the custom batch fixer to address non-deterministic behavior in the one provided by Roslyn.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No. I had the test fail exactly once in the IDE. I have not been able to reproduce it since.

return FixAll.Instance;
}

/// <inheritdoc/>
Expand All @@ -52,16 +53,25 @@ private static async Task<Document> GetTransformedDocumentAsync(Document documen
{
var syntaxRoot = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
var settings = SettingsHelper.GetStyleCopSettings(document.Project.AnalyzerOptions, cancellationToken);
var tokensToReplace = new Dictionary<SyntaxToken, SyntaxToken>();

AddTokensToReplaceToMap(tokensToReplace, syntaxRoot, diagnostic, settings);

var newSyntaxRoot = syntaxRoot.ReplaceTokens(tokensToReplace.Keys, (original, rewritten) => tokensToReplace[original]);
var newDocument = document.WithSyntaxRoot(newSyntaxRoot.WithoutFormatting());

return newDocument;
}

private static void AddTokensToReplaceToMap(Dictionary<SyntaxToken, SyntaxToken> tokensToReplace, SyntaxNode syntaxRoot, Diagnostic diagnostic, StyleCopSettings settings)
{
var attributeListSyntax = (AttributeListSyntax)syntaxRoot.FindNode(diagnostic.Location.SourceSpan);

// use the containing type to determine the indentation level, anything else is less reliable.
var containingType = attributeListSyntax.Parent?.Parent;
var indentationSteps = (containingType != null) ? IndentationHelper.GetIndentationSteps(settings.Indentation, containingType) + 1 : 0;
var indentationTrivia = IndentationHelper.GenerateWhitespaceTrivia(settings.Indentation, indentationSteps);

var tokensToReplace = new Dictionary<SyntaxToken, SyntaxToken>();

if (diagnostic.Properties.ContainsKey(SA1134AttributesMustNotShareLine.FixWithNewLineBeforeKey))
{
var token = attributeListSyntax.OpenBracketToken;
Expand All @@ -83,11 +93,34 @@ private static async Task<Document> GetTransformedDocumentAsync(Document documen
var newLeadingTrivia = nextToken.LeadingTrivia.Insert(0, indentationTrivia);
tokensToReplace[nextToken] = nextToken.WithLeadingTrivia(newLeadingTrivia);
}
}

var newSyntaxRoot = syntaxRoot.ReplaceTokens(tokensToReplace.Keys, (original, rewritten) => tokensToReplace[original]);
var newDocument = document.WithSyntaxRoot(newSyntaxRoot.WithoutFormatting());
private class FixAll : DocumentBasedFixAllProvider
{
public static FixAllProvider Instance { get; } = new FixAll();

return newDocument;
protected override string CodeActionTitle => ReadabilityResources.SA1134CodeFix;

protected override async Task<SyntaxNode> FixAllInDocumentAsync(FixAllContext fixAllContext, Document document, ImmutableArray<Diagnostic> diagnostics)
{
if (diagnostics.IsEmpty)
{
return null;
}

var syntaxRoot = await document.GetSyntaxRootAsync(fixAllContext.CancellationToken).ConfigureAwait(false);
var settings = SettingsHelper.GetStyleCopSettings(document.Project.AnalyzerOptions, fixAllContext.CancellationToken);
var tokensToReplace = new Dictionary<SyntaxToken, SyntaxToken>();

foreach (var diagnostic in diagnostics)
{
AddTokensToReplaceToMap(tokensToReplace, syntaxRoot, diagnostic, settings);
}

var newSyntaxRoot = syntaxRoot.ReplaceTokens(tokensToReplace.Keys, (original, rewritten) => tokensToReplace[original]);

return newSyntaxRoot;
}
}
}
}