rumdl 0.2.78

A fast Markdown linter and formatter written in Rust
Documentation
---
description: "Lint and format the Markdown inside Rust doc comments (`///` and `//!`) in .rs files, with fixes written back behind the original comment prefixes."
---

# 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`:

```rust
//! #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:

```text
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:

```rust
//! # 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:

```bash
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:

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

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

```bash
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-settings.md#include):

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

!!! warning "`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`:

```yaml
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](md025.md) | Several H1 headings (`# Errors`, `# Examples`, `# Safety`) are standard in rustdoc          |
| [MD033](md033.md) | HTML such as `<div class="warning">` is rustdoc syntax for warning blocks                   |
| [MD040](md040.md) | Rustdoc treats an unlabeled code block as Rust                                              |
| [MD041](md041.md) | A doc block is not a standalone document, so it need not start with a heading               |
| [MD047](md047.md) | Same reason: a doc block does not end a file                                                |
| [MD051](md051.md) | Anchors such as `#method.bar` and `#structfield.name` are rustdoc targets, not headings     |
| [MD052](md052.md) | Intra-doc links such as `[crate::io]` are resolved by rustdoc, not by reference definitions |
| [MD054](md054.md) | 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`](global-settings.md#per-file-ignores):

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

## Line length

[MD013](md013.md) 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](code-block-tools.md) 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]lsp.md 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.