diagnostics

package
v0.0.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 11, 2026 License: Apache-2.0 Imports: 4 Imported by: 0

Documentation

Overview

Package diagnostics defines the user-facing error model shared by every compiler phase. All user mistakes surface as Diagnostics with stable codes; bare Go errors are reserved for environmental failures.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func HasErrors

func HasErrors(diags []Diagnostic) bool

HasErrors reports whether any diagnostic is of Error severity.

func LineCol

func LineCol(src []byte, off int) (line, col int)

LineCol converts a byte offset into 1-based line and column (column counts bytes; rune-aware columns arrive with the P7 renderer). It builds a one-shot line index; callers rendering many diagnostics for the same source should build a LineMap once and reuse it (see PrintDiags) to avoid O(offset) per lookup.

func Sort

func Sort(diags []Diagnostic)

Sort orders diagnostics by (file, offset, code) for stable output.

Types

type Code

type Code string

Code is a stable diagnostic identifier (e.g. "SQLETCH001"). Ranges: 0xx lexical/structural (scanner), 1xx rules, 2xx oracle, 3xx codegen/config.

const (
	CodeConstructGrammar   Code = "SQLETCH001" // malformed/unterminated construct
	CodeBadIdentifier      Code = "SQLETCH002" // guard/case/param names must be snake_case
	CodeMissingHeader      Code = "SQLETCH003" // statement without -- name: header
	CodeDuplicateQueryName Code = "SQLETCH004"
	CodeMultipleStatements Code = "SQLETCH005"
	CodeConstructNested    Code = "SQLETCH006" // construct inside parens/subquery
	CodeConstructBadSlot   Code = "SQLETCH007" // construct at a non-slot clause
	CodeConjunctNeedsAnd   Code = "SQLETCH008"
	CodeChooseStructure    Code = "SQLETCH009"
	CodeTooManyGuards      Code = "SQLETCH010"
	CodePositionalParam    Code = "SQLETCH011"
	CodeConstructNesting   Code = "SQLETCH012" // guard inside guard (R5)
	CodeTooManyParams      Code = "SQLETCH013" // more parameters than the bind plan's int16 index holds
	CodeWhenIntLiteral     Code = "SQLETCH014" // @when integer literal is ambiguous (leading zero) or out of int64 range
	CodeWhenStringLiteral  Code = "SQLETCH015" // @when string literal is not a plain single-quoted SQL string (delimiters/escapes would survive into the generated guard)
)

Scanner-phase codes (see docs/design/01-template-scanner.md).

const (
	CodeGoParse        Code = "SQLETCH020" // the .go file does not parse
	CodeGoMarkerTarget Code = "SQLETCH021" // //sqletch:query on a non-const declaration
	CodeGoNotRawString Code = "SQLETCH022" // marked const's value is not a raw string literal
	CodeGoBadConstSpec Code = "SQLETCH023" // marked const has no value, or names/values disagree
)

Go-source input codes: templates authored in a `//sqletch:query` const inside a .go file (see docs/design/13-go-source-input.md). They sit in the scanner band because they report on the same phase — getting template bytes out of a file and into the scanner.

