Add some more analyzer tests

This commit is contained in:
Tyrrrz
2022-01-10 23:56:54 +02:00
parent 32ee0b2bd6
commit 0f7cea4ed1
4 changed files with 160 additions and 6 deletions

View File

@@ -4,9 +4,9 @@ using Xunit;
namespace CliFx.Analyzers.Tests; namespace CliFx.Analyzers.Tests;
public class ParameterMustBeLastIfNotRequiredAnalyzerSpecs public class ParameterMustBeLastIfNonRequiredAnalyzerSpecs
{ {
private static DiagnosticAnalyzer Analyzer { get; } = new ParameterMustBeLastIfNotRequiredAnalyzer(); private static DiagnosticAnalyzer Analyzer { get; } = new ParameterMustBeLastIfNonRequiredAnalyzer();
[Fact] [Fact]
public void Analyzer_reports_an_error_if_a_non_required_parameter_is_not_the_last_in_order() public void Analyzer_reports_an_error_if_a_non_required_parameter_is_not_the_last_in_order()
@@ -20,7 +20,7 @@ public class MyCommand : ICommand
[CommandParameter(0, Name = ""foo"", IsRequired = false)] [CommandParameter(0, Name = ""foo"", IsRequired = false)]
public string Foo { get; set; } public string Foo { get; set; }
[CommandParameter(1, Name = ""bar"", IsRequired = false)] [CommandParameter(1, Name = ""bar"")]
public string Bar { get; set; } public string Bar { get; set; }
public ValueTask ExecuteAsync(IConsole console) => default; public ValueTask ExecuteAsync(IConsole console) => default;
@@ -39,7 +39,7 @@ public class MyCommand : ICommand
[Command] [Command]
public class MyCommand : ICommand public class MyCommand : ICommand
{ {
[CommandParameter(0, Name = ""foo"", IsRequired = true)] [CommandParameter(0, Name = ""foo"")]
public string Foo { get; set; } public string Foo { get; set; }
[CommandParameter(1, Name = ""bar"", IsRequired = false)] [CommandParameter(1, Name = ""bar"", IsRequired = false)]

View File

@@ -0,0 +1,94 @@
using CliFx.Analyzers.Tests.Utils;
using Microsoft.CodeAnalysis.Diagnostics;
using Xunit;
namespace CliFx.Analyzers.Tests;
public class ParameterMustBeSingleIfNonRequiredAnalyzerSpecs
{
private static DiagnosticAnalyzer Analyzer { get; } = new ParameterMustBeSingleIfNonRequiredAnalyzer();
[Fact]
public void Analyzer_reports_an_error_if_more_than_one_non_required_parameters_are_defined()
{
// Arrange
// language=cs
const string code = @"
[Command]
public class MyCommand : ICommand
{
[CommandParameter(0, IsRequired = false)]
public string Foo { get; set; }
[CommandParameter(1, IsRequired = false)]
public string Bar { get; set; }
public ValueTask ExecuteAsync(IConsole console) => default;
}";
// Act & assert
Analyzer.Should().ProduceDiagnostics(code);
}
[Fact]
public void Analyzer_does_not_report_an_error_if_only_one_non_required_parameter_is_defined()
{
// Arrange
// language=cs
const string code = @"
[Command]
public class MyCommand : ICommand
{
[CommandParameter(0)]
public string Foo { get; set; }
[CommandParameter(1, IsRequired = false)]
public string Bar { get; set; }
public ValueTask ExecuteAsync(IConsole console) => default;
}";
// Act & assert
Analyzer.Should().NotProduceDiagnostics(code);
}
[Fact]
public void Analyzer_does_not_report_an_error_if_no_non_required_parameters_are_defined()
{
// Arrange
// language=cs
const string code = @"
[Command]
public class MyCommand : ICommand
{
[CommandParameter(0)]
public string Foo { get; set; }
[CommandParameter(1, IsRequired = true)]
public string Bar { get; set; }
public ValueTask ExecuteAsync(IConsole console) => default;
}";
// Act & assert
Analyzer.Should().NotProduceDiagnostics(code);
}
[Fact]
public void Analyzer_does_not_report_an_error_on_a_property_that_is_not_a_parameter()
{
// Arrange
// language=cs
const string code = @"
[Command]
public class MyCommand : ICommand
{
public string Foo { get; set; }
public ValueTask ExecuteAsync(IConsole console) => default;
}";
// Act & assert
Analyzer.Should().NotProduceDiagnostics(code);
}
}

View File

@@ -8,9 +8,9 @@ using Microsoft.CodeAnalysis.Diagnostics;
namespace CliFx.Analyzers; namespace CliFx.Analyzers;
[DiagnosticAnalyzer(LanguageNames.CSharp)] [DiagnosticAnalyzer(LanguageNames.CSharp)]
public class ParameterMustBeLastIfNotRequiredAnalyzer : AnalyzerBase public class ParameterMustBeLastIfNonRequiredAnalyzer : AnalyzerBase
{ {
public ParameterMustBeLastIfNotRequiredAnalyzer() public ParameterMustBeLastIfNonRequiredAnalyzer()
: base( : base(
"Parameters marked as non-required must be the last in order", "Parameters marked as non-required must be the last in order",
"This parameter is non-required so it must be the last in order (its order must be highest within the command).") "This parameter is non-required so it must be the last in order (its order must be highest within the command).")

View File

@@ -0,0 +1,60 @@
using System.Linq;
using CliFx.Analyzers.ObjectModel;
using CliFx.Analyzers.Utils.Extensions;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;
namespace CliFx.Analyzers;
[DiagnosticAnalyzer(LanguageNames.CSharp)]
public class ParameterMustBeSingleIfNonRequiredAnalyzer : AnalyzerBase
{
public ParameterMustBeSingleIfNonRequiredAnalyzer()
: base(
"Parameters marked as non-required are limited to one per command",
"This parameter is non-required so it must be the only such parameter in the command.")
{
}
private void Analyze(
SyntaxNodeAnalysisContext context,
PropertyDeclarationSyntax propertyDeclaration,
IPropertySymbol property)
{
if (property.ContainingType is null)
return;
var parameter = CommandParameterSymbol.TryResolve(property);
if (parameter is null)
return;
if (parameter.IsRequired != false)
return;
var otherProperties = property
.ContainingType
.GetMembers()
.OfType<IPropertySymbol>()
.Where(m => !m.Equals(property, SymbolEqualityComparer.Default))
.ToArray();
foreach (var otherProperty in otherProperties)
{
var otherParameter = CommandParameterSymbol.TryResolve(otherProperty);
if (otherParameter is null)
continue;
if (otherParameter.IsRequired == false)
{
context.ReportDiagnostic(CreateDiagnostic(propertyDeclaration.GetLocation()));
}
}
}
public override void Initialize(AnalysisContext context)
{
base.Initialize(context);
context.HandlePropertyDeclaration(Analyze);
}
}