mirror of
https://github.com/nsnail/spectre.console.git
synced 2025-04-16 17:02:51 +08:00
Adding analyzer project
Contains two analyzers with fixes * Use AnsiConsole over System.Console * Favor local instance over static implementation
This commit is contained in:
parent
d015a4966f
commit
4f293d887d
16
examples/Console/AnalyzerTester/AnalyzerTester.csproj
Normal file
16
examples/Console/AnalyzerTester/AnalyzerTester.csproj
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<OutputType>Exe</OutputType>
|
||||||
|
<TargetFramework>net5.0</TargetFramework>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\..\..\src\Spectre.Console\Spectre.Console.csproj" />
|
||||||
|
<ProjectReference Include="..\..\..\src\Spectre.Console.Analyzer\Spectre.Console.Analyzer.csproj"
|
||||||
|
PrivateAssets="all"
|
||||||
|
ReferenceOutputAssembly="false"
|
||||||
|
OutputItemType="Analyzer" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
32
examples/Console/AnalyzerTester/Program.cs
Normal file
32
examples/Console/AnalyzerTester/Program.cs
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
using Spectre.Console;
|
||||||
|
|
||||||
|
namespace AnalyzerTester
|
||||||
|
{
|
||||||
|
class Program
|
||||||
|
{
|
||||||
|
static void Main(string[] args)
|
||||||
|
{
|
||||||
|
AnsiConsole.WriteLine("Hello World!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Dependency
|
||||||
|
{
|
||||||
|
private readonly IAnsiConsole _ansiConsole;
|
||||||
|
|
||||||
|
public Dependency(IAnsiConsole ansiConsole)
|
||||||
|
{
|
||||||
|
_ansiConsole = ansiConsole;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void DoIt()
|
||||||
|
{
|
||||||
|
_ansiConsole.WriteLine("Hey mom!");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void DoIt(IAnsiConsole thisConsole)
|
||||||
|
{
|
||||||
|
thisConsole.WriteLine("Hey mom!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
25
src/Spectre.Console.Analyzer/Analyzers/BaseAnalyzer.cs
Normal file
25
src/Spectre.Console.Analyzer/Analyzers/BaseAnalyzer.cs
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
using Microsoft.CodeAnalysis.Diagnostics;
|
||||||
|
|
||||||
|
namespace Spectre.Console.Analyzer
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Base class for Spectre analyzers.
|
||||||
|
/// </summary>
|
||||||
|
public abstract class BaseAnalyzer : DiagnosticAnalyzer
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override void Initialize(AnalysisContext context)
|
||||||
|
{
|
||||||
|
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.Analyze | GeneratedCodeAnalysisFlags.ReportDiagnostics);
|
||||||
|
context.EnableConcurrentExecution();
|
||||||
|
|
||||||
|
context.RegisterCompilationStartAction(AnalyzeCompilation);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Analyze compilation.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="compilationStartContext">Compilation Start Analysis Context.</param>
|
||||||
|
protected abstract void AnalyzeCompilation(CompilationStartAnalysisContext compilationStartContext);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,89 @@
|
|||||||
|
using System.Collections.Immutable;
|
||||||
|
using System.Linq;
|
||||||
|
using Microsoft.CodeAnalysis;
|
||||||
|
using Microsoft.CodeAnalysis.CSharp;
|
||||||
|
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||||
|
using Microsoft.CodeAnalysis.Diagnostics;
|
||||||
|
using Microsoft.CodeAnalysis.Operations;
|
||||||
|
|
||||||
|
namespace Spectre.Console.Analyzer
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Analyzer to suggest using available instances of AnsiConsole over the static methods.
|
||||||
|
/// </summary>
|
||||||
|
[DiagnosticAnalyzer(LanguageNames.CSharp)]
|
||||||
|
public class FavorInstanceAnsiConsoleOverStaticAnalyzer : BaseAnalyzer
|
||||||
|
{
|
||||||
|
private static readonly DiagnosticDescriptor _diagnosticDescriptor =
|
||||||
|
Descriptors.S1010_FavorInstanceAnsiConsoleOverStatic;
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics =>
|
||||||
|
ImmutableArray.Create(_diagnosticDescriptor);
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void AnalyzeCompilation(CompilationStartAnalysisContext compilationStartContext)
|
||||||
|
{
|
||||||
|
compilationStartContext.RegisterOperationAction(
|
||||||
|
context =>
|
||||||
|
{
|
||||||
|
var ansiConsoleType = context.Compilation.GetTypeByMetadataName("Spectre.Console.AnsiConsole");
|
||||||
|
|
||||||
|
// if this operation isn't an invocation against one of the System.Console methods
|
||||||
|
// defined in _methods then we can safely stop analyzing and return;
|
||||||
|
var invocationOperation = (IInvocationOperation)context.Operation;
|
||||||
|
if (!Equals(invocationOperation.TargetMethod.ContainingType, ansiConsoleType))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!HasFieldAnsiConsole(invocationOperation.Syntax) &&
|
||||||
|
!HasParameterAnsiConsole(invocationOperation.Syntax))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var methodSymbol = invocationOperation.TargetMethod;
|
||||||
|
|
||||||
|
var displayString = SymbolDisplay.ToDisplayString(
|
||||||
|
methodSymbol,
|
||||||
|
SymbolDisplayFormat.CSharpShortErrorMessageFormat
|
||||||
|
.WithParameterOptions(SymbolDisplayParameterOptions.None)
|
||||||
|
.WithGenericsOptions(SymbolDisplayGenericsOptions.None));
|
||||||
|
|
||||||
|
context.ReportDiagnostic(
|
||||||
|
Diagnostic.Create(
|
||||||
|
_diagnosticDescriptor,
|
||||||
|
invocationOperation.Syntax.GetLocation(),
|
||||||
|
displayString));
|
||||||
|
}, OperationKind.Invocation);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool HasParameterAnsiConsole(SyntaxNode syntaxNode)
|
||||||
|
{
|
||||||
|
return syntaxNode
|
||||||
|
.Ancestors().OfType<MethodDeclarationSyntax>()
|
||||||
|
.First()
|
||||||
|
.ParameterList.Parameters
|
||||||
|
.Any(i => i.Type.NormalizeWhitespace().ToString() == "IAnsiConsole");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool HasFieldAnsiConsole(SyntaxNode syntaxNode)
|
||||||
|
{
|
||||||
|
var isStatic = syntaxNode
|
||||||
|
.Ancestors()
|
||||||
|
.OfType<MethodDeclarationSyntax>()
|
||||||
|
.First()
|
||||||
|
.Modifiers.Any(i => i.Kind() == SyntaxKind.StaticKeyword);
|
||||||
|
|
||||||
|
return syntaxNode
|
||||||
|
.Ancestors().OfType<ClassDeclarationSyntax>()
|
||||||
|
.First()
|
||||||
|
.Members
|
||||||
|
.OfType<FieldDeclarationSyntax>()
|
||||||
|
.Any(i =>
|
||||||
|
i.Declaration.Type.NormalizeWhitespace().ToString() == "IAnsiConsole" &&
|
||||||
|
(!isStatic ^ i.Modifiers.Any(modifier => modifier.Kind() == SyntaxKind.StaticKeyword)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,63 @@
|
|||||||
|
using System.Collections.Immutable;
|
||||||
|
using System.Linq;
|
||||||
|
using Microsoft.CodeAnalysis;
|
||||||
|
using Microsoft.CodeAnalysis.CSharp;
|
||||||
|
using Microsoft.CodeAnalysis.Diagnostics;
|
||||||
|
using Microsoft.CodeAnalysis.Operations;
|
||||||
|
|
||||||
|
namespace Spectre.Console.Analyzer
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Analyzer to enforce the use of AnsiConsole over System.Console for known methods.
|
||||||
|
/// </summary>
|
||||||
|
[DiagnosticAnalyzer(LanguageNames.CSharp)]
|
||||||
|
public class UseSpectreInsteadOfSystemConsoleAnalyzer : BaseAnalyzer
|
||||||
|
{
|
||||||
|
private static readonly DiagnosticDescriptor _diagnosticDescriptor =
|
||||||
|
Descriptors.S1000_UseAnsiConsoleOverSystemConsole;
|
||||||
|
|
||||||
|
private static readonly string[] _methods = { "WriteLine", "Write" };
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics =>
|
||||||
|
ImmutableArray.Create(_diagnosticDescriptor);
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void AnalyzeCompilation(CompilationStartAnalysisContext compilationStartContext)
|
||||||
|
{
|
||||||
|
compilationStartContext.RegisterOperationAction(
|
||||||
|
context =>
|
||||||
|
{
|
||||||
|
// if this operation isn't an invocation against one of the System.Console methods
|
||||||
|
// defined in _methods then we can safely stop analyzing and return;
|
||||||
|
var invocationOperation = (IInvocationOperation)context.Operation;
|
||||||
|
var systemConsoleType = context.Compilation.GetTypeByMetadataName("System.Console");
|
||||||
|
|
||||||
|
if (!Equals(invocationOperation.TargetMethod.ContainingType, systemConsoleType))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var methodName = System.Array.Find(_methods, i => i.Equals(invocationOperation.TargetMethod.Name));
|
||||||
|
if (methodName == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var methodSymbol = invocationOperation.TargetMethod;
|
||||||
|
|
||||||
|
var displayString = SymbolDisplay.ToDisplayString(
|
||||||
|
methodSymbol,
|
||||||
|
SymbolDisplayFormat.CSharpShortErrorMessageFormat
|
||||||
|
.WithParameterOptions(SymbolDisplayParameterOptions.None)
|
||||||
|
.WithGenericsOptions(SymbolDisplayGenericsOptions.None));
|
||||||
|
|
||||||
|
context.ReportDiagnostic(
|
||||||
|
Diagnostic.Create(
|
||||||
|
_diagnosticDescriptor,
|
||||||
|
invocationOperation.Syntax.GetLocation(),
|
||||||
|
displayString));
|
||||||
|
}, OperationKind.Invocation);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
8
src/Spectre.Console.Analyzer/Constants.cs
Normal file
8
src/Spectre.Console.Analyzer/Constants.cs
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
namespace Spectre.Console.Analyzer
|
||||||
|
{
|
||||||
|
internal static class Constants
|
||||||
|
{
|
||||||
|
internal const string StaticInstance = "AnsiConsole";
|
||||||
|
internal const string SpectreConsole = "Spectre.Console";
|
||||||
|
}
|
||||||
|
}
|
49
src/Spectre.Console.Analyzer/Descriptors.cs
Normal file
49
src/Spectre.Console.Analyzer/Descriptors.cs
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
using System.Collections.Concurrent;
|
||||||
|
using Microsoft.CodeAnalysis;
|
||||||
|
using static Microsoft.CodeAnalysis.DiagnosticSeverity;
|
||||||
|
using static Spectre.Console.Analyzer.Descriptors.Category;
|
||||||
|
|
||||||
|
namespace Spectre.Console.Analyzer
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Code analysis descriptors.
|
||||||
|
/// </summary>
|
||||||
|
public static class Descriptors
|
||||||
|
{
|
||||||
|
internal enum Category
|
||||||
|
{
|
||||||
|
Usage, // 1xxx
|
||||||
|
}
|
||||||
|
|
||||||
|
private static readonly ConcurrentDictionary<Category, string> _categoryMapping = new();
|
||||||
|
|
||||||
|
private static DiagnosticDescriptor Rule(string id, string title, Category category, DiagnosticSeverity defaultSeverity, string messageFormat, string? description = null)
|
||||||
|
{
|
||||||
|
var helpLink = $"https://spectreconsole.net/spectre.console.analyzers/rules/{id}";
|
||||||
|
const bool IsEnabledByDefault = true;
|
||||||
|
return new DiagnosticDescriptor(id, title, messageFormat, _categoryMapping.GetOrAdd(category, c => c.ToString()), defaultSeverity, IsEnabledByDefault, description, helpLink);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets definitions of diagnostics Spectre1000.
|
||||||
|
/// </summary>
|
||||||
|
public static DiagnosticDescriptor S1000_UseAnsiConsoleOverSystemConsole { get; } =
|
||||||
|
Rule(
|
||||||
|
"Spectre1000",
|
||||||
|
"Use AnsiConsole instead of System.Console",
|
||||||
|
Usage,
|
||||||
|
Warning,
|
||||||
|
"Use AnsiConsole instead of System.Console");
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets definitions of diagnostics Spectre1010.
|
||||||
|
/// </summary>
|
||||||
|
public static DiagnosticDescriptor S1010_FavorInstanceAnsiConsoleOverStatic { get; } =
|
||||||
|
Rule(
|
||||||
|
"Spectre1010",
|
||||||
|
"Favor the use of the instance of AnsiConsole over the static helper.",
|
||||||
|
Usage,
|
||||||
|
Info,
|
||||||
|
"Favor the use of the instance of AnsiConsole over the static helper.");
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,115 @@
|
|||||||
|
using System.Linq;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.CodeAnalysis;
|
||||||
|
using Microsoft.CodeAnalysis.CodeActions;
|
||||||
|
using Microsoft.CodeAnalysis.CSharp;
|
||||||
|
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||||
|
using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;
|
||||||
|
|
||||||
|
namespace Spectre.Console.Analyzer.CodeActions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Code action to change calls to System.Console to AnsiConsole.
|
||||||
|
/// </summary>
|
||||||
|
public class SwitchToAnsiConsoleAction : CodeAction
|
||||||
|
{
|
||||||
|
private readonly Document _document;
|
||||||
|
private readonly InvocationExpressionSyntax _originalInvocation;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="SwitchToAnsiConsoleAction"/> class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="document">Document to change.</param>
|
||||||
|
/// <param name="originalInvocation">The method to change.</param>
|
||||||
|
/// <param name="title">Title of the fix.</param>
|
||||||
|
public SwitchToAnsiConsoleAction(Document document, InvocationExpressionSyntax originalInvocation, string title)
|
||||||
|
{
|
||||||
|
_document = document;
|
||||||
|
_originalInvocation = originalInvocation;
|
||||||
|
Title = title;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override string Title { get; }
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override string EquivalenceKey => Title;
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override async Task<Document> GetChangedDocumentAsync(CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
var originalCaller = ((MemberAccessExpressionSyntax)_originalInvocation.Expression).Name.ToString();
|
||||||
|
|
||||||
|
var syntaxTree = await _document.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false);
|
||||||
|
var root = (CompilationUnitSyntax)await syntaxTree.GetRootAsync(cancellationToken).ConfigureAwait(false);
|
||||||
|
|
||||||
|
// If there is an ansiConsole passed into the method then we'll use it.
|
||||||
|
// otherwise we'll check for a field level instance.
|
||||||
|
// if neither of those exist we'll fall back to the static param.
|
||||||
|
var ansiConsoleParameterDeclaration = GetAnsiConsoleParameterDeclaration();
|
||||||
|
var ansiConsoleFieldIdentifier = GetAnsiConsoleFieldDeclaration();
|
||||||
|
var ansiConsoleIdentifier = ansiConsoleParameterDeclaration ??
|
||||||
|
ansiConsoleFieldIdentifier ??
|
||||||
|
Constants.StaticInstance;
|
||||||
|
|
||||||
|
// Replace the System.Console call with a call to the identifier above.
|
||||||
|
var newRoot = root.ReplaceNode(
|
||||||
|
_originalInvocation,
|
||||||
|
GetImportedSpectreCall(originalCaller, ansiConsoleIdentifier));
|
||||||
|
|
||||||
|
// If we are calling the static instance and Spectre isn't imported yet we should do so.
|
||||||
|
if (ansiConsoleIdentifier == Constants.StaticInstance && root.Usings.ToList().All(i => i.Name.ToString() != Constants.SpectreConsole))
|
||||||
|
{
|
||||||
|
newRoot = newRoot.AddUsings(Syntax.SpectreUsing);
|
||||||
|
}
|
||||||
|
|
||||||
|
return _document.WithSyntaxRoot(newRoot);
|
||||||
|
}
|
||||||
|
|
||||||
|
private string? GetAnsiConsoleParameterDeclaration()
|
||||||
|
{
|
||||||
|
return _originalInvocation
|
||||||
|
.Ancestors().OfType<MethodDeclarationSyntax>()
|
||||||
|
.First()
|
||||||
|
.ParameterList.Parameters
|
||||||
|
.FirstOrDefault(i => i.Type.NormalizeWhitespace().ToString() == "IAnsiConsole")
|
||||||
|
?.Identifier.Text;
|
||||||
|
}
|
||||||
|
|
||||||
|
private string? GetAnsiConsoleFieldDeclaration()
|
||||||
|
{
|
||||||
|
// 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.
|
||||||
|
var isStatic = _originalInvocation
|
||||||
|
.Ancestors()
|
||||||
|
.OfType<MethodDeclarationSyntax>()
|
||||||
|
.First()
|
||||||
|
.Modifiers.Any(i => i.Kind() == SyntaxKind.StaticKeyword);
|
||||||
|
|
||||||
|
return _originalInvocation
|
||||||
|
.Ancestors().OfType<ClassDeclarationSyntax>()
|
||||||
|
.First()
|
||||||
|
.Members
|
||||||
|
.OfType<FieldDeclarationSyntax>()
|
||||||
|
.FirstOrDefault(i =>
|
||||||
|
i.Declaration.Type.NormalizeWhitespace().ToString() == "IAnsiConsole" &&
|
||||||
|
(!isStatic ^ i.Modifiers.Any(modifier => modifier.Kind() == SyntaxKind.StaticKeyword)))
|
||||||
|
?.Declaration.Variables.First().Identifier.Text;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ExpressionSyntax GetImportedSpectreCall(string originalCaller, string ansiConsoleIdentifier)
|
||||||
|
{
|
||||||
|
return ExpressionStatement(
|
||||||
|
InvocationExpression(
|
||||||
|
MemberAccessExpression(
|
||||||
|
SyntaxKind.SimpleMemberAccessExpression,
|
||||||
|
IdentifierName(ansiConsoleIdentifier),
|
||||||
|
IdentifierName(originalCaller)))
|
||||||
|
.WithArgumentList(
|
||||||
|
_originalInvocation.ArgumentList))
|
||||||
|
.Expression;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,35 @@
|
|||||||
|
using System.Collections.Immutable;
|
||||||
|
using System.Composition;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.CodeAnalysis;
|
||||||
|
using Microsoft.CodeAnalysis.CodeFixes;
|
||||||
|
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||||
|
using Spectre.Console.Analyzer.CodeActions;
|
||||||
|
|
||||||
|
namespace Spectre.Console.Analyzer.FixProviders
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Fix provider to change System.Console calls to AnsiConsole calls.
|
||||||
|
/// </summary>
|
||||||
|
[ExportCodeFixProvider(LanguageNames.CSharp)]
|
||||||
|
[Shared]
|
||||||
|
public class StaticAnsiConsoleToInstanceFix : CodeFixProvider
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public sealed override ImmutableArray<string> FixableDiagnosticIds => ImmutableArray.Create(
|
||||||
|
Descriptors.S1010_FavorInstanceAnsiConsoleOverStatic.Id);
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public sealed override FixAllProvider GetFixAllProvider() => WellKnownFixAllProviders.BatchFixer;
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override async Task RegisterCodeFixesAsync(CodeFixContext context)
|
||||||
|
{
|
||||||
|
var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false);
|
||||||
|
var methodDeclaration = root.FindNode(context.Span).FirstAncestorOrSelf<InvocationExpressionSyntax>();
|
||||||
|
context.RegisterCodeFix(
|
||||||
|
new SwitchToAnsiConsoleAction(context.Document, methodDeclaration, "Convert static AnsiConsole calls to local instance."),
|
||||||
|
context.Diagnostics);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,35 @@
|
|||||||
|
using System.Collections.Immutable;
|
||||||
|
using System.Composition;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.CodeAnalysis;
|
||||||
|
using Microsoft.CodeAnalysis.CodeFixes;
|
||||||
|
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||||
|
using Spectre.Console.Analyzer.CodeActions;
|
||||||
|
|
||||||
|
namespace Spectre.Console.Analyzer.FixProviders
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Fix provider to change System.Console calls to AnsiConsole calls.
|
||||||
|
/// </summary>
|
||||||
|
[ExportCodeFixProvider(LanguageNames.CSharp)]
|
||||||
|
[Shared]
|
||||||
|
public class SystemConsoleToAnsiConsoleFix : CodeFixProvider
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public sealed override ImmutableArray<string> FixableDiagnosticIds => ImmutableArray.Create(
|
||||||
|
Descriptors.S1000_UseAnsiConsoleOverSystemConsole.Id);
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public sealed override FixAllProvider GetFixAllProvider() => WellKnownFixAllProviders.BatchFixer;
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override async Task RegisterCodeFixesAsync(CodeFixContext context)
|
||||||
|
{
|
||||||
|
var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false);
|
||||||
|
var methodDeclaration = root.FindNode(context.Span).FirstAncestorOrSelf<InvocationExpressionSyntax>();
|
||||||
|
context.RegisterCodeFix(
|
||||||
|
new SwitchToAnsiConsoleAction(context.Document, methodDeclaration, "Convert static call to AnsiConsole to Spectre.Console.AnsiConsole"),
|
||||||
|
context.Diagnostics);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
10
src/Spectre.Console.Analyzer/Fixes/Syntax.cs
Normal file
10
src/Spectre.Console.Analyzer/Fixes/Syntax.cs
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||||
|
using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;
|
||||||
|
|
||||||
|
namespace Spectre.Console.Analyzer
|
||||||
|
{
|
||||||
|
internal static class Syntax
|
||||||
|
{
|
||||||
|
public static readonly UsingDirectiveSyntax SpectreUsing = UsingDirective(QualifiedName(IdentifierName("Spectre"), IdentifierName("Console")));
|
||||||
|
}
|
||||||
|
}
|
23
src/Spectre.Console.Analyzer/Spectre.Console.Analyzer.csproj
Normal file
23
src/Spectre.Console.Analyzer/Spectre.Console.Analyzer.csproj
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>netstandard2.0</TargetFramework>
|
||||||
|
<IsPackable>true</IsPackable>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
</PropertyGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<AdditionalFiles Include="..\stylecop.json" Link="Properties/stylecop.json" />
|
||||||
|
<None Include="../../resources/gfx/small-logo.png" Pack="true" PackagePath="\" Link="Properties/small-logo.png" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="2.9.8" PrivateAssets="all" />
|
||||||
|
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="2.6.1" />
|
||||||
|
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="2.6.1" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<None Update="tools\*.ps1" CopyToOutputDirectory="Always" Pack="true" PackagePath="tools" />
|
||||||
|
<None Include="$(OutputPath)\$(AssemblyName).dll" Pack="true" PackagePath="analyzers/dotnet/cs" Visible="false" />
|
||||||
|
</ItemGroup>
|
||||||
|
</Project>
|
@ -0,0 +1,39 @@
|
|||||||
|
using Microsoft.CodeAnalysis;
|
||||||
|
using Microsoft.CodeAnalysis.Testing;
|
||||||
|
using Spectre.Console.Analyzer;
|
||||||
|
using Xunit;
|
||||||
|
using AnalyzerVerify =
|
||||||
|
Spectre.Console.Tests.CodeAnalyzers.SpectreAnalyzerVerifier<
|
||||||
|
Spectre.Console.Analyzer.FavorInstanceAnsiConsoleOverStaticAnalyzer>;
|
||||||
|
|
||||||
|
namespace Spectre.Console.Tests.CodeAnalyzers.Analyzers
|
||||||
|
{
|
||||||
|
public class FavorInstanceAnsiConsoleOverStaticAnalyzerTests
|
||||||
|
{
|
||||||
|
private static readonly DiagnosticResult _expectedDiagnostics = new(
|
||||||
|
Descriptors.S1010_FavorInstanceAnsiConsoleOverStatic.Id,
|
||||||
|
DiagnosticSeverity.Info);
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async void Console_Write_Has_Warning()
|
||||||
|
{
|
||||||
|
const string Source = @"
|
||||||
|
using Spectre.Console;
|
||||||
|
|
||||||
|
class TestClass
|
||||||
|
{
|
||||||
|
IAnsiConsole _ansiConsole = AnsiConsole.Console;
|
||||||
|
|
||||||
|
void TestMethod()
|
||||||
|
{
|
||||||
|
_ansiConsole.Write(""this is fine"");
|
||||||
|
AnsiConsole.Write(""Hello, World"");
|
||||||
|
}
|
||||||
|
}";
|
||||||
|
|
||||||
|
await AnalyzerVerify
|
||||||
|
.VerifyAnalyzerAsync(Source, _expectedDiagnostics.WithLocation(11, 9))
|
||||||
|
.ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,53 @@
|
|||||||
|
using Microsoft.CodeAnalysis;
|
||||||
|
using Microsoft.CodeAnalysis.Testing;
|
||||||
|
using Spectre.Console.Analyzer;
|
||||||
|
using Xunit;
|
||||||
|
using AnalyzerVerify =
|
||||||
|
Spectre.Console.Tests.CodeAnalyzers.SpectreAnalyzerVerifier<
|
||||||
|
Spectre.Console.Analyzer.UseSpectreInsteadOfSystemConsoleAnalyzer>;
|
||||||
|
|
||||||
|
namespace Spectre.Console.Tests.CodeAnalyzers.Analyzers
|
||||||
|
{
|
||||||
|
public class UseSpectreInsteadOfSystemConsoleAnalyzerTests
|
||||||
|
{
|
||||||
|
private static readonly DiagnosticResult _expectedDiagnostics = new(
|
||||||
|
Descriptors.S1000_UseAnsiConsoleOverSystemConsole.Id,
|
||||||
|
DiagnosticSeverity.Warning);
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async void Console_Write_Has_Warning()
|
||||||
|
{
|
||||||
|
const string Source = @"
|
||||||
|
using System;
|
||||||
|
|
||||||
|
class TestClass {
|
||||||
|
void TestMethod()
|
||||||
|
{
|
||||||
|
Console.Write(""Hello, World"");
|
||||||
|
}
|
||||||
|
}";
|
||||||
|
|
||||||
|
await AnalyzerVerify
|
||||||
|
.VerifyAnalyzerAsync(Source, _expectedDiagnostics.WithLocation(7, 9))
|
||||||
|
.ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async void Console_WriteLine_Has_Warning()
|
||||||
|
{
|
||||||
|
const string Source = @"
|
||||||
|
using System;
|
||||||
|
|
||||||
|
class TestClass
|
||||||
|
{
|
||||||
|
void TestMethod() {
|
||||||
|
Console.WriteLine(""Hello, World"");
|
||||||
|
}
|
||||||
|
}";
|
||||||
|
|
||||||
|
await AnalyzerVerify
|
||||||
|
.VerifyAnalyzerAsync(Source, _expectedDiagnostics.WithLocation(7, 9))
|
||||||
|
.ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,56 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Collections.Immutable;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.CodeAnalysis.CodeFixes;
|
||||||
|
using Microsoft.VisualStudio.Composition;
|
||||||
|
using Spectre.Console.Analyzer;
|
||||||
|
using Spectre.Console.Analyzer.FixProviders;
|
||||||
|
|
||||||
|
namespace Spectre.Console.Tests.CodeAnalyzers
|
||||||
|
{
|
||||||
|
internal static class CodeFixProviderDiscovery
|
||||||
|
{
|
||||||
|
private static readonly Lazy<IExportProviderFactory> _exportProviderFactory;
|
||||||
|
|
||||||
|
static CodeFixProviderDiscovery()
|
||||||
|
{
|
||||||
|
_exportProviderFactory = new Lazy<IExportProviderFactory>(
|
||||||
|
() =>
|
||||||
|
{
|
||||||
|
var discovery = new AttributedPartDiscovery(Resolver.DefaultInstance, isNonPublicSupported: true);
|
||||||
|
var parts = Task.Run(() => discovery.CreatePartsAsync(typeof(SystemConsoleToAnsiConsoleFix).Assembly)).GetAwaiter().GetResult();
|
||||||
|
var catalog = ComposableCatalog.Create(Resolver.DefaultInstance).AddParts(parts);
|
||||||
|
|
||||||
|
var configuration = CompositionConfiguration.Create(catalog);
|
||||||
|
var runtimeComposition = RuntimeComposition.CreateRuntimeComposition(configuration);
|
||||||
|
return runtimeComposition.CreateExportProviderFactory();
|
||||||
|
},
|
||||||
|
LazyThreadSafetyMode.ExecutionAndPublication);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IEnumerable<CodeFixProvider> GetCodeFixProviders(string language)
|
||||||
|
{
|
||||||
|
var exportProvider = _exportProviderFactory.Value.CreateExportProvider();
|
||||||
|
var exports = exportProvider.GetExports<CodeFixProvider, LanguageMetadata>();
|
||||||
|
return exports.Where(export => export.Metadata.Languages.Contains(language)).Select(export => export.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private class LanguageMetadata
|
||||||
|
{
|
||||||
|
public LanguageMetadata(IDictionary<string, object> data)
|
||||||
|
{
|
||||||
|
if (!data.TryGetValue(nameof(ExportCodeFixProviderAttribute.Languages), out var languages))
|
||||||
|
{
|
||||||
|
languages = Array.Empty<string>();
|
||||||
|
}
|
||||||
|
|
||||||
|
Languages = ((string[])languages).ToImmutableArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ImmutableArray<string> Languages { get; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,54 @@
|
|||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.CodeAnalysis;
|
||||||
|
using Microsoft.CodeAnalysis.Testing;
|
||||||
|
using Spectre.Console.Analyzer;
|
||||||
|
using Xunit;
|
||||||
|
using AnalyzerVerify =
|
||||||
|
Spectre.Console.Tests.CodeAnalyzers.SpectreAnalyzerVerifier<
|
||||||
|
Spectre.Console.Analyzer.FavorInstanceAnsiConsoleOverStaticAnalyzer>;
|
||||||
|
|
||||||
|
namespace Spectre.Console.Tests.CodeAnalyzers.Fixes
|
||||||
|
{
|
||||||
|
public class UseInstanceOfStaticAnsiConsoleTests
|
||||||
|
{
|
||||||
|
private static readonly DiagnosticResult _expectedDiagnostic = new(
|
||||||
|
Descriptors.S1010_FavorInstanceAnsiConsoleOverStatic.Id,
|
||||||
|
DiagnosticSeverity.Info);
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task SystemConsole_replaced_with_AnsiConsole()
|
||||||
|
{
|
||||||
|
const string Source = @"
|
||||||
|
using Spectre.Console;
|
||||||
|
|
||||||
|
class TestClass
|
||||||
|
{
|
||||||
|
IAnsiConsole _ansiConsole = AnsiConsole.Console;
|
||||||
|
|
||||||
|
void TestMethod()
|
||||||
|
{
|
||||||
|
_ansiConsole.Write(""this is fine"");
|
||||||
|
AnsiConsole.Write(""Hello, World"");
|
||||||
|
}
|
||||||
|
}";
|
||||||
|
|
||||||
|
const string FixedSource = @"
|
||||||
|
using Spectre.Console;
|
||||||
|
|
||||||
|
class TestClass
|
||||||
|
{
|
||||||
|
IAnsiConsole _ansiConsole = AnsiConsole.Console;
|
||||||
|
|
||||||
|
void TestMethod()
|
||||||
|
{
|
||||||
|
_ansiConsole.Write(""this is fine"");
|
||||||
|
_ansiConsole.Write(""Hello, World"");
|
||||||
|
}
|
||||||
|
}";
|
||||||
|
|
||||||
|
await AnalyzerVerify
|
||||||
|
.VerifyCodeFixAsync(Source, _expectedDiagnostic.WithLocation(11, 9), FixedSource)
|
||||||
|
.ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,152 @@
|
|||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.CodeAnalysis;
|
||||||
|
using Microsoft.CodeAnalysis.Testing;
|
||||||
|
using Spectre.Console.Analyzer;
|
||||||
|
using Xunit;
|
||||||
|
using AnalyzerVerify =
|
||||||
|
Spectre.Console.Tests.CodeAnalyzers.SpectreAnalyzerVerifier<
|
||||||
|
Spectre.Console.Analyzer.UseSpectreInsteadOfSystemConsoleAnalyzer>;
|
||||||
|
|
||||||
|
namespace Spectre.Console.Tests.CodeAnalyzers.Fixes
|
||||||
|
{
|
||||||
|
public class UseSpectreInsteadOfSystemConsoleFixTests
|
||||||
|
{
|
||||||
|
private static readonly DiagnosticResult _expectedDiagnostic = new(
|
||||||
|
Descriptors.S1000_UseAnsiConsoleOverSystemConsole.Id,
|
||||||
|
DiagnosticSeverity.Warning);
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task SystemConsole_replaced_with_AnsiConsole()
|
||||||
|
{
|
||||||
|
const string Source = @"
|
||||||
|
using System;
|
||||||
|
|
||||||
|
class TestClass
|
||||||
|
{
|
||||||
|
void TestMethod()
|
||||||
|
{
|
||||||
|
Console.WriteLine(""Hello, World"");
|
||||||
|
}
|
||||||
|
}";
|
||||||
|
|
||||||
|
const string FixedSource = @"
|
||||||
|
using System;
|
||||||
|
using Spectre.Console;
|
||||||
|
|
||||||
|
class TestClass
|
||||||
|
{
|
||||||
|
void TestMethod()
|
||||||
|
{
|
||||||
|
AnsiConsole.WriteLine(""Hello, World"");
|
||||||
|
}
|
||||||
|
}";
|
||||||
|
|
||||||
|
await AnalyzerVerify
|
||||||
|
.VerifyCodeFixAsync(Source, _expectedDiagnostic.WithLocation(8, 9), FixedSource)
|
||||||
|
.ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task SystemConsole_replaced_with_imported_AnsiConsole()
|
||||||
|
{
|
||||||
|
const string Source = @"
|
||||||
|
using System;
|
||||||
|
|
||||||
|
class TestClass
|
||||||
|
{
|
||||||
|
void TestMethod()
|
||||||
|
{
|
||||||
|
Console.WriteLine(""Hello, World"");
|
||||||
|
}
|
||||||
|
}";
|
||||||
|
|
||||||
|
const string FixedSource = @"
|
||||||
|
using System;
|
||||||
|
using Spectre.Console;
|
||||||
|
|
||||||
|
class TestClass
|
||||||
|
{
|
||||||
|
void TestMethod()
|
||||||
|
{
|
||||||
|
AnsiConsole.WriteLine(""Hello, World"");
|
||||||
|
}
|
||||||
|
}";
|
||||||
|
|
||||||
|
await AnalyzerVerify
|
||||||
|
.VerifyCodeFixAsync(Source, _expectedDiagnostic.WithLocation(8, 9), FixedSource)
|
||||||
|
.ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task SystemConsole_replaced_with_field_AnsiConsole()
|
||||||
|
{
|
||||||
|
const string Source = @"
|
||||||
|
using System;
|
||||||
|
using Spectre.Console;
|
||||||
|
|
||||||
|
class TestClass
|
||||||
|
{
|
||||||
|
IAnsiConsole _ansiConsole;
|
||||||
|
|
||||||
|
void TestMethod()
|
||||||
|
{
|
||||||
|
Console.WriteLine(""Hello, World"");
|
||||||
|
}
|
||||||
|
}";
|
||||||
|
|
||||||
|
const string FixedSource = @"
|
||||||
|
using System;
|
||||||
|
using Spectre.Console;
|
||||||
|
|
||||||
|
class TestClass
|
||||||
|
{
|
||||||
|
IAnsiConsole _ansiConsole;
|
||||||
|
|
||||||
|
void TestMethod()
|
||||||
|
{
|
||||||
|
_ansiConsole.WriteLine(""Hello, World"");
|
||||||
|
}
|
||||||
|
}";
|
||||||
|
|
||||||
|
await AnalyzerVerify
|
||||||
|
.VerifyCodeFixAsync(Source, _expectedDiagnostic.WithLocation(11, 9), FixedSource)
|
||||||
|
.ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task SystemConsole_replaced_with_static_field_AnsiConsole()
|
||||||
|
{
|
||||||
|
const string Source = @"
|
||||||
|
using System;
|
||||||
|
using Spectre.Console;
|
||||||
|
|
||||||
|
class TestClass
|
||||||
|
{
|
||||||
|
static IAnsiConsole _ansiConsole;
|
||||||
|
|
||||||
|
static void TestMethod()
|
||||||
|
{
|
||||||
|
Console.WriteLine(""Hello, World"");
|
||||||
|
}
|
||||||
|
}";
|
||||||
|
|
||||||
|
const string FixedSource = @"
|
||||||
|
using System;
|
||||||
|
using Spectre.Console;
|
||||||
|
|
||||||
|
class TestClass
|
||||||
|
{
|
||||||
|
static IAnsiConsole _ansiConsole;
|
||||||
|
|
||||||
|
static void TestMethod()
|
||||||
|
{
|
||||||
|
_ansiConsole.WriteLine(""Hello, World"");
|
||||||
|
}
|
||||||
|
}";
|
||||||
|
|
||||||
|
await AnalyzerVerify
|
||||||
|
.VerifyCodeFixAsync(Source, _expectedDiagnostic.WithLocation(11, 9), FixedSource)
|
||||||
|
.ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,87 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Collections.Immutable;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.CodeAnalysis.CodeFixes;
|
||||||
|
using Microsoft.CodeAnalysis.CSharp.Testing;
|
||||||
|
using Microsoft.CodeAnalysis.Diagnostics;
|
||||||
|
using Microsoft.CodeAnalysis.Testing;
|
||||||
|
using Microsoft.CodeAnalysis.Testing.Verifiers;
|
||||||
|
|
||||||
|
namespace Spectre.Console.Tests.CodeAnalyzers
|
||||||
|
{
|
||||||
|
public static class SpectreAnalyzerVerifier<TAnalyzer>
|
||||||
|
where TAnalyzer : DiagnosticAnalyzer, new()
|
||||||
|
{
|
||||||
|
public static Task VerifyCodeFixAsync(string source, DiagnosticResult expected, string fixedSource)
|
||||||
|
=> VerifyCodeFixAsync(source, new[] { expected }, fixedSource);
|
||||||
|
|
||||||
|
private static Task VerifyCodeFixAsync(string source, IEnumerable<DiagnosticResult> expected, string fixedSource)
|
||||||
|
{
|
||||||
|
// Roslyn fixers always use \r\n for newlines, regardless of OS environment settings, so we normalize
|
||||||
|
// the source as it typically comes from multi-line strings with varying newlines.
|
||||||
|
if (Environment.NewLine != "\r\n")
|
||||||
|
{
|
||||||
|
source = source.Replace(Environment.NewLine, "\r\n");
|
||||||
|
fixedSource = fixedSource.Replace(Environment.NewLine, "\r\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
var test = new Test
|
||||||
|
{
|
||||||
|
TestCode = source,
|
||||||
|
FixedCode = fixedSource,
|
||||||
|
};
|
||||||
|
|
||||||
|
test.ExpectedDiagnostics.AddRange(expected);
|
||||||
|
return test.RunAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Task VerifyAnalyzerAsync(string source, params DiagnosticResult[] expected)
|
||||||
|
{
|
||||||
|
var test = new Test
|
||||||
|
{
|
||||||
|
TestCode = source,
|
||||||
|
CompilerDiagnostics = CompilerDiagnostics.All,
|
||||||
|
ReferenceAssemblies = CodeAnalyzerHelper.CurrentSpectre,
|
||||||
|
TestBehaviors = TestBehaviors.SkipGeneratedCodeCheck,
|
||||||
|
};
|
||||||
|
|
||||||
|
test.ExpectedDiagnostics.AddRange(expected);
|
||||||
|
return test.RunAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Code fix tests support both analyzer and code fix testing. This test class is derived from the code fix test
|
||||||
|
// to avoid the need to maintain duplicate copies of the customization work.
|
||||||
|
private class Test : CSharpCodeFixTest<TAnalyzer, EmptyCodeFixProvider, XUnitVerifier>
|
||||||
|
{
|
||||||
|
public Test()
|
||||||
|
{
|
||||||
|
ReferenceAssemblies = CodeAnalyzerHelper.CurrentSpectre;
|
||||||
|
TestBehaviors |= TestBehaviors.SkipGeneratedCodeCheck;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override IEnumerable<CodeFixProvider> GetCodeFixProviders()
|
||||||
|
{
|
||||||
|
var analyzer = new TAnalyzer();
|
||||||
|
foreach (var provider in CodeFixProviderDiscovery.GetCodeFixProviders(Language))
|
||||||
|
{
|
||||||
|
if (analyzer.SupportedDiagnostics.Any(diagnostic => provider.FixableDiagnosticIds.Contains(diagnostic.Id)))
|
||||||
|
{
|
||||||
|
yield return provider;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static class CodeAnalyzerHelper
|
||||||
|
{
|
||||||
|
internal static ReferenceAssemblies CurrentSpectre { get; }
|
||||||
|
|
||||||
|
static CodeAnalyzerHelper()
|
||||||
|
{
|
||||||
|
CurrentSpectre = ReferenceAssemblies.Net.Net50.AddAssemblies(ImmutableArray.Create(typeof(AnsiConsole).Assembly.Location.Replace(".dll", string.Empty)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -17,6 +17,9 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Analyzer.Testing.XUnit" Version="1.1.0" />
|
||||||
|
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.CodeFix.Testing.XUnit" Version="1.1.0" />
|
||||||
|
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="2.6.1" />
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.8.3" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.8.3" />
|
||||||
<PackageReference Include="Shouldly" Version="4.0.3" />
|
<PackageReference Include="Shouldly" Version="4.0.3" />
|
||||||
<PackageReference Include="Spectre.Verify.Extensions" Version="0.3.0" />
|
<PackageReference Include="Spectre.Verify.Extensions" Version="0.3.0" />
|
||||||
@ -29,6 +32,7 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\Spectre.Console.Analyzer\Spectre.Console.Analyzer.csproj" />
|
||||||
<ProjectReference Include="..\Spectre.Console.Testing\Spectre.Console.Testing.csproj" />
|
<ProjectReference Include="..\Spectre.Console.Testing\Spectre.Console.Testing.csproj" />
|
||||||
<ProjectReference Include="..\Spectre.Console\Spectre.Console.csproj" />
|
<ProjectReference Include="..\Spectre.Console\Spectre.Console.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
@ -90,6 +90,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Shared", "..\examples\Share
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Live", "..\examples\Console\Live\Live.csproj", "{E607AA2A-A4A6-48E4-8AAB-B0EB74EACAA1}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Live", "..\examples\Console\Live\Live.csproj", "{E607AA2A-A4A6-48E4-8AAB-B0EB74EACAA1}"
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AnalyzerTester", "..\examples\Console\AnalyzerTester\AnalyzerTester.csproj", "{D2B32355-D99F-480B-92BF-9FAABE79ADD4}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Spectre.Console.Analyzer", "Spectre.Console.Analyzer\Spectre.Console.Analyzer.csproj", "{26006ACD-F19D-4C2A-8864-FE0D6C15B58C}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
Debug|Any CPU = Debug|Any CPU
|
||||||
@ -472,6 +476,30 @@ Global
|
|||||||
{E607AA2A-A4A6-48E4-8AAB-B0EB74EACAA1}.Release|x64.Build.0 = Release|Any CPU
|
{E607AA2A-A4A6-48E4-8AAB-B0EB74EACAA1}.Release|x64.Build.0 = Release|Any CPU
|
||||||
{E607AA2A-A4A6-48E4-8AAB-B0EB74EACAA1}.Release|x86.ActiveCfg = Release|Any CPU
|
{E607AA2A-A4A6-48E4-8AAB-B0EB74EACAA1}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
{E607AA2A-A4A6-48E4-8AAB-B0EB74EACAA1}.Release|x86.Build.0 = Release|Any CPU
|
{E607AA2A-A4A6-48E4-8AAB-B0EB74EACAA1}.Release|x86.Build.0 = Release|Any CPU
|
||||||
|
{D2B32355-D99F-480B-92BF-9FAABE79ADD4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{D2B32355-D99F-480B-92BF-9FAABE79ADD4}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{D2B32355-D99F-480B-92BF-9FAABE79ADD4}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{D2B32355-D99F-480B-92BF-9FAABE79ADD4}.Debug|x64.Build.0 = Debug|Any CPU
|
||||||
|
{D2B32355-D99F-480B-92BF-9FAABE79ADD4}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{D2B32355-D99F-480B-92BF-9FAABE79ADD4}.Debug|x86.Build.0 = Debug|Any CPU
|
||||||
|
{D2B32355-D99F-480B-92BF-9FAABE79ADD4}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{D2B32355-D99F-480B-92BF-9FAABE79ADD4}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{D2B32355-D99F-480B-92BF-9FAABE79ADD4}.Release|x64.ActiveCfg = Release|Any CPU
|
||||||
|
{D2B32355-D99F-480B-92BF-9FAABE79ADD4}.Release|x64.Build.0 = Release|Any CPU
|
||||||
|
{D2B32355-D99F-480B-92BF-9FAABE79ADD4}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{D2B32355-D99F-480B-92BF-9FAABE79ADD4}.Release|x86.Build.0 = Release|Any CPU
|
||||||
|
{26006ACD-F19D-4C2A-8864-FE0D6C15B58C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{26006ACD-F19D-4C2A-8864-FE0D6C15B58C}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{26006ACD-F19D-4C2A-8864-FE0D6C15B58C}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{26006ACD-F19D-4C2A-8864-FE0D6C15B58C}.Debug|x64.Build.0 = Debug|Any CPU
|
||||||
|
{26006ACD-F19D-4C2A-8864-FE0D6C15B58C}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{26006ACD-F19D-4C2A-8864-FE0D6C15B58C}.Debug|x86.Build.0 = Debug|Any CPU
|
||||||
|
{26006ACD-F19D-4C2A-8864-FE0D6C15B58C}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{26006ACD-F19D-4C2A-8864-FE0D6C15B58C}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{26006ACD-F19D-4C2A-8864-FE0D6C15B58C}.Release|x64.ActiveCfg = Release|Any CPU
|
||||||
|
{26006ACD-F19D-4C2A-8864-FE0D6C15B58C}.Release|x64.Build.0 = Release|Any CPU
|
||||||
|
{26006ACD-F19D-4C2A-8864-FE0D6C15B58C}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{26006ACD-F19D-4C2A-8864-FE0D6C15B58C}.Release|x86.Build.0 = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
@ -508,6 +536,7 @@ Global
|
|||||||
{A0C772BA-C5F4-451D-AA7A-4045F2FA0201} = {F0575243-121F-4DEE-9F6B-246E26DC0844}
|
{A0C772BA-C5F4-451D-AA7A-4045F2FA0201} = {F0575243-121F-4DEE-9F6B-246E26DC0844}
|
||||||
{8428A7DD-29FC-4417-9CA0-B90D34B26AB2} = {A0C772BA-C5F4-451D-AA7A-4045F2FA0201}
|
{8428A7DD-29FC-4417-9CA0-B90D34B26AB2} = {A0C772BA-C5F4-451D-AA7A-4045F2FA0201}
|
||||||
{E607AA2A-A4A6-48E4-8AAB-B0EB74EACAA1} = {F0575243-121F-4DEE-9F6B-246E26DC0844}
|
{E607AA2A-A4A6-48E4-8AAB-B0EB74EACAA1} = {F0575243-121F-4DEE-9F6B-246E26DC0844}
|
||||||
|
{D2B32355-D99F-480B-92BF-9FAABE79ADD4} = {A0C772BA-C5F4-451D-AA7A-4045F2FA0201}
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||||
SolutionGuid = {5729B071-67A0-48FB-8B1B-275E6822086C}
|
SolutionGuid = {5729B071-67A0-48FB-8B1B-275E6822086C}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user