diff --git a/CliFx.Analyzers.Tests/CliFx.Analyzers.Tests.csproj b/CliFx.Analyzers.Tests/CliFx.Analyzers.Tests.csproj
deleted file mode 100644
index 5520438..0000000
--- a/CliFx.Analyzers.Tests/CliFx.Analyzers.Tests.csproj
+++ /dev/null
@@ -1,28 +0,0 @@
-
-
-
- net8.0
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/CliFx.Analyzers.Tests/CommandMustBeAnnotatedAnalyzerSpecs.cs b/CliFx.Analyzers.Tests/CommandMustBeAnnotatedAnalyzerSpecs.cs
deleted file mode 100644
index 1e90212..0000000
--- a/CliFx.Analyzers.Tests/CommandMustBeAnnotatedAnalyzerSpecs.cs
+++ /dev/null
@@ -1,75 +0,0 @@
-using CliFx.Analyzers.Tests.Utils;
-using Microsoft.CodeAnalysis.Diagnostics;
-using Xunit;
-
-namespace CliFx.Analyzers.Tests;
-
-public class CommandMustBeAnnotatedAnalyzerSpecs
-{
- private static DiagnosticAnalyzer Analyzer { get; } = new CommandMustBeAnnotatedAnalyzer();
-
- [Fact]
- public void Analyzer_reports_an_error_if_a_command_is_not_annotated_with_the_command_attribute()
- {
- // Arrange
- // lang=csharp
- const string code = """
- public class MyCommand : ICommand
- {
- public ValueTask ExecuteAsync(IConsole console) => default;
- }
- """;
-
- // Act & assert
- Analyzer.Should().ProduceDiagnostics(code);
- }
-
- [Fact]
- public void Analyzer_does_not_report_an_error_if_a_command_is_annotated_with_the_command_attribute()
- {
- // Arrange
- // lang=csharp
- const string code = """
- [Command]
- public abstract class MyCommand : ICommand
- {
- public ValueTask ExecuteAsync(IConsole console) => default;
- }
- """;
-
- // Act & assert
- Analyzer.Should().NotProduceDiagnostics(code);
- }
-
- [Fact]
- public void Analyzer_does_not_report_an_error_if_a_command_is_implemented_as_an_abstract_class()
- {
- // Arrange
- // lang=csharp
- const string code = """
- public abstract class MyCommand : ICommand
- {
- public ValueTask ExecuteAsync(IConsole console) => default;
- }
- """;
-
- // Act & assert
- Analyzer.Should().NotProduceDiagnostics(code);
- }
-
- [Fact]
- public void Analyzer_does_not_report_an_error_on_a_class_that_is_not_a_command()
- {
- // Arrange
- // lang=csharp
- const string code = """
- public class Foo
- {
- public int Bar { get; init; } = 5;
- }
- """;
-
- // Act & assert
- Analyzer.Should().NotProduceDiagnostics(code);
- }
-}
diff --git a/CliFx.Analyzers.Tests/CommandMustImplementInterfaceAnalyzerSpecs.cs b/CliFx.Analyzers.Tests/CommandMustImplementInterfaceAnalyzerSpecs.cs
deleted file mode 100644
index 04d7f1c..0000000
--- a/CliFx.Analyzers.Tests/CommandMustImplementInterfaceAnalyzerSpecs.cs
+++ /dev/null
@@ -1,61 +0,0 @@
-using CliFx.Analyzers.Tests.Utils;
-using Microsoft.CodeAnalysis.Diagnostics;
-using Xunit;
-
-namespace CliFx.Analyzers.Tests;
-
-public class CommandMustImplementInterfaceAnalyzerSpecs
-{
- private static DiagnosticAnalyzer Analyzer { get; } =
- new CommandMustImplementInterfaceAnalyzer();
-
- [Fact]
- public void Analyzer_reports_an_error_if_a_command_does_not_implement_ICommand_interface()
- {
- // Arrange
- // lang=csharp
- const string code = """
- [Command]
- public class MyCommand
- {
- public ValueTask ExecuteAsync(IConsole console) => default;
- }
- """;
-
- // Act & assert
- Analyzer.Should().ProduceDiagnostics(code);
- }
-
- [Fact]
- public void Analyzer_does_not_report_an_error_if_a_command_implements_ICommand_interface()
- {
- // Arrange
- // lang=csharp
- const string code = """
- [Command]
- public class MyCommand : ICommand
- {
- public ValueTask ExecuteAsync(IConsole console) => default;
- }
- """;
-
- // Act & assert
- Analyzer.Should().NotProduceDiagnostics(code);
- }
-
- [Fact]
- public void Analyzer_does_not_report_an_error_on_a_class_that_is_not_a_command()
- {
- // Arrange
- // lang=csharp
- const string code = """
- public class Foo
- {
- public int Bar { get; init; } = 5;
- }
- """;
-
- // Act & assert
- Analyzer.Should().NotProduceDiagnostics(code);
- }
-}
diff --git a/CliFx.Analyzers.Tests/GeneralSpecs.cs b/CliFx.Analyzers.Tests/GeneralSpecs.cs
deleted file mode 100644
index 0aaa23a..0000000
--- a/CliFx.Analyzers.Tests/GeneralSpecs.cs
+++ /dev/null
@@ -1,29 +0,0 @@
-using System;
-using System.Linq;
-using FluentAssertions;
-using Microsoft.CodeAnalysis.Diagnostics;
-using Xunit;
-
-namespace CliFx.Analyzers.Tests;
-
-public class GeneralSpecs
-{
- [Fact]
- public void All_analyzers_have_unique_diagnostic_IDs()
- {
- // Arrange
- var analyzers = typeof(AnalyzerBase)
- .Assembly.GetTypes()
- .Where(t => !t.IsAbstract && t.IsAssignableTo(typeof(DiagnosticAnalyzer)))
- .Select(t => (DiagnosticAnalyzer)Activator.CreateInstance(t)!)
- .ToArray();
-
- // Act
- var diagnosticIds = analyzers
- .SelectMany(a => a.SupportedDiagnostics.Select(d => d.Id))
- .ToArray();
-
- // Assert
- diagnosticIds.Should().OnlyHaveUniqueItems();
- }
-}
diff --git a/CliFx.Analyzers.Tests/OptionMustBeInsideCommandAnalyzerSpecs.cs b/CliFx.Analyzers.Tests/OptionMustBeInsideCommandAnalyzerSpecs.cs
deleted file mode 100644
index 97a5121..0000000
--- a/CliFx.Analyzers.Tests/OptionMustBeInsideCommandAnalyzerSpecs.cs
+++ /dev/null
@@ -1,83 +0,0 @@
-using CliFx.Analyzers.Tests.Utils;
-using Microsoft.CodeAnalysis.Diagnostics;
-using Xunit;
-
-namespace CliFx.Analyzers.Tests;
-
-public class OptionMustBeInsideCommandAnalyzerSpecs
-{
- private static DiagnosticAnalyzer Analyzer { get; } = new OptionMustBeInsideCommandAnalyzer();
-
- [Fact]
- public void Analyzer_reports_an_error_if_an_option_is_inside_a_class_that_is_not_a_command()
- {
- // Arrange
- // lang=csharp
- const string code = """
- public class MyClass
- {
- [CommandOption("foo")]
- public string? Foo { get; init; }
- }
- """;
-
- // Act & assert
- Analyzer.Should().ProduceDiagnostics(code);
- }
-
- [Fact]
- public void Analyzer_does_not_report_an_error_if_an_option_is_inside_a_command()
- {
- // Arrange
- // lang=csharp
- const string code = """
- [Command]
- public class MyCommand : ICommand
- {
- [CommandOption("foo")]
- public string? Foo { get; init; }
-
- public ValueTask ExecuteAsync(IConsole console) => default;
- }
- """;
-
- // Act & assert
- Analyzer.Should().NotProduceDiagnostics(code);
- }
-
- [Fact]
- public void Analyzer_does_not_report_an_error_if_an_option_is_inside_an_abstract_class()
- {
- // Arrange
- // lang=csharp
- const string code = """
- public abstract class MyCommand
- {
- [CommandOption("foo")]
- public string? Foo { get; init; }
- }
- """;
-
- // Act & assert
- Analyzer.Should().NotProduceDiagnostics(code);
- }
-
- [Fact]
- public void Analyzer_does_not_report_an_error_on_a_property_that_is_not_an_option()
- {
- // Arrange
- // lang=csharp
- const string code = """
- [Command]
- public class MyCommand : ICommand
- {
- public string? Foo { get; init; }
-
- public ValueTask ExecuteAsync(IConsole console) => default;
- }
- """;
-
- // Act & assert
- Analyzer.Should().NotProduceDiagnostics(code);
- }
-}
diff --git a/CliFx.Analyzers.Tests/OptionMustBeRequiredIfPropertyRequiredAnalyzerSpecs.cs b/CliFx.Analyzers.Tests/OptionMustBeRequiredIfPropertyRequiredAnalyzerSpecs.cs
deleted file mode 100644
index f41468c..0000000
--- a/CliFx.Analyzers.Tests/OptionMustBeRequiredIfPropertyRequiredAnalyzerSpecs.cs
+++ /dev/null
@@ -1,110 +0,0 @@
-using CliFx.Analyzers.Tests.Utils;
-using Microsoft.CodeAnalysis.Diagnostics;
-using Xunit;
-
-namespace CliFx.Analyzers.Tests;
-
-public class OptionMustBeRequiredIfPropertyRequiredAnalyzerSpecs
-{
- private static DiagnosticAnalyzer Analyzer { get; } =
- new OptionMustBeRequiredIfPropertyRequiredAnalyzer();
-
- [Fact]
- public void Analyzer_reports_an_error_if_a_non_required_option_is_bound_to_a_required_property()
- {
- // Arrange
- // lang=csharp
- const string code = """
- [Command]
- public class MyCommand : ICommand
- {
- [CommandOption('f', IsRequired = false)]
- public required string Foo { get; init; }
-
- public ValueTask ExecuteAsync(IConsole console) => default;
- }
- """;
-
- // Act & assert
- Analyzer.Should().ProduceDiagnostics(code);
- }
-
- [Fact]
- public void Analyzer_does_not_report_an_error_if_a_required_option_is_bound_to_a_required_property()
- {
- // Arrange
- // lang=csharp
- const string code = """
- [Command]
- public class MyCommand : ICommand
- {
- [CommandOption('f')]
- public required string Foo { get; init; }
-
- public ValueTask ExecuteAsync(IConsole console) => default;
- }
- """;
-
- // Act & assert
- Analyzer.Should().NotProduceDiagnostics(code);
- }
-
- [Fact]
- public void Analyzer_does_not_report_an_error_if_a_non_required_option_is_bound_to_a_non_required_property()
- {
- // Arrange
- // lang=csharp
- const string code = """
- [Command]
- public class MyCommand : ICommand
- {
- [CommandOption('f', IsRequired = false)]
- public string? Foo { get; init; }
-
- public ValueTask ExecuteAsync(IConsole console) => default;
- }
- """;
-
- // Act & assert
- Analyzer.Should().NotProduceDiagnostics(code);
- }
-
- [Fact]
- public void Analyzer_does_not_report_an_error_if_a_required_option_is_bound_to_a_non_required_property()
- {
- // Arrange
- // lang=csharp
- const string code = """
- [Command]
- public class MyCommand : ICommand
- {
- [CommandOption('f')]
- public required string Foo { get; init; }
-
- 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_an_option()
- {
- // Arrange
- // lang=csharp
- const string code = """
- [Command]
- public class MyCommand : ICommand
- {
- public required string Foo { get; init; }
-
- public ValueTask ExecuteAsync(IConsole console) => default;
- }
- """;
-
- // Act & assert
- Analyzer.Should().NotProduceDiagnostics(code);
- }
-}
diff --git a/CliFx.Analyzers.Tests/OptionMustHaveNameOrShortNameAnalyzerSpecs.cs b/CliFx.Analyzers.Tests/OptionMustHaveNameOrShortNameAnalyzerSpecs.cs
deleted file mode 100644
index 2eff6f5..0000000
--- a/CliFx.Analyzers.Tests/OptionMustHaveNameOrShortNameAnalyzerSpecs.cs
+++ /dev/null
@@ -1,90 +0,0 @@
-using CliFx.Analyzers.Tests.Utils;
-using Microsoft.CodeAnalysis.Diagnostics;
-using Xunit;
-
-namespace CliFx.Analyzers.Tests;
-
-public class OptionMustHaveNameOrShortNameAnalyzerSpecs
-{
- private static DiagnosticAnalyzer Analyzer { get; } =
- new OptionMustHaveNameOrShortNameAnalyzer();
-
- [Fact]
- public void Analyzer_reports_an_error_if_an_option_does_not_have_a_name_or_short_name()
- {
- // Arrange
- // lang=csharp
- const string code = """
- [Command]
- public class MyCommand : ICommand
- {
- [CommandOption(null)]
- public string? Foo { get; init; }
-
- public ValueTask ExecuteAsync(IConsole console) => default;
- }
- """;
-
- // Act & assert
- Analyzer.Should().ProduceDiagnostics(code);
- }
-
- [Fact]
- public void Analyzer_does_not_report_an_error_if_an_option_has_a_name()
- {
- // Arrange
- // lang=csharp
- const string code = """
- [Command]
- public class MyCommand : ICommand
- {
- [CommandOption("foo")]
- public string? Foo { get; init; }
-
- public ValueTask ExecuteAsync(IConsole console) => default;
- }
- """;
-
- // Act & assert
- Analyzer.Should().NotProduceDiagnostics(code);
- }
-
- [Fact]
- public void Analyzer_does_not_report_an_error_if_an_option_has_a_short_name()
- {
- // Arrange
- // lang=csharp
- const string code = """
- [Command]
- public class MyCommand : ICommand
- {
- [CommandOption('f')]
- public string? Foo { get; init; }
-
- 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_an_option()
- {
- // Arrange
- // lang=csharp
- const string code = """
- [Command]
- public class MyCommand : ICommand
- {
- public string? Foo { get; init; }
-
- public ValueTask ExecuteAsync(IConsole console) => default;
- }
- """;
-
- // Act & assert
- Analyzer.Should().NotProduceDiagnostics(code);
- }
-}
diff --git a/CliFx.Analyzers.Tests/OptionMustHaveUniqueNameAnalyzerSpecs.cs b/CliFx.Analyzers.Tests/OptionMustHaveUniqueNameAnalyzerSpecs.cs
deleted file mode 100644
index 3a70039..0000000
--- a/CliFx.Analyzers.Tests/OptionMustHaveUniqueNameAnalyzerSpecs.cs
+++ /dev/null
@@ -1,95 +0,0 @@
-using CliFx.Analyzers.Tests.Utils;
-using Microsoft.CodeAnalysis.Diagnostics;
-using Xunit;
-
-namespace CliFx.Analyzers.Tests;
-
-public class OptionMustHaveUniqueNameAnalyzerSpecs
-{
- private static DiagnosticAnalyzer Analyzer { get; } = new OptionMustHaveUniqueNameAnalyzer();
-
- [Fact]
- public void Analyzer_reports_an_error_if_an_option_has_the_same_name_as_another_option()
- {
- // Arrange
- // lang=csharp
- const string code = """
- [Command]
- public class MyCommand : ICommand
- {
- [CommandOption("foo")]
- public string? Foo { get; init; }
-
- [CommandOption("foo")]
- public string? Bar { get; init; }
-
- public ValueTask ExecuteAsync(IConsole console) => default;
- }
- """;
-
- // Act & assert
- Analyzer.Should().ProduceDiagnostics(code);
- }
-
- [Fact]
- public void Analyzer_does_not_report_an_error_if_an_option_has_a_unique_name()
- {
- // Arrange
- // lang=csharp
- const string code = """
- [Command]
- public class MyCommand : ICommand
- {
- [CommandOption("foo")]
- public string? Foo { get; init; }
-
- [CommandOption("bar")]
- public string? Bar { get; init; }
-
- public ValueTask ExecuteAsync(IConsole console) => default;
- }
- """;
-
- // Act & assert
- Analyzer.Should().NotProduceDiagnostics(code);
- }
-
- [Fact]
- public void Analyzer_does_not_report_an_error_if_an_option_does_not_have_a_name()
- {
- // Arrange
- // lang=csharp
- const string code = """
- [Command]
- public class MyCommand : ICommand
- {
- [CommandOption('f')]
- public string? Foo { get; init; }
-
- 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_an_option()
- {
- // Arrange
- // lang=csharp
- const string code = """
- [Command]
- public class MyCommand : ICommand
- {
- public string? Foo { get; init; }
-
- public ValueTask ExecuteAsync(IConsole console) => default;
- }
- """;
-
- // Act & assert
- Analyzer.Should().NotProduceDiagnostics(code);
- }
-}
diff --git a/CliFx.Analyzers.Tests/OptionMustHaveUniqueShortNameAnalyzerSpecs.cs b/CliFx.Analyzers.Tests/OptionMustHaveUniqueShortNameAnalyzerSpecs.cs
deleted file mode 100644
index 09c2f82..0000000
--- a/CliFx.Analyzers.Tests/OptionMustHaveUniqueShortNameAnalyzerSpecs.cs
+++ /dev/null
@@ -1,119 +0,0 @@
-using CliFx.Analyzers.Tests.Utils;
-using Microsoft.CodeAnalysis.Diagnostics;
-using Xunit;
-
-namespace CliFx.Analyzers.Tests;
-
-public class OptionMustHaveUniqueShortNameAnalyzerSpecs
-{
- private static DiagnosticAnalyzer Analyzer { get; } =
- new OptionMustHaveUniqueShortNameAnalyzer();
-
- [Fact]
- public void Analyzer_reports_an_error_if_an_option_has_the_same_short_name_as_another_option()
- {
- // Arrange
- // lang=csharp
- const string code = """
- [Command]
- public class MyCommand : ICommand
- {
- [CommandOption('f')]
- public string? Foo { get; init; }
-
- [CommandOption('f')]
- public string? Bar { get; init; }
-
- public ValueTask ExecuteAsync(IConsole console) => default;
- }
- """;
-
- // Act & assert
- Analyzer.Should().ProduceDiagnostics(code);
- }
-
- [Fact]
- public void Analyzer_does_not_report_an_error_if_an_option_has_a_unique_short_name()
- {
- // Arrange
- // lang=csharp
- const string code = """
- [Command]
- public class MyCommand : ICommand
- {
- [CommandOption('f')]
- public string? Foo { get; init; }
-
- [CommandOption('b')]
- public string? Bar { get; init; }
-
- public ValueTask ExecuteAsync(IConsole console) => default;
- }
- """;
-
- // Act & assert
- Analyzer.Should().NotProduceDiagnostics(code);
- }
-
- [Fact]
- public void Analyzer_does_not_report_an_error_if_an_option_has_a_short_name_which_is_unique_only_in_casing()
- {
- // Arrange
- // lang=csharp
- const string code = """
- [Command]
- public class MyCommand : ICommand
- {
- [CommandOption('f')]
- public string? Foo { get; init; }
-
- [CommandOption('F')]
- public string? Bar { get; init; }
-
- public ValueTask ExecuteAsync(IConsole console) => default;
- }
- """;
-
- // Act & assert
- Analyzer.Should().NotProduceDiagnostics(code);
- }
-
- [Fact]
- public void Analyzer_does_not_report_an_error_if_an_option_does_not_have_a_short_name()
- {
- // Arrange
- // lang=csharp
- const string code = """
- [Command]
- public class MyCommand : ICommand
- {
- [CommandOption("foo")]
- public string? Foo { get; init; }
-
- 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_an_option()
- {
- // Arrange
- // lang=csharp
- const string code = """
- [Command]
- public class MyCommand : ICommand
- {
- public string? Foo { get; init; }
-
- public ValueTask ExecuteAsync(IConsole console) => default;
- }
- """;
-
- // Act & assert
- Analyzer.Should().NotProduceDiagnostics(code);
- }
-}
diff --git a/CliFx.Analyzers.Tests/OptionMustHaveValidConverterAnalyzerSpecs.cs b/CliFx.Analyzers.Tests/OptionMustHaveValidConverterAnalyzerSpecs.cs
deleted file mode 100644
index dc163ab..0000000
--- a/CliFx.Analyzers.Tests/OptionMustHaveValidConverterAnalyzerSpecs.cs
+++ /dev/null
@@ -1,175 +0,0 @@
-using CliFx.Analyzers.Tests.Utils;
-using Microsoft.CodeAnalysis.Diagnostics;
-using Xunit;
-
-namespace CliFx.Analyzers.Tests;
-
-public class OptionMustHaveValidConverterAnalyzerSpecs
-{
- private static DiagnosticAnalyzer Analyzer { get; } =
- new OptionMustHaveValidConverterAnalyzer();
-
- [Fact]
- public void Analyzer_reports_an_error_if_an_option_has_a_converter_that_does_not_derive_from_BindingConverter()
- {
- // Arrange
- // lang=csharp
- const string code = """
- public class MyConverter
- {
- public string Convert(string? rawValue) => rawValue;
- }
-
- [Command]
- public class MyCommand : ICommand
- {
- [CommandOption("foo", Converter = typeof(MyConverter))]
- public string? Foo { get; init; }
-
- public ValueTask ExecuteAsync(IConsole console) => default;
- }
- """;
-
- // Act & assert
- Analyzer.Should().ProduceDiagnostics(code);
- }
-
- [Fact]
- public void Analyzer_reports_an_error_if_an_option_has_a_converter_that_does_not_derive_from_a_compatible_BindingConverter()
- {
- // Arrange
- // lang=csharp
- const string code = """
- public class MyConverter : BindingConverter
- {
- public override int Convert(string? rawValue) => 42;
- }
-
- [Command]
- public class MyCommand : ICommand
- {
- [CommandOption("foo", Converter = typeof(MyConverter))]
- public string? Foo { get; init; }
-
- public ValueTask ExecuteAsync(IConsole console) => default;
- }
- """;
-
- // Act & assert
- Analyzer.Should().ProduceDiagnostics(code);
- }
-
- [Fact]
- public void Analyzer_does_not_report_an_error_if_an_option_has_a_converter_that_derives_from_a_compatible_BindingConverter()
- {
- // Arrange
- // lang=csharp
- const string code = """
- public class MyConverter : BindingConverter
- {
- public override string Convert(string? rawValue) => rawValue;
- }
-
- [Command]
- public class MyCommand : ICommand
- {
- [CommandOption("foo", Converter = typeof(MyConverter))]
- public string? Foo { get; init; }
-
- public ValueTask ExecuteAsync(IConsole console) => default;
- }
- """;
-
- // Act & assert
- Analyzer.Should().NotProduceDiagnostics(code);
- }
-
- [Fact]
- public void Analyzer_does_not_report_an_error_if_a_nullable_option_has_a_converter_that_derives_from_a_compatible_BindingConverter()
- {
- // Arrange
- // lang=csharp
- const string code = """
- public class MyConverter : BindingConverter
- {
- public override int Convert(string? rawValue) => 42;
- }
-
- [Command]
- public class MyCommand : ICommand
- {
- [CommandOption("foo", Converter = typeof(MyConverter))]
- public int? Foo { get; init; }
-
- public ValueTask ExecuteAsync(IConsole console) => default;
- }
- """;
-
- // Act & assert
- Analyzer.Should().NotProduceDiagnostics(code);
- }
-
- [Fact]
- public void Analyzer_does_not_report_an_error_if_a_non_scalar_option_has_a_converter_that_derives_from_a_compatible_BindingConverter()
- {
- // Arrange
- // lang=csharp
- const string code = """
- public class MyConverter : BindingConverter
- {
- public override string Convert(string? rawValue) => rawValue;
- }
-
- [Command]
- public class MyCommand : ICommand
- {
- [CommandOption("foo", Converter = typeof(MyConverter))]
- public IReadOnlyList? Foo { get; init; }
-
- public ValueTask ExecuteAsync(IConsole console) => default;
- }
- """;
-
- // Act & assert
- Analyzer.Should().NotProduceDiagnostics(code);
- }
-
- [Fact]
- public void Analyzer_does_not_report_an_error_if_an_option_does_not_have_a_converter()
- {
- // Arrange
- // lang=csharp
- const string code = """
- [Command]
- public class MyCommand : ICommand
- {
- [CommandOption("foo")]
- public string? Foo { get; init; }
-
- 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_an_option()
- {
- // Arrange
- // lang=csharp
- const string code = """
- [Command]
- public class MyCommand : ICommand
- {
- public string? Foo { get; init; }
-
- public ValueTask ExecuteAsync(IConsole console) => default;
- }
- """;
-
- // Act & assert
- Analyzer.Should().NotProduceDiagnostics(code);
- }
-}
diff --git a/CliFx.Analyzers.Tests/OptionMustHaveValidNameAnalyzerSpecs.cs b/CliFx.Analyzers.Tests/OptionMustHaveValidNameAnalyzerSpecs.cs
deleted file mode 100644
index 2f4ab73..0000000
--- a/CliFx.Analyzers.Tests/OptionMustHaveValidNameAnalyzerSpecs.cs
+++ /dev/null
@@ -1,109 +0,0 @@
-using CliFx.Analyzers.Tests.Utils;
-using Microsoft.CodeAnalysis.Diagnostics;
-using Xunit;
-
-namespace CliFx.Analyzers.Tests;
-
-public class OptionMustHaveValidNameAnalyzerSpecs
-{
- private static DiagnosticAnalyzer Analyzer { get; } = new OptionMustHaveValidNameAnalyzer();
-
- [Fact]
- public void Analyzer_reports_an_error_if_an_option_has_a_name_which_is_too_short()
- {
- // Arrange
- // lang=csharp
- const string code = """
- [Command]
- public class MyCommand : ICommand
- {
- [CommandOption("f")]
- public string? Foo { get; init; }
-
- public ValueTask ExecuteAsync(IConsole console) => default;
- }
- """;
-
- // Act & assert
- Analyzer.Should().ProduceDiagnostics(code);
- }
-
- [Fact]
- public void Analyzer_reports_an_error_if_an_option_has_a_name_that_starts_with_a_non_letter_character()
- {
- // Arrange
- // lang=csharp
- const string code = """
- [Command]
- public class MyCommand : ICommand
- {
- [CommandOption("1foo")]
- public string? Foo { get; init; }
-
- public ValueTask ExecuteAsync(IConsole console) => default;
- }
- """;
-
- // Act & assert
- Analyzer.Should().ProduceDiagnostics(code);
- }
-
- [Fact]
- public void Analyzer_does_not_report_an_error_if_an_option_has_a_valid_name()
- {
- // Arrange
- // lang=csharp
- const string code = """
- [Command]
- public class MyCommand : ICommand
- {
- [CommandOption("foo")]
- public string? Foo { get; init; }
-
- public ValueTask ExecuteAsync(IConsole console) => default;
- }
- """;
-
- // Act & assert
- Analyzer.Should().NotProduceDiagnostics(code);
- }
-
- [Fact]
- public void Analyzer_does_not_report_an_error_if_an_option_does_not_have_a_name()
- {
- // Arrange
- // lang=csharp
- const string code = """
- [Command]
- public class MyCommand : ICommand
- {
- [CommandOption('f')]
- public string? Foo { get; init; }
-
- 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_an_option()
- {
- // Arrange
- // lang=csharp
- const string code = """
- [Command]
- public class MyCommand : ICommand
- {
- public string? Foo { get; init; }
-
- public ValueTask ExecuteAsync(IConsole console) => default;
- }
- """;
-
- // Act & assert
- Analyzer.Should().NotProduceDiagnostics(code);
- }
-}
diff --git a/CliFx.Analyzers.Tests/OptionMustHaveValidShortNameAnalyzerSpecs.cs b/CliFx.Analyzers.Tests/OptionMustHaveValidShortNameAnalyzerSpecs.cs
deleted file mode 100644
index e810e79..0000000
--- a/CliFx.Analyzers.Tests/OptionMustHaveValidShortNameAnalyzerSpecs.cs
+++ /dev/null
@@ -1,90 +0,0 @@
-using CliFx.Analyzers.Tests.Utils;
-using Microsoft.CodeAnalysis.Diagnostics;
-using Xunit;
-
-namespace CliFx.Analyzers.Tests;
-
-public class OptionMustHaveValidShortNameAnalyzerSpecs
-{
- private static DiagnosticAnalyzer Analyzer { get; } =
- new OptionMustHaveValidShortNameAnalyzer();
-
- [Fact]
- public void Analyzer_reports_an_error_if_an_option_has_a_short_name_which_is_not_a_letter_character()
- {
- // Arrange
- // lang=csharp
- const string code = """
- [Command]
- public class MyCommand : ICommand
- {
- [CommandOption('1')]
- public string? Foo { get; init; }
-
- public ValueTask ExecuteAsync(IConsole console) => default;
- }
- """;
-
- // Act & assert
- Analyzer.Should().ProduceDiagnostics(code);
- }
-
- [Fact]
- public void Analyzer_does_not_report_an_error_if_an_option_has_a_valid_short_name()
- {
- // Arrange
- // lang=csharp
- const string code = """
- [Command]
- public class MyCommand : ICommand
- {
- [CommandOption('f')]
- public string? Foo { get; init; }
-
- public ValueTask ExecuteAsync(IConsole console) => default;
- }
- """;
-
- // Act & assert
- Analyzer.Should().NotProduceDiagnostics(code);
- }
-
- [Fact]
- public void Analyzer_does_not_report_an_error_if_an_option_does_not_have_a_short_name()
- {
- // Arrange
- // lang=csharp
- const string code = """
- [Command]
- public class MyCommand : ICommand
- {
- [CommandOption("foo")]
- public string? Foo { get; init; }
-
- 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_an_option()
- {
- // Arrange
- // lang=csharp
- const string code = """
- [Command]
- public class MyCommand : ICommand
- {
- public string? Foo { get; init; }
-
- public ValueTask ExecuteAsync(IConsole console) => default;
- }
- """;
-
- // Act & assert
- Analyzer.Should().NotProduceDiagnostics(code);
- }
-}
diff --git a/CliFx.Analyzers.Tests/OptionMustHaveValidValidatorsAnalyzerSpecs.cs b/CliFx.Analyzers.Tests/OptionMustHaveValidValidatorsAnalyzerSpecs.cs
deleted file mode 100644
index ebd2546..0000000
--- a/CliFx.Analyzers.Tests/OptionMustHaveValidValidatorsAnalyzerSpecs.cs
+++ /dev/null
@@ -1,125 +0,0 @@
-using CliFx.Analyzers.Tests.Utils;
-using Microsoft.CodeAnalysis.Diagnostics;
-using Xunit;
-
-namespace CliFx.Analyzers.Tests;
-
-public class OptionMustHaveValidValidatorsAnalyzerSpecs
-{
- private static DiagnosticAnalyzer Analyzer { get; } =
- new OptionMustHaveValidValidatorsAnalyzer();
-
- [Fact]
- public void Analyzer_reports_an_error_if_an_option_has_a_validator_that_does_not_derive_from_BindingValidator()
- {
- // Arrange
- // lang=csharp
- const string code = """
- public class MyValidator
- {
- public void Validate(string value) {}
- }
-
- [Command]
- public class MyCommand : ICommand
- {
- [CommandOption("foo", Validators = new[] { typeof(MyValidator) })]
- public string? Foo { get; init; }
-
- public ValueTask ExecuteAsync(IConsole console) => default;
- }
- """;
-
- // Act & assert
- Analyzer.Should().ProduceDiagnostics(code);
- }
-
- [Fact]
- public void Analyzer_reports_an_error_if_an_option_has_a_validator_that_does_not_derive_from_a_compatible_BindingValidator()
- {
- // Arrange
- // lang=csharp
- const string code = """
- public class MyValidator : BindingValidator
- {
- public override BindingValidationError Validate(int value) => Ok();
- }
-
- [Command]
- public class MyCommand : ICommand
- {
- [CommandOption("foo", Validators = new[] { typeof(MyValidator) })]
- public string? Foo { get; init; }
-
- public ValueTask ExecuteAsync(IConsole console) => default;
- }
- """;
-
- // Act & assert
- Analyzer.Should().ProduceDiagnostics(code);
- }
-
- [Fact]
- public void Analyzer_does_not_report_an_error_if_an_option_has_validators_that_all_derive_from_compatible_BindingValidators()
- {
- // Arrange
- // lang=csharp
- const string code = """
- public class MyValidator : BindingValidator
- {
- public override BindingValidationError Validate(string value) => Ok();
- }
-
- [Command]
- public class MyCommand : ICommand
- {
- [CommandOption("foo", Validators = new[] { typeof(MyValidator) })]
- public string? Foo { get; init; }
-
- public ValueTask ExecuteAsync(IConsole console) => default;
- }
- """;
-
- // Act & assert
- Analyzer.Should().NotProduceDiagnostics(code);
- }
-
- [Fact]
- public void Analyzer_does_not_report_an_error_if_an_option_does_not_have_validators()
- {
- // Arrange
- // lang=csharp
- const string code = """
- [Command]
- public class MyCommand : ICommand
- {
- [CommandOption("foo")]
- public string? Foo { get; init; }
-
- 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_an_option()
- {
- // Arrange
- // lang=csharp
- const string code = """
- [Command]
- public class MyCommand : ICommand
- {
- public string? Foo { get; init; }
-
- public ValueTask ExecuteAsync(IConsole console) => default;
- }
- """;
-
- // Act & assert
- Analyzer.Should().NotProduceDiagnostics(code);
- }
-}
diff --git a/CliFx.Analyzers.Tests/ParameterMustBeInsideCommandAnalyzerSpecs.cs b/CliFx.Analyzers.Tests/ParameterMustBeInsideCommandAnalyzerSpecs.cs
deleted file mode 100644
index b7b4b36..0000000
--- a/CliFx.Analyzers.Tests/ParameterMustBeInsideCommandAnalyzerSpecs.cs
+++ /dev/null
@@ -1,84 +0,0 @@
-using CliFx.Analyzers.Tests.Utils;
-using Microsoft.CodeAnalysis.Diagnostics;
-using Xunit;
-
-namespace CliFx.Analyzers.Tests;
-
-public class ParameterMustBeInsideCommandAnalyzerSpecs
-{
- private static DiagnosticAnalyzer Analyzer { get; } =
- new ParameterMustBeInsideCommandAnalyzer();
-
- [Fact]
- public void Analyzer_reports_an_error_if_a_parameter_is_inside_a_class_that_is_not_a_command()
- {
- // Arrange
- // lang=csharp
- const string code = """
- public class MyClass
- {
- [CommandParameter(0)]
- public required string Foo { get; init; }
- }
- """;
-
- // Act & assert
- Analyzer.Should().ProduceDiagnostics(code);
- }
-
- [Fact]
- public void Analyzer_does_not_report_an_error_if_a_parameter_is_inside_a_command()
- {
- // Arrange
- // lang=csharp
- const string code = """
- [Command]
- public class MyCommand : ICommand
- {
- [CommandParameter(0)]
- public required string Foo { get; init; }
-
- public ValueTask ExecuteAsync(IConsole console) => default;
- }
- """;
-
- // Act & assert
- Analyzer.Should().NotProduceDiagnostics(code);
- }
-
- [Fact]
- public void Analyzer_does_not_report_an_error_if_a_parameter_is_inside_an_abstract_class()
- {
- // Arrange
- // lang=csharp
- const string code = """
- public abstract class MyCommand
- {
- [CommandParameter(0)]
- public required string Foo { get; init; }
- }
- """;
-
- // Act & assert
- Analyzer.Should().NotProduceDiagnostics(code);
- }
-
- [Fact]
- public void Analyzer_does_not_report_an_error_on_a_property_that_is_not_a_parameter()
- {
- // Arrange
- // lang=csharp
- const string code = """
- [Command]
- public class MyCommand : ICommand
- {
- public string? Foo { get; init; }
-
- public ValueTask ExecuteAsync(IConsole console) => default;
- }
- """;
-
- // Act & assert
- Analyzer.Should().NotProduceDiagnostics(code);
- }
-}
diff --git a/CliFx.Analyzers.Tests/ParameterMustBeLastIfNonRequiredAnalyzerSpecs.cs b/CliFx.Analyzers.Tests/ParameterMustBeLastIfNonRequiredAnalyzerSpecs.cs
deleted file mode 100644
index 3f4bb66..0000000
--- a/CliFx.Analyzers.Tests/ParameterMustBeLastIfNonRequiredAnalyzerSpecs.cs
+++ /dev/null
@@ -1,99 +0,0 @@
-using CliFx.Analyzers.Tests.Utils;
-using Microsoft.CodeAnalysis.Diagnostics;
-using Xunit;
-
-namespace CliFx.Analyzers.Tests;
-
-public class ParameterMustBeLastIfNonRequiredAnalyzerSpecs
-{
- private static DiagnosticAnalyzer Analyzer { get; } =
- new ParameterMustBeLastIfNonRequiredAnalyzer();
-
- [Fact]
- public void Analyzer_reports_an_error_if_a_non_required_parameter_is_not_the_last_in_order()
- {
- // Arrange
- // lang=csharp
- const string code = """
- [Command]
- public class MyCommand : ICommand
- {
- [CommandParameter(0, IsRequired = false)]
- public string? Foo { get; init; }
-
- [CommandParameter(1)]
- public required string Bar { get; init; }
-
- public ValueTask ExecuteAsync(IConsole console) => default;
- }
- """;
-
- // Act & assert
- Analyzer.Should().ProduceDiagnostics(code);
- }
-
- [Fact]
- public void Analyzer_does_not_report_an_error_if_a_non_required_parameter_is_the_last_in_order()
- {
- // Arrange
- // lang=csharp
- const string code = """
- [Command]
- public class MyCommand : ICommand
- {
- [CommandParameter(0)]
- public required string Foo { get; init; }
-
- [CommandParameter(1, IsRequired = false)]
- public string? Bar { get; init; }
-
- 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
- // lang=csharp
- const string code = """
- [Command]
- public class MyCommand : ICommand
- {
- [CommandParameter(0)]
- public required string Foo { get; init; }
-
- [CommandParameter(1)]
- public required string Bar { get; init; }
-
- 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
- // lang=csharp
- const string code = """
- [Command]
- public class MyCommand : ICommand
- {
- public string? Foo { get; init; }
-
- public ValueTask ExecuteAsync(IConsole console) => default;
- }
- """;
-
- // Act & assert
- Analyzer.Should().NotProduceDiagnostics(code);
- }
-}
diff --git a/CliFx.Analyzers.Tests/ParameterMustBeLastIfNonScalarAnalyzerSpecs.cs b/CliFx.Analyzers.Tests/ParameterMustBeLastIfNonScalarAnalyzerSpecs.cs
deleted file mode 100644
index abe13db..0000000
--- a/CliFx.Analyzers.Tests/ParameterMustBeLastIfNonScalarAnalyzerSpecs.cs
+++ /dev/null
@@ -1,99 +0,0 @@
-using CliFx.Analyzers.Tests.Utils;
-using Microsoft.CodeAnalysis.Diagnostics;
-using Xunit;
-
-namespace CliFx.Analyzers.Tests;
-
-public class ParameterMustBeLastIfNonScalarAnalyzerSpecs
-{
- private static DiagnosticAnalyzer Analyzer { get; } =
- new ParameterMustBeLastIfNonScalarAnalyzer();
-
- [Fact]
- public void Analyzer_reports_an_error_if_a_non_scalar_parameter_is_not_the_last_in_order()
- {
- // Arrange
- // lang=csharp
- const string code = """
- [Command]
- public class MyCommand : ICommand
- {
- [CommandParameter(0)]
- public required string[] Foo { get; init; }
-
- [CommandParameter(1)]
- public required string Bar { get; init; }
-
- public ValueTask ExecuteAsync(IConsole console) => default;
- }
- """;
-
- // Act & assert
- Analyzer.Should().ProduceDiagnostics(code);
- }
-
- [Fact]
- public void Analyzer_does_not_report_an_error_if_a_non_scalar_parameter_is_the_last_in_order()
- {
- // Arrange
- // lang=csharp
- const string code = """
- [Command]
- public class MyCommand : ICommand
- {
- [CommandParameter(0)]
- public required string Foo { get; init; }
-
- [CommandParameter(1)]
- public required string[] Bar { get; init; }
-
- public ValueTask ExecuteAsync(IConsole console) => default;
- }
- """;
-
- // Act & assert
- Analyzer.Should().NotProduceDiagnostics(code);
- }
-
- [Fact]
- public void Analyzer_does_not_report_an_error_if_no_non_scalar_parameters_are_defined()
- {
- // Arrange
- // lang=csharp
- const string code = """
- [Command]
- public class MyCommand : ICommand
- {
- [CommandParameter(0)]
- public required string Foo { get; init; }
-
- [CommandParameter(1)]
- public required string Bar { get; init; }
-
- 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
- // lang=csharp
- const string code = """
- [Command]
- public class MyCommand : ICommand
- {
- public string? Foo { get; init; }
-
- public ValueTask ExecuteAsync(IConsole console) => default;
- }
- """;
-
- // Act & assert
- Analyzer.Should().NotProduceDiagnostics(code);
- }
-}
diff --git a/CliFx.Analyzers.Tests/ParameterMustBeRequiredIfPropertyRequiredAnalyzerSpecs.cs b/CliFx.Analyzers.Tests/ParameterMustBeRequiredIfPropertyRequiredAnalyzerSpecs.cs
deleted file mode 100644
index fe2162f..0000000
--- a/CliFx.Analyzers.Tests/ParameterMustBeRequiredIfPropertyRequiredAnalyzerSpecs.cs
+++ /dev/null
@@ -1,110 +0,0 @@
-using CliFx.Analyzers.Tests.Utils;
-using Microsoft.CodeAnalysis.Diagnostics;
-using Xunit;
-
-namespace CliFx.Analyzers.Tests;
-
-public class ParameterMustBeRequiredIfPropertyRequiredAnalyzerSpecs
-{
- private static DiagnosticAnalyzer Analyzer { get; } =
- new ParameterMustBeRequiredIfPropertyRequiredAnalyzer();
-
- [Fact]
- public void Analyzer_reports_an_error_if_a_non_required_parameter_is_bound_to_a_required_property()
- {
- // Arrange
- // lang=csharp
- const string code = """
- [Command]
- public class MyCommand : ICommand
- {
- [CommandParameter(0, IsRequired = false)]
- public required string? Foo { get; init; }
-
- public ValueTask ExecuteAsync(IConsole console) => default;
- }
- """;
-
- // Act & assert
- Analyzer.Should().ProduceDiagnostics(code);
- }
-
- [Fact]
- public void Analyzer_does_not_report_an_error_if_a_required_parameter_is_bound_to_a_required_property()
- {
- // Arrange
- // lang=csharp
- const string code = """
- [Command]
- public class MyCommand : ICommand
- {
- [CommandParameter(0)]
- public required string Foo { get; init; }
-
- public ValueTask ExecuteAsync(IConsole console) => default;
- }
- """;
-
- // Act & assert
- Analyzer.Should().NotProduceDiagnostics(code);
- }
-
- [Fact]
- public void Analyzer_does_not_report_an_error_if_a_non_required_parameter_is_bound_to_a_non_required_property()
- {
- // Arrange
- // lang=csharp
- const string code = """
- [Command]
- public class MyCommand : ICommand
- {
- [CommandParameter(0, IsRequired = false)]
- public string? Foo { get; init; }
-
- public ValueTask ExecuteAsync(IConsole console) => default;
- }
- """;
-
- // Act & assert
- Analyzer.Should().NotProduceDiagnostics(code);
- }
-
- [Fact]
- public void Analyzer_does_not_report_an_error_if_a_required_parameter_is_bound_to_a_non_required_property()
- {
- // Arrange
- // lang=csharp
- const string code = """
- [Command]
- public class MyCommand : ICommand
- {
- [CommandParameter(0)]
- public required string Foo { get; init; }
-
- 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
- // lang=csharp
- const string code = """
- [Command]
- public class MyCommand : ICommand
- {
- public required string Foo { get; init; }
-
- public ValueTask ExecuteAsync(IConsole console) => default;
- }
- """;
-
- // Act & assert
- Analyzer.Should().NotProduceDiagnostics(code);
- }
-}
diff --git a/CliFx.Analyzers.Tests/ParameterMustBeSingleIfNonRequiredAnalyzerSpecs.cs b/CliFx.Analyzers.Tests/ParameterMustBeSingleIfNonRequiredAnalyzerSpecs.cs
deleted file mode 100644
index 5decbc1..0000000
--- a/CliFx.Analyzers.Tests/ParameterMustBeSingleIfNonRequiredAnalyzerSpecs.cs
+++ /dev/null
@@ -1,99 +0,0 @@
-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
- // lang=csharp
- const string code = """
- [Command]
- public class MyCommand : ICommand
- {
- [CommandParameter(0, IsRequired = false)]
- public string? Foo { get; init; }
-
- [CommandParameter(1, IsRequired = false)]
- public string? Bar { get; init; }
-
- 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
- // lang=csharp
- const string code = """
- [Command]
- public class MyCommand : ICommand
- {
- [CommandParameter(0)]
- public required string Foo { get; init; }
-
- [CommandParameter(1, IsRequired = false)]
- public string? Bar { get; init; }
-
- 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
- // lang=csharp
- const string code = """
- [Command]
- public class MyCommand : ICommand
- {
- [CommandParameter(0)]
- public required string Foo { get; init; }
-
- [CommandParameter(1)]
- public required string Bar { get; init; }
-
- 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
- // lang=csharp
- const string code = """
- [Command]
- public class MyCommand : ICommand
- {
- public string? Foo { get; init; }
-
- public ValueTask ExecuteAsync(IConsole console) => default;
- }
- """;
-
- // Act & assert
- Analyzer.Should().NotProduceDiagnostics(code);
- }
-}
diff --git a/CliFx.Analyzers.Tests/ParameterMustBeSingleIfNonScalarAnalyzerSpecs.cs b/CliFx.Analyzers.Tests/ParameterMustBeSingleIfNonScalarAnalyzerSpecs.cs
deleted file mode 100644
index be4bd76..0000000
--- a/CliFx.Analyzers.Tests/ParameterMustBeSingleIfNonScalarAnalyzerSpecs.cs
+++ /dev/null
@@ -1,99 +0,0 @@
-using CliFx.Analyzers.Tests.Utils;
-using Microsoft.CodeAnalysis.Diagnostics;
-using Xunit;
-
-namespace CliFx.Analyzers.Tests;
-
-public class ParameterMustBeSingleIfNonScalarAnalyzerSpecs
-{
- private static DiagnosticAnalyzer Analyzer { get; } =
- new ParameterMustBeSingleIfNonScalarAnalyzer();
-
- [Fact]
- public void Analyzer_reports_an_error_if_more_than_one_non_scalar_parameters_are_defined()
- {
- // Arrange
- // lang=csharp
- const string code = """
- [Command]
- public class MyCommand : ICommand
- {
- [CommandParameter(0)]
- public required string[] Foo { get; init; }
-
- [CommandParameter(1)]
- public required string[] Bar { get; init; }
-
- 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_scalar_parameter_is_defined()
- {
- // Arrange
- // lang=csharp
- const string code = """
- [Command]
- public class MyCommand : ICommand
- {
- [CommandParameter(0)]
- public required string Foo { get; init; }
-
- [CommandParameter(1)]
- public required string[] Bar { get; init; }
-
- public ValueTask ExecuteAsync(IConsole console) => default;
- }
- """;
-
- // Act & assert
- Analyzer.Should().NotProduceDiagnostics(code);
- }
-
- [Fact]
- public void Analyzer_does_not_report_an_error_if_no_non_scalar_parameters_are_defined()
- {
- // Arrange
- // lang=csharp
- const string code = """
- [Command]
- public class MyCommand : ICommand
- {
- [CommandParameter(0)]
- public required string Foo { get; init; }
-
- [CommandParameter(1)]
- public required string Bar { get; init; }
-
- 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
- // lang=csharp
- const string code = """
- [Command]
- public class MyCommand : ICommand
- {
- public string? Foo { get; init; }
-
- public ValueTask ExecuteAsync(IConsole console) => default;
- }
- """;
-
- // Act & assert
- Analyzer.Should().NotProduceDiagnostics(code);
- }
-}
diff --git a/CliFx.Analyzers.Tests/ParameterMustHaveUniqueNameAnalyzerSpecs.cs b/CliFx.Analyzers.Tests/ParameterMustHaveUniqueNameAnalyzerSpecs.cs
deleted file mode 100644
index 30fc5b9..0000000
--- a/CliFx.Analyzers.Tests/ParameterMustHaveUniqueNameAnalyzerSpecs.cs
+++ /dev/null
@@ -1,75 +0,0 @@
-using CliFx.Analyzers.Tests.Utils;
-using Microsoft.CodeAnalysis.Diagnostics;
-using Xunit;
-
-namespace CliFx.Analyzers.Tests;
-
-public class ParameterMustHaveUniqueNameAnalyzerSpecs
-{
- private static DiagnosticAnalyzer Analyzer { get; } = new ParameterMustHaveUniqueNameAnalyzer();
-
- [Fact]
- public void Analyzer_reports_an_error_if_a_parameter_has_the_same_name_as_another_parameter()
- {
- // Arrange
- // lang=csharp
- const string code = """
- [Command]
- public class MyCommand : ICommand
- {
- [CommandParameter(0, Name = "foo")]
- public required string Foo { get; init; }
-
- [CommandParameter(1, Name = "foo")]
- public required string Bar { get; init; }
-
- public ValueTask ExecuteAsync(IConsole console) => default;
- }
- """;
-
- // Act & assert
- Analyzer.Should().ProduceDiagnostics(code);
- }
-
- [Fact]
- public void Analyzer_does_not_report_an_error_if_a_parameter_has_unique_name()
- {
- // Arrange
- // lang=csharp
- const string code = """
- [Command]
- public class MyCommand : ICommand
- {
- [CommandParameter(0, Name = "foo")]
- public required string Foo { get; init; }
-
- [CommandParameter(1, Name = "bar")]
- public required string Bar { get; init; }
-
- 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
- // lang=csharp
- const string code = """
- [Command]
- public class MyCommand : ICommand
- {
- public string? Foo { get; init; }
-
- public ValueTask ExecuteAsync(IConsole console) => default;
- }
- """;
-
- // Act & assert
- Analyzer.Should().NotProduceDiagnostics(code);
- }
-}
diff --git a/CliFx.Analyzers.Tests/ParameterMustHaveUniqueOrderAnalyzerSpecs.cs b/CliFx.Analyzers.Tests/ParameterMustHaveUniqueOrderAnalyzerSpecs.cs
deleted file mode 100644
index 1262046..0000000
--- a/CliFx.Analyzers.Tests/ParameterMustHaveUniqueOrderAnalyzerSpecs.cs
+++ /dev/null
@@ -1,76 +0,0 @@
-using CliFx.Analyzers.Tests.Utils;
-using Microsoft.CodeAnalysis.Diagnostics;
-using Xunit;
-
-namespace CliFx.Analyzers.Tests;
-
-public class ParameterMustHaveUniqueOrderAnalyzerSpecs
-{
- private static DiagnosticAnalyzer Analyzer { get; } =
- new ParameterMustHaveUniqueOrderAnalyzer();
-
- [Fact]
- public void Analyzer_reports_an_error_if_a_parameter_has_the_same_order_as_another_parameter()
- {
- // Arrange
- // lang=csharp
- const string code = """
- [Command]
- public class MyCommand : ICommand
- {
- [CommandParameter(0)]
- public required string Foo { get; init; }
-
- [CommandParameter(0)]
- public required string Bar { get; init; }
-
- public ValueTask ExecuteAsync(IConsole console) => default;
- }
- """;
-
- // Act & assert
- Analyzer.Should().ProduceDiagnostics(code);
- }
-
- [Fact]
- public void Analyzer_does_not_report_an_error_if_a_parameter_has_unique_order()
- {
- // Arrange
- // lang=csharp
- const string code = """
- [Command]
- public class MyCommand : ICommand
- {
- [CommandParameter(0)]
- public required string Foo { get; init; }
-
- [CommandParameter(1)]
- public required string Bar { get; init; }
-
- 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
- // lang=csharp
- const string code = """
- [Command]
- public class MyCommand : ICommand
- {
- public string? Foo { get; init; }
-
- public ValueTask ExecuteAsync(IConsole console) => default;
- }
- """;
-
- // Act & assert
- Analyzer.Should().NotProduceDiagnostics(code);
- }
-}
diff --git a/CliFx.Analyzers.Tests/ParameterMustHaveValidConverterAnalyzerSpecs.cs b/CliFx.Analyzers.Tests/ParameterMustHaveValidConverterAnalyzerSpecs.cs
deleted file mode 100644
index 60a79b7..0000000
--- a/CliFx.Analyzers.Tests/ParameterMustHaveValidConverterAnalyzerSpecs.cs
+++ /dev/null
@@ -1,175 +0,0 @@
-using CliFx.Analyzers.Tests.Utils;
-using Microsoft.CodeAnalysis.Diagnostics;
-using Xunit;
-
-namespace CliFx.Analyzers.Tests;
-
-public class ParameterMustHaveValidConverterAnalyzerSpecs
-{
- private static DiagnosticAnalyzer Analyzer { get; } =
- new ParameterMustHaveValidConverterAnalyzer();
-
- [Fact]
- public void Analyzer_reports_an_error_if_a_parameter_has_a_converter_that_does_not_derive_from_BindingConverter()
- {
- // Arrange
- // lang=csharp
- const string code = """
- public class MyConverter
- {
- public string Convert(string? rawValue) => rawValue;
- }
-
- [Command]
- public class MyCommand : ICommand
- {
- [CommandParameter(0, Converter = typeof(MyConverter))]
- public required string Foo { get; init; }
-
- public ValueTask ExecuteAsync(IConsole console) => default;
- }
- """;
-
- // Act & assert
- Analyzer.Should().ProduceDiagnostics(code);
- }
-
- [Fact]
- public void Analyzer_reports_an_error_if_a_parameter_has_a_converter_that_does_not_derive_from_a_compatible_BindingConverter()
- {
- // Arrange
- // lang=csharp
- const string code = """
- public class MyConverter : BindingConverter
- {
- public override int Convert(string? rawValue) => 42;
- }
-
- [Command]
- public class MyCommand : ICommand
- {
- [CommandParameter(0, Converter = typeof(MyConverter))]
- public required string Foo { get; init; }
-
- public ValueTask ExecuteAsync(IConsole console) => default;
- }
- """;
-
- // Act & assert
- Analyzer.Should().ProduceDiagnostics(code);
- }
-
- [Fact]
- public void Analyzer_does_not_report_an_error_if_a_parameter_has_a_converter_that_derives_from_a_compatible_BindingConverter()
- {
- // Arrange
- // lang=csharp
- const string code = """
- public class MyConverter : BindingConverter
- {
- public override string Convert(string? rawValue) => rawValue;
- }
-
- [Command]
- public class MyCommand : ICommand
- {
- [CommandParameter(0, Converter = typeof(MyConverter))]
- public required string Foo { get; init; }
-
- public ValueTask ExecuteAsync(IConsole console) => default;
- }
- """;
-
- // Act & assert
- Analyzer.Should().NotProduceDiagnostics(code);
- }
-
- [Fact]
- public void Analyzer_does_not_report_an_error_if_a_nullable_parameter_has_a_converter_that_derives_from_a_compatible_BindingConverter()
- {
- // Arrange
- // lang=csharp
- const string code = """
- public class MyConverter : BindingConverter
- {
- public override int Convert(string? rawValue) => 42;
- }
-
- [Command]
- public class MyCommand : ICommand
- {
- [CommandOption("foo", Converter = typeof(MyConverter))]
- public int? Foo { get; init; }
-
- public ValueTask ExecuteAsync(IConsole console) => default;
- }
- """;
-
- // Act & assert
- Analyzer.Should().NotProduceDiagnostics(code);
- }
-
- [Fact]
- public void Analyzer_does_not_report_an_error_if_a_non_scalar_parameter_has_a_converter_that_derives_from_a_compatible_BindingConverter()
- {
- // Arrange
- // lang=csharp
- const string code = """
- public class MyConverter : BindingConverter
- {
- public override string Convert(string? rawValue) => rawValue;
- }
-
- [Command]
- public class MyCommand : ICommand
- {
- [CommandParameter(0, Converter = typeof(MyConverter))]
- public required IReadOnlyList Foo { get; init; }
-
- public ValueTask ExecuteAsync(IConsole console) => default;
- }
- """;
-
- // Act & assert
- Analyzer.Should().NotProduceDiagnostics(code);
- }
-
- [Fact]
- public void Analyzer_does_not_report_an_error_if_a_parameter_does_not_have_a_converter()
- {
- // Arrange
- // lang=csharp
- const string code = """
- [Command]
- public class MyCommand : ICommand
- {
- [CommandParameter(0)]
- public required string Foo { get; init; }
-
- 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
- // lang=csharp
- const string code = """
- [Command]
- public class MyCommand : ICommand
- {
- public string? Foo { get; init; }
-
- public ValueTask ExecuteAsync(IConsole console) => default;
- }
- """;
-
- // Act & assert
- Analyzer.Should().NotProduceDiagnostics(code);
- }
-}
diff --git a/CliFx.Analyzers.Tests/ParameterMustHaveValidValidatorsAnalyzerSpecs.cs b/CliFx.Analyzers.Tests/ParameterMustHaveValidValidatorsAnalyzerSpecs.cs
deleted file mode 100644
index d1c3bb8..0000000
--- a/CliFx.Analyzers.Tests/ParameterMustHaveValidValidatorsAnalyzerSpecs.cs
+++ /dev/null
@@ -1,125 +0,0 @@
-using CliFx.Analyzers.Tests.Utils;
-using Microsoft.CodeAnalysis.Diagnostics;
-using Xunit;
-
-namespace CliFx.Analyzers.Tests;
-
-public class ParameterMustHaveValidValidatorsAnalyzerSpecs
-{
- private static DiagnosticAnalyzer Analyzer { get; } =
- new ParameterMustHaveValidValidatorsAnalyzer();
-
- [Fact]
- public void Analyzer_reports_an_error_a_parameter_has_a_validator_that_does_not_derive_from_BindingValidator()
- {
- // Arrange
- // lang=csharp
- const string code = """
- public class MyValidator
- {
- public void Validate(string value) {}
- }
-
- [Command]
- public class MyCommand : ICommand
- {
- [CommandParameter(0, Validators = new[] { typeof(MyValidator) })]
- public required string Foo { get; init; }
-
- public ValueTask ExecuteAsync(IConsole console) => default;
- }
- """;
-
- // Act & assert
- Analyzer.Should().ProduceDiagnostics(code);
- }
-
- [Fact]
- public void Analyzer_reports_an_error_if_a_parameter_has_a_validator_that_does_not_derive_from_a_compatible_BindingValidator()
- {
- // Arrange
- // lang=csharp
- const string code = """
- public class MyValidator : BindingValidator
- {
- public override BindingValidationError Validate(int value) => Ok();
- }
-
- [Command]
- public class MyCommand : ICommand
- {
- [CommandParameter(0, Validators = new[] { typeof(MyValidator) })]
- public required string Foo { get; init; }
-
- public ValueTask ExecuteAsync(IConsole console) => default;
- }
- """;
-
- // Act & assert
- Analyzer.Should().ProduceDiagnostics(code);
- }
-
- [Fact]
- public void Analyzer_does_not_report_an_error_if_a_parameter_has_validators_that_all_derive_from_compatible_BindingValidators()
- {
- // Arrange
- // lang=csharp
- const string code = """
- public class MyValidator : BindingValidator
- {
- public override BindingValidationError Validate(string value) => Ok();
- }
-
- [Command]
- public class MyCommand : ICommand
- {
- [CommandParameter(0, Validators = new[] { typeof(MyValidator) })]
- public required string Foo { get; init; }
-
- public ValueTask ExecuteAsync(IConsole console) => default;
- }
- """;
-
- // Act & assert
- Analyzer.Should().NotProduceDiagnostics(code);
- }
-
- [Fact]
- public void Analyzer_does_not_report_an_error_if_a_parameter_does_not_have_validators()
- {
- // Arrange
- // lang=csharp
- const string code = """
- [Command]
- public class MyCommand : ICommand
- {
- [CommandParameter(0)]
- public required string Foo { get; init; }
-
- 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
- // lang=csharp
- const string code = """
- [Command]
- public class MyCommand : ICommand
- {
- public string? Foo { get; init; }
-
- public ValueTask ExecuteAsync(IConsole console) => default;
- }
- """;
-
- // Act & assert
- Analyzer.Should().NotProduceDiagnostics(code);
- }
-}
diff --git a/CliFx.Analyzers.Tests/SystemConsoleShouldBeAvoidedAnalyzerSpecs.cs b/CliFx.Analyzers.Tests/SystemConsoleShouldBeAvoidedAnalyzerSpecs.cs
deleted file mode 100644
index a2db943..0000000
--- a/CliFx.Analyzers.Tests/SystemConsoleShouldBeAvoidedAnalyzerSpecs.cs
+++ /dev/null
@@ -1,134 +0,0 @@
-using CliFx.Analyzers.Tests.Utils;
-using Microsoft.CodeAnalysis.Diagnostics;
-using Xunit;
-
-namespace CliFx.Analyzers.Tests;
-
-public class SystemConsoleShouldBeAvoidedAnalyzerSpecs
-{
- private static DiagnosticAnalyzer Analyzer { get; } =
- new SystemConsoleShouldBeAvoidedAnalyzer();
-
- [Fact]
- public void Analyzer_reports_an_error_if_a_command_calls_a_method_on_SystemConsole()
- {
- // Arrange
- // lang=csharp
- const string code = """
- [Command]
- public class MyCommand : ICommand
- {
- public ValueTask ExecuteAsync(IConsole console)
- {
- Console.WriteLine("Hello world");
- return default;
- }
- }
- """;
-
- // Act & assert
- Analyzer.Should().ProduceDiagnostics(code);
- }
-
- [Fact]
- public void Analyzer_reports_an_error_if_a_command_accesses_a_property_on_SystemConsole()
- {
- // Arrange
- // lang=csharp
- const string code = """
- [Command]
- public class MyCommand : ICommand
- {
- public ValueTask ExecuteAsync(IConsole console)
- {
- Console.ForegroundColor = ConsoleColor.Black;
- return default;
- }
- }
- """;
-
- // Act & assert
- Analyzer.Should().ProduceDiagnostics(code);
- }
-
- [Fact]
- public void Analyzer_reports_an_error_if_a_command_calls_a_method_on_a_property_of_SystemConsole()
- {
- // Arrange
- // lang=csharp
- const string code = """
- [Command]
- public class MyCommand : ICommand
- {
- public ValueTask ExecuteAsync(IConsole console)
- {
- Console.Error.WriteLine("Hello world");
- return default;
- }
- }
- """;
-
- // Act & assert
- Analyzer.Should().ProduceDiagnostics(code);
- }
-
- [Fact]
- public void Analyzer_does_not_report_an_error_if_a_command_interacts_with_the_console_through_IConsole()
- {
- // Arrange
- // lang=csharp
- const string code = """
- [Command]
- public class MyCommand : ICommand
- {
- public ValueTask ExecuteAsync(IConsole console)
- {
- console.WriteLine("Hello world");
- return default;
- }
- }
- """;
-
- // Act & assert
- Analyzer.Should().NotProduceDiagnostics(code);
- }
-
- [Fact]
- public void Analyzer_does_not_report_an_error_if_IConsole_is_not_available_in_the_current_method()
- {
- // Arrange
- // lang=csharp
- const string code = """
- [Command]
- public class MyCommand : ICommand
- {
- public void SomeOtherMethod() => Console.WriteLine("Test");
-
- public ValueTask ExecuteAsync(IConsole console) => default;
- }
- """;
-
- // Act & assert
- Analyzer.Should().NotProduceDiagnostics(code);
- }
-
- [Fact]
- public void Analyzer_does_not_report_an_error_if_a_command_does_not_access_SystemConsole()
- {
- // Arrange
- // lang=csharp
- const string code = """
- [Command]
- public class MyCommand : ICommand
- {
- public ValueTask ExecuteAsync(IConsole console)
- {
- return default;
- }
- }
- """;
-
- // Act & assert
- Analyzer.Should().NotProduceDiagnostics(code);
- }
-}
diff --git a/CliFx.Analyzers.Tests/Utils/AnalyzerAssertions.cs b/CliFx.Analyzers.Tests/Utils/AnalyzerAssertions.cs
deleted file mode 100644
index 4475db6..0000000
--- a/CliFx.Analyzers.Tests/Utils/AnalyzerAssertions.cs
+++ /dev/null
@@ -1,174 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Collections.Immutable;
-using System.Linq;
-using System.Text;
-using Basic.Reference.Assemblies;
-using FluentAssertions.Execution;
-using FluentAssertions.Primitives;
-using Microsoft.CodeAnalysis;
-using Microsoft.CodeAnalysis.CSharp;
-using Microsoft.CodeAnalysis.Diagnostics;
-using Microsoft.CodeAnalysis.Text;
-
-namespace CliFx.Analyzers.Tests.Utils;
-
-internal class AnalyzerAssertions(DiagnosticAnalyzer analyzer)
- : ReferenceTypeAssertions(analyzer)
-{
- protected override string Identifier => "analyzer";
-
- private Compilation Compile(string sourceCode)
- {
- // Get default system namespaces
- var defaultSystemNamespaces = new[]
- {
- "System",
- "System.Collections.Generic",
- "System.Threading.Tasks"
- };
-
- // Get default CliFx namespaces
- var defaultCliFxNamespaces = typeof(ICommand)
- .Assembly.GetTypes()
- .Where(t => t.IsPublic)
- .Select(t => t.Namespace)
- .Distinct()
- .ToArray();
-
- // Append default imports to the source code
- var sourceCodeWithUsings =
- string.Join(Environment.NewLine, defaultSystemNamespaces.Select(n => $"using {n};"))
- + string.Join(Environment.NewLine, defaultCliFxNamespaces.Select(n => $"using {n};"))
- + Environment.NewLine
- + sourceCode;
-
- // Parse the source code
- var ast = SyntaxFactory.ParseSyntaxTree(
- SourceText.From(sourceCodeWithUsings),
- CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.Preview)
- );
-
- // Compile the code to IL
- var compilation = CSharpCompilation.Create(
- "CliFxTests_DynamicAssembly_" + Guid.NewGuid(),
- [ast],
- Net80.References.All.Append(
- MetadataReference.CreateFromFile(typeof(ICommand).Assembly.Location)
- ),
- // DLL to avoid having to define the Main() method
- new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary)
- );
-
- var compilationErrors = compilation
- .GetDiagnostics()
- .Where(d => d.Severity >= DiagnosticSeverity.Error)
- .ToArray();
-
- if (compilationErrors.Any())
- {
- throw new InvalidOperationException(
- $"""
- Failed to compile code.
- {string.Join(Environment.NewLine, compilationErrors.Select(e => e.ToString()))}
- """
- );
- }
-
- return compilation;
- }
-
- private IReadOnlyList GetProducedDiagnostics(string sourceCode)
- {
- var analyzers = ImmutableArray.Create(Subject);
- var compilation = Compile(sourceCode);
-
- return compilation
- .WithAnalyzers(analyzers)
- .GetAnalyzerDiagnosticsAsync(analyzers, default)
- .GetAwaiter()
- .GetResult();
- }
-
- public void ProduceDiagnostics(string sourceCode)
- {
- var expectedDiagnostics = Subject.SupportedDiagnostics;
- var producedDiagnostics = GetProducedDiagnostics(sourceCode);
-
- var expectedDiagnosticIds = expectedDiagnostics.Select(d => d.Id).Distinct().ToArray();
- var producedDiagnosticIds = producedDiagnostics.Select(d => d.Id).Distinct().ToArray();
-
- var isSuccessfulAssertion =
- expectedDiagnosticIds.Intersect(producedDiagnosticIds).Count()
- == expectedDiagnosticIds.Length;
-
- Execute
- .Assertion.ForCondition(isSuccessfulAssertion)
- .FailWith(() =>
- {
- var buffer = new StringBuilder();
-
- buffer.AppendLine("Expected and produced diagnostics do not match.");
- buffer.AppendLine();
-
- buffer.AppendLine("Expected diagnostics:");
-
- foreach (var expectedDiagnostic in expectedDiagnostics)
- {
- buffer.Append(" - ");
- buffer.Append(expectedDiagnostic.Id);
- buffer.AppendLine();
- }
-
- buffer.AppendLine();
-
- buffer.AppendLine("Produced diagnostics:");
-
- if (producedDiagnostics.Any())
- {
- foreach (var producedDiagnostic in producedDiagnostics)
- {
- buffer.Append(" - ");
- buffer.Append(producedDiagnostic);
- }
- }
- else
- {
- buffer.AppendLine(" < none >");
- }
-
- return new FailReason(buffer.ToString());
- });
- }
-
- public void NotProduceDiagnostics(string sourceCode)
- {
- var producedDiagnostics = GetProducedDiagnostics(sourceCode);
- var isSuccessfulAssertion = !producedDiagnostics.Any();
-
- Execute
- .Assertion.ForCondition(isSuccessfulAssertion)
- .FailWith(() =>
- {
- var buffer = new StringBuilder();
-
- buffer.AppendLine("Expected no produced diagnostics.");
- buffer.AppendLine();
-
- buffer.AppendLine("Produced diagnostics:");
-
- foreach (var producedDiagnostic in producedDiagnostics)
- {
- buffer.Append(" - ");
- buffer.Append(producedDiagnostic);
- }
-
- return new FailReason(buffer.ToString());
- });
- }
-}
-
-internal static class AnalyzerAssertionsExtensions
-{
- public static AnalyzerAssertions Should(this DiagnosticAnalyzer analyzer) => new(analyzer);
-}
diff --git a/CliFx.Analyzers.Tests/xunit.runner.json b/CliFx.Analyzers.Tests/xunit.runner.json
deleted file mode 100644
index 186540e..0000000
--- a/CliFx.Analyzers.Tests/xunit.runner.json
+++ /dev/null
@@ -1,5 +0,0 @@
-{
- "$schema": "https://xunit.net/schema/current/xunit.runner.schema.json",
- "methodDisplayOptions": "all",
- "methodDisplay": "method"
-}
\ No newline at end of file
diff --git a/CliFx.Analyzers/AnalyzerBase.cs b/CliFx.Analyzers/AnalyzerBase.cs
deleted file mode 100644
index c022ca0..0000000
--- a/CliFx.Analyzers/AnalyzerBase.cs
+++ /dev/null
@@ -1,40 +0,0 @@
-using System.Collections.Immutable;
-using CliFx.Analyzers.Utils.Extensions;
-using Microsoft.CodeAnalysis;
-using Microsoft.CodeAnalysis.Diagnostics;
-
-namespace CliFx.Analyzers;
-
-public abstract class AnalyzerBase : DiagnosticAnalyzer
-{
- public DiagnosticDescriptor SupportedDiagnostic { get; }
-
- public sealed override ImmutableArray SupportedDiagnostics { get; }
-
- protected AnalyzerBase(
- string diagnosticTitle,
- string diagnosticMessage,
- DiagnosticSeverity diagnosticSeverity = DiagnosticSeverity.Error
- )
- {
- SupportedDiagnostic = new DiagnosticDescriptor(
- "CliFx_" + GetType().Name.TrimEnd("Analyzer"),
- diagnosticTitle,
- diagnosticMessage,
- "CliFx",
- diagnosticSeverity,
- true
- );
-
- SupportedDiagnostics = ImmutableArray.Create(SupportedDiagnostic);
- }
-
- protected Diagnostic CreateDiagnostic(Location location, params object?[]? messageArgs) =>
- Diagnostic.Create(SupportedDiagnostic, location, messageArgs);
-
- public override void Initialize(AnalysisContext context)
- {
- context.EnableConcurrentExecution();
- context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
- }
-}
diff --git a/CliFx.Analyzers/CommandMustBeAnnotatedAnalyzer.cs b/CliFx.Analyzers/CommandMustBeAnnotatedAnalyzer.cs
deleted file mode 100644
index 78c98f1..0000000
--- a/CliFx.Analyzers/CommandMustBeAnnotatedAnalyzer.cs
+++ /dev/null
@@ -1,50 +0,0 @@
-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 CommandMustBeAnnotatedAnalyzer()
- : AnalyzerBase(
- $"Commands must be annotated with `{SymbolNames.CliFxCommandAttribute}`",
- $"This type must be annotated with `{SymbolNames.CliFxCommandAttribute}` in order to be a valid command."
- )
-{
- private void Analyze(
- SyntaxNodeAnalysisContext context,
- ClassDeclarationSyntax classDeclaration,
- ITypeSymbol type
- )
- {
- // Ignore abstract classes, because they may be used to define
- // base implementations for commands, in which case the command
- // attribute doesn't make sense.
- if (type.IsAbstract)
- return;
-
- var implementsCommandInterface = type.AllInterfaces.Any(i =>
- i.DisplayNameMatches(SymbolNames.CliFxCommandInterface)
- );
-
- var hasCommandAttribute = type.GetAttributes()
- .Select(a => a.AttributeClass)
- .Any(c => c.DisplayNameMatches(SymbolNames.CliFxCommandAttribute));
-
- // If the interface is implemented, but the attribute is missing,
- // then it's very likely a user error.
- if (implementsCommandInterface && !hasCommandAttribute)
- {
- context.ReportDiagnostic(CreateDiagnostic(classDeclaration.Identifier.GetLocation()));
- }
- }
-
- public override void Initialize(AnalysisContext context)
- {
- base.Initialize(context);
- context.HandleClassDeclaration(Analyze);
- }
-}
diff --git a/CliFx.Analyzers/CommandMustImplementInterfaceAnalyzer.cs b/CliFx.Analyzers/CommandMustImplementInterfaceAnalyzer.cs
deleted file mode 100644
index 94321b4..0000000
--- a/CliFx.Analyzers/CommandMustImplementInterfaceAnalyzer.cs
+++ /dev/null
@@ -1,44 +0,0 @@
-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 CommandMustImplementInterfaceAnalyzer()
- : AnalyzerBase(
- $"Commands must implement `{SymbolNames.CliFxCommandInterface}` interface",
- $"This type must implement `{SymbolNames.CliFxCommandInterface}` interface in order to be a valid command."
- )
-{
- private void Analyze(
- SyntaxNodeAnalysisContext context,
- ClassDeclarationSyntax classDeclaration,
- ITypeSymbol type
- )
- {
- var hasCommandAttribute = type.GetAttributes()
- .Select(a => a.AttributeClass)
- .Any(c => c.DisplayNameMatches(SymbolNames.CliFxCommandAttribute));
-
- var implementsCommandInterface = type.AllInterfaces.Any(i =>
- i.DisplayNameMatches(SymbolNames.CliFxCommandInterface)
- );
-
- // If the attribute is present, but the interface is not implemented,
- // it's very likely a user error.
- if (hasCommandAttribute && !implementsCommandInterface)
- {
- context.ReportDiagnostic(CreateDiagnostic(classDeclaration.Identifier.GetLocation()));
- }
- }
-
- public override void Initialize(AnalysisContext context)
- {
- base.Initialize(context);
- context.HandleClassDeclaration(Analyze);
- }
-}
diff --git a/CliFx.Analyzers/ObjectModel/CommandOptionSymbol.cs b/CliFx.Analyzers/ObjectModel/CommandOptionSymbol.cs
deleted file mode 100644
index 1307035..0000000
--- a/CliFx.Analyzers/ObjectModel/CommandOptionSymbol.cs
+++ /dev/null
@@ -1,89 +0,0 @@
-using System.Collections.Generic;
-using System.Linq;
-using CliFx.Analyzers.Utils.Extensions;
-using Microsoft.CodeAnalysis;
-
-namespace CliFx.Analyzers.ObjectModel;
-
-internal partial class CommandOptionSymbol(
- IPropertySymbol property,
- string? name,
- char? shortName,
- bool? isRequired,
- ITypeSymbol? converterType,
- IReadOnlyList validatorTypes
-) : ICommandMemberSymbol
-{
- public IPropertySymbol Property { get; } = property;
-
- public string? Name { get; } = name;
-
- public char? ShortName { get; } = shortName;
-
- public bool? IsRequired { get; } = isRequired;
-
- public ITypeSymbol? ConverterType { get; } = converterType;
-
- public IReadOnlyList ValidatorTypes { get; } = validatorTypes;
-}
-
-internal partial class CommandOptionSymbol
-{
- private static AttributeData? TryGetOptionAttribute(IPropertySymbol property) =>
- property
- .GetAttributes()
- .FirstOrDefault(a =>
- a.AttributeClass?.DisplayNameMatches(SymbolNames.CliFxCommandOptionAttribute)
- == true
- );
-
- public static CommandOptionSymbol? TryResolve(IPropertySymbol property)
- {
- var attribute = TryGetOptionAttribute(property);
- if (attribute is null)
- return null;
-
- var name =
- attribute
- .ConstructorArguments.Where(a => a.Type?.SpecialType == SpecialType.System_String)
- .Select(a => a.Value)
- .FirstOrDefault() as string;
-
- var shortName =
- attribute
- .ConstructorArguments.Where(a => a.Type?.SpecialType == SpecialType.System_Char)
- .Select(a => a.Value)
- .FirstOrDefault() as char?;
-
- var isRequired =
- attribute
- .NamedArguments.Where(a => a.Key == "IsRequired")
- .Select(a => a.Value.Value)
- .FirstOrDefault() as bool?;
-
- var converter = attribute
- .NamedArguments.Where(a => a.Key == "Converter")
- .Select(a => a.Value.Value)
- .Cast()
- .FirstOrDefault();
-
- var validators = attribute
- .NamedArguments.Where(a => a.Key == "Validators")
- .SelectMany(a => a.Value.Values)
- .Select(c => c.Value)
- .Cast()
- .ToArray();
-
- return new CommandOptionSymbol(
- property,
- name,
- shortName,
- isRequired,
- converter,
- validators
- );
- }
-
- public static bool IsOptionProperty(IPropertySymbol property) =>
- TryGetOptionAttribute(property) is not null;
-}
diff --git a/CliFx.Analyzers/ObjectModel/CommandParameterSymbol.cs b/CliFx.Analyzers/ObjectModel/CommandParameterSymbol.cs
deleted file mode 100644
index ac5b6e6..0000000
--- a/CliFx.Analyzers/ObjectModel/CommandParameterSymbol.cs
+++ /dev/null
@@ -1,78 +0,0 @@
-using System.Collections.Generic;
-using System.Linq;
-using CliFx.Analyzers.Utils.Extensions;
-using Microsoft.CodeAnalysis;
-
-namespace CliFx.Analyzers.ObjectModel;
-
-internal partial class CommandParameterSymbol(
- IPropertySymbol property,
- int order,
- string? name,
- bool? isRequired,
- ITypeSymbol? converterType,
- IReadOnlyList validatorTypes
-) : ICommandMemberSymbol
-{
- public IPropertySymbol Property { get; } = property;
-
- public int Order { get; } = order;
-
- public string? Name { get; } = name;
-
- public bool? IsRequired { get; } = isRequired;
-
- public ITypeSymbol? ConverterType { get; } = converterType;
-
- public IReadOnlyList ValidatorTypes { get; } = validatorTypes;
-}
-
-internal partial class CommandParameterSymbol
-{
- private static AttributeData? TryGetParameterAttribute(IPropertySymbol property) =>
- property
- .GetAttributes()
- .FirstOrDefault(a =>
- a.AttributeClass?.DisplayNameMatches(SymbolNames.CliFxCommandParameterAttribute)
- == true
- );
-
- public static CommandParameterSymbol? TryResolve(IPropertySymbol property)
- {
- var attribute = TryGetParameterAttribute(property);
- if (attribute is null)
- return null;
-
- var order = (int)attribute.ConstructorArguments.Select(a => a.Value).First()!;
-
- var name =
- attribute
- .NamedArguments.Where(a => a.Key == "Name")
- .Select(a => a.Value.Value)
- .FirstOrDefault() as string;
-
- var isRequired =
- attribute
- .NamedArguments.Where(a => a.Key == "IsRequired")
- .Select(a => a.Value.Value)
- .FirstOrDefault() as bool?;
-
- var converter = attribute
- .NamedArguments.Where(a => a.Key == "Converter")
- .Select(a => a.Value.Value)
- .Cast()
- .FirstOrDefault();
-
- var validators = attribute
- .NamedArguments.Where(a => a.Key == "Validators")
- .SelectMany(a => a.Value.Values)
- .Select(c => c.Value)
- .Cast()
- .ToArray();
-
- return new CommandParameterSymbol(property, order, name, isRequired, converter, validators);
- }
-
- public static bool IsParameterProperty(IPropertySymbol property) =>
- TryGetParameterAttribute(property) is not null;
-}
diff --git a/CliFx.Analyzers/ObjectModel/ICommandMemberSymbol.cs b/CliFx.Analyzers/ObjectModel/ICommandMemberSymbol.cs
deleted file mode 100644
index 377f13b..0000000
--- a/CliFx.Analyzers/ObjectModel/ICommandMemberSymbol.cs
+++ /dev/null
@@ -1,21 +0,0 @@
-using System.Collections.Generic;
-using CliFx.Analyzers.Utils.Extensions;
-using Microsoft.CodeAnalysis;
-
-namespace CliFx.Analyzers.ObjectModel;
-
-internal interface ICommandMemberSymbol
-{
- IPropertySymbol Property { get; }
-
- ITypeSymbol? ConverterType { get; }
-
- IReadOnlyList ValidatorTypes { get; }
-}
-
-internal static class CommandMemberSymbolExtensions
-{
- public static bool IsScalar(this ICommandMemberSymbol member) =>
- member.Property.Type.SpecialType == SpecialType.System_String
- || member.Property.Type.TryGetEnumerableUnderlyingType() is null;
-}
diff --git a/CliFx.Analyzers/ObjectModel/SymbolNames.cs b/CliFx.Analyzers/ObjectModel/SymbolNames.cs
deleted file mode 100644
index cc4b817..0000000
--- a/CliFx.Analyzers/ObjectModel/SymbolNames.cs
+++ /dev/null
@@ -1,13 +0,0 @@
-namespace CliFx.Analyzers.ObjectModel;
-
-internal static class SymbolNames
-{
- public const string CliFxCommandInterface = "CliFx.ICommand";
- public const string CliFxCommandAttribute = "CliFx.Attributes.CommandAttribute";
- public const string CliFxCommandParameterAttribute =
- "CliFx.Attributes.CommandParameterAttribute";
- public const string CliFxCommandOptionAttribute = "CliFx.Attributes.CommandOptionAttribute";
- public const string CliFxConsoleInterface = "CliFx.Infrastructure.IConsole";
- public const string CliFxBindingConverterClass = "CliFx.Extensibility.BindingConverter";
- public const string CliFxBindingValidatorClass = "CliFx.Extensibility.BindingValidator";
-}
diff --git a/CliFx.Analyzers/OptionMustBeInsideCommandAnalyzer.cs b/CliFx.Analyzers/OptionMustBeInsideCommandAnalyzer.cs
deleted file mode 100644
index 82f3121..0000000
--- a/CliFx.Analyzers/OptionMustBeInsideCommandAnalyzer.cs
+++ /dev/null
@@ -1,49 +0,0 @@
-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 OptionMustBeInsideCommandAnalyzer()
- : AnalyzerBase(
- "Options must be defined inside commands",
- $"This option must be defined inside a class that implements `{SymbolNames.CliFxCommandInterface}`."
- )
-{
- private void Analyze(
- SyntaxNodeAnalysisContext context,
- PropertyDeclarationSyntax propertyDeclaration,
- IPropertySymbol property
- )
- {
- if (property.ContainingType is null)
- return;
-
- if (property.ContainingType.IsAbstract)
- return;
-
- if (!CommandOptionSymbol.IsOptionProperty(property))
- return;
-
- var isInsideCommand = property.ContainingType.AllInterfaces.Any(i =>
- i.DisplayNameMatches(SymbolNames.CliFxCommandInterface)
- );
-
- if (!isInsideCommand)
- {
- context.ReportDiagnostic(
- CreateDiagnostic(propertyDeclaration.Identifier.GetLocation())
- );
- }
- }
-
- public override void Initialize(AnalysisContext context)
- {
- base.Initialize(context);
- context.HandlePropertyDeclaration(Analyze);
- }
-}
diff --git a/CliFx.Analyzers/OptionMustBeRequiredIfPropertyRequiredAnalyzer.cs b/CliFx.Analyzers/OptionMustBeRequiredIfPropertyRequiredAnalyzer.cs
deleted file mode 100644
index 146f527..0000000
--- a/CliFx.Analyzers/OptionMustBeRequiredIfPropertyRequiredAnalyzer.cs
+++ /dev/null
@@ -1,43 +0,0 @@
-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 OptionMustBeRequiredIfPropertyRequiredAnalyzer()
- : AnalyzerBase(
- "Options bound to required properties cannot be marked as non-required",
- "This option cannot be marked as non-required because it's bound to a required property."
- )
-{
- private void Analyze(
- SyntaxNodeAnalysisContext context,
- PropertyDeclarationSyntax propertyDeclaration,
- IPropertySymbol property
- )
- {
- if (property.ContainingType is null)
- return;
-
- if (!property.IsRequired())
- return;
-
- var option = CommandOptionSymbol.TryResolve(property);
- if (option is null)
- return;
-
- if (option.IsRequired != false)
- return;
-
- context.ReportDiagnostic(CreateDiagnostic(propertyDeclaration.Identifier.GetLocation()));
- }
-
- public override void Initialize(AnalysisContext context)
- {
- base.Initialize(context);
- context.HandlePropertyDeclaration(Analyze);
- }
-}
diff --git a/CliFx.Analyzers/OptionMustHaveNameOrShortNameAnalyzer.cs b/CliFx.Analyzers/OptionMustHaveNameOrShortNameAnalyzer.cs
deleted file mode 100644
index 9f94b7f..0000000
--- a/CliFx.Analyzers/OptionMustHaveNameOrShortNameAnalyzer.cs
+++ /dev/null
@@ -1,39 +0,0 @@
-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 OptionMustHaveNameOrShortNameAnalyzer()
- : AnalyzerBase(
- "Options must have either a name or short name specified",
- "This option must have either a name or short name specified."
- )
-{
- private void Analyze(
- SyntaxNodeAnalysisContext context,
- PropertyDeclarationSyntax propertyDeclaration,
- IPropertySymbol property
- )
- {
- var option = CommandOptionSymbol.TryResolve(property);
- if (option is null)
- return;
-
- if (string.IsNullOrWhiteSpace(option.Name) && option.ShortName is null)
- {
- context.ReportDiagnostic(
- CreateDiagnostic(propertyDeclaration.Identifier.GetLocation())
- );
- }
- }
-
- public override void Initialize(AnalysisContext context)
- {
- base.Initialize(context);
- context.HandlePropertyDeclaration(Analyze);
- }
-}
diff --git a/CliFx.Analyzers/OptionMustHaveUniqueNameAnalyzer.cs b/CliFx.Analyzers/OptionMustHaveUniqueNameAnalyzer.cs
deleted file mode 100644
index 3e779e8..0000000
--- a/CliFx.Analyzers/OptionMustHaveUniqueNameAnalyzer.cs
+++ /dev/null
@@ -1,69 +0,0 @@
-using System;
-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 OptionMustHaveUniqueNameAnalyzer()
- : AnalyzerBase(
- "Options must have unique names",
- "This option's name must be unique within the command (comparison IS NOT case sensitive). "
- + "Specified name: `{0}`. "
- + "Property bound to another option with the same name: `{1}`."
- )
-{
- private void Analyze(
- SyntaxNodeAnalysisContext context,
- PropertyDeclarationSyntax propertyDeclaration,
- IPropertySymbol property
- )
- {
- if (property.ContainingType is null)
- return;
-
- var option = CommandOptionSymbol.TryResolve(property);
- if (option is null)
- return;
-
- if (string.IsNullOrWhiteSpace(option.Name))
- return;
-
- var otherProperties = property
- .ContainingType.GetMembers()
- .OfType()
- .Where(m => !m.Equals(property))
- .ToArray();
-
- foreach (var otherProperty in otherProperties)
- {
- var otherOption = CommandOptionSymbol.TryResolve(otherProperty);
- if (otherOption is null)
- continue;
-
- if (string.IsNullOrWhiteSpace(otherOption.Name))
- continue;
-
- if (string.Equals(option.Name, otherOption.Name, StringComparison.OrdinalIgnoreCase))
- {
- context.ReportDiagnostic(
- CreateDiagnostic(
- propertyDeclaration.Identifier.GetLocation(),
- option.Name,
- otherProperty.Name
- )
- );
- }
- }
- }
-
- public override void Initialize(AnalysisContext context)
- {
- base.Initialize(context);
- context.HandlePropertyDeclaration(Analyze);
- }
-}
diff --git a/CliFx.Analyzers/OptionMustHaveUniqueShortNameAnalyzer.cs b/CliFx.Analyzers/OptionMustHaveUniqueShortNameAnalyzer.cs
deleted file mode 100644
index 6f9bb4c..0000000
--- a/CliFx.Analyzers/OptionMustHaveUniqueShortNameAnalyzer.cs
+++ /dev/null
@@ -1,68 +0,0 @@
-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 OptionMustHaveUniqueShortNameAnalyzer()
- : AnalyzerBase(
- "Options must have unique short names",
- "This option's short name must be unique within the command (comparison IS case sensitive). "
- + "Specified short name: `{0}` "
- + "Property bound to another option with the same short name: `{1}`."
- )
-{
- private void Analyze(
- SyntaxNodeAnalysisContext context,
- PropertyDeclarationSyntax propertyDeclaration,
- IPropertySymbol property
- )
- {
- if (property.ContainingType is null)
- return;
-
- var option = CommandOptionSymbol.TryResolve(property);
- if (option is null)
- return;
-
- if (option.ShortName is null)
- return;
-
- var otherProperties = property
- .ContainingType.GetMembers()
- .OfType()
- .Where(m => !m.Equals(property))
- .ToArray();
-
- foreach (var otherProperty in otherProperties)
- {
- var otherOption = CommandOptionSymbol.TryResolve(otherProperty);
- if (otherOption is null)
- continue;
-
- if (otherOption.ShortName is null)
- continue;
-
- if (option.ShortName == otherOption.ShortName)
- {
- context.ReportDiagnostic(
- CreateDiagnostic(
- propertyDeclaration.Identifier.GetLocation(),
- option.ShortName,
- otherProperty.Name
- )
- );
- }
- }
- }
-
- public override void Initialize(AnalysisContext context)
- {
- base.Initialize(context);
- context.HandlePropertyDeclaration(Analyze);
- }
-}
diff --git a/CliFx.Analyzers/OptionMustHaveValidConverterAnalyzer.cs b/CliFx.Analyzers/OptionMustHaveValidConverterAnalyzer.cs
deleted file mode 100644
index a8e3ef6..0000000
--- a/CliFx.Analyzers/OptionMustHaveValidConverterAnalyzer.cs
+++ /dev/null
@@ -1,65 +0,0 @@
-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 OptionMustHaveValidConverterAnalyzer()
- : AnalyzerBase(
- $"Option converters must derive from `{SymbolNames.CliFxBindingConverterClass}`",
- $"Converter specified for this option must derive from a compatible `{SymbolNames.CliFxBindingConverterClass}`."
- )
-{
- private void Analyze(
- SyntaxNodeAnalysisContext context,
- PropertyDeclarationSyntax propertyDeclaration,
- IPropertySymbol property
- )
- {
- var option = CommandOptionSymbol.TryResolve(property);
- if (option is null)
- return;
-
- if (option.ConverterType is null)
- return;
-
- var converterValueType = option
- .ConverterType.GetBaseTypes()
- .FirstOrDefault(t =>
- t.ConstructedFrom.DisplayNameMatches(SymbolNames.CliFxBindingConverterClass)
- )
- ?.TypeArguments.FirstOrDefault();
-
- // Value returned by the converter must be assignable to the property type
- var isCompatible =
- converterValueType is not null
- && (
- option.IsScalar()
- // Scalar
- ? context.Compilation.IsAssignable(converterValueType, property.Type)
- // Non-scalar (assume we can handle all IEnumerable types for simplicity)
- : property.Type.TryGetEnumerableUnderlyingType() is { } enumerableUnderlyingType
- && context.Compilation.IsAssignable(
- converterValueType,
- enumerableUnderlyingType
- )
- );
-
- if (!isCompatible)
- {
- context.ReportDiagnostic(
- CreateDiagnostic(propertyDeclaration.Identifier.GetLocation())
- );
- }
- }
-
- public override void Initialize(AnalysisContext context)
- {
- base.Initialize(context);
- context.HandlePropertyDeclaration(Analyze);
- }
-}
diff --git a/CliFx.Analyzers/OptionMustHaveValidNameAnalyzer.cs b/CliFx.Analyzers/OptionMustHaveValidNameAnalyzer.cs
deleted file mode 100644
index 66b0cd1..0000000
--- a/CliFx.Analyzers/OptionMustHaveValidNameAnalyzer.cs
+++ /dev/null
@@ -1,43 +0,0 @@
-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 OptionMustHaveValidNameAnalyzer()
- : AnalyzerBase(
- "Options must have valid names",
- "This option's name must be at least 2 characters long and must start with a letter. "
- + "Specified name: `{0}`."
- )
-{
- private void Analyze(
- SyntaxNodeAnalysisContext context,
- PropertyDeclarationSyntax propertyDeclaration,
- IPropertySymbol property
- )
- {
- var option = CommandOptionSymbol.TryResolve(property);
- if (option is null)
- return;
-
- if (string.IsNullOrWhiteSpace(option.Name))
- return;
-
- if (option.Name.Length < 2 || !char.IsLetter(option.Name[0]))
- {
- context.ReportDiagnostic(
- CreateDiagnostic(propertyDeclaration.Identifier.GetLocation(), option.Name)
- );
- }
- }
-
- public override void Initialize(AnalysisContext context)
- {
- base.Initialize(context);
- context.HandlePropertyDeclaration(Analyze);
- }
-}
diff --git a/CliFx.Analyzers/OptionMustHaveValidShortNameAnalyzer.cs b/CliFx.Analyzers/OptionMustHaveValidShortNameAnalyzer.cs
deleted file mode 100644
index 69e3446..0000000
--- a/CliFx.Analyzers/OptionMustHaveValidShortNameAnalyzer.cs
+++ /dev/null
@@ -1,43 +0,0 @@
-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 OptionMustHaveValidShortNameAnalyzer()
- : AnalyzerBase(
- "Option short names must be letter characters",
- "This option's short name must be a single letter character. "
- + "Specified short name: `{0}`."
- )
-{
- private void Analyze(
- SyntaxNodeAnalysisContext context,
- PropertyDeclarationSyntax propertyDeclaration,
- IPropertySymbol property
- )
- {
- var option = CommandOptionSymbol.TryResolve(property);
- if (option is null)
- return;
-
- if (option.ShortName is null)
- return;
-
- if (!char.IsLetter(option.ShortName.Value))
- {
- context.ReportDiagnostic(
- CreateDiagnostic(propertyDeclaration.Identifier.GetLocation(), option.ShortName)
- );
- }
- }
-
- public override void Initialize(AnalysisContext context)
- {
- base.Initialize(context);
- context.HandlePropertyDeclaration(Analyze);
- }
-}
diff --git a/CliFx.Analyzers/OptionMustHaveValidValidatorsAnalyzer.cs b/CliFx.Analyzers/OptionMustHaveValidValidatorsAnalyzer.cs
deleted file mode 100644
index e1be507..0000000
--- a/CliFx.Analyzers/OptionMustHaveValidValidatorsAnalyzer.cs
+++ /dev/null
@@ -1,58 +0,0 @@
-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 OptionMustHaveValidValidatorsAnalyzer()
- : AnalyzerBase(
- $"Option validators must derive from `{SymbolNames.CliFxBindingValidatorClass}`",
- $"Each validator specified for this option must derive from a compatible `{SymbolNames.CliFxBindingValidatorClass}`."
- )
-{
- private void Analyze(
- SyntaxNodeAnalysisContext context,
- PropertyDeclarationSyntax propertyDeclaration,
- IPropertySymbol property
- )
- {
- var option = CommandOptionSymbol.TryResolve(property);
- if (option is null)
- return;
-
- foreach (var validatorType in option.ValidatorTypes)
- {
- var validatorValueType = validatorType
- .GetBaseTypes()
- .FirstOrDefault(t =>
- t.ConstructedFrom.DisplayNameMatches(SymbolNames.CliFxBindingValidatorClass)
- )
- ?.TypeArguments.FirstOrDefault();
-
- // Value passed to the validator must be assignable from the property type
- var isCompatible =
- validatorValueType is not null
- && context.Compilation.IsAssignable(property.Type, validatorValueType);
-
- if (!isCompatible)
- {
- context.ReportDiagnostic(
- CreateDiagnostic(propertyDeclaration.Identifier.GetLocation())
- );
-
- // No need to report multiple identical diagnostics on the same node
- break;
- }
- }
- }
-
- public override void Initialize(AnalysisContext context)
- {
- base.Initialize(context);
- context.HandlePropertyDeclaration(Analyze);
- }
-}
diff --git a/CliFx.Analyzers/ParameterMustBeInsideCommandAnalyzer.cs b/CliFx.Analyzers/ParameterMustBeInsideCommandAnalyzer.cs
deleted file mode 100644
index f7bb43f..0000000
--- a/CliFx.Analyzers/ParameterMustBeInsideCommandAnalyzer.cs
+++ /dev/null
@@ -1,49 +0,0 @@
-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 ParameterMustBeInsideCommandAnalyzer()
- : AnalyzerBase(
- "Parameters must be defined inside commands",
- $"This parameter must be defined inside a class that implements `{SymbolNames.CliFxCommandInterface}`."
- )
-{
- private void Analyze(
- SyntaxNodeAnalysisContext context,
- PropertyDeclarationSyntax propertyDeclaration,
- IPropertySymbol property
- )
- {
- if (property.ContainingType is null)
- return;
-
- if (property.ContainingType.IsAbstract)
- return;
-
- if (!CommandParameterSymbol.IsParameterProperty(property))
- return;
-
- var isInsideCommand = property.ContainingType.AllInterfaces.Any(i =>
- i.DisplayNameMatches(SymbolNames.CliFxCommandInterface)
- );
-
- if (!isInsideCommand)
- {
- context.ReportDiagnostic(
- CreateDiagnostic(propertyDeclaration.Identifier.GetLocation())
- );
- }
- }
-
- public override void Initialize(AnalysisContext context)
- {
- base.Initialize(context);
- context.HandlePropertyDeclaration(Analyze);
- }
-}
diff --git a/CliFx.Analyzers/ParameterMustBeLastIfNonRequiredAnalyzer.cs b/CliFx.Analyzers/ParameterMustBeLastIfNonRequiredAnalyzer.cs
deleted file mode 100644
index 578e815..0000000
--- a/CliFx.Analyzers/ParameterMustBeLastIfNonRequiredAnalyzer.cs
+++ /dev/null
@@ -1,63 +0,0 @@
-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 ParameterMustBeLastIfNonRequiredAnalyzer()
- : AnalyzerBase(
- "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). "
- + "Property bound to another non-required parameter: `{0}`."
- )
-{
- 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()
- .Where(m => !m.Equals(property))
- .ToArray();
-
- foreach (var otherProperty in otherProperties)
- {
- var otherParameter = CommandParameterSymbol.TryResolve(otherProperty);
- if (otherParameter is null)
- continue;
-
- if (otherParameter.Order > parameter.Order)
- {
- context.ReportDiagnostic(
- CreateDiagnostic(
- propertyDeclaration.Identifier.GetLocation(),
- otherProperty.Name
- )
- );
- }
- }
- }
-
- public override void Initialize(AnalysisContext context)
- {
- base.Initialize(context);
- context.HandlePropertyDeclaration(Analyze);
- }
-}
diff --git a/CliFx.Analyzers/ParameterMustBeLastIfNonScalarAnalyzer.cs b/CliFx.Analyzers/ParameterMustBeLastIfNonScalarAnalyzer.cs
deleted file mode 100644
index 3eee558..0000000
--- a/CliFx.Analyzers/ParameterMustBeLastIfNonScalarAnalyzer.cs
+++ /dev/null
@@ -1,63 +0,0 @@
-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 ParameterMustBeLastIfNonScalarAnalyzer()
- : AnalyzerBase(
- "Parameters of non-scalar types must be the last in order",
- "This parameter has a non-scalar type so it must be the last in order (its order must be highest within the command). "
- + "Property bound to another non-scalar parameter: `{0}`."
- )
-{
- 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.IsScalar())
- return;
-
- var otherProperties = property
- .ContainingType.GetMembers()
- .OfType()
- .Where(m => !m.Equals(property))
- .ToArray();
-
- foreach (var otherProperty in otherProperties)
- {
- var otherParameter = CommandParameterSymbol.TryResolve(otherProperty);
- if (otherParameter is null)
- continue;
-
- if (otherParameter.Order > parameter.Order)
- {
- context.ReportDiagnostic(
- CreateDiagnostic(
- propertyDeclaration.Identifier.GetLocation(),
- otherProperty.Name
- )
- );
- }
- }
- }
-
- public override void Initialize(AnalysisContext context)
- {
- base.Initialize(context);
- context.HandlePropertyDeclaration(Analyze);
- }
-}
diff --git a/CliFx.Analyzers/ParameterMustBeRequiredIfPropertyRequiredAnalyzer.cs b/CliFx.Analyzers/ParameterMustBeRequiredIfPropertyRequiredAnalyzer.cs
deleted file mode 100644
index 8d92b39..0000000
--- a/CliFx.Analyzers/ParameterMustBeRequiredIfPropertyRequiredAnalyzer.cs
+++ /dev/null
@@ -1,43 +0,0 @@
-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 ParameterMustBeRequiredIfPropertyRequiredAnalyzer()
- : AnalyzerBase(
- "Parameters bound to required properties cannot be marked as non-required",
- "This parameter cannot be marked as non-required because it's bound to a required property."
- )
-{
- private void Analyze(
- SyntaxNodeAnalysisContext context,
- PropertyDeclarationSyntax propertyDeclaration,
- IPropertySymbol property
- )
- {
- if (property.ContainingType is null)
- return;
-
- if (!property.IsRequired())
- return;
-
- var parameter = CommandParameterSymbol.TryResolve(property);
- if (parameter is null)
- return;
-
- if (parameter.IsRequired != false)
- return;
-
- context.ReportDiagnostic(CreateDiagnostic(propertyDeclaration.Identifier.GetLocation()));
- }
-
- public override void Initialize(AnalysisContext context)
- {
- base.Initialize(context);
- context.HandlePropertyDeclaration(Analyze);
- }
-}
diff --git a/CliFx.Analyzers/ParameterMustBeSingleIfNonRequiredAnalyzer.cs b/CliFx.Analyzers/ParameterMustBeSingleIfNonRequiredAnalyzer.cs
deleted file mode 100644
index 5c0e186..0000000
--- a/CliFx.Analyzers/ParameterMustBeSingleIfNonRequiredAnalyzer.cs
+++ /dev/null
@@ -1,63 +0,0 @@
-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(
- "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. "
- + "Property bound to another non-required parameter: `{0}`."
- )
-{
- 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()
- .Where(m => !m.Equals(property))
- .ToArray();
-
- foreach (var otherProperty in otherProperties)
- {
- var otherParameter = CommandParameterSymbol.TryResolve(otherProperty);
- if (otherParameter is null)
- continue;
-
- if (otherParameter.IsRequired == false)
- {
- context.ReportDiagnostic(
- CreateDiagnostic(
- propertyDeclaration.Identifier.GetLocation(),
- otherProperty.Name
- )
- );
- }
- }
- }
-
- public override void Initialize(AnalysisContext context)
- {
- base.Initialize(context);
- context.HandlePropertyDeclaration(Analyze);
- }
-}
diff --git a/CliFx.Analyzers/ParameterMustBeSingleIfNonScalarAnalyzer.cs b/CliFx.Analyzers/ParameterMustBeSingleIfNonScalarAnalyzer.cs
deleted file mode 100644
index d25c2aa..0000000
--- a/CliFx.Analyzers/ParameterMustBeSingleIfNonScalarAnalyzer.cs
+++ /dev/null
@@ -1,63 +0,0 @@
-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 ParameterMustBeSingleIfNonScalarAnalyzer()
- : AnalyzerBase(
- "Parameters of non-scalar types are limited to one per command",
- "This parameter has a non-scalar type so it must be the only such parameter in the command. "
- + "Property bound to another non-scalar parameter: `{0}`."
- )
-{
- 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.IsScalar())
- return;
-
- var otherProperties = property
- .ContainingType.GetMembers()
- .OfType()
- .Where(m => !m.Equals(property))
- .ToArray();
-
- foreach (var otherProperty in otherProperties)
- {
- var otherParameter = CommandParameterSymbol.TryResolve(otherProperty);
- if (otherParameter is null)
- continue;
-
- if (!otherParameter.IsScalar())
- {
- context.ReportDiagnostic(
- CreateDiagnostic(
- propertyDeclaration.Identifier.GetLocation(),
- otherProperty.Name
- )
- );
- }
- }
- }
-
- public override void Initialize(AnalysisContext context)
- {
- base.Initialize(context);
- context.HandlePropertyDeclaration(Analyze);
- }
-}
diff --git a/CliFx.Analyzers/ParameterMustHaveUniqueNameAnalyzer.cs b/CliFx.Analyzers/ParameterMustHaveUniqueNameAnalyzer.cs
deleted file mode 100644
index 7d8d55d..0000000
--- a/CliFx.Analyzers/ParameterMustHaveUniqueNameAnalyzer.cs
+++ /dev/null
@@ -1,75 +0,0 @@
-using System;
-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 ParameterMustHaveUniqueNameAnalyzer()
- : AnalyzerBase(
- "Parameters must have unique names",
- "This parameter's name must be unique within the command (comparison IS NOT case sensitive). "
- + "Specified name: `{0}`. "
- + "Property bound to another parameter with the same name: `{1}`."
- )
-{
- 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 (string.IsNullOrWhiteSpace(parameter.Name))
- return;
-
- var otherProperties = property
- .ContainingType.GetMembers()
- .OfType()
- .Where(m => !m.Equals(property))
- .ToArray();
-
- foreach (var otherProperty in otherProperties)
- {
- var otherParameter = CommandParameterSymbol.TryResolve(otherProperty);
- if (otherParameter is null)
- continue;
-
- if (string.IsNullOrWhiteSpace(otherParameter.Name))
- continue;
-
- if (
- string.Equals(
- parameter.Name,
- otherParameter.Name,
- StringComparison.OrdinalIgnoreCase
- )
- )
- {
- context.ReportDiagnostic(
- CreateDiagnostic(
- propertyDeclaration.Identifier.GetLocation(),
- parameter.Name,
- otherProperty.Name
- )
- );
- }
- }
- }
-
- public override void Initialize(AnalysisContext context)
- {
- base.Initialize(context);
- context.HandlePropertyDeclaration(Analyze);
- }
-}
diff --git a/CliFx.Analyzers/ParameterMustHaveUniqueOrderAnalyzer.cs b/CliFx.Analyzers/ParameterMustHaveUniqueOrderAnalyzer.cs
deleted file mode 100644
index c46643b..0000000
--- a/CliFx.Analyzers/ParameterMustHaveUniqueOrderAnalyzer.cs
+++ /dev/null
@@ -1,62 +0,0 @@
-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 ParameterMustHaveUniqueOrderAnalyzer()
- : AnalyzerBase(
- "Parameters must have unique order",
- "This parameter's order must be unique within the command. "
- + "Specified order: {0}. "
- + "Property bound to another parameter with the same order: `{1}`."
- )
-{
- 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;
-
- var otherProperties = property
- .ContainingType.GetMembers()
- .OfType()
- .Where(m => !m.Equals(property))
- .ToArray();
-
- foreach (var otherProperty in otherProperties)
- {
- var otherParameter = CommandParameterSymbol.TryResolve(otherProperty);
- if (otherParameter is null)
- continue;
-
- if (parameter.Order == otherParameter.Order)
- {
- context.ReportDiagnostic(
- CreateDiagnostic(
- propertyDeclaration.Identifier.GetLocation(),
- parameter.Order,
- otherProperty.Name
- )
- );
- }
- }
- }
-
- public override void Initialize(AnalysisContext context)
- {
- base.Initialize(context);
- context.HandlePropertyDeclaration(Analyze);
- }
-}
diff --git a/CliFx.Analyzers/ParameterMustHaveValidConverterAnalyzer.cs b/CliFx.Analyzers/ParameterMustHaveValidConverterAnalyzer.cs
deleted file mode 100644
index 5e59790..0000000
--- a/CliFx.Analyzers/ParameterMustHaveValidConverterAnalyzer.cs
+++ /dev/null
@@ -1,65 +0,0 @@
-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 ParameterMustHaveValidConverterAnalyzer()
- : AnalyzerBase(
- $"Parameter converters must derive from `{SymbolNames.CliFxBindingConverterClass}`",
- $"Converter specified for this parameter must derive from a compatible `{SymbolNames.CliFxBindingConverterClass}`."
- )
-{
- private void Analyze(
- SyntaxNodeAnalysisContext context,
- PropertyDeclarationSyntax propertyDeclaration,
- IPropertySymbol property
- )
- {
- var parameter = CommandParameterSymbol.TryResolve(property);
- if (parameter is null)
- return;
-
- if (parameter.ConverterType is null)
- return;
-
- var converterValueType = parameter
- .ConverterType.GetBaseTypes()
- .FirstOrDefault(t =>
- t.ConstructedFrom.DisplayNameMatches(SymbolNames.CliFxBindingConverterClass)
- )
- ?.TypeArguments.FirstOrDefault();
-
- // Value returned by the converter must be assignable to the property type
- var isCompatible =
- converterValueType is not null
- && (
- parameter.IsScalar()
- // Scalar
- ? context.Compilation.IsAssignable(converterValueType, property.Type)
- // Non-scalar (assume we can handle all IEnumerable types for simplicity)
- : property.Type.TryGetEnumerableUnderlyingType() is { } enumerableUnderlyingType
- && context.Compilation.IsAssignable(
- converterValueType,
- enumerableUnderlyingType
- )
- );
-
- if (!isCompatible)
- {
- context.ReportDiagnostic(
- CreateDiagnostic(propertyDeclaration.Identifier.GetLocation())
- );
- }
- }
-
- public override void Initialize(AnalysisContext context)
- {
- base.Initialize(context);
- context.HandlePropertyDeclaration(Analyze);
- }
-}
diff --git a/CliFx.Analyzers/ParameterMustHaveValidValidatorsAnalyzer.cs b/CliFx.Analyzers/ParameterMustHaveValidValidatorsAnalyzer.cs
deleted file mode 100644
index 4a64c02..0000000
--- a/CliFx.Analyzers/ParameterMustHaveValidValidatorsAnalyzer.cs
+++ /dev/null
@@ -1,58 +0,0 @@
-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 ParameterMustHaveValidValidatorsAnalyzer()
- : AnalyzerBase(
- $"Parameter validators must derive from `{SymbolNames.CliFxBindingValidatorClass}`",
- $"Each validator specified for this parameter must derive from a compatible `{SymbolNames.CliFxBindingValidatorClass}`."
- )
-{
- private void Analyze(
- SyntaxNodeAnalysisContext context,
- PropertyDeclarationSyntax propertyDeclaration,
- IPropertySymbol property
- )
- {
- var parameter = CommandParameterSymbol.TryResolve(property);
- if (parameter is null)
- return;
-
- foreach (var validatorType in parameter.ValidatorTypes)
- {
- var validatorValueType = validatorType
- .GetBaseTypes()
- .FirstOrDefault(t =>
- t.ConstructedFrom.DisplayNameMatches(SymbolNames.CliFxBindingValidatorClass)
- )
- ?.TypeArguments.FirstOrDefault();
-
- // Value passed to the validator must be assignable from the property type
- var isCompatible =
- validatorValueType is not null
- && context.Compilation.IsAssignable(property.Type, validatorValueType);
-
- if (!isCompatible)
- {
- context.ReportDiagnostic(
- CreateDiagnostic(propertyDeclaration.Identifier.GetLocation())
- );
-
- // No need to report multiple identical diagnostics on the same node
- break;
- }
- }
- }
-
- public override void Initialize(AnalysisContext context)
- {
- base.Initialize(context);
- context.HandlePropertyDeclaration(Analyze);
- }
-}
diff --git a/CliFx.Analyzers/SystemConsoleShouldBeAvoidedAnalyzer.cs b/CliFx.Analyzers/SystemConsoleShouldBeAvoidedAnalyzer.cs
deleted file mode 100644
index d4718a5..0000000
--- a/CliFx.Analyzers/SystemConsoleShouldBeAvoidedAnalyzer.cs
+++ /dev/null
@@ -1,74 +0,0 @@
-using System.Linq;
-using CliFx.Analyzers.ObjectModel;
-using CliFx.Analyzers.Utils.Extensions;
-using Microsoft.CodeAnalysis;
-using Microsoft.CodeAnalysis.CSharp;
-using Microsoft.CodeAnalysis.CSharp.Syntax;
-using Microsoft.CodeAnalysis.Diagnostics;
-
-namespace CliFx.Analyzers;
-
-[DiagnosticAnalyzer(LanguageNames.CSharp)]
-public class SystemConsoleShouldBeAvoidedAnalyzer()
- : AnalyzerBase(
- $"Avoid calling `System.Console` where `{SymbolNames.CliFxConsoleInterface}` is available",
- $"Use the provided `{SymbolNames.CliFxConsoleInterface}` abstraction instead of `System.Console` to ensure that the command can be tested in isolation.",
- DiagnosticSeverity.Warning
- )
-{
- private MemberAccessExpressionSyntax? TryGetSystemConsoleMemberAccess(
- SyntaxNodeAnalysisContext context,
- SyntaxNode node
- )
- {
- var currentNode = node;
-
- while (currentNode is MemberAccessExpressionSyntax memberAccess)
- {
- var member = context.SemanticModel.GetSymbolInfo(memberAccess).Symbol;
-
- if (member?.ContainingType?.DisplayNameMatches("System.Console") == true)
- {
- return memberAccess;
- }
-
- // Get inner expression, which may be another member access expression.
- // Example: System.Console.Error
- // ~~~~~~~~~~~~~~ <- inner member access expression
- // -------------------- <- outer member access expression
- currentNode = memberAccess.Expression;
- }
-
- return null;
- }
-
- private void Analyze(SyntaxNodeAnalysisContext context)
- {
- // Try to get a member access on System.Console in the current expression,
- // or in any of its inner expressions.
- var systemConsoleMemberAccess = TryGetSystemConsoleMemberAccess(context, context.Node);
- if (systemConsoleMemberAccess is null)
- return;
-
- // Check if IConsole is available in scope as an alternative to System.Console
- var isConsoleInterfaceAvailable = context
- .Node.Ancestors()
- .OfType()
- .SelectMany(m => m.ParameterList.Parameters)
- .Select(p => p.Type)
- .Select(t => context.SemanticModel.GetSymbolInfo(t).Symbol)
- .Where(s => s is not null)
- .Any(s => s.DisplayNameMatches(SymbolNames.CliFxConsoleInterface));
-
- if (isConsoleInterfaceAvailable)
- {
- context.ReportDiagnostic(CreateDiagnostic(systemConsoleMemberAccess.GetLocation()));
- }
- }
-
- public override void Initialize(AnalysisContext context)
- {
- base.Initialize(context);
- context.RegisterSyntaxNodeAction(Analyze, SyntaxKind.SimpleMemberAccessExpression);
- }
-}
diff --git a/CliFx.Analyzers/Utils/Extensions/RoslynExtensions.cs b/CliFx.Analyzers/Utils/Extensions/RoslynExtensions.cs
deleted file mode 100644
index 1e01aff..0000000
--- a/CliFx.Analyzers/Utils/Extensions/RoslynExtensions.cs
+++ /dev/null
@@ -1,98 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using Microsoft.CodeAnalysis;
-using Microsoft.CodeAnalysis.CSharp;
-using Microsoft.CodeAnalysis.CSharp.Syntax;
-using Microsoft.CodeAnalysis.Diagnostics;
-
-namespace CliFx.Analyzers.Utils.Extensions;
-
-internal static class RoslynExtensions
-{
- public static bool DisplayNameMatches(this ISymbol symbol, string name) =>
- string.Equals(
- // Fully qualified name, without `global::`
- symbol.ToDisplayString(SymbolDisplayFormat.CSharpErrorMessageFormat),
- name,
- StringComparison.Ordinal
- );
-
- public static IEnumerable GetBaseTypes(this ITypeSymbol type)
- {
- var current = type.BaseType;
-
- while (current is not null)
- {
- yield return current;
- current = current.BaseType;
- }
- }
-
- public static ITypeSymbol? TryGetEnumerableUnderlyingType(this ITypeSymbol type) =>
- type
- .AllInterfaces.FirstOrDefault(i =>
- i.ConstructedFrom.SpecialType
- == SpecialType.System_Collections_Generic_IEnumerable_T
- )
- ?.TypeArguments[0];
-
- // Detect if the property is required through roundabout means so as to not have to take dependency
- // on higher versions of the C# compiler.
- public static bool IsRequired(this IPropertySymbol property) =>
- property
- // Can't rely on the RequiredMemberAttribute because it's generated by the compiler, not added by the user,
- // so we have to check for the presence of the `required` modifier in the syntax tree instead.
- .DeclaringSyntaxReferences.Select(r => r.GetSyntax())
- .OfType()
- .SelectMany(p => p.Modifiers)
- .Any(m => m.IsKind((SyntaxKind)8447));
-
- public static bool IsAssignable(
- this Compilation compilation,
- ITypeSymbol source,
- ITypeSymbol destination
- ) => compilation.ClassifyConversion(source, destination).Exists;
-
- public static void HandleClassDeclaration(
- this AnalysisContext analysisContext,
- Action analyze
- )
- {
- analysisContext.RegisterSyntaxNodeAction(
- ctx =>
- {
- if (ctx.Node is not ClassDeclarationSyntax classDeclaration)
- return;
-
- var type = ctx.SemanticModel.GetDeclaredSymbol(classDeclaration);
- if (type is null)
- return;
-
- analyze(ctx, classDeclaration, type);
- },
- SyntaxKind.ClassDeclaration
- );
- }
-
- public static void HandlePropertyDeclaration(
- this AnalysisContext analysisContext,
- Action analyze
- )
- {
- analysisContext.RegisterSyntaxNodeAction(
- ctx =>
- {
- if (ctx.Node is not PropertyDeclarationSyntax propertyDeclaration)
- return;
-
- var property = ctx.SemanticModel.GetDeclaredSymbol(propertyDeclaration);
- if (property is null)
- return;
-
- analyze(ctx, propertyDeclaration, property);
- },
- SyntaxKind.PropertyDeclaration
- );
- }
-}
diff --git a/CliFx.Analyzers/Utils/Extensions/StringExtensions.cs b/CliFx.Analyzers/Utils/Extensions/StringExtensions.cs
deleted file mode 100644
index 0848e56..0000000
--- a/CliFx.Analyzers/Utils/Extensions/StringExtensions.cs
+++ /dev/null
@@ -1,18 +0,0 @@
-using System;
-
-namespace CliFx.Analyzers.Utils.Extensions;
-
-internal static class StringExtensions
-{
- public static string TrimEnd(
- this string str,
- string sub,
- StringComparison comparison = StringComparison.Ordinal
- )
- {
- while (str.EndsWith(sub, comparison))
- str = str[..^sub.Length];
-
- return str;
- }
-}
diff --git a/CliFx.Demo/CliFx.Demo.csproj b/CliFx.Demo/CliFx.Demo.csproj
index 32de33e..baef5e8 100644
--- a/CliFx.Demo/CliFx.Demo.csproj
+++ b/CliFx.Demo/CliFx.Demo.csproj
@@ -14,7 +14,7 @@
-
+
\ No newline at end of file
diff --git a/CliFx.Demo/Program.cs b/CliFx.Demo/Program.cs
index 3cb8faf..67144a7 100644
--- a/CliFx.Demo/Program.cs
+++ b/CliFx.Demo/Program.cs
@@ -2,20 +2,17 @@
using CliFx.Demo.Domain;
using Microsoft.Extensions.DependencyInjection;
+// We use Microsoft.Extensions.DependencyInjection for injecting dependencies in commands
+var services = new ServiceCollection();
+services.AddSingleton();
+
+// Register all commands as transient services
+foreach (var commandType in commandTypes)
+ services.AddTransient(commandType);
+
return await new CliApplicationBuilder()
.SetDescription("Demo application showcasing CliFx features.")
.AddCommandsFromThisAssembly()
- .UseTypeActivator(commandTypes =>
- {
- // We use Microsoft.Extensions.DependencyInjection for injecting dependencies in commands
- var services = new ServiceCollection();
- services.AddSingleton();
-
- // Register all commands as transient services
- foreach (var commandType in commandTypes)
- services.AddTransient(commandType);
-
- return services.BuildServiceProvider();
- })
+ .UseTypeActivator(services.BuildServiceProvider())
.Build()
.RunAsync();
diff --git a/CliFx.Analyzers/CliFx.Analyzers.csproj b/CliFx.SourceGeneration/CliFx.SourceGeneration.csproj
similarity index 100%
rename from CliFx.Analyzers/CliFx.Analyzers.csproj
rename to CliFx.SourceGeneration/CliFx.SourceGeneration.csproj
diff --git a/CliFx.Tests.Dummy/CliFx.Tests.Dummy.csproj b/CliFx.Tests.Dummy/CliFx.Tests.Dummy.csproj
index 7feda9b..c460114 100644
--- a/CliFx.Tests.Dummy/CliFx.Tests.Dummy.csproj
+++ b/CliFx.Tests.Dummy/CliFx.Tests.Dummy.csproj
@@ -12,7 +12,7 @@
-
+
\ No newline at end of file
diff --git a/CliFx.sln b/CliFx.sln
index 7587f88..f3d688a 100644
--- a/CliFx.sln
+++ b/CliFx.sln
@@ -20,9 +20,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CliFx.Demo", "CliFx.Demo\Cl
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CliFx.Tests.Dummy", "CliFx.Tests.Dummy\CliFx.Tests.Dummy.csproj", "{F717347D-8656-44DA-A4A2-BE515E8C4655}"
EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CliFx.Analyzers", "CliFx.Analyzers\CliFx.Analyzers.csproj", "{F8460D69-F8CF-405C-A6ED-BED02A21DB42}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CliFx.Analyzers.Tests", "CliFx.Analyzers.Tests\CliFx.Analyzers.Tests.csproj", "{49878E75-2097-4C79-9151-B98A28FBB973}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CliFx.SourceGeneration", "CliFx.SourceGeneration\CliFx.SourceGeneration.csproj", "{F8460D69-F8CF-405C-A6ED-BED02A21DB42}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -106,18 +104,6 @@ Global
{F8460D69-F8CF-405C-A6ED-BED02A21DB42}.Release|x64.Build.0 = Release|Any CPU
{F8460D69-F8CF-405C-A6ED-BED02A21DB42}.Release|x86.ActiveCfg = Release|Any CPU
{F8460D69-F8CF-405C-A6ED-BED02A21DB42}.Release|x86.Build.0 = Release|Any CPU
- {49878E75-2097-4C79-9151-B98A28FBB973}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {49878E75-2097-4C79-9151-B98A28FBB973}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {49878E75-2097-4C79-9151-B98A28FBB973}.Debug|x64.ActiveCfg = Debug|Any CPU
- {49878E75-2097-4C79-9151-B98A28FBB973}.Debug|x64.Build.0 = Debug|Any CPU
- {49878E75-2097-4C79-9151-B98A28FBB973}.Debug|x86.ActiveCfg = Debug|Any CPU
- {49878E75-2097-4C79-9151-B98A28FBB973}.Debug|x86.Build.0 = Debug|Any CPU
- {49878E75-2097-4C79-9151-B98A28FBB973}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {49878E75-2097-4C79-9151-B98A28FBB973}.Release|Any CPU.Build.0 = Release|Any CPU
- {49878E75-2097-4C79-9151-B98A28FBB973}.Release|x64.ActiveCfg = Release|Any CPU
- {49878E75-2097-4C79-9151-B98A28FBB973}.Release|x64.Build.0 = Release|Any CPU
- {49878E75-2097-4C79-9151-B98A28FBB973}.Release|x86.ActiveCfg = Release|Any CPU
- {49878E75-2097-4C79-9151-B98A28FBB973}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/CliFx/Attributes/CommandAttribute.cs b/CliFx/Attributes/CommandAttribute.cs
index 13025b6..16024fe 100644
--- a/CliFx/Attributes/CommandAttribute.cs
+++ b/CliFx/Attributes/CommandAttribute.cs
@@ -25,4 +25,4 @@ public class CommandAttribute(string? name = null) : Attribute
/// This is shown to the user in the help text.
///
public string? Description { get; set; }
-}
\ No newline at end of file
+}
diff --git a/CliFx/Attributes/CommandInputAttribute.cs b/CliFx/Attributes/CommandInputAttribute.cs
index 53457ce..dd98dee 100644
--- a/CliFx/Attributes/CommandInputAttribute.cs
+++ b/CliFx/Attributes/CommandInputAttribute.cs
@@ -31,4 +31,4 @@ public abstract class CommandInputAttribute : Attribute
/// Validators must derive from .
///
public Type[] Validators { get; set; } = [];
-}
\ No newline at end of file
+}
diff --git a/CliFx/CliApplication.cs b/CliFx/CliApplication.cs
index fdd82a6..7709b7f 100644
--- a/CliFx/CliApplication.cs
+++ b/CliFx/CliApplication.cs
@@ -114,7 +114,7 @@ public class CliApplication(
try
{
// Activate the command instance with the provided input
- commandSchema.Activate(commandInput, commandInstance);
+ commandSchema.Activate(commandInstance, commandInput);
// Handle the version option
if (commandInstance is ICommandWithVersionOption { IsVersionRequested: true })
diff --git a/CliFx/CliApplicationBuilder.cs b/CliFx/CliApplicationBuilder.cs
index 9a8dc14..c55c98c 100644
--- a/CliFx/CliApplicationBuilder.cs
+++ b/CliFx/CliApplicationBuilder.cs
@@ -26,6 +26,10 @@ public partial class CliApplicationBuilder
private IConsole? _console;
private ITypeActivator? _typeActivator;
+ // TODO:
+ // The source generator should generate an internal extension method for the builder called
+ // AddCommandsFromThisAssembly() that would add all command types from the assembly where the builder is used.
+
///
/// Adds a command to the application.
///
@@ -35,6 +39,17 @@ public partial class CliApplicationBuilder
return this;
}
+ ///
+ /// Adds multiple commands to the application.
+ ///
+ public CliApplicationBuilder AddCommands(IReadOnlyList commandSchemas)
+ {
+ foreach (var commandSchema in commandSchemas)
+ AddCommand(commandSchema);
+
+ return this;
+ }
+
///
/// Specifies whether debug mode (enabled with the [debug] directive) is allowed in the application.
///
diff --git a/CliFx/CliFx.csproj b/CliFx/CliFx.csproj
index d584fd7..81549f1 100644
--- a/CliFx/CliFx.csproj
+++ b/CliFx/CliFx.csproj
@@ -31,17 +31,7 @@
-
-
-
-
-
-
-
-
-
-
-
+
\ No newline at end of file
diff --git a/CliFx/Extensibility/IBindingConverter.cs b/CliFx/Extensibility/IBindingConverter.cs
index fa4fbd9..86f9fe0 100644
--- a/CliFx/Extensibility/IBindingConverter.cs
+++ b/CliFx/Extensibility/IBindingConverter.cs
@@ -13,5 +13,5 @@ public interface IBindingConverter
///
/// Parses the value from a raw command-line argument.
///
- object? Convert(string? rawValue, IFormatProvider? formatProvider);
+ object? Convert(string? rawArgument, IFormatProvider? formatProvider);
}
diff --git a/CliFx/Extensibility/NoopBindingConverter.cs b/CliFx/Extensibility/NoopBindingConverter.cs
index 92537cd..fef732e 100644
--- a/CliFx/Extensibility/NoopBindingConverter.cs
+++ b/CliFx/Extensibility/NoopBindingConverter.cs
@@ -8,5 +8,5 @@ namespace CliFx.Extensibility;
public class NoopBindingConverter : IBindingConverter
{
///
- public object? Convert(string? rawValue, IFormatProvider? formatProvider) => rawValue;
+ public object? Convert(string? rawArgument, IFormatProvider? formatProvider) => rawArgument;
}
diff --git a/CliFx/FallbackDefaultCommand.cs b/CliFx/FallbackDefaultCommand.cs
index 6e8fd7d..3ef1406 100644
--- a/CliFx/FallbackDefaultCommand.cs
+++ b/CliFx/FallbackDefaultCommand.cs
@@ -9,9 +9,7 @@ namespace CliFx;
// Fallback command used when the application doesn't have one configured.
// This command is only used as a stub for help text.
[Command]
-internal partial class FallbackDefaultCommand
- : ICommandWithHelpOption,
- ICommandWithVersionOption
+internal partial class FallbackDefaultCommand : ICommandWithHelpOption, ICommandWithVersionOption
{
[CommandHelpOption]
public bool IsHelpRequested { get; init; }
@@ -26,5 +24,6 @@ internal partial class FallbackDefaultCommand
internal partial class FallbackDefaultCommand
{
- public static CommandSchema Schema { get; } = new CommandSchema(null, null, []);
+ public static CommandSchema Schema { get; } =
+ new CommandSchema(null, null, []);
}
diff --git a/CliFx/Formatting/CommandInputConsoleFormatter.cs b/CliFx/Formatting/CommandInputConsoleFormatter.cs
index fd9a9d6..3bac812 100644
--- a/CliFx/Formatting/CommandInputConsoleFormatter.cs
+++ b/CliFx/Formatting/CommandInputConsoleFormatter.cs
@@ -36,7 +36,7 @@ internal class CommandInputConsoleFormatter(ConsoleWriter consoleWriter)
Write('[');
// Identifier
- Write(ConsoleColor.White, optionInput.GetFormattedIdentifier());
+ Write(ConsoleColor.White, optionInput.FormattedIdentifier);
// Value(s)
foreach (var value in optionInput.Values)
diff --git a/CliFx/Infrastructure/ConsoleReader.cs b/CliFx/Infrastructure/ConsoleReader.cs
index ef83201..c0f0f28 100644
--- a/CliFx/Infrastructure/ConsoleReader.cs
+++ b/CliFx/Infrastructure/ConsoleReader.cs
@@ -7,11 +7,11 @@ using System.Threading.Tasks;
namespace CliFx.Infrastructure;
///
-/// Implements a for reading characters from a console stream.
+/// Implements a for reading characters or binary data from a console stream.
///
// Both the underlying stream AND the stream reader must be synchronized!
// https://github.com/Tyrrrz/CliFx/issues/123
-public class ConsoleReader(IConsole console, Stream stream, Encoding encoding)
+public sealed class ConsoleReader(IConsole console, Stream stream, Encoding encoding)
: StreamReader(Stream.Synchronized(stream), encoding, false, 4096)
{
///
diff --git a/CliFx/Infrastructure/ConsoleWriter.cs b/CliFx/Infrastructure/ConsoleWriter.cs
index 0675599..516945e 100644
--- a/CliFx/Infrastructure/ConsoleWriter.cs
+++ b/CliFx/Infrastructure/ConsoleWriter.cs
@@ -8,11 +8,11 @@ using CliFx.Utils;
namespace CliFx.Infrastructure;
///
-/// Implements a for writing characters to a console stream.
+/// Implements a for writing characters or binary data to a console stream.
///
// Both the underlying stream AND the stream writer must be synchronized!
// https://github.com/Tyrrrz/CliFx/issues/123
-public class ConsoleWriter : StreamWriter
+public sealed class ConsoleWriter : StreamWriter
{
///
/// Initializes an instance of .
diff --git a/CliFx/Input/DirectiveInput.cs b/CliFx/Input/CommandDirectiveInput.cs
similarity index 90%
rename from CliFx/Input/DirectiveInput.cs
rename to CliFx/Input/CommandDirectiveInput.cs
index 9e934e7..2253695 100644
--- a/CliFx/Input/DirectiveInput.cs
+++ b/CliFx/Input/CommandDirectiveInput.cs
@@ -5,7 +5,7 @@ namespace CliFx.Input;
///
/// Input provided by the means of a directive.
///
-public class DirectiveInput(string name)
+public class CommandDirectiveInput(string name)
{
///
/// Directive name.
diff --git a/CliFx/Input/CommandInput.cs b/CliFx/Input/CommandInput.cs
index 63a98fb..a6751f4 100644
--- a/CliFx/Input/CommandInput.cs
+++ b/CliFx/Input/CommandInput.cs
@@ -10,9 +10,9 @@ namespace CliFx.Input;
///
public partial class CommandInput(
string? commandName,
- IReadOnlyList directives,
- IReadOnlyList parameters,
- IReadOnlyList options,
+ IReadOnlyList directives,
+ IReadOnlyList parameters,
+ IReadOnlyList options,
IReadOnlyList environmentVariables
)
{
@@ -24,17 +24,17 @@ public partial class CommandInput(
///
/// Provided directives.
///
- public IReadOnlyList Directives { get; } = directives;
+ public IReadOnlyList Directives { get; } = directives;
///
/// Provided parameters.
///
- public IReadOnlyList Parameters { get; } = parameters;
+ public IReadOnlyList Parameters { get; } = parameters;
///
/// Provided options.
///
- public IReadOnlyList Options { get; } = options;
+ public IReadOnlyList Options { get; } = options;
///
/// Provided environment variables.
@@ -49,12 +49,12 @@ public partial class CommandInput(
public partial class CommandInput
{
- private static IReadOnlyList ParseDirectives(
+ private static IReadOnlyList ParseDirectives(
IReadOnlyList commandLineArguments,
ref int index
)
{
- var result = new List();
+ var result = new List();
// Consume all consecutive directive arguments
for (; index < commandLineArguments.Count; index++)
@@ -66,7 +66,7 @@ public partial class CommandInput
break;
var directiveName = argument.Substring(1, argument.Length - 2);
- result.Add(new DirectiveInput(directiveName));
+ result.Add(new CommandDirectiveInput(directiveName));
}
return result;
@@ -108,12 +108,12 @@ public partial class CommandInput
return commandName;
}
- private static IReadOnlyList ParseParameters(
+ private static IReadOnlyList ParseParameters(
IReadOnlyList commandLineArguments,
ref int index
)
{
- var result = new List();
+ var result = new List();
// Consume all arguments until the first option identifier
for (; index < commandLineArguments.Count; index++)
@@ -135,18 +135,18 @@ public partial class CommandInput
if (isOptionIdentifier)
break;
- result.Add(new ParameterInput(index, argument));
+ result.Add(new CommandParameterInput(index, argument));
}
return result;
}
- private static IReadOnlyList ParseOptions(
+ private static IReadOnlyList ParseOptions(
IReadOnlyList commandLineArguments,
ref int index
)
{
- var result = new List();
+ var result = new List();
var lastOptionIdentifier = default(string?);
var lastOptionValues = new List();
@@ -165,7 +165,7 @@ public partial class CommandInput
{
// Flush previous
if (!string.IsNullOrWhiteSpace(lastOptionIdentifier))
- result.Add(new OptionInput(lastOptionIdentifier, lastOptionValues));
+ result.Add(new CommandOptionInput(lastOptionIdentifier, lastOptionValues));
lastOptionIdentifier = argument[2..];
lastOptionValues = [];
@@ -177,7 +177,7 @@ public partial class CommandInput
{
// Flush previous
if (!string.IsNullOrWhiteSpace(lastOptionIdentifier))
- result.Add(new OptionInput(lastOptionIdentifier, lastOptionValues));
+ result.Add(new CommandOptionInput(lastOptionIdentifier, lastOptionValues));
lastOptionIdentifier = identifier.AsString();
lastOptionValues = [];
@@ -192,7 +192,7 @@ public partial class CommandInput
// Flush the last option
if (!string.IsNullOrWhiteSpace(lastOptionIdentifier))
- result.Add(new OptionInput(lastOptionIdentifier, lastOptionValues));
+ result.Add(new CommandOptionInput(lastOptionIdentifier, lastOptionValues));
return result;
}
diff --git a/CliFx/Input/OptionInput.cs b/CliFx/Input/CommandOptionInput.cs
similarity index 64%
rename from CliFx/Input/OptionInput.cs
rename to CliFx/Input/CommandOptionInput.cs
index 99ed920..a90e623 100644
--- a/CliFx/Input/OptionInput.cs
+++ b/CliFx/Input/CommandOptionInput.cs
@@ -5,22 +5,22 @@ namespace CliFx.Input;
///
/// Input provided by the means of an option.
///
-public class OptionInput(string identifier, IReadOnlyList values)
+public class CommandOptionInput(string identifier, IReadOnlyList values)
{
///
/// Option identifier (either the name or the short name).
///
public string Identifier { get; } = identifier;
+ internal string FormattedIdentifier { get; } =
+ identifier switch
+ {
+ { Length: >= 2 } => "--" + identifier,
+ _ => '-' + identifier
+ };
+
///
/// Option value(s).
///
public IReadOnlyList Values { get; } = values;
-
- internal string GetFormattedIdentifier() =>
- Identifier switch
- {
- { Length: >= 2 } => "--" + Identifier,
- _ => '-' + Identifier
- };
}
diff --git a/CliFx/Input/ParameterInput.cs b/CliFx/Input/CommandParameterInput.cs
similarity index 86%
rename from CliFx/Input/ParameterInput.cs
rename to CliFx/Input/CommandParameterInput.cs
index 2a1713e..6e87a2a 100644
--- a/CliFx/Input/ParameterInput.cs
+++ b/CliFx/Input/CommandParameterInput.cs
@@ -3,7 +3,7 @@
///
/// Input provided by the means of a parameter.
///
-public class ParameterInput(int order, string value)
+public class CommandParameterInput(int order, string value)
{
///
/// Parameter order.
diff --git a/CliFx/Schema/ApplicationSchema.cs b/CliFx/Schema/ApplicationSchema.cs
index 488ce9a..8b463b5 100644
--- a/CliFx/Schema/ApplicationSchema.cs
+++ b/CliFx/Schema/ApplicationSchema.cs
@@ -32,7 +32,7 @@ public class ApplicationSchema(IReadOnlyList commands)
foreach (var potentialDescendantCommand in potentialDescendantCommands)
{
- // Default commands can't be descendant of anything
+ // Default commands can't be descendants of anything
if (string.IsNullOrWhiteSpace(potentialDescendantCommand.Name))
continue;
diff --git a/CliFx/Schema/CommandInputSchema.cs b/CliFx/Schema/CommandInputSchema.cs
index 962bfe8..4c08463 100644
--- a/CliFx/Schema/CommandInputSchema.cs
+++ b/CliFx/Schema/CommandInputSchema.cs
@@ -66,7 +66,7 @@ public abstract class CommandInputSchema(
}
}
- internal void Activate(ICommand instance, IReadOnlyList rawInputs)
+ internal void Activate(ICommand instance, IReadOnlyList rawArguments)
{
var formatProvider = CultureInfo.InvariantCulture;
@@ -75,15 +75,20 @@ public abstract class CommandInputSchema(
// Multiple values expected, single or multiple values provided
if (IsSequence)
{
- var value = rawInputs.Select(v => Converter.Convert(v, formatProvider)).ToArray();
+ var value = rawArguments
+ .Select(v => Converter.Convert(v, formatProvider))
+ .ToArray();
+
+ // TODO: cast array to the proper type
+
Validate(value);
Property.SetValue(instance, value);
}
// Single value expected, single value provided
- else if (rawInputs.Count <= 1)
+ else if (rawArguments.Count <= 1)
{
- var value = Converter.Convert(rawInputs.SingleOrDefault(), formatProvider);
+ var value = Converter.Convert(rawArguments.SingleOrDefault(), formatProvider);
Validate(value);
Property.SetValue(instance, value);
@@ -93,9 +98,9 @@ public abstract class CommandInputSchema(
{
throw CliFxException.UserError(
$"""
- {Kind} {FormattedIdentifier} expects a single argument, but provided with multiple:
- {rawInputs.Select(v => '<' + v + '>').JoinToString(" ")}
- """
+ {Kind} {FormattedIdentifier} expects a single argument, but provided with multiple:
+ {rawArguments.Select(v => '<' + v + '>').JoinToString(" ")}
+ """
);
}
}
@@ -103,16 +108,17 @@ public abstract class CommandInputSchema(
{
throw CliFxException.UserError(
$"""
- {Kind} {FormattedIdentifier} cannot be set from the provided argument(s):
- {rawInputs.Select(v => '<' + v + '>').JoinToString(" ")}
- Error: {ex.Message}
- """,
+ {Kind} {FormattedIdentifier} cannot be set from the provided argument(s):
+ {rawArguments.Select(v => '<' + v + '>').JoinToString(" ")}
+ Error: {ex.Message}
+ """,
ex
);
}
}
///
+ [ExcludeFromCodeCoverage]
public override string ToString() => FormattedIdentifier;
}
diff --git a/CliFx/Schema/CommandOptionSchema.cs b/CliFx/Schema/CommandOptionSchema.cs
index 598f80c..025c509 100644
--- a/CliFx/Schema/CommandOptionSchema.cs
+++ b/CliFx/Schema/CommandOptionSchema.cs
@@ -18,7 +18,7 @@ public class CommandOptionSchema(
string? description,
IBindingConverter converter,
IReadOnlyList validators
-) : CommandInputSchema(property,description, converter, validators)
+) : CommandInputSchema(property, description, converter, validators)
{
internal override string Kind => "Option";
diff --git a/CliFx/Schema/CommandSchema.cs b/CliFx/Schema/CommandSchema.cs
index fc027c8..310a929 100644
--- a/CliFx/Schema/CommandSchema.cs
+++ b/CliFx/Schema/CommandSchema.cs
@@ -53,7 +53,8 @@ public class CommandSchema(
///
/// Option inputs of the command.
///
- public IReadOnlyList Options { get; } = inputs.OfType().ToArray();
+ public IReadOnlyList Options { get; } =
+ inputs.OfType().ToArray();
internal bool MatchesName(string? name) =>
!string.IsNullOrWhiteSpace(Name)
@@ -79,7 +80,7 @@ public class CommandSchema(
return result;
}
- private void ActivateParameters(CommandInput input, ICommand instance)
+ private void ActivateParameters(ICommand instance, CommandInput input)
{
// Ensure there are no unexpected parameters and that all parameters are provided
var remainingParameterInputs = input.Parameters.ToList();
@@ -107,10 +108,7 @@ public class CommandSchema(
{
var parameterInputs = input.Parameters.Skip(position).ToArray();
- parameterSchema.Activate(
- instance,
- parameterInputs.Select(p => p.Value).ToArray()
- );
+ parameterSchema.Activate(instance, parameterInputs.Select(p => p.Value).ToArray());
position += parameterInputs.Length;
remainingParameterInputs.RemoveRange(parameterInputs);
@@ -142,12 +140,11 @@ public class CommandSchema(
}
}
- private void ActivateOptions(CommandInput input, ICommand instance)
+ private void ActivateOptions(ICommand instance, CommandInput input)
{
// Ensure there are no unrecognized options and that all required options are set
var remainingOptionInputs = input.Options.ToList();
- var remainingRequiredOptionSchemas = Options.Where(o => o.IsRequired)
- .ToList();
+ var remainingRequiredOptionSchemas = Options.Where(o => o.IsRequired).ToList();
foreach (var optionSchema in Options)
{
@@ -197,7 +194,7 @@ public class CommandSchema(
throw CliFxException.UserError(
$"""
Unrecognized option(s):
- {remainingOptionInputs.Select(o => o.GetFormattedIdentifier()).JoinToString(", ")}
+ {remainingOptionInputs.Select(o => o.FormattedIdentifier).JoinToString(", ")}
"""
);
}
@@ -215,11 +212,15 @@ public class CommandSchema(
}
}
- internal void Activate(CommandInput input, ICommand instance)
+ internal void Activate(ICommand instance, CommandInput input)
{
- ActivateParameters(input, instance);
- ActivateOptions(input, instance);
+ ActivateParameters(instance, input);
+ ActivateOptions(instance, input);
}
+
+ ///
+ [ExcludeFromCodeCoverage]
+ public override string ToString() => Name ?? "";
}
///
diff --git a/CliFx/Schema/PropertyBinding.cs b/CliFx/Schema/PropertyBinding.cs
index 4d267d4..36130a8 100644
--- a/CliFx/Schema/PropertyBinding.cs
+++ b/CliFx/Schema/PropertyBinding.cs
@@ -6,7 +6,7 @@ using System.Linq;
namespace CliFx.Schema;
///
-/// Represents a wrapper around a CLR property that provides read and write access to its value.
+/// Provides read and write access to a CLR property.
///
public class PropertyBinding(
[DynamicallyAccessedMembers(
diff --git a/CliFx/Utils/Extensions/CollectionExtensions.cs b/CliFx/Utils/Extensions/CollectionExtensions.cs
index cc1e165..f620de6 100644
--- a/CliFx/Utils/Extensions/CollectionExtensions.cs
+++ b/CliFx/Utils/Extensions/CollectionExtensions.cs
@@ -13,6 +13,16 @@ internal static class CollectionExtensions
yield return (o, i++);
}
+ public static IEnumerable WhereNotNull(this IEnumerable source)
+ where T : class
+ {
+ foreach (var i in source)
+ {
+ if (i is not null)
+ yield return i;
+ }
+ }
+
public static IEnumerable WhereNotNullOrWhiteSpace(this IEnumerable source)
{
foreach (var i in source)
diff --git a/CliFx/Utils/Extensions/TypeExtensions.cs b/CliFx/Utils/Extensions/TypeExtensions.cs
index 1d469a0..4592778 100644
--- a/CliFx/Utils/Extensions/TypeExtensions.cs
+++ b/CliFx/Utils/Extensions/TypeExtensions.cs
@@ -10,31 +10,29 @@ internal static class TypeExtensions
{
public static Type? TryGetEnumerableUnderlyingType(
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)] this Type type
- )
- {
- if (type.IsPrimitive)
- return null;
+ ) =>
+ type.GetInterfaces()
+ .Select(i =>
+ {
+ if (i == typeof(IEnumerable))
+ return typeof(object);
- if (type == typeof(IEnumerable))
- return typeof(object);
+ if (i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IEnumerable<>))
+ return i.GetGenericArguments().FirstOrDefault();
- if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(IEnumerable<>))
- return type.GetGenericArguments().FirstOrDefault();
-
- return type.GetInterfaces()
- .Select(t => TryGetEnumerableUnderlyingType(t))
- .Where(t => t is not null)
+ return null;
+ })
+ .WhereNotNull()
// Every IEnumerable implements IEnumerable (which is essentially IEnumerable