Skip to content
cccskills
FreePublished on GitHubEnglish overview

Diagnostics

Use this skill to add or revise diagnostics in the Carbon toolchain. It explains how to register message kinds, format typed arguments, attach source labels and context, and verify that diagnostics follow the project’s wording and test coverage rules.

OriginalShow the original description

Instructions for declaring, formatting, emitting, testing, and styling diagnostic messages (errors, warnings, and the labels that explain them) in the Carbon toolchain.

carbon-language/carbon-lang33,929 stars(GitHub 33,929 + cccskills 0)

Run score Not tested

See how to install

Good for

  • Adding an error or warning to the Carbon compiler
  • Attaching source locations and explanatory labels
  • Checking diagnostic wording and formatting conventions
  • Verifying diagnostic test coverage

Overview

What it does

This skill explains how to add or change errors and warnings in the Carbon toolchain, from registration through display and validation. It covers registering diagnostic kinds and keeping each declaration unique, building messages with typed arguments, and attaching labels and context to source locations. It also describes wording conventions, such as starting messages with a lowercase letter and omitting a final period, plus coverage checks that use test data to verify each diagnostic.

When it helps

Use it when developing the Carbon compiler and adding new errors or warnings, or when making existing diagnostics easier to read. It is also useful when you need to connect a message to the relevant code location or check the relationship between diagnostic declarations, labels, and tests.

Example requests

  • “How should I label the source location for this error?”
  • “Show me how to add a diagnostic kind and cover it in tests.”

Notes

This skill assumes the Carbon toolchain’s C++ implementation and testing conventions.

This overview was written by AI (OpenAI Codex) from the public SKILL.md. Check the original below for the exact behavior.

Document check

AI checked the SKILL.md for operations that need care and for setup requirements. The score comes only from actually running the skill (hands-on test).

SafetyAre sending data, deleting files, and running commands disclosed?
AGoodThe material describes diagnostic definitions and tests, with no deletion, external sending, or credential access.
Easy setupDoes it need few tools or API keys, and are they documented?
BPartly lackingIt assumes the Carbon toolchain’s C++ implementation and test conventions but gives no setup steps.

Not counted in the score. Rated by AI (OpenAI Codex) as of Oct 26, 2026. Treat it as a guide and read the original before installing.

Disagree? Request a re-evaluation

Tell us which point is wrong and why. No login needed. We will check it against the SKILL.md, redo the evaluation, and show the result here.

This form is protected by reCAPTCHA, and the Google Privacy Policy and Terms of Service apply.

Files included (1)

  • SKILL.md13.9 KB

SKILL.md (original)

Review the instructions the agent will receive before you install.

Diagnostics in the Carbon Toolchain

<!-- Part of the Carbon Language project, under the Apache License v2.0 with LLVM Exceptions. See /LICENSE for license information. SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -->

The Carbon compiler features a highly-engineered, context-aware diagnostics framework designed to deliver precise, readable, and highly targetable diagnostic output (errors, warnings, and the labels that explain them). This document establishes strict rules for declaring, formatting, emitting, testing, and styling compiler diagnostics.


Architecture Overview

graph TD
  Kind[kind.def Registry] -->|Registration| Enum[Kind Enum ID]
  Enum -->|Build/Emit| Emitter[Emitter LocT]
  Emitter -->|ConvertLoc| Loc[Converted Physical Loc]
  Emitter -->|formatv serialization| Formatting[format_providers.h / Custom Types]
  Emitter -->|Emit Messages| Consumer[Console / Sorting Consumer]
  Consumer -->|stable sort| StdErr[Compiler Standard Error]

Diagnostics are handled via three decoupled core components:

  1. Registry: Globally enumerated kinds inside kind.def.
  2. Emitters: Specialized formatting pipelines (parameterized on custom phase location types LocT like Token or LocId) that convert raw tokens to standardized physical source locations (file, line, column, and the text of the line).
  3. Consumers: Pipelines that process, track, filter, and sort diagnostics. The default SortingConsumer buffers and stable-sorts diagnostics based on their last_byte_offset matching compiler traversal order to ensure perfect causal ordering.

1. Declaring and Registering Diagnostics

All diagnostic types must pass structural uniqueness and coverage verifications.

