Skip to content

Add new invalid_markdown_table rustdoc lint - #159583

Open
GuillaumeGomez wants to merge 6 commits into
rust-lang:mainfrom
GuillaumeGomez:unescaped_pipe_in_table_cell
Open

Add new invalid_markdown_table rustdoc lint#159583
GuillaumeGomez wants to merge 6 commits into
rust-lang:mainfrom
GuillaumeGomez:unescaped_pipe_in_table_cell

Conversation

@GuillaumeGomez

@GuillaumeGomez GuillaumeGomez commented Jul 19, 2026

Copy link
Copy Markdown
Member

@rustbot rustbot added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-rustdoc Relevant to the rustdoc team, which will review and decide on the PR/issue. labels Jul 19, 2026
@rust-log-analyzer

This comment has been minimized.

Comment thread tests/rustdoc-ui/lints/invalid_markdown_table.rs
Comment thread src/librustdoc/passes/lint/invalid_markdown_table.rs Outdated
Comment thread src/librustdoc/passes/lint/table_pipe_escape.rs Outdated
Comment thread src/librustdoc/passes/lint/table_pipe_escape.rs Outdated
Comment thread src/librustdoc/passes/lint/table_pipe_escape.rs Outdated
Comment thread src/librustdoc/passes/lint/table_pipe_escape.rs Outdated
Comment thread src/librustdoc/passes/lint/invalid_markdown_table.rs Outdated
@Urgau Urgau added S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Jul 20, 2026
@GuillaumeGomez
GuillaumeGomez force-pushed the unescaped_pipe_in_table_cell branch from 045f5a3 to 6fd9b1b Compare July 21, 2026 12:16
@GuillaumeGomez

Copy link
Copy Markdown
Member Author

Applied comments.

@Urgau Urgau left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Implementation looks good to me.

View changes since this review

@Urgau Urgau added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. and removed S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. labels Jul 21, 2026
@Urgau

This comment was marked as outdated.

@rust-rfcbot

This comment was marked as resolved.

@Urgau

Urgau commented Jul 21, 2026

Copy link
Copy Markdown
Member

2nd try. Let's do an FCP.

unescaped_pipe_in_table_cell details

unescaped_pipe_in_table_cell

This lint is warn-by-default. It detects unescaped pipes (|) in table rows which
lead to some row cells being ignored. For example:

//! | col1 |
//! | ---- |
//! | `code_with(|arg| arg)` |

Which will give:

error: table row has too many columns
  --> $DIR/unescaped_pipe_in_table_cell.rs:5:18
   |
5  | //! | `code_with(|arg| arg)` |
   |                  ^ help: any content after this column divider is discarded
   |
   = help: to escape `|` characters in tables, add a `\` before them like `\|`
note: the lint level is defined here
  --> $DIR/unescaped_pipe_in_table_cell.rs:1:9
   |
1  | #![deny(rustdoc::unescaped_pipe_in_table_cell)]
   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

@rfcbot merge rustdoc-internals

@rust-rfcbot

rust-rfcbot commented Jul 21, 2026

Copy link
Copy Markdown
Collaborator

@Urgau has proposed to merge this. The next step is review by the rest of the tagged team members:

Concerns:

Once a majority of reviewers approve (and at most 2 approvals are outstanding), this will enter its final comment period. If you spot a major issue that hasn't been raised at any point in this process, please speak up!

See this document for info about what commands tagged team members can give me.

@rust-rfcbot rust-rfcbot added proposed-final-comment-period Proposed to merge/close by relevant subteam, see T-<team> label. Will enter FCP once signed off. disposition-merge This issue / PR is in PFCP or FCP with a disposition to merge it. labels Jul 21, 2026
@Urgau Urgau added S-waiting-on-fcp Status: PR is in FCP and is awaiting for FCP to complete. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Jul 21, 2026
```rust
//! | col1 |
//! | ---- |
//! | `code_with(|arg| arg)` |

@camelid camelid Jul 21, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Wow TIL that this is how GFM works. Very strange design...

View changes since the review

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Yeah I was super confused as well.

Comment thread src/librustdoc/passes/lint/table_pipe_escape.rs Outdated
Comment thread tests/rustdoc-ui/lints/invalid_markdown_table.rs
Comment thread tests/rustdoc-ui/lints/unescaped_pipe_in_table_cell.rs Outdated
@notriddle