const (
	CodeRenderingParse    Code = "SQLETCH100" // a rendering fails to parse
	CodeJoinTypeForbidden Code = "SQLETCH101" // optional join not INNER/LEFT (R2)
	CodeNodeIncomplete    Code = "SQLETCH102" // fragment is not one complete node (R1)
	CodeNotSingleDML      Code = "SQLETCH103" // not exactly one SELECT/UPDATE/INSERT/DELETE

	CodeVacuousGuard     Code = "SQLETCH110" // required param used as guard (R9)
	CodeGuardNeverBinds  Code = "SQLETCH111" // guard param binds nowhere under itself (R9)
	CodeChooseParamBinds Code = "SQLETCH112" // @choose control param used as :name (R9)
	CodeUnanchoredClause Code = "SQLETCH113" // all conjuncts optional, no anchor (R6)
	CodeAmbiguousRef     Code = "SQLETCH114" // unqualified ref matches several relations
	CodeScopeViolation   Code = "SQLETCH115" // reference into optional join w/o guard (R3)
	CodePlannerSensitive Code = "SQLETCH116" // e.g. FOR UPDATE + optional LEFT JOIN
	CodeStarExpansion    Code = "SQLETCH117" // SELECT * would include optional-join columns (R2)
	CodeUnanchoredSet    Code = "SQLETCH118" // every SET/INSERT-list item optional, no anchor (R6)
	CodePairedGuards     Code = "SQLETCH119" // INSERT column/value guard pairing broken (R7)
	CodeInParamScalarMix Code = "SQLETCH120" // an @in list param also bound as a plain scalar (R9)
	CodeOrderByDistinct  Code = "SQLETCH122" // @order-by under DISTINCT ON (prefix-sensitive)
	CodeOrderByNeedsDflt Code = "SQLETCH123" // WITH TIES requires an @order-by @default
	CodePolicyUnscoped   Code = "SQLETCH124" // designated table without the scoping conjunct in every shape
	CodePolicyUnweavable Code = "SQLETCH125" // a policy applies but cannot be woven into this query
	CodePolicyBadOptOut  Code = "SQLETCH126" // @policy-optout names an unknown or inapplicable policy
)

