Skip to content

Commit cd7c130

Browse files
committed
more
1 parent 7d7a84f commit cd7c130

File tree

7 files changed

+328
-38
lines changed

7 files changed

+328
-38
lines changed
Lines changed: 28 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,39 @@
1-
using System.ComponentModel.DataAnnotations;
2-
using System.Diagnostics.CodeAnalysis;
3-
using ConsoleAppFramework;
4-
5-
6-
7-
args = ["show", "--aaa", "a", "value", "10.2"];
1+
using ConsoleAppFramework;
2+
using Foo.Bar;
3+
using System.ComponentModel.DataAnnotations;
84

95
var app = ConsoleApp.Create();
106
app.Add<Test>();
7+
app.Add("", (int x, int y) => // different
8+
{
9+
Console.WriteLine("body"); // body
10+
});
11+
app.Add("foo", () => { }); // newline
12+
app.UseFilter<MyFilter>();
1113
app.Run(args);
1214

13-
14-
15-
15+
Console.WriteLine(""); // unrelated line
1616

1717
public class Test
1818
{
1919
public void Show(string aaa, [Range(0, 1)] double value) => ConsoleApp.Log($"{value}");
2020
}
2121

22+
namespace Foo.Bar
23+
{
24+
public class MyFilter(ConsoleAppFilter next) : ConsoleAppFilter(next)
25+
{
26+
public override Task InvokeAsync(ConsoleAppContext context, CancellationToken cancellationToken)
27+
{
28+
throw new NotImplementedException();
29+
}
30+
}
31+
32+
public class MyFilter2(ConsoleAppFilter next) : ConsoleAppFilter(next)
33+
{
34+
public override Task InvokeAsync(ConsoleAppContext context, CancellationToken cancellationToken)
35+
{
36+
throw new NotImplementedException();
37+
}
38+
}
39+
}

