From 955fe07bac969022add8898ffb73f1bff73d8e8f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A9rald=20Barr=C3=A9?= Date: Sun, 19 Feb 2023 15:03:45 -0500 Subject: [PATCH] Allow to apply code fix in top-level statements --- .../CodeActions/SwitchToAnsiConsoleAction.cs | 19 +++++++++++------ .../StaticAnsiConsoleToInstanceFix.cs | 2 +- .../SpectreAnalyzerVerifier.cs | 13 +++++++++--- ...seSpectreInsteadOfSystemConsoleFixTests.cs | 21 +++++++++++++++++++ 4 files changed, 45 insertions(+), 10 deletions(-) diff --git a/src/Spectre.Console.Analyzer/Fixes/CodeActions/SwitchToAnsiConsoleAction.cs b/src/Spectre.Console.Analyzer/Fixes/CodeActions/SwitchToAnsiConsoleAction.cs index 11a1e17..c2930fb 100644 --- a/src/Spectre.Console.Analyzer/Fixes/CodeActions/SwitchToAnsiConsoleAction.cs +++ b/src/Spectre.Console.Analyzer/Fixes/CodeActions/SwitchToAnsiConsoleAction.cs @@ -69,8 +69,8 @@ public class SwitchToAnsiConsoleAction : CodeAction { return _originalInvocation .Ancestors().OfType() - .First() - .ParameterList.Parameters + .FirstOrDefault() + ?.ParameterList.Parameters .FirstOrDefault(i => i.Type?.NormalizeWhitespace()?.ToString() == "IAnsiConsole") ?.Identifier.Text; } @@ -79,12 +79,19 @@ public class SwitchToAnsiConsoleAction : CodeAction { // let's look to see if our call is in a static method. // if so we'll only want to look for static IAnsiConsoles - // and vice-versa if we aren't. + // and vice-versa if we aren't. + // If there is no parent method, the SyntaxNode should be in + // a top-level statement, so there is no field anyway. var isStatic = _originalInvocation .Ancestors() .OfType() - .First() - .Modifiers.Any(i => i.IsKind(SyntaxKind.StaticKeyword)); + .FirstOrDefault() + ?.Modifiers.Any(i => i.IsKind(SyntaxKind.StaticKeyword)); + + if (isStatic == null) + { + return null; + } return _originalInvocation .Ancestors().OfType() @@ -93,7 +100,7 @@ public class SwitchToAnsiConsoleAction : CodeAction .OfType() .FirstOrDefault(i => i.Declaration.Type.NormalizeWhitespace().ToString() == "IAnsiConsole" && - (!isStatic ^ i.Modifiers.Any(modifier => modifier.IsKind(SyntaxKind.StaticKeyword)))) + (!isStatic.GetValueOrDefault() ^ i.Modifiers.Any(modifier => modifier.IsKind(SyntaxKind.StaticKeyword)))) ?.Declaration.Variables.First().Identifier.Text; } diff --git a/src/Spectre.Console.Analyzer/Fixes/FixProviders/StaticAnsiConsoleToInstanceFix.cs b/src/Spectre.Console.Analyzer/Fixes/FixProviders/StaticAnsiConsoleToInstanceFix.cs index dfed1f3..d930d5c 100644 --- a/src/Spectre.Console.Analyzer/Fixes/FixProviders/StaticAnsiConsoleToInstanceFix.cs +++ b/src/Spectre.Console.Analyzer/Fixes/FixProviders/StaticAnsiConsoleToInstanceFix.cs @@ -20,7 +20,7 @@ public class StaticAnsiConsoleToInstanceFix : CodeFixProvider var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); if (root != null) { - var methodDeclaration = root.FindNode(context.Span).FirstAncestorOrSelf(); + var methodDeclaration = root.FindNode(context.Span, getInnermostNodeForTie: true).FirstAncestorOrSelf(); if (methodDeclaration != null) { context.RegisterCodeFix( diff --git a/test/Spectre.Console.Analyzer.Tests/SpectreAnalyzerVerifier.cs b/test/Spectre.Console.Analyzer.Tests/SpectreAnalyzerVerifier.cs index 8aadab6..677c8d0 100644 --- a/test/Spectre.Console.Analyzer.Tests/SpectreAnalyzerVerifier.cs +++ b/test/Spectre.Console.Analyzer.Tests/SpectreAnalyzerVerifier.cs @@ -4,13 +4,20 @@ public static class SpectreAnalyzerVerifier where TAnalyzer : DiagnosticAnalyzer, new() { public static Task VerifyCodeFixAsync(string source, DiagnosticResult expected, string fixedSource) - => VerifyCodeFixAsync(source, new[] { expected }, fixedSource); + => VerifyCodeFixAsync(source, OutputKind.DynamicallyLinkedLibrary, new[] { expected }, fixedSource); + + public static Task VerifyCodeFixAsync(string source, OutputKind outputKind, DiagnosticResult expected, string fixedSource) + => VerifyCodeFixAsync(source, outputKind, new[] { expected }, fixedSource); - private static Task VerifyCodeFixAsync(string source, IEnumerable expected, string fixedSource) + private static Task VerifyCodeFixAsync(string source, OutputKind outputKind, IEnumerable expected, string fixedSource) { var test = new Test { - TestCode = source, + TestCode = source, + TestState = + { + OutputKind = outputKind, + }, FixedCode = fixedSource, }; diff --git a/test/Spectre.Console.Analyzer.Tests/Unit/Fixes/UseSpectreInsteadOfSystemConsoleFixTests.cs b/test/Spectre.Console.Analyzer.Tests/Unit/Fixes/UseSpectreInsteadOfSystemConsoleFixTests.cs index 3e2a7e3..1e0f615 100644 --- a/test/Spectre.Console.Analyzer.Tests/Unit/Fixes/UseSpectreInsteadOfSystemConsoleFixTests.cs +++ b/test/Spectre.Console.Analyzer.Tests/Unit/Fixes/UseSpectreInsteadOfSystemConsoleFixTests.cs @@ -139,4 +139,25 @@ class TestClass .VerifyCodeFixAsync(Source, _expectedDiagnostic.WithLocation(11, 9), FixedSource) .ConfigureAwait(false); } + + [Fact] + public async Task SystemConsole_replaced_with_AnsiConsole_in_top_level_statements() + { + const string Source = @" +using System; + +Console.WriteLine(""Hello, World""); +"; + + const string FixedSource = @" +using System; +using Spectre.Console; + +AnsiConsole.WriteLine(""Hello, World""); +"; + + await SpectreAnalyzerVerifier + .VerifyCodeFixAsync(Source, OutputKind.ConsoleApplication, _expectedDiagnostic.WithLocation(4, 1), FixedSource) + .ConfigureAwait(false); + } }