Skip to content

Docstrings

Shape docstrings are first-class language metadata. They are parsed from /// comments, attached directly to AST nodes, stored in structured form, and consumed by the LSP, package registry, and stdlib metadata.

  • Only /// creates documentation.
  • /** ... */ is not documentation.
  • Doc comments attach to the immediately following AST item or member.
  • Documentation is structured data first and markdown second.
  • Cross-links must use fully qualified targets.
  • Invalid docs are diagnosed instead of guessed.

/// doc comments can attach to:

  • modules, functions, foreign functions, builtin declarations
  • annotations, type aliases
  • structs and struct fields
  • enums and enum variants
  • interfaces and interface members
  • traits and trait members
  • associated types, type parameters

Shape does not use parent-owned child tags. If a field, variant, method, or type parameter needs docs, document that declaration directly.

/// Compute the spread between the maximum and minimum value.
///
/// @param series Input series.
/// @returns max(series) - min(series).
pub fn spread(series: Vec<number>) -> number {
series.max() - series.min()
}

The first prose paragraph becomes the summary. Additional prose remains part of the markdown body.

TagSyntaxPurpose
@module@module <fully-qualified-module>Declare module-level docs
@typeparam@typeparam <name> <description>Document a type parameter
@param@param <name> <description>Document a function parameter
@returns@returns <description>Document the return value
@throws@throws <description>Document possible errors
@deprecated@deprecated <description>Mark as deprecated
@requires@requires <description>Document capability requirements
@since@since <description>Version when introduced
@see@see <fully-qualified-target>Cross-reference another item
@link@link <fully-qualified-target> [label]Cross-reference with custom label
@note@note <description>Additional notes
@example@example (followed by code)Usage example
/// Compute a Hull moving average.
///
/// @param series Input series.
/// @param period Window size.
/// @returns Hull moving average series.
/// @see std::finance::indicators::moving_averages::wma
/// @example
/// let value = hma(close, 21)
pub fn hma(series, period) {
// ...
}

Type Parameters, Fields, Variants, and Members

Section titled “Type Parameters, Fields, Variants, and Members”

Type parameters have their own doc blocks:

fn identity<
/// Element type preserved by the function.
T
>(value: T) -> T {
value
}

Struct fields have their own doc blocks:

pub type Candle {
/// Opening price.
open: number,
/// Highest traded price in the interval.
high: number,
}

Trait and interface members can also be documented directly:

/// Convert a value into a human-readable string.
trait Display {
/// Render `self` as text.
fn display() -> string;
}

Cross-links are explicit and semantic.

Use @see for a simple reference:

/// @see std::core::utils::rolling::rolling_mean

Use @link when you want a custom label:

/// @link std::finance::indicators::volatility::atr ATR helper

Rules:

  • Targets must be fully qualified.
  • Annotation targets use their canonical symbol path, with the final segment written as @name.
  • Relative links are intentionally unsupported.
  • Unresolved links are diagnosed by the LSP.

The LSP uses the AST doc model directly.

Hover shows the declaration signature, markdown body text, structured sections (parameters, returns, notes, examples), and resolved @see/@link references.

Signature help uses @param text for parameter documentation and the doc summary for function-level help.

Inside /// blocks, the LSP offers completion for:

  • tag names after @
  • parameter names inside @param
  • type parameter names inside @typeparam
  • fully qualified symbol targets inside @see and @link

This is semantic completion, not text matching. Parameter and type-parameter suggestions come from the attached AST owner.

The Generate doc comment code action inserts a structured stub above supported declarations. The generated stub includes a summary placeholder, @typeparam entries when generics exist, @param entries for callable parameters, and @returns when the declaration can return a value.

Shape validates doc comments semantically. The LSP reports:

  • Unknown tag names
  • Duplicate singleton tags (@returns, @deprecated, @since, @module)
  • Duplicate @param or @typeparam entries
  • @param names that do not exist on the callable
  • @typeparam names that do not exist on the item
  • @returns on non-callable or void-only targets
  • Non-fully-qualified @see or @link targets
  • Unresolved link targets
  • Empty tags that require content
  • Orphan doc comments without a valid AST target

Diagnostics are span-precise — the LSP highlights the exact tag or link target that is invalid.

Good Shape docstrings should explain:

  • Semantic meaning
  • Invariants or edge cases
  • Units and coordinate systems when relevant
  • Availability or capability requirements
  • Relationships to nearby APIs through @see and @link

Good docstrings should not duplicate:

  • Obvious type information already present in the signature
  • Syntax already visible in the declaration
/// Score values for a custom review workflow.
///
/// @typeparam T Value type evaluated by the scorer.
trait Scorer<T> {
/// Compute a score for the input value.
///
/// @param value Value to score.
/// @returns Numeric score used by downstream decisions.
fn score(value: T) -> number;
/// Return whether the input value passes the requested threshold.
///
/// @param value Value to evaluate.
/// @param min_score Minimum score required for acceptance.
/// @returns `true` when the value meets the threshold.
fn passes(value: T, min_score: number) -> bool;
}

Docstrings are not an editor-only feature. They exist as structured AST data (comment body, ordered tags, typed tag kinds, attachment target metadata, tag and link spans) and are embedded in compiled .shapec package bundles. This makes them available to the package registry for rendering API documentation pages.