Skip to content

Commit

Permalink
Correctly find the node to fix in regex generator codefix (dotnet#72079)
Browse files Browse the repository at this point in the history
Co-authored-by: Jose Perez Rodriguez <[email protected]>
  • Loading branch information
Youssef1313 and joperezr authored Jul 19, 2022
1 parent c37f002 commit e0f614d
Show file tree
Hide file tree
Showing 2 changed files with 30 additions and 12 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ public override async Task RegisterCodeFixesAsync(CodeFixContext context)
return;
}

SyntaxNode nodeToFix = root.FindNode(context.Span, getInnermostNodeForTie: false);
SyntaxNode nodeToFix = root.FindNode(context.Span, getInnermostNodeForTie: true);
if (nodeToFix is null)
{
return;
Expand All @@ -55,7 +55,7 @@ public override async Task RegisterCodeFixesAsync(CodeFixContext context)
context.RegisterCodeFix(
CodeAction.Create(
SR.UseRegexSourceGeneratorTitle,
cancellationToken => ConvertToSourceGenerator(context.Document, context.Diagnostics[0], cancellationToken),
cancellationToken => ConvertToSourceGenerator(context.Document, root, nodeToFix, context.Diagnostics[0], cancellationToken),
equivalenceKey: SR.UseRegexSourceGeneratorTitle),
context.Diagnostics);
}
Expand All @@ -65,10 +65,12 @@ public override async Task RegisterCodeFixesAsync(CodeFixContext context)
/// nodes in order to apply the code fix to the diagnostic.
/// </summary>
/// <param name="document">The original document.</param>
/// <param name="root">The root of the syntax tree.</param>
/// <param name="nodeToFix">The node to fix. This is where the diagnostic was produced.</param>
/// <param name="diagnostic">The diagnostic to fix.</param>
/// <param name="cancellationToken">The cancellation token for the async operation.</param>
/// <returns>The new document with the replaced nodes after applying the code fix.</returns>
private static async Task<Document> ConvertToSourceGenerator(Document document, Diagnostic diagnostic, CancellationToken cancellationToken)
private static async Task<Document> ConvertToSourceGenerator(Document document, SyntaxNode root, SyntaxNode nodeToFix, Diagnostic diagnostic, CancellationToken cancellationToken)
{
// We first get the compilation object from the document
SemanticModel? semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);
Expand All @@ -86,14 +88,6 @@ private static async Task<Document> ConvertToSourceGenerator(Document document,
return document;
}

// Find the node that corresponding to the diagnostic which we will then fix.
SyntaxNode? root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
if (root is null)
{
return document;
}

SyntaxNode nodeToFix = root.FindNode(diagnostic.Location.SourceSpan, getInnermostNodeForTie: false);
// Save the operation object from the nodeToFix before it gets replaced by the new method invocation.
// We will later use this operation to get the parameters out and pass them into the RegexGenerator attribute.
IOperation? operation = semanticModel.GetOperation(nodeToFix, cancellationToken);
Expand Down Expand Up @@ -144,7 +138,7 @@ private static async Task<Document> ConvertToSourceGenerator(Document document,
});

// We find nodeToFix again by calculating the offset of how many partial keywords we had to add.
nodeToFix = root.FindNode(new TextSpan(nodeToFix.Span.Start + (typesModified * "partial".Length), nodeToFix.Span.Length));
nodeToFix = root.FindNode(new TextSpan(nodeToFix.Span.Start + (typesModified * "partial".Length), nodeToFix.Span.Length), getInnermostNodeForTie: true);
if (nodeToFix is null)
{
return document;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -791,6 +791,30 @@ public static string CollapseWhitespace(this string text) =>
await VerifyCS.VerifyCodeFixAsync(test, expectedDiagnostic, expectedFixedCode);
}

[Fact]
public async Task TestAsArgument()
{
string test = @"using System.Text.RegularExpressions;
public class C
{
void M1(Regex r) => _ = r;
void M2() => M1([|new Regex("""")|]);
}
";

string fixedCode = @"using System.Text.RegularExpressions;
public partial class C
{
void M1(Regex r) => _ = r;
void M2() => M1(MyRegex());
[RegexGenerator("""")]
private static partial Regex MyRegex();
}
";

await VerifyCS.VerifyCodeFixAsync(test, fixedCode);
}

#region Test helpers

private static string ConstructRegexInvocation(InvocationType invocationType, string pattern, string? options = null)
Expand Down

0 comments on commit e0f614d

Please sign in to comment.