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