Copy link
Copy Markdown
Contributor

Minor nits about the implementation, but massive approval for the lint in concept!

@camelid

camelid commented Jul 23, 2026

Copy link
Copy Markdown
Member

Definitely in favor of the lint itself, but I wonder if we should have a more generic lint name and then we group together this and any related (current or future) lints about Markdown gotchas? E.g. we have rustdoc::invalid_html_tags that groups together anything related to that, including (soon: #158709) issues when HTML is interleaved with Markdown.

For example, it could be rustdoc::invalid_markdown, or rustdoc::markdown_gotchas, or a Better Name (TM). I could even imagine us renaming rustdoc::invalid_html_tags to something more general like rustdoc::invalid_markdown and including this as part of it.

On a related note, my feeling with #158709 is that additions of new cases to an existing warn-by-default lint do not require FCP since they are not part of stability guarantees, but please let me know if you disagree.

@camelid

camelid commented Jul 23, 2026

Copy link
Copy Markdown
Member

@rfcbot reviewed
@rfcbot concern overly specific lint name

@GuillaumeGomez

Copy link
Copy Markdown
Member Author

@camelid: Very fair point. I think having a lint group for all such lints is a good idea. As for the current lint name, I'll rename it as invalid_markdown_table.

@notriddle

Copy link
Copy Markdown
Contributor

Sure.

@GuillaumeGomez

Copy link
Copy Markdown
Member Author

Nice, thanks!

@camelid: Is your concern resolved?

@camelid

camelid commented Jul 25, 2026

Copy link
Copy Markdown
Member

Well, I would prefer the lint name to be even more general like invalid_markdown, and maybe be merged with invalid_html_tags (superseding it). What do you think?

I can't think of a case where people would want to control them separately. It seems more likely they'd want all these lints to be either enabled or disabled rather than one-by-one controlling them.

@notriddle

Copy link
Copy Markdown
Contributor

I can’t think of a case where anyone would want to disable this table lint at all.

The HTML lint, however, you might disable it if you intentionally rely on HTML tag correction. The rules are spelled out in the spec, after all, and they allow you to write more terse markup.

@GuillaumeGomez

Copy link
Copy Markdown
Member Author

invalid_markdown should be a lint group, I donnt think we should merge both lints.

@camelid

camelid commented Jul 25, 2026

Copy link
Copy Markdown
Member

Based on @notriddle's comment, I propose we rename this lint to just invalid_markdown (rather than having it be a lint group) but leave it separate from the invalid_html_tags lint.

@GuillaumeGomez

Copy link
Copy Markdown
Member Author

It's too general: we already have other "invalid markdown" specialized lints. If we want to make a group out of them, that's a good name for the group. But otherwise I think it's too general.

@camelid

camelid commented Jul 25, 2026

Copy link
Copy Markdown
Member

What other invalid markdown lints do we have? All of the lints I see are either specific to rustdoc features (like invalid Rust code blocks) or stylistic issues (redundant explicit links). https://doc.rust-lang.org/rustdoc/lints.html

That said, I'm not going to block on this since ultimately it's a matter of taste. I would just prefer not creating too many overly specific lints -- like I can't imagine why someone would want to control linting of tables separately from other markdown features, unless they were doing something really unusual.

@GuillaumeGomez

GuillaumeGomez commented Jul 25, 2026

Copy link
Copy Markdown
Member Author

I see at least bare_urls and invalid_html_tags. And once we're done with #137858, there will be a new one.

EDIT: copied the wrong lint name, replaced it.

@camelid

camelid commented Jul 25, 2026

Copy link
Copy Markdown
Member

Right, but bare_urls doesn't actually indicate invalid Markdown. The other two are closer to that though. Anyway, I'll resolve my concern. I'm fine with doing a lint group for these like you proposed, even though I'd rather not separate them at all.

@rfcbot resolve overly specific lint name

@rust-rfcbot rust-rfcbot added final-comment-period In the final comment period and will be merged soon unless new substantive objections are raised. and removed proposed-final-comment-period Proposed to merge/close by relevant subteam, see T-<team> label. Will enter FCP once signed off. labels Jul 27, 2026
@rust-rfcbot

Copy link
Copy Markdown
Collaborator

🔔 This is now entering its final comment period, as per the review above. 🔔

@rust-rfcbot rust-rfcbot added finished-final-comment-period The final comment period is finished for this PR / Issue. S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. and removed final-comment-period In the final comment period and will be merged soon unless new substantive objections are raised. S-waiting-on-fcp Status: PR is in FCP and is awaiting for FCP to complete. labels Aug 6, 2026
@rust-rfcbot

Copy link
Copy Markdown
Collaborator

The final comment period, with a disposition to merge, as per the review above, is now complete.

As the automated representative of the governance process, I would like to thank the author for their work and everyone else who contributed.

@GuillaumeGomez

Copy link
Copy Markdown
Member Author

Thanks everyone!

@bors r=Urgau,notriddle,camelid rollup

@rust-bors

rust-bors Bot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

📌 Commit 3c70378 has been approved by Urgau,notriddle,camelid

It is now in the queue for this repository.

🌲 The tree is currently closed for pull requests below priority 6. This pull request will be tested once the tree is reopened.

Reason for tree closure: Github problems

@rust-bors rust-bors Bot added S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Aug 6, 2026
jhpratt added a commit to jhpratt/rust that referenced this pull request Aug 7, 2026
…able_cell, r=Urgau,notriddle,camelid

Add new `invalid_markdown_table` rustdoc lint

Fixes rust-lang#159186.

r? @Urgau
@jhpratt

jhpratt commented Aug 7, 2026

Copy link
Copy Markdown
Member

Looks like there's some pre-existing tests that need to be updated.

#160661 (comment)

@bors r-

208 
+ warning: table row has too many columns
+   --> $DIR/invalid-html-tags.rs:294:24
+    |
+ LL | /// | <i>`one|two`</i> | `three|four` |
+    |                        ^ any content after this column divider is discarded
+    |
+    = help: to escape `|` characters in tables, add a `\` before them like `\|`
+    = note: `#[warn(rustdoc::invalid_markdown_table)]` on by default
+ 
209 error: unopened HTML tag `div`
210   --> $DIR/invalid-html-tags.rs:304:5
211    |

216 LL | /// </div>
217    |     ^^^^^^ this unopened tag
218 
- error: aborting due to 31 previous errors
+ error: aborting due to 31 previous errors; 1 warning emitted
220 
221 

Note: some mismatched output was normalized before being compared
-   --> /checkout/tests/rustdoc-ui/lints/invalid-html-tags.rs:294:24
+ warning: table row has too many columns
+   --> $DIR/invalid-html-tags.rs:294:24
+    |
+ LL | /// | <i>`one|two`</i> | `three|four` |
+    |                        ^ any content after this column divider is discarded
+    |
+    = help: to escape `|` characters in tables, add a `\` before them like `\|`
+    = note: `#[warn(rustdoc::invalid_markdown_table)]` on by default
+ 
+ error: aborting due to 31 previous errors; 1 warning emitted


The actual stderr differed from the expected stderr
To update references, rerun the tests and pass the `--bless` flag
To only update this specific test, also pass `--test-args lints/invalid-html-tags.rs`

error: 1 errors occurred comparing output.
status: exit status: 1
command: env -u RUSTC_LOG_COLOR RUSTC_ICE="0" RUST_BACKTRACE="short" "/checkout/obj/build/x86_64-unknown-linux-gnu/stage1/bin/rustdoc" "/checkout/tests/rustdoc-ui/lints/invalid-html-tags.rs" "-Zsimulate-remapped-rust-src-base=/rustc/FAKE_PREFIX" "-Ztranslate-remapped-path-to-local-path=no" "-Z" "ignore-directory-in-diagnostics-source-blocks=/cargo" "-Z" "ignore-directory-in-diagnostics-source-blocks=/checkout/vendor" "--sysroot" "/checkout/obj/build/x86_64-unknown-linux-gnu/stage1" "--target=x86_64-unknown-linux-gnu" "--check-cfg" "cfg(test,FALSE)" "--error-format" "json" "--json" "future-incompat" "-Ccodegen-units=1" "-Zui-testing" "-Zdeduplicate-diagnostics=no" "-Zwrite-long-types-to-disk=no" "-Cstrip=debuginfo" "-o" "/checkout/obj/build/x86_64-unknown-linux-gnu/test/rustdoc-ui/lints/invalid-html-tags" "-A" "internal_features" "-A" "incomplete_features" "-A" "unused_parens" "-A" "unused_braces" "-Cdebuginfo=0"
stdout: none
--- stderr -------------------------------
error: unclosed HTML tag `p`
##[error]  --> /checkout/tests/rustdoc-ui/lints/invalid-html-tags.rs:4:5
   |
LL | //! <p>💩<p>
   |     ^^^
   |
note: the lint level is defined here
  --> /checkout/tests/rustdoc-ui/lints/invalid-html-tags.rs:1:9
   |
LL | #![deny(rustdoc::invalid_html_tags)]
   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^

error: unclosed HTML tag `p`
##[error]  --> /checkout/tests/rustdoc-ui/lints/invalid-html-tags.rs:4:9
   |
LL | //! <p>💩<p>
   |          ^^^

error: unclosed HTML tag `unknown`
##[error]  --> /checkout/tests/rustdoc-ui/lints/invalid-html-tags.rs:12:5
   |
LL | /// <unknown>
   |     ^^^^^^^^^

error: unclosed HTML tag `script`
##[error]  --> /checkout/tests/rustdoc-ui/lints/invalid-html-tags.rs:15:5
   |
LL | /// <script>
   |     ^^^^^^^^

error: unopened HTML tag `hello`
##[error]  --> /checkout/tests/rustdoc-ui/lints/invalid-html-tags.rs:25:5
   |
LL | /// </hello>
   |     ^^^^^^^^

---

error: unclosed HTML tag `p`
##[error]  --> /checkout/tests/rustdoc-ui/lints/invalid-html-tags.rs:30:14
   |
LL | ///    <br/> <p>
   |              ^^^

error: incomplete HTML tag `script`
##[error]  --> /checkout/tests/rustdoc-ui/lints/invalid-html-tags.rs:46:5
   |
LL | /// <script
   |     ^^^^^^^

error: unclosed HTML tag `div`
##[error]  --> /checkout/tests/rustdoc-ui/lints/invalid-html-tags.rs:42:5
   |
LL | /// <div style="hello">
   |     ^^^^

error: unclosed HTML tag `h3`
##[error]  --> /checkout/tests/rustdoc-ui/lints/invalid-html-tags.rs:44:7
   |
LL | ///   <h3>
   |       ^^^^

error: incomplete HTML tag `div`
##[error]  --> /checkout/tests/rustdoc-ui/lints/invalid-html-tags.rs:74:10
   |
LL | /// <div></div
   |          ^^^^^

error: unclosed HTML tag `div`
##[error]  --> /checkout/tests/rustdoc-ui/lints/invalid-html-tags.rs:74:5
   |
LL | /// <div></div
   |     ^^^^^

error: Unclosed HTML comment
##[error]  --> /checkout/tests/rustdoc-ui/lints/invalid-html-tags.rs:89:5
   |
LL | /// <!--
   |     ^^^^

error: unopened HTML tag `unopened-tag`
##[error]  --> /checkout/tests/rustdoc-ui/lints/invalid-html-tags.rs:116:26
   |
LL | /// Web Components style </unopened-tag>
   |                          ^^^^^^^^^^^^^^^

error: unclosed HTML tag `dashed-tags`
##[error]  --> /checkout/tests/rustdoc-ui/lints/invalid-html-tags.rs:114:26
   |
LL | /// Web Components style <dashed-tags>
   |                          ^^^^^^^^^^^^^

error: unclosed HTML tag `a`
##[error]  --> /checkout/tests/rustdoc-ui/lints/invalid-html-tags.rs:123:19
   |
LL | /// backslashed \<<a href="">
   |                   ^^

error: incomplete HTML tag `img`
##[error]  --> /checkout/tests/rustdoc-ui/lints/invalid-html-tags.rs:157:5
   |
LL | /// <img
   |     ^^^^

error: incomplete HTML tag `img`
##[error]  --> /checkout/tests/rustdoc-ui/lints/invalid-html-tags.rs:162:8
   |
LL | /// <p><img</p>
   |        ^^^^

error: incomplete HTML tag `img`
##[error]  --> /checkout/tests/rustdoc-ui/lints/invalid-html-tags.rs:167:7
   |
LL |   /// > <img
   |  _______^
LL | | /// > href="#broken"
   | |____________________^

error: incomplete HTML tag `br`
##[error]  --> /checkout/tests/rustdoc-ui/lints/invalid-html-tags.rs:173:5
   |
LL | /// <br<br>
   |     ^^^

error: incomplete HTML tag `br`
##[error]  --> /checkout/tests/rustdoc-ui/lints/invalid-html-tags.rs:178:5
   |
LL | /// <br
   |     ^^^

error: incomplete HTML tag `p`
##[error]  --> /checkout/tests/rustdoc-ui/lints/invalid-html-tags.rs:211:5
   |
LL | /// <p <!-->foobar</p>
   |     ^^^

error: Unclosed HTML comment
##[error]  --> /checkout/tests/rustdoc-ui/lints/invalid-html-tags.rs:211:8
   |
LL | /// <p <!-->foobar</p>
   |        ^^^^

error: unopened HTML tag `span`
##[error]  --> /checkout/tests/rustdoc-ui/lints/invalid-html-tags.rs:216:36
   |
LL | /// *Improperly <span> nested* HTML</span>
   |     --------------------------     ^^^^^^^ this unopened tag
   |     |           |
   |     |           does not match this unclosed tag
   |     because of this Markdown emphasis

error: improperly nested Markdown emphasis
##[error]  --> /checkout/tests/rustdoc-ui/lints/invalid-html-tags.rs:223:22
   |
LL | /// <span>Improperly *nested </span> HTML*
   |     -----------------^^^^^^^^^^^^^^^^^^^^^
   |     |
   |     because of this HTML `span`

error: unopened HTML tag `span`
##[error]  --> /checkout/tests/rustdoc-ui/lints/invalid-html-tags.rs:228:43
   |
LL | /// <em>Improperly <span> nested</em> HTML</span>
   |     ---------------------------------     ^^^^^^^ this unopened tag
   |     |              |
   |     |              does not match this unclosed tag
   |     because of this HTML `em`

error: unopened HTML tag `em`
##[error]  --> /checkout/tests/rustdoc-ui/lints/invalid-html-tags.rs:237:10
   |
LL | /// Improperly <em> nested
   |                ---- does not match this unclosed tag
LL | ///
   | - because the Markdown paragraph ends here
LL | /// HTML </em> emphasis
   |          ^^^^^ this unopened tag

error: unopened HTML tag `svg`
##[error]  --> /checkout/tests/rustdoc-ui/lints/invalid-html-tags.rs:285:5
   |
LL | /// <svg viewBox="0 0 10 10">
   |     ---- does not match this unclosed tag
...
LL | /// >
   |     - because the Markdown paragraph is interrupted by this block quote
LL | /// </svg>
   |     ^^^^^^ this unopened tag

error: unopened HTML tag `i`
##[error]  --> /checkout/tests/rustdoc-ui/lints/invalid-html-tags.rs:294:19
   |
LL | /// | <i>`one|two`</i> | `three|four` |
   |       ---    -    ^^^^ this unopened tag
   |       |      |
   |       |      because the Markdown table cell ends here
   |       does not match this unclosed tag

warning: table row has too many columns
##[warning]  --> /checkout/tests/rustdoc-ui/lints/invalid-html-tags.rs:294:24
   |
LL | /// | <i>`one|two`</i> | `three|four` |
   |                        ^ any content after this column divider is discarded
   |
   = help: to escape `|` characters in tables, add a `\` before them like `\|`
   = note: `#[warn(rustdoc::invalid_markdown_table)]` on by default

error: unopened HTML tag `div`
##[error]  --> /checkout/tests/rustdoc-ui/lints/invalid-html-tags.rs:304:5
   |
LL | /// | <div> | foo |
   |       ----- - because the Markdown table cell ends here
   |       |
   |       does not match this unclosed tag
LL | /// </div>
   |     ^^^^^^ this unopened tag

error: aborting due to 31 previous errors; 1 warning emitted
------------------------------------------

---- [ui] tests/rustdoc-ui/lints/invalid-html-tags.rs stdout end ----

@rust-bors rust-bors Bot added S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. and removed S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. labels Aug 7, 2026
@rust-bors

rust-bors Bot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

This pull request was unapproved.

This PR was contained in a rollup (#160661), which was unapproved.

View changes since this unapproval

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

Labels

disposition-merge This issue / PR is in PFCP or FCP with a disposition to merge it. finished-final-comment-period The final comment period is finished for this PR / Issue. S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. T-rustdoc Relevant to the rustdoc team, which will review and decide on the PR/issue. to-announce Announce this issue on triage meeting

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Markdown tables can silently discard content containing |

8 participants