leanprover / leanprover/verso

Automatically generated tags for headings are not deduplicated

Open Beginner friendly
#897 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Lean
Stars
384
Forks
124
Avg merge
22h 28m
Merged PRs (30d)
10

Description

Summary

When automatically generated tags for headings collide, freshTag should append a suffix to make them unique. Instead, this deduplication does not work, resulting in a "Duplicate tag" error during manual traversal.

In particular, this issue is easy to encounter with non-ASCII headings. Every unsupported character is converted to ___, so completely different headings can still collide if they contain the same number of unsupported characters, such as Japanese characters.

This occurs in Verso v4.29.0 and on the current main branch.

Steps to reproduce

Create the following Lean file in a Verso project:

import VersoManual
open Verso.Genre Manual

def main := manualMain <| Verso.Doc.VersoDoc.toPart <| #doc (Manual) "Test" =>
# Same
First.

# Same
Second.

Then run lake exe.

Expected behavior

Traversal succeeds. The generated tags are unique, for example Test--Same and Test--Same-0.

Actual behavior

lake exe exits with status 1 and reports:

Duplicate tag '_private.VersoManual.Basic.0.Verso.Genre.Manual.Tag.internal "Test--Same"'
An error was encountered!

lake build alone does not expose the problem because it compiles the document without running traversal.

Cause

In src/verso-manual/VersoManual/Basic.lean, freshTag checks whether the String candidate is already present:
https://github.com/leanprover/verso/blob/f91d46ca8b63be638b2f4fef2b1011012c540a41/src/verso-manual/VersoManual/Basic.lean#L420

Because attempt is a String, the Coe String Tag instance converts it to Tag.provided attempt. However, generated tags are inserted as Tag.internal:
https://github.com/leanprover/verso/blob/f91d46ca8b63be638b2f4fef2b1011012c540a41/src/verso-manual/VersoManual/Basic.lean#L424

The lookup therefore cannot find an existing generated tag. This appears to be a regression introduced by commit https://github.com/leanprover/verso/commit/e842684798df452fa30c7528e49be21b7a5d682c. That commit changed tagStr from producing a Tag.internal value to producing a plain String. The insertion was updated to wrap the string in Tag.internal, but the preceding lookup was not.

Suggested fix

Check for the same tag variant that is inserted:

if (← get).tags.contains (Tag.internal attempt) then

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start in src/verso-manual/VersoManual/Basic.lean at freshTag, where the issue identifies the mismatched tag lookup and insertion. Reproduce with the Lean document in the issue and run lake exe, since lake build does not traverse it. Done means colliding generated headings produce unique tags and traversal succeeds without a duplicate-tag error.

Written by the indexing model from the issue text.

Assessment

Domain
documentation
Issue type
Bug
Difficulty
2/5
Estimated time
1-3 hours
Activity status
Quiet
Clarity
Clearly specified
Newbie friendliness
85/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.