ADR-134: /* … */ Is a Comment, It Nests, and It Is Not a Doc Comment

Status: Superseded by ADR-307 — its D1-D3 are D1-D3 there Date: September 19, 2026 Target Version: Nikaia 0.0.10 Related: ADR-296 (a grammar’s vocabulary page), Part I chapter 2 (a comment)

1. Context

A comment began with // and ran to the end of the line, and that was the only form: /* … */ did not parse. A line comment is enough for a sentence beside a statement and not for commenting out a block of code while debugging, for a paragraph above a function, or for a note inside an argument list — the three places a reader reaches for /* … */ and finds it in C, Rust, JavaScript and every language between.

2. Decisions

D1 — /* opens a comment that */ closes, across lines, anywhere whitespace may stand.

A block comment may stand wherever whitespace may: between any two tokens, inside an argument list, inside a grammar { … } block, across as many lines as it needs. It is whitespace to the parser, as a line comment is. A /* inside a string literal is text, not a comment; a // inside a block comment is comment, not a second comment.

D2 — Block comments nest.

/* a /* b */ c */ is one comment that ends at the second */, as in Rust: a block of code that already holds a block comment can be commented out without hunting for the inner close. An unclosed block comment is a parse error reported at the /* that opened it — the place the reader must fix, not the end of the file where the parser gave up.

D3 — /** … */ and /*! … */ are block comments and nothing more.

The language has no doc comment (ADR-291’s page says the same of ///), and this record does not add one by the back door: a comment that begins with two stars is a comment. What a doc comment would be — a sentence the ledger carries and the prompt bundle reads — is a decision of its own, in docs/open-decisions.md.

3. Consequences

4. What this does not decide

5. What is built

All of it. BLOCK_COMMENT stands beside COMMENT in WS, which is the only place a comment can be said — the generator puts WS between the tokens of every syntactic rule, so a block comment stands wherever whitespace may. It nests, because the step inside it tries the nested comment before the plain character. A /* inside a string literal is text with no help needed: STRING is a lexical rule, so no implicit whitespace runs between its characters and the comment rule is never asked. /** and /*! are comments, and nothing is carried into the generated Rust.

Two things the building settled that the record did not name.

The rule’s steps hand back a char they have no use for. Two alternatives of different value types make the generator write a unit conversion, which clippy::unused_unit refuses, so the nested arm answers with a space — which is what a comment is to the parser anyway.

The error at the opening is not the grammar’s to give. The cut fires where the input ran out, which is the end of the file: a comment that swallowed the rest of a program fails at its last byte, and a caret there points at nothing. The opening may be thousands of bytes behind. So the offset is found by scanning, on a parse that has already failed with */ missing, and the scan is the reading the lexer does — a " opens a string until its unescaped close, a // runs to the end of its line, /* and */ count against each other. The condition is the grammar’s own, which is what keeps the one thing the scan cannot read — a /* inside a dsl … eod or a grammar { … } block, where it is text — from turning a good message into a wrong one.

Six tests in crates/nikaia/tests/block_comments.rs: the nested comment inside an argument list, /* inside a string, a // inside a block comment (the case a scan that read line comments everywhere would get wrong, because the close is behind one), the unclosed comment at its opening and at the outer opening where a nested one is what is missing, /** and /*!, and division.

This site is open source. Improve this page.