The Diagnostic Registry

Every diagnostic kind must be registered globally as an enum option under kind.def:

// toolchain/diagnostics/kind.def
CARBON_DIAGNOSTIC_KIND(RealLiteralTooLargeForUnsizedInt)

The Uniqueness Rule

To ensure optimal compile-time and analysis integrity, every diagnostic kind declared in kind.def MUST be mapped to one and only one C++ macro declaration (CARBON_DIAGNOSTIC or CARBON_DIAGNOSTIC_ON_SCOPE).

  • DO NOT duplicate diagnostic definitions across different locations.

  • The C++ representation of the diagnostic is a static/global constant of type DiagnosticBase<Args...>.

  • Local Scope (Recommended): If the diagnostic is unique to a single block/function body, declare it locally inside the function body adjacent to its Emit trigger:

    void ConvertFloatValueToInt(...) {
      CARBON_DIAGNOSTIC(FloatNaNConvertedToInt, Error,
                        "cannot convert NaN to integer type {0}", SemIR::TypeId);
      context.emitter().Emit(loc_id, FloatNaNConvertedToInt, dest_type_id);
    }
    
  • File Scope: If the diagnostic is shared among multiple functions inside the same file, declare it at file scope inside the anonymous namespace of the .cpp file.

  • Global Scope: If a diagnostic (such as a shared helper label) is reused across different physical files, define it in a shared header (e.g. context/check helpers) and mark it extern where applicable, ensuring the macro is only invoked once.


2. Formatting Diagnostic Arguments

Carbon diagnostics leverage LLVM's formatv engine. Parameters must be passed using strongly-typed arguments to preserve translation capability.

String Lifetimes & Pitfalls

  • llvm::StringRef is DISALLOWED: Do not pass StringRef as a parameter type to CARBON_DIAGNOSTIC due to unsafe lifetime and buffer-allocation boundaries.
  • llvm::StringLiteral is DISALLOWED: Do not use literal types as arguments as they prevent future diagnostic localization and translations.
  • Use std::string: If string formatting or custom allocations are required, declare the parameter storage type as std::string.

Format Selectors (format_providers.h)

Use specialized formatting wrappers under format_providers.h to express clean inline options in format strings:

WrapperTarget Format StyleExample UsageOutput
BoolAsSelect{Index:true|false}"{0:is signed|is unsigned}"Maps bool to selection string.
IntAsSelect{Index:=Val:String|:Default}"{0:=1:is|:are}"Matches exact options.
IntAsSelect (Plural){Index:s}"{0} argument{0:s}"Prints "s" if value != 1 (e.g., "1 argument", "3 arguments").

Custom Toolchain Type Mappings

Custom structures can define how they serialize inside diagnostics using the DiagnosticType tag mapping to Diagnostics::TypeInfo<StorageType>:

  • Identifiers & Names (declared in check/diagnostic_helpers.h):
    • NameId: Formats raw identifier spelling, safely escaping keyword conflicts under backticks automatically.
    • LibraryNameId: Formats custom library descriptors cleanly (e.g. default library or library "foo").
  • Sized Primitives:
    • TypedInt: Formats an APInt constant exactly, extracting target signedness representation automatically from its bound type representation.
  • Type Formatter Hierarchy: When choosing parameter types to print compiler type representations, follow this priority list:
    1. TypeOfInstId (Preferred): Resolves the backing type of an InstId, preserving programmatic aliasing, constraints, and source spelling context. Enclosed under backticks automatically.
    2. InstIdAsType: Converts an InstId for a type expression, printing custom type layouts under backticks.
    3. TypeId (Fallback): Canonical description of the type. Avoid when possible because type canonicalization loses intermediate source program spelling and aliasing metadata.
    4. *AsRawType (e.g. InstIdAsRawType, TypeIdAsRawType): Formats the type layout exactly like their counter-structures above, but omits enclosing backticks (useful when inserting types inside larger code snippets).

3. Fluent Emission Builders & RAII Scopes

Fluent Builder Pattern

A diagnostic is one message plus the labels that mark the source explaining it. Use Build to state the message and Attach to mark each place the reader has to look, chained fluently:

CARBON_DIAGNOSTIC_LABEL(ModifierPrevious, Info, "`{0}` previously appeared here",
                        Lex::TokenKind);

