[Repo Assist] Fix FormatException when #if directive appears between attribute lists in mutually recursive type#3290
Draft
github-actions[bot] wants to merge 1 commit intomainfrom
Conversation
…s in mutually recursive type (fixes #3174) Root cause: genOnelinerAttributes merged all AttributeListNodes into a single one-liner, dropping ContentBefore trivia (including #if/#endif directives) from all but the MultipleAttributeListNode itself. When the define was active, extra attribute lists existed and their per-list directives were silently dropped, producing a different hash-fragment count than the no-define variant and causing mergeMultipleFormatResults to throw a FormatException. Fix (CodePrinter.fs): - genOnelinerAttributes detects when any AttributeListNode at index ≥ 1 has a compiler directive in its ContentBefore, and falls back to rendering each list individually via genNode (preserving ContentBefore trivia) with sepNln between lists and sepSpace after the last, keeping the type name on the same line as the final attribute. - genTypeDefn detects directive-containing attribute lists for 'and' type definitions and forces indent/unindent so the formatted output is valid F# in both define variants (column-0 attributes after 'and' are a parse error). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
28 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
🤖 This is an automated pull request from Repo Assist, an AI assistant.
Closes #3174
Root Cause
genOnelinerAttributesmerged allAttributeListNodes into a single one-liner by collecting all attributes, taking the opening token from the first list and the closing token from the last. This silently droppedContentBeforetrivia (including#if/#endifdirectives) from all individualAttributeListNodes — only the outerMultipleAttributeListNode.ContentBeforewas preserved viagenNode n.When
NET5_0_OR_GREATERis defined, the Oak tree has twoAttributeListNodes ([(Interface)]and[(Class)]) with the#endifdirective stored in the second list'sContentBefore. The one-liner path dropped this directive, producing only 1 hash-fragment boundary instead of 2. The no-define variant (where both#ifand#endifare on the outer node'sContentBefore) produced 2 boundaries. The mismatch causedmergeMultipleFormatResultsto throwFormatException.Fix
CodePrinter.fs—genOnelinerAttributesAdded detection: if any
AttributeListNodeat index ≥ 1 has aTriviaContent.Directivein itsContentBefore, fall back to rendering each list individually viagenNode(which preserves per-listContentBeforetrivia). The lists are separated bysepNlnand the last list is followed bysepSpace(notsepNln) so the type name follows on the same line.CodePrinter.fs—genTypeDefnAdded directive detection for
andtype definitions. When attributes contain compiler directives,indent/unindentare forced around the attribute rendering and type name. This is required because attributes at column 0 afterand\nare a parse error in F# — they must be indented. Theindentalso ensures thatsplitWhenHash(which usesTrimStart()) still detects the directives correctly, keeping fragment counts consistent across define variants.Output
Input:
Formatted output (stable and idempotent):
Test Status
dotnet fantomas