From e0f614d3cd9d0de3e55e02ed61f64123c16d6ed9 Mon Sep 17 00:00:00 2001 From: Youssef Victor Date: Tue, 19 Jul 2022 23:52:24 +0200 Subject: [PATCH] Correctly find the node to fix in regex generator codefix (#72079) Co-authored-by: Jose Perez Rodriguez --- .../gen/UpgradeToRegexGeneratorCodeFixer.cs | 18 +++++--------- .../UpgradeToRegexGeneratorAnalyzerTests.cs | 24 +++++++++++++++++++ 2 files changed, 30 insertions(+), 12 deletions(-) diff --git a/src/libraries/System.Text.RegularExpressions/gen/UpgradeToRegexGeneratorCodeFixer.cs b/src/libraries/System.Text.RegularExpressions/gen/UpgradeToRegexGeneratorCodeFixer.cs index a31f7edb67ef5..32ba674fccb5d 100644 --- a/src/libraries/System.Text.RegularExpressions/gen/UpgradeToRegexGeneratorCodeFixer.cs +++ b/src/libraries/System.Text.RegularExpressions/gen/UpgradeToRegexGeneratorCodeFixer.cs @@ -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; @@ -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); } @@ -65,10 +65,12 @@ public override async Task RegisterCodeFixesAsync(CodeFixContext context) /// nodes in order to apply the code fix to the diagnostic. /// /// The original document. + /// The root of the syntax tree. + /// The node to fix. This is where the diagnostic was produced. /// The diagnostic to fix. /// The cancellation token for the async operation. /// The new document with the replaced nodes after applying the code fix. - private static async Task ConvertToSourceGenerator(Document document, Diagnostic diagnostic, CancellationToken cancellationToken) + private static async Task 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); @@ -86,14 +88,6 @@ private static async Task 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); @@ -144,7 +138,7 @@ private static async Task 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; diff --git a/src/libraries/System.Text.RegularExpressions/tests/FunctionalTests/UpgradeToRegexGeneratorAnalyzerTests.cs b/src/libraries/System.Text.RegularExpressions/tests/FunctionalTests/UpgradeToRegexGeneratorAnalyzerTests.cs index 42c27dbc97418..4e8b8c171fcd2 100644 --- a/src/libraries/System.Text.RegularExpressions/tests/FunctionalTests/UpgradeToRegexGeneratorAnalyzerTests.cs +++ b/src/libraries/System.Text.RegularExpressions/tests/FunctionalTests/UpgradeToRegexGeneratorAnalyzerTests.cs @@ -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)