imping_codegen/error.rs
1//! Error and warning types.
2
3use core::fmt;
4use imping_ir::ValidationErrors;
5
6/// Why [`Generator::generate`](crate::Generator::generate) failed.
7///
8/// Messages name the module, item or build involved.
9#[derive(Debug)]
10#[non_exhaustive]
11pub enum Error {
12 /// The IR failed validation.
13 InvalidIr(ValidationErrors),
14 /// Two IR modules would get the same Rust module name.
15 ModuleNameClash {
16 /// The first IR module with that name.
17 first: String,
18 /// The second IR module with that name.
19 second: String,
20 /// The Rust module name both would get.
21 rust_name: String,
22 },
23 /// [`Generator::module_name`](crate::Generator::module_name) or
24 /// [`Generator::accept_build`](crate::Generator::accept_build) names a
25 /// module that isn't in the IR.
26 UnknownModule {
27 /// The option that named it: `"module_name"` or `"accept_build"`.
28 option: &'static str,
29 /// The IR module name given.
30 name: String,
31 },
32 /// [`Generator::module_name`](crate::Generator::module_name) was given a
33 /// name that isn't a valid Rust module name.
34 InvalidModuleName {
35 /// The Rust module name given.
36 name: String,
37 },
38 /// Two builds have the same label, or labels that make the same `Build`
39 /// variant.
40 DuplicateBuild {
41 /// The second build's label.
42 label: String,
43 },
44 /// A build's label isn't lowercase letters, digits and single
45 /// underscores, starting with a letter.
46 InvalidBuildLabel {
47 /// The label given.
48 label: String,
49 },
50 /// [`Generator::accept_build`](crate::Generator::accept_build) names a
51 /// build label no IR has.
52 UnknownBuild {
53 /// The label given.
54 label: String,
55 },
56 /// Two builds' IRs disagree on a type, a function's signature or a
57 /// global's type, so one set of bindings can't fit both.
58 BuildsDiffer(Box<BuildsDiffer>),
59}
60
61/// What two builds' IRs disagree on; see [`Error::BuildsDiffer`].
62///
63/// Its [`Display`](fmt::Display) is the error's message.
64#[derive(Debug, Clone, PartialEq, Eq)]
65#[non_exhaustive]
66pub struct BuildsDiffer {
67 /// The IR module the item is in.
68 pub module: String,
69 /// The item, such as ``type `cVec4` `` or ``function `Entity::Update` ``.
70 pub item: String,
71 /// The label of the build whose IR has it first.
72 pub first: String,
73 /// The label of the build that disagrees.
74 pub second: String,
75 /// How they differ, such as `size 0x10 vs 0x20`.
76 pub difference: String,
77}
78
79impl fmt::Display for BuildsDiffer {
80 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
81 let BuildsDiffer {
82 module,
83 item,
84 first,
85 second,
86 difference,
87 } = self;
88 write!(
89 f,
90 "`{module}`: {item} differs between builds `{first}` and `{second}`: {difference}"
91 )
92 }
93}
94
95impl fmt::Display for Error {
96 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
97 match self {
98 Error::InvalidIr(errors) => write!(f, "the IR is invalid: {errors}"),
99 Error::ModuleNameClash {
100 first,
101 second,
102 rust_name,
103 } => write!(
104 f,
105 "IR modules `{first}` and `{second}` both become Rust module `{rust_name}`; rename one"
106 ),
107 Error::UnknownModule { option, name } => {
108 write!(f, "`{option}` names `{name}`, which is not in the IR")
109 }
110 Error::InvalidModuleName { name } => {
111 write!(f, "`{name}` is not a valid Rust module name")
112 }
113 Error::DuplicateBuild { label } => {
114 write!(
115 f,
116 "build `{label}` is given twice, or as another label's `Build` variant"
117 )
118 }
119 Error::InvalidBuildLabel { label } => write!(
120 f,
121 "`{label}` is not a valid build label; use lowercase letters, digits and single \
122 underscores, starting with a letter, as in `steam_2021`"
123 ),
124 Error::UnknownBuild { label } => {
125 write!(f, "`accept_build` names build `{label}`, which no IR has")
126 }
127 Error::BuildsDiffer(differ) => differ.fmt(f),
128 }
129 }
130}
131
132impl core::error::Error for Error {
133 fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
134 match self {
135 Error::InvalidIr(errors) => Some(errors),
136 _ => None,
137 }
138 }
139}
140
141/// Something the generator skipped or simplified.
142///
143/// Warnings never stop generation. Their [`Display`](fmt::Display) names the
144/// module and item.
145#[derive(Debug, Clone, PartialEq, Eq)]
146#[non_exhaustive]
147pub enum Warning {
148 /// A function uses custom argument storage, so only its RVA is generated.
149 CustomStorage {
150 /// The IR module the function is in.
151 module: String,
152 /// The function's name, including its namespace.
153 function: String,
154 },
155 /// A struct or union's layout can't be represented in Rust, so it was
156 /// emitted as opaque bytes of its size.
157 TypeMadeOpaque {
158 /// The IR module the type is in.
159 module: String,
160 /// The type's IR name.
161 type_name: String,
162 /// Why its layout can't be represented.
163 reason: String,
164 },
165 /// A function was skipped.
166 FunctionSkipped {
167 /// The IR module the function is in.
168 module: String,
169 /// The function's name, including its namespace.
170 function: String,
171 /// Why it was skipped.
172 reason: String,
173 },
174 /// A function can be called but not hooked, so it has no `hook`.
175 ///
176 /// Not raised for variadic functions, which never get a hook.
177 HookUnavailable {
178 /// The IR module the function is in.
179 module: String,
180 /// The function's name, including its namespace.
181 function: String,
182 /// Why it can't be hooked.
183 reason: String,
184 },
185 /// A function is tagged `imping-safe`, but a parameter has no safe form,
186 /// so its call wrapper stays `unsafe`.
187 SafeCallUnavailable {
188 /// The IR module the function is in.
189 module: String,
190 /// The function's name, including its namespace.
191 function: String,
192 /// Why the call can't be safe.
193 reason: String,
194 },
195 /// An enum constant's value fits neither the signed nor the unsigned
196 /// range of the enum's underlying type, so it was truncated to fit.
197 ///
198 /// A value that fits the other signedness of the same width (such as -1
199 /// in a `u32` enum) wraps as in C, and is not reported.
200 EnumValueTruncated {
201 /// The IR module the enum is in.
202 module: String,
203 /// The enum's IR name.
204 enum_name: String,
205 /// The constant's IR name.
206 variant: String,
207 /// The constant's value in the IR.
208 value: i64,
209 },
210}
211
212impl fmt::Display for Warning {
213 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
214 match self {
215 Warning::CustomStorage { module, function } => write!(
216 f,
217 "`{module}` function `{function}` uses custom storage; only its RVA is generated"
218 ),
219 Warning::TypeMadeOpaque {
220 module,
221 type_name,
222 reason,
223 } => write!(
224 f,
225 "`{module}` type `{type_name}` emitted as opaque bytes: {reason}"
226 ),
227 Warning::FunctionSkipped {
228 module,
229 function,
230 reason,
231 } => write!(f, "`{module}` function `{function}` skipped: {reason}"),
232 Warning::HookUnavailable {
233 module,
234 function,
235 reason,
236 } => write!(
237 f,
238 "`{module}` function `{function}` can be called but not hooked: {reason}"
239 ),
240 Warning::SafeCallUnavailable {
241 module,
242 function,
243 reason,
244 } => write!(
245 f,
246 "`{module}` function `{function}` is tagged `imping-safe` but its call stays \
247 unsafe: {reason}"
248 ),
249 Warning::EnumValueTruncated {
250 module,
251 enum_name,
252 variant,
253 value,
254 } => write!(
255 f,
256 "`{module}` enum `{enum_name}` constant `{variant}` = {value} does not fit \
257 the underlying type and was truncated"
258 ),
259 }
260 }
261}