Skip to content

Rust Doc Comments

rumdl lints the Markdown inside Rust doc comments. Each /// and //! block in a .rs file is checked as Markdown, rules that clash with rustdoc conventions are skipped, and rumdl fmt writes fixes back behind the original comment prefixes without touching the Rust code.

Given this src/lib.rs:

//! #Parsing utilities
//! Helpers for  reading input.

/// Parses `input`.
/// # Errors
/// Returns [`ParseError`] when `input` is empty.
pub fn parse(input: &str) -> Result<(), ParseError> {
    todo!()
}

rumdl check src/lib.rs reports positions in the .rs file:

src/lib.rs:1:6: [MD018] No space after # in heading
src/lib.rs:2:16: [MD064] Multiple consecutive spaces (2) found
src/lib.rs:5:5: [MD022] Expected 1 blank line above heading
src/lib.rs:5:5: [MD022] Expected 1 blank line below heading

and rumdl fmt src/lib.rs rewrites the doc comments:

//! # Parsing utilities
//!
//! Helpers for reading input.

/// Parses `input`.
///
/// # Errors
///
/// Returns [`ParseError`] when `input` is empty.
pub fn parse(input: &str) -> Result<(), ParseError> {
    todo!()
}

Enabling it

A plain rumdl check . discovers Markdown files only, so Rust files have to be selected.

On the command line

A file named on the command line is always linted:

rumdl check src/lib.rs
rumdl fmt src/lib.rs src/parser.rs

A directory named on the command line is searched for Markdown files only, so rumdl check src/ finds no Rust files. For a run over a tree, --include takes comma-separated patterns:

rumdl check . --include "**/*.md,src/**/*.rs"

Standard input works when --stdin-filename names a .rs file:

rumdl check --stdin --stdin-filename src/lib.rs < src/lib.rs

In your configuration

To lint doc comments on every run, add .rs files to include:

[global]
include = ["**/*.md", "**/*.rs"]

include replaces the default file selection

Without include, rumdl lints every Markdown extension it knows (.md, .markdown, .mdx, .qmd, .rmd and others). Once include is set, only files matching it are linted, so the example above stops checking .mdx or .markdown files. List a pattern for each extension your project uses. The same applies to --include.

With pre-commit

The rumdl-check and rumdl-fmt hooks only receive Markdown files. To pass Rust files to them as well, override both types and types_or:

repos:
  - repo: https://github.com/rvben/rumdl-pre-commit
    rev: vX.Y.Z
    hooks:
      - id: rumdl-check
        types: [file]
        types_or: [markdown, rust]

Setting types_or alone is not enough: pre-commit requires a file to match both, and the hook's own types: [markdown] still excludes .rs files.

What is linted

  • Line doc comments: outer /// and inner //!.
  • Consecutive lines of the same kind form one block, and each block is linted as a standalone Markdown document. A non-doc line, or a switch between /// and //!, starts a new block.
  • //// is a regular comment and is ignored, as are ordinary // comments and the Rust code itself.
  • ///content (no space), /// content and ///<tab>content are all recognized, as rustdoc accepts them.

Rules skipped in doc comments

Some rules conflict with rustdoc conventions and never run on doc comments:

Rule Why it is skipped
MD025 Several H1 headings (# Errors, # Examples, # Safety) are standard in rustdoc
MD033 HTML such as <div class="warning"> is rustdoc syntax for warning blocks
MD040 Rustdoc treats an unlabeled code block as Rust
MD041 A doc block is not a standalone document, so it need not start with a heading
MD047 Same reason: a doc block does not end a file
MD051 Anchors such as #method.bar and #structfield.name are rustdoc targets, not headings
MD052 Intra-doc links such as [crate::io] are resolved by rustdoc, not by reference definitions
MD054 The shortcut form [crate::module] is the canonical intra-doc link syntax

To turn off other rules for Rust files only, use per-file-ignores:

[per-file-ignores]
"**/*.rs" = ["MD013"]

Line length

MD013 measures the Markdown text of each line, after the indentation and the /// or //! prefix are removed. A doc comment on a method indented by four spaces can therefore run eight columns past line-length in the source file before it is flagged. Code blocks inside doc comments are not checked for length, since rustfmt formats that code.

Fixing

rumdl fmt and rumdl check --fix fix the Markdown in each block and restore every line's indentation and comment prefix. Lines a fix adds, such as the blank line MD022 inserts around a heading, get the block's indentation and a bare /// or //!.

Code block tools do not run on code blocks inside doc comments.

Limitations

  • Only lowercase .rs files are read this way.
  • Block doc comments (/** ... */ and /*! ... */) and #[doc = "..."] attributes are not extracted.
  • The language server and editor extensions do not lint doc comments; use the CLI or pre-commit.
  • The file must be valid UTF-8, as the Rust compiler requires.