diff --git a/Rewrite/src/Rewrite.CSharp/CSharpPrinter.cs b/Rewrite/src/Rewrite.CSharp/CSharpPrinter.cs index 3b48692..bfa9177 100644 --- a/Rewrite/src/Rewrite.CSharp/CSharpPrinter.cs +++ b/Rewrite/src/Rewrite.CSharp/CSharpPrinter.cs @@ -63,6 +63,26 @@ public CSharpPrinter() return tupleExpression; } + public override J? VisitTupleType(Cs.TupleType node, PrintOutputCapture p) + { + BeforeSyntax(node, CsSpace.Location.TUPLE_TYPE_PREFIX, p); + VisitContainer("(", node.Padding.Elements, CsContainer.Location.TUPLE_TYPE_ELEMENTS, ",", ")", p); + AfterSyntax(node, p); + return node; + } + + public override J? VisitTupleElement(Cs.TupleElement node, PrintOutputCapture p) + { + BeforeSyntax(node, CsSpace.Location.TUPLE_ELEMENT_PREFIX, p); + Visit(node.Type, p); + if (node.Name != null) + { + Visit(node.Name, p); + } + AfterSyntax(node, p); + return node; + } + public override J? VisitParenthesizedVariableDesignation(Cs.ParenthesizedVariableDesignation node, PrintOutputCapture p) { BeforeSyntax(node, CsSpace.Location.NAMED_ARGUMENT_PREFIX, p); @@ -677,6 +697,7 @@ private class CSharpJavaPrinter(CSharpPrinter _parent) : JavaPrinter p) { BeforeSyntax(newArray, Space.Location.NEW_ARRAY_PREFIX, p); diff --git a/Rewrite/src/Rewrite.CSharp/CSharpVisitor.g.cs b/Rewrite/src/Rewrite.CSharp/CSharpVisitor.g.cs index 94cb5e2..385324c 100644 --- a/Rewrite/src/Rewrite.CSharp/CSharpVisitor.g.cs +++ b/Rewrite/src/Rewrite.CSharp/CSharpVisitor.g.cs @@ -558,6 +558,29 @@ public override bool IsAcceptable(SourceFile sourceFile, P p) return constructorInitializer; } + public virtual J? VisitTupleType(Cs.TupleType tupleType, P p) + { + tupleType = tupleType.WithPrefix(VisitSpace(tupleType.Prefix, CsSpace.Location.TUPLE_TYPE_PREFIX, p)!); + var tempExpression = (Expression) VisitExpression(tupleType, p); + if (tempExpression is not Cs.TupleType) + { + return tempExpression; + } + tupleType = (Cs.TupleType) tempExpression; + tupleType = tupleType.WithMarkers(VisitMarkers(tupleType.Markers, p)); + tupleType = tupleType.Padding.WithElements(VisitContainer(tupleType.Padding.Elements, CsContainer.Location.TUPLE_TYPE_ELEMENTS, p)!); + return tupleType; + } + + public virtual J? VisitTupleElement(Cs.TupleElement tupleElement, P p) + { + tupleElement = tupleElement.WithPrefix(VisitSpace(tupleElement.Prefix, CsSpace.Location.TUPLE_ELEMENT_PREFIX, p)!); + tupleElement = tupleElement.WithMarkers(VisitMarkers(tupleElement.Markers, p)); + tupleElement = tupleElement.WithType(VisitAndCast(tupleElement.Type, p)!); + tupleElement = tupleElement.WithName(VisitAndCast(tupleElement.Name, p)); + return tupleElement; + } + protected virtual JContainer? VisitContainer(JContainer? container, CsContainer.Location loc, P p) where J2 : J { if (container == null) { diff --git a/Rewrite/src/Rewrite.CSharp/Parser/CSharpParserVisitor.cs b/Rewrite/src/Rewrite.CSharp/Parser/CSharpParserVisitor.cs index 2264f08..dd7c494 100644 --- a/Rewrite/src/Rewrite.CSharp/Parser/CSharpParserVisitor.cs +++ b/Rewrite/src/Rewrite.CSharp/Parser/CSharpParserVisitor.cs @@ -1324,6 +1324,11 @@ private J.Identifier MapIdentifier(SyntaxToken identifier, JavaType? type) } public override J? VisitPredefinedType(PredefinedTypeSyntax node) + { + return MapTypeTree(node); + } + + public TypeTree MapTypeTree(SyntaxNode node) { var type = MapType(node); if (type is JavaType.Primitive) @@ -1410,14 +1415,25 @@ private J.Identifier MapIdentifier(SyntaxToken identifier, JavaType? type) public override J? VisitTupleType(TupleTypeSyntax node) { - // This was added in C# 7.0 - return base.VisitTupleType(node); + return new Cs.TupleType( + Core.Tree.RandomId(), + Format(Leading(node)), + Markers.EMPTY, + JContainer.Create(node.Elements.Select(x => JRightPadded.Create(Convert(x)!, Format(Trailing(x)))).ToList()), + MapType(node) + ); } - public override J? VisitTupleElement(TupleElementSyntax node) + public override Cs VisitTupleElement(TupleElementSyntax node) { - // This was added in C# 7.0 - return base.VisitTupleElement(node); + var type = MapType(node.Type); + return new Cs.TupleElement( + Core.Tree.RandomId(), + Format(Leading(node)), + Markers.EMPTY, + MapTypeTree(node.Type), + MapIdentifier(node.Identifier, type)); + } public override J? VisitOmittedTypeArgument(OmittedTypeArgumentSyntax node) @@ -1563,16 +1579,6 @@ private Enum MapPostfixUnaryOperator(SyntaxToken operatorToken) return type; } - // private Cs.Unary.Type MapPostfixUnaryOperatorToJ(SyntaxToken operatorToken) - // { - // J.Unary.Type? type = operatorToken.Kind() switch - // { - // SyntaxKind.PlusPlusToken => J.Unary.Type.PostIncrement, - // SyntaxKind.MinusMinusToken => J.Unary.Type.PostDecrement, - // _ => null - // }; - // return type; - // } public override J? VisitMemberAccessExpression(MemberAccessExpressionSyntax node) { @@ -2523,6 +2529,7 @@ private JRightPadded MapAnonymousObjectMember(AnonymousObjectMemberDe public override J? VisitIsPatternExpression(IsPatternExpressionSyntax node) { + // todo: handle ConstantPatternSyntax (such as "something is true") if (node.Pattern is DeclarationPatternSyntax dp) { return new J.InstanceOf( diff --git a/Rewrite/src/Rewrite.CSharp/Tree/TupleElement.g.cs b/Rewrite/src/Rewrite.CSharp/Tree/TupleElement.g.cs new file mode 100644 index 0000000..ec54493 --- /dev/null +++ b/Rewrite/src/Rewrite.CSharp/Tree/TupleElement.g.cs @@ -0,0 +1,87 @@ +//------------------------------------------------------------------------------ +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ +#nullable enable +#pragma warning disable CS0108 +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using Rewrite.Core; +using Rewrite.Core.Marker; +using FileAttributes = Rewrite.Core.FileAttributes; +using Rewrite.RewriteJava.Tree; + +namespace Rewrite.RewriteCSharp.Tree; + +[SuppressMessage("ReSharper", "InconsistentNaming")] +[SuppressMessage("ReSharper", "PossibleUnintendedReferenceComparison")] +[SuppressMessage("ReSharper", "InvertIf")] +[SuppressMessage("ReSharper", "RedundantExtendsListEntry")] +[SuppressMessage("ReSharper", "UnusedMember.Global")] +[SuppressMessage("ReSharper", "RedundantNameQualifier")] +public partial interface Cs : J +{ + #if DEBUG_VISITOR + [DebuggerStepThrough] + #endif + public partial class TupleElement( + Guid id, + Space prefix, + Markers markers, + TypeTree type, + J.Identifier? name + ) : Cs, MutableTree + { + public J? AcceptCSharp

(CSharpVisitor

v, P p) + { + return v.VisitTupleElement(this, p); + } + + public Guid Id => id; + + public TupleElement WithId(Guid newId) + { + return newId == id ? this : new TupleElement(newId, prefix, markers, type, name); + } + public Space Prefix => prefix; + + public TupleElement WithPrefix(Space newPrefix) + { + return newPrefix == prefix ? this : new TupleElement(id, newPrefix, markers, type, name); + } + public Markers Markers => markers; + + public TupleElement WithMarkers(Markers newMarkers) + { + return ReferenceEquals(newMarkers, markers) ? this : new TupleElement(id, prefix, newMarkers, type, name); + } + public TypeTree Type => type; + + public TupleElement WithType(TypeTree newType) + { + return ReferenceEquals(newType, type) ? this : new TupleElement(id, prefix, markers, newType, name); + } + public J.Identifier? Name => name; + + public TupleElement WithName(J.Identifier? newName) + { + return ReferenceEquals(newName, name) ? this : new TupleElement(id, prefix, markers, type, newName); + } + #if DEBUG_VISITOR + [DebuggerStepThrough] + #endif + public bool Equals(Rewrite.Core.Tree? other) + { + return other is TupleElement && other.Id == Id; + } + #if DEBUG_VISITOR + [DebuggerStepThrough] + #endif + public override int GetHashCode() + { + return Id.GetHashCode(); + } + } +} \ No newline at end of file diff --git a/Rewrite/src/Rewrite.CSharp/Tree/TupleType.g.cs b/Rewrite/src/Rewrite.CSharp/Tree/TupleType.g.cs new file mode 100644 index 0000000..b6d1832 --- /dev/null +++ b/Rewrite/src/Rewrite.CSharp/Tree/TupleType.g.cs @@ -0,0 +1,124 @@ +//------------------------------------------------------------------------------ +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ +#nullable enable +#pragma warning disable CS0108 +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using Rewrite.Core; +using Rewrite.Core.Marker; +using FileAttributes = Rewrite.Core.FileAttributes; +using Rewrite.RewriteJava.Tree; + +namespace Rewrite.RewriteCSharp.Tree; + +[SuppressMessage("ReSharper", "InconsistentNaming")] +[SuppressMessage("ReSharper", "PossibleUnintendedReferenceComparison")] +[SuppressMessage("ReSharper", "InvertIf")] +[SuppressMessage("ReSharper", "RedundantExtendsListEntry")] +[SuppressMessage("ReSharper", "UnusedMember.Global")] +[SuppressMessage("ReSharper", "RedundantNameQualifier")] +public partial interface Cs : J +{ + #if DEBUG_VISITOR + [DebuggerStepThrough] + #endif + public partial class TupleType( + Guid id, + Space prefix, + Markers markers, + JContainer elements, + JavaType? type + ) : Cs, TypeTree, Expression, MutableTree + { + [NonSerialized] private WeakReference? _padding; + + public PaddingHelper Padding + { + get + { + PaddingHelper? p; + if (_padding == null) + { + p = new PaddingHelper(this); + _padding = new WeakReference(p); + } + else + { + _padding.TryGetTarget(out p); + if (p == null || p.T != this) + { + p = new PaddingHelper(this); + _padding.SetTarget(p); + } + } + return p; + } + } + + public J? AcceptCSharp

(CSharpVisitor

v, P p) + { + return v.VisitTupleType(this, p); + } + + public Guid Id => id; + + public TupleType WithId(Guid newId) + { + return newId == id ? this : new TupleType(newId, prefix, markers, _elements, type); + } + public Space Prefix => prefix; + + public TupleType WithPrefix(Space newPrefix) + { + return newPrefix == prefix ? this : new TupleType(id, newPrefix, markers, _elements, type); + } + public Markers Markers => markers; + + public TupleType WithMarkers(Markers newMarkers) + { + return ReferenceEquals(newMarkers, markers) ? this : new TupleType(id, prefix, newMarkers, _elements, type); + } + private readonly JContainer _elements = elements; + public IList Elements => _elements.GetElements(); + + public TupleType WithElements(IList newElements) + { + return Padding.WithElements(JContainer.WithElements(_elements, newElements)); + } + public JavaType? Type => type; + + public TupleType WithType(JavaType? newType) + { + return newType == type ? this : new TupleType(id, prefix, markers, _elements, newType); + } + public sealed record PaddingHelper(Cs.TupleType T) + { + public JContainer Elements => T._elements; + + public Cs.TupleType WithElements(JContainer newElements) + { + return T._elements == newElements ? T : new Cs.TupleType(T.Id, T.Prefix, T.Markers, newElements, T.Type); + } + + } + + #if DEBUG_VISITOR + [DebuggerStepThrough] + #endif + public bool Equals(Rewrite.Core.Tree? other) + { + return other is TupleType && other.Id == Id; + } + #if DEBUG_VISITOR + [DebuggerStepThrough] + #endif + public override int GetHashCode() + { + return Id.GetHashCode(); + } + } +} \ No newline at end of file diff --git a/Rewrite/src/Rewrite.Remote.Codec/CSharp/CSharpReceiver.g.cs b/Rewrite/src/Rewrite.Remote.Codec/CSharp/CSharpReceiver.g.cs index aef2e1c..e1db1d5 100644 --- a/Rewrite/src/Rewrite.Remote.Codec/CSharp/CSharpReceiver.g.cs +++ b/Rewrite/src/Rewrite.Remote.Codec/CSharp/CSharpReceiver.g.cs @@ -448,6 +448,26 @@ public override J VisitConstructorInitializer(Cs.ConstructorInitializer construc return constructorInitializer; } + public override J VisitTupleType(Cs.TupleType tupleType, ReceiverContext ctx) + { + tupleType = tupleType.WithId(ctx.ReceiveValue(tupleType.Id)!); + tupleType = tupleType.WithPrefix(ctx.ReceiveNode(tupleType.Prefix, ReceiveSpace)!); + tupleType = tupleType.WithMarkers(ctx.ReceiveNode(tupleType.Markers, ctx.ReceiveMarkers)!); + tupleType = tupleType.Padding.WithElements(ctx.ReceiveNode(tupleType.Padding.Elements, ReceiveContainer)!); + tupleType = tupleType.WithType(ctx.ReceiveValue(tupleType.Type)); + return tupleType; + } + + public override J VisitTupleElement(Cs.TupleElement tupleElement, ReceiverContext ctx) + { + tupleElement = tupleElement.WithId(ctx.ReceiveValue(tupleElement.Id)!); + tupleElement = tupleElement.WithPrefix(ctx.ReceiveNode(tupleElement.Prefix, ReceiveSpace)!); + tupleElement = tupleElement.WithMarkers(ctx.ReceiveNode(tupleElement.Markers, ctx.ReceiveMarkers)!); + tupleElement = tupleElement.WithType(ctx.ReceiveNode(tupleElement.Type, ctx.ReceiveTree)!); + tupleElement = tupleElement.WithName(ctx.ReceiveNode(tupleElement.Name, ctx.ReceiveTree)); + return tupleElement; + } + public override J VisitAnnotatedType(J.AnnotatedType annotatedType, ReceiverContext ctx) { annotatedType = annotatedType.WithId(ctx.ReceiveValue(annotatedType.Id)!); @@ -1620,6 +1640,28 @@ public Rewrite.Core.Tree Create(string type, ReceiverContext ctx) where T : R ); } + if (type is "Rewrite.RewriteCSharp.Tree.Cs.TupleType" or "org.openrewrite.csharp.tree.Cs$TupleType") + { + return new Cs.TupleType( + ctx.ReceiveValue(default(Guid))!, + ctx.ReceiveNode(default(Space), ReceiveSpace)!, + ctx.ReceiveNode(default(Markers), ctx.ReceiveMarkers)!, + ctx.ReceiveNode(default(JContainer), ReceiveContainer)!, + ctx.ReceiveValue(default(JavaType)) + ); + } + + if (type is "Rewrite.RewriteCSharp.Tree.Cs.TupleElement" or "org.openrewrite.csharp.tree.Cs$TupleElement") + { + return new Cs.TupleElement( + ctx.ReceiveValue(default(Guid))!, + ctx.ReceiveNode(default(Space), ReceiveSpace)!, + ctx.ReceiveNode(default(Markers), ctx.ReceiveMarkers)!, + ctx.ReceiveNode(default(TypeTree), ctx.ReceiveTree)!, + ctx.ReceiveNode(default(J.Identifier), ctx.ReceiveTree) + ); + } + if (type is "Rewrite.RewriteCSharp.Tree.J.AnnotatedType" or "org.openrewrite.java.tree.J$AnnotatedType") { return new J.AnnotatedType( diff --git a/Rewrite/src/Rewrite.Remote.Codec/CSharp/CSharpSender.g.cs b/Rewrite/src/Rewrite.Remote.Codec/CSharp/CSharpSender.g.cs index c3d7018..ab90295 100644 --- a/Rewrite/src/Rewrite.Remote.Codec/CSharp/CSharpSender.g.cs +++ b/Rewrite/src/Rewrite.Remote.Codec/CSharp/CSharpSender.g.cs @@ -439,6 +439,26 @@ public override J VisitConstructorInitializer(Cs.ConstructorInitializer construc return constructorInitializer; } + public override J VisitTupleType(Cs.TupleType tupleType, SenderContext ctx) + { + ctx.SendValue(tupleType, v => v.Id); + ctx.SendNode(tupleType, v => v.Prefix, SendSpace); + ctx.SendNode(tupleType, v => v.Markers, ctx.SendMarkers); + ctx.SendNode(tupleType, v => v.Padding.Elements, SendContainer); + ctx.SendTypedValue(tupleType, v => v.Type); + return tupleType; + } + + public override J VisitTupleElement(Cs.TupleElement tupleElement, SenderContext ctx) + { + ctx.SendValue(tupleElement, v => v.Id); + ctx.SendNode(tupleElement, v => v.Prefix, SendSpace); + ctx.SendNode(tupleElement, v => v.Markers, ctx.SendMarkers); + ctx.SendNode(tupleElement, v => v.Type, ctx.SendTree); + ctx.SendNode(tupleElement, v => v.Name, ctx.SendTree); + return tupleElement; + } + public override J VisitAnnotatedType(J.AnnotatedType annotatedType, SenderContext ctx) { ctx.SendValue(annotatedType, v => v.Id); diff --git a/Rewrite/src/Rewrite.Test.Engine.Remote/RemotingFixture.cs b/Rewrite/src/Rewrite.Test.Engine.Remote/RemotingFixture.cs index 48104ba..5a62da3 100644 --- a/Rewrite/src/Rewrite.Test.Engine.Remote/RemotingFixture.cs +++ b/Rewrite/src/Rewrite.Test.Engine.Remote/RemotingFixture.cs @@ -11,7 +11,7 @@ namespace Rewrite.Test.Engine.Remote; -public class RemotingFixture : IAsyncLifetime +public class RemotingFixture : IDisposable { private string? _rewriteRemoteDir; private Socket _socket = null!; @@ -84,7 +84,7 @@ public void ExtractEmbeddedResources(string outputDirectory) - public async Task InitializeAsync() + public RemotingFixture() { // register recipes (try to do this dynamically) IRemotingContext.RegisterRecipeFactory(Recipe.Noop().GetType().FullName!, _ => Recipe.Noop()); @@ -94,7 +94,7 @@ public async Task InitializeAsync() try { - await _socket.ConnectAsync(IPAddress.Loopback, 65432); + _socket.Connect(IPAddress.Loopback, 65432); remotingContext.Connect(_socket); remotingContext.SetCurrent(); ITestExecutionContext.SetCurrent(new RemotingTestExecutionContext(remotingContext)); @@ -108,14 +108,14 @@ public async Task InitializeAsync() { try { - await _socket.ConnectAsync(IPAddress.Loopback, 65432); + _socket.Connect(IPAddress.Loopback, 65432); remotingContext.Connect(_socket); remotingContext.SetCurrent(); break; } catch (SocketException) { - await Task.Delay(1000); + AsyncHelper.RunSync(() => Task.Delay(1000)); } } } @@ -124,7 +124,7 @@ public async Task InitializeAsync() IPrinterFactory.Set(new RemotePrinterFactory(remotingContext.Client!)); } - public async Task DisposeAsync() + public void Dispose() { _socket.Close(); @@ -149,7 +149,7 @@ public async Task DisposeAsync() } catch (Exception) { - await Task.Delay(1000); + AsyncHelper.RunSync(() => Task.Delay(1000)); } } } diff --git a/Rewrite/tests/Rewrite.CSharp.Tests/TestSetup.cs b/Rewrite/tests/Rewrite.CSharp.Tests/TestSetup.cs index 062a62a..b3c4b66 100644 --- a/Rewrite/tests/Rewrite.CSharp.Tests/TestSetup.cs +++ b/Rewrite/tests/Rewrite.CSharp.Tests/TestSetup.cs @@ -1,27 +1,27 @@ -// -// -// using Rewrite.Test.Engine.Remote; -// using Xunit.Abstractions; -// using Xunit.Sdk; -// -// [assembly: Xunit.TestFramework("Rewrite.CSharp.Tests.TestSetup", "Rewrite.CSharp.Tests")] -// -// namespace Rewrite.CSharp.Tests; -// -// public sealed class TestSetup : XunitTestFramework, IDisposable -// { -// #if REMOTE_PRINTER -// private readonly IDisposable _fixture = new RemotingFixture(); -// #else -// private readonly IDisposable _fixture = new LocalPrinterFixture(); -// #endif -// public TestSetup(IMessageSink messageSink) -// :base(messageSink) -// { -// } -// -// public new void Dispose() -// { -// _fixture.Dispose(); -// } -// } + + +using Rewrite.Test.Engine.Remote; +using Xunit.Abstractions; +using Xunit.Sdk; + +[assembly: Xunit.TestFramework("Rewrite.CSharp.Tests.TestSetup", "Rewrite.CSharp.Tests")] + +namespace Rewrite.CSharp.Tests; + +public sealed class TestSetup : XunitTestFramework, IDisposable +{ +#if REMOTE_PRINTER + private readonly IDisposable _fixture = new RemotingFixture(); +#else + private readonly IDisposable _fixture = new LocalPrinterFixture(); +#endif + public TestSetup(IMessageSink messageSink) + :base(messageSink) + { + } + + public new void Dispose() + { + _fixture.Dispose(); + } +} diff --git a/Rewrite/tests/Rewrite.CSharp.Tests/Tree/MethodDeclarationTests.cs b/Rewrite/tests/Rewrite.CSharp.Tests/Tree/MethodDeclarationTests.cs index c4df359..3baa2dc 100644 --- a/Rewrite/tests/Rewrite.CSharp.Tests/Tree/MethodDeclarationTests.cs +++ b/Rewrite/tests/Rewrite.CSharp.Tests/Tree/MethodDeclarationTests.cs @@ -37,7 +37,7 @@ public class Foo public void ConstructorDelegation() { RewriteRun( - spec => spec.TypeValidation = new TypeValidation(Unknowns: false), + // spec => spec.TypeValidation = new TypeValidation(Unknowns: false), CSharp( """ public class Foo diff --git a/Rewrite/tests/Rewrite.CSharp.Tests/Tree/TupleTests.cs b/Rewrite/tests/Rewrite.CSharp.Tests/Tree/TupleTests.cs index 9c1deff..7269a90 100644 --- a/Rewrite/tests/Rewrite.CSharp.Tests/Tree/TupleTests.cs +++ b/Rewrite/tests/Rewrite.CSharp.Tests/Tree/TupleTests.cs @@ -43,7 +43,44 @@ void M() ); var lst = src.Parse(); - var statement = lst.Descendents().OfType().First().Body!.Statements.First(); + lst.ToString().ShouldBeSameAs(src.Before); + } + + [Fact] + public void NamedTupleType() + { + var src = CSharp( + """ + public class T + { + void M() + { + (int x, int y) myTuple; + } + } + """ + ); + + var lst = src.Parse(); + lst.ToString().ShouldBeSameAs(src.Before); + } + + [Fact] + public void BasicTupleType() + { + var src = CSharp( + """ + public class T + { + void M() + { + (int, int) myTuple; + } + } + """ + ); + + var lst = src.Parse(); lst.ToString().ShouldBeSameAs(src.Before); } } diff --git a/rewrite-csharp-remote/src/main/java/org/openrewrite/csharp/remote/CSharpReceiver.java b/rewrite-csharp-remote/src/main/java/org/openrewrite/csharp/remote/CSharpReceiver.java index b253c00..266a70d 100644 --- a/rewrite-csharp-remote/src/main/java/org/openrewrite/csharp/remote/CSharpReceiver.java +++ b/rewrite-csharp-remote/src/main/java/org/openrewrite/csharp/remote/CSharpReceiver.java @@ -475,6 +475,26 @@ public Cs.ConstructorInitializer visitConstructorInitializer(Cs.ConstructorIniti return constructorInitializer; } + @Override + public Cs.TupleType visitTupleType(Cs.TupleType tupleType, ReceiverContext ctx) { + tupleType = tupleType.withId(ctx.receiveNonNullValue(tupleType.getId(), UUID.class)); + tupleType = tupleType.withPrefix(ctx.receiveNonNullNode(tupleType.getPrefix(), CSharpReceiver::receiveSpace)); + tupleType = tupleType.withMarkers(ctx.receiveNonNullNode(tupleType.getMarkers(), ctx::receiveMarkers)); + tupleType = tupleType.getPadding().withElements(ctx.receiveNonNullNode(tupleType.getPadding().getElements(), CSharpReceiver::receiveContainer)); + tupleType = tupleType.withType(ctx.receiveValue(tupleType.getType(), JavaType.class)); + return tupleType; + } + + @Override + public Cs.TupleElement visitTupleElement(Cs.TupleElement tupleElement, ReceiverContext ctx) { + tupleElement = tupleElement.withId(ctx.receiveNonNullValue(tupleElement.getId(), UUID.class)); + tupleElement = tupleElement.withPrefix(ctx.receiveNonNullNode(tupleElement.getPrefix(), CSharpReceiver::receiveSpace)); + tupleElement = tupleElement.withMarkers(ctx.receiveNonNullNode(tupleElement.getMarkers(), ctx::receiveMarkers)); + tupleElement = tupleElement.withType(ctx.receiveNonNullNode(tupleElement.getType(), ctx::receiveTree)); + tupleElement = tupleElement.withName(ctx.receiveNode(tupleElement.getName(), ctx::receiveTree)); + return tupleElement; + } + @Override public J.AnnotatedType visitAnnotatedType(J.AnnotatedType annotatedType, ReceiverContext ctx) { annotatedType = annotatedType.withId(ctx.receiveNonNullValue(annotatedType.getId(), UUID.class)); @@ -1577,6 +1597,26 @@ public T create(Class type, ReceiverContext ctx) { ); } + if (type == Cs.TupleType.class) { + return (T) new Cs.TupleType( + ctx.receiveNonNullValue(null, UUID.class), + ctx.receiveNonNullNode(null, CSharpReceiver::receiveSpace), + ctx.receiveNonNullNode(null, ctx::receiveMarkers), + ctx.receiveNonNullNode(null, CSharpReceiver::receiveContainer), + ctx.receiveValue(null, JavaType.class) + ); + } + + if (type == Cs.TupleElement.class) { + return (T) new Cs.TupleElement( + ctx.receiveNonNullValue(null, UUID.class), + ctx.receiveNonNullNode(null, CSharpReceiver::receiveSpace), + ctx.receiveNonNullNode(null, ctx::receiveMarkers), + ctx.receiveNonNullNode(null, ctx::receiveTree), + ctx.receiveNode(null, ctx::receiveTree) + ); + } + if (type == J.AnnotatedType.class) { return (T) new J.AnnotatedType( ctx.receiveNonNullValue(null, UUID.class), diff --git a/rewrite-csharp-remote/src/main/java/org/openrewrite/csharp/remote/CSharpSender.java b/rewrite-csharp-remote/src/main/java/org/openrewrite/csharp/remote/CSharpSender.java index 3f6cf81..9ce7c0e 100644 --- a/rewrite-csharp-remote/src/main/java/org/openrewrite/csharp/remote/CSharpSender.java +++ b/rewrite-csharp-remote/src/main/java/org/openrewrite/csharp/remote/CSharpSender.java @@ -458,6 +458,26 @@ public Cs.ConstructorInitializer visitConstructorInitializer(Cs.ConstructorIniti return constructorInitializer; } + @Override + public Cs.TupleType visitTupleType(Cs.TupleType tupleType, SenderContext ctx) { + ctx.sendValue(tupleType, Cs.TupleType::getId); + ctx.sendNode(tupleType, Cs.TupleType::getPrefix, CSharpSender::sendSpace); + ctx.sendNode(tupleType, Cs.TupleType::getMarkers, ctx::sendMarkers); + ctx.sendNode(tupleType, e -> e.getPadding().getElements(), CSharpSender::sendContainer); + ctx.sendTypedValue(tupleType, Cs.TupleType::getType); + return tupleType; + } + + @Override + public Cs.TupleElement visitTupleElement(Cs.TupleElement tupleElement, SenderContext ctx) { + ctx.sendValue(tupleElement, Cs.TupleElement::getId); + ctx.sendNode(tupleElement, Cs.TupleElement::getPrefix, CSharpSender::sendSpace); + ctx.sendNode(tupleElement, Cs.TupleElement::getMarkers, ctx::sendMarkers); + ctx.sendNode(tupleElement, Cs.TupleElement::getType, ctx::sendTree); + ctx.sendNode(tupleElement, Cs.TupleElement::getName, ctx::sendTree); + return tupleElement; + } + @Override public J.AnnotatedType visitAnnotatedType(J.AnnotatedType annotatedType, SenderContext ctx) { ctx.sendValue(annotatedType, J.AnnotatedType::getId); diff --git a/rewrite-csharp/src/main/java/org/openrewrite/csharp/CSharpIsoVisitor.java b/rewrite-csharp/src/main/java/org/openrewrite/csharp/CSharpIsoVisitor.java index 0553d8d..9bccceb 100644 --- a/rewrite-csharp/src/main/java/org/openrewrite/csharp/CSharpIsoVisitor.java +++ b/rewrite-csharp/src/main/java/org/openrewrite/csharp/CSharpIsoVisitor.java @@ -230,6 +230,16 @@ public Cs.ConstructorInitializer visitConstructorInitializer(Cs.ConstructorIniti return (Cs.ConstructorInitializer) super.visitConstructorInitializer(constructorInitializer, p); } + @Override + public Cs.TupleType visitTupleType(Cs.TupleType tupleType, P p) { + return (Cs.TupleType) super.visitTupleType(tupleType, p); + } + + @Override + public Cs.TupleElement visitTupleElement(Cs.TupleElement tupleElement, P p) { + return (Cs.TupleElement) super.visitTupleElement(tupleElement, p); + } + @Override public J.AnnotatedType visitAnnotatedType(J.AnnotatedType annotatedType, P p) { return (J.AnnotatedType) super.visitAnnotatedType(annotatedType, p); diff --git a/rewrite-csharp/src/main/java/org/openrewrite/csharp/CSharpPrinter.java b/rewrite-csharp/src/main/java/org/openrewrite/csharp/CSharpPrinter.java index b6ea6a7..cfd4a59 100644 --- a/rewrite-csharp/src/main/java/org/openrewrite/csharp/CSharpPrinter.java +++ b/rewrite-csharp/src/main/java/org/openrewrite/csharp/CSharpPrinter.java @@ -46,6 +46,46 @@ public J visit(@Nullable Tree tree, PrintOutputCapture

p) { } } + @Override + public Cs visitTupleExpression(Cs.TupleExpression tupleExpression, PrintOutputCapture

p) + { + beforeSyntax(tupleExpression, CsSpace.Location.TUPLE_EXPRESSION_PREFIX, p); + visitContainer("(", tupleExpression.getPadding().getArguments(), CsContainer.Location.TUPLE_EXPRESSION_ARGUMENTS, ",", ")", p); + afterSyntax(tupleExpression, p); + return tupleExpression; + } + + @Override + public Cs visitTupleType(Cs.TupleType node, PrintOutputCapture

p) + { + beforeSyntax(node, CsSpace.Location.TUPLE_TYPE_PREFIX, p); + visitContainer("(", node.getPadding().getElements(), CsContainer.Location.TUPLE_TYPE_ELEMENTS, ",", ")", p); + afterSyntax(node, p); + return node; + } + + @Override + public Cs visitTupleElement(Cs.TupleElement node, PrintOutputCapture

p) + { + beforeSyntax(node, CsSpace.Location.TUPLE_ELEMENT_PREFIX, p); + visit(node.getType(), p); + if (node.getName() != null) + { + visit(node.getName(), p); + } + afterSyntax(node, p); + return node; + } + + @Override + public Cs visitParenthesizedVariableDesignation(Cs.ParenthesizedVariableDesignation node, PrintOutputCapture

p) + { + beforeSyntax(node, CsSpace.Location.PARENTHESIZED_VARIABLE_DESIGNATION_VARIABLES, p); + visitContainer("(", node.getPadding().getVariables(), CsContainer.Location.PARENTHESIZED_VARIABLE_DESIGNATION_VARIABLES, ",", ")", p); + afterSyntax(node, p); + return node; + } + @Override public J visitArgument(Cs.Argument argument, PrintOutputCapture

p) { beforeSyntax(argument, CsSpace.Location.NAMED_ARGUMENT_PREFIX, p); diff --git a/rewrite-csharp/src/main/java/org/openrewrite/csharp/CSharpVisitor.java b/rewrite-csharp/src/main/java/org/openrewrite/csharp/CSharpVisitor.java index 623e439..03a011e 100644 --- a/rewrite-csharp/src/main/java/org/openrewrite/csharp/CSharpVisitor.java +++ b/rewrite-csharp/src/main/java/org/openrewrite/csharp/CSharpVisitor.java @@ -535,6 +535,27 @@ public J visitConstructorInitializer(Cs.ConstructorInitializer constructorInitia return constructorInitializer; } + public J visitTupleType(Cs.TupleType tupleType, P p) { + tupleType = tupleType.withPrefix(visitSpace(tupleType.getPrefix(), CsSpace.Location.TUPLE_TYPE_PREFIX, p)); + Expression tempExpression = (Expression) visitExpression(tupleType, p); + if (!(tempExpression instanceof Cs.TupleType)) + { + return tempExpression; + } + tupleType = (Cs.TupleType) tempExpression; + tupleType = tupleType.withMarkers(visitMarkers(tupleType.getMarkers(), p)); + tupleType = tupleType.getPadding().withElements(visitContainer(tupleType.getPadding().getElements(), CsContainer.Location.TUPLE_TYPE_ELEMENTS, p)); + return tupleType; + } + + public J visitTupleElement(Cs.TupleElement tupleElement, P p) { + tupleElement = tupleElement.withPrefix(visitSpace(tupleElement.getPrefix(), CsSpace.Location.TUPLE_ELEMENT_PREFIX, p)); + tupleElement = tupleElement.withMarkers(visitMarkers(tupleElement.getMarkers(), p)); + tupleElement = tupleElement.withType(visitAndCast(tupleElement.getType(), p)); + tupleElement = tupleElement.withName(visitAndCast(tupleElement.getName(), p)); + return tupleElement; + } + public JContainer visitContainer(@Nullable JContainer container, CsContainer.Location loc, P p) { if (container == null) { diff --git a/rewrite-csharp/src/main/java/org/openrewrite/csharp/tree/Cs.java b/rewrite-csharp/src/main/java/org/openrewrite/csharp/tree/Cs.java index 07aea6e..f287aef 100644 --- a/rewrite-csharp/src/main/java/org/openrewrite/csharp/tree/Cs.java +++ b/rewrite-csharp/src/main/java/org/openrewrite/csharp/tree/Cs.java @@ -3226,4 +3226,141 @@ public ConstructorInitializer withArguments(JContainer arguments) { } } } + + /** + * Represents a C# tuple type specification, which allows grouping multiple types into a single type. + * Can be used in method returns, variable declarations, etc. + *

+ * For example: + *

+     *   // Simple tuple type
+     *   (int, string) coordinates;
+     *
+     *   // Tuple type with named elements
+     *   (int x, string label) namedTuple;
+     *
+     *   // Nested tuple types
+     *   (int, (string, bool)) complexTuple;
+     *
+     *   // As method return type
+     *   public (string name, int age) GetPersonDetails() { }
+     *
+     *   // As parameter type
+     *   public void ProcessData((int id, string value) data) { }
+     * 
+ */ + @FieldDefaults(makeFinal = true, level = AccessLevel.PRIVATE) + @EqualsAndHashCode(callSuper = false, onlyExplicitlyIncluded = true) + @RequiredArgsConstructor + @AllArgsConstructor(access = AccessLevel.PRIVATE) + public class TupleType implements Cs, TypeTree, Expression { + @Nullable + @NonFinal + transient WeakReference padding; + + @With + @Getter + @EqualsAndHashCode.Include + UUID id; + + @With + @Getter + Space prefix; + + @With + @Getter + Markers markers; + + JContainer elements; + + @With + @Nullable + @Getter + JavaType type; + + public List getElements() { + return elements.getElements(); + } + + public TupleType withElements(List elements) { + return getPadding().withElements(JContainer.withElements(this.elements, elements)); + } + + @Override + public

J acceptCSharp(CSharpVisitor

v, P p) { + return v.visitTupleType(this, p); + } + + @Override + public CoordinateBuilder.Expression getCoordinates() { + return new CoordinateBuilder.Expression(this); + } + + public Padding getPadding() { + Padding p; + if (this.padding == null) { + p = new Padding(this); + this.padding = new WeakReference<>(p); + } else { + p = this.padding.get(); + if (p == null || p.t != this) { + p = new Padding(this); + this.padding = new WeakReference<>(p); + } + } + return p; + } + + @RequiredArgsConstructor + public static class Padding { + private final TupleType t; + + public JContainer getElements() { + return t.elements; + } + + public TupleType withElements(JContainer elements) { + return t.elements == elements ? t : new TupleType(t.id, t.prefix, t.markers, elements, t.type); + } + } + } + + /** + * Represents a single element within a tuple type, which may include an optional + * identifier for named tuple elements. + */ + @FieldDefaults(makeFinal = true, level = AccessLevel.PRIVATE) + @EqualsAndHashCode(callSuper = false, onlyExplicitlyIncluded = true) + @AllArgsConstructor(access = AccessLevel.PUBLIC) + public class TupleElement implements Cs { + + @With + @Getter + @EqualsAndHashCode.Include + UUID id; + + @With + @Getter + Space prefix; + + @With + @Getter + Markers markers; + + @With + @Getter + TypeTree type; + + @With + @Getter + J.@Nullable Identifier name; + + + @Override + public

J acceptCSharp(CSharpVisitor

v, P p) { + return v.visitTupleElement(this, p); + } + + } + } diff --git a/rewrite-csharp/src/main/java/org/openrewrite/csharp/tree/CsContainer.java b/rewrite-csharp/src/main/java/org/openrewrite/csharp/tree/CsContainer.java index 4c30f8b..ceeaed9 100644 --- a/rewrite-csharp/src/main/java/org/openrewrite/csharp/tree/CsContainer.java +++ b/rewrite-csharp/src/main/java/org/openrewrite/csharp/tree/CsContainer.java @@ -30,7 +30,8 @@ public enum Location { DECLARATION_EXPRESSION_VARIABLES(CsSpace.Location.DECLARATION_EXPRESSION_VARIABLES, CsRightPadded.Location.DECLARATION_EXPRESSION_VARIABLES), TUPLE_EXPRESSION_ARGUMENTS(CsSpace.Location.TUPLE_EXPRESSION_ARGUMENTS, CsRightPadded.Location.TUPLE_EXPRESSION_ARGUMENTS), METHOD_DECLARATION_PARAMETERS(CsSpace.Location.METHOD_DECLARATION_PARAMETERS, CsRightPadded.Location.METHOD_DECLARATION_PARAMETERS), - CONSTRUCTOR_INITIALIZER_ARGUMENTS(CsSpace.Location.CONSTRUCTOR_INITIALIZER_ARGUMENTS, CsRightPadded.Location.CONSTRUCTOR_INITIALIZER_ARGUMENTS); + CONSTRUCTOR_INITIALIZER_ARGUMENTS(CsSpace.Location.CONSTRUCTOR_INITIALIZER_ARGUMENTS, CsRightPadded.Location.CONSTRUCTOR_INITIALIZER_ARGUMENTS), + TUPLE_TYPE_ELEMENTS(CsSpace.Location.TUPLE_TYPE_ELEMENTS, CsRightPadded.Location.TUPLE_TYPE_ELEMENTS); private final CsSpace.Location beforeLocation; private final CsRightPadded.Location elementLocation; diff --git a/rewrite-csharp/src/main/java/org/openrewrite/csharp/tree/CsRightPadded.java b/rewrite-csharp/src/main/java/org/openrewrite/csharp/tree/CsRightPadded.java index acfa51c..36938bc 100644 --- a/rewrite-csharp/src/main/java/org/openrewrite/csharp/tree/CsRightPadded.java +++ b/rewrite-csharp/src/main/java/org/openrewrite/csharp/tree/CsRightPadded.java @@ -57,7 +57,8 @@ enum Location { PARENTHESIZED_VARIABLE_DESIGNATION_VARIABLES(CsSpace.Location.PARENTHESIZED_VARIABLE_DESIGNATION_VARIABLES), TUPLE_EXPRESSION_ARGUMENTS(CsSpace.Location.TUPLE_EXPRESSION_ARGUMENTS), METHOD_DECLARATION_PARAMETERS(CsSpace.Location.METHOD_DECLARATION_PARAMETERS), - CONSTRUCTOR_INITIALIZER_ARGUMENTS(CsSpace.Location.CONSTRUCTOR_INITIALIZER_ARGUMENTS); + CONSTRUCTOR_INITIALIZER_ARGUMENTS(CsSpace.Location.CONSTRUCTOR_INITIALIZER_ARGUMENTS), + TUPLE_TYPE_ELEMENTS(CsSpace.Location.TUPLE_TYPE_ELEMENTS); private final CsSpace.Location afterLocation; diff --git a/rewrite-csharp/src/main/java/org/openrewrite/csharp/tree/CsSpace.java b/rewrite-csharp/src/main/java/org/openrewrite/csharp/tree/CsSpace.java index dadbd86..4ea458a 100644 --- a/rewrite-csharp/src/main/java/org/openrewrite/csharp/tree/CsSpace.java +++ b/rewrite-csharp/src/main/java/org/openrewrite/csharp/tree/CsSpace.java @@ -108,7 +108,7 @@ public enum Location { TUPLE_EXPRESSION_ARGUMENTS, TUPLE_EXPRESSION_PREFIX, METHOD_DECLARATION_PARAMETERS, - CONSTRUCTOR_INITIALIZER_ARGUMENTS, CONSTRUCTOR_PREFIX, UNARY_PREFIX, UNARY_OPERATOR, CONSTRUCTOR_INITIALIZER_PREFIX; + CONSTRUCTOR_INITIALIZER_ARGUMENTS, CONSTRUCTOR_PREFIX, UNARY_PREFIX, UNARY_OPERATOR, CONSTRUCTOR_INITIALIZER_PREFIX, TUPLE_ELEMENT_PREFIX, TUPLE_TYPE_PREFIX, TUPLE_TYPE_ELEMENTS; }