Skip to content

dartdoc comment references

Sam Rawlins edited this page Apr 5, 2022 · 10 revisions

Dartdoc comment references

A "comment reference" in dartdoc is an expression wrapped by brackets within a documentation comment. They have a syntax similar to Dart expressions and are interpreted by dartdoc to create links to different parts of the documentation. The behavior is largely unspecified in the Dart language and so, this document describes Dartdoc's interpretation.

Syntax

The syntax of a documentation comment often looks like a simplified version of a Dart expression. Dartdoc's grammar for them is as follows:

<rawCommentReference> ::= <prefix>?<commentReference><suffix>?

<prefix> ::= <constructorPrefixHint>
  | <leadingJunk>

<suffix> ::= <callablePostfixHint>
  | <trailingJunk>

<leadingJunk> ::= ('const' | 'final' | 'var')(<whitespace>+)

<trailingJunk> ::= '('<notClosedParenthesis>*')'
  | '<'<notGreaterThan>*'>'
  | '?'
  | '!'
  | <whitespace>*

<constructorPrefixHint> ::= 'new '

<callableHintSuffix> ::= '()'

<commentReference> ::= <globalScopeReference>? <dartdocIdentifier> <typeArguments>? ('.' <identifier> <typeArguments>?)*

<globalScopeReference> ::= (<packageName> '.')? (<libraryName> '.')

<dartdocIdentifier> ::= <dartIdentifier>
  | ('operator' <whitespace>*)?<operator>

A rawCommentReference includes all text between the two brackets, passed into dartdoc via the Markdown parser which interprets documentation comments generally.

The prefix and suffix include extra, optional characters that are allowed to either make the documentation comment more readable in prose, or to pass information to Dartdoc on what kind of identifier should be resolved.

The constructorPrefix, if present, signals to Dartdoc that we should resolve the comment reference exclusively to a constructor, to disambiguate a reference from a type of the same name.

The callableSuffix, if present, signals to Dartdoc to resolve the comment reference to callable items, including constructors. This can be used to disambiguate constructors, or simply to restrict resolution to a function or method.

leadingJunk and trailingJunk are accepted by the parser, but ignored, if present.

The commentReference isolated from the prefix/suffix, consists of a period-separated list of dartdoc identifiers. It can optionally include globalScopeReference, with a package name and library name, or just a library name, to further specify what the reference refers to. Some examples: foo, ClassName.foo, libName.ClassName.foo, and even packageName.libName.ClassName.foo.

libraryName is the name of the library a symbol is defined in or exposed in via reexporting. Because library names can contain periods, using them can create ambiguity in parsing, with multiple possible parses for a single commentReference. See below for how this is handled.

packageName is the name of a Dart "package", which can be the Dart SDK, a pub or pub-like package defined by a pubspec.yaml file, or another package type if dartdoc is extended to support it. Like libraryName, this can create ambiguities in parsing. See below for how this is handled.

A dartdocIdentifier is different from a Dart identifier in that it can include the word operator, some intervening whitespace, and a Dart operator, itself.

Basic Syntax

Here are some rawCommentReference examples (with the enclosing brackets for clarity) along with descriptions of how Dartdoc will parse them.

  • [anIdentifier] : This is just a single dartdocIdentifier with nothing special about it.
  • [AThing.AnotherThing] : This is parsed as two dartdocIdentifiers chained together.
  • [Foo.operator +] : Two dartdocIdentifiers chained together again.
  • [var libName.topLevelName.memberName] : The var is treated as leadingJunk, and this is three identifiers together.
  • [packageName.topLevelName.functionName()] : Three identifiers chained together, but the parser notes that we have a callablePostfixHint.
  • [new ClassName.ConstructorName()] : Two identifiers chained together, the parser noting the presence of callablePostFixHint and constructorPrefixHint.

Ambiguous Syntax Handling

Some aspects that really should be handled in the parser are instead handled at evaluation time due to the ambiguity introduced by library and package names allowing periods. There is no way of knowing up front whether a reference might eventually be evaluated as a library name containing multiple names.

A library can therefore be ambiguous with its own members in terms of naming. Does foo.Thing.AnotherThing refer to a library literally named foo.Thing.AnotherThing, a top level variable named AnotherThing in the library named foo.Thing, or a member of class Thing named AnotherThing in library Foo, and so on. Dartdoc answers this question by not answering it, and searching every possibility in sequence until it finds a match. While convenient for users who therefore don't have to worry about these ambiguities unless there is a problem, this is likely to change in the future as ambiguous parsing is confusing when there is a problem.

Dartdoc always tries interpreting as though package names and library names do not contain periods, first. So, it is recommended to avoid creating package names and libraries with periods in it until this ambiguity issue can be addressed with a syntax change in a future version of dartdoc.

Evaluation

Dartdoc evaluates comment references after parsing to attempt to match the comment reference to part of the public or private API surface -- or a library or package. If found and public, that will result in a hyperlink being generated to the documentation page for the requested symbol, library, or package.

Simple evaluation

Dartdoc evaluates comment references to Dart symbols by traversing a hierarchy similar to the scopes of the Dart language. A package contains libraries, which contains classes, top level variables, enums, typedefs, extension methods, and so on. Dartdoc will search the scope where the comment reference is declared, first. For example, given a class B with a dartdoc containing a reference foo, dartdoc will first look to members of class B for foo. Then, dartdoc will search the enclosing library's scope for classes, enums, etc for symbols named foo. After that, dartdoc searches the package for libraries named foo.

Libraries are referred to by their declared name using the library directive, or for anonymous libraries, a name generated by dartdoc based on the filename (removing any .dart suffix). It is recommended to refer to documented symbols only via libraries with declared names.

Packages are referred to by the name defined in pubspec.yaml, or for the dart SDK, the name Dart.

Resolved vs Documented References

Exports, reexports, and scope

Categories and the Package Graph

Member variables

Parameters and Type Parameters

Callables

Constructors

Borrowing documentation via superchains

Ambiguous Evaluation

Disambiguation

Resolving errors

Unresolved doc references

Examples

  • Unnamed constructor: [MyClass.new]
  • Named constructors: [MyClass.namedConstructor]
library myLib;
final String myString = "hi";

String param;

class A {
  final String myString = "there";

  /// The lovely [A] class's [foo] method.
  /// [myString] - refers to A.myString
  /// [myLib.myString] - refers to the top level myString
  /// [param] - refers to the parameter of foo.
  /// [myLib.param] - refers to the top level param.
  void foo(int param) {}
}