dartDocComments function
Lifts the /// doc comments of source out of the Dart around them.
A Dart doc comment is markdown, so the resolver needs no second set of
rules — it needs the markdown handed to it without the ///. Two properties
make the lift worth doing rather than scanning the raw file:
- Line count is preserved, so a violation is still reported at the line a reader will open.
- Non-doc lines become empty, which does two jobs at once. It keeps
§inside a//note or a string literal out of the scan — neither is read by a reader following a citation — and it makes each declaration's doc comment its own block, so the lookback for a document name cannot reach back past the declaration above.
Without the lift, a citation whose document name wrapped onto the previous
line would read as bare: the lookback crosses a soft wrap but not a ///.
Every comment is lifted, // as well as ///, and that is a measured
reversal of an earlier position. The rule used to be /// only, on the
reasoning that a // note is implementation commentary rather than
something a reader follows as documentation. The corpus disagrees with that
reading of itself: it carries 492 §-citing // lines, and 362 of
them already name their document — authors were applying the citation
convention there voluntarily, in comments no gate has ever read. A rule that
called them undocumentation would have been describing a practice nobody
follows.
It also removed an asymmetry that had no defence: validator.dart cites
tom_specs_model_rules.md §10.2 from both a /// and a // comment, and
only the first was held. The same citation, in the same file, gated or not
by which marker it happened to be written under.
A /** */ block is still not lifted — no Dart file in the corpus uses one,
so it would be brace tracking for no reader.
The lift is a parse, not a line scan, and that becomes load-bearing the
moment test/ is in scope. A test that exercises this gate writes its
fixtures as Dart string literals whose contents are /// lines — that is
what a fixture for a doc-comment scanner has to look like. A line scanner
reads those as doc comments of the file containing them, so every
deliberately-malformed fixture is reported as a defect of the test, and the
line-scoped exhibit marker cannot excuse them: the marker sits at the
fixture's own line numbers, not the file's. Asking the analyzer which tokens
are actually doc comments is what makes the third paragraph above true —
it claimed to keep a string literal out of the scan and did not.
Implementation
String dartDocComments(String source) {
final ParseStringResult parsed;
try {
parsed = parseString(content: source, throwIfDiagnostics: false);
} on Object {
return _dartDocCommentsByLine(source);
}
final lines = List<String>.filled(source.split('\n').length, '');
final lineInfo = parsed.lineInfo;
final seen = <int>{};
for (
Token? token = parsed.unit.beginToken;
token != null && token.type != TokenType.EOF;
token = token.next
) {
for (
Token? comment = token.precedingComments;
comment != null;
comment = comment.next
) {
if (!seen.add(comment.offset)) continue;
// Every comment form, not `///` alone. Taking them from the parse
// rather than by line is what keeps a `//` inside a string literal out:
// the analyzer says which tokens are comments, and a literal is not one.
if (!comment.lexeme.startsWith('//')) continue;
final line = lineInfo.getLocation(comment.offset).lineNumber - 1;
if (line < 0 || line >= lines.length) continue;
lines[line] = _docLine.firstMatch(comment.lexeme)?.group(1) ?? '';
}
}
return lines.join('\n');
}