# ============================================================================= # Projet : AIR — Accounting Intermediate Representation # Auteur : Simon-Pierre Boucher # Contact : contact@spboucher.ai # Fichier : diagnostics.py # Description : Clang-style diagnostics — precise location, cause, suggestion. # ============================================================================= """Compiler-quality diagnostics for AIC. Modeled on clang: every diagnostic names WHERE (event id / field path), WHAT (computed cause, e.g. the exact imbalance), and HOW TO FIX (suggestion). Errors abort compilation; warnings and notes ride along in the result. """ from __future__ import annotations import enum from dataclasses import dataclass class Severity(enum.Enum): ERROR = "error" WARNING = "warning" NOTE = "note" @dataclass(frozen=True, slots=True) class Diagnostic: code: str # e.g. "AIR-E101" severity: Severity message: str location: str # e.g. "event evt_01H..., field items[0].unit_price" suggestion: str | None = None origin_pass: str | None = None def render(self) -> str: head = f"{self.severity.value}[{self.code}]: {self.message}" lines = [head, f" --> {self.location}"] if self.origin_pass: lines.append(f" pass: {self.origin_pass}") if self.suggestion: lines.append(f" help: {self.suggestion}") return "\n".join(lines) class CompilationError(Exception): """Raised when any ERROR diagnostic is produced; carries all diagnostics.""" def __init__(self, diagnostics: list[Diagnostic]): self.diagnostics = diagnostics errors = [d for d in diagnostics if d.severity is Severity.ERROR] super().__init__( f"{len(errors)} error(s):\n" + "\n".join(d.render() for d in errors) )