Expand description
Where a "…" literal ends and where its holes are — one answer, for
both readers (§8.1, ADR-147).
A { inside a text literal opens an interpolation hole holding a full
expression (ADR-147 decision 1), so "Part 2: {part2}" is not one token: the
lexer splits it into fragment tokens with the hole’s ordinary tokens between
them, which is what gives a name inside a hole a real range in the lossless
tree and therefore makes it a closure capture.
Two readers have to agree about the extent of such a run: text_end, which
the lexer asks before it emits anything, and fragment_end, which the
lexer asks again each time a hole closes and it has to resume scanning text.
They are the same rule read from two starting points, so they live in one
module and call one another rather than each owning a copy of it — see
template’s doc.
§The rule
A text literal ends at the line it opens on; crate::template gives
backtick templates the same bound. Within a literal:
\hides the next scalar, so"\""is one literal and"\{"is a literal brace (crate::literal::decode_escapeowns which escapes mean what; this module only needs to know that a backslash consumes one scalar).{opens a hole. A}in ordinary text closes nothing and is literal — the asymmetry is deliberate, because outside a hole there is nothing for a}to be ambiguous with.- Inside a hole the scanner reads expression source: braces nest, and a
"…", a'…', a`…`and a/* … */are each skipped whole, because each of them can hold a}that is not structure. A nested"…"is skipped by re-enteringtext_end, so"{f("{y}")}"is one literal containing another. - A
//inside a hole means the rest of the line is a comment, so the literal cannot close on its line: that is an unterminated literal, and it is reported as one.
§Why an unterminated run is the whole answer
TextEnd::Unterminated is not a detail of error reporting. The lexer only
enters interpolation mode — the brace-depth stack that decides whether a }
closes a hole or a block — for a literal this module has already proved
closes. So there is no path on which a newline or an EOF reaches that stack,
and an unterminated literal is one TextLit token plus T004 (ADR-147
decision 5).
That is also why nesting past MAX_INTERPOLATION_NESTING answers
Unterminated rather than “stop treating quotes as structure”. Refusing to
enter is a bound both readers observe by construction; a bound that changed
what a quote means would put the lexer’s resume path and this scanner on
different rules at exactly the depth nobody tests.
Enums§
- Fragment
End - What ends a run of literal text inside a
"…". - TextEnd
- Where a
"…"literal ends, and whether it has any holes.
Functions§
- fragment_
end - Find where the run of literal text starting just past
atends. - text_
end - Find the end of the
"…"literal whose opening quote is atopen, and the first hole in it.