Skip to content

F# folding: the open and xml_doc arms in collect_folding are dead code #219

Description

@MelbourneDeveloper

Summary

src/sharplsp/src/syntax.rs collect_folding gained F# node kinds on PR #218. Two of the new match arms can never produce a fold:

// Comments: C# `comment`, F# `block_comment` ((* *)) and `xml_doc` (///) — multi-line only.
"comment" | "block_comment" | "xml_doc"
    if node.start_position().row != node.end_position().row => { ... }
// Using directives group (C# `using_directive`, F# `open`)
"using_directive" | "open" => Some(FoldingRangeKind::Imports),

1. "open" matches a keyword token, not a declaration node

In tree-sitter-fsharp 0.3.11 (fsharp/src/node-types.json), open is "named": false — it is the bare open keyword token. The F# import declaration node is import_decl ("named": true, one long_identifier child).

The open keyword token always spans exactly one line, so start.row < end.row in the emit guard is never true. The arm is unreachable in effect.

2. "xml_doc" never spans more than one line

The grammar emits one xml_doc node per /// line, so a multi-line doc block is N single-line nodes, and the start_position().row != end_position().row guard is never satisfied. The arm is dead too.

Reproduction

Adding to the fixture in test_folding_range_on_fsharp_file (src/sharplsp/tests/e2e_modules/folding.rs):

module M
open System
(* a multi-line
   block comment *)
/// Computes the area of a shape.
/// The doc block spans two lines, so it folds.
type Shape =
    ...

textDocument/foldingRange returns 4 ranges — three region folds and exactly one comment fold (the (* *) block, lines 2–3). There is no imports fold for open System and no comment fold for the two /// lines.

The existing assertion passes only because kinds.contains(&"comment") is satisfied by the (* *) block alone.

Expected

  • Consecutive /// (xml_doc) sibling nodes should merge into a single comment fold spanning the run — this is what Visual Studio / Rider / C# Dev Kit do for /// blocks, and F# is a first-class citizen (CLAUDE.md aim Expand Forge LSP features, editor tooling, and CI packaging #2).
  • Consecutive import_decl (F#) / using_directive (C#) siblings should merge into a single imports fold. Note the C# side has the same latent problem: a one-line using X; also fails the start.row < end.row guard, so FoldingRangeKind::Imports is likely never emitted for either language today.

Both need sibling-run grouping in collect_folding, not just a node-kind rename.

Notes

Coverage does not catch this: the arms share instrumented lines with live arms, so the file reports 220/224 lines covered on both Linux CI and macOS.

Found while auditing PR #218's Rust coverage gate.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions