bynk_emit/project/diagnostics.rs
1use super::*;
2
3/// Internal: do the work, given a source root (for commons/contexts) and a
4/// test root (for test units). When both roots are the same path the
5/// behaviour is identical to the v0.4+ single-tree layout. When they differ
6/// — v0.9.1's split-paths mode — sources and tests are discovered separately
7/// and the new `inconsistent_test_path` check fires.
8/// v0.24 (ADR 0052): how the project pipeline is driven. `Build` preserves
9/// the CLI contract exactly (bail at the structural and pre-emit gates);
10/// `Analyse` never bails after discovery, skips all emission, and lets
11/// independent unit groups resolve/check past another group's errors.
12#[derive(Clone, Copy, PartialEq, Eq)]
13pub(crate) enum Mode {
14 Build,
15 Analyse,
16}
17
18/// v0.24 (ADR 0052): a compile error attributed — where possible — to the
19/// project-relative source file it belongs to, tagged at the collection
20/// point (the phase that produced it knows which file it was processing).
21/// `None` is the project-level bucket: validations spanning files
22/// (group/cycle/directory consistency) with no single owning file.
23pub struct AttributedError {
24 pub source_path: Option<PathBuf>,
25 pub error: CompileError,
26}
27
28/// Collection-point error sink (ADR 0052). Helpers keep their plain
29/// `&mut Vec<CompileError>` signatures; call sites attribute via
30/// `extend_for` with the file in scope at that point.
31pub(crate) struct ErrorSink {
32 entries: Vec<AttributedError>,
33 /// v0.89 (ADR 0117): non-failing warnings, classified on push by
34 /// `Severity::for_error`. Kept apart so `is_empty`/`len` — the build-failure
35 /// gates — stay errors-only, while every warning source (commons-fn checks,
36 /// service/agent handler validation, parser) is captured uniformly.
37 warnings: Vec<AttributedError>,
38}
39
40impl ErrorSink {
41 pub(crate) fn new() -> Self {
42 Self {
43 entries: Vec::new(),
44 warnings: Vec::new(),
45 }
46 }
47 pub(crate) fn push_for(&mut self, file: Option<&Path>, error: CompileError) {
48 let attributed = AttributedError {
49 source_path: file.map(Path::to_path_buf),
50 error,
51 };
52 match bynk_syntax::Severity::for_error(&attributed.error) {
53 bynk_syntax::Severity::Warning => self.warnings.push(attributed),
54 bynk_syntax::Severity::Error => self.entries.push(attributed),
55 }
56 }
57 pub(crate) fn extend_for(
58 &mut self,
59 file: Option<&Path>,
60 errs: impl IntoIterator<Item = CompileError>,
61 ) {
62 for e in errs {
63 self.push_for(file, e);
64 }
65 }
66 /// True when no **error-severity** diagnostic has been collected — the
67 /// build-failure gate. Warnings do not count (ADR 0117).
68 pub(crate) fn is_empty(&self) -> bool {
69 self.entries.is_empty()
70 }
71 /// Consume the sink, yielding the non-failing **warnings** (ADR 0117).
72 pub(crate) fn into_warnings(self) -> Vec<AttributedError> {
73 self.warnings
74 }
75 /// Consume the sink, yielding errors then warnings — the full diagnostic
76 /// list the LSP and a failed build render together.
77 pub(crate) fn into_all(self) -> Vec<AttributedError> {
78 let mut all = self.entries;
79 all.extend(self.warnings);
80 all
81 }
82 /// The count of **error-severity** diagnostics.
83 pub(crate) fn len(&self) -> usize {
84 self.entries.len()
85 }
86}
87
88/// v0.24: the analyse-mode result — every discovered file's analysed text
89/// snapshot (positions must convert against the text that was analysed,
90/// not a newer buffer) plus the attributed diagnostics.
91pub struct ProjectAnalysis {
92 /// `(project-relative source path, analysed text)` for every file read,
93 /// including clean files (the LSP needs them to clear diagnostics).
94 pub snapshots: Vec<(PathBuf, String)>,
95 pub errors: Vec<AttributedError>,
96 /// v0.25 (ADR 0053): the project-wide binding index. Empty when the
97 /// pipeline bails before resolution (discovery/parse failures).
98 pub index: ProjectIndex,
99 /// v0.27 (ADR 0056): per-file inferred-type inlay hints — `(binding-name
100 /// span, label)`, span-ordered, harvested from the checker's binding
101 /// sites. Empty for files the pipeline never type-checked.
102 pub hints: FileHints,
103 /// v0.30.2 (ADR 0063): per-file expression types — `(expr span, Ty)`,
104 /// captured on the Ok path (a file that checks clean), for `.`-member
105 /// completion's receiver typing. Empty for files with errors (the
106 /// clean-file ceiling) and for synthetic files.
107 pub expr_types: FileExprTypes,
108 /// v0.31 (ADR 0064): per-file local bindings with their scope ranges —
109 /// `let`/`let <-`, fn/handler/lambda params — for the scope-at-offset
110 /// query backing locals completion + navigation. Synthetic files muted.
111 pub locals: FileLocals,
112 /// v0.99: per-file capability-requirement ledger — every capability-consuming
113 /// site (direct call, store op), covered or not, with its provenance. Drives
114 /// the ghost `given` inlay hint and capability hover. Empty for files the
115 /// pipeline never type-checked, and for synthetic/test files (muted).
116 pub requirements: FileRequirements,
117 /// Slice 6b (ADR 0095): qualified unit name → the project source file(s)
118 /// that comprise it, in discovery order — the unit→file map backing document
119 /// links and consumed-context navigation. Excludes synthetic (toolchain-
120 /// injected) units; empty when the pipeline bails before the checker.
121 pub unit_sources: HashMap<String, Vec<PathBuf>>,
122}
123
124/// v0.24: a failed build with its attribution and snapshots intact — what
125/// the CLI renders rich (ariadne source context per file); the plain
126/// `compile_project*` wrappers flatten it to the pre-v0.24 error list.
127pub struct ProjectFailure {
128 pub errors: Vec<AttributedError>,
129 pub snapshots: Vec<(PathBuf, String)>,
130}
131
132impl ProjectFailure {
133 /// The pre-v0.24 contract: collection-ordered, attribution dropped.
134 pub fn flatten(self) -> Vec<CompileError> {
135 self.errors.into_iter().map(|a| a.error).collect()
136 }
137}