Skip to content

Conversation

@szakarias
Copy link
Contributor

No description provided.

@szakarias
Copy link
Contributor Author

@sgrekhov
Copy link

The questions below are mainly questions about future versions of this spec.

  1. If a declaration is augmented how to write doc comment in this case?
/// Some doc comment about class C
class C {
  /// Some doc comments about the constructor
  C([int p]);
}

/// Another doc comment about class C
augment class C {
  /// Comment about the constructor
  augment C([int p = 0]) {
  // Augmentation added the defaulf value and the body
  }
}
  1. Is there a way to add doc coments for a primary constructor?
/// Some doc comment. Is it related to the class or to the constructor?
class C(var int x);

Copy link
Member

@srawlins srawlins left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤩 Very cool, very exciting!

This document does not cover:

* The implementation details of specific documentation tools (e.g., `dartdoc`, `analysis server`).
* The syntax and behavior of tool-specific directives (e.g., `@template`, `@canonicalFor`).
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would you consider these in-scope in a later version? It would be very beneficial for DAS and dartdoc to agree on how these function. And new ones can be specified like @snippet, @sample.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We definitely could! Or we can clean up directives.md and add new ones there.

### **2.1. Line-Based Doc Comments (Recommended standard)**

* Starts with `///`.
* All consecutive lines starting with `///` are treated as part of the same comment block.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, I think even a comment block with a line starting with // is considered one comment block (which does not include the non-doc comment text).

Not sure about /// lines separated by a blank line...

* **Display:** The getter and setter should be presented as a single item in the final documentation.
* **Precedence:**
* Documentation for the property should only be placed on the getter.
* The tooling should *issue a warning* if both a getter and its corresponding setter have unique doc comments.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How does inheritance factor in here?

If I override a documented field with a setter, can I document the setter?

If a supertype has a documented setter, and an undocumented getter, and I override the getter, can I document the overriding getter (thus making both the getter and setter documented)?

Copy link
Member

@munificent munificent left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I love it!

### **2.1. Line-Based Doc Comments (Recommended standard)**

* Starts with `///`.
* All consecutive lines starting with `///` are treated as part of the same comment block.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be good to specify here how the comment text is extracted from these lines like you say for 2.2. In particular, that the leading /// and one subsequent space is removed.


Documentation tools should handle this property with the following rules:

* **Display:** The getter and setter should be presented as a single item in the final documentation.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I disagree. The hover should know that it is a setter, but display the singular documentation for the property, and that documentation should cover both is read and write aspect. This would also avoid having documentation that isn't shown; if you painstakingly write documentation on the setter it would be unfortunate that is only shown when you hover and not elsewhere.

As a side note, DAS is a bit inconsistent with this. For instance if you ask for usage on a setter, you still get all the results that read the property, indicating that it is using the conceptual property in this case.

### **2.2. Block-Based Doc Comments (Historic, not recommended)**

* Starts with `/**` and ends with the matching `*/`.
* Block comments can be nested (e.g., `\*\* \* inner */ */`). Documentation tools must respect this, ensuring that the comment block only ends at the closing \*/ that matches the opening delimiter.
Copy link
Member

@lrhn lrhn Nov 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

\ -> / in the example.

* Starts with `/**` and ends with the matching `*/`.
* Block comments can be nested (e.g., `\*\* \* inner */ */`). Documentation tools must respect this, ensuring that the comment block only ends at the closing \*/ that matches the opening delimiter.
* The content within the delimiters is treated as the documentation.
* Leading whitespace followed by a single asterisk and a single following space on subsequent lines are considered stylistic and are stripped by doc generators.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(What if there is a * that is not followed by a space? Is the * removed or not?)

### **2.1. Line-Based Doc Comments (Recommended standard)**

* Starts with `///`.
* All consecutive lines starting with `///` are treated as part of the same comment block.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The block-doc below says that the single space following the * is omitted. Is a single space following /// also omitted?

(It probably is. That would be consistent, but then it should also be stated consistently. The only place where it may make a difference is withing code-fences, where the leading space becomes visible as indentation.)


* **Doc Comment:** A comment intended to be processed by documentation generation tools, like dartdoc.
* **Element**: A specific, declared element in the Dart code, such as a class, method, function, variable, or type parameter.
* **Identifier:** An individual name in the code (e.g., `MyClass`, myMethod, prefix).
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code-quote myMethod too, probably also prefix.


A reference is a special directive within the Markdown content that creates a hyperlink to a Dart element. It is written by enclosing a Dart identifier in square brackets (e.g., `[foo]`). See [Section 4](#4.-referenceable-elements) for detailed information about which elements can be referenced.

Conceptually, these behave like [reference-style links](https://www.markdownguide.org/basic-syntax/#reference-style-links) in Markdown. The documentation generator resolves the name against the available source code to create the link's destination. See [Section 5](#5.-reference-lookup-and-resolution) for detailed resolution rules.
Copy link
Member

@lrhn lrhn Nov 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Worth saying that a CommonMark links, or other valid CommonMark uses of square brackets, are not parsed as a DartDoc reference:

  • [Banana](https://example.com "Bananas are fun") - A link, not to the Banana class.
  • ![An image](https://example.com/image.png) - An image link.
  • [Link with reference][banana] + `[banana]: https://example.com "Bananas are fun"
  • [^footnote] + [^footnote]: This note is afoot.

CommonMark allows a stand-alone [text] that is not preceded or followed by something specific, as a shorthand link equivalent to [text][], which is again equivalent to [text][text]. That's the syntax that DartDoc takes over and gives a new meaning to.

Or rather, if the text inside [...] is not defined as a reference by the document, then that text has no meaning to CommonMark, and DartDoc can use it for its own. (We should consider doing the same thing for a [some text][MyClass.someMethod] if possible.)


### **3.5. Invalid Placements**

While not strictly disallowed by the language, any other placement of a comment with the /// syntax, is not considered a doc comment, and hence should be ignored by documentation tools.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do state explicitly whether metadata is considered part of the declaration. That is, can you do:

@override
/// This function overrides!
void foo() {}

?
(I'm guessing "no", the DartDoc must precede any metadata before the declaration.)


* The *syntax* for writing documentation comments.
* The rules governing where documentation comments can be *placed*.
* The mechanism for *resolving* references between elements.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is an "element"?

### **1.3. Terminology**

* **Doc Comment:** A comment intended to be processed by documentation generation tools, like dartdoc.
* **Element**: A specific, declared element in the Dart code, such as a class, method, function, variable, or type parameter.
Copy link
Member

@lrhn lrhn Nov 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(Cyclic definition: An element is a (specific declared) element. Then some examples, but one has to guess what they have in common.
The concept of "element" is an implementation detail in our tools, it's not a term that the language specification uses. It's not a word we can assume a reader will understand by themselves, so if we want to use it here, it needs to be defined, non-circularly. Or just use "declaration" or maybe "named declaration".)

* Classes (e.g., `[MyClass]`)
* Mixins (e.g., `[MyMixin]`)
* Enums (e.g., `[MyEnum]`)
* Named extensions (e.g., `[MyExtension]`)
Copy link
Member

@lrhn lrhn Nov 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: Not a type. Another reason to just use "top-level declarations". 😉


A reference in a doc comment (e.g., `[name]`) can link to any Dart element that is visible from the Dart scope of the documented element. See [Section 5](#5.-reference-lookup-and-resolution) for more details about scoping. This includes:

### **4.1. Types**
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These are also top-level declarations. In 3.2 they were included in the "top-level declarations" section.
Any reason to put them by themselves here? ("Type declarations" would be more consistent with the next section's name.)

### **4.2. Top-Level Declarations:**

* Functions (e.g., `[myTopLevelFunction]`)
* Variables and constants (e.g., `[myTopLevelVar]`)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Getters and setters?

* Fields (constants and variables) (e.g., `[myField]`, `[MyClass.myField]`)
* Getters and Setters (See [Section 6.2.2](#6.2.2.-getters-and-setters) for full details)
* Constructors (e.g., `[MyClass.new]`, `[MyClass.named]`, `[MyClass.named()]`)
* Enum constants (e.g., `[MyEnum.value]`)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe worth mentioning that there is no syntactic distinction between references to static and instance members.

(There is also an ambiguity, since [Class.name] can refer both to a constructor and an instance method, and a class declaration can have both. Need to say how it's disambiguated. Prefix new may still work.)

### **4.4. Local Scope Parameters (within a member's doc comment):**

* Parameters of the documented method/function (e.g., `[parameterName]`)
* Type parameters of the documented element (e.g., `[T]`)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And type parameters of surrounding declaration.

class ValueHolder<T> {
   final T value;
   ValueHolder(this.value);
   /// Converts a [T] to an [R] using [function].
   R callWith<R>(R Function(T) function) => function(value);
}


* Parameters of the documented method/function (e.g., `[parameterName]`)
* Type parameters of the documented element (e.g., `[T]`)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(Could this section simply say "any declaration whose name is in scope other than import prefixes"? Plus qualified names where the first identifier must denote "any declaration in scope".)


* **Disambiguation via Qualification:** To prevent ambiguity or to reference an element from a distant scope, a reference should be qualified. This is done by prefixing the name with a class name (e.g., `[ClassName.memberName]`) or an import prefix (e.g., `[prefix.elementName]`).

* **Handling Ambiguity:** If a reference could resolve to multiple declarations within the same scope, or if no resolution is found after checking all scopes, documentation tools should issue a warning.
Copy link
Member

@lrhn lrhn Nov 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And resolve to one of them, or refuse to resolve?

| +----------------------------------------------------------------------+ |
| | 6. Imported Scopes (all 'import' directives + implicit 'dart:core') | |
| | +------------------------------------------------------------------+ | |
| | | 5. Library Scope (all other declarations in the file) | | |
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(Including prefixes.)

| | | | | +------------------------------------------------------+ | | | | |
| | | | | | 2. Method Type Parameter Scope (e.g., <R>) | | | | | |
| | | | | | +--------------------------------------------------+ | | | | | |
| | | | | | | 1. Formal Parameter Scope (e.g., parameter names)| | | | | | |
Copy link
Member

@lrhn lrhn Nov 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(And for initializing constructors, use the initializer list scope instead, since that contains the names of initializing formals and super parameters too.)


#### **5.3.1. Instance Methods and Constructors**

This applies to doc comments on methods, constructors, and operators within a class, enum, mixin, extension, or extension type. The search begins with the method's own parameters.
Copy link
Member

@lrhn lrhn Nov 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(But not setters. Maybe say that explicitly.
They have a parameter list too, but I can see that's inconsistent with treating them as properties. If I do:

class C {
   void cake() {}
   /// Is it [cake]?
   set bar(int cake) {}
}

it looks like DartDoc refuses to make [cake] a link. It probably resolves to the setter parameter, but there is no target for a setter parameter to link to. If that's the case, a setter doc does start at the parameter scope.
I guess that answers my "anything but import prefixes" question above in the negative. "Anything but import prefixes and setter parameters."?)

class MyClass<T> {
T? value;

MyClass.named();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(Consider:

  /// A non-redirecting generative constructor.
  ///
  /// Lookup examples:
  /// * [value]: Resolves to the parameter.
  /// * [other]: Resolves to the parameter.
  /// * [MyClass.value]: Resolves to the instance variable.
  /// ... (more?) ...
  MyClass.value(this.value, int other);

)


```

#### **5.3.2. Instance Fields**
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same for static fields?


This applies to doc comments on fields within a class, enum, or mixin. Since fields have no parameters, the search starts at the member level, but otherwise follows the same route as instance methods.

* **Starting Scope**: Class Member Scope
Copy link
Member

@lrhn lrhn Nov 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(And getters and setters are treated like fields?)


* **Starting Scope**: Library Scope

#### **5.3.5. Top-Level Declarations**
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The two prior sections are also top-level declarations.


#### **5.3.6. Typedefs**

The starting scope for a typedef doc comment depends on what it is an alias for. For function and record types, the scope starts at the parameter/field level. For all other types, it starts at the typedef's own type parameter scope.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(This may need to be accounted for in the definition of what an "element" is. Type aliases for structural types do not introduce any declarations, their field/parameter names are not what I would usually consider expect to be an "element".)


### **5.4. Resolving Qualified Names**

When a reference contains a qualified name (e.g., `[prefix.ClassName.member]`), the resolution process is an iterative, left-to-right evaluation of each Identifier.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"identifier", lower-case.

*Case 1: Import Prefix*

* **Namespace:** The export scope of the imported library, as filtered by any show or hide combinators on the import directive.
* **Example:** In `[math.pi]`, the identifier `math` resolves to an import prefix (e.g., from `import dart:math' as math;`). The tool then searches the public namespace of dart:math for the identifier pi.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

... searches the prefixed import namespace for the identifier pi.

If you have multiple imports with the same prefix and/or show/hide modifiers, the public namespace of the imported library isn't the whole truth.


* **Notes on Generics:**
* The namespace does not include the element's own type parameters. For a class `MyClass<T>`, a reference like `[MyClass.T]` is invalid because T is not a member of `MyClass`'s namespace.
* References are also made to the generic declaration, not a specific instantiation (e.g., write `[List.filled]`, not `[List<int>.filled]`).
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or else?
Currently it seem like the type argument is ignored, but the linking works.

* **Example:** To resolve the reference `[collection.BoolList.empty]`:
* The identifier collection resolves to an import prefix.
* The identifier BoolList resolves to a class element within the collection library's public namespace.
* The identifier empty resolves to a named constructor element within the BoolList class's member namespace.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Today List.List.empty also resolves to the List.empty constructor in the List namespace. (Which can be convenient?)

* **Namespace:** The set of all members declared within that element, including:
* Instance Members (fields, methods, etc.)
* Static Members
* Constructors
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also type-aliases (typedef) probably works like the type they alias, if they alias a specific type.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

8 participants