From 0f1f76235ccb98dee931883718a6e441d1d3dfaa Mon Sep 17 00:00:00 2001 From: Florian Verdonck Date: Mon, 28 Aug 2023 08:38:58 +0200 Subject: [PATCH] Initial support of SynExpr.DotLambda and SynType.Intersection (#2920) * Initial support of SynExpr.DotLambda. * Update tests * Add initial support for SynType.Intersection * Add regression test for corrected range. * Add changelog entries. --- CHANGELOG.md | 12 ++++ Directory.Build.props | 2 +- src/Fantomas.Core.Tests/ClassTests.fs | 26 ++++++++ .../ConstraintIntersectionTests.fs | 52 +++++++++++++++ src/Fantomas.Core.Tests/DotLambdaTests.fs | 65 +++++++++++++++++++ .../Fantomas.Core.Tests.fsproj | 2 + src/Fantomas.Core/ASTTransformer.fs | 28 +++++++- src/Fantomas.Core/CodePrinter.fs | 11 ++++ src/Fantomas.Core/SyntaxOak.fs | 22 +++++++ 9 files changed, 218 insertions(+), 2 deletions(-) create mode 100644 src/Fantomas.Core.Tests/ConstraintIntersectionTests.fs create mode 100644 src/Fantomas.Core.Tests/DotLambdaTests.fs diff --git a/CHANGELOG.md b/CHANGELOG.md index bfe087cd52..853299e9e9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,17 @@ # Changelog +## [Unreleased] + +### Changed +* Update FCS to 'Included get,set in range of AutoProperty', commit d508186f510681d1261291474e3f9f25485999a8 + +### Added +* Initial support for `SynExpr.DotLambda` [#2920](https://github.com/fsprojects/fantomas/pull/2920) (See [dotnet/fsharp#13907](https://github.com/dotnet/fsharp/pull/13907)) +* Initial support for `SynType.Intersection` [#2920](https://github.com/fsprojects/fantomas/pull/2920) (See [dotnet/fsharp#15413](https://github.com/dotnet/fsharp/pull/15413)) + +### Fixed +* Comment no longer attached to autoproperty. [#2948](https://github.com/fsprojects/fantomas/issues/2948) + ## 6.1.3 - 2023-08-25 ### Changed diff --git a/Directory.Build.props b/Directory.Build.props index 875ebc62e4..1041ed9299 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -39,7 +39,7 @@ Some common use cases include: - 6ed38fcb360a0015828973b1f32cd2ea6b58c6ad + d508186f510681d1261291474e3f9f25485999a8 2.8.28 6.0.1 diff --git a/src/Fantomas.Core.Tests/ClassTests.fs b/src/Fantomas.Core.Tests/ClassTests.fs index b3460a0c07..b602deeb02 100644 --- a/src/Fantomas.Core.Tests/ClassTests.fs +++ b/src/Fantomas.Core.Tests/ClassTests.fs @@ -1355,3 +1355,29 @@ type Foo = with get (i: int, j: char): string = "" and set (i: int, j: char) (x: string) = printfn "%i %c" i j """ + +[] +let ``trivia above with get/set in autoproperty, 2948`` () = + formatSourceString + false + """ +module A + +type X() = + member val Y: int = 7 + // some comment + with get,set +""" + config + |> prepend newline + |> should + equal + """ +module A + +type X() = + member val Y: int = + 7 + // some comment + with get, set +""" diff --git a/src/Fantomas.Core.Tests/ConstraintIntersectionTests.fs b/src/Fantomas.Core.Tests/ConstraintIntersectionTests.fs new file mode 100644 index 0000000000..b66b5bb704 --- /dev/null +++ b/src/Fantomas.Core.Tests/ConstraintIntersectionTests.fs @@ -0,0 +1,52 @@ +module Fantomas.Core.Tests.ConstraintIntersectionTests + +open NUnit.Framework +open FsUnit +open Fantomas.Core.Tests.TestHelpers + +[] +let ``constraint intersection in type annotation`` () = + formatSourceString + false + """ +let y (f: #I & #Task & #seq) = () +""" + config + |> prepend newline + |> should + equal + """ +let y (f: #I & #Task & #seq) = () +""" + +[] +let ``constraint intersection with leading typar`` () = + formatSourceString + false + """ +let y (f: 't & #I & #IDisposable & #seq & #I2) = () +""" + config + |> prepend newline + |> should + equal + """ +let y (f: 't & #I & #IDisposable & #seq & #I2) = () +""" + +[] +let ``usage in member`` () = + formatSourceString + false + """ +type I = + abstract h: #IDisposable & #seq & #I -> unit +""" + config + |> prepend newline + |> should + equal + """ +type I = + abstract h: #IDisposable & #seq & #I -> unit +""" diff --git a/src/Fantomas.Core.Tests/DotLambdaTests.fs b/src/Fantomas.Core.Tests/DotLambdaTests.fs new file mode 100644 index 0000000000..ac84a9883d --- /dev/null +++ b/src/Fantomas.Core.Tests/DotLambdaTests.fs @@ -0,0 +1,65 @@ +module Fantomas.Core.Tests.DotLambdaTests + +open NUnit.Framework +open FsUnit +open Fantomas.Core.Tests.TestHelpers + +[] +let ``function call`` () = + formatSourceString + false + """ +let x = "a" |> _.ToString() +""" + config + |> prepend newline + |> should + equal + """ +let x = "a" |> _.ToString() +""" + +[] +let ``property call`` () = + formatSourceString + false + """ +let x = "a" |> _.Length +""" + config + |> prepend newline + |> should + equal + """ +let x = "a" |> _.Length +""" + +[] +let ``property of method invocation`` () = + formatSourceString + false + """ +let c = _.ToString().Length +""" + config + |> prepend newline + |> should + equal + """ +let c = _.ToString().Length +""" + +[] +let ``property of function invocation`` () = + formatSourceString + false + """ +let c = _.foo().Length +""" + config + |> prepend newline + |> should + equal + """ +let c = _.foo().Length +""" diff --git a/src/Fantomas.Core.Tests/Fantomas.Core.Tests.fsproj b/src/Fantomas.Core.Tests/Fantomas.Core.Tests.fsproj index 621f333f3c..257ea09bb3 100644 --- a/src/Fantomas.Core.Tests/Fantomas.Core.Tests.fsproj +++ b/src/Fantomas.Core.Tests/Fantomas.Core.Tests.fsproj @@ -128,6 +128,8 @@ + + diff --git a/src/Fantomas.Core/ASTTransformer.fs b/src/Fantomas.Core/ASTTransformer.fs index 4dc47ff730..1645ea50c1 100644 --- a/src/Fantomas.Core/ASTTransformer.fs +++ b/src/Fantomas.Core/ASTTransformer.fs @@ -1570,6 +1570,12 @@ let mkExpr (creationAide: CreationAide) (e: SynExpr) : Expr = |> Expr.IndexRange | SynExpr.IndexFromEnd(e, _) -> ExprIndexFromEndNode(mkExpr creationAide e, exprRange) |> Expr.IndexFromEnd | SynExpr.Typar(typar, _) -> mkSynTypar typar |> Expr.Typar + | SynExpr.DotLambda( + expr = e + trivia = { DotRange = mDot + UnderscoreRange = mUnderscore }) -> + ExprDotLambda(stn "_" mUnderscore, stn "." mDot, mkExpr creationAide e, exprRange) + |> Expr.DotLambda | _ -> failwithf "todo for %A" e let mkExprQuote creationAide isRaw e range : ExprQuoteNode = @@ -1976,7 +1982,7 @@ let mkModuleDecl (creationAide: CreationAide) (decl: SynModuleDecl) = |> ModuleDecl.NestedModule | decl -> failwithf $"Failed to create ModuleDecl for %A{decl}" -let mkSynTyparDecl (creationAide: CreationAide) (SynTyparDecl(attrs, typar)) = +let mkSynTyparDecl (creationAide: CreationAide) (SynTyparDecl(attributes = attrs; typar = typar)) = let m = match List.tryHead attrs with | None -> typar.Range @@ -2199,6 +2205,26 @@ let mkType (creationAide: CreationAide) (t: SynType) : Type = | SynType.Or(lhs, rhs, _, trivia) -> TypeOrNode(mkType creationAide lhs, stn "or" trivia.OrKeyword, mkType creationAide rhs, typeRange) |> Type.Or + | SynType.Intersection(optTypar, ts, m, trivia) -> + let typesAndSeparators = + let headNode, ts = + match optTypar with + | Some typar -> + // We model the typar as Type.Var out of convenience + Type.Var(mkSynTypar typar), ts + | None -> + match ts with + | [] -> failwith "SynType.Intersection does not contain typar or any intersectionConstraints" + | head :: tail -> mkType creationAide head, tail + + assert (ts.Length = trivia.AmpersandRanges.Length) + + [ yield Choice1Of2 headNode + for t, mAmp in List.zip ts trivia.AmpersandRanges do + yield Choice2Of2(stn "&" mAmp) + yield Choice1Of2(mkType creationAide t) ] + + TypeIntersectionNode(typesAndSeparators, m) |> Type.Intersection | t -> failwith $"unexpected type: {t}" let rec (|OpenL|_|) = diff --git a/src/Fantomas.Core/CodePrinter.fs b/src/Fantomas.Core/CodePrinter.fs index f6d23f8d3b..4f6af00b75 100644 --- a/src/Fantomas.Core/CodePrinter.fs +++ b/src/Fantomas.Core/CodePrinter.fs @@ -1591,6 +1591,11 @@ let genExpr (e: Expr) = |> genNode node | Expr.IndexFromEnd node -> !- "^" +> genExpr node.Expr |> genNode node | Expr.Typar node -> genSingleTextNode node + | Expr.DotLambda node -> + genSingleTextNode node.Underscore + +> genSingleTextNode node.Dot + +> genExpr node.Expr + |> genNode node let genQuoteExpr (node: ExprQuoteNode) = genSingleTextNode node.OpenToken @@ -2469,6 +2474,7 @@ let genAppWithLambda sep (node: ExprAppWithLambdaNode) = let sepSpaceBeforeParenInFuncInvocation (functionExpr: Expr) (argExpr: Expr) ctx = match functionExpr, argExpr with + | Expr.DotLambda _, _ -> ctx | Expr.Constant _, _ -> sepSpace ctx | ParenExpr _, _ -> sepSpace ctx | UppercaseExpr, ParenExpr _ -> onlyIf ctx.Config.SpaceBeforeUppercaseInvocation sepSpace ctx @@ -3167,6 +3173,11 @@ let genType (t: Type) = | Type.LongIdentApp node -> genType node.AppType +> sepDot +> genIdentListNode node.LongIdent |> genNode node + | Type.Intersection node -> + col sepSpace node.TypesAndSeparators (function + | Choice1Of2 t -> genType t + | Choice2Of2 amp -> genSingleTextNode amp) + |> genNode node let genSynTupleTypeSegments (path: Choice list) = let genTs addNewline = diff --git a/src/Fantomas.Core/SyntaxOak.fs b/src/Fantomas.Core/SyntaxOak.fs index 79b7814529..080d9a03f3 100644 --- a/src/Fantomas.Core/SyntaxOak.fs +++ b/src/Fantomas.Core/SyntaxOak.fs @@ -332,6 +332,17 @@ type TypeLongIdentAppNode(appType: Type, longIdent: IdentListNode, range) = member val AppType = appType member val LongIdent = longIdent +type TypeIntersectionNode(typesAndSeparators: Choice list, range) = + inherit NodeBase(range) + + override val Children: Node array = + [| for t in typesAndSeparators do + match t with + | Choice1Of2 t -> Type.Node t + | Choice2Of2 amp -> amp |] + + member val TypesAndSeparators = typesAndSeparators + [] type Type = | Funs of TypeFunsNode @@ -355,6 +366,7 @@ type Type = | SignatureParameter of TypeSignatureParameterNode | Or of TypeOrNode | LongIdentApp of TypeLongIdentAppNode + | Intersection of TypeIntersectionNode static member Node(x: Type) : Node = match x with @@ -379,6 +391,7 @@ type Type = | SignatureParameter n -> n | Or n -> n | LongIdentApp n -> n + | Intersection n -> n /// A pattern composed from a left hand-side pattern, a single text token/operator and a right hand-side pattern. type PatLeftMiddleRight(lhs: Pattern, middle: Choice, rhs: Pattern, range) = @@ -1604,6 +1617,13 @@ type ExprIndexFromEndNode(expr: Expr, range) = override val Children: Node array = [| Expr.Node expr |] member val Expr = expr +type ExprDotLambda(underscore: SingleTextNode, dot: SingleTextNode, expr: Expr, range: range) = + inherit NodeBase(range) + override val Children: Node array = [| underscore; dot; Expr.Node expr |] + member val Underscore = underscore + member val Dot = dot + member val Expr = expr + [] type Expr = | Lazy of ExprLazyNode @@ -1668,6 +1688,7 @@ type Expr = | IndexFromEnd of ExprIndexFromEndNode | Typar of SingleTextNode | Chain of ExprChain + | DotLambda of ExprDotLambda static member Node(x: Expr) : Node = match x with @@ -1733,6 +1754,7 @@ type Expr = | IndexFromEnd n -> n | Typar n -> n | Chain n -> n + | DotLambda n -> n member e.HasParentheses: bool = match e with