From c1df1fd233aea743fea61f83fe9c38e6cc3262a2 Mon Sep 17 00:00:00 2001 From: jindraivanek Date: Sun, 13 Nov 2022 09:25:28 +0100 Subject: [PATCH 1/8] add tests --- .../CompilerDirectivesTests.fs | 82 +++++++++++++++++++ 1 file changed, 82 insertions(+) diff --git a/src/Fantomas.Core.Tests/CompilerDirectivesTests.fs b/src/Fantomas.Core.Tests/CompilerDirectivesTests.fs index ec9c367d3d..22aa9ad544 100644 --- a/src/Fantomas.Core.Tests/CompilerDirectivesTests.fs +++ b/src/Fantomas.Core.Tests/CompilerDirectivesTests.fs @@ -2983,3 +2983,85 @@ let ``dead code in active block`` () = b #endif """ + +[] +let ``dont delete #if attr in "and" type, 628`` () = + formatSourceStringWithDefines + [ "NET2" ] + """ +#if NET2 +[] +#else +[] +#endif +[] +type internal T1 = T1 +and [] +#if NET2 +[] +#else +[] +#endif + internal T2 = T2 +""" + config + |> prepend newline + |> should + equal + """ +#if NET2 +[] +#else +#endif +[] +type internal T1 = T1 + +and [] +#if NET2 +[] +#else +#endif +internal T2 = T2 +""" + +[] +let ``attributes in "and" type`` () = + formatSourceString + false + """ +#if NET2 +[] +#else +[] +#endif +[] +type internal T1 = T1 +and [] +#if NET2 +[] +#else +[] +#endif + internal T2 = T2 +""" + config + |> prepend newline + |> should + equal + """ +#if NET2 +[] +#else +[] +#endif +[] +type internal T1 = T1 + +and [] +#if NET2 +[] +#else +[] +#endif +internal T2 = T2 +""" From 97e275f4732a2f2c46deac188a8240475da5eadb Mon Sep 17 00:00:00 2001 From: jindraivanek Date: Sun, 13 Nov 2022 09:28:38 +0100 Subject: [PATCH 2/8] map directive trivia as AddAfter in Attribute after "and" type case (WIP) --- src/Fantomas.Core/Trivia.fs | 30 +++++++++++++++++++++++++----- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/src/Fantomas.Core/Trivia.fs b/src/Fantomas.Core/Trivia.fs index 6d680f33d4..b70824b4a4 100644 --- a/src/Fantomas.Core/Trivia.fs +++ b/src/Fantomas.Core/Trivia.fs @@ -248,20 +248,40 @@ let triviaBeforeOrAfterEntireTree (rootNode: TriviaNode) (trivia: Trivia) : Triv /// Try to put the trivia on top of the closest node /// If that didn't work put it after the last node let simpleTriviaToTriviaInstruction (containerNode: TriviaNode) (trivia: Trivia) : TriviaInstruction option = - containerNode.Children - |> Array.tryFind (fun node -> node.Range.StartLine > trivia.Range.StartLine) + //TODO: generalize + let addAfterTypes = set [ FsAstType.SynAttributeList_ ] + + let addAfterNode = + match trivia.Item with + | Directive _ when containerNode.Children.[0].Type = FsAstType.SynTypeDefn_And -> + containerNode.Children + |> Array.rev + |> Seq.tryFind (fun node -> + Set.contains node.Type addAfterTypes + && node.Range.StartLine < trivia.Range.StartLine) + | _ -> None + + addAfterNode |> Option.map (fun node -> { Trivia = trivia Type = node.Type Range = node.Range - AddBefore = true }) + AddBefore = false }) |> Option.orElseWith (fun () -> - Array.tryLast containerNode.Children + containerNode.Children + |> Array.tryFind (fun node -> node.Range.StartLine > trivia.Range.StartLine) |> Option.map (fun node -> { Trivia = trivia Type = node.Type Range = node.Range - AddBefore = false })) + AddBefore = true }) + |> Option.orElseWith (fun () -> + Array.tryLast containerNode.Children + |> Option.map (fun node -> + { Trivia = trivia + Type = node.Type + Range = node.Range + AddBefore = false }))) /// Try and find the smallest possible node let lineCommentAfterSourceCodeToTriviaInstruction From 8af7d0895bc7e073e7a5252d70ce8503c42e6666 Mon Sep 17 00:00:00 2001 From: jindraivanek Date: Sun, 13 Nov 2022 09:29:50 +0100 Subject: [PATCH 3/8] switch to full "genAttributes" for "and" type --- src/Fantomas.Core/CodePrinter.fs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Fantomas.Core/CodePrinter.fs b/src/Fantomas.Core/CodePrinter.fs index 9890c98d1f..98ba3a00d2 100644 --- a/src/Fantomas.Core/CodePrinter.fs +++ b/src/Fantomas.Core/CodePrinter.fs @@ -2916,7 +2916,7 @@ and genTypeDefn (TypeDef(ats, px, leadingKeyword, ao, tds, tcs, equalsRange, tdr let genLeadingKeyword = match leadingKeyword with | SynTypeDefnLeadingKeyword.Type mType -> genAttributes ats +> genTriviaFor SynTypeDefn_Type mType !- "type " - | SynTypeDefnLeadingKeyword.And mAnd -> genTriviaFor SynTypeDefn_And mAnd !- "and " +> genOnelinerAttributes ats + | SynTypeDefnLeadingKeyword.And mAnd -> genTriviaFor SynTypeDefn_And mAnd !- "and " +> genAttributes ats | SynTypeDefnLeadingKeyword.StaticType _ | SynTypeDefnLeadingKeyword.Synthetic -> sepNone From 4e28ab79aba8228b181395126c550f9c7375b4ce Mon Sep 17 00:00:00 2001 From: jindraivanek Date: Sun, 13 Nov 2022 09:30:47 +0100 Subject: [PATCH 4/8] fix generating extra newlines for Directive trivias that are AddAfter --- src/Fantomas.Core/Context.fs | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/Fantomas.Core/Context.fs b/src/Fantomas.Core/Context.fs index 77554bcb26..9d8738f093 100644 --- a/src/Fantomas.Core/Context.fs +++ b/src/Fantomas.Core/Context.fs @@ -1045,7 +1045,7 @@ let sepSemi (ctx: Context) = let ifAlignBrackets f g = ifElseCtx (fun ctx -> ctx.Config.MultilineBlockBracketsOnSameColumn) f g -let printTriviaContent (c: TriviaContent) (ctx: Context) = +let printTriviaContent (c: TriviaContent) addBefore (ctx: Context) = let currentLastLine = ctx.WriterModel.Lines |> List.tryHead // Some items like #if or Newline should be printed on a newline @@ -1073,11 +1073,18 @@ let printTriviaContent (c: TriviaContent) (ctx: Context) = +> ifElse after sepNlnForTrivia sepNone | Newline -> (ifElse addNewline (sepNlnForTrivia +> sepNlnForTrivia) sepNlnForTrivia) | Directive s - | Comment(CommentOnSingleLine s) -> (ifElse addNewline sepNlnForTrivia sepNone) +> !-s +> sepNlnForTrivia + | Comment(CommentOnSingleLine s) -> + (ifElse addNewline sepNlnForTrivia sepNone) + +> !-s + +> (ifElse addBefore sepNlnForTrivia sepNone) <| ctx let printTriviaInstructions (triviaInstructions: TriviaInstruction list) = - col sepNone triviaInstructions (fun { Trivia = trivia } -> printTriviaContent trivia.Item) + col + sepNone + triviaInstructions + (fun { Trivia = trivia + AddBefore = addBefore } -> printTriviaContent trivia.Item addBefore) let enterNodeFor (mainNodeName: FsAstType) (range: Range) (ctx: Context) = match Map.tryFind mainNodeName ctx.TriviaBefore with From f612b7e8ba5eeb842afc98ecc84b05826187498b Mon Sep 17 00:00:00 2001 From: jindraivanek Date: Thu, 17 Nov 2022 10:29:07 +0100 Subject: [PATCH 5/8] genAttributes variant without newline after oneliner --- src/Fantomas.Core/CodePrinter.fs | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/Fantomas.Core/CodePrinter.fs b/src/Fantomas.Core/CodePrinter.fs index 98ba3a00d2..42da2b98c4 100644 --- a/src/Fantomas.Core/CodePrinter.fs +++ b/src/Fantomas.Core/CodePrinter.fs @@ -482,10 +482,14 @@ and genOnelinerAttributes ats = /// Try to group attributes if they are on the same line /// Separate same-line attributes by ';' /// Each bucket is printed in a different line -and genAttributes (ats: SynAttributes) = - colPost sepNlnUnlessLastEventIsNewline sepNln ats (fun a -> - (genAttributesCore a.Attributes |> genTriviaFor SynAttributeList_ a.Range) - +> sepNlnWhenWriteBeforeNewlineNotEmpty) +and genAttributes' nlnAfterOneliner (ats: SynAttributes) = + let addNln = nlnAfterOneliner || ats.Length > 1 + + colPost (ifElse addNln sepNlnUnlessLastEventIsNewline sepSpace) sepNln ats (fun a -> + let expr = genAttributesCore a.Attributes |> genTriviaFor SynAttributeList_ a.Range + expr) + +and genAttributes (ats: SynAttributes) = genAttributes' true ats and genPreXmlDoc (PreXmlDoc(lines, _)) = colPost sepNln sepNln lines (sprintf "///%s" >> (!-)) @@ -2916,7 +2920,7 @@ and genTypeDefn (TypeDef(ats, px, leadingKeyword, ao, tds, tcs, equalsRange, tdr let genLeadingKeyword = match leadingKeyword with | SynTypeDefnLeadingKeyword.Type mType -> genAttributes ats +> genTriviaFor SynTypeDefn_Type mType !- "type " - | SynTypeDefnLeadingKeyword.And mAnd -> genTriviaFor SynTypeDefn_And mAnd !- "and " +> genAttributes ats + | SynTypeDefnLeadingKeyword.And mAnd -> genTriviaFor SynTypeDefn_And mAnd !- "and " +> genAttributes' false ats | SynTypeDefnLeadingKeyword.StaticType _ | SynTypeDefnLeadingKeyword.Synthetic -> sepNone From 2076d50aa555f959d23c352c68d2b94559c8b338 Mon Sep 17 00:00:00 2001 From: jindraivanek Date: Thu, 17 Nov 2022 16:53:04 +0100 Subject: [PATCH 6/8] generalize simpleTriviaToTriviaInstruction --- src/Fantomas.Core/Trivia.fs | 49 ++++++++++++++++++------------------- 1 file changed, 24 insertions(+), 25 deletions(-) diff --git a/src/Fantomas.Core/Trivia.fs b/src/Fantomas.Core/Trivia.fs index b70824b4a4..c4967b1e16 100644 --- a/src/Fantomas.Core/Trivia.fs +++ b/src/Fantomas.Core/Trivia.fs @@ -246,42 +246,41 @@ let triviaBeforeOrAfterEntireTree (rootNode: TriviaNode) (trivia: Trivia) : Triv AddBefore = isBefore } /// Try to put the trivia on top of the closest node +/// (or after closest node before in some special cases) /// If that didn't work put it after the last node let simpleTriviaToTriviaInstruction (containerNode: TriviaNode) (trivia: Trivia) : TriviaInstruction option = - //TODO: generalize - let addAfterTypes = set [ FsAstType.SynAttributeList_ ] + let childrenTypes = containerNode.Children |> Array.map (fun n -> n.Type) + + let mkTriviaNode addBefore (node: TriviaNode) = + { Trivia = trivia + Type = node.Type + Range = node.Range + AddBefore = addBefore } + + let findChildNodeToAddAfter mainType prevType = + containerNode.Children + |> Array.indexed + |> Array.rev + |> Seq.tryFind (fun (i, node) -> + node.Type = mainType + && node.Range.StartLine < trivia.Range.StartLine + && childrenTypes[0 .. i - 1] |> Array.contains prevType) + |> Option.map snd let addAfterNode = match trivia.Item with - | Directive _ when containerNode.Children.[0].Type = FsAstType.SynTypeDefn_And -> - containerNode.Children - |> Array.rev - |> Seq.tryFind (fun node -> - Set.contains node.Type addAfterTypes - && node.Range.StartLine < trivia.Range.StartLine) + | Directive _ -> + // link directive after attribute for "... and ..." type + findChildNodeToAddAfter FsAstType.SynAttributeList_ FsAstType.SynTypeDefn_And | _ -> None addAfterNode - |> Option.map (fun node -> - { Trivia = trivia - Type = node.Type - Range = node.Range - AddBefore = false }) + |> Option.map (mkTriviaNode false) |> Option.orElseWith (fun () -> containerNode.Children |> Array.tryFind (fun node -> node.Range.StartLine > trivia.Range.StartLine) - |> Option.map (fun node -> - { Trivia = trivia - Type = node.Type - Range = node.Range - AddBefore = true }) - |> Option.orElseWith (fun () -> - Array.tryLast containerNode.Children - |> Option.map (fun node -> - { Trivia = trivia - Type = node.Type - Range = node.Range - AddBefore = false }))) + |> Option.map (mkTriviaNode true) + |> Option.orElseWith (fun () -> Array.tryLast containerNode.Children |> Option.map (mkTriviaNode false))) /// Try and find the smallest possible node let lineCommentAfterSourceCodeToTriviaInstruction From 67073b11b8cc026d1b67a44c82659fb3a69e8a73 Mon Sep 17 00:00:00 2001 From: jindraivanek Date: Thu, 17 Nov 2022 18:00:54 +0100 Subject: [PATCH 7/8] better test name --- src/Fantomas.Core.Tests/CompilerDirectivesTests.fs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Fantomas.Core.Tests/CompilerDirectivesTests.fs b/src/Fantomas.Core.Tests/CompilerDirectivesTests.fs index 22aa9ad544..8100275b88 100644 --- a/src/Fantomas.Core.Tests/CompilerDirectivesTests.fs +++ b/src/Fantomas.Core.Tests/CompilerDirectivesTests.fs @@ -3025,7 +3025,7 @@ internal T2 = T2 """ [] -let ``attributes in "and" type`` () = +let ``attributes with directives in "and" type, 628`` () = formatSourceString false """ From aaf4c47a563480f1970a47907114efb5a2d10adb Mon Sep 17 00:00:00 2001 From: jindraivanek Date: Sat, 19 Nov 2022 08:19:39 +0100 Subject: [PATCH 8/8] add CHANGELOG entry --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ea193d4427..df8091cc61 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ ### Fixed * Indenting problem with `match` workaround for single-line stroustrup expressions [#2586](https://github.com/fsprojects/fantomas/issues/2586) +* System.Exception: Fantomas is trying to format the input multiple times due to the detect of multiple defines. [#628](https://github.com/fsprojects/fantomas/issues/628). ## [5.1.3] - 2022-11-14