From aa508ef1ab99da9ba481a7cff0e6c7e3f8b1b73b Mon Sep 17 00:00:00 2001 From: Florian Verdonck Date: Fri, 28 Jan 2022 14:28:36 +0100 Subject: [PATCH] Extract keyword for SingleExpr expressions. (#2045) Fixes #1875. --- src/Fantomas.Tests/Fantomas.Tests.fsproj | 1 + src/Fantomas.Tests/LazyTests.fs | 32 ++++ src/Fantomas.Tests/SingleExprTests.fs | 215 +++++++++++++++++++++++ src/Fantomas/AstTransformer.fs | 72 ++++++-- src/Fantomas/CodePrinter.fs | 39 ++-- src/Fantomas/RangeHelpers.fs | 14 +- src/Fantomas/SourceParser.fs | 69 ++++---- src/Fantomas/TriviaTypes.fs | 12 ++ 8 files changed, 374 insertions(+), 80 deletions(-) create mode 100644 src/Fantomas.Tests/SingleExprTests.fs diff --git a/src/Fantomas.Tests/Fantomas.Tests.fsproj b/src/Fantomas.Tests/Fantomas.Tests.fsproj index ba242c2448..b0d59db56a 100644 --- a/src/Fantomas.Tests/Fantomas.Tests.fsproj +++ b/src/Fantomas.Tests/Fantomas.Tests.fsproj @@ -91,6 +91,7 @@ + diff --git a/src/Fantomas.Tests/LazyTests.fs b/src/Fantomas.Tests/LazyTests.fs index cc10dc4d5a..393606b8a1 100644 --- a/src/Fantomas.Tests/LazyTests.fs +++ b/src/Fantomas.Tests/LazyTests.fs @@ -76,3 +76,35 @@ let setup = let value = 1 value) """ + +[] +let ``comment after lazy keyword`` () = + formatSourceString + false + """ +lazy // comment + foobar +""" + config + |> prepend newline + |> should + equal + """ +lazy // comment + foobar +""" + +[] +let ``lazy with long indent expr should not get any additional parenthesis`` () = + formatSourceString + false + """ +let theme = lazy Application.Current.RequestedTheme +""" + config + |> prepend newline + |> should + equal + """ +let theme = lazy Application.Current.RequestedTheme +""" diff --git a/src/Fantomas.Tests/SingleExprTests.fs b/src/Fantomas.Tests/SingleExprTests.fs new file mode 100644 index 0000000000..7fc8913e52 --- /dev/null +++ b/src/Fantomas.Tests/SingleExprTests.fs @@ -0,0 +1,215 @@ +module Fantomas.Tests.SingleExprTests + +open NUnit.Framework +open FsUnit +open Fantomas.Tests.TestHelper + +[] +let ``comment after do keyword, 1875`` () = + formatSourceString + false + """ +do // Comment DO + let x = 1 + f (x) +""" + config + |> prepend newline + |> should + equal + """ +do // Comment DO + let x = 1 + f (x) +""" + +[] +let ``comment after do bang keyword`` () = + formatSourceString + false + """ +async { + do! // Comment DO BANG + let x = 1 + f (x) } +""" + config + |> prepend newline + |> should + equal + """ +async { + do! // Comment DO BANG + let x = 1 + f (x) +} +""" + +[] +let ``comment after return bang keyword`` () = + formatSourceString + false + """ +async { + return! // Comment RETURN BANG + foobar() +} +""" + config + |> prepend newline + |> should + equal + """ +async { + return! // Comment RETURN BANG + foobar () +} +""" + +[] +let ``comment after yield bang keyword`` () = + formatSourceString + false + """ +seq { + yield! // Comment YIELD BANG + foobar() +} +""" + config + |> prepend newline + |> should + equal + """ +seq { + yield! // Comment YIELD BANG + foobar () +} +""" + +[] +let ``comment after return keyword`` () = + formatSourceString + false + """ +async { + return // Comment RETURN + foobar() +} +""" + config + |> prepend newline + |> should + equal + """ +async { + return // Comment RETURN + foobar () +} +""" + +[] +let ``comment after yield keyword`` () = + formatSourceString + false + """ +seq { + yield // Comment YIELD + foobar() +} +""" + config + |> prepend newline + |> should + equal + """ +seq { + yield // Comment YIELD + foobar () +} +""" + +[] +let ``comment after assert keyword`` () = + formatSourceString + false + """ +assert // comment + foobar +""" + config + |> prepend newline + |> should + equal + """ +assert // comment + foobar +""" + +[] +let ``comment after downcast keyword`` () = + formatSourceString + false + """ +downcast // comment + foobar +""" + config + |> prepend newline + |> should + equal + """ +downcast // comment + foobar +""" + +[] +let ``comment after upcast keyword`` () = + formatSourceString + false + """ +upcast // comment + foobar +""" + config + |> prepend newline + |> should + equal + """ +upcast // comment + foobar +""" + +[] +let ``comment after address of token`` () = + formatSourceString + false + """ +& // comment + foobar +""" + config + |> prepend newline + |> should + equal + """ +& // comment + foobar +""" + +[] +let ``comment after address of tokens`` () = + formatSourceString + false + """ +&& // comment + foobar +""" + config + |> prepend newline + |> should + equal + """ +&& // comment + foobar +""" diff --git a/src/Fantomas/AstTransformer.fs b/src/Fantomas/AstTransformer.fs index 65d7b42835..56f15f6e67 100644 --- a/src/Fantomas/AstTransformer.fs +++ b/src/Fantomas/AstTransformer.fs @@ -227,13 +227,17 @@ module private Ast = yield! nodes yield! (List.collect visitSynMatchClause clauses) ] |> finalContinuation) - | SynExpr.Do (expr, range) -> + | SynExpr.Do (expr, StartRange 2 (doKeyword, range)) -> visit expr (fun nodes -> - mkNode SynExpr_Do range :: nodes + [ yield mkNode SynExpr_Do range + yield mkNode SynExpr_Do_Do doKeyword + yield! nodes ] |> finalContinuation) - | SynExpr.Assert (expr, range) -> + | SynExpr.Assert (expr, StartRange 6 (assertKeyword, range)) -> visit expr (fun nodes -> - mkNode SynExpr_Assert range :: nodes + [ yield mkNode SynExpr_Assert range + yield mkNode SynExpr_Assert_Assert assertKeyword + yield! nodes ] |> finalContinuation) | SynExpr.App (_, _, funcExpr, argExpr, range) -> let continuations: ((TriviaNodeAssigner list -> TriviaNodeAssigner list) -> TriviaNodeAssigner list) list = @@ -272,9 +276,11 @@ module private Ast = |> finalContinuation Continuation.sequence continuations finalContinuation - | SynExpr.Lazy (ex, range) -> + | SynExpr.Lazy (ex, StartRange 4 (lazyKeyword, range)) -> visit ex (fun nodes -> - mkNode SynExpr_Lazy range :: nodes + [ yield mkNode SynExpr_Lazy range + yield mkNode SynExpr_Lazy_Lazy lazyKeyword + yield! nodes ] |> finalContinuation) | SynExpr.Sequential (_, _, expr1, expr2, _) -> visit expr2 (fun nodes1 -> visit expr1 (fun nodes2 -> nodes1 @ nodes2 |> finalContinuation)) @@ -456,21 +462,33 @@ module private Ast = yield! nodes yield! visitSynType typeName ] |> finalContinuation) - | SynExpr.InferredUpcast (expr, range) -> + | SynExpr.InferredUpcast (expr, StartRange 6 (upcastKeyword, range)) -> visit expr (fun nodes -> - mkNode SynExpr_InferredUpcast range :: nodes + [ yield mkNode SynExpr_InferredUpcast range + yield mkNode SynExpr_InferredUpcast_Upcast upcastKeyword + yield! nodes ] |> finalContinuation) - | SynExpr.InferredDowncast (expr, range) -> + | SynExpr.InferredDowncast (expr, StartRange 8 (downcastKeyword, range)) -> visit expr (fun nodes -> - mkNode SynExpr_InferredDowncast range :: nodes + [ yield mkNode SynExpr_InferredDowncast range + yield mkNode SynExpr_InferredDowncast_Downcast downcastKeyword + yield! nodes ] |> finalContinuation) | SynExpr.Null range -> mkNode SynExpr_Null range |> List.singleton |> finalContinuation - | SynExpr.AddressOf (_, expr, _, range) -> + | SynExpr.AddressOf (isByRef, expr, _, range) -> + let ampersandRange, ampersandType = + if isByRef then + RangeHelpers.mkStartRange 1 range, SynExpr_AddressOf_SingleAmpersand + else + RangeHelpers.mkStartRange 2 range, SynExpr_AddressOf_DoubleAmpersand + visit expr (fun nodes -> - mkNode SynExpr_AddressOf range :: nodes + [ yield mkNode SynExpr_AddressOf range + yield mkNode ampersandType ampersandRange + yield! nodes ] |> finalContinuation) | SynExpr.TraitCall (_typars, sign, expr, range) -> visit expr (fun nodes -> @@ -492,13 +510,29 @@ module private Ast = mkNode SynExpr_ImplicitZero range |> List.singleton |> finalContinuation - | SynExpr.YieldOrReturn (_, expr, range) -> + | SynExpr.YieldOrReturn ((isYield, _), expr, range) -> + let keywordType, keywordRange = + if isYield then + SynExpr_YieldOrReturn_Yield, RangeHelpers.mkStartRange 5 range + else + SynExpr_YieldOrReturn_Return, RangeHelpers.mkStartRange 6 range + visit expr (fun nodes -> - mkNode SynExpr_YieldOrReturn range :: nodes + [ yield mkNode SynExpr_YieldOrReturn range + yield mkNode keywordType keywordRange + yield! nodes ] |> finalContinuation) - | SynExpr.YieldOrReturnFrom (_, expr, range) -> + | SynExpr.YieldOrReturnFrom ((isYield, _), expr, range) -> + let keywordType, keywordRange = + if isYield then + SynExpr_YieldOrReturnFrom_YieldBang, RangeHelpers.mkStartRange 6 range + else + SynExpr_YieldOrReturnFrom_ReturnBang, RangeHelpers.mkStartRange 7 range + visit expr (fun nodes -> - mkNode SynExpr_YieldOrReturnFrom range :: nodes + [ yield mkNode SynExpr_YieldOrReturnFrom range + yield mkNode keywordType keywordRange + yield! nodes ] |> finalContinuation) | SynExpr.LetOrUseBang (_, _, _, pat, rhsExpr, andBangs, body, range) -> let continuations: ((TriviaNodeAssigner list -> TriviaNodeAssigner list) -> TriviaNodeAssigner list) list = @@ -522,9 +556,11 @@ module private Ast = yield! nodes yield! clauses |> List.collect visitSynMatchClause ] |> finalContinuation) - | SynExpr.DoBang (expr, range) -> + | SynExpr.DoBang (expr, StartRange 3 (doBangKeyword, range)) -> visit expr (fun nodes -> - mkNode SynExpr_DoBang range :: nodes + [ yield mkNode SynExpr_DoBang range + yield mkNode SynExpr_DoBang_DoBang doBangKeyword + yield! nodes ] |> finalContinuation) | SynExpr.LibraryOnlyILAssembly (_, _, _, _, range) -> mkNode SynExpr_LibraryOnlyILAssembly range diff --git a/src/Fantomas/CodePrinter.fs b/src/Fantomas/CodePrinter.fs index e20701d6f2..5aac3cb103 100644 --- a/src/Fantomas/CodePrinter.fs +++ b/src/Fantomas/CodePrinter.fs @@ -1209,10 +1209,7 @@ and genExpr astContext synExpr ctx = isShortExpression ctx.Config.MaxElmishWidth smallExpression longExpression ctx - | SingleExpr (Lazy, e) -> - // Always add braces when dealing with lazy - let hasParenthesis = hasParenthesis e - + | LazyExpr (lazyKeyword, e) -> let isInfixExpr = match e with | InfixApp _ -> true @@ -1231,26 +1228,28 @@ and genExpr astContext synExpr ctx = ctx let genNonInfixExpr = - autoIndentAndNlnIfExpressionExceedsPageWidth ( - onlyIfNot hasParenthesis sepOpenT - +> genExpr astContext e - +> onlyIfNot hasParenthesis sepCloseT - ) + autoIndentAndNlnIfExpressionExceedsPageWidth (genExpr astContext e) - str "lazy " + genTriviaFor SynExpr_Lazy_Lazy lazyKeyword !- "lazy " +> ifElse isInfixExpr genInfixExpr genNonInfixExpr | SingleExpr (kind, e) -> - str kind - +> (match kind with - | YieldFrom - | Yield - | Return - | ReturnFrom - | Do - | DoBang -> autoIndentAndNlnIfExpressionExceedsPageWidth (genExpr astContext e) - | _ -> genExpr astContext e) - + match kind with + | InferredDowncast downcastKeyword -> + genTriviaFor SynExpr_InferredDowncast_Downcast downcastKeyword !- "downcast " + | InferredUpcast upcastKeyword -> genTriviaFor SynExpr_InferredUpcast_Upcast upcastKeyword !- "upcast " + | Assert assertKeyword -> genTriviaFor SynExpr_Assert_Assert assertKeyword !- "assert " + | AddressOfSingle ampersandToken -> genTriviaFor SynExpr_AddressOf_SingleAmpersand ampersandToken !- "&" + | AddressOfDouble ampersandToken -> genTriviaFor SynExpr_AddressOf_DoubleAmpersand ampersandToken !- "&&" + | Yield yieldKeyword -> genTriviaFor SynExpr_YieldOrReturn_Yield yieldKeyword !- "yield " + | Return returnKeyword -> genTriviaFor SynExpr_YieldOrReturn_Return returnKeyword !- "return " + | YieldFrom yieldBangKeyword -> + genTriviaFor SynExpr_YieldOrReturnFrom_YieldBang yieldBangKeyword !- "yield! " + | ReturnFrom returnBangKeyword -> + genTriviaFor SynExpr_YieldOrReturnFrom_ReturnBang returnBangKeyword !- "return! " + | Do doKeyword -> genTriviaFor SynExpr_Do_Do doKeyword !- "do " + | DoBang doBangKeyword -> genTriviaFor SynExpr_DoBang_DoBang doBangKeyword !- "do! " + +> autoIndentAndNlnIfExpressionExceedsPageWidth (genExpr astContext e) | ConstExpr (c, r) -> genConst c r | NullExpr -> !- "null" // Not sure about the role of e1 diff --git a/src/Fantomas/RangeHelpers.fs b/src/Fantomas/RangeHelpers.fs index 3c9bdb0aef..fde7c783fe 100644 --- a/src/Fantomas/RangeHelpers.fs +++ b/src/Fantomas/RangeHelpers.fs @@ -31,9 +31,11 @@ module RangeHelpers = && r1.End.Line = r2.Start.Line && r1.EndColumn = r2.StartColumn - let rec mkStartEndRange (size: int) (r: range) : range * range = - let startRange = - Range.mkRange r.FileName r.Start (Position.mkPos r.StartLine (r.StartColumn + size)) + let mkStartRange (size: int) (r: range) : range = + Range.mkRange r.FileName r.Start (Position.mkPos r.StartLine (r.StartColumn + size)) + + let mkStartEndRange (size: int) (r: range) : range * range = + let startRange = mkStartRange size r let endRange = Range.mkRange r.FileName (Position.mkPos r.EndLine (r.EndColumn - size)) r.End @@ -44,3 +46,9 @@ module RangePatterns = let (|StartEndRange|) (size: int) (range: range) = let o, c = RangeHelpers.mkStartEndRange size range o, range, c + + let (|StartRange|) (size: int) (range: range) = + let startRange = + Range.mkRange range.FileName range.Start (Position.mkPos range.StartLine (range.StartColumn + size)) + + startRange, range diff --git a/src/Fantomas/SourceParser.fs b/src/Fantomas/SourceParser.fs index 4eddc0f932..7457ed1fca 100644 --- a/src/Fantomas/SourceParser.fs +++ b/src/Fantomas/SourceParser.fs @@ -551,48 +551,39 @@ let (|Paren|_|) = | SynExpr.Paren (e, lpr, rpr, r) -> Some(lpr, e, rpr, r) | _ -> None +let (|LazyExpr|_|) (e: SynExpr) = + match e with + | SynExpr.Lazy (e, StartRange 4 (lazyKeyword, _range)) -> Some(lazyKeyword, e) + | _ -> None + type ExprKind = - | InferredDowncast - | InferredUpcast - | Lazy - | Assert - | AddressOfSingle - | AddressOfDouble - | Yield - | Return - | YieldFrom - | ReturnFrom - | Do - | DoBang - override x.ToString() = - match x with - | InferredDowncast -> "downcast " - | InferredUpcast -> "upcast " - | Lazy -> "lazy " - | Assert -> "assert " - | AddressOfSingle -> "&" - | AddressOfDouble -> "&&" - | Yield -> "yield " - | Return -> "return " - | YieldFrom -> "yield! " - | ReturnFrom -> "return! " - | Do -> "do " - | DoBang -> "do! " + | InferredDowncast of keyword: range + | InferredUpcast of keyword: range + | Assert of keyword: range + | AddressOfSingle of token: range + | AddressOfDouble of token: range + | Yield of keyword: range + | Return of keyword: range + | YieldFrom of keyword: range + | ReturnFrom of keyword: range + | Do of keyword: range + | DoBang of Keyword: range let (|SingleExpr|_|) = function - | SynExpr.InferredDowncast (e, _) -> Some(InferredDowncast, e) - | SynExpr.InferredUpcast (e, _) -> Some(InferredUpcast, e) - | SynExpr.Lazy (e, _) -> Some(Lazy, e) - | SynExpr.Assert (e, _) -> Some(Assert, e) - | SynExpr.AddressOf (true, e, _, _) -> Some(AddressOfSingle, e) - | SynExpr.AddressOf (false, e, _, _) -> Some(AddressOfDouble, e) - | SynExpr.YieldOrReturn ((true, _), e, _) -> Some(Yield, e) - | SynExpr.YieldOrReturn ((false, _), e, _) -> Some(Return, e) - | SynExpr.YieldOrReturnFrom ((true, _), e, _) -> Some(YieldFrom, e) - | SynExpr.YieldOrReturnFrom ((false, _), e, _) -> Some(ReturnFrom, e) - | SynExpr.Do (e, _) -> Some(Do, e) - | SynExpr.DoBang (e, _) -> Some(DoBang, e) + | SynExpr.InferredDowncast (e, StartRange 8 (downcastKeyword, _range)) -> Some(InferredDowncast downcastKeyword, e) + | SynExpr.InferredUpcast (e, StartRange 6 (upcastKeyword, _range)) -> Some(InferredUpcast upcastKeyword, e) + | SynExpr.Assert (e, StartRange 6 (assertKeyword, _range)) -> Some(Assert assertKeyword, e) + | SynExpr.AddressOf (true, e, _, StartRange 1 (ampersandToken, _range)) -> Some(AddressOfSingle ampersandToken, e) + | SynExpr.AddressOf (false, e, _, StartRange 2 (ampersandToken, _range)) -> Some(AddressOfDouble ampersandToken, e) + | SynExpr.YieldOrReturn ((true, _), e, StartRange 5 (yieldKeyword, _range)) -> Some(Yield yieldKeyword, e) + | SynExpr.YieldOrReturn ((false, _), e, StartRange 6 (returnKeyword, _range)) -> Some(Return returnKeyword, e) + | SynExpr.YieldOrReturnFrom ((true, _), e, StartRange 6 (yieldBangKeyword, _range)) -> + Some(YieldFrom yieldBangKeyword, e) + | SynExpr.YieldOrReturnFrom ((false, _), e, StartRange 7 (returnBangKeyword, _range)) -> + Some(ReturnFrom returnBangKeyword, e) + | SynExpr.Do (e, StartRange 2 (doKeyword, _range)) -> Some(Do doKeyword, e) + | SynExpr.DoBang (e, StartRange 3 (doBangKeyword, _range)) -> Some(DoBang doBangKeyword, e) | _ -> None type TypedExprKind = @@ -980,7 +971,7 @@ let rec (|CompExprBody|_|) expr = let (|ForEach|_|) = function - | SynExpr.ForEach (_, SeqExprOnly true, _, pat, e1, SingleExpr (Yield, e2), _) -> Some(pat, e1, e2, true) + | SynExpr.ForEach (_, SeqExprOnly true, _, pat, e1, SingleExpr (Yield _, e2), _) -> Some(pat, e1, e2, true) | SynExpr.ForEach (_, SeqExprOnly isArrow, _, pat, e1, e2, _) -> Some(pat, e1, e2, isArrow) | _ -> None diff --git a/src/Fantomas/TriviaTypes.fs b/src/Fantomas/TriviaTypes.fs index 90b0b88bf7..179b487e1a 100644 --- a/src/Fantomas/TriviaTypes.fs +++ b/src/Fantomas/TriviaTypes.fs @@ -137,13 +137,16 @@ type FsAstType = | SynExpr_MatchLambda_Function | SynExpr_Match | SynExpr_Do + | SynExpr_Do_Do | SynExpr_Assert + | SynExpr_Assert_Assert | SynExpr_App | SynExpr_TypeApp // | SynExpr_LetOrUse use first nested SynExpr | SynExpr_TryWith | SynExpr_TryFinally | SynExpr_Lazy + | SynExpr_Lazy_Lazy // | SynExpr_Sequential use first nested SynExpr | SynExpr_SequentialOrImplicitYield | SynExpr_IfThenElse @@ -165,17 +168,26 @@ type FsAstType = | SynExpr_Upcast | SynExpr_Downcast | SynExpr_InferredUpcast + | SynExpr_InferredUpcast_Upcast | SynExpr_InferredDowncast + | SynExpr_InferredDowncast_Downcast | SynExpr_Null | SynExpr_AddressOf + | SynExpr_AddressOf_SingleAmpersand + | SynExpr_AddressOf_DoubleAmpersand | SynExpr_TraitCall | SynExpr_JoinIn | SynExpr_ImplicitZero | SynExpr_YieldOrReturn + | SynExpr_YieldOrReturn_Return + | SynExpr_YieldOrReturn_Yield | SynExpr_YieldOrReturnFrom + | SynExpr_YieldOrReturnFrom_ReturnBang + | SynExpr_YieldOrReturnFrom_YieldBang | SynExpr_LetOrUseBang | SynExpr_MatchBang | SynExpr_DoBang + | SynExpr_DoBang_DoBang | SynExpr_LibraryOnlyILAssembly | SynExpr_LibraryOnlyStaticOptimization | SynExpr_LibraryOnlyUnionCaseFieldGet