mirror of
https://github.com/nsnail/spectre.console.git
synced 2025-04-16 00:42: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>
|
||||
<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="Shouldly" Version="4.0.3" />
|
||||
<PackageReference Include="Spectre.Verify.Extensions" Version="0.3.0" />
|
||||
@ -29,6 +32,7 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Spectre.Console.Analyzer\Spectre.Console.Analyzer.csproj" />
|
||||
<ProjectReference Include="..\Spectre.Console.Testing\Spectre.Console.Testing.csproj" />
|
||||
<ProjectReference Include="..\Spectre.Console\Spectre.Console.csproj" />
|
||||
</ItemGroup>
|
||||
|
@ -90,6 +90,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Shared", "..\examples\Share
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Live", "..\examples\Console\Live\Live.csproj", "{E607AA2A-A4A6-48E4-8AAB-B0EB74EACAA1}"
|
||||
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
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
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|x86.ActiveCfg = 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
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
@ -508,6 +536,7 @@ Global
|
||||
{A0C772BA-C5F4-451D-AA7A-4045F2FA0201} = {F0575243-121F-4DEE-9F6B-246E26DC0844}
|
||||
{8428A7DD-29FC-4417-9CA0-B90D34B26AB2} = {A0C772BA-C5F4-451D-AA7A-4045F2FA0201}
|
||||
{E607AA2A-A4A6-48E4-8AAB-B0EB74EACAA1} = {F0575243-121F-4DEE-9F6B-246E26DC0844}
|
||||
{D2B32355-D99F-480B-92BF-9FAABE79ADD4} = {A0C772BA-C5F4-451D-AA7A-4045F2FA0201}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {5729B071-67A0-48FB-8B1B-275E6822086C}
|
||||
|
Loading…
x
Reference in New Issue
Block a user