From 3d4d1a2f4c5d6b6503254e1be799bb0499f038bb Mon Sep 17 00:00:00 2001 From: Nick Strupat Date: Tue, 28 Apr 2026 21:06:30 -0400 Subject: [PATCH] Allow nameof() expressions in Add() command name arguments --- src/ConsoleAppFramework/Parser.cs | 28 ++++++++++++++++--- .../DiagnosticsTest.cs | 13 +++++++-- 2 files changed, 34 insertions(+), 7 deletions(-) diff --git a/src/ConsoleAppFramework/Parser.cs b/src/ConsoleAppFramework/Parser.cs index 22e7324..644dc9f 100644 --- a/src/ConsoleAppFramework/Parser.cs +++ b/src/ConsoleAppFramework/Parser.cs @@ -35,13 +35,13 @@ internal class Parser(ConsoleAppFrameworkGeneratorOptions generatorOptions, Diag { var commandName = args[0]; - if (!commandName.Expression.IsKind(SyntaxKind.StringLiteralExpression)) + if (!IsStringLiteralOrNameOf(commandName.Expression)) { context.ReportDiagnostic(DiagnosticDescriptors.AddCommandMustBeStringLiteral, commandName.GetLocation()); return null; } - var name = (commandName.Expression as LiteralExpressionSyntax)!.Token.ValueText; + var name = GetStringValue(commandName.Expression); var command = ExpressionToCommand(args[1].Expression, name); if (command != null) { @@ -66,13 +66,13 @@ internal class Parser(ConsoleAppFrameworkGeneratorOptions generatorOptions, Diag if (node2.ArgumentList.Arguments.Count == 1) { var commandName = args[0]; - if (!commandName.Expression.IsKind(SyntaxKind.StringLiteralExpression)) + if (!IsStringLiteralOrNameOf(commandName.Expression)) { context.ReportDiagnostic(DiagnosticDescriptors.AddCommandMustBeStringLiteral, commandName.GetLocation()); return []; } - commandPath = (commandName.Expression as LiteralExpressionSyntax)!.Token.ValueText; + commandPath = GetStringValue(commandName.Expression); } // T @@ -874,4 +874,24 @@ void ParseParameterDescription(string originalDescription, out string[] aliases, description = originalDescription; } } + + static bool IsStringLiteralOrNameOf(ExpressionSyntax expression) => + expression.IsKind(SyntaxKind.StringLiteralExpression) || + expression is InvocationExpressionSyntax { Expression: IdentifierNameSyntax { Identifier.Text: "nameof" } }; + + static string GetStringValue(ExpressionSyntax expression) => + expression switch + { + LiteralExpressionSyntax literal => literal.Token.ValueText, + InvocationExpressionSyntax invocation => GetLastIdentifier(invocation.ArgumentList.Arguments[0].Expression), + _ => throw new InvalidOperationException() + }; + + static string GetLastIdentifier(ExpressionSyntax expression) => + expression switch + { + MemberAccessExpressionSyntax memberAccess => memberAccess.Name.Identifier.Text, + AliasQualifiedNameSyntax aliasQualified => aliasQualified.Name.Identifier.Text, + _ => expression.ToString() + }; } diff --git a/tests/ConsoleAppFramework.GeneratorTests/DiagnosticsTest.cs b/tests/ConsoleAppFramework.GeneratorTests/DiagnosticsTest.cs index ae9d5d7..eec2dfc 100644 --- a/tests/ConsoleAppFramework.GeneratorTests/DiagnosticsTest.cs +++ b/tests/ConsoleAppFramework.GeneratorTests/DiagnosticsTest.cs @@ -89,15 +89,22 @@ public async Task FunctionPointerValidation() public async Task BuilderAddConstCommandName() { await verifier.Verify(6, """ -var builder = ConsoleApp.Create(); +var builder = ConsoleApp.Create(); var baz = "foo"; builder.Add(baz, (int x, int y) => { } ); """, "baz"); await verifier.Ok(""" -var builder = ConsoleApp.Create(); +var builder = ConsoleApp.Create(); builder.Add("foo", (int x, int y) => { } ); builder.Run(args); +"""); + + await verifier.Ok(""" +var builder = ConsoleApp.Create(); +var baz = string.Empty; +builder.Add(nameof(baz), (int x, int y) => { } ); +builder.Run(args); """); } @@ -105,7 +112,7 @@ await verifier.Ok(""" public async Task DuplicateCommandName() { await verifier.Verify(7, """ -var builder = ConsoleApp.Create(); +var builder = ConsoleApp.Create(); builder.Add("foo", (int x, int y) => { } ); builder.Add("foo", (int x, int y) => { } ); """, "\"foo\"");