1use indexmap::set::IndexSet as HashSet;
10
11use crate::minisyn::Ident;
12use itertools::Itertools;
13use miette::{Diagnostic, SourceSpan};
14use proc_macro2::Span;
15use thiserror::Error;
16
17use crate::{
18 known_types, proc_macro_span_to_miette_span,
19 types::{make_ident, InvalidIdentError, Namespace, QualifiedName},
20};
21
22#[derive(Debug, Clone, Error, Diagnostic)]
24pub enum ConvertError {
25 #[error("The initial run of 'bindgen' did not generate any content. This might be because none of the requested items for generation could be converted.")]
26 NoContent,
27 #[error(transparent)]
28 Cpp(ConvertErrorFromCpp),
29 #[error(transparent)]
30 #[diagnostic(transparent)]
31 Rust(LocatedConvertErrorFromRust),
32}
33
34#[derive(Debug, Clone, Error)]
39pub enum ConvertErrorFromCpp {
40 #[error("An item was requested using 'generate_pod' which was not safe to hold by value in Rust. {0}")]
41 UnsafePodType(String),
42 #[error("Bindgen generated some unexpected code in a foreign mod section. You may have specified something in a 'generate' directive which is not currently compatible with autocxx.")]
43 UnexpectedForeignItem,
44 #[error("Bindgen generated some unexpected code in an inner namespace mod. You may have specified something in a 'generate' directive which is not currently compatible with autocxx.")]
45 UnexpectedItemInMod,
46 #[error("autocxx was unable to produce a typdef pointing to the complex type {0}.")]
47 ComplexTypedefTarget(String),
48 #[error("Unexpected type for 'this' in the function {}.", .0.to_cpp_name())]
49 UnexpectedThisType(QualifiedName),
50 #[error("autocxx does not yet know how to support the built-in C++ type {} - please raise an issue on github", .0.to_cpp_name())]
51 UnsupportedBuiltInType(QualifiedName),
52 #[error("Type {} has templated arguments and so does the typedef to which it points", .0.to_cpp_name())]
53 ConflictingTemplatedArgsWithTypedef(QualifiedName),
54 #[error("Function {0} has a parameter or return type which is either on the blocklist or a forward declaration")]
55 UnacceptableParam(String),
56 #[error("Function {0} has a reference return value, but no reference parameters, so the lifetime of the output reference cannot be deduced.")]
57 NoInputReference(String),
58 #[error("Function {0} has a reference return value, but >1 input reference parameters, so the lifetime of the output reference cannot be deduced.")]
59 MultipleInputReferences(String),
60 #[error("Function {0} has a mutable reference return value, but no mutable reference parameters, so the lifetime of the output reference cannot be deduced.")]
61 NoMutableInputReference(String),
62 #[error("Function {0} has a mutable reference return value, but >1 input mutable reference parameters, so the lifetime of the output reference cannot be deduced.")]
63 MultipleMutableInputReferences(String),
64 #[error("Encountered type not yet supported by autocxx: {0}")]
65 UnsupportedType(String),
66 #[error("Encountered type not yet known by autocxx: {0}")]
67 UnknownType(String),
68 #[error("Encountered mutable static data, not yet supported: {0}")]
69 StaticData(String),
70 #[error("Encountered typedef to itself - this is a known bindgen bug: {0}")]
71 InfinitelyRecursiveTypedef(QualifiedName),
72 #[error("Unexpected 'use' statement encountered: {}", .0.as_ref().map(|s| s.as_str()).unwrap_or("<unknown>"))]
73 UnexpectedUseStatement(Option<String>),
74 #[error("Type {} was parameterized over something complex which we don't yet support", .0.to_cpp_name())]
75 TemplatedTypeContainingNonPathArg(QualifiedName),
76 #[error("Pointer pointed to an array, which is not yet supported")]
77 InvalidArrayPointee,
78 #[error("Pointer pointed to another pointer, which is not yet supported")]
79 InvalidPointerPointee,
80 #[error("Pointer pointed to something unsupported (autocxx only supports pointers to named types): {0}")]
81 InvalidPointee(String),
82 #[error("The 'generate' or 'generate_pod' directive for '{0}' did not result in any code being generated. Perhaps this was mis-spelled or you didn't qualify the name with any namespaces? Otherwise please report a bug.")]
83 DidNotGenerateAnything(String),
84 #[error("Found an attempt at using a forward declaration ({}) inside a templated cxx type such as UniquePtr or CxxVector. If the forward declaration is a typedef, perhaps autocxx wasn't sure whether or not it involved a forward declaration. If you're sure it didn't, then you may be able to solve this by using instantiable!.", .0.to_cpp_name())]
85 TypeContainingForwardDeclaration(QualifiedName),
86 #[error("Found an attempt at using a type marked as blocked! ({})", .0.to_cpp_name())]
87 Blocked(QualifiedName),
88 #[error("This function or method uses a type where one of the template parameters was incomprehensible to bindgen/autocxx - probably because it uses template specialization.")]
89 UnusedTemplateParam,
90 #[error("This item relies on a type not known to autocxx ({})", .0.to_cpp_name())]
91 UnknownDependentType(QualifiedName),
92 #[error("This item depends on some other type(s) which autocxx could not generate, some of them are: {}", .0.iter().join(", "))]
93 IgnoredDependent(HashSet<QualifiedName>),
94 #[error(transparent)]
95 InvalidIdent(InvalidIdentError),
96 #[error("This item name is used in multiple namespaces. At present, autocxx and cxx allow only one type of a given name. This limitation will be fixed in future. (Items found with this name: {})", .0.iter().join(", "))]
97 DuplicateCxxBridgeName(Vec<String>),
98 #[error("This is a method on a type which can't be used as the receiver in Rust (i.e. self/this). This is probably because some type involves template specialization.")]
99 UnsupportedReceiver,
100 #[error("A rust::Box<T> was encountered where T was not known to be a Rust type. Use rust_type!(T): {}", .0.to_cpp_name())]
101 BoxContainingNonRustType(QualifiedName),
102 #[error("A qualified Rust type was found (i.e. one containing ::): {}. Rust types must always be a simple identifier.", .0.to_cpp_name())]
103 RustTypeWithAPath(QualifiedName),
104 #[error("This type is nested within another struct/class, yet is abstract (or is not on the allowlist so we can't be sure). This is not yet supported by autocxx. If you don't believe this type is abstract, add it to the allowlist.")]
105 AbstractNestedType,
106 #[error("This typedef was nested within another struct/class. autocxx is unable to represent inner types if they might be abstract. Unfortunately, autocxx couldn't prove that this type isn't abstract, so it can't represent it.")]
107 NestedOpaqueTypedef,
108 #[error(
109 "This type is nested within another struct/class with protected or private visibility."
110 )]
111 NonPublicNestedType,
112 #[error("This function returns an rvalue reference (&&) which is not yet supported.")]
113 RValueReturn,
114 #[error("This method is private")]
115 PrivateMethod,
116 #[error("autocxx does not know how to generate bindings to operator=")]
117 AssignmentOperator,
118 #[error("This function was marked =delete")]
119 Deleted,
120 #[error("This structure has an rvalue reference field (&&) which is not yet supported.")]
121 RValueReferenceField,
122 #[error("This type was not on the allowlist, so we are not generating methods for it.")]
123 MethodOfNonAllowlistedType,
124 #[error("This type is templated, so we can't generate bindings. We will instead generate bindings for each instantiation.")]
125 MethodOfGenericType,
126 #[error("bindgen generated multiple different APIs (functions/types) with this name. autocxx doesn't know how to disambiguate them, so we won't generate bindings for any of them.")]
127 DuplicateItemsFoundInParsing,
128 #[error(
129 "bindgen generated a move or copy constructor with an unexpected number of parameters."
130 )]
131 ConstructorWithOnlyOneParam,
132 #[error("A copy or move constructor was found to take extra parameters. These are likely to be parameters with defaults, which are not yet supported by autocxx, so this constructor has been ignored.")]
133 ConstructorWithMultipleParams,
134 #[error("A C++ unique_ptr, shared_ptr or weak_ptr was found containing some type that cxx can't accommodate in that position ({})", .0.to_cpp_name())]
135 InvalidTypeForCppPtr(QualifiedName),
136 #[error("A C++ std::vector was found containing some type that cxx can't accommodate as a vector element ({})", .0.to_cpp_name())]
137 InvalidTypeForCppVector(QualifiedName),
138 #[error("Variadic functions are not supported by cxx or autocxx.")]
139 Variadic,
140 #[error("A type had a template inside a std::vector, which is not supported.")]
141 GenericsWithinVector,
142 #[error("This typedef takes generic parameters, not yet supported by autocxx.")]
143 TypedefTakesGenericParameters,
144 #[error("This method belonged to an item in an anonymous namespace, not currently supported.")]
145 MethodInAnonymousNamespace,
146 #[error("We're unable to make a concrete version of this template, because we found an error handling the template.")]
147 ConcreteVersionOfIgnoredTemplate,
148 #[error("This is a typedef to a type in an anonymous namespace, not currently supported.")]
149 TypedefToTypeInAnonymousNamespace,
150 #[error("This type refers to a generic type parameter of an outer type, which is not yet supported.")]
151 ReferringToGenericTypeParam,
152 #[error("This forward declaration was nested within another struct/class. autocxx is unable to represent inner types if they are forward declarations.")]
153 ForwardDeclaredNestedType,
154 #[error("Problem handling function argument {arg}: {err}")]
155 Argument {
156 arg: String,
157 #[source]
158 err: Box<ConvertErrorFromCpp>,
159 },
160}
161
162#[derive(Debug, Clone, Error)]
165pub enum ConvertErrorFromRust {
166 #[error("extern_rust_function only supports limited parameter and return types. This is not such a supported type")]
167 UnsupportedTypeForExternFun,
168 #[error("extern_rust_function requires a fully qualified receiver, that is: fn a(self: &SomeType) as opposed to fn a(&self)")]
169 ExternRustFunRequiresFullyQualifiedReceiver,
170 #[error("extern_rust_function cannot support &mut T references; instead use Pin<&mut T> (see cxx documentation for more details")]
171 PinnedReferencesRequiredForExternFun,
172 #[error("extern_rust_function cannot currently support qualified type paths (that is, foo::bar::Baz). All type paths must be within the current module, imported using 'use'. This restriction may be lifted in future.")]
173 NamespacesNotSupportedForExternFun,
174 #[error("extern_rust_function signatures must never reference Self: instead, spell out the type explicitly.")]
175 ExplicitSelf,
176}
177
178#[derive(Error, Debug, Diagnostic, Clone)]
181#[error("{err}")]
182pub struct LocatedConvertErrorFromRust {
183 err: ConvertErrorFromRust,
184 #[source_code]
185 file: String,
186 #[label("error here")]
187 span: SourceSpan,
188}
189
190impl LocatedConvertErrorFromRust {
191 pub(crate) fn new(err: ConvertErrorFromRust, span: &Span, file: &str) -> Self {
192 Self {
193 err,
194 span: proc_macro_span_to_miette_span(span),
195 file: file.to_string(),
196 }
197 }
198}
199
200#[derive(Clone, Debug)]
203struct PhantomSanitized;
204
205#[derive(Clone, Debug)]
209pub(crate) struct ErrorContext(Box<ErrorContextType>, PhantomSanitized);
210
211#[derive(Clone, Debug)]
213pub(crate) enum ErrorContextType {
214 Item(Ident),
215 SanitizedItem(Ident),
216 Method { self_ty: Ident, method: Ident },
217}
218
219impl ErrorContext {
220 pub(crate) fn new_for_item(id: Ident) -> Self {
221 match Self::sanitize_error_ident(&id) {
222 None => Self(Box::new(ErrorContextType::Item(id)), PhantomSanitized),
223 Some(sanitized) => Self(
224 Box::new(ErrorContextType::SanitizedItem(sanitized)),
225 PhantomSanitized,
226 ),
227 }
228 }
229
230 pub(crate) fn new_for_method(self_ty: Ident, method: Ident) -> Self {
231 match Self::sanitize_error_ident(&self_ty) {
235 None => Self(
236 Box::new(ErrorContextType::Method {
237 self_ty,
238 method: Self::sanitize_error_ident(&method).unwrap_or(method),
239 }),
240 PhantomSanitized,
241 ),
242 Some(_) => Self(
243 Box::new(ErrorContextType::SanitizedItem(make_ident(format!(
244 "{self_ty}_{method}"
245 )))),
246 PhantomSanitized,
247 ),
248 }
249 }
250
251 fn sanitize_error_ident(id: &Ident) -> Option<Ident> {
254 let qn = QualifiedName::new(&Namespace::new(), id.clone());
255 if known_types().conflicts_with_built_in_type(&qn) {
256 Some(make_ident(format!("{}_autocxx_error", qn.get_final_item())))
257 } else {
258 None
259 }
260 }
261
262 pub(crate) fn get_type(&self) -> &ErrorContextType {
263 &self.0
264 }
265
266 pub(crate) fn into_type(self) -> ErrorContextType {
267 *self.0
268 }
269}
270
271impl std::fmt::Display for ErrorContext {
272 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
273 match &*self.0 {
274 ErrorContextType::Item(id) | ErrorContextType::SanitizedItem(id) => write!(f, "{id}"),
275 ErrorContextType::Method { self_ty, method } => write!(f, "{self_ty}::{method}"),
276 }
277 }
278}
279
280#[derive(Clone)]
281pub(crate) struct ConvertErrorWithContext(
282 pub(crate) ConvertErrorFromCpp,
283 pub(crate) Option<ErrorContext>,
284);
285
286impl std::fmt::Debug for ConvertErrorWithContext {
287 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
288 write!(f, "{}", self.0)
289 }
290}
291
292impl std::fmt::Display for ConvertErrorWithContext {
293 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
294 write!(f, "{}", self.0)
295 }
296}