From 41cb8647b57b9b2f3b5a7f270dec7076aff8e498 Mon Sep 17 00:00:00 2001 From: Oleksii Holub <1935960+Tyrrrz@users.noreply.github.com> Date: Sat, 16 Apr 2022 22:54:11 +0000 Subject: [PATCH] Produce analyzer errors for invalid generic arguments in converters and validators Closes #103 --- ...tionMustHaveValidConverterAnalyzerSpecs.cs | 26 +++++++++++++++++- ...ionMustHaveValidValidatorsAnalyzerSpecs.cs | 26 +++++++++++++++++- ...eterMustHaveValidConverterAnalyzerSpecs.cs | 27 ++++++++++++++++++- ...terMustHaveValidValidatorsAnalyzerSpecs.cs | 26 +++++++++++++++++- .../Utils/AnalyzerAssertions.cs | 24 ++++++++++------- .../CommandMustBeAnnotatedAnalyzer.cs | 4 +-- .../CommandMustImplementInterfaceAnalyzer.cs | 4 +-- .../ObjectModel/CommandOptionSymbol.cs | 2 +- CliFx.Analyzers/ObjectModel/SymbolNames.cs | 2 -- .../OptionMustBeInsideCommandAnalyzer.cs | 2 +- .../OptionMustHaveValidConverterAnalyzer.cs | 14 +++++----- .../OptionMustHaveValidValidatorsAnalyzer.cs | 14 +++++----- .../ParameterMustBeInsideCommandAnalyzer.cs | 2 +- .../ParameterMustBeLastIfNonScalarAnalyzer.cs | 2 +- ...arameterMustBeSingleIfNonScalarAnalyzer.cs | 2 +- ...ParameterMustHaveValidConverterAnalyzer.cs | 14 +++++----- ...arameterMustHaveValidValidatorsAnalyzer.cs | 14 +++++----- .../Utils/Extensions/RoslynExtensions.cs | 18 +++++++++++++ CliFx/CliApplication.cs | 4 +-- CliFx/CommandBinder.cs | 22 +++++++-------- 20 files changed, 188 insertions(+), 61 deletions(-) diff --git a/CliFx.Analyzers.Tests/OptionMustHaveValidConverterAnalyzerSpecs.cs b/CliFx.Analyzers.Tests/OptionMustHaveValidConverterAnalyzerSpecs.cs index 649bf2f..447f254 100644 --- a/CliFx.Analyzers.Tests/OptionMustHaveValidConverterAnalyzerSpecs.cs +++ b/CliFx.Analyzers.Tests/OptionMustHaveValidConverterAnalyzerSpecs.cs @@ -33,7 +33,31 @@ public class MyCommand : ICommand } [Fact] - public void Analyzer_does_not_report_an_error_if_the_specified_option_converter_derives_from_BindingConverter() + public void Analyzer_reports_an_error_if_the_specified_option_converter_does_not_derive_from_a_compatible_BindingConverter() + { + // Arrange + // language=cs + 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; set; } + + public ValueTask ExecuteAsync(IConsole console) => default; +}"; + + // Act & assert + Analyzer.Should().ProduceDiagnostics(code); + } + + [Fact] + public void Analyzer_does_not_report_an_error_if_the_specified_option_converter_derives_from_a_compatible_BindingConverter() { // Arrange // language=cs diff --git a/CliFx.Analyzers.Tests/OptionMustHaveValidValidatorsAnalyzerSpecs.cs b/CliFx.Analyzers.Tests/OptionMustHaveValidValidatorsAnalyzerSpecs.cs index dbbdbc1..35b0d0c 100644 --- a/CliFx.Analyzers.Tests/OptionMustHaveValidValidatorsAnalyzerSpecs.cs +++ b/CliFx.Analyzers.Tests/OptionMustHaveValidValidatorsAnalyzerSpecs.cs @@ -33,7 +33,31 @@ public class MyCommand : ICommand } [Fact] - public void Analyzer_does_not_report_an_error_if_all_specified_option_validators_derive_from_BindingValidator() + public void Analyzer_reports_an_error_if_one_of_the_specified_option_validators_does_not_derive_from_a_compatible_BindingValidator() + { + // Arrange + // language=cs + 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; set; } + + public ValueTask ExecuteAsync(IConsole console) => default; +}"; + + // Act & assert + Analyzer.Should().ProduceDiagnostics(code); + } + + [Fact] + public void Analyzer_does_not_report_an_error_if_each_specified_option_validator_derives_from_a_compatible_BindingValidator() { // Arrange // language=cs diff --git a/CliFx.Analyzers.Tests/ParameterMustHaveValidConverterAnalyzerSpecs.cs b/CliFx.Analyzers.Tests/ParameterMustHaveValidConverterAnalyzerSpecs.cs index 01e3c15..eed9ef2 100644 --- a/CliFx.Analyzers.Tests/ParameterMustHaveValidConverterAnalyzerSpecs.cs +++ b/CliFx.Analyzers.Tests/ParameterMustHaveValidConverterAnalyzerSpecs.cs @@ -33,7 +33,32 @@ public class MyCommand : ICommand } [Fact] - public void Analyzer_does_not_report_an_error_if_the_specified_parameter_converter_derives_from_BindingConverter() + public void Analyzer_reports_an_error_if_the_specified_parameter_converter_does_not_derive_from_a_compatible_BindingConverter() + { + // Arrange + // language=cs + 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 string Foo { get; set; } + + public ValueTask ExecuteAsync(IConsole console) => default; +}"; + + + // Act & assert + Analyzer.Should().ProduceDiagnostics(code); + } + + [Fact] + public void Analyzer_does_not_report_an_error_if_the_specified_parameter_converter_derives_from_a_compatible_BindingConverter() { // Arrange // language=cs diff --git a/CliFx.Analyzers.Tests/ParameterMustHaveValidValidatorsAnalyzerSpecs.cs b/CliFx.Analyzers.Tests/ParameterMustHaveValidValidatorsAnalyzerSpecs.cs index f559afd..66f67a8 100644 --- a/CliFx.Analyzers.Tests/ParameterMustHaveValidValidatorsAnalyzerSpecs.cs +++ b/CliFx.Analyzers.Tests/ParameterMustHaveValidValidatorsAnalyzerSpecs.cs @@ -33,7 +33,31 @@ public class MyCommand : ICommand } [Fact] - public void Analyzer_does_not_report_an_error_if_all_specified_parameter_validators_derive_from_BindingValidator() + public void Analyzer_reports_an_error_if_one_of_the_specified_parameter_validators_does_not_derive_from_a_compatible_BindingValidator() + { + // Arrange + // language=cs + 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 string Foo { get; set; } + + public ValueTask ExecuteAsync(IConsole console) => default; +}"; + + // Act & assert + Analyzer.Should().ProduceDiagnostics(code); + } + + [Fact] + public void Analyzer_does_not_report_an_error_if_each_specified_parameter_validator_derives_from_a_compatible_BindingValidator() { // Arrange // language=cs diff --git a/CliFx.Analyzers.Tests/Utils/AnalyzerAssertions.cs b/CliFx.Analyzers.Tests/Utils/AnalyzerAssertions.cs index 39bc0b1..ec0e60b 100644 --- a/CliFx.Analyzers.Tests/Utils/AnalyzerAssertions.cs +++ b/CliFx.Analyzers.Tests/Utils/AnalyzerAssertions.cs @@ -57,7 +57,7 @@ internal class AnalyzerAssertions : ReferenceTypeAssertions d.Id).Distinct().ToArray(); var producedDiagnosticIds = producedDiagnostics.Select(d => d.Id).Distinct().ToArray(); - var result = + var isSuccessfulAssertion = expectedDiagnosticIds.Intersect(producedDiagnosticIds).Count() == expectedDiagnosticIds.Length; - Execute.Assertion.ForCondition(result).FailWith(() => + Execute.Assertion.ForCondition(isSuccessfulAssertion).FailWith(() => { var buffer = new StringBuilder(); @@ -125,10 +125,17 @@ internal class AnalyzerAssertions : ReferenceTypeAssertions"); } return new FailReason(buffer.ToString()); @@ -138,10 +145,9 @@ internal class AnalyzerAssertions : ReferenceTypeAssertions + Execute.Assertion.ForCondition(isSuccessfulAssertion).FailWith(() => { var buffer = new StringBuilder(); diff --git a/CliFx.Analyzers/CommandMustBeAnnotatedAnalyzer.cs b/CliFx.Analyzers/CommandMustBeAnnotatedAnalyzer.cs index e02aade..56f5175 100644 --- a/CliFx.Analyzers/CommandMustBeAnnotatedAnalyzer.cs +++ b/CliFx.Analyzers/CommandMustBeAnnotatedAnalyzer.cs @@ -30,12 +30,12 @@ public class CommandMustBeAnnotatedAnalyzer : AnalyzerBase var implementsCommandInterface = type .AllInterfaces - .Any(s => s.DisplayNameMatches(SymbolNames.CliFxCommandInterface)); + .Any(i => i.DisplayNameMatches(SymbolNames.CliFxCommandInterface)); var hasCommandAttribute = type .GetAttributes() .Select(a => a.AttributeClass) - .Any(s => s.DisplayNameMatches(SymbolNames.CliFxCommandAttribute)); + .Any(c => c.DisplayNameMatches(SymbolNames.CliFxCommandAttribute)); // If the interface is implemented, but the attribute is missing, // then it's very likely a user error. diff --git a/CliFx.Analyzers/CommandMustImplementInterfaceAnalyzer.cs b/CliFx.Analyzers/CommandMustImplementInterfaceAnalyzer.cs index 2862a51..a2710d5 100644 --- a/CliFx.Analyzers/CommandMustImplementInterfaceAnalyzer.cs +++ b/CliFx.Analyzers/CommandMustImplementInterfaceAnalyzer.cs @@ -25,11 +25,11 @@ public class CommandMustImplementInterfaceAnalyzer : AnalyzerBase var hasCommandAttribute = type .GetAttributes() .Select(a => a.AttributeClass) - .Any(s => s.DisplayNameMatches(SymbolNames.CliFxCommandAttribute)); + .Any(c => c.DisplayNameMatches(SymbolNames.CliFxCommandAttribute)); var implementsCommandInterface = type .AllInterfaces - .Any(s => s.DisplayNameMatches(SymbolNames.CliFxCommandInterface)); + .Any(i => i.DisplayNameMatches(SymbolNames.CliFxCommandInterface)); // If the attribute is present, but the interface is not implemented, // it's very likely a user error. diff --git a/CliFx.Analyzers/ObjectModel/CommandOptionSymbol.cs b/CliFx.Analyzers/ObjectModel/CommandOptionSymbol.cs index 2d53e1a..0f18bd1 100644 --- a/CliFx.Analyzers/ObjectModel/CommandOptionSymbol.cs +++ b/CliFx.Analyzers/ObjectModel/CommandOptionSymbol.cs @@ -1,7 +1,7 @@ using System.Collections.Generic; -using Microsoft.CodeAnalysis; using System.Linq; using CliFx.Analyzers.Utils.Extensions; +using Microsoft.CodeAnalysis; namespace CliFx.Analyzers.ObjectModel; diff --git a/CliFx.Analyzers/ObjectModel/SymbolNames.cs b/CliFx.Analyzers/ObjectModel/SymbolNames.cs index 1d137e2..aae8629 100644 --- a/CliFx.Analyzers/ObjectModel/SymbolNames.cs +++ b/CliFx.Analyzers/ObjectModel/SymbolNames.cs @@ -7,8 +7,6 @@ internal static class SymbolNames public const string CliFxCommandParameterAttribute = "CliFx.Attributes.CommandParameterAttribute"; public const string CliFxCommandOptionAttribute = "CliFx.Attributes.CommandOptionAttribute"; public const string CliFxConsoleInterface = "CliFx.Infrastructure.IConsole"; - public const string CliFxBindingConverterInterface = "CliFx.Extensibility.IBindingConverter"; public const string CliFxBindingConverterClass = "CliFx.Extensibility.BindingConverter"; - public const string CliFxBindingValidatorInterface = "CliFx.Extensibility.IBindingValidator"; public const string CliFxBindingValidatorClass = "CliFx.Extensibility.BindingValidator"; } \ No newline at end of file diff --git a/CliFx.Analyzers/OptionMustBeInsideCommandAnalyzer.cs b/CliFx.Analyzers/OptionMustBeInsideCommandAnalyzer.cs index 1f666e5..7975ba3 100644 --- a/CliFx.Analyzers/OptionMustBeInsideCommandAnalyzer.cs +++ b/CliFx.Analyzers/OptionMustBeInsideCommandAnalyzer.cs @@ -34,7 +34,7 @@ public class OptionMustBeInsideCommandAnalyzer : AnalyzerBase var isInsideCommand = property .ContainingType .AllInterfaces - .Any(s => s.DisplayNameMatches(SymbolNames.CliFxCommandInterface)); + .Any(i => i.DisplayNameMatches(SymbolNames.CliFxCommandInterface)); if (!isInsideCommand) { diff --git a/CliFx.Analyzers/OptionMustHaveValidConverterAnalyzer.cs b/CliFx.Analyzers/OptionMustHaveValidConverterAnalyzer.cs index 139a2fc..835b74a 100644 --- a/CliFx.Analyzers/OptionMustHaveValidConverterAnalyzer.cs +++ b/CliFx.Analyzers/OptionMustHaveValidConverterAnalyzer.cs @@ -13,7 +13,7 @@ public class OptionMustHaveValidConverterAnalyzer : AnalyzerBase public OptionMustHaveValidConverterAnalyzer() : base( $"Option converters must derive from `{SymbolNames.CliFxBindingConverterClass}`", - $"Converter specified for this option must derive from `{SymbolNames.CliFxBindingConverterClass}`.") + $"Converter specified for this option must derive from a compatible `{SymbolNames.CliFxBindingConverterClass}`.") { } @@ -29,13 +29,15 @@ public class OptionMustHaveValidConverterAnalyzer : AnalyzerBase if (option.ConverterType is null) return; - // We check against an internal interface because checking against a generic class is a pain - var converterImplementsInterface = option + var converterValueType = option .ConverterType - .AllInterfaces - .Any(s => s.DisplayNameMatches(SymbolNames.CliFxBindingConverterInterface)); + .GetBaseTypes() + .FirstOrDefault(t => t.ConstructedFrom.DisplayNameMatches(SymbolNames.CliFxBindingConverterClass))? + .TypeArguments + .FirstOrDefault(); - if (!converterImplementsInterface) + // Value returned by the converter must be assignable to the property type + if (converterValueType is null || !property.Type.IsAssignableFrom(converterValueType)) { context.ReportDiagnostic(CreateDiagnostic(propertyDeclaration.GetLocation())); } diff --git a/CliFx.Analyzers/OptionMustHaveValidValidatorsAnalyzer.cs b/CliFx.Analyzers/OptionMustHaveValidValidatorsAnalyzer.cs index 3297cd6..d5f7b67 100644 --- a/CliFx.Analyzers/OptionMustHaveValidValidatorsAnalyzer.cs +++ b/CliFx.Analyzers/OptionMustHaveValidValidatorsAnalyzer.cs @@ -13,7 +13,7 @@ public class OptionMustHaveValidValidatorsAnalyzer : AnalyzerBase public OptionMustHaveValidValidatorsAnalyzer() : base( $"Option validators must derive from `{SymbolNames.CliFxBindingValidatorClass}`", - $"All validators specified for this option must derive from `{SymbolNames.CliFxBindingValidatorClass}`.") + $"Each validator specified for this option must derive from a compatible `{SymbolNames.CliFxBindingValidatorClass}`.") { } @@ -28,12 +28,14 @@ public class OptionMustHaveValidValidatorsAnalyzer : AnalyzerBase foreach (var validatorType in option.ValidatorTypes) { - // We check against an internal interface because checking against a generic class is a pain - var validatorImplementsInterface = validatorType - .AllInterfaces - .Any(s => s.DisplayNameMatches(SymbolNames.CliFxBindingValidatorInterface)); + var validatorValueType = validatorType + .GetBaseTypes() + .FirstOrDefault(t => t.ConstructedFrom.DisplayNameMatches(SymbolNames.CliFxBindingValidatorClass))? + .TypeArguments + .FirstOrDefault(); - if (!validatorImplementsInterface) + // Value passed to the validator must be assignable from the property type + if (validatorValueType is null || !validatorValueType.IsAssignableFrom(property.Type)) { context.ReportDiagnostic(CreateDiagnostic(propertyDeclaration.GetLocation())); diff --git a/CliFx.Analyzers/ParameterMustBeInsideCommandAnalyzer.cs b/CliFx.Analyzers/ParameterMustBeInsideCommandAnalyzer.cs index 0fe55ad..5cbe96d 100644 --- a/CliFx.Analyzers/ParameterMustBeInsideCommandAnalyzer.cs +++ b/CliFx.Analyzers/ParameterMustBeInsideCommandAnalyzer.cs @@ -34,7 +34,7 @@ public class ParameterMustBeInsideCommandAnalyzer : AnalyzerBase var isInsideCommand = property .ContainingType .AllInterfaces - .Any(s => s.DisplayNameMatches(SymbolNames.CliFxCommandInterface)); + .Any(i => i.DisplayNameMatches(SymbolNames.CliFxCommandInterface)); if (!isInsideCommand) { diff --git a/CliFx.Analyzers/ParameterMustBeLastIfNonScalarAnalyzer.cs b/CliFx.Analyzers/ParameterMustBeLastIfNonScalarAnalyzer.cs index ccfbb09..ee76e55 100644 --- a/CliFx.Analyzers/ParameterMustBeLastIfNonScalarAnalyzer.cs +++ b/CliFx.Analyzers/ParameterMustBeLastIfNonScalarAnalyzer.cs @@ -22,7 +22,7 @@ public class ParameterMustBeLastIfNonScalarAnalyzer : AnalyzerBase type.DisplayNameMatches("System.String") || !type.AllInterfaces .Select(i => i.ConstructedFrom) - .Any(s => s.DisplayNameMatches("System.Collections.Generic.IEnumerable")); + .Any(t => t.DisplayNameMatches("System.Collections.Generic.IEnumerable")); private void Analyze( SyntaxNodeAnalysisContext context, diff --git a/CliFx.Analyzers/ParameterMustBeSingleIfNonScalarAnalyzer.cs b/CliFx.Analyzers/ParameterMustBeSingleIfNonScalarAnalyzer.cs index db014f6..7790ff8 100644 --- a/CliFx.Analyzers/ParameterMustBeSingleIfNonScalarAnalyzer.cs +++ b/CliFx.Analyzers/ParameterMustBeSingleIfNonScalarAnalyzer.cs @@ -22,7 +22,7 @@ public class ParameterMustBeSingleIfNonScalarAnalyzer : AnalyzerBase type.DisplayNameMatches("System.String") || !type.AllInterfaces .Select(i => i.ConstructedFrom) - .Any(s => s.DisplayNameMatches("System.Collections.Generic.IEnumerable")); + .Any(t => t.DisplayNameMatches("System.Collections.Generic.IEnumerable")); private void Analyze( SyntaxNodeAnalysisContext context, diff --git a/CliFx.Analyzers/ParameterMustHaveValidConverterAnalyzer.cs b/CliFx.Analyzers/ParameterMustHaveValidConverterAnalyzer.cs index aead684..0056310 100644 --- a/CliFx.Analyzers/ParameterMustHaveValidConverterAnalyzer.cs +++ b/CliFx.Analyzers/ParameterMustHaveValidConverterAnalyzer.cs @@ -13,7 +13,7 @@ public class ParameterMustHaveValidConverterAnalyzer : AnalyzerBase public ParameterMustHaveValidConverterAnalyzer() : base( $"Parameter converters must derive from `{SymbolNames.CliFxBindingConverterClass}`", - $"Converter specified for this parameter must derive from `{SymbolNames.CliFxBindingConverterClass}`.") + $"Converter specified for this parameter must derive from a compatible `{SymbolNames.CliFxBindingConverterClass}`.") { } @@ -29,13 +29,15 @@ public class ParameterMustHaveValidConverterAnalyzer : AnalyzerBase if (parameter.ConverterType is null) return; - // We check against an internal interface because checking against a generic class is a pain - var converterImplementsInterface = parameter + var converterValueType = parameter .ConverterType - .AllInterfaces - .Any(s => s.DisplayNameMatches(SymbolNames.CliFxBindingConverterInterface)); + .GetBaseTypes() + .FirstOrDefault(t => t.ConstructedFrom.DisplayNameMatches(SymbolNames.CliFxBindingConverterClass))? + .TypeArguments + .FirstOrDefault(); - if (!converterImplementsInterface) + // Value returned by the converter must be assignable to the property type + if (converterValueType is null || !property.Type.IsAssignableFrom(converterValueType)) { context.ReportDiagnostic(CreateDiagnostic(propertyDeclaration.GetLocation())); } diff --git a/CliFx.Analyzers/ParameterMustHaveValidValidatorsAnalyzer.cs b/CliFx.Analyzers/ParameterMustHaveValidValidatorsAnalyzer.cs index 8bdf0cd..6d96595 100644 --- a/CliFx.Analyzers/ParameterMustHaveValidValidatorsAnalyzer.cs +++ b/CliFx.Analyzers/ParameterMustHaveValidValidatorsAnalyzer.cs @@ -13,7 +13,7 @@ public class ParameterMustHaveValidValidatorsAnalyzer : AnalyzerBase public ParameterMustHaveValidValidatorsAnalyzer() : base( $"Parameter validators must derive from `{SymbolNames.CliFxBindingValidatorClass}`", - $"All validators specified for this parameter must derive from `{SymbolNames.CliFxBindingValidatorClass}`.") + $"Each validator specified for this parameter must derive from a compatible `{SymbolNames.CliFxBindingValidatorClass}`.") { } @@ -28,12 +28,14 @@ public class ParameterMustHaveValidValidatorsAnalyzer : AnalyzerBase foreach (var validatorType in parameter.ValidatorTypes) { - // We check against an internal interface because checking against a generic class is a pain - var validatorImplementsInterface = validatorType - .AllInterfaces - .Any(s => s.DisplayNameMatches(SymbolNames.CliFxBindingValidatorInterface)); + var validatorValueType = validatorType + .GetBaseTypes() + .FirstOrDefault(t => t.ConstructedFrom.DisplayNameMatches(SymbolNames.CliFxBindingValidatorClass))? + .TypeArguments + .FirstOrDefault(); - if (!validatorImplementsInterface) + // Value passed to the validator must be assignable from the property type + if (validatorValueType is null || !validatorValueType.IsAssignableFrom(property.Type)) { context.ReportDiagnostic(CreateDiagnostic(propertyDeclaration.GetLocation())); diff --git a/CliFx.Analyzers/Utils/Extensions/RoslynExtensions.cs b/CliFx.Analyzers/Utils/Extensions/RoslynExtensions.cs index d52de86..870faf9 100644 --- a/CliFx.Analyzers/Utils/Extensions/RoslynExtensions.cs +++ b/CliFx.Analyzers/Utils/Extensions/RoslynExtensions.cs @@ -1,4 +1,6 @@ using System; +using System.Collections.Generic; +using System.Linq; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; @@ -16,6 +18,22 @@ internal static class RoslynExtensions 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 bool IsAssignableFrom(this ITypeSymbol target, ITypeSymbol source) => + SymbolEqualityComparer.Default.Equals(target, source) || + source.GetBaseTypes().Contains(target, SymbolEqualityComparer.Default) || + source.AllInterfaces.Contains(target, SymbolEqualityComparer.Default); + public static void HandleClassDeclaration( this AnalysisContext analysisContext, Action analyze) diff --git a/CliFx/CliApplication.cs b/CliFx/CliApplication.cs index 84d9e24..41ffeb7 100644 --- a/CliFx/CliApplication.cs +++ b/CliFx/CliApplication.cs @@ -112,7 +112,7 @@ public class CliApplication // Activate command instance var commandInstance = commandSchema == FallbackDefaultCommand.Schema ? new FallbackDefaultCommand() // bypass activator - : (ICommand) _typeActivator.CreateInstance(commandSchema.Type); + : (ICommand)_typeActivator.CreateInstance(commandSchema.Type); // Assemble help context var helpContext = new HelpContext( @@ -176,7 +176,7 @@ public class CliApplication { try { - // Console colors may have already been overriden by the parent process, + // Console colors may have already been overridden by the parent process, // so we need to reset it to make sure that everything we write looks properly. _console.ResetColor(); diff --git a/CliFx/CommandBinder.cs b/CliFx/CommandBinder.cs index 4f6ddac..5cbbb79 100644 --- a/CliFx/CommandBinder.cs +++ b/CliFx/CommandBinder.cs @@ -27,7 +27,7 @@ internal class CommandBinder // Custom converter if (memberSchema.ConverterType is not null) { - var converter = (IBindingConverter) _typeActivator.CreateInstance(memberSchema.ConverterType); + var converter = (IBindingConverter)_typeActivator.CreateInstance(memberSchema.ConverterType); return converter.Convert(rawValue); } @@ -78,24 +78,24 @@ internal class CommandBinder } // String-constructible (FileInfo, etc) - var stringConstructor = targetType.GetConstructor(new[] {typeof(string)}); + var stringConstructor = targetType.GetConstructor(new[] { typeof(string) }); if (stringConstructor is not null) { - return stringConstructor.Invoke(new object?[] {rawValue}); + return stringConstructor.Invoke(new object?[] { rawValue }); } // String-parseable (with IFormatProvider) var parseMethodWithFormatProvider = targetType.TryGetStaticParseMethod(true); if (parseMethodWithFormatProvider is not null) { - return parseMethodWithFormatProvider.Invoke(null, new object?[] {rawValue, _formatProvider}); + return parseMethodWithFormatProvider.Invoke(null, new object?[] { rawValue, _formatProvider }); } // String-parseable (without IFormatProvider) var parseMethod = targetType.TryGetStaticParseMethod(); if (parseMethod is not null) { - return parseMethod.Invoke(null, new object?[] {rawValue}); + return parseMethod.Invoke(null, new object?[] { rawValue }); } throw CliFxException.InternalError( @@ -126,10 +126,10 @@ internal class CommandBinder } // Array-constructible (List, HashSet, etc) - var arrayConstructor = targetEnumerableType.GetConstructor(new[] {arrayType}); + var arrayConstructor = targetEnumerableType.GetConstructor(new[] { arrayType }); if (arrayConstructor is not null) { - return arrayConstructor.Invoke(new object?[] {array}); + return arrayConstructor.Invoke(new object?[] { array }); } throw CliFxException.InternalError( @@ -192,7 +192,7 @@ internal class CommandBinder foreach (var validatorType in memberSchema.ValidatorTypes) { - var validator = (IBindingValidator) _typeActivator.CreateInstance(validatorType); + var validator = (IBindingValidator)_typeActivator.CreateInstance(validatorType); var error = validator.Validate(convertedValue); if (error is not null) @@ -238,7 +238,7 @@ internal class CommandBinder { var parameterInput = commandInput.Parameters[position]; - var rawValues = new[] {parameterInput.Value}; + var rawValues = new[] { parameterInput.Value }; BindMember(parameterSchema, commandInstance, rawValues); position++; @@ -278,7 +278,7 @@ internal class CommandBinder "Missing required parameter(s):" + Environment.NewLine + remainingRequiredParameterSchemas - .Select(o => o.GetFormattedIdentifier()) + .Select(p => p.GetFormattedIdentifier()) .JoinToString(" ") ); } @@ -316,7 +316,7 @@ internal class CommandBinder else if (environmentVariableInput is not null) { var rawValues = optionSchema.Property.IsScalar() - ? new[] {environmentVariableInput.Value} + ? new[] { environmentVariableInput.Value } : environmentVariableInput.SplitValues(); BindMember(optionSchema, commandInstance, rawValues);