Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,6 @@
<RepositoryType>git</RepositoryType>
</PropertyGroup>

<Import Project="$(MSBuildThisFileDirectory)src\DotNetCampus.CommandLine\Package\build\Package.props" />
<Import Project="$(MSBuildThisFileDirectory)src\DotNetCampus.CommandLine\Package\buildTransitive\Package.props" />

</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -44,18 +44,31 @@ public static IncrementalValuesProvider<InterceptorGeneratingModel> SelectMethod
{
return context.SyntaxProvider.CreateSyntaxProvider((node, ct) =>
{
// 检查 commandLine.As<T>() 方法调用。
// 检查 commandLine.Xxx<T>() 或 commandLine.Xxx((T o) => { }) 方法调用。
if (node is InvocationExpressionSyntax
{
Expression: MemberAccessExpressionSyntax
{
Name: GenericNameSyntax
{
TypeArgumentList.Arguments.Count: 1,
} syntax,
Name: var nameSyntax,
},
} invocationExpressionNode && syntax.Identifier.Text == methodName)
} invocationExpressionNode)
{
// 支持显式泛型参数(GenericNameSyntax)和隐式推断(SimpleNameSyntax/IdentifierNameSyntax)。
var isMatch = nameSyntax switch
{
GenericNameSyntax
{
TypeArgumentList.Arguments.Count: 1,
} genericName => genericName.Identifier.Text == methodName,
not null => nameSyntax.Identifier.Text == methodName,
_ => false,
};

if (!isMatch)
{
return false;
}

// 再检查方法的参数列表是否是指定类型。
var expectedParameterCount = parameterTypeFullNameRegexes.Length;
var argumentList = invocationExpressionNode.ArgumentList.Arguments;
Expand Down Expand Up @@ -97,14 +110,25 @@ public static IncrementalValuesProvider<InterceptorGeneratingModel> SelectMethod
}
}

// 获取 commandLine.As<T>() 中的 T。
var genericTypeNode = ((GenericNameSyntax)((MemberAccessExpressionSyntax)node.Expression).Name).TypeArgumentList.Arguments[0];
var symbol = ModelExtensions.GetSymbolInfo(c.SemanticModel, genericTypeNode, ct).Symbol as INamedTypeSymbol;
// 获取 commandLine.Xxx<T>() 中的 T。
// 支持从语法节点获取(显式泛型参数)或从方法符号获取(隐式推断)。
var nameSyntax = ((MemberAccessExpressionSyntax)node.Expression).Name;
var symbol = nameSyntax switch
{
// commandLine.Xxx<T>() 显式获取类型符号。
GenericNameSyntax genericNameSyntax => ModelExtensions.GetSymbolInfo(c.SemanticModel, genericNameSyntax.TypeArgumentList.Arguments[0], ct)
.Symbol as INamedTypeSymbol,
// commandLine.Xxx((T o) => { }) 隐式获取类型符号。
not null when methodSymbol.TypeArguments.Length == 1 => methodSymbol.TypeArguments[0] as INamedTypeSymbol,
_ => null,
};

var interceptableLocation = c.SemanticModel.GetInterceptableLocation(node, ct);
if (interceptableLocation is null || symbol is null)
{
return null;
}

// 获取 [Command("xxx")] 或 [Verb("xxx")] 特性中的 xxx。
var commandAttribute = symbol.GetAttributes().FirstOrDefault(a => a.AttributeClass!.IsAttributeOf<CommandAttribute>())
#pragma warning disable CS0618 // 类型或成员已过时
Expand Down
2 changes: 1 addition & 1 deletion src/DotNetCampus.CommandLine/CommandRunner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ public Task<CommandRunningResult> RunAsync()
{
throw new CommandNameNotFoundException(
string.IsNullOrEmpty(possibleCommandNames)
? "No command handler found. Please ensure that at least one command handler is registered by AddHandler()."
? "No command handler found. Please ensure that at least one command handler is registered by AddHandler(), especially a default command handler."
: $"No command handler found for command '{possibleCommandNames}'. Please ensure that the command handler is registered by AddHandler().",
possibleCommandNames);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@
<Target Name="_IncludeAllDependencies" BeforeTargets="_GetPackageFiles">
<ItemGroup>
<None Include="$(RepositoryRoot)README.md" Pack="true" PackagePath="\" />
<None Include="Package\build\Package.props" Pack="True" PackagePath="build\$(PackageId).props" />
<None Include="Package\buildTransitive\Package.props" Pack="True" PackagePath="buildTransitive\$(PackageId).props" />
<None Include="$(ArtifactsPath)bin\DotNetCampus.CommandLine.Analyzer\$(Configuration)\**\*.dll" Pack="True" PackagePath="analyzers\dotnet\cs" />
</ItemGroup>
</Target>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,32 @@ public void AddHandler_Mix3(string[] args, string expectedCommand, string expect
Assert.AreEqual(1, exitCode);
}

[TestMethod]
[DataRow(new[] { "foo" }, nameof(FooOptions), "Foo", TestCommandLineStyle.Flexible, DisplayName = "[Flexible] foo")]
[DataRow(new[] { "foo" }, nameof(FooOptions), "Foo", TestCommandLineStyle.DotNet, DisplayName = "[DotNet] foo")]
[DataRow(new[] { "foo" }, nameof(FooOptions), "Foo", TestCommandLineStyle.Gnu, DisplayName = "[Gnu] foo")]
[DataRow(new[] { "foo" }, nameof(FooOptions), "Foo", TestCommandLineStyle.Windows, DisplayName = "[Windows] foo")]
public async Task AddHandler_TypedDelegate(string[] args, string expectedCommand, string expectedValue, TestCommandLineStyle style)
{
// Arrange
string? matched = null;
var commandLine = CommandLine.Parse(args, style.ToParsingOptions());

// Act
var result = await commandLine
.AddHandler(async (FooOptions o) =>
{
await Task.Yield();
matched = o.Value;
})
.RunAsync();
var matchedTypeName = result.HandledBy!.GetType().Name;

// Assert
Assert.AreEqual(expectedCommand, matchedTypeName);
Assert.AreEqual(expectedValue, matched);
}

// ReSharper disable once RedundantAssignment
private int RunWithExitCode<T>(ref T field, T value)
{
Expand Down
Loading