diff --git a/System.CommandLine.sln b/System.CommandLine.sln index 9e5fb55777..c2dd2a7ff0 100644 --- a/System.CommandLine.sln +++ b/System.CommandLine.sln @@ -33,8 +33,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "dotnet-suggest", "src\Syste EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "dotnet-suggest.Tests", "src\System.CommandLine.Suggest.Tests\dotnet-suggest.Tests.csproj", "{E41F0471-B14D-4FA0-9D8B-1E7750695AE9}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.CommandLine.Generator.CommandHandler", "src\System.CommandLine.Generator.CommandHandler\System.CommandLine.Generator.CommandHandler.csproj", "{591EF370-7AD7-4624-8B9D-FD15010CA657}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.CommandLine.ApiCompatibility.Tests", "src\System.CommandLine.ApiCompatibility.Tests\System.CommandLine.ApiCompatibility.Tests.csproj", "{A54EE328-D456-4BAF-A180-84E77E6409AC}" EndProject Global @@ -95,18 +93,6 @@ Global {E41F0471-B14D-4FA0-9D8B-1E7750695AE9}.Release|x64.Build.0 = Release|Any CPU {E41F0471-B14D-4FA0-9D8B-1E7750695AE9}.Release|x86.ActiveCfg = Release|Any CPU {E41F0471-B14D-4FA0-9D8B-1E7750695AE9}.Release|x86.Build.0 = Release|Any CPU - {591EF370-7AD7-4624-8B9D-FD15010CA657}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {591EF370-7AD7-4624-8B9D-FD15010CA657}.Debug|Any CPU.Build.0 = Debug|Any CPU - {591EF370-7AD7-4624-8B9D-FD15010CA657}.Debug|x64.ActiveCfg = Debug|Any CPU - {591EF370-7AD7-4624-8B9D-FD15010CA657}.Debug|x64.Build.0 = Debug|Any CPU - {591EF370-7AD7-4624-8B9D-FD15010CA657}.Debug|x86.ActiveCfg = Debug|Any CPU - {591EF370-7AD7-4624-8B9D-FD15010CA657}.Debug|x86.Build.0 = Debug|Any CPU - {591EF370-7AD7-4624-8B9D-FD15010CA657}.Release|Any CPU.ActiveCfg = Release|Any CPU - {591EF370-7AD7-4624-8B9D-FD15010CA657}.Release|Any CPU.Build.0 = Release|Any CPU - {591EF370-7AD7-4624-8B9D-FD15010CA657}.Release|x64.ActiveCfg = Release|Any CPU - {591EF370-7AD7-4624-8B9D-FD15010CA657}.Release|x64.Build.0 = Release|Any CPU - {591EF370-7AD7-4624-8B9D-FD15010CA657}.Release|x86.ActiveCfg = Release|Any CPU - {591EF370-7AD7-4624-8B9D-FD15010CA657}.Release|x86.Build.0 = Release|Any CPU {A54EE328-D456-4BAF-A180-84E77E6409AC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {A54EE328-D456-4BAF-A180-84E77E6409AC}.Debug|Any CPU.Build.0 = Debug|Any CPU {A54EE328-D456-4BAF-A180-84E77E6409AC}.Debug|x64.ActiveCfg = Debug|Any CPU @@ -128,7 +114,6 @@ Global {F843CCCA-4CC9-422C-A881-3AE6A998B53F} = {E5B1EC71-0FC4-4FAA-9C65-32D5016FBC45} {E23C760E-B826-4B4F-BE76-916D86BAD2DB} = {E5B1EC71-0FC4-4FAA-9C65-32D5016FBC45} {E41F0471-B14D-4FA0-9D8B-1E7750695AE9} = {E5B1EC71-0FC4-4FAA-9C65-32D5016FBC45} - {591EF370-7AD7-4624-8B9D-FD15010CA657} = {E5B1EC71-0FC4-4FAA-9C65-32D5016FBC45} {A54EE328-D456-4BAF-A180-84E77E6409AC} = {E5B1EC71-0FC4-4FAA-9C65-32D5016FBC45} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution diff --git a/src/System.CommandLine.Generator.CommandHandler/CommandExtensions.cs b/src/System.CommandLine.Generator.CommandHandler/CommandExtensions.cs deleted file mode 100644 index f972403650..0000000000 --- a/src/System.CommandLine.Generator.CommandHandler/CommandExtensions.cs +++ /dev/null @@ -1,26 +0,0 @@ -namespace System.CommandLine; - -/// -/// Provides extension methods for . -/// -public static class CommandExtensions -{ - private const string _messageForWhenGeneratorIsNotInUse = - "This overload should not be called. You should reference the System.CommandLine.Generator package which will generate a more specific overload for your delegate."; - - /// - /// Sets a command handler. - /// - /// Currently, this method only works with C# source generators. - /// The command on which to set the handler. - /// A delegate implementing the handler for the command. - /// The symbols used to bind the handler's parameters. - public static void SetHandler( - this Command command, - TDelegate @delegate, - params Symbol[] symbols) - { - throw new InvalidOperationException(_messageForWhenGeneratorIsNotInUse); - } -} - diff --git a/src/System.CommandLine.Generator.CommandHandler/System.CommandLine.Generator.CommandHandler.csproj b/src/System.CommandLine.Generator.CommandHandler/System.CommandLine.Generator.CommandHandler.csproj deleted file mode 100644 index 9171be4f8a..0000000000 --- a/src/System.CommandLine.Generator.CommandHandler/System.CommandLine.Generator.CommandHandler.csproj +++ /dev/null @@ -1,11 +0,0 @@ - - - - netstandard2.0 - - - - - - - diff --git a/src/System.CommandLine.Generator.Tests/Directory.Build.props b/src/System.CommandLine.Generator.Tests/Directory.Build.props deleted file mode 100644 index 431713f682..0000000000 --- a/src/System.CommandLine.Generator.Tests/Directory.Build.props +++ /dev/null @@ -1,10 +0,0 @@ - - - - true - IDE1006 - - - - - diff --git a/src/System.CommandLine.Generator.Tests/GeneratedCommandHandlerTests.cs b/src/System.CommandLine.Generator.Tests/GeneratedCommandHandlerTests.cs deleted file mode 100644 index 20af68db54..0000000000 --- a/src/System.CommandLine.Generator.Tests/GeneratedCommandHandlerTests.cs +++ /dev/null @@ -1,328 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - -using System.Threading.Tasks; -using FluentAssertions; -using Xunit; - -namespace System.CommandLine.Generator.Tests -{ - public class GeneratedCommandHandlerTests - { - [Fact] - public async Task Can_generate_handler_for_void_returning_method() - { - string? boundName = default; - int boundAge = default; - - void Execute(string fullnameOrNickname, int age) - { - boundName = fullnameOrNickname; - boundAge = age; - } - - var nameArgument = new CliArgument("arg"); - var ageOption = new CliOption("--age"); - - var command = new CliCommand("command") - { - nameArgument, - ageOption - }; - - command.SetHandler> - (Execute, nameArgument, ageOption); - - await command.Parse("command Gandalf --age 425").InvokeAsync(); - - boundName.Should().Be("Gandalf"); - boundAge.Should().Be(425); - } - - [Fact] - public async Task Can_generate_handler_for_void_returning_delegate() - { - string? boundName = default; - int boundAge = default; - - var nameArgument = new CliArgument("arg"); - var ageOption = new CliOption("--age"); - - var command = new CliCommand("command") - { - nameArgument, - ageOption - }; - - command.SetHandler> - ((fullnameOrNickname, age) => - { - boundName = fullnameOrNickname; - boundAge = age; - }, nameArgument, ageOption); - - await command.Parse("command Gandalf --age 425").InvokeAsync(); - - boundName.Should().Be("Gandalf"); - boundAge.Should().Be(425); - } - - [Fact] - public async Task Can_generate_handler_for_method_with_model() - { - string? boundName = default; - int boundAge = default; - - void Execute(Character character) - { - boundName = character.FullName; - boundAge = character.Age; - } - - var command = new CliCommand("command"); - var nameOption = new CliOption("--name"); - command.Options.Add(nameOption); - var ageOption = new CliOption("--age"); - command.Options.Add(ageOption); - - command.SetHandler>(Execute, nameOption, ageOption); - - await command.Parse("command --age 425 --name Gandalf").InvokeAsync(); - - boundName.Should().Be("Gandalf"); - boundAge.Should().Be(425); - } - - [Fact] - public async Task Can_generate_handler_for_int_returning_method() - { - int Execute(int first, int second) - { - return first + second; - } - - var command = new CliCommand("add"); - var firstArgument = new CliArgument("first"); - command.Arguments.Add(firstArgument); - var secondArgument = new CliArgument("second"); - command.Arguments.Add(secondArgument); - - command.SetHandler>(Execute, firstArgument, secondArgument); - - int result = await command.Parse("add 1 2").InvokeAsync(); - - result.Should().Be(3); - } - - [Fact] - public async Task Can_generate_handler_with_well_know_parameters_types() - { - ParseResult? boundParseResult = null; - - void Execute( - ParseResult parseResult) - { - boundParseResult = parseResult; - } - - var command = new CliCommand("command"); - - command.SetHandler>(Execute); - - await command.Parse("command").InvokeAsync(); - - boundParseResult.Should().NotBeNull(); - } - - [Fact] - public async Task Can_generate_handler_for_async_method() - { - string? boundName = default; - int boundAge = default; - - async Task ExecuteAsync(string fullnameOrNickname, int age) - { - await Task.Yield(); - boundName = fullnameOrNickname; - boundAge = age; - } - - var nameArgument = new CliArgument("arg"); - var ageOption = new CliOption("--age"); - - var command = new CliCommand("command") - { - nameArgument, - ageOption - }; - - command.SetHandler> - (ExecuteAsync, nameArgument, ageOption); - - await command.Parse("command Gandalf --age 425").InvokeAsync(); - - boundName.Should().Be("Gandalf"); - boundAge.Should().Be(425); - } - - [Fact] - public async Task Can_generate_handler_for_async_task_of_int_returning_method() - { - async Task Execute(int first, int second) - { - await Task.Yield(); - return first + second; - } - - var firstArgument = new CliArgument("first"); - var secondArgument = new CliArgument("second"); - var command = new CliCommand("add") - { - firstArgument, - secondArgument - }; - - command.SetHandler>> - (Execute, firstArgument, secondArgument); - - int result = await command.Parse("add 1 2").InvokeAsync(); - - result.Should().Be(3); - } - - [Fact] - public async Task Can_generate_handler_for_multiple_commands_with_the_same_signature() - { - string firstValue = ""; - - void Execute1(string value) - { - firstValue = value; - } - - string secondValue = ""; - - void Execute2(string value) - { - secondValue = value; - } - - var command1 = new CliCommand("first"); - var argument1 = new CliArgument("first-value"); - command1.Arguments.Add(argument1); - command1.SetHandler>(Execute1, argument1); - - var command2 = new CliCommand("second"); - var argument2 = new CliArgument("second-value"); - command2.Arguments.Add(argument2); - command2.SetHandler>(Execute2, argument2); - - await command1.Parse("first v1").InvokeAsync(); - await command2.Parse("second v2").InvokeAsync(); - - firstValue.Should().Be("v1"); - secondValue.Should().Be("v2"); - } - - [Fact] - public async Task Can_generate_handler_natural_type_delegates() - { - string? boundName = default; - int boundAge = default; - - void Execute(string fullnameOrNickname, int age) - { - boundName = fullnameOrNickname; - boundAge = age; - } - - var nameArgument = new CliArgument("arg"); - var ageOption = new CliOption("--age"); - - var command = new CliCommand("command") - { - nameArgument, - ageOption - }; - - command.SetHandler(Execute, nameArgument, ageOption); - - await command.Parse("command Gandalf --age 425").InvokeAsync(); - - boundName.Should().Be("Gandalf"); - boundAge.Should().Be(425); - } - - [Fact] - public async Task Can_generate_handler_for_lambda() - { - string? boundName = default; - int boundAge = default; - - var nameArgument = new CliArgument("arg"); - var ageOption = new CliOption("--age"); - - var command = new CliCommand("command") - { - nameArgument, - ageOption - }; - - command.SetHandler((string fullnameOrNickname, int age) => - { - boundName = fullnameOrNickname; - boundAge = age; - }, nameArgument, ageOption); - - await command.Parse("command Gandalf --age 425").InvokeAsync(); - - boundName.Should().Be("Gandalf"); - boundAge.Should().Be(425); - } - - [Fact] - public async Task Can_generate_handler_for_lambda_wth_return_type_specified() - { - string? boundName = default; - int boundAge = default; - - var nameArgument = new CliArgument("arg"); - var ageOption = new CliOption("--age"); - - var command = new CliCommand("command") - { - nameArgument, - ageOption - }; - - command.SetHandler(int (string fullnameOrNickname, int age) => - { - boundName = fullnameOrNickname; - boundAge = age; - return 42; - }, nameArgument, ageOption); - - int rv = await command.Parse("command Gandalf --age 425").InvokeAsync(); - - rv.Should().Be(42); - boundName.Should().Be("Gandalf"); - boundAge.Should().Be(425); - } - - public class Character - { - public Character(string? fullName, int age) - { - FullName = fullName; - Age = age; - } - - public Character() - { - } - - public string? FullName { get; set; } - public int Age { get; set; } - } - } -} diff --git a/src/System.CommandLine.Generator.Tests/System.CommandLine.Generator.Tests.csproj b/src/System.CommandLine.Generator.Tests/System.CommandLine.Generator.Tests.csproj deleted file mode 100644 index 2d5bf5eaae..0000000000 --- a/src/System.CommandLine.Generator.Tests/System.CommandLine.Generator.Tests.csproj +++ /dev/null @@ -1,22 +0,0 @@ - - - - $(TargetFrameworkForNETSDK);$(NetFrameworkCurrent) - true - true - true - enable - - - - - - - - - - - - - - diff --git a/src/System.CommandLine.Generator/CommandHandlerSourceGenerator.cs b/src/System.CommandLine.Generator/CommandHandlerSourceGenerator.cs deleted file mode 100644 index 378c107267..0000000000 --- a/src/System.CommandLine.Generator/CommandHandlerSourceGenerator.cs +++ /dev/null @@ -1,194 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - -using System.CommandLine.Generator.Invocations; -using System.Linq; -using System.Text; -using Microsoft.CodeAnalysis; - -namespace System.CommandLine.Generator -{ - [Generator] - public class CommandHandlerSourceGenerator : ISourceGenerator - { - private const string CliActionType = "System.CommandLine.CliAction"; - - public void Execute(GeneratorExecutionContext context) - { - SyntaxReceiver rx = (SyntaxReceiver)context.SyntaxContextReceiver!; - - if (rx.Invocations.Count == 0) - { - return; - } - - StringBuilder builder = new(); - builder.Append( -$@"// Copyright (c) .NET Foundation and contributors. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - -using System.ComponentModel; -using System.CommandLine.Binding; -using System.Reflection; -using System.Threading.Tasks; -using System.CommandLine.Invocation; - -#pragma warning disable - -namespace System.CommandLine -{{ - [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] - [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)] - internal static class GeneratedCommandHandlers - {{ -"); - int handlerCount = 1; - - foreach (var invocation in rx.Invocations) - { - var methodParameters = GetMethodParameters(invocation); - - GenerateSetHandler(builder, invocation, methodParameters, handlerCount, true); - //The non-geric overload is to support C# 10 natural type lambdas - GenerateSetHandler(builder, invocation, methodParameters, handlerCount, false); - - GenerateHandlerClass(builder, invocation, methodParameters, handlerCount); - - //TODO: fully qualify type names - - handlerCount++; - } - - builder.Append(@" - } -} -"); - - context.AddSource("CommandHandlerGeneratorExtensions_Generated.g.cs", builder.ToString()); - } - - private static void GenerateHandlerClass( - StringBuilder builder, - DelegateInvocation invocation, - (string Type, string Name)[] methodParameters, - int handlerCount) - { - builder.Append($@" - private class GeneratedHandler_{handlerCount} : {CliActionType} - {{ - public GeneratedHandler_{handlerCount}( - {invocation.DelegateType.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)} method"); - - if (methodParameters.Length > 0) - { - builder.Append(","); - builder.Append(string.Join($", ", methodParameters.Select(x => $@" - {x.Type} {x.Name}")) + ")"); - } - else - { - builder.Append(")"); - } - - builder.Append($@" - {{ - Method = method;"); - foreach (var propertyAssignment in invocation.Parameters - .Select(x => x.GetPropertyAssignment()) - .Where(x => !string.IsNullOrWhiteSpace(x))) - { - builder.Append($@" - {propertyAssignment}"); - } - - builder.AppendLine($@" - }} - - public {invocation.DelegateType.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)} Method {{ get; }}"); - - foreach (var propertyDeclaration in invocation.Parameters - .Select(x => x.GetPropertyDeclaration()) - .Where(x => !string.IsNullOrWhiteSpace(x))) - { - builder.Append($@" - {propertyDeclaration}"); - } - - builder.Append($@" - public override int Invoke(global::System.CommandLine.ParseResult context) => InvokeAsync(context, global::System.Threading.CancellationToken.None).GetAwaiter().GetResult();"); - - builder.Append($@" - public override async global::System.Threading.Tasks.Task InvokeAsync(global::System.CommandLine.ParseResult context, global::System.Threading.CancellationToken cancellationToken) - {{"); - builder.Append($@" - {invocation.InvokeContents()}"); - builder.Append($@" - }} - }}"); - } - - private static (string Type, string Name)[] GetMethodParameters(DelegateInvocation invocation) - { - return invocation.Parameters - .Select(x => x.GetMethodParameter()) - .Where(x => !string.IsNullOrWhiteSpace(x.Name)) - .ToArray(); - } - - private static void GenerateSetHandler( - StringBuilder builder, - DelegateInvocation invocation, - (string Type, string Name)[] methodParameters, - int handlerCount, - bool isGeneric) - { - builder.Append( - @$" - public static void SetHandler"); - - if (isGeneric) - { - builder.Append($"<{string.Join(", ", Enumerable.Range(1, invocation.NumberOfGenerericParameters).Select(x => $@"T{x}"))}>"); - } - builder.Append(@$"( - this global::System.CommandLine.CliCommand command,"); - - builder.Append($@" - {invocation.DelegateType.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)} method"); - - if (methodParameters.Length > 0) - { - builder.Append(","); - builder.AppendLine(string.Join(", ", methodParameters.Select(x => $@" - {x.Type} {x.Name}")) + ")"); - } - else - { - builder.Append(")"); - } - - builder.Append(@" - {"); - builder.Append($@" - command.Action = new GeneratedHandler_{handlerCount}(method"); - - if (methodParameters.Length > 0) - { - builder.Append(", "); - builder.Append(string.Join(", ", methodParameters.Select(x => x.Name))); - } - - builder.Append(");"); - - builder.AppendLine(@" - }"); - } - - public void Initialize(GeneratorInitializationContext context) - { - //System.Diagnostics.Debugger.Launch(); - - context.RegisterForSyntaxNotifications(() => new SyntaxReceiver()); - } - } -} \ No newline at end of file diff --git a/src/System.CommandLine.Generator/Directory.Build.props b/src/System.CommandLine.Generator/Directory.Build.props deleted file mode 100644 index 47aea44a94..0000000000 --- a/src/System.CommandLine.Generator/Directory.Build.props +++ /dev/null @@ -1,9 +0,0 @@ - - - - true - - - - - diff --git a/src/System.CommandLine.Generator/Invocations/ConstructorModelBindingInvocation.cs b/src/System.CommandLine.Generator/Invocations/ConstructorModelBindingInvocation.cs deleted file mode 100644 index 167b58046c..0000000000 --- a/src/System.CommandLine.Generator/Invocations/ConstructorModelBindingInvocation.cs +++ /dev/null @@ -1,88 +0,0 @@ -using Microsoft.CodeAnalysis; -using System.Linq; -using System.Text; - -namespace System.CommandLine.Generator.Invocations -{ - internal class ConstructorModelBindingInvocation : DelegateInvocation, IEquatable - { - public ConstructorModelBindingInvocation( - IMethodSymbol constructor, - ReturnPattern returnPattern, - ITypeSymbol delegateType) - : base(delegateType, returnPattern, 1) - { - Constructor = constructor; - } - - public IMethodSymbol Constructor { get; } - - public override string InvokeContents() - { - StringBuilder builder = new(); - builder.Append($@" - var model = new {Constructor.ContainingType.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)}("); - builder.Append(string.Join(", ", Parameters.Take(Constructor.Parameters.Length) - .Select(x => x.GetValueFromContext()))); - builder.AppendLine(");"); - - switch (ReturnPattern) - { - case ReturnPattern.FunctionReturnValue: - case ReturnPattern.AwaitFunction: - case ReturnPattern.AwaitFunctionReturnValue: - builder.Append("var rv = "); - break; - } - builder.Append(@" - Method.Invoke(model"); - var remainigParameters = Parameters.Skip(Constructor.Parameters.Length).ToArray(); - if (remainigParameters.Length > 0) - { - builder.Append(", "); - builder.Append(string.Join(", ", remainigParameters.Select(x => x.GetValueFromContext()))); - } - builder.Append(");"); - - switch (ReturnPattern) - { - case ReturnPattern.InvocationContextExitCode: - builder.Append(@" - return 0;"); - break; - case ReturnPattern.FunctionReturnValue: - builder.Append(@" - return rv;"); - break; - case ReturnPattern.AwaitFunction: - builder.Append(@" - await rv;"); - builder.Append(@" - return 0;"); - break; - case ReturnPattern.AwaitFunctionReturnValue: - builder.Append(@" - return await rv;"); - break; - } - - return builder.ToString(); - } - - public override int GetHashCode() - { - return base.GetHashCode() * -1521134295 + - SymbolComparer.GetHashCode(Constructor); - } - - public override bool Equals(object? obj) - => Equals(obj as ConstructorModelBindingInvocation); - - public bool Equals(ConstructorModelBindingInvocation? other) - { - if (other is null) return false; - return base.Equals(other) && - SymbolComparer.Equals(Constructor, other.Constructor); - } - } -} diff --git a/src/System.CommandLine.Generator/Invocations/DelegateInvocation.cs b/src/System.CommandLine.Generator/Invocations/DelegateInvocation.cs deleted file mode 100644 index 075d1b2688..0000000000 --- a/src/System.CommandLine.Generator/Invocations/DelegateInvocation.cs +++ /dev/null @@ -1,113 +0,0 @@ -using Microsoft.CodeAnalysis; -using System.Collections.Generic; -using System.CommandLine.Generator.Parameters; -using System.Diagnostics.CodeAnalysis; -using System.Linq; -using System.Text; - -namespace System.CommandLine.Generator.Invocations -{ - internal class DelegateInvocation : IEquatable - { - protected static SymbolEqualityComparer SymbolComparer { get; } = SymbolEqualityComparer.Default; - - public ITypeSymbol DelegateType { get; } - public ReturnPattern ReturnPattern { get; } - public int NumberOfGenerericParameters { get; } - - public DelegateInvocation( - ITypeSymbol delegateType, - ReturnPattern returnPattern, - int numberOfGenerericParameters) - { - DelegateType = delegateType; - ReturnPattern = returnPattern; - NumberOfGenerericParameters = numberOfGenerericParameters; - } - - public List Parameters { get; } = new(); - - public virtual string InvokeContents() - { - StringBuilder builder = new(); - - switch (ReturnPattern) - { - case ReturnPattern.FunctionReturnValue: - case ReturnPattern.AwaitFunction: - case ReturnPattern.AwaitFunctionReturnValue: - builder.Append(@" - var rv = "); - break; - } - - builder.Append(@" - Method.Invoke("); - builder.Append(string.Join(", ", Parameters.Select(x => x.GetValueFromContext()))); - builder.AppendLine(");"); - - switch (ReturnPattern) - { - case ReturnPattern.InvocationContextExitCode: - builder.Append(@" - return 0;"); - break; - case ReturnPattern.FunctionReturnValue: - builder.Append(@" - return rv;"); - break; - case ReturnPattern.AwaitFunction: - builder.Append(@" - await rv;"); - builder.Append(@" - return 0;"); - break; - case ReturnPattern.AwaitFunctionReturnValue: - builder.Append(@" - return await rv;"); - break; - } - return builder.ToString(); - } - - public override int GetHashCode() - { - int hashCode = SymbolComparer.GetHashCode(DelegateType) * -1521134295 + - HashCode(ReturnPattern) * -1521134295 + - HashCode(NumberOfGenerericParameters) * -1521134295; - - foreach(Parameter parameter in Parameters) - { - hashCode += HashCode(parameter) * -1521134295; - } - - return hashCode; - } - - protected static int HashCode([DisallowNull] T value) - => EqualityComparer.Default.GetHashCode(value); - - public override bool Equals(object? obj) - { - return Equals(obj as DelegateInvocation); - } - - public bool Equals(DelegateInvocation? other) - { - if (other is null) return false; - - bool areEqual = SymbolComparer.Equals(DelegateType, other.DelegateType) && - Equals(ReturnPattern, other.ReturnPattern) && - Equals(NumberOfGenerericParameters, other.NumberOfGenerericParameters) && - Equals(Parameters.Count, other.Parameters.Count); - for(int i = 0; areEqual && i < Parameters.Count; i++) - { - areEqual &= Equals(Parameters[i], other.Parameters[i]); - } - return areEqual; - } - - protected static bool Equals(T first, T second) - => EqualityComparer.Default.Equals(first, second); - } -} diff --git a/src/System.CommandLine.Generator/Invocations/ReturnPattern.cs b/src/System.CommandLine.Generator/Invocations/ReturnPattern.cs deleted file mode 100644 index 514a9193f0..0000000000 --- a/src/System.CommandLine.Generator/Invocations/ReturnPattern.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace System.CommandLine.Generator.Invocations -{ - internal enum ReturnPattern - { - None, - InvocationContextExitCode, - FunctionReturnValue, - AwaitFunction, - AwaitFunctionReturnValue - } -} diff --git a/src/System.CommandLine.Generator/Parameters/ArgumentParameter.cs b/src/System.CommandLine.Generator/Parameters/ArgumentParameter.cs deleted file mode 100644 index b1a2b4823c..0000000000 --- a/src/System.CommandLine.Generator/Parameters/ArgumentParameter.cs +++ /dev/null @@ -1,27 +0,0 @@ -using Microsoft.CodeAnalysis; - -namespace System.CommandLine.Generator.Parameters -{ - internal class ArgumentParameter : PropertyParameter, IEquatable - { - public ArgumentParameter(string localName, INamedTypeSymbol type, ITypeSymbol valueType) - : base(localName, type, valueType) - { - } - - public override string GetValueFromContext() - => $"context.GetValue({LocalName})"; - - public override int GetHashCode() - => base.GetHashCode(); - - public override bool Equals(object? obj) - => Equals(obj as ArgumentParameter); - - public bool Equals(ArgumentParameter? other) - { - if (other is null) return false; - return base.Equals(other); - } - } -} diff --git a/src/System.CommandLine.Generator/Parameters/BindingContextParameter.cs b/src/System.CommandLine.Generator/Parameters/BindingContextParameter.cs deleted file mode 100644 index 2614007971..0000000000 --- a/src/System.CommandLine.Generator/Parameters/BindingContextParameter.cs +++ /dev/null @@ -1,27 +0,0 @@ -using Microsoft.CodeAnalysis; - -namespace System.CommandLine.Generator.Parameters -{ - internal class BindingContextParameter : Parameter, IEquatable - { - public BindingContextParameter(ITypeSymbol bindingContextType) - : base(bindingContextType) - { - } - - public override string GetValueFromContext() - => "context.GetBindingContext()"; - - public override int GetHashCode() - => base.GetHashCode(); - - public override bool Equals(object? obj) - => Equals(obj as BindingContextParameter); - - public bool Equals(BindingContextParameter? other) - { - if (other is null) return false; - return base.Equals(other); - } - } -} diff --git a/src/System.CommandLine.Generator/Parameters/ConsoleParameter.cs b/src/System.CommandLine.Generator/Parameters/ConsoleParameter.cs deleted file mode 100644 index 78ceda3a1f..0000000000 --- a/src/System.CommandLine.Generator/Parameters/ConsoleParameter.cs +++ /dev/null @@ -1,27 +0,0 @@ -using Microsoft.CodeAnalysis; - -namespace System.CommandLine.Generator.Parameters -{ - internal class ConsoleParameter : Parameter, IEquatable - { - public ConsoleParameter(ITypeSymbol consoleType) - : base(consoleType) - { - } - - public override string GetValueFromContext() - => "context.Console"; - - public override int GetHashCode() - => base.GetHashCode(); - - public override bool Equals(object? obj) - => Equals(obj as ConsoleParameter); - - public bool Equals(ConsoleParameter? other) - { - if (other is null) return false; - return base.Equals(other); - } - } -} diff --git a/src/System.CommandLine.Generator/Parameters/OptionParameter.cs b/src/System.CommandLine.Generator/Parameters/OptionParameter.cs deleted file mode 100644 index b0c29c485a..0000000000 --- a/src/System.CommandLine.Generator/Parameters/OptionParameter.cs +++ /dev/null @@ -1,28 +0,0 @@ -using Microsoft.CodeAnalysis; - -namespace System.CommandLine.Generator.Parameters -{ - - internal class OptionParameter : PropertyParameter, IEquatable - { - public OptionParameter(string localName, INamedTypeSymbol type, ITypeSymbol valueType) - : base(localName, type, valueType) - { - } - - public override string GetValueFromContext() - => $"context.GetValue({LocalName})"; - - public override int GetHashCode() - => base.GetHashCode(); - - public override bool Equals(object? obj) - => Equals(obj as OptionParameter); - - public bool Equals(OptionParameter? other) - { - if (other is null) return false; - return base.Equals(other); - } - } -} diff --git a/src/System.CommandLine.Generator/Parameters/Parameter.cs b/src/System.CommandLine.Generator/Parameters/Parameter.cs deleted file mode 100644 index 7f4bf33420..0000000000 --- a/src/System.CommandLine.Generator/Parameters/Parameter.cs +++ /dev/null @@ -1,46 +0,0 @@ -using Microsoft.CodeAnalysis; -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; - -namespace System.CommandLine.Generator.Parameters -{ - internal abstract class Parameter : IEquatable - { - protected static SymbolEqualityComparer SymbolComparer { get; } = SymbolEqualityComparer.Default; - - public ITypeSymbol ValueType { get; } - - protected Parameter(ITypeSymbol valueType) - { - ValueType = valueType; - } - - public abstract string GetValueFromContext(); - - public virtual string GetPropertyDeclaration() => ""; - public virtual string GetPropertyAssignment() => ""; - public virtual (string Type, string Name) GetMethodParameter() => ("", ""); - - public override int GetHashCode() - { - return SymbolComparer.GetHashCode(ValueType); - } - - protected static int HashCode([DisallowNull] T value) - => EqualityComparer.Default.GetHashCode(value); - - public override bool Equals(object? obj) - { - return base.Equals(obj as Parameter); - } - - public bool Equals(Parameter? other) - { - if (other is null) return false; - return SymbolComparer.Equals(ValueType, other.ValueType); - } - - protected static bool Equals(T first, T second) - => EqualityComparer.Default.Equals(first, second); - } -} diff --git a/src/System.CommandLine.Generator/Parameters/ParseResultParameter.cs b/src/System.CommandLine.Generator/Parameters/ParseResultParameter.cs deleted file mode 100644 index 52ed2cb5bb..0000000000 --- a/src/System.CommandLine.Generator/Parameters/ParseResultParameter.cs +++ /dev/null @@ -1,27 +0,0 @@ -using Microsoft.CodeAnalysis; - -namespace System.CommandLine.Generator.Parameters -{ - internal class ParseResultParameter : Parameter, IEquatable - { - public ParseResultParameter(ITypeSymbol parseResultType) - : base(parseResultType) - { - } - - public override string GetValueFromContext() - => "context"; - - public override int GetHashCode() - => base.GetHashCode(); - - public override bool Equals(object? obj) - => Equals(obj as ParseResultParameter); - - public bool Equals(ParseResultParameter? other) - { - if (other is null) return false; - return base.Equals(other); - } - } -} diff --git a/src/System.CommandLine.Generator/Parameters/PropertyParameter.cs b/src/System.CommandLine.Generator/Parameters/PropertyParameter.cs deleted file mode 100644 index 96f53e2a27..0000000000 --- a/src/System.CommandLine.Generator/Parameters/PropertyParameter.cs +++ /dev/null @@ -1,46 +0,0 @@ -using Microsoft.CodeAnalysis; - -namespace System.CommandLine.Generator.Parameters -{ - internal abstract class PropertyParameter : Parameter, IEquatable - { - protected PropertyParameter(string localName, ITypeSymbol type, ITypeSymbol valueType) - : base(valueType) - { - LocalName = localName; - Type = type; - } - - public ITypeSymbol Type { get; } - - public string LocalName { get; } - - public string ParameterName => LocalName.ToLowerInvariant(); - - public override string GetPropertyDeclaration() - => $"private {Type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)} {LocalName} {{ get; }}"; - - public override string GetPropertyAssignment() - => $"{LocalName} = {ParameterName};"; - - public override (string Type, string Name) GetMethodParameter() - => (Type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat), ParameterName); - - public override int GetHashCode() - { - return base.GetHashCode() * -1521134295 + - SymbolComparer.GetHashCode(Type) * -1521134295 + - HashCode(LocalName); - } - - public override bool Equals(object? obj) - => Equals(obj as PropertyParameter); - - public bool Equals(PropertyParameter? other) - { - return base.Equals(other) && - SymbolComparer.Equals(Type, other.Type) && - Equals(LocalName, other.LocalName); - } - } -} diff --git a/src/System.CommandLine.Generator/Parameters/RawParameter.cs b/src/System.CommandLine.Generator/Parameters/RawParameter.cs deleted file mode 100644 index fda789076b..0000000000 --- a/src/System.CommandLine.Generator/Parameters/RawParameter.cs +++ /dev/null @@ -1,27 +0,0 @@ -using Microsoft.CodeAnalysis; - -namespace System.CommandLine.Generator.Parameters -{ - internal class RawParameter : PropertyParameter, IEquatable - { - public RawParameter(string localName, ITypeSymbol valueType) - : base(localName, valueType, valueType) - { - } - - public override string GetValueFromContext() - => LocalName; - - public override int GetHashCode() - => base.GetHashCode(); - - public override bool Equals(object? obj) - => Equals(obj as RawParameter); - - public bool Equals(RawParameter? other) - { - if (other is null) return false; - return base.Equals(other); - } - } -} diff --git a/src/System.CommandLine.Generator/SyntaxReceiver.cs b/src/System.CommandLine.Generator/SyntaxReceiver.cs deleted file mode 100644 index bf025a51a0..0000000000 --- a/src/System.CommandLine.Generator/SyntaxReceiver.cs +++ /dev/null @@ -1,252 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - -using System.Collections.Generic; -using System.CommandLine.Generator.Invocations; -using System.CommandLine.Generator.Parameters; -using System.Linq; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp.Syntax; - -namespace System.CommandLine.Generator -{ - internal class SyntaxReceiver : ISyntaxContextReceiver - { - private static readonly string _nameOfExtensionMethodAnchorType = "global::System.CommandLine.CliCommand"; - - public HashSet Invocations { get; } = new(); - - public void OnVisitSyntaxNode(GeneratorSyntaxContext context) - { - if (context.Node is not InvocationExpressionSyntax { Expression: MemberAccessExpressionSyntax memberAccess } invocationExpression) - { - return; - } - - if (memberAccess.Name.Identifier.Text != "SetHandler") - { - return; - } - - if (context.SemanticModel.GetSymbolInfo(invocationExpression) is not { Symbol: IMethodSymbol invokeMethodSymbol }) - { - return; - } - - if (invokeMethodSymbol.ReceiverType?.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat) != _nameOfExtensionMethodAnchorType) - { - return; - } - - if (invokeMethodSymbol.TypeArguments.Length is not 1) - { - return; - } - - SymbolEqualityComparer symbolEqualityComparer = SymbolEqualityComparer.Default; - WellKnownTypes wellKnownTypes = new(context.SemanticModel.Compilation, symbolEqualityComparer); - - var delegateParameters = Array.Empty(); - - //Check for model binding condition - if (invokeMethodSymbol.TypeArguments[0] is INamedTypeSymbol { TypeArguments: { Length: > 0 } } namedDelegateType) - { - if (namedDelegateType.DelegateInvokeMethod?.ReturnsVoid == false) - { - delegateParameters = namedDelegateType.TypeArguments - .Take(namedDelegateType.TypeArguments.Length - 1) - .Cast() - .ToArray(); - } - else - { - delegateParameters = namedDelegateType.TypeArguments - .Cast() - .ToArray(); - } - } - - var symbols = invocationExpression.ArgumentList - .Arguments - .Skip(1) - .Select(x => context.SemanticModel.GetSymbolInfo(x.Expression).Symbol) - .ToArray(); - - if (symbols.Any(x => x is null)) - { - return; - } - - IReadOnlyList givenParameters = GetParameters(symbols!); - ITypeSymbol delegateType = invokeMethodSymbol.TypeArguments[0]; - ReturnPattern returnPattern = GetReturnPattern(delegateType, context.SemanticModel.Compilation); - - if (IsMatch(delegateParameters, givenParameters, wellKnownTypes)) - { - - var invocation = new DelegateInvocation(delegateType, returnPattern, 1); - foreach (var parameter in PopulateParameters(delegateParameters, givenParameters, wellKnownTypes)) - { - invocation.Parameters.Add(parameter); - } - - Invocations.Add(invocation); - } - else if (delegateParameters[0] is INamedTypeSymbol modelType) - { - foreach (var ctor in modelType.Constructors.OrderByDescending(x => x.Parameters.Length)) - { - var targetTypes = - ctor.Parameters.Select(x => x.Type) - .Concat(delegateParameters.Skip(1)) - .ToArray(); - if (IsMatch(targetTypes, givenParameters, wellKnownTypes)) - { - var invocation = new ConstructorModelBindingInvocation(ctor, returnPattern, delegateType); - foreach (var parameter in PopulateParameters(targetTypes, givenParameters, wellKnownTypes)) - { - invocation.Parameters.Add(parameter); - } - - Invocations.Add(invocation); - break; - } - } - } - - static bool IsMatch( - IReadOnlyList targetSymbols, - IReadOnlyList providedSymbols, - WellKnownTypes knownTypes) - { - SymbolEqualityComparer symbolEqualityComparer = SymbolEqualityComparer.Default; - int j = 0; - for (int i = 0; i < targetSymbols.Count; i++) - { - if (j < providedSymbols.Count && - symbolEqualityComparer.Equals(providedSymbols[j].ValueType, targetSymbols[i])) - { - j++; - //TODO: Handle the case where there are more provided symbols than needed - } - else if (!knownTypes.Contains(targetSymbols[i])) - { - return false; - } - } - - return j == providedSymbols.Count; - } - } - - private static IReadOnlyList PopulateParameters( - IReadOnlyList symbols, - IReadOnlyList givenParameters, - WellKnownTypes knownTypes) - { - List parameters = new(givenParameters); - for (int i = 0; i < symbols.Count; i++) - { - if (knownTypes.TryGet(symbols[i], out Parameter? parameter)) - { - parameters.Insert(i, parameter!); - } - } - - return parameters; - } - - private static IReadOnlyList GetParameters(IEnumerable symbols) - { - List parameters = new(); - int parameterIndex = 1; - foreach (ISymbol symbol in symbols) - { - parameters.Add(GetParameter(symbol, $"Param{parameterIndex}")); - parameterIndex++; - } - - return parameters; - } - - private static Parameter GetParameter(ISymbol argumentSymbol, string localName) - { - return argumentSymbol switch - { - ILocalSymbol local => FromTypeSymbol(local.Type), - INamedTypeSymbol namedType => FromNamedTypeSymbol(namedType), - IMethodSymbol methodSymbol => FromTypeSymbol(methodSymbol.ReturnType), - _ => throw new NotImplementedException($"Cannot convert from '{argumentSymbol?.Kind}' {argumentSymbol?.ToDisplayString()}") - }; - - Parameter FromNamedTypeSymbol(INamedTypeSymbol namedTypeSymbol) - { - if (namedTypeSymbol.TypeArguments.Length > 0) - { - if (namedTypeSymbol.Name == "CliOption") - { - return new OptionParameter(localName, namedTypeSymbol, namedTypeSymbol.TypeArguments[0]); - } - - if (namedTypeSymbol.Name == "CliArgument") - { - return new ArgumentParameter(localName, namedTypeSymbol, namedTypeSymbol.TypeArguments[0]); - } - } - - return new RawParameter(localName, namedTypeSymbol); - } - - Parameter FromTypeSymbol(ITypeSymbol typeSymbol) - { - return typeSymbol switch - { - INamedTypeSymbol namedType => FromNamedTypeSymbol(namedType), - _ => throw new NotImplementedException($"Cannot convert from type symbol '{typeSymbol?.Kind}' {typeSymbol?.ToDisplayString()}") - }; - } - } - - private static ReturnPattern GetReturnPattern(ITypeSymbol delegateType, Compilation compilation) - { - ITypeSymbol? returnType = null; - if (delegateType is INamedTypeSymbol { DelegateInvokeMethod: { ReturnsVoid: false } delegateInvokeMethod }) - { - returnType = delegateInvokeMethod.ReturnType; - } - - if (returnType is null) - { - return ReturnPattern.InvocationContextExitCode; - } - - SymbolEqualityComparer symbolEqualityComparer = SymbolEqualityComparer.Default; - - INamedTypeSymbol intType = compilation.GetSpecialType(SpecialType.System_Int32); - if (symbolEqualityComparer.Equals(returnType, intType)) - { - return ReturnPattern.FunctionReturnValue; - } - - //TODO: what about other awaitables? - INamedTypeSymbol taskType = compilation.GetTypeByMetadataName("System.Threading.Tasks.Task") - ?? throw new InvalidOperationException("Failed to find Task"); - if (symbolEqualityComparer.Equals(returnType, taskType)) - { - return ReturnPattern.AwaitFunction; - } - - INamedTypeSymbol taskOfTType = compilation.GetTypeByMetadataName("System.Threading.Tasks.Task`1") - ?? throw new InvalidOperationException("Failed to find Task"); - - if (returnType is INamedTypeSymbol { TypeArguments: { Length: 1 } } namedReturnType && symbolEqualityComparer.Equals(namedReturnType.TypeArguments[0], intType) && - symbolEqualityComparer.Equals(namedReturnType.ConstructUnboundGenericType(), taskOfTType.ConstructUnboundGenericType())) - { - return ReturnPattern.AwaitFunctionReturnValue; - } - - //TODO: Should this be an error? - return ReturnPattern.InvocationContextExitCode; - } - } -} \ No newline at end of file diff --git a/src/System.CommandLine.Generator/System.CommandLine.Generator.csproj b/src/System.CommandLine.Generator/System.CommandLine.Generator.csproj deleted file mode 100644 index ba581bf1b5..0000000000 --- a/src/System.CommandLine.Generator/System.CommandLine.Generator.csproj +++ /dev/null @@ -1,26 +0,0 @@ - - - - netstandard2.0 - enable - true - false - $(NoWarn);NU5128 - - - - - - - - - - - - - - - - - - diff --git a/src/System.CommandLine.Generator/WellKnownTypes.cs b/src/System.CommandLine.Generator/WellKnownTypes.cs deleted file mode 100644 index fe70110842..0000000000 --- a/src/System.CommandLine.Generator/WellKnownTypes.cs +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - -using System.Collections.Generic; -using System.CommandLine.Generator.Parameters; -using Microsoft.CodeAnalysis; - -namespace System.CommandLine.Generator -{ - internal class WellKnownTypes - { - public INamedTypeSymbol ParseResult { get; } - public IEqualityComparer Comparer { get; } - - public WellKnownTypes(Compilation compilation, IEqualityComparer comparer) - { - ParseResult = GetType("System.CommandLine.ParseResult"); - - INamedTypeSymbol GetType(string typeName) - => compilation.GetTypeByMetadataName(typeName) - ?? throw new InvalidOperationException($"Could not find well known type '{typeName}'"); - - Comparer = comparer; - } - - internal bool Contains(ISymbol symbol) => TryGet(symbol, out _); - - internal bool TryGet(ISymbol symbol, out Parameter? parameter) - { - if (Comparer.Equals(ParseResult, symbol)) - { - parameter = new ParseResultParameter(ParseResult); - return true; - } - - if (symbol.MetadataName == "System.CommandLine.Binding.BindingContext" && symbol is INamedTypeSymbol bindingContext) - { - parameter = new BindingContextParameter(bindingContext); - return true; - } - - parameter = null; - return false; - } - } -} \ No newline at end of file