Do not splice the type name into constructor and field references - #1468
Do not splice the type name into constructor and field references#1468MavenRain wants to merge 2 commits into
Conversation
a51efac to
37e9047
Compare
The name of a type is not part of the OCaml path of its constructors and of its fields: `M.t.Foo` is not valid syntax whereas `M.Foo` is. Rendering a reference as `M.t.Foo` invents syntax that does not exist, which is what ocaml#372 reports. `render_resolved` now renders the path of the parent of the type rather than the path of the type itself for the four arms that name a member of a type: `Constructor, `PolyConstructor, `Field and `UnboxedField. The enclosing module path is kept, so `{!Bla.Alpha}` renders as `Bla.Alpha`. Anchors are unaffected: they are built from `Reference.Resolved.identifier`, an independent traversal, so a link whose text is now `Bla.Alpha` still points at `#type-ha.Alpha`. Fields of the inline record of an extension constructor are parented by a signature rather than by a type, and keep that parent in their path. Fixes ocaml#372 Signed-off-by: Onyeka Obi <softwareengineerasaservant@isurvivable.cv>
37e9047 to
9745a5e
Compare
panglesd
left a comment
There was a problem hiding this comment.
Looks good to me. Thanks for the PR.
About the two questions: I think both of them should not be done, but if they were done it should anyway be in another PR.
- About polymorphic variants... yes the reference is invalid OCaml syntax. But "go to declaration" does not make sense in OCaml, yet it does in odoc. So I don't know what is the best, but I would tend to keep it as it is now.
- Tooltips are good like they are here, in my opinion. They could be used to distinguish which type we are speaking about, in the very rare case where there is ambiguity, but the reader can just click on the link to find out in those cases.
| | `Identifier | ||
| { | ||
| iv = `Root _ | `Module _ | `Parameter _ | `Result _ | `ModuleType _; | ||
| _; | ||
| } | ||
| | `Alias _ | `AliasModuleType _ | `Module _ | `Hidden _ | `ModuleType _ -> | ||
| render_resolved (r :> t) ^ "." |
There was a problem hiding this comment.
I think it would improve the readability if we replaced at least the identifier with a type:
| `Identifier { iv = #Identifier.Signature.t_pv; _ }(or something like that)
I don't think we can do the same with the rest of the clauses, but it is fine to keep them explicitly matched.
`render_field_parent` listed the five signature identifier constructors explicitly. Match them with `#Identifier.Signature.t_pv` instead, as Paul-Elliot suggested in review. `field_parent_pv` is `[ signature_pv | datatype_pv ]`, so this arm and the `` `Type `` identifier arm above it still partition the identifier case between them with no overlap and no wildcard, and the match stays exhaustive. The remaining clauses are kept explicitly matched. Also fill in the changelog placeholder now that the PR number exists. Signed-off-by: Onyeka Obi <softwareengineerasaservant@isurvivable.cv>
|
Thanks for the review, and for the call on the two questions. The identifier pattern. Applied. | `Identifier { iv = #Identifier.Signature.t_pv; _ }
| `Alias _ | `AliasModuleType _ | `Module _ | `Hidden _ | `ModuleType _ ->
render_resolved (r :> t) ^ "."It reads much better, and it keeps the exhaustiveness check rather than I left the remaining clauses explicitly matched, as you suggested. The two questions. Both left out of this PR, so polymorphic variant tags Changelog. I also filled in the Testing. CI. The red check is not this PR. Of the 50 lanes, 47 are green, the That is the nondeterministic FreeBSD builder fault tracked in |
Summary
Fixes #372. A reference to a constructor or to a record field is no longer
rendered with the name of its type spliced in:
{!Bla.Alpha}now renders asBla.Alpharather thanBla.ha.Alpha.Problem
Odoc_document.Comment.Reference.render_resolvedbuilds the text a referenceis displayed as by walking the resolved reference and joining every component
with a dot. For a constructor or a record field, one of those components is
the type, and the type is not part of the OCaml path of its own constructors:
Bla.ha.Alphais not syntax you can write, whereasBla.Alphais. As #372puts it, odoc should not invent OCaml syntax that does not exist.
The type name is spliced in even when the author never wrote it, so
{!Bla.Alpha}and{!Bla.ha.Alpha}both came out asBla.ha.Alpha.Fix
The four arms that name a member of a type (
`Constructor,`PolyConstructor,`Field,`UnboxedField) now render the pathof the parent of the type rather than the path of the type, via two small
helpers in the same recursive group. Everything else is untouched.
The enclosing module path is kept, so only the invented component goes away:
{!Bla.Alpha}Bla.ha.AlphaBla.Alpha{!Bla.ha.Alpha}Bla.ha.AlphaBla.Alpha{!t.Alpha}(same module)t.AlphaAlpha{!Alpha}(same module)AlphaAlpha{!Bla.Inner.i.Gamma}Bla.Inner.i.GammaBla.Inner.Gamma{!Bla.ra.fla}Bla.ra.flaBla.fla{!switch.`On}(same module)switch.`On`On{!Bla.Ext_a},{!Bla.Exn_a},{!Bla.ha}Anchors are not affected. The link target is built from
Reference.Resolved.identifierandUrl.from_identifier, a traversalindependent of the renderer, so a link whose text is now
Bla.Alphastillpoints at
#type-ha.Alpha. Text and anchor already diverged this way forenvironment-resolved references before this PR (
github_issue_447.trenderedFoowhile linking#type-u.Foo); this generalises that.Fields of the inline record of an extension constructor or of an exception are
parented by the enclosing signature rather than by a type, and keep that
parent.
Two things I would like your call on
Polymorphic variant tags reached through a module.
{!Bla.sw.`On}now renders
Bla.`On. Dropping the type is clearly right, but astructural tag is never module-qualified in OCaml either, so
Bla.`Onisnot valid syntax the way
Bla.Alphais. I kept the module prefix as alocator because it is the smaller, uniform change, and the common
same-module case does become exactly
`On. Rendering the bare taginstead is a one-line change if you prefer it.
Tooltips. When the author supplies their own text, the rendered
reference becomes the
title=tooltip, so tooltips shorten too. Makingthe tooltip keep the full path while the text is shortened would recover
the lost information, but it touches
to_irand would add atitle=tomany more links, so I left it out of this PR.
What is lost
{!Bla.Alpha}and{!Bla.ha.Alpha}now render identically, and two types ofone module sharing a constructor name render identically (only the anchor
distinguishes them). That is inherent to the fix rather than incidental, so
the new test pins it explicitly with an
Ambiguousmodule. Nothing is lostthat was not already dropped for
{!Alpha}, and the anchor still carries thetype.
Testing
test/xref2/github_issue_372.t, covering: the issue's case;the same reference with the type spelled out; same-module constructor and
field references, both bare and type-qualified; a record field; polymorphic
tags with a module parent; a nested module path; extension constructor,
exception and type references (which must not change); two types sharing a
constructor name; a reference with replacement text (the tooltip path); and
an unresolved class-parented reference (which must stay plain text).
github_issue_447.t(u.FootoFoo,M.t.FootoM.Foo) andreference_to_polymorphic.t(switch.`Onto`On).dune runtest --forceon OCaml 5.3.0: 101 passed, 0 failed, including thesherlodoc lane. No other golden in the tree changes; in particular
test/generators,test/searchandtest/occurrencesare byte-identical,since the search index builds its names from
Identifier.fullnameratherthan from this renderer.
render_resolvedhunk turns the newtest red on the constructor, field and polymorphic lines while leaving the
environment-resolved, extension, exception and unresolved lines green.
Reverting only the
`Fieldarm turns exactly the field lines red.ocamlformat0.27.0 (the pinned version) is clean.One gap worth naming: the
`UnboxedFieldarm is changed by symmetry butis not exercised, because nothing in the test corpus declares an OxCaml
unboxed record and the case needs an OxCaml toolchain. It shares
render_datatype_parentwith the`Constructorarm, whose behaviour ispinned, and
UnboxedFieldParent.tis the same two-arm sum asDataType.t.Happy to add an
%{ocaml-config:ox}-gated cram case if you would rather haveone.
Drafted with AI assistance; I reviewed, built and tested every change
myself.