Rules-phase codes (R1 runs in P2's pipeline position; see docs/design/02-rendering.md and 03-structural-rules.md).

const (
	CodeServerVersionMismatch Code = "SQLETCH200" // pinned version != connected server
	CodeIndeterminateParam    Code = "SQLETCH201" // undetermined parameter type (add a cast)
	CodeOracleFailure         Code = "SQLETCH202" // prepare/describe failed
	CodeCacheServerDrift      Code = "SQLETCH203" // committed cache was generated against a different server version
	CodeDestructiveReset      Code = "SQLETCH204" // refused to reset a user-supplied database's schema (pass --allow-destructive)
	CodeColumnAgreement       Code = "SQLETCH210" // renderings disagree on result columns
	CodeParamAgreement        Code = "SQLETCH211" // renderings disagree on a param's type
	CodeOptionalInsertNotNull Code = "SQLETCH212" // optional NOT NULL column without default (warning)
	CodeParamHintConflict     Code = "SQLETCH213" // `-- @param` hint disagrees with the oracle (Tier 1)
	CodeNativeUnsupported     Code = "SQLETCH214" // native oracle: query construct outside the modeled subset
	CodeNativeDDL             Code = "SQLETCH215" // native oracle: schema DDL outside the catalog builder's subset
	CodeColumnHintConflict    Code = "SQLETCH216" // `-- @column` hint disagrees with the oracle's column type
)

Oracle-phase codes (see docs/design/04-type-oracle.md).

const (
	CodeConfigParse     Code = "SQLETCH300" // sqletch.yaml unreadable/unknown keys
	CodeConfigInvalid   Code = "SQLETCH301" // sqletch.yaml field validation
	CodeExpansionLarge  Code = "SQLETCH302" // static expansion exceeds max_shapes
	CodePolicyInvalid   Code = "SQLETCH303" // a policy declaration is malformed
	CodeShapeCapReached Code = "SQLETCH304" // shape enumeration stopped at its cap (explain --max-shapes, verification.max_shapes)
	CodePathEscape      Code = "SQLETCH306" // cache.path/output.path escapes the project directory
	// A result column's name does not form a valid Go identifier once
	// mapped (an oracle column or quoted alias carrying spaces, braces,
	// punctuation, …): emitting it verbatim would either fail gofmt with
	// no span or, worse, splice attacker-influenced text into the
	// generated package. Refuse it and ask for an `AS` alias / `-- @column`.
	CodeInvalidColumnIdentifier Code = "SQLETCH307"
	CodeSourceUnreadable        Code = "SQLETCH308" // a glob-matched template file could not be read (LSP degrades past it)
	CodeNameCollision           Code = "SQLETCH310" // generated Go identifiers collide
	CodeUnsupportedType         Code = "SQLETCH311" // no Go mapping for a database type
	// null_overrides hygiene: overrides are the analyzer's escape
	// hatch and are applied by RESULT-COLUMN NAME, so a key that
	// matches nothing is dead config and a key matching several
	// same-named columns forces all of them at once.
	CodeOverrideUnknownColumn   Code = "SQLETCH312" // null_overrides names no result column of the query
	CodeOverrideAmbiguousColumn Code = "SQLETCH313" // null_overrides matches multiple same-named result columns

	// Multi-target resolution (docs/design/19-multi-target-output.md §4).
	// A template file belongs to exactly one generated package, and an
	// output directory to exactly one package name — otherwise "which
	// package is this query in" has no answer, and generate would emit
	// two packages over one directory.
	CodeTargetFileOverlap Code = "SQLETCH314" // a template file is claimed by two targets
	CodeTargetCollision   Code = "SQLETCH315" // two targets resolve to one path with different packages
	CodeTargetNoMatch     Code = "SQLETCH316" // a queries pattern matched no file (warning)
	CodeTargetNameSpan    Code = "SQLETCH317" // an overrides/static_expansion name exists in several targets (warning)
)

Codegen/config codes.

type Diagnostic

type Diagnostic struct {
	Code     Code
	Severity Severity
	Span     Span
	Message  string
	Hint     string
}

func Errorf

func Errorf(code Code, span Span, format string, args ...any) Diagnostic

func Warnf

func Warnf(code Code, span Span, format string, args ...any) Diagnostic

func (Diagnostic) Render

func (d Diagnostic) Render(src []byte) string

Render produces the human-readable one-line form "file:line:col: error[CODE]: message". The full excerpt renderer arrives in P7; this form is enough for tests and early CLI output.

func (Diagnostic) RenderExcerpt

func (d Diagnostic) RenderExcerpt(src []byte) string

RenderExcerpt produces the multi-line form with a source excerpt and caret underline (design 07 §3):

file:12:7: error[SQLETCHnnn]: message
   |
12 |   AND organization_id = :org
   |       ^^^^^^^^^^^^^^^
help: …

func (Diagnostic) RenderExcerptWith

func (d Diagnostic) RenderExcerptWith(src []byte, lm *LineMap) string

RenderExcerptWith is RenderExcerpt reusing a precomputed line index (see RenderWith / PrintDiags). Output is byte-identical to RenderExcerpt.

func (Diagnostic) RenderWith

func (d Diagnostic) RenderWith(src []byte, lm *LineMap) string

RenderWith is Render reusing a precomputed line index. Rendering a batch of diagnostics for one file builds the index once (O(n)) and then locates each diagnostic in O(log lines) instead of O(offset) — see PrintDiags. Output is byte-identical to Render.

func (Diagnostic) WithHint

func (d Diagnostic) WithHint(format string, args ...any) Diagnostic

type LineMap

type LineMap struct {
	// contains filtered or unexported fields
}

LineMap is a precomputed index of line-start byte offsets for one source, so byte offset → (line, col) is an O(log lines) binary search instead of an O(offset) rescan. Rendering d diagnostics for an n-byte file drops from O(n·d) to O(n + d·log lines) by building it once.

func NewLineMap

func NewLineMap(src []byte) *LineMap

NewLineMap builds the line index for src.

func (*LineMap) LineCol

func (m *LineMap) LineCol(off int) (line, col int)

LineCol returns the 1-based line and (byte-)column of off. It is byte-identical to the previous linear implementation: an offset past EOF clamps to EOF, the column counts bytes since the last '\n', and a '\n' byte itself belongs to the line it terminates.

func (*LineMap) LineStartOffset

func (m *LineMap) LineStartOffset(line0 int) int

LineStartOffset returns the byte offset at which the 0-based line begins. A line index at or past the last line clamps to len(src), so a position past EOF resolves to EOF — matching the previous linear walk that returned len(src) when the requested line ran off the end.

type Severity

type Severity int
const (
	Error Severity = iota
	Warning
)

func (Severity) String

func (sv Severity) String() string

type Span

type Span struct {
	File  string
	Start int // byte offset, inclusive
	End   int // byte offset, exclusive
}

Span is a byte range into an original template file. It lives here (not in internal/template) so that every package can attach spans to diagnostics without import cycles.

func (Span) IsZero

func (s Span) IsZero() bool

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL