mirror of
https://github.com/Tyrrrz/CliFx.git
synced 2025-10-25 15:19:17 +00:00
Compare commits
12 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6d33c5cdad | ||
|
|
e4c899c6c2 | ||
|
|
35b3ad0d63 | ||
|
|
4e70557b47 | ||
|
|
0a8d58255a | ||
|
|
d3fbc9c643 | ||
|
|
1cbf8776be | ||
|
|
16e33f7b8f | ||
|
|
5c848056c5 | ||
|
|
864efd3179 | ||
|
|
7f206a0c77 | ||
|
|
22c15f8ec6 |
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
4
.github/workflows/main.yml
vendored
4
.github/workflows/main.yml
vendored
@@ -4,7 +4,9 @@ on: [push, pull_request]
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
main:
|
main:
|
||||||
uses: Tyrrrz/.github/.github/workflows/NuGet.yml@master
|
uses: Tyrrrz/.github/.github/workflows/nuget.yml@master
|
||||||
|
with:
|
||||||
|
dotnet-version: 6.0.x
|
||||||
secrets:
|
secrets:
|
||||||
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
||||||
NUGET_TOKEN: ${{ secrets.NUGET_TOKEN }}
|
NUGET_TOKEN: ${{ secrets.NUGET_TOKEN }}
|
||||||
|
|||||||
@@ -1,3 +1,9 @@
|
|||||||
|
### v2.2.4 (22-Apr-2022)
|
||||||
|
|
||||||
|
- Added more contextual information to analyzer diagnostics.
|
||||||
|
- Fixed an issue where the analyzer incorrectly reported an error on converters that didn't directly match the target type but were compatible through known built-in conversions.
|
||||||
|
- Fixed an issue where MSBuild produced a lot of analyzer-related warnings in certain circumstances.
|
||||||
|
|
||||||
### v2.2.3 (17-Apr-2022)
|
### v2.2.3 (17-Apr-2022)
|
||||||
|
|
||||||
- Changed method signature of `IConsole.ReadKey()` to return `ConsoleKeyInfo` instead of `void`. The return type was originally defined as `void` by mistake. This change is source-backwards-compatible but may break on binary level if you were previously calling this method indirectly (i.e. through a library).
|
- Changed method signature of `IConsole.ReadKey()` to return `ConsoleKeyInfo` instead of `void`. The return type was originally defined as `void` by mistake. This change is source-backwards-compatible but may break on binary level if you were previously calling this method indirectly (i.e. through a library).
|
||||||
|
|||||||
@@ -10,8 +10,8 @@
|
|||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Basic.Reference.Assemblies" Version="1.2.4" />
|
<PackageReference Include="Basic.Reference.Assemblies" Version="1.2.4" />
|
||||||
<PackageReference Include="GitHubActionsTestLogger" Version="1.3.0" PrivateAssets="all" />
|
<PackageReference Include="GitHubActionsTestLogger" Version="1.4.1" PrivateAssets="all" />
|
||||||
<PackageReference Include="FluentAssertions" Version="6.5.1" />
|
<PackageReference Include="FluentAssertions" Version="6.6.0" />
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.1.0" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.1.0" />
|
||||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.1.0" />
|
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.1.0" />
|
||||||
<PackageReference Include="xunit" Version="2.4.1" />
|
<PackageReference Include="xunit" Version="2.4.1" />
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ public class OptionMustHaveValidConverterAnalyzerSpecs
|
|||||||
private static DiagnosticAnalyzer Analyzer { get; } = new OptionMustHaveValidConverterAnalyzer();
|
private static DiagnosticAnalyzer Analyzer { get; } = new OptionMustHaveValidConverterAnalyzer();
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Analyzer_reports_an_error_if_the_specified_option_converter_does_not_derive_from_BindingConverter()
|
public void Analyzer_reports_an_error_if_an_option_has_a_converter_that_does_not_derive_from_BindingConverter()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
// language=cs
|
// language=cs
|
||||||
@@ -33,7 +33,7 @@ public class MyCommand : ICommand
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Analyzer_reports_an_error_if_the_specified_option_converter_does_not_derive_from_a_compatible_BindingConverter()
|
public void Analyzer_reports_an_error_if_an_option_has_a_converter_that_does_not_derive_from_a_compatible_BindingConverter()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
// language=cs
|
// language=cs
|
||||||
@@ -57,7 +57,7 @@ public class MyCommand : ICommand
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Analyzer_does_not_report_an_error_if_the_specified_option_converter_derives_from_a_compatible_BindingConverter()
|
public void Analyzer_does_not_report_an_error_if_an_option_has_a_converter_that_derives_from_a_compatible_BindingConverter()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
// language=cs
|
// language=cs
|
||||||
@@ -80,6 +80,54 @@ public class MyCommand : ICommand
|
|||||||
Analyzer.Should().NotProduceDiagnostics(code);
|
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
|
||||||
|
// language=cs
|
||||||
|
const string code = @"
|
||||||
|
public class MyConverter : BindingConverter<int>
|
||||||
|
{
|
||||||
|
public override int Convert(string rawValue) => 42;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Command]
|
||||||
|
public class MyCommand : ICommand
|
||||||
|
{
|
||||||
|
[CommandOption(""foo"", Converter = typeof(MyConverter))]
|
||||||
|
public int? Foo { get; set; }
|
||||||
|
|
||||||
|
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
|
||||||
|
// language=cs
|
||||||
|
const string code = @"
|
||||||
|
public class MyConverter : BindingConverter<string>
|
||||||
|
{
|
||||||
|
public override string Convert(string rawValue) => rawValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Command]
|
||||||
|
public class MyCommand : ICommand
|
||||||
|
{
|
||||||
|
[CommandOption(""foo"", Converter = typeof(MyConverter))]
|
||||||
|
public IReadOnlyList<string> Foo { get; set; }
|
||||||
|
|
||||||
|
public ValueTask ExecuteAsync(IConsole console) => default;
|
||||||
|
}";
|
||||||
|
|
||||||
|
// Act & assert
|
||||||
|
Analyzer.Should().NotProduceDiagnostics(code);
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Analyzer_does_not_report_an_error_if_an_option_does_not_have_a_converter()
|
public void Analyzer_does_not_report_an_error_if_an_option_does_not_have_a_converter()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ public class OptionMustHaveValidValidatorsAnalyzerSpecs
|
|||||||
private static DiagnosticAnalyzer Analyzer { get; } = new OptionMustHaveValidValidatorsAnalyzer();
|
private static DiagnosticAnalyzer Analyzer { get; } = new OptionMustHaveValidValidatorsAnalyzer();
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Analyzer_reports_an_error_if_one_of_the_specified_option_validators_does_not_derive_from_BindingValidator()
|
public void Analyzer_reports_an_error_if_an_option_has_a_validator_that_does_not_derive_from_BindingValidator()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
// language=cs
|
// language=cs
|
||||||
@@ -33,7 +33,7 @@ public class MyCommand : ICommand
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Analyzer_reports_an_error_if_one_of_the_specified_option_validators_does_not_derive_from_a_compatible_BindingValidator()
|
public void Analyzer_reports_an_error_if_an_option_has_a_validator_that_does_not_derive_from_a_compatible_BindingValidator()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
// language=cs
|
// language=cs
|
||||||
@@ -57,7 +57,7 @@ public class MyCommand : ICommand
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Analyzer_does_not_report_an_error_if_each_specified_option_validator_derives_from_a_compatible_BindingValidator()
|
public void Analyzer_does_not_report_an_error_if_an_option_has_validators_that_all_derive_from_compatible_BindingValidators()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
// language=cs
|
// language=cs
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ public class ParameterMustHaveValidConverterAnalyzerSpecs
|
|||||||
private static DiagnosticAnalyzer Analyzer { get; } = new ParameterMustHaveValidConverterAnalyzer();
|
private static DiagnosticAnalyzer Analyzer { get; } = new ParameterMustHaveValidConverterAnalyzer();
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Analyzer_reports_an_error_if_the_specified_parameter_converter_does_not_derive_from_BindingConverter()
|
public void Analyzer_reports_an_error_if_a_parameter_has_a_converter_that_does_not_derive_from_BindingConverter()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
// language=cs
|
// language=cs
|
||||||
@@ -33,7 +33,7 @@ public class MyCommand : ICommand
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Analyzer_reports_an_error_if_the_specified_parameter_converter_does_not_derive_from_a_compatible_BindingConverter()
|
public void Analyzer_reports_an_error_if_a_parameter_has_a_converter_that_does_not_derive_from_a_compatible_BindingConverter()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
// language=cs
|
// language=cs
|
||||||
@@ -58,7 +58,7 @@ public class MyCommand : ICommand
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Analyzer_does_not_report_an_error_if_the_specified_parameter_converter_derives_from_a_compatible_BindingConverter()
|
public void Analyzer_does_not_report_an_error_if_a_parameter_has_a_converter_that_derives_from_a_compatible_BindingConverter()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
// language=cs
|
// language=cs
|
||||||
@@ -81,6 +81,54 @@ public class MyCommand : ICommand
|
|||||||
Analyzer.Should().NotProduceDiagnostics(code);
|
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
|
||||||
|
// language=cs
|
||||||
|
const string code = @"
|
||||||
|
public class MyConverter : BindingConverter<int>
|
||||||
|
{
|
||||||
|
public override int Convert(string rawValue) => 42;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Command]
|
||||||
|
public class MyCommand : ICommand
|
||||||
|
{
|
||||||
|
[CommandOption(""foo"", Converter = typeof(MyConverter))]
|
||||||
|
public int? Foo { get; set; }
|
||||||
|
|
||||||
|
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
|
||||||
|
// language=cs
|
||||||
|
const string code = @"
|
||||||
|
public class MyConverter : BindingConverter<string>
|
||||||
|
{
|
||||||
|
public override string Convert(string rawValue) => rawValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Command]
|
||||||
|
public class MyCommand : ICommand
|
||||||
|
{
|
||||||
|
[CommandParameter(0, Converter = typeof(MyConverter))]
|
||||||
|
public IReadOnlyList<string> Foo { get; set; }
|
||||||
|
|
||||||
|
public ValueTask ExecuteAsync(IConsole console) => default;
|
||||||
|
}";
|
||||||
|
|
||||||
|
// Act & assert
|
||||||
|
Analyzer.Should().NotProduceDiagnostics(code);
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Analyzer_does_not_report_an_error_if_a_parameter_does_not_have_a_converter()
|
public void Analyzer_does_not_report_an_error_if_a_parameter_does_not_have_a_converter()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ public class ParameterMustHaveValidValidatorsAnalyzerSpecs
|
|||||||
private static DiagnosticAnalyzer Analyzer { get; } = new ParameterMustHaveValidValidatorsAnalyzer();
|
private static DiagnosticAnalyzer Analyzer { get; } = new ParameterMustHaveValidValidatorsAnalyzer();
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Analyzer_reports_an_error_if_one_of_the_specified_parameter_validators_does_not_derive_from_BindingValidator()
|
public void Analyzer_reports_an_error_a_parameter_has_a_validator_that_does_not_derive_from_BindingValidator()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
// language=cs
|
// language=cs
|
||||||
@@ -33,7 +33,7 @@ public class MyCommand : ICommand
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Analyzer_reports_an_error_if_one_of_the_specified_parameter_validators_does_not_derive_from_a_compatible_BindingValidator()
|
public void Analyzer_reports_an_error_if_a_parameter_has_a_validator_that_does_not_derive_from_a_compatible_BindingValidator()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
// language=cs
|
// language=cs
|
||||||
@@ -57,7 +57,7 @@ public class MyCommand : ICommand
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Analyzer_does_not_report_an_error_if_each_specified_parameter_validator_derives_from_a_compatible_BindingValidator()
|
public void Analyzer_does_not_report_an_error_if_a_parameter_has_validators_that_all_derive_from_compatible_BindingValidators()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
// language=cs
|
// language=cs
|
||||||
|
|||||||
@@ -8,7 +8,9 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.1.0" PrivateAssets="all" />
|
<!-- Keep this dependency as low as possible since we can't bundle it -->
|
||||||
|
<!-- https://github.com/Tyrrrz/CliFx/issues/127 -->
|
||||||
|
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="3.8.0" PrivateAssets="all" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
@@ -5,8 +5,10 @@ using Microsoft.CodeAnalysis;
|
|||||||
|
|
||||||
namespace CliFx.Analyzers.ObjectModel;
|
namespace CliFx.Analyzers.ObjectModel;
|
||||||
|
|
||||||
internal partial class CommandOptionSymbol
|
internal partial class CommandOptionSymbol : ICommandMemberSymbol
|
||||||
{
|
{
|
||||||
|
public IPropertySymbol Property { get; }
|
||||||
|
|
||||||
public string? Name { get; }
|
public string? Name { get; }
|
||||||
|
|
||||||
public char? ShortName { get; }
|
public char? ShortName { get; }
|
||||||
@@ -16,11 +18,13 @@ internal partial class CommandOptionSymbol
|
|||||||
public IReadOnlyList<ITypeSymbol> ValidatorTypes { get; }
|
public IReadOnlyList<ITypeSymbol> ValidatorTypes { get; }
|
||||||
|
|
||||||
public CommandOptionSymbol(
|
public CommandOptionSymbol(
|
||||||
|
IPropertySymbol property,
|
||||||
string? name,
|
string? name,
|
||||||
char? shortName,
|
char? shortName,
|
||||||
ITypeSymbol? converterType,
|
ITypeSymbol? converterType,
|
||||||
IReadOnlyList<ITypeSymbol> validatorTypes)
|
IReadOnlyList<ITypeSymbol> validatorTypes)
|
||||||
{
|
{
|
||||||
|
Property = property;
|
||||||
Name = name;
|
Name = name;
|
||||||
ShortName = shortName;
|
ShortName = shortName;
|
||||||
ConverterType = converterType;
|
ConverterType = converterType;
|
||||||
@@ -30,22 +34,25 @@ internal partial class CommandOptionSymbol
|
|||||||
|
|
||||||
internal partial class CommandOptionSymbol
|
internal partial class CommandOptionSymbol
|
||||||
{
|
{
|
||||||
private static AttributeData? TryGetOptionAttribute(IPropertySymbol property) =>
|
private static AttributeData? TryGetOptionAttribute(IPropertySymbol property) => property
|
||||||
property
|
|
||||||
.GetAttributes()
|
.GetAttributes()
|
||||||
.FirstOrDefault(a => a.AttributeClass.DisplayNameMatches(SymbolNames.CliFxCommandOptionAttribute));
|
.FirstOrDefault(a => a.AttributeClass?.DisplayNameMatches(SymbolNames.CliFxCommandOptionAttribute) == true);
|
||||||
|
|
||||||
private static CommandOptionSymbol FromAttribute(AttributeData attribute)
|
public static CommandOptionSymbol? TryResolve(IPropertySymbol property)
|
||||||
{
|
{
|
||||||
|
var attribute = TryGetOptionAttribute(property);
|
||||||
|
if (attribute is null)
|
||||||
|
return null;
|
||||||
|
|
||||||
var name = attribute
|
var name = attribute
|
||||||
.ConstructorArguments
|
.ConstructorArguments
|
||||||
.Where(a => a.Type.DisplayNameMatches("string") || a.Type.DisplayNameMatches("System.String"))
|
.Where(a => a.Type?.SpecialType == SpecialType.System_String)
|
||||||
.Select(a => a.Value)
|
.Select(a => a.Value)
|
||||||
.FirstOrDefault() as string;
|
.FirstOrDefault() as string;
|
||||||
|
|
||||||
var shortName = attribute
|
var shortName = attribute
|
||||||
.ConstructorArguments
|
.ConstructorArguments
|
||||||
.Where(a => a.Type.DisplayNameMatches("char") || a.Type.DisplayNameMatches("System.Char"))
|
.Where(a => a.Type?.SpecialType == SpecialType.System_Char)
|
||||||
.Select(a => a.Value)
|
.Select(a => a.Value)
|
||||||
.FirstOrDefault() as char?;
|
.FirstOrDefault() as char?;
|
||||||
|
|
||||||
@@ -64,16 +71,7 @@ internal partial class CommandOptionSymbol
|
|||||||
.Cast<ITypeSymbol>()
|
.Cast<ITypeSymbol>()
|
||||||
.ToArray();
|
.ToArray();
|
||||||
|
|
||||||
return new CommandOptionSymbol(name, shortName, converter, validators);
|
return new CommandOptionSymbol(property, name, shortName, converter, validators);
|
||||||
}
|
|
||||||
|
|
||||||
public static CommandOptionSymbol? TryResolve(IPropertySymbol property)
|
|
||||||
{
|
|
||||||
var attribute = TryGetOptionAttribute(property);
|
|
||||||
|
|
||||||
return attribute is not null
|
|
||||||
? FromAttribute(attribute)
|
|
||||||
: null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool IsOptionProperty(IPropertySymbol property) =>
|
public static bool IsOptionProperty(IPropertySymbol property) =>
|
||||||
|
|||||||
@@ -5,8 +5,10 @@ using Microsoft.CodeAnalysis;
|
|||||||
|
|
||||||
namespace CliFx.Analyzers.ObjectModel;
|
namespace CliFx.Analyzers.ObjectModel;
|
||||||
|
|
||||||
internal partial class CommandParameterSymbol
|
internal partial class CommandParameterSymbol : ICommandMemberSymbol
|
||||||
{
|
{
|
||||||
|
public IPropertySymbol Property { get; }
|
||||||
|
|
||||||
public int Order { get; }
|
public int Order { get; }
|
||||||
|
|
||||||
public string? Name { get; }
|
public string? Name { get; }
|
||||||
@@ -18,12 +20,14 @@ internal partial class CommandParameterSymbol
|
|||||||
public IReadOnlyList<ITypeSymbol> ValidatorTypes { get; }
|
public IReadOnlyList<ITypeSymbol> ValidatorTypes { get; }
|
||||||
|
|
||||||
public CommandParameterSymbol(
|
public CommandParameterSymbol(
|
||||||
|
IPropertySymbol property,
|
||||||
int order,
|
int order,
|
||||||
string? name,
|
string? name,
|
||||||
bool? isRequired,
|
bool? isRequired,
|
||||||
ITypeSymbol? converterType,
|
ITypeSymbol? converterType,
|
||||||
IReadOnlyList<ITypeSymbol> validatorTypes)
|
IReadOnlyList<ITypeSymbol> validatorTypes)
|
||||||
{
|
{
|
||||||
|
Property = property;
|
||||||
Order = order;
|
Order = order;
|
||||||
Name = name;
|
Name = name;
|
||||||
IsRequired = isRequired;
|
IsRequired = isRequired;
|
||||||
@@ -34,13 +38,16 @@ internal partial class CommandParameterSymbol
|
|||||||
|
|
||||||
internal partial class CommandParameterSymbol
|
internal partial class CommandParameterSymbol
|
||||||
{
|
{
|
||||||
private static AttributeData? TryGetParameterAttribute(IPropertySymbol property) =>
|
private static AttributeData? TryGetParameterAttribute(IPropertySymbol property) => property
|
||||||
property
|
|
||||||
.GetAttributes()
|
.GetAttributes()
|
||||||
.FirstOrDefault(a => a.AttributeClass.DisplayNameMatches(SymbolNames.CliFxCommandParameterAttribute));
|
.FirstOrDefault(a => a.AttributeClass?.DisplayNameMatches(SymbolNames.CliFxCommandParameterAttribute) == true);
|
||||||
|
|
||||||
private static CommandParameterSymbol FromAttribute(AttributeData attribute)
|
public static CommandParameterSymbol? TryResolve(IPropertySymbol property)
|
||||||
{
|
{
|
||||||
|
var attribute = TryGetParameterAttribute(property);
|
||||||
|
if (attribute is null)
|
||||||
|
return null;
|
||||||
|
|
||||||
var order = (int)attribute
|
var order = (int)attribute
|
||||||
.ConstructorArguments
|
.ConstructorArguments
|
||||||
.Select(a => a.Value)
|
.Select(a => a.Value)
|
||||||
@@ -73,16 +80,7 @@ internal partial class CommandParameterSymbol
|
|||||||
.Cast<ITypeSymbol>()
|
.Cast<ITypeSymbol>()
|
||||||
.ToArray();
|
.ToArray();
|
||||||
|
|
||||||
return new CommandParameterSymbol(order, name, isRequired, converter, validators);
|
return new CommandParameterSymbol(property, order, name, isRequired, converter, validators);
|
||||||
}
|
|
||||||
|
|
||||||
public static CommandParameterSymbol? TryResolve(IPropertySymbol property)
|
|
||||||
{
|
|
||||||
var attribute = TryGetParameterAttribute(property);
|
|
||||||
|
|
||||||
return attribute is not null
|
|
||||||
? FromAttribute(attribute)
|
|
||||||
: null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool IsParameterProperty(IPropertySymbol property) =>
|
public static bool IsParameterProperty(IPropertySymbol property) =>
|
||||||
|
|||||||
21
CliFx.Analyzers/ObjectModel/ICommandMemberSymbol.cs
Normal file
21
CliFx.Analyzers/ObjectModel/ICommandMemberSymbol.cs
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
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<ITypeSymbol> 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;
|
||||||
|
}
|
||||||
@@ -15,7 +15,8 @@ public class OptionMustHaveUniqueNameAnalyzer : AnalyzerBase
|
|||||||
: base(
|
: base(
|
||||||
"Options must have unique names",
|
"Options must have unique names",
|
||||||
"This option's name must be unique within the command (comparison IS NOT case sensitive). " +
|
"This option's name must be unique within the command (comparison IS NOT case sensitive). " +
|
||||||
"Specified name: '{0}'.")
|
"Specified name: `{0}`. " +
|
||||||
|
"Property bound to another option with the same name: `{1}`.")
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -55,7 +56,8 @@ public class OptionMustHaveUniqueNameAnalyzer : AnalyzerBase
|
|||||||
context.ReportDiagnostic(
|
context.ReportDiagnostic(
|
||||||
CreateDiagnostic(
|
CreateDiagnostic(
|
||||||
propertyDeclaration.Identifier.GetLocation(),
|
propertyDeclaration.Identifier.GetLocation(),
|
||||||
option.Name
|
option.Name,
|
||||||
|
otherProperty.Name
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,7 +14,8 @@ public class OptionMustHaveUniqueShortNameAnalyzer : AnalyzerBase
|
|||||||
: base(
|
: base(
|
||||||
"Options must have unique short names",
|
"Options must have unique short names",
|
||||||
"This option's short name must be unique within the command (comparison IS case sensitive). " +
|
"This option's short name must be unique within the command (comparison IS case sensitive). " +
|
||||||
"Specified short name: '{0}'.")
|
"Specified short name: `{0}` " +
|
||||||
|
"Property bound to another option with the same short name: `{1}`.")
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -54,7 +55,8 @@ public class OptionMustHaveUniqueShortNameAnalyzer : AnalyzerBase
|
|||||||
context.ReportDiagnostic(
|
context.ReportDiagnostic(
|
||||||
CreateDiagnostic(
|
CreateDiagnostic(
|
||||||
propertyDeclaration.Identifier.GetLocation(),
|
propertyDeclaration.Identifier.GetLocation(),
|
||||||
option.ShortName
|
option.ShortName,
|
||||||
|
otherProperty.Name
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,7 +37,16 @@ public class OptionMustHaveValidConverterAnalyzer : AnalyzerBase
|
|||||||
.FirstOrDefault();
|
.FirstOrDefault();
|
||||||
|
|
||||||
// Value returned by the converter must be assignable to the property type
|
// Value returned by the converter must be assignable to the property type
|
||||||
if (converterValueType is null || !property.Type.IsAssignableFrom(converterValueType))
|
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(
|
context.ReportDiagnostic(
|
||||||
CreateDiagnostic(propertyDeclaration.Identifier.GetLocation())
|
CreateDiagnostic(propertyDeclaration.Identifier.GetLocation())
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ public class OptionMustHaveValidNameAnalyzer : AnalyzerBase
|
|||||||
: base(
|
: base(
|
||||||
"Options must have valid names",
|
"Options must have valid names",
|
||||||
"This option's name must be at least 2 characters long and must start with a letter. " +
|
"This option's name must be at least 2 characters long and must start with a letter. " +
|
||||||
"Specified name: '{0}'.")
|
"Specified name: `{0}`.")
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ public class OptionMustHaveValidShortNameAnalyzer : AnalyzerBase
|
|||||||
: base(
|
: base(
|
||||||
"Option short names must be letter characters",
|
"Option short names must be letter characters",
|
||||||
"This option's short name must be a single letter character. " +
|
"This option's short name must be a single letter character. " +
|
||||||
"Specified short name: '{0}'.")
|
"Specified short name: `{0}`.")
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -35,7 +35,11 @@ public class OptionMustHaveValidValidatorsAnalyzer : AnalyzerBase
|
|||||||
.FirstOrDefault();
|
.FirstOrDefault();
|
||||||
|
|
||||||
// Value passed to the validator must be assignable from the property type
|
// Value passed to the validator must be assignable from the property type
|
||||||
if (validatorValueType is null || !validatorValueType.IsAssignableFrom(property.Type))
|
var isCompatible =
|
||||||
|
validatorValueType is not null &&
|
||||||
|
context.Compilation.IsAssignable(property.Type, validatorValueType);
|
||||||
|
|
||||||
|
if (!isCompatible)
|
||||||
{
|
{
|
||||||
context.ReportDiagnostic(
|
context.ReportDiagnostic(
|
||||||
CreateDiagnostic(propertyDeclaration.Identifier.GetLocation())
|
CreateDiagnostic(propertyDeclaration.Identifier.GetLocation())
|
||||||
|
|||||||
@@ -13,7 +13,8 @@ public class ParameterMustBeLastIfNonRequiredAnalyzer : AnalyzerBase
|
|||||||
public ParameterMustBeLastIfNonRequiredAnalyzer()
|
public ParameterMustBeLastIfNonRequiredAnalyzer()
|
||||||
: base(
|
: base(
|
||||||
"Parameters marked as non-required must be the last in order",
|
"Parameters marked as non-required must be the last in order",
|
||||||
"This parameter is non-required so it must be the last in order (its order must be highest within the command).")
|
"This parameter is non-required so it must be the last in order (its order must be highest within the command). " +
|
||||||
|
"Property bound to another non-required parameter: `{0}`.")
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -48,7 +49,10 @@ public class ParameterMustBeLastIfNonRequiredAnalyzer : AnalyzerBase
|
|||||||
if (otherParameter.Order > parameter.Order)
|
if (otherParameter.Order > parameter.Order)
|
||||||
{
|
{
|
||||||
context.ReportDiagnostic(
|
context.ReportDiagnostic(
|
||||||
CreateDiagnostic(propertyDeclaration.Identifier.GetLocation())
|
CreateDiagnostic(
|
||||||
|
propertyDeclaration.Identifier.GetLocation(),
|
||||||
|
otherProperty.Name
|
||||||
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,17 +13,11 @@ public class ParameterMustBeLastIfNonScalarAnalyzer : AnalyzerBase
|
|||||||
public ParameterMustBeLastIfNonScalarAnalyzer()
|
public ParameterMustBeLastIfNonScalarAnalyzer()
|
||||||
: base(
|
: base(
|
||||||
"Parameters of non-scalar types must be the last in order",
|
"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).")
|
"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 static bool IsScalar(ITypeSymbol type) =>
|
|
||||||
type.DisplayNameMatches("string") ||
|
|
||||||
type.DisplayNameMatches("System.String") ||
|
|
||||||
!type.AllInterfaces
|
|
||||||
.Select(i => i.ConstructedFrom)
|
|
||||||
.Any(t => t.DisplayNameMatches("System.Collections.Generic.IEnumerable<T>"));
|
|
||||||
|
|
||||||
private void Analyze(
|
private void Analyze(
|
||||||
SyntaxNodeAnalysisContext context,
|
SyntaxNodeAnalysisContext context,
|
||||||
PropertyDeclarationSyntax propertyDeclaration,
|
PropertyDeclarationSyntax propertyDeclaration,
|
||||||
@@ -32,13 +26,13 @@ public class ParameterMustBeLastIfNonScalarAnalyzer : AnalyzerBase
|
|||||||
if (property.ContainingType is null)
|
if (property.ContainingType is null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (IsScalar(property.Type))
|
|
||||||
return;
|
|
||||||
|
|
||||||
var parameter = CommandParameterSymbol.TryResolve(property);
|
var parameter = CommandParameterSymbol.TryResolve(property);
|
||||||
if (parameter is null)
|
if (parameter is null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
if (parameter.IsScalar())
|
||||||
|
return;
|
||||||
|
|
||||||
var otherProperties = property
|
var otherProperties = property
|
||||||
.ContainingType
|
.ContainingType
|
||||||
.GetMembers()
|
.GetMembers()
|
||||||
@@ -55,7 +49,10 @@ public class ParameterMustBeLastIfNonScalarAnalyzer : AnalyzerBase
|
|||||||
if (otherParameter.Order > parameter.Order)
|
if (otherParameter.Order > parameter.Order)
|
||||||
{
|
{
|
||||||
context.ReportDiagnostic(
|
context.ReportDiagnostic(
|
||||||
CreateDiagnostic(propertyDeclaration.Identifier.GetLocation())
|
CreateDiagnostic(
|
||||||
|
propertyDeclaration.Identifier.GetLocation(),
|
||||||
|
otherProperty.Name
|
||||||
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,7 +13,8 @@ public class ParameterMustBeSingleIfNonRequiredAnalyzer : AnalyzerBase
|
|||||||
public ParameterMustBeSingleIfNonRequiredAnalyzer()
|
public ParameterMustBeSingleIfNonRequiredAnalyzer()
|
||||||
: base(
|
: base(
|
||||||
"Parameters marked as non-required are limited to one per command",
|
"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.")
|
"This parameter is non-required so it must be the only such parameter in the command. " +
|
||||||
|
"Property bound to another non-required parameter: `{0}`.")
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -48,7 +49,10 @@ public class ParameterMustBeSingleIfNonRequiredAnalyzer : AnalyzerBase
|
|||||||
if (otherParameter.IsRequired == false)
|
if (otherParameter.IsRequired == false)
|
||||||
{
|
{
|
||||||
context.ReportDiagnostic(
|
context.ReportDiagnostic(
|
||||||
CreateDiagnostic(propertyDeclaration.Identifier.GetLocation())
|
CreateDiagnostic(
|
||||||
|
propertyDeclaration.Identifier.GetLocation(),
|
||||||
|
otherProperty.Name
|
||||||
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,17 +13,11 @@ public class ParameterMustBeSingleIfNonScalarAnalyzer : AnalyzerBase
|
|||||||
public ParameterMustBeSingleIfNonScalarAnalyzer()
|
public ParameterMustBeSingleIfNonScalarAnalyzer()
|
||||||
: base(
|
: base(
|
||||||
"Parameters of non-scalar types are limited to one per command",
|
"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.")
|
"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 static bool IsScalar(ITypeSymbol type) =>
|
|
||||||
type.DisplayNameMatches("string") ||
|
|
||||||
type.DisplayNameMatches("System.String") ||
|
|
||||||
!type.AllInterfaces
|
|
||||||
.Select(i => i.ConstructedFrom)
|
|
||||||
.Any(t => t.DisplayNameMatches("System.Collections.Generic.IEnumerable<T>"));
|
|
||||||
|
|
||||||
private void Analyze(
|
private void Analyze(
|
||||||
SyntaxNodeAnalysisContext context,
|
SyntaxNodeAnalysisContext context,
|
||||||
PropertyDeclarationSyntax propertyDeclaration,
|
PropertyDeclarationSyntax propertyDeclaration,
|
||||||
@@ -32,10 +26,11 @@ public class ParameterMustBeSingleIfNonScalarAnalyzer : AnalyzerBase
|
|||||||
if (property.ContainingType is null)
|
if (property.ContainingType is null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!CommandParameterSymbol.IsParameterProperty(property))
|
var parameter = CommandParameterSymbol.TryResolve(property);
|
||||||
|
if (parameter is null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (IsScalar(property.Type))
|
if (parameter.IsScalar())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var otherProperties = property
|
var otherProperties = property
|
||||||
@@ -47,13 +42,17 @@ public class ParameterMustBeSingleIfNonScalarAnalyzer : AnalyzerBase
|
|||||||
|
|
||||||
foreach (var otherProperty in otherProperties)
|
foreach (var otherProperty in otherProperties)
|
||||||
{
|
{
|
||||||
if (!CommandParameterSymbol.IsParameterProperty(otherProperty))
|
var otherParameter = CommandParameterSymbol.TryResolve(otherProperty);
|
||||||
|
if (otherParameter is null)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (!IsScalar(otherProperty.Type))
|
if (!otherParameter.IsScalar())
|
||||||
{
|
{
|
||||||
context.ReportDiagnostic(
|
context.ReportDiagnostic(
|
||||||
CreateDiagnostic(propertyDeclaration.Identifier.GetLocation())
|
CreateDiagnostic(
|
||||||
|
propertyDeclaration.Identifier.GetLocation(),
|
||||||
|
otherProperty.Name
|
||||||
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,8 @@ public class ParameterMustHaveUniqueNameAnalyzer : AnalyzerBase
|
|||||||
: base(
|
: base(
|
||||||
"Parameters must have unique names",
|
"Parameters must have unique names",
|
||||||
"This parameter's name must be unique within the command (comparison IS NOT case sensitive). " +
|
"This parameter's name must be unique within the command (comparison IS NOT case sensitive). " +
|
||||||
"Specified name: '{0}'.")
|
"Specified name: `{0}`. " +
|
||||||
|
"Property bound to another parameter with the same name: `{1}`.")
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -55,7 +56,8 @@ public class ParameterMustHaveUniqueNameAnalyzer : AnalyzerBase
|
|||||||
context.ReportDiagnostic(
|
context.ReportDiagnostic(
|
||||||
CreateDiagnostic(
|
CreateDiagnostic(
|
||||||
propertyDeclaration.Identifier.GetLocation(),
|
propertyDeclaration.Identifier.GetLocation(),
|
||||||
parameter.Name
|
parameter.Name,
|
||||||
|
otherProperty.Name
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,7 +14,8 @@ public class ParameterMustHaveUniqueOrderAnalyzer : AnalyzerBase
|
|||||||
: base(
|
: base(
|
||||||
"Parameters must have unique order",
|
"Parameters must have unique order",
|
||||||
"This parameter's order must be unique within the command. " +
|
"This parameter's order must be unique within the command. " +
|
||||||
"Specified order: {0}.")
|
"Specified order: {0}. " +
|
||||||
|
"Property bound to another parameter with the same order: `{1}`.")
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -48,7 +49,8 @@ public class ParameterMustHaveUniqueOrderAnalyzer : AnalyzerBase
|
|||||||
context.ReportDiagnostic(
|
context.ReportDiagnostic(
|
||||||
CreateDiagnostic(
|
CreateDiagnostic(
|
||||||
propertyDeclaration.Identifier.GetLocation(),
|
propertyDeclaration.Identifier.GetLocation(),
|
||||||
parameter.Order
|
parameter.Order,
|
||||||
|
otherProperty.Name
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,7 +37,16 @@ public class ParameterMustHaveValidConverterAnalyzer : AnalyzerBase
|
|||||||
.FirstOrDefault();
|
.FirstOrDefault();
|
||||||
|
|
||||||
// Value returned by the converter must be assignable to the property type
|
// Value returned by the converter must be assignable to the property type
|
||||||
if (converterValueType is null || !property.Type.IsAssignableFrom(converterValueType))
|
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(
|
context.ReportDiagnostic(
|
||||||
CreateDiagnostic(propertyDeclaration.Identifier.GetLocation())
|
CreateDiagnostic(propertyDeclaration.Identifier.GetLocation())
|
||||||
|
|||||||
@@ -35,7 +35,11 @@ public class ParameterMustHaveValidValidatorsAnalyzer : AnalyzerBase
|
|||||||
.FirstOrDefault();
|
.FirstOrDefault();
|
||||||
|
|
||||||
// Value passed to the validator must be assignable from the property type
|
// Value passed to the validator must be assignable from the property type
|
||||||
if (validatorValueType is null || !validatorValueType.IsAssignableFrom(property.Type))
|
var isCompatible =
|
||||||
|
validatorValueType is not null &&
|
||||||
|
context.Compilation.IsAssignable(property.Type, validatorValueType);
|
||||||
|
|
||||||
|
if (!isCompatible)
|
||||||
{
|
{
|
||||||
context.ReportDiagnostic(
|
context.ReportDiagnostic(
|
||||||
CreateDiagnostic(propertyDeclaration.Identifier.GetLocation())
|
CreateDiagnostic(propertyDeclaration.Identifier.GetLocation())
|
||||||
|
|||||||
@@ -29,10 +29,13 @@ internal static class RoslynExtensions
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool IsAssignableFrom(this ITypeSymbol target, ITypeSymbol source) =>
|
public static ITypeSymbol? TryGetEnumerableUnderlyingType(this ITypeSymbol type) => type
|
||||||
SymbolEqualityComparer.Default.Equals(target, source) ||
|
.AllInterfaces
|
||||||
source.GetBaseTypes().Contains(target, SymbolEqualityComparer.Default) ||
|
.FirstOrDefault(i => i.ConstructedFrom.SpecialType == SpecialType.System_Collections_Generic_IEnumerable_T)?
|
||||||
source.AllInterfaces.Contains(target, SymbolEqualityComparer.Default);
|
.TypeArguments[0];
|
||||||
|
|
||||||
|
public static bool IsAssignable(this Compilation compilation, ITypeSymbol source, ITypeSymbol destination) =>
|
||||||
|
compilation.ClassifyConversion(source, destination).Exists;
|
||||||
|
|
||||||
public static void HandleClassDeclaration(
|
public static void HandleClassDeclaration(
|
||||||
this AnalysisContext analysisContext,
|
this AnalysisContext analysisContext,
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\CliFx\CliFx.csproj" />
|
<ProjectReference Include="..\CliFx\CliFx.csproj" />
|
||||||
<ProjectReference Include="..\CliFx.Analyzers\CliFx.Analyzers.csproj" ReferenceOutputAssembly="false" OutputItemType="Analyzer" />
|
<ProjectReference Include="..\CliFx.Analyzers\CliFx.Analyzers.csproj" ReferenceOutputAssembly="false" OutputItemType="analyzer" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
@@ -7,7 +7,7 @@
|
|||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\CliFx\CliFx.csproj" />
|
<ProjectReference Include="..\CliFx\CliFx.csproj" />
|
||||||
<ProjectReference Include="..\CliFx.Analyzers\CliFx.Analyzers.csproj" ReferenceOutputAssembly="false" OutputItemType="Analyzer" />
|
<ProjectReference Include="..\CliFx.Analyzers\CliFx.Analyzers.csproj" ReferenceOutputAssembly="false" OutputItemType="analyzer" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
using System.Reflection;
|
using System;
|
||||||
|
using System.Reflection;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace CliFx.Tests.Dummy;
|
namespace CliFx.Tests.Dummy;
|
||||||
@@ -14,9 +15,17 @@ public static partial class Program
|
|||||||
|
|
||||||
public static partial class Program
|
public static partial class Program
|
||||||
{
|
{
|
||||||
public static async Task Main() =>
|
public static async Task Main()
|
||||||
|
{
|
||||||
|
// Make sure color codes are not produced because we rely on the output in tests
|
||||||
|
Environment.SetEnvironmentVariable(
|
||||||
|
"DOTNET_SYSTEM_CONSOLE_ALLOW_ANSI_COLOR_REDIRECTION",
|
||||||
|
"false"
|
||||||
|
);
|
||||||
|
|
||||||
await new CliApplicationBuilder()
|
await new CliApplicationBuilder()
|
||||||
.AddCommandsFromThisAssembly()
|
.AddCommandsFromThisAssembly()
|
||||||
.Build()
|
.Build()
|
||||||
.RunAsync();
|
.RunAsync();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
@@ -10,9 +10,9 @@
|
|||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Basic.Reference.Assemblies" Version="1.2.4" />
|
<PackageReference Include="Basic.Reference.Assemblies" Version="1.2.4" />
|
||||||
<PackageReference Include="CliWrap" Version="3.4.1" />
|
<PackageReference Include="CliWrap" Version="3.4.3" />
|
||||||
<PackageReference Include="FluentAssertions" Version="6.5.1" />
|
<PackageReference Include="FluentAssertions" Version="6.6.0" />
|
||||||
<PackageReference Include="GitHubActionsTestLogger" Version="1.3.0" PrivateAssets="all" />
|
<PackageReference Include="GitHubActionsTestLogger" Version="1.4.1" PrivateAssets="all" />
|
||||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.1.0" />
|
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.1.0" />
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.1.0" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.1.0" />
|
||||||
<PackageReference Include="xunit" Version="2.4.1" />
|
<PackageReference Include="xunit" Version="2.4.1" />
|
||||||
|
|||||||
@@ -33,10 +33,8 @@
|
|||||||
|
|
||||||
<!-- Pack the analyzer assembly inside the package -->
|
<!-- Pack the analyzer assembly inside the package -->
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="../CliFx.Analyzers/CliFx.Analyzers.csproj" ReferenceOutputAssembly="false" OutputItemType="Analyzer" />
|
<ProjectReference Include="../CliFx.Analyzers/CliFx.Analyzers.csproj" ReferenceOutputAssembly="false" OutputItemType="analyzer" />
|
||||||
<None Include="../CliFx.Analyzers/bin/$(Configuration)/netstandard2.0/CliFx.Analyzers.dll" Pack="true" PackagePath="analyzers/dotnet/cs" Visible="false" />
|
<None Include="../CliFx.Analyzers/bin/$(Configuration)/netstandard2.0/CliFx.Analyzers.dll" Pack="true" PackagePath="analyzers/dotnet/cs" Visible="false" />
|
||||||
<None Include="../CliFx.Analyzers/bin/$(Configuration)/netstandard2.0/Microsoft.CodeAnalysis.dll" Pack="true" PackagePath="analyzers/dotnet/cs" Visible="false" />
|
|
||||||
<None Include="../CliFx.Analyzers/bin/$(Configuration)/netstandard2.0/Microsoft.CodeAnalysis.CSharp.dll" Pack="true" PackagePath="analyzers/dotnet/cs" Visible="false" />
|
|
||||||
<None Include="../CliFx.Analyzers/bin/$(Configuration)/netstandard2.0/System.Buffers.dll" Pack="true" PackagePath="analyzers/dotnet/cs" Visible="false" />
|
<None Include="../CliFx.Analyzers/bin/$(Configuration)/netstandard2.0/System.Buffers.dll" Pack="true" PackagePath="analyzers/dotnet/cs" Visible="false" />
|
||||||
<None Include="../CliFx.Analyzers/bin/$(Configuration)/netstandard2.0/System.Collections.Immutable.dll" Pack="true" PackagePath="analyzers/dotnet/cs" Visible="false" />
|
<None Include="../CliFx.Analyzers/bin/$(Configuration)/netstandard2.0/System.Collections.Immutable.dll" Pack="true" PackagePath="analyzers/dotnet/cs" Visible="false" />
|
||||||
<None Include="../CliFx.Analyzers/bin/$(Configuration)/netstandard2.0/System.Memory.dll" Pack="true" PackagePath="analyzers/dotnet/cs" Visible="false" />
|
<None Include="../CliFx.Analyzers/bin/$(Configuration)/netstandard2.0/System.Memory.dll" Pack="true" PackagePath="analyzers/dotnet/cs" Visible="false" />
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<Project>
|
<Project>
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<Version>2.2.3</Version>
|
<Version>2.2.4</Version>
|
||||||
<Company>Tyrrrz</Company>
|
<Company>Tyrrrz</Company>
|
||||||
<Copyright>Copyright (C) Oleksii Holub</Copyright>
|
<Copyright>Copyright (C) Oleksii Holub</Copyright>
|
||||||
<LangVersion>latest</LangVersion>
|
<LangVersion>latest</LangVersion>
|
||||||
|
|||||||
@@ -2,6 +2,9 @@
|
|||||||
<configuration>
|
<configuration>
|
||||||
<packageSources>
|
<packageSources>
|
||||||
<clear />
|
<clear />
|
||||||
<add key="nuget.org" value="https://api.nuget.org/v3/index.json" />
|
<add key="nuget.org" value="https://api.nuget.org/v3/index.json" protocolVersion="3" />
|
||||||
</packageSources>
|
</packageSources>
|
||||||
|
<config>
|
||||||
|
<add key="defaultPushSource" value="https://api.nuget.org/v3/index.json" />
|
||||||
|
</config>
|
||||||
</configuration>
|
</configuration>
|
||||||
@@ -45,7 +45,7 @@ To learn more about the war and how you can help, [click here](https://tyrrrz.me
|
|||||||
|
|
||||||
## Screenshots
|
## Screenshots
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user