src/ConsoleAppFramework/Command.cs

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ public record class Command
2424
{
2525
public required bool IsAsync { get; init; } // Task or Task<int>
2626
public required bool IsVoid { get; init; } // void or int
27-
27+
2828
public bool IsRootCommand => Name == "";
2929
public required string Name { get; init; }
3030

@@ -150,6 +150,7 @@ public record class CommandParameter
150150
{
151151
public required ITypeSymbol Type { get; init; }
152152
public required Location Location { get; init; }
153+
public required WellKnownTypes WellKnownTypes { get; init; }
153154
public required bool IsNullableReference { get; init; }
154155
public required bool IsParams { get; init; }
155156
public required string Name { get; init; }
@@ -170,7 +171,7 @@ public record class CommandParameter
170171
public bool RequireCheckArgumentParsed => !(HasDefaultValue || IsParams || IsFlag);
171172

172173
// increment = false when passed from [Argument]
173-
public string BuildParseMethod(int argCount, string argumentName, WellKnownTypes wellKnownTypes, bool increment)
174+
public string BuildParseMethod(int argCount, string argumentName, bool increment)
174175
{
175176
var incrementIndex = increment ? "!TryIncrementIndex(ref i, args.Length) || " : "";
176177
return Core(Type, false);
@@ -207,7 +208,7 @@ string Core(ITypeSymbol type, bool nullable)
207208
{
208209
return $"arg{argCount} = args[i];";
209210
}
210-
211+
211212
case SpecialType.System_Boolean:
212213
return $"arg{argCount} = true;"; // bool is true flag
213214
case SpecialType.System_Char:
@@ -242,7 +243,7 @@ string Core(ITypeSymbol type, bool nullable)
242243
if (type.TypeKind == TypeKind.Array)
243244
{
244245
var elementType = (type as IArrayTypeSymbol)!.ElementType;
245-
var parsable = wellKnownTypes.ISpanParsable;
246+
var parsable = WellKnownTypes.ISpanParsable;
246247
if (parsable != null) // has parsable
247248
{
248249
if (elementType.AllInterfaces.Any(x => x.EqualsUnconstructedGenericType(parsable)))
@@ -254,12 +255,12 @@ string Core(ITypeSymbol type, bool nullable)
254255
}
255256

256257
// System.DateTimeOffset, System.Guid, System.Version
257-
tryParseKnownPrimitive = wellKnownTypes.HasTryParse(type);
258+
tryParseKnownPrimitive = WellKnownTypes.HasTryParse(type);
258259

259260
if (!tryParseKnownPrimitive)
260261
{
261262
// ISpanParsable<T> (BigInteger, Complex, Half, Int128, etc...)
262-
var parsable = wellKnownTypes.ISpanParsable;
263+
var parsable = WellKnownTypes.ISpanParsable;
263264
if (parsable != null) // has parsable
264265
{
265266
tryParseIParsable = type.AllInterfaces.Any(x => x.EqualsUnconstructedGenericType(parsable));

src/ConsoleAppFramework/ConsoleAppGenerator.cs

Lines changed: 73 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ public void Initialize(IncrementalGeneratorInitializationContext context)
3838

3939
return false;
4040
}, (context, ct) => new RunContext((InvocationExpressionSyntax)context.Node, context.SemanticModel))
41-
.WithTrackingName("ConsoleApp.Run.CreateSyntaxProvider");
41+
.WithTrackingName("ConsoleApp.Run.CreateSyntaxProvider"); // annotate for IncrementalGeneratorTest
4242

4343
context.RegisterSourceOutput(runSource, EmitConsoleAppRun);
4444

@@ -584,7 +584,7 @@ static void EmitConsoleAppRun(SourceProductionContext sourceProductionContext, R
584584
sb.AppendLine(GeneratedCodeHeader);
585585
using (sb.BeginBlock("internal static partial class ConsoleApp"))
586586
{
587-
var emitter = new Emitter(wellKnownTypes);
587+
var emitter = new Emitter();
588588
var withId = new Emitter.CommandWithId(null, command, -1);
589589
emitter.EmitRun(sb, withId, isRunAsync);
590590
}
@@ -594,7 +594,7 @@ static void EmitConsoleAppRun(SourceProductionContext sourceProductionContext, R
594594
help.AppendLine(GeneratedCodeHeader);
595595
using (help.BeginBlock("internal static partial class ConsoleApp"))
596596
{
597-
var emitter = new Emitter(wellKnownTypes);
597+
var emitter = new Emitter();
598598
emitter.EmitHelp(help, command);
599599
}
600600
sourceProductionContext.AddSource("ConsoleApp.Run.Help.g.cs", help.ToString());
@@ -604,9 +604,9 @@ static void EmitConsoleAppBuilder(SourceProductionContext sourceProductionContex
604604
{
605605
if (generatorSyntaxContexts.Length == 0) return;
606606

607-
var model = generatorSyntaxContexts[0].Model;
607+
// var model = generatorSyntaxContexts[0].Model;
608608

609-
var wellKnownTypes = new WellKnownTypes(model.Compilation);
609+
// var wellKnownTypes = new WellKnownTypes(model.Compilation);
610610

611611
// validation, invoke in loop is not allowed.
612612
foreach (var item in generatorSyntaxContexts)
@@ -638,7 +638,7 @@ static void EmitConsoleAppBuilder(SourceProductionContext sourceProductionContex
638638
{
639639
var genericName = (x.Node.Expression as MemberAccessExpressionSyntax)?.Name as GenericNameSyntax;
640640
var genericType = genericName!.TypeArgumentList.Arguments[0];
641-
var type = model.GetTypeInfo(genericType).Type;
641+
var type = x.Model.GetTypeInfo(genericType).Type;
642642
if (type == null) return null!;
643643

644644
var filter = FilterInfo.Create(type);
@@ -663,6 +663,7 @@ static void EmitConsoleAppBuilder(SourceProductionContext sourceProductionContex
663663
var commands1 = methodGroup["Add"]
664664
.Select(x =>
665665
{
666+
var wellKnownTypes = new WellKnownTypes(x.Model.Compilation);
666667
var parser = new Parser(sourceProductionContext, x.Node, x.Model, wellKnownTypes, DelegateBuildType.OnlyActionFunc, globalFilters);
667668
var command = parser.ParseAndValidateForBuilderDelegateRegistration();
668669

@@ -680,6 +681,7 @@ static void EmitConsoleAppBuilder(SourceProductionContext sourceProductionContex
680681
var commands2 = methodGroup["Add<T>"]
681682
.SelectMany(x =>
682683
{
684+
var wellKnownTypes = new WellKnownTypes(x.Model.Compilation);
683685
var parser = new Parser(sourceProductionContext, x.Node, x.Model, wellKnownTypes, DelegateBuildType.None, globalFilters);
684686
var commands = parser.ParseAndValidateForBuilderClassRegistration();
685687

@@ -728,7 +730,7 @@ static void EmitConsoleAppBuilder(SourceProductionContext sourceProductionContex
728730

729731
using (sb.BeginBlock("internal static partial class ConsoleApp"))
730732
{
731-
var emitter = new Emitter(wellKnownTypes);
733+
var emitter = new Emitter();
732734
emitter.EmitBuilder(sb, commandIds, hasRun, hasRunAsync);
733735
}
734736
sourceProductionContext.AddSource("ConsoleApp.Builder.g.cs", sb.ToString());
@@ -740,7 +742,7 @@ static void EmitConsoleAppBuilder(SourceProductionContext sourceProductionContex
740742
using (help.BeginBlock("internal static partial class ConsoleApp"))
741743
using (help.BeginBlock("internal partial struct ConsoleAppBuilder"))
742744
{
743-
var emitter = new Emitter(wellKnownTypes);
745+
var emitter = new Emitter();
744746
emitter.EmitHelp(help, commandIds!);
745747
}
746748
sourceProductionContext.AddSource("ConsoleApp.Builder.Help.g.cs", help.ToString());
@@ -755,6 +757,18 @@ public bool Equals(RunContext other)
755757
{
756758
if (!SyntaxNodeTextEqualityComparer.Default.Equals(node.Expression, other.Node.Expression)) return false;
757759

760+
return DelegateEquals(node, model, (other.Node, other.SemanticModel));
761+
}
762+
763+
public override int GetHashCode()
764+
{
765+
// maybe this does not called so don't care impl.
766+
return SyntaxNodeTextEqualityComparer.Default.GetHashCode(node);
767+
}
768+
769+
// use for both Run and Builder.Add
770+
public static bool DelegateEquals(InvocationExpressionSyntax node, SemanticModel model, (InvocationExpressionSyntax Node, SemanticModel SemanticModel) other)
771+
{
758772
var args1 = node.ArgumentList.Arguments;
759773
var args2 = other.Node.ArgumentList.Arguments;
760774

@@ -836,11 +850,6 @@ public bool Equals(RunContext other)
836850

837851
return false;
838852
}
839-
840-
public override int GetHashCode()
841-
{
842-
return SyntaxNodeTextEqualityComparer.Default.GetHashCode(node);
843-
}
844853
}
845854

846855
readonly struct BuilderContext(InvocationExpressionSyntax node, string name, SemanticModel model) : IEquatable<BuilderContext>
@@ -851,15 +860,61 @@ readonly struct BuilderContext(InvocationExpressionSyntax node, string name, Sem
851860

852861
public bool Equals(BuilderContext other)
853862
{
854-
// TODO:
855-
return node == other.Node;
863+
if (this.Name != other.Name) return false;
864+
865+
var typeInfo = Model.GetTypeInfo((Node.Expression as MemberAccessExpressionSyntax)!.Expression);
866+
if (typeInfo.Type?.Name != "ConsoleAppBuilder")
867+
{
868+
return false;
869+
}
870+
871+
switch (Name)
872+
{
873+
case "Add": // Add or Add<T>
874+
if ((Node.Expression as MemberAccessExpressionSyntax)?.Name.IsKind(SyntaxKind.GenericName) ?? false)
875+
{
876+
return EqualsAddClass(other);
877+
}
878+
else
879+
{
880+
return RunContext.DelegateEquals(node, model, (other.Node, other.Model));
881+
}
882+
case "UseFilter":
883+
return EqualsUseFilter(other);
884+
case "Run":
885+
case "RunAsync":
886+
return true; // only check name
887+
default:
888+
break;
889+
}
890+
891+
return false;
892+
}
893+
894+
bool EqualsAddClass(BuilderContext other)
895+
{
896+
return true; // TODO:final
897+
}
898+
899+
bool EqualsUseFilter(BuilderContext other)
900+
{
901+
var l = GetType(Node, model);
902+
var r = GetType(other.Node, other.Model);
903+
904+
return l.EqualsNamespaceAndName(r);
905+
906+
static ITypeSymbol? GetType(InvocationExpressionSyntax expression, SemanticModel model)
907+
{
908+
var genericName = (expression.Expression as MemberAccessExpressionSyntax)?.Name as GenericNameSyntax;
909+
var genericType = genericName!.TypeArgumentList.Arguments[0];
910+
return model.GetTypeInfo(genericType).Type;
911+
}
856912
}
857913

858914
public override int GetHashCode()
859915
{
860-
// TODO:
861-
return base.GetHashCode();
916+
// maybe this does not called so don't care impl.
917+
return SyntaxNodeTextEqualityComparer.Default.GetHashCode(node);
862918
}
863919
}
864-
865920
}

src/ConsoleAppFramework/Emitter.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
namespace ConsoleAppFramework;
55

6-
internal class Emitter(WellKnownTypes wellKnownTypes)
6+
internal class Emitter
77
{
88
public void EmitRun(SourceBuilder sb, CommandWithId commandWithId, bool isRunAsync, string? methodName = null)
99
{
@@ -124,7 +124,7 @@ public void EmitRun(SourceBuilder sb, CommandWithId commandWithId, bool isRunAsy
124124
sb.AppendLine($"if (i == {parameter.ArgumentIndex})");
125125
using (sb.BeginBlock())
126126
{
127-
sb.AppendLine($"{parameter.BuildParseMethod(i, parameter.Name, wellKnownTypes, increment: false)}");
127+
sb.AppendLine($"{parameter.BuildParseMethod(i, parameter.Name, increment: false)}");
128128
if (parameter.RequireCheckArgumentParsed)
129129
{
130130
sb.AppendLine($"arg{i}Parsed = true;");
@@ -154,7 +154,7 @@ public void EmitRun(SourceBuilder sb, CommandWithId commandWithId, bool isRunAsy
154154
}
155155
using (sb.BeginBlock())
156156
{
157-
sb.AppendLine($"{parameter.BuildParseMethod(i, parameter.Name, wellKnownTypes, increment: true)}");
157+
sb.AppendLine($"{parameter.BuildParseMethod(i, parameter.Name, increment: true)}");
158158
if (parameter.RequireCheckArgumentParsed)
159159
{
160160
sb.AppendLine($"arg{i}Parsed = true;");
@@ -180,7 +180,7 @@ public void EmitRun(SourceBuilder sb, CommandWithId commandWithId, bool isRunAsy
180180
}
181181
using (sb.BeginBlock())
182182
{
183-
sb.AppendLine($"{parameter.BuildParseMethod(i, parameter.Name, wellKnownTypes, increment: true)}");
183+
sb.AppendLine($"{parameter.BuildParseMethod(i, parameter.Name, increment: true)}");
184184
if (parameter.RequireCheckArgumentParsed)
185185
{
186186
sb.AppendLine($"arg{i}Parsed = true;");

src/ConsoleAppFramework/Parser.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -343,6 +343,7 @@ internal class Parser(SourceProductionContext context, InvocationExpressionSynta
343343
return new CommandParameter
344344
{
345345
Name = NameConverter.ToKebabCase(x.Identifier.Text),
346+
WellKnownTypes = wellKnownTypes,
346347
OriginalParameterName = x.Identifier.Text,
347348
IsNullableReference = isNullableReference,
348349
IsConsoleAppContext = isConsoleAppContext,
@@ -498,6 +499,7 @@ internal class Parser(SourceProductionContext context, InvocationExpressionSynta
498499
return new CommandParameter
499500
{
500501
Name = NameConverter.ToKebabCase(x.Name),
502+
WellKnownTypes = wellKnownTypes,
501503
OriginalParameterName = x.Name,
502504
IsNullableReference = isNullableReference,
503505
IsConsoleAppContext = isConsoleAppContext,

src/ConsoleAppFramework/RoslynExtensions.cs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,24 @@ public static IEnumerable<INamedTypeSymbol> GetBaseTypes(this INamedTypeSymbol t
2929
}
3030
}
3131

32+
public static bool EqualsNamespaceAndName(this ITypeSymbol? left, ITypeSymbol? right)
33+
{
34+
if (left == null && right == null) return true;
35+
if (left == null || right == null) return false;
36+
37+
var l = left.ContainingNamespace;
38+
var r = right.ContainingNamespace;
39+
while (l != null && r != null)
40+
{
41+
if (l.Name != r.Name) return false;
42+
43+
l = l.ContainingNamespace;
44+
r = r.ContainingNamespace;
45+
}
46+
47+
return (left.Name == right.Name);
48+
}
49+
3250
public static DocumentationCommentTriviaSyntax? GetDocumentationCommentTriviaSyntax(this SyntaxNode node)
3351
{
3452
// Hack note:

0 commit comments

Comments
 (0)