prebindgen_registry/registry/error.rs
1//! What can go wrong before a binding is written.
2
3use std::fmt;
4
5use prebindgen::SourceLocation;
6
7impl From<prebindgen_flat::flat::ParseError> for ScanError {
8 fn from(e: prebindgen_flat::flat::ParseError) -> Self {
9 match e {
10 prebindgen_flat::flat::ParseError::DuplicateName(d) => {
11 ScanError::DuplicateName(Box::new(DuplicateNameError {
12 name: d.name,
13 first: d.first,
14 second: d.second,
15 first_crate: d.first_crate,
16 second_crate: d.second_crate,
17 }))
18 }
19 }
20 }
21}
22
23/// One item of a [`ScanError::NotExpressible`] report.
24#[derive(Debug)]
25pub struct NotExpressibleEntry {
26 /// The item's name, or `None` for an item kind that has none.
27 pub name: Option<syn::Ident>,
28 /// Rendered [`ItemError`](prebindgen_flat::flat::ItemError) — the frontend's own
29 /// message, so one authority produces it.
30 pub reason: String,
31 pub location: SourceLocation,
32}
33
34/// Payload of [`ScanError::DuplicateName`], boxed to keep the error enum
35/// small (`clippy::result_large_err`).
36#[derive(Debug)]
37pub struct DuplicateNameError {
38 pub name: syn::Ident,
39 pub first: SourceLocation,
40 pub second: SourceLocation,
41 /// Origin crates of the colliding items, when known (multi-source
42 /// ingestion via several `Flat::builder().source(..)` feeders) — the `SourceLocation`
43 /// file paths are crate-relative, so with several sources they alone
44 /// may not identify the colliding crates.
45 pub first_crate: Option<String>,
46 pub second_crate: Option<String>,
47}
48
49/// Errors surfaced by the scan phase.
50#[derive(Debug)]
51pub enum ScanError {
52 DuplicateName(Box<DuplicateNameError>),
53 /// Items the flat language cannot express, all of them at once.
54 ///
55 /// The message for each comes from
56 /// [`ItemError`](prebindgen_flat::flat::ItemError), so one authority produces it.
57 /// This replaces the per-item guards the registry used to duplicate — a `self`
58 /// receiver, a non-ident parameter pattern, a disallowed `impl Trait` — which
59 /// the frontend now catches with a richer diagnosis (it names the parameter).
60 NotExpressible {
61 entries: Vec<NotExpressibleEntry>,
62 },
63 /// An adapter-invariant check failed — see
64 /// [`Prebindgen::validate`](crate::Prebindgen::validate).
65 /// The message is adapter-authored and printed verbatim.
66 AdapterInvariant {
67 message: String,
68 },
69 /// Explicitly declared items (functions, helper functions, constants)
70 /// that match no indexed `#[prebindgen]` item. A declaration is a
71 /// statement of intent — its target being absent is always a bug (a
72 /// typo in build.rs, or the item was renamed/removed in the source
73 /// crate), so this is a hard error, unlike the soft warnings for stale
74 /// *ignore* entries. All missing names are collected before failing.
75 DeclaredNotFound {
76 entries: Vec<(&'static str, String)>,
77 },
78 /// Declared type keys that qualify a source item with its crate path
79 /// (`ptr_class!(myflat::Foo)` where `myflat` is a chained source crate).
80 /// Source items live in one flat namespace and are keyed by their bare
81 /// name — the qualified spelling can never match a captured signature,
82 /// so it is a hard error with a fix-it instead of a silent miss (issue
83 /// #95). All offenders are collected before failing.
84 QualifiedDeclaredTypes {
85 /// `(qualified spelling, bare fix-it name)` pairs.
86 entries: Vec<(String, String)>,
87 },
88}
89
90impl fmt::Display for ScanError {
91 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
92 match self {
93 ScanError::DuplicateName(e) => {
94 let in_crate = |c: &Option<String>| match c {
95 Some(c) => format!(" in crate `{c}`"),
96 None => String::new(),
97 };
98 write!(
99 f,
100 "duplicate prebindgen name `{}`: first{} at {}, second{} at {} — prebindgen \
101 items live in one flat namespace across all sources; rename one of them",
102 e.name,
103 in_crate(&e.first_crate),
104 e.first,
105 in_crate(&e.second_crate),
106 e.second
107 )
108 }
109 ScanError::NotExpressible { entries } => {
110 // Not "`#[prebindgen]` item(s)": two populations reach this report
111 // and only one of them is a marked item. The other is a type the
112 // *binding* put on the boundary — a declared crossing, or a
113 // spelling expansion composed — which no source crate ever wrote
114 // and whose author would go looking for a `#[prebindgen]` that is
115 // not there. Each entry's own line says which it is.
116 write!(
117 f,
118 "the flat language cannot express {} of this binding's items and types:",
119 entries.len()
120 )?;
121 for e in entries {
122 // The crate, because a captured path is crate-relative: with
123 // several sources, two offenders both read `src/lib.rs:..`
124 // and the location alone says nothing about which one to fix.
125 // Same reason the duplicate-name diagnostic carries it.
126 //
127 // Gated on `has_position`, because not every offender has a
128 // place: a type a binding composed was never written in a file,
129 // and neither was an item from a hand-built stream. Rendering
130 // the default location anyway prints `:0:0:`, which reads as a
131 // real position — the fault this whole `has_position` split
132 // exists to prevent, and it is worse than saying nothing.
133 let mut prefix = String::new();
134 if e.location.has_position() {
135 prefix.push_str(&e.location.to_string());
136 }
137 if let Some(c) = &e.location.crate_name {
138 if !prefix.is_empty() {
139 prefix.push(' ');
140 }
141 prefix.push_str(&format!("in crate `{c}`"));
142 }
143 if !prefix.is_empty() {
144 prefix.push_str(": ");
145 }
146 match &e.name {
147 Some(name) => write!(f, "\n {prefix}{name} {}", e.reason)?,
148 None => write!(f, "\n {prefix}{}", e.reason)?,
149 }
150 }
151 Ok(())
152 }
153 ScanError::AdapterInvariant { message } => write!(f, "{}", message),
154 ScanError::DeclaredNotFound { entries } => {
155 writeln!(
156 f,
157 "{} declared item(s) not found among #[prebindgen] items:",
158 entries.len()
159 )?;
160 for (kind, name) in entries {
161 writeln!(f, " - {kind} `{name}`")?;
162 }
163 write!(
164 f,
165 "a declaration names an item that does not exist — typo in build.rs, \
166 or renamed/removed in the source crate?"
167 )
168 }
169 ScanError::QualifiedDeclaredTypes { entries } => {
170 writeln!(
171 f,
172 "{} declared type(s) qualify a source item with its crate path:",
173 entries.len()
174 )?;
175 for (spelled, bare) in entries {
176 writeln!(f, " - `{spelled}` — declare it as `{bare}`")?;
177 }
178 write!(
179 f,
180 "source items live in one flat namespace keyed by their bare name; \
181 a crate-qualified spelling never matches captured signatures"
182 )
183 }
184 }
185 }
186}
187
188impl std::error::Error for ScanError {}
189
190/// Combined error surfaced by `RegistryBuilder::build` and by a generator's
191/// own `build` / `write_rust`.
192#[derive(Debug)]
193pub enum WriteRustError {
194 Scan(ScanError),
195 Expand(crate::expand::ExpandError),
196 Unfold(crate::unfold::UnfoldError),
197 Resolve(crate::resolve::ResolveError),
198 Write(crate::write::WriteError),
199}
200
201impl fmt::Display for WriteRustError {
202 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
203 match self {
204 WriteRustError::Scan(e) => write!(f, "{}", e),
205 WriteRustError::Expand(e) => write!(f, "{}", e),
206 WriteRustError::Unfold(e) => write!(f, "{}", e),
207 WriteRustError::Resolve(e) => write!(f, "{}", e),
208 WriteRustError::Write(e) => write!(f, "{}", e),
209 }
210 }
211}
212
213impl std::error::Error for WriteRustError {}
214
215impl From<ScanError> for WriteRustError {
216 fn from(e: ScanError) -> Self {
217 WriteRustError::Scan(e)
218 }
219}
220
221impl From<crate::expand::ExpandError> for WriteRustError {
222 fn from(e: crate::expand::ExpandError) -> Self {
223 WriteRustError::Expand(e)
224 }
225}
226
227impl From<crate::unfold::UnfoldError> for WriteRustError {
228 fn from(e: crate::unfold::UnfoldError) -> Self {
229 WriteRustError::Unfold(e)
230 }
231}
232
233impl From<crate::resolve::ResolveError> for WriteRustError {
234 fn from(e: crate::resolve::ResolveError) -> Self {
235 WriteRustError::Resolve(e)
236 }
237}
238
239impl From<crate::write::WriteError> for WriteRustError {
240 fn from(e: crate::write::WriteError) -> Self {
241 WriteRustError::Write(e)
242 }
243}