Skip to content

feat: report unavailable docstring metadata - #239

Draft
ejgallego wants to merge 9 commits into
leanprover:mainfrom
ejgallego:codex/docstring-lookup-diagnostics
Draft

ejgallego wants to merge 9 commits into
leanprover:mainfrom
ejgallego:codex/docstring-lookup-diagnostics

Conversation

@ejgallego

@ejgallego ejgallego commented Sep 12, 2026 •

Copy link
Copy Markdown
Contributor

When using Lean's module system, a plain import can leave docstrings unavailable in batch builds. Downstream consumers, such as Verso and SubVerso, thus cannot tell from a failed lookup whether a constant has no docstring or its documentation was not imported.

We add a staging API, SubVerso.findDocString, which returns .found, .absent, or .unavailable moduleName. Eventually this API should move upstream to Lean. This lets clients suggest import all M when documentation metadata is unavailable. It does not establish that the declaration actually has a docstring.

The implementation has three parts, which can be read in this order:

  1. Look up documentation — DocString.lean. Successful lookups delegate to Lean, preserving its rendering, builtin documentation, tactic aliases, and inherited documentation. When lookup fails, we follow tactic aliases and loaded inherit_doc references and inspect the defining module's effective import mode. This includes transitive imports. A local declaration can therefore report an unavailable imported module if it inherits documentation from it. .absent means no documentation was found and no known unavailable metadata blocks the lookup.

  2. Collect diagnostics for the chosen hover — Highlighting/Code.lean. The warning's contract is to report unavailable metadata when it may prevent documentation from appearing in an output hover. Lean's elaboration information can associate several expressions or declarations with the same source span. SubVerso classifies each matching entry into a possible token kind (a “candidate”), then uses its existing priorities to choose the identifier's hover.

    A concrete example is a documented DSL keyword, combineWarnings, whose elaborator returns the imported function SubVerso.Highlighting.Diagnostics.append. Classification finds that function, but the output uses a keyword token with the syntax's own documentation, “Combine warning summaries.” A plain import leaves the function's docstring unavailable; adding import all SubVerso.Highlighting.Diagnostics restores it without changing the keyword's hover. Warning about that module here would therefore give the user an import suggestion that does not improve the displayed documentation. An explicit reference to Diagnostics.append does warn under a plain import, and gains its documentation with import all.

    To implement this distinction, tokenWithDocString returns the lookup information with the candidate instead of immediately adding a warning to the summary. The existing KindWithPPSig tuple gains a third field: some M if metadata was unavailable, or none otherwise. identCandidate selects a candidate without changing the summary; retainKind adds its optional module to HighlightState.missingDocStringModules when that candidate supplies an output token's hover. Lookup still happens during classification, in the environment appropriate to each candidate; only recording the module waits for that choice. The extra tuple field is temporary and is not serialized to clients.

    “Discarded” refers to unused candidate information for a token. Both source branches of an if are highlighted and can contribute diagnostics. “Format-only annotations” are the colour and binding information attached to names in pretty-printed signatures and types; they do not include documentation hovers for those names. cleanFormatTags reuses classification to build these annotations without calling retainKind. Pretty-printer output rendered as tokens with documentation hovers still contributes diagnostics. Regression tests check that discarded candidates and these annotations add no warnings, while output hovers with unavailable metadata do identify their modules.

  3. Return a summary to clients — Highlighting/Diagnostics.lean. Each highlighting entrypoint now returns (highlighted, diagnostics). Diagnostics.missingDocStringModules is a NameSet, and callers merge summaries with ++ when combining results. The changes in helpers, module extraction, signatures, and saved examples carry this summary through to their consumers, separately from Lean messages. JSON encodes the set as a sorted array; decoding restores uniqueness and accepts older payloads that omit the diagnostics field.

This is an intentional result-level design: clients can render one import all warning per module; individual token lookup statuses and source locations are not retained. A summary covers the whole returned result. Slicing highlighted output does not narrow it, so an excerpt-specific warning needs diagnostics collected at that excerpt's boundary.

We also need an upstream API to query whether a module's server metadata is available in an environment. Until then, we use declaration ranges from the whole module as evidence that server metadata was loaded. Checking the whole module also covers generated declarations with no range of their own. A module with no recorded ranges may still be reported as unavailable even when its metadata is loaded. Older Lean versions without the module system return only .found or .absent.

InternalTests.lean covers plain imports, import all, server-loaded metadata, local and inherited documentation, discarded candidates, retained hovers, and diagnostic serialization. Existing internal tests already execute during lake build through #eval and assertion commands. The new import-mode tests run from the executable's main, so CI also invokes lake exe subverso-internal-tests. The pinned toolchain is updated to Lean 4.34.0-rc2.

Validation: the clean local build and runtime regressions passed, and the full CI run for this revision passed.

@ejgallego ejgallego changed the title feat: stage docstring lookup and report unavailable metadata feat: report unavailable docstring metadata Sep 12, 2026
Comment thread .github/workflows/nightly.yml
Comment thread src/SubVerso/Highlighting/Code.lean
Detect how SubVerso's defining module was compiled before selecting the import mode in runtime tests. Module API support alone does not mean the library was built with module syntax.

Keep missing modules in a NameSet, remove redundant sorting, and rebuild the set when decoding JSON. Explain why CI runs the runtime tests explicitly.

Upstream follow-up: Lean should expose whether server metadata is available for a module in an environment. Until then, declaration ranges are conservative evidence: a module without ranges may be reported as unavailable even when its metadata was loaded.
def highlightMessage (message : Message)
(suppressNamespaces : List Name := []) (collectFormat := false) :
TermElabM Highlighted.Message := do
TermElabM (Highlighted.Message × Diagnostics) := do

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could introduce a record for the result type, but IMHO that's premature.

Existing internal assertions run during elaboration as part of lake build. The additional executable invocation runs the new import-mode tests in main; it does not repair a gap in the previous internal-test coverage.
Defer documentation lookup until token selection, retaining each candidate's
lookup environment. Discarded semantic candidates and format-only annotations
no longer contribute missing-metadata warnings.

Require diagnostics when saving examples and constructing helper results,
return signature data in a named record, and document summary scope.
Add regressions for selection, format annotations, and retained hovers.
Look up documentation during classification and carry the optional unavailable
module with the candidate. Add it to the summary only when retaining the hover.
This preserves lookup provenance without deferred environments or a separate
pass to reconstruct token documentation.

Keep the existing signature-result tuple and defer the optional record cleanup.
Retain the diagnostic regressions and update the design documentation.
Extend KindWithPPSig with the optional unavailable module, preserving the
original tuple patterns. Retained hovers still commit their diagnostics after
selection, with regression coverage unchanged.

Condense the README while retaining the lookup and summary contracts.
unavailable, even an undocumented declaration can contribute its module. Inherited documentation
contributes the module reached through the loaded `inherit_doc` references.
-/
missingDocStringModules : NameSet := {}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this may not be as useful as we'd want it to be.

The use case we talked about was putting a yellow squiggly on a name that could benefit from import all, and I don't think this really gives us a non-roundabout way to do it. I'd expect to at least have this be a NameMap Name, with keys being the names whose lookup failed and the values being the module that needs to be import all'd. Then, a quick traverse of the infotree is enough to emit the warnings in Verso.

@ejgallego ejgallego Oct 5, 2026 •

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just saw this comment: I was blocked on making this better due to not finding the right Lean upstream API to get the info we need.

@ejgallego
ejgallego marked this pull request as draft September 16, 2026 14:08
ejgallego added a commit to ejgallego/verso-slides that referenced this pull request Oct 7, 2026
Document that using Lean's module system with Verso Slides is
experimental and may cause issues until
leanprover/subverso#239 lands.

I've left the module system annotations in `Demo.lean` commented out.
I'm split on whether to keep them there or just remove them.
david-christiansen added a commit to leanprover/verso-slides that referenced this pull request Oct 8, 2026
Document that using Lean's module system with Verso Slides is
experimental and may cause issues until
leanprover/subverso#239 lands.

---------

Co-authored-by: David Thrane Christiansen <david@davidchristiansen.dk>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants