diagnostics-development

Use this skill when creating diagnostics - the error messages, warnings, and hints shown to users. Covers the Diagnostic trait, advice types, and best practices for clear, actionable messages.

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "diagnostics-development" with this command: npx skills add biomejs/biome/biomejs-biome-diagnostics-development

Purpose

Use this skill when creating diagnostics - the error messages, warnings, and hints shown to users. Covers the Diagnostic trait, advice types, and best practices for clear, actionable messages.

Prerequisites

  • Read crates/biome_diagnostics/CONTRIBUTING.md for concepts

  • Understand Biome's Technical Principles

  • Follow the "show don't tell" philosophy

Diagnostic Principles

  • Explain what - State what the error is (diagnostic message)

  • Explain why - Explain why it's an error (advice notes)

  • Tell how to fix - Provide actionable fixes (code actions, diff advice, command advice)

Follow Technical Principles:

  • Informative: Explain, don't just state

  • Concise: Short messages, rich context via advices

  • Actionable: Always suggest how to fix

  • Show don't tell: Prefer code frames over textual explanations

Common Workflows

Create a Diagnostic Type

Use the #[derive(Diagnostic)] macro:

use biome_diagnostics::{Diagnostic, category};

#[derive(Debug, Diagnostic)] #[diagnostic( severity = Error, category = "lint/correctness/noVar" )] struct NoVarDiagnostic { #[location(span)] span: TextRange,

#[message]
#[description]
message: MessageAndDescription,

#[advice]
advice: NoVarAdvice,

}

#[derive(Debug)] struct MessageAndDescription;

impl fmt::Display for MessageAndDescription { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "Use 'let' or 'const' instead of 'var'") } }

Implement Advices

Create advice types that implement Advices trait:

use biome_diagnostics::{Advices, Visit}; use biome_console::markup;

struct NoVarAdvice { is_const_candidate: bool, }

impl Advices for NoVarAdvice { fn record(&self, visitor: &mut dyn Visit) -> std::io::Result<()> { if self.is_const_candidate { visitor.record_log( LogCategory::Info, &markup! { "This variable is never reassigned, use 'const' instead." } )?; } else { visitor.record_log( LogCategory::Info, &markup! { "Variables declared with 'var' are function-scoped, use 'let' for block-scoping." } )?; } Ok(()) } }

Use Built-in Advice Types

use biome_diagnostics::{LogAdvice, CodeFrameAdvice, DiffAdvice, CommandAdvice, LogCategory};

// Log advice - simple text message LogAdvice { category: LogCategory::Info, text: markup! { "Consider using arrow functions." }, }

// Code frame advice - highlight code location // Fields: path (AsResource), span (AsSpan), source_code (AsSourceCode) CodeFrameAdvice { path: "file.js", span: node.text_range(), source_code: ctx.source_code(), }

// Diff advice - show a TextEdit diff DiffAdvice { diff: text_edit, // must implement AsRef<TextEdit> }

// Command advice - suggest CLI command CommandAdvice { command: "biome check --write", }

In practice, most lint rules use the RuleDiagnostic builder pattern instead of constructing advice types directly. See the Add Diagnostic to Rule section below.

Add Diagnostic to Rule

use biome_analyze::{Rule, RuleDiagnostic};

impl Rule for NoVar { fn diagnostic(ctx: &RuleContext<Self>, state: &Self::State) -> Option<RuleDiagnostic> { let node = ctx.query();

    Some(
        RuleDiagnostic::new(
            rule_category!(),
            node.range(),
            markup! {
                "Use "&#x3C;Emphasis>"let"&#x3C;/Emphasis>" or "&#x3C;Emphasis>"const"&#x3C;/Emphasis>" instead of "&#x3C;Emphasis>"var"&#x3C;/Emphasis>"."
            },
        )
        .note(markup! {
            "Variables declared with "&#x3C;Emphasis>"var"&#x3C;/Emphasis>" are function-scoped, not block-scoped."
        })
        .note(markup! {
            "See the "&#x3C;Hyperlink href="https://developer.mozilla.org/docs/Web/JavaScript/Reference/Statements/var">"MDN documentation"&#x3C;/Hyperlink>" for more details."
        })
    )
}

}

Use Markup for Rich Text

Biome supports rich markup in diagnostic messages:

use biome_console::markup;

markup! { // Emphasis (bold/colored) "Use "<Emphasis>"const"</Emphasis>" instead."

// Code/identifiers
"The variable "&#x3C;Emphasis>{variable_name}&#x3C;/Emphasis>" is never used."

// Hyperlinks
"See the "&#x3C;Hyperlink href="https://example.com">"documentation"&#x3C;/Hyperlink>"."

// Interpolation
"Found "{count}" issues."

}

Register Diagnostic Category

Add new categories to crates/biome_diagnostics_categories/src/categories.rs :

define_categories! { // Existing categories...

"lint/correctness/noVar": "https://biomejs.dev/linter/rules/no-var",
"lint/style/useConst": "https://biomejs.dev/linter/rules/use-const",

}

Create Multi-Advice Diagnostics

#[derive(Debug, Diagnostic)] #[diagnostic(severity = Warning)] struct ComplexDiagnostic { #[location(span)] span: TextRange,

#[message]
message: &#x26;'static str,

// Multiple advices
#[advice]
first_advice: LogAdvice&#x3C;MarkupBuf>,

#[advice]
code_frame: CodeFrameAdvice&#x3C;String, TextRange, String>,

#[verbose_advice]
verbose_help: LogAdvice&#x3C;MarkupBuf>,

}

Add Tags to Diagnostics

#[derive(Debug, Diagnostic)] #[diagnostic( severity = Warning, tags(FIXABLE, DEPRECATED_CODE) // Add diagnostic tags )] struct MyDiagnostic { // ... }

Available tags:

  • FIXABLE

  • Diagnostic has fix information

  • INTERNAL

  • Internal error in Biome

  • UNNECESSARY_CODE

  • Code is unused

  • DEPRECATED_CODE

  • Code uses deprecated features

Best Practices

Message Guidelines

Good messages:

// Good - specific and actionable "Use 'let' or 'const' instead of 'var'"

// Good - explains why "This variable is never reassigned, consider using 'const'"

// Good - shows what to do "Remove the unused import statement"

Bad messages:

// Bad - too vague "Invalid syntax"

// Bad - just states the obvious "Variable declared with 'var'"

// Bad - no guidance "This code has a problem"

Advice Guidelines

Show, don't tell:

// Good - shows code frame CodeFrameAdvice { path: "file.js", span: node.text_range(), source_code: source, }

// Less helpful - just text LogAdvice { category: LogCategory::Info, text: markup! { "The expression at line 5 is always truthy" }, }

Provide actionable fixes:

// Good - shows exact change DiffAdvice { diff: text_edit, // AsRef<TextEdit> }

// Less helpful - describes change LogAdvice { category: LogCategory::Info, text: markup! { "Change 'var' to 'const'" }, }

Severity Levels

Choose appropriate severity:

// Fatal - Biome can't continue severity = Fatal

// Error - Must be fixed (correctness, security, a11y) severity = Error

// Warning - Should be fixed (suspicious code) severity = Warning

// Information - Style suggestions severity = Information

// Hint - Minor improvements severity = Hint

Common Patterns

// Pattern 1: Simple diagnostic with note RuleDiagnostic::new( rule_category!(), node.range(), markup! { "Main message" }, ) .note(markup! { "Additional context" })

// Pattern 2: Diagnostic with code frame RuleDiagnostic::new( rule_category!(), node.range(), markup! { "Main message" }, ) .detail( node.syntax().text_range(), markup! { "This part is problematic" } )

// Pattern 3: Diagnostic with link RuleDiagnostic::new( rule_category!(), node.range(), markup! { "Main message" }, ) .note(markup! { "See "<Hyperlink href="https://biomejs.dev/linter">"documentation"&#x3C;/Hyperlink>"." })

// Pattern 4: Conditional advice impl Advices for MyAdvice { fn record(&self, visitor: &mut dyn Visit) -> std::io::Result<()> { if self.show_hint { visitor.record_log( LogCategory::Info, &markup! { "Hint: ..." } )?; } Ok(()) } }

Tips

  • Category format: Use area/group/ruleName format (e.g., lint/correctness/noVar )

  • Markup formatting: Use markup! macro for all user-facing text

  • Hyperlinks: Always link to documentation for more details

  • Code frames: Include for spatial context when helpful

  • Multiple advices: Chain multiple pieces of information

  • Verbose advices: Use for extra details users can opt into

  • Description vs Message: Description for plain text contexts (IDE popover), message for rich display

  • Register categories: Don't forget to add to categories.rs

References

  • Full guide: crates/biome_diagnostics/CONTRIBUTING.md

  • Technical principles: https://biomejs.dev/internals/philosophy/#technical

  • Diagnostic trait: crates/biome_diagnostics/src/diagnostic.rs

  • Advice types: crates/biome_diagnostics/src/advice.rs

  • Examples: Search for #[derive(Diagnostic)] in codebase

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

Coding

biome-developer

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

parser-development

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

testing-codegen

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

formatter-development

No summary provided by upstream source.

Repository SourceNeeds Review