context.emitter()
    .Build(second_node, ModifierRepeated, context.token_kind(second_node))
    .Attach(first_node, ModifierPrevious, context.token_kind(first_node))
    .Emit();

Labels are declared with CARBON_DIAGNOSTIC_LABEL where they are attached, and are not registered in kind.def; check_diagnostics.py checks that each one is attached and covered. A label is either Primary -- the range the message itself is about -- or Info, for somewhere else the reader must look; those two are the only categories, because anything not read against the code it names is not a label. Attach(loc) with no label marks a range wordlessly, which is what to use when a label would only restate the message.

Every diagnostic should attach at least one label; a message with nothing attached has nothing to show. See diagnostics.md for which range to pick and when a label earns words.

[!SAFETY] Emitter builders are marked [[nodiscard]]. To prevent a developer from creating a builder but failing to terminal-chain .Emit(), the builder uses an rvalue overload Emit() && that triggers a compile-time static_assert(false). You must save the builder to an lvalue or execute the chain exactly as emitter.Build(...).Attach(...).Emit().

RAII Context & Annotation Scopes

Manage large checking structures requiring blanket context using RAII block scopes:

  • ContextScope: Attaches a context to every diagnostic emitted within its scope, describing the higher-level operation that failed because of it. A context is declared with CARBON_DIAGNOSTIC_CONTEXT, or CARBON_DIAGNOSTIC_SOFT_CONTEXT for one dropped when the diagnostic already has a context:

    Diagnostics::ContextScope diagnostic_context(
        &context.emitter(), [&](auto& builder) {
          CARBON_DIAGNOSTIC_CONTEXT(
              QualifiedDeclInIncompleteClassScope,
              "cannot declare a member of incomplete class {0}", SemIR::TypeId);
          builder.Attach(loc_id, QualifiedDeclInIncompleteClassScope,
                         context.classes().Get(class_id).self_type_id);
        });
    

    A context is not a label: its text stands alone as a sentence, which is why it leads the diagnostic rather than following it.

  • AnnotationScope: RAII block scope that attaches labels to every diagnostic emitted within it, using the same Builder as a direct emission.


4. Diagnostics Wording Style Guide

Refer to the official Diagnostic message style guide for complete details.

To maintain message consistency and integrate cleanly with Clang diagnostics in interoperable code, adhere strictly to these rules:

  • Start with lowercase and omit periods: Start diagnostic messages with a lowercase letter or quoted code, and do not end them with a period (e.g., "cannot convert..." or "`self` declared...").
  • Use backticks for quoted code: Enclose identifiers, code constructs, and types inside standard backticks (e.g., "`{0}` is bad").
  • Phrase as bullet points without articles: Phrase diagnostics as descriptive bullet points or sentence fragments rather than full sentences. Leave out standard articles (a, an, the) unless necessary for logical clarity. Semicolons can be used to separate fragments within a message.
  • Describe the situation and language rule: Diagnostics should describe the exact situation the toolchain observed. The language rule violated can be mentioned if it wouldn't otherwise be clear:
    • Situation-only: "redeclaration of X" (implies that redeclaration is not permitted).
    • Rule-inclusion: "`self` declared in invalid context; can only be declared in implicit parameter list".
  • Wording Choice ("cannot" vs "allowed"): Explicitly avoid "allowed", "legal", "permitted", "valid", and related passive wording. You may use "cannot" if needed, but try to use phrasing that does not require it:
    • Correct: "`export` in `impl` file" (Avoids "allowed")
    • Incorrect: "`export` is only allowed in API files"
    • Correct: "`extern library` specifies current library" (Avoids "cannot")
    • Incorrect: "`extern library` cannot specify the current library"
  • Developer Intent Hints: It is acceptable for a diagnostic to guess at the developer's intent and provide a hint after explaining the situation and the rule, but never as a substitute for that:
    • Correct: "cannot implicitly convert `i32` to `String`; add `as String` for explicit conversion"
    • Incorrect: "add `as String` to convert `i32` to `String`" (Lacks the core violation message).
  • Structure for Tooling API: Try to structure diagnostics such that parameter inputs can be programmatically extracted without string parsing (prefer strongly-typed parameters over format placeholders where possible).

5. Diagnostics Testing & Coverage Verification

Carbon strictly enforces testing coverage at build-time.

  1. Tag Verification Requirement: Every diagnostic kind declared in kind.def (which is not blacklisted in the UntestedKinds array under coverage_test.cpp) MUST be verified by at least one testcase file inside toolchain/*/testdata/.

  2. Stderr Checklist Matchers: The testcase split verifying the diagnostic must catch it using standard CHECK matchers, explicitly tracking the matching enum tag in standard error comments:

    // CHECK:STDERR: fail_bounds.carbon:[[@LINE+1]]:15: error: cannot convert NaN to integer type `i32` [FloatNaNConvertedToInt]
    let a: i32 = Convert(nan_val);
    
  3. Build Enforcement: Failing to provide a diagnostic test check matcher triggers a failure on the target test //toolchain/diagnostics:coverage_test.

  4. Label Enforcement: A label is declared where it is attached rather than in a central registry, so coverage_test has no list of labels to compare a matched name against and leaves any name that isn't a kind alone. check_diagnostics.py is what checks them: it reads the declarations and the testdata directly, so it catches a label with no test, a name matched on that is neither a kind nor a label, and a kind registered in kind.def that nothing declares. It runs as a pre-commit hook, which CI runs too, rather than as a bazel test -- reading every source file is what it needs and what a test in the sandbox cannot do.

Reviews

No reviews yet. Tell others what you thought.

Comments

No login needed. Share how it worked for you, ask a question, or note where it did not run.

No comments yet. Be the first to write one.

Comments are published right away. Do not include personal information such as email addresses or phone numbers. Up to 2 URLs.

0 / 1,000 characters

This form is protected by reCAPTCHA, and the Google Privacy Policy and Terms of Service apply.

More skills from this repository

Use this skill to read GitHub issue details and comments from the command line with `gh`. It covers issues referenced by number or URL, including issues in other repositories, and keeps access read-only so issue state is not changed.

  • Reading an issue and its full comment history
  • Opening an issue referenced by URL
  • Viewing an issue from another repository
carbon-language/carbon-lang33.9KUpdated Oct 26, 2026

Agent tools

FreeSummary

Use this skill to guide an AI assistant working in the Carbon repository on viewing, searching, and editing files with specialized environment tools. It also sets rules for temporary files and when custom programs may be used.

  • Choosing approved tools to view, search, or edit files
  • Checking where temporary analysis and test files belong
  • Deciding when a custom transformation program is allowed
carbon-language/carbon-lang33.9KUpdated Oct 26, 2026

Bazel usage

FreeSummary

Follow the Carbon project's Bazel workflow through Bazelisk, a version-managing launcher. Learn commands for building targets, running tests and binaries, enabling AddressSanitizer, and refreshing cached state after system dependency changes.

  • Building a Carbon target
  • Running focused project tests
  • Launching a Bazel-built binary
carbon-language/carbon-lang33.9KUpdated Oct 26, 2026

Use this skill to implement builtin functions in the Carbon compiler, from registering a function name and checking its signature to evaluating constant calls and generating LLVM IR for runtime calls. It also covers prelude bindings and focused tests.

  • Registering a new Carbon builtin function
  • Adding compile-time evaluation behavior
  • Lowering runtime builtins to LLVM IR
carbon-language/carbon-lang33.9KUpdated Oct 26, 2026

Code style

FreeSummary

Apply Carbon toolchain conventions for formatting Bazel, C++, Carbon, Markdown, and Python files. The skill also covers clear comments and documentation, accurate names, design choices, and concise commit descriptions.

  • Formatting a changed source file
  • Writing or revising code comments
  • Choosing names that match their meaning
carbon-language/carbon-lang33.9KUpdated Oct 26, 2026

GitHub CLI usage

FreeSummary

Use `gh`, the GitHub command-line tool, to list, inspect, and search issues, pull requests, repositories, and code. The skill also explains read-only REST and GraphQL queries, pagination, and ways to filter or format returned data.

  • Checking an issue or pull request
  • Searching issues, code, or repositories
  • Querying GitHub data with the API
carbon-language/carbon-lang33.9KUpdated Oct 26, 2026

See all skills by carbon-language

Report a problem with this skill