blob: 855b2359dc1cb8cd4fb7c63255c0b326d893bf4e [file] [log] [blame]
Brian Silverman4e662aa2022-05-11 23:10:19 -07001// Copyright 2020 Google LLC
2//
3// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
5// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
6// option. This file may not be copied, modified, or distributed
7// except according to those terms.
8
9use indexmap::set::IndexSet as HashSet;
10
Austin Schuh6ea9bfa2023-08-06 19:05:10 -070011use crate::minisyn::Ident;
Brian Silverman4e662aa2022-05-11 23:10:19 -070012use itertools::Itertools;
Austin Schuh6ea9bfa2023-08-06 19:05:10 -070013use miette::{Diagnostic, SourceSpan};
14use proc_macro2::Span;
Brian Silverman4e662aa2022-05-11 23:10:19 -070015use thiserror::Error;
16
17use crate::{
Austin Schuh6ea9bfa2023-08-06 19:05:10 -070018 known_types, proc_macro_span_to_miette_span,
19 types::{make_ident, InvalidIdentError, Namespace, QualifiedName},
Brian Silverman4e662aa2022-05-11 23:10:19 -070020};
21
Austin Schuh6ea9bfa2023-08-06 19:05:10 -070022/// Errors which can occur during conversion
23#[derive(Debug, Clone, Error, Diagnostic)]
Brian Silverman4e662aa2022-05-11 23:10:19 -070024pub 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,
Austin Schuh6ea9bfa2023-08-06 19:05:10 -070027 #[error(transparent)]
28 Cpp(ConvertErrorFromCpp),
29 #[error(transparent)]
30 #[diagnostic(transparent)]
31 Rust(LocatedConvertErrorFromRust),
32}
33
34/// Errors that can occur during conversion which are detected from some C++
35/// source code. Currently, we do not gain span information from bindgen
36/// so these errors are presented without useful source code snippets.
37/// We hope to change this in future.
38#[derive(Debug, Clone, Error)]
39pub enum ConvertErrorFromCpp {
Brian Silverman4e662aa2022-05-11 23:10:19 -070040 #[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 its outermost mod section. You may have specified something in a 'generate' directive which is not currently compatible with autocxx.")]
45 UnexpectedOuterItem,
46 #[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.")]
47 UnexpectedItemInMod,
48 #[error("autocxx was unable to produce a typdef pointing to the complex type {0}.")]
49 ComplexTypedefTarget(String),
50 #[error("Unexpected type for 'this' in the function {}.", .0.to_cpp_name())]
51 UnexpectedThisType(QualifiedName),
52 #[error("autocxx does not yet know how to support the built-in C++ type {} - please raise an issue on github", .0.to_cpp_name())]
53 UnsupportedBuiltInType(QualifiedName),
54 #[error("Type {} has templated arguments and so does the typedef to which it points", .0.to_cpp_name())]
55 ConflictingTemplatedArgsWithTypedef(QualifiedName),
56 #[error("Function {0} has a parameter or return type which is either on the blocklist or a forward declaration")]
57 UnacceptableParam(String),
Austin Schuh6ea9bfa2023-08-06 19:05:10 -070058 #[error("Function {0} has a reference return value, but no reference parameters, so the lifetime of the output reference cannot be deduced.")]
59 NoInputReference(String),
60 #[error("Function {0} has a reference return value, but >1 input reference parameters, so the lifetime of the output reference cannot be deduced.")]
61 MultipleInputReferences(String),
62 #[error("Function {0} has a mutable reference return value, but no mutable reference parameters, so the lifetime of the output reference cannot be deduced.")]
63 NoMutableInputReference(String),
64 #[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.")]
65 MultipleMutableInputReferences(String),
Brian Silverman4e662aa2022-05-11 23:10:19 -070066 #[error("Encountered type not yet supported by autocxx: {0}")]
67 UnsupportedType(String),
68 #[error("Encountered type not yet known by autocxx: {0}")]
69 UnknownType(String),
70 #[error("Encountered mutable static data, not yet supported: {0}")]
71 StaticData(String),
72 #[error("Encountered typedef to itself - this is a known bindgen bug: {0}")]
73 InfinitelyRecursiveTypedef(QualifiedName),
74 #[error("Unexpected 'use' statement encountered: {}", .0.as_ref().map(|s| s.as_str()).unwrap_or("<unknown>"))]
75 UnexpectedUseStatement(Option<String>),
76 #[error("Type {} was parameterized over something complex which we don't yet support", .0.to_cpp_name())]
77 TemplatedTypeContainingNonPathArg(QualifiedName),
Austin Schuh6ea9bfa2023-08-06 19:05:10 -070078 #[error("Pointer pointed to an array, which is not yet supported")]
79 InvalidArrayPointee,
80 #[error("Pointer pointed to another pointer, which is not yet supported")]
81 InvalidPointerPointee,
82 #[error("Pointer pointed to something unsupported (autocxx only supports pointers to named types): {0}")]
83 InvalidPointee(String),
Brian Silverman4e662aa2022-05-11 23:10:19 -070084 #[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.")]
85 DidNotGenerateAnything(String),
86 #[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())]
87 TypeContainingForwardDeclaration(QualifiedName),
88 #[error("Found an attempt at using a type marked as blocked! ({})", .0.to_cpp_name())]
89 Blocked(QualifiedName),
90 #[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.")]
91 UnusedTemplateParam,
Brian Silverman4e662aa2022-05-11 23:10:19 -070092 #[error("This item relies on a type not known to autocxx ({})", .0.to_cpp_name())]
93 UnknownDependentType(QualifiedName),
94 #[error("This item depends on some other type(s) which autocxx could not generate, some of them are: {}", .0.iter().join(", "))]
95 IgnoredDependent(HashSet<QualifiedName>),
Austin Schuh6ea9bfa2023-08-06 19:05:10 -070096 #[error(transparent)]
97 InvalidIdent(InvalidIdentError),
Brian Silverman4e662aa2022-05-11 23:10:19 -070098 #[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(", "))]
99 DuplicateCxxBridgeName(Vec<String>),
100 #[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.")]
101 UnsupportedReceiver,
102 #[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())]
103 BoxContainingNonRustType(QualifiedName),
104 #[error("A qualified Rust type was found (i.e. one containing ::): {}. Rust types must always be a simple identifier.", .0.to_cpp_name())]
105 RustTypeWithAPath(QualifiedName),
106 #[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.")]
107 AbstractNestedType,
108 #[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.")]
109 NestedOpaqueTypedef,
110 #[error(
111 "This type is nested within another struct/class with protected or private visibility."
112 )]
113 NonPublicNestedType,
114 #[error("This function returns an rvalue reference (&&) which is not yet supported.")]
115 RValueReturn,
116 #[error("This method is private")]
117 PrivateMethod,
118 #[error("autocxx does not know how to generate bindings to operator=")]
119 AssignmentOperator,
120 #[error("This function was marked =delete")]
121 Deleted,
122 #[error("This structure has an rvalue reference field (&&) which is not yet supported.")]
123 RValueReferenceField,
124 #[error("This type was not on the allowlist, so we are not generating methods for it.")]
125 MethodOfNonAllowlistedType,
126 #[error("This type is templated, so we can't generate bindings. We will instead generate bindings for each instantiation.")]
127 MethodOfGenericType,
128 #[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.")]
129 DuplicateItemsFoundInParsing,
130 #[error(
131 "bindgen generated a move or copy constructor with an unexpected number of parameters."
132 )]
133 ConstructorWithOnlyOneParam,
134 #[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.")]
135 ConstructorWithMultipleParams,
136 #[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())]
137 InvalidTypeForCppPtr(QualifiedName),
138 #[error("A C++ std::vector was found containing some type that cxx can't accommodate as a vector element ({})", .0.to_cpp_name())]
139 InvalidTypeForCppVector(QualifiedName),
140 #[error("Variadic functions are not supported by cxx or autocxx.")]
141 Variadic,
142 #[error("A type had a template inside a std::vector, which is not supported.")]
143 GenericsWithinVector,
144 #[error("This typedef takes generic parameters, not yet supported by autocxx.")]
145 TypedefTakesGenericParameters,
146 #[error("This method belonged to an item in an anonymous namespace, not currently supported.")]
147 MethodInAnonymousNamespace,
148 #[error("We're unable to make a concrete version of this template, because we found an error handling the template.")]
149 ConcreteVersionOfIgnoredTemplate,
Brian Silvermanf3ec38b2022-07-06 20:43:36 -0700150 #[error("This is a typedef to a type in an anonymous namespace, not currently supported.")]
151 TypedefToTypeInAnonymousNamespace,
152 #[error("This type refers to a generic type parameter of an outer type, which is not yet supported.")]
153 ReferringToGenericTypeParam,
154 #[error("This forward declaration was nested within another struct/class. autocxx is unable to represent inner types if they are forward declarations.")]
155 ForwardDeclaredNestedType,
Austin Schuh6ea9bfa2023-08-06 19:05:10 -0700156 #[error("Problem handling function argument {arg}: {err}")]
157 Argument {
158 arg: String,
159 #[source]
160 err: Box<ConvertErrorFromCpp>,
161 },
162}
163
164/// Error types derived from Rust code. This is separate from [`ConvertError`] because these
165/// may have spans attached for better diagnostics.
166#[derive(Debug, Clone, Error)]
167pub enum ConvertErrorFromRust {
168 #[error("extern_rust_function only supports limited parameter and return types. This is not such a supported type")]
169 UnsupportedTypeForExternFun,
170 #[error("extern_rust_function requires a fully qualified receiver, that is: fn a(self: &SomeType) as opposed to fn a(&self)")]
171 ExternRustFunRequiresFullyQualifiedReceiver,
172 #[error("extern_rust_function cannot support &mut T references; instead use Pin<&mut T> (see cxx documentation for more details")]
173 PinnedReferencesRequiredForExternFun,
174 #[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.")]
175 NamespacesNotSupportedForExternFun,
176 #[error("extern_rust_function signatures must never reference Self: instead, spell out the type explicitly.")]
177 ExplicitSelf,
178}
179
180/// A [`ConvertErrorFromRust`] which also implements [`miette::Diagnostic`] so can be pretty-printed
181/// to show the affected span of code.
182#[derive(Error, Debug, Diagnostic, Clone)]
183#[error("{err}")]
184pub struct LocatedConvertErrorFromRust {
185 err: ConvertErrorFromRust,
186 #[source_code]
187 file: String,
188 #[label("error here")]
189 span: SourceSpan,
190}
191
192impl LocatedConvertErrorFromRust {
193 pub(crate) fn new(err: ConvertErrorFromRust, span: &Span, file: &str) -> Self {
194 Self {
195 err,
196 span: proc_macro_span_to_miette_span(span),
197 file: file.to_string(),
198 }
199 }
Brian Silverman4e662aa2022-05-11 23:10:19 -0700200}
201
202/// Ensures that error contexts are always created using the constructors in this
203/// mod, therefore undergoing identifier sanitation.
Austin Schuh6ea9bfa2023-08-06 19:05:10 -0700204#[derive(Clone, Debug)]
Brian Silverman4e662aa2022-05-11 23:10:19 -0700205struct PhantomSanitized;
206
207/// The context of an error, e.g. whether it applies to a function or a method.
208/// This is used to generate suitable rustdoc in the output codegen so that
209/// the errors can be revealed in rust-analyzer-based IDEs, etc.
Austin Schuh6ea9bfa2023-08-06 19:05:10 -0700210#[derive(Clone, Debug)]
211pub(crate) struct ErrorContext(Box<ErrorContextType>, PhantomSanitized);
Brian Silverman4e662aa2022-05-11 23:10:19 -0700212
213/// All idents in this structure are guaranteed to be something we can safely codegen for.
Austin Schuh6ea9bfa2023-08-06 19:05:10 -0700214#[derive(Clone, Debug)]
Brian Silverman4e662aa2022-05-11 23:10:19 -0700215pub(crate) enum ErrorContextType {
216 Item(Ident),
217 SanitizedItem(Ident),
218 Method { self_ty: Ident, method: Ident },
219}
220
221impl ErrorContext {
222 pub(crate) fn new_for_item(id: Ident) -> Self {
223 match Self::sanitize_error_ident(&id) {
Austin Schuh6ea9bfa2023-08-06 19:05:10 -0700224 None => Self(Box::new(ErrorContextType::Item(id)), PhantomSanitized),
225 Some(sanitized) => Self(
226 Box::new(ErrorContextType::SanitizedItem(sanitized)),
227 PhantomSanitized,
228 ),
Brian Silverman4e662aa2022-05-11 23:10:19 -0700229 }
230 }
231
232 pub(crate) fn new_for_method(self_ty: Ident, method: Ident) -> Self {
233 // If this IgnoredItem relates to a method on a self_ty which we can't represent,
234 // e.g. u8, then forget about trying to attach this error text to something within
235 // an impl block.
236 match Self::sanitize_error_ident(&self_ty) {
237 None => Self(
Austin Schuh6ea9bfa2023-08-06 19:05:10 -0700238 Box::new(ErrorContextType::Method {
Brian Silverman4e662aa2022-05-11 23:10:19 -0700239 self_ty,
240 method: Self::sanitize_error_ident(&method).unwrap_or(method),
Austin Schuh6ea9bfa2023-08-06 19:05:10 -0700241 }),
Brian Silverman4e662aa2022-05-11 23:10:19 -0700242 PhantomSanitized,
243 ),
244 Some(_) => Self(
Austin Schuh6ea9bfa2023-08-06 19:05:10 -0700245 Box::new(ErrorContextType::SanitizedItem(make_ident(format!(
246 "{self_ty}_{method}"
247 )))),
Brian Silverman4e662aa2022-05-11 23:10:19 -0700248 PhantomSanitized,
249 ),
250 }
251 }
252
253 /// Because errors may be generated for invalid types or identifiers,
254 /// we may need to scrub the name
255 fn sanitize_error_ident(id: &Ident) -> Option<Ident> {
256 let qn = QualifiedName::new(&Namespace::new(), id.clone());
257 if known_types().conflicts_with_built_in_type(&qn) {
258 Some(make_ident(format!("{}_autocxx_error", qn.get_final_item())))
259 } else {
260 None
261 }
262 }
263
264 pub(crate) fn get_type(&self) -> &ErrorContextType {
265 &self.0
266 }
267
268 pub(crate) fn into_type(self) -> ErrorContextType {
Austin Schuh6ea9bfa2023-08-06 19:05:10 -0700269 *self.0
Brian Silverman4e662aa2022-05-11 23:10:19 -0700270 }
271}
272
273impl std::fmt::Display for ErrorContext {
274 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
Austin Schuh6ea9bfa2023-08-06 19:05:10 -0700275 match &*self.0 {
276 ErrorContextType::Item(id) | ErrorContextType::SanitizedItem(id) => write!(f, "{id}"),
277 ErrorContextType::Method { self_ty, method } => write!(f, "{self_ty}::{method}"),
Brian Silverman4e662aa2022-05-11 23:10:19 -0700278 }
279 }
280}
281
282#[derive(Clone)]
Austin Schuh6ea9bfa2023-08-06 19:05:10 -0700283pub(crate) struct ConvertErrorWithContext(
284 pub(crate) ConvertErrorFromCpp,
285 pub(crate) Option<ErrorContext>,
286);
Brian Silverman4e662aa2022-05-11 23:10:19 -0700287
288impl std::fmt::Debug for ConvertErrorWithContext {
289 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
290 write!(f, "{}", self.0)
291 }
292}
293
294impl std::fmt::Display for ConvertErrorWithContext {
295 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
296 write!(f, "{}", self.0)
297 }
298}