blob: 0de4f19d7239021e468258d879153b9d44263d35 [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
11use itertools::Itertools;
12use syn::Ident;
13use thiserror::Error;
14
15use crate::{
16 known_types,
17 types::{make_ident, Namespace, QualifiedName},
18};
19
20#[derive(Debug, Clone, Error)]
21pub enum ConvertError {
22 #[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.")]
23 NoContent,
24 #[error("An item was requested using 'generate_pod' which was not safe to hold by value in Rust. {0}")]
25 UnsafePodType(String),
26 #[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.")]
27 UnexpectedForeignItem,
28 #[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.")]
29 UnexpectedOuterItem,
30 #[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.")]
31 UnexpectedItemInMod,
32 #[error("autocxx was unable to produce a typdef pointing to the complex type {0}.")]
33 ComplexTypedefTarget(String),
34 #[error("Unexpected type for 'this' in the function {}.", .0.to_cpp_name())]
35 UnexpectedThisType(QualifiedName),
36 #[error("autocxx does not yet know how to support the built-in C++ type {} - please raise an issue on github", .0.to_cpp_name())]
37 UnsupportedBuiltInType(QualifiedName),
38 #[error("Type {} has templated arguments and so does the typedef to which it points", .0.to_cpp_name())]
39 ConflictingTemplatedArgsWithTypedef(QualifiedName),
40 #[error("Function {0} has a parameter or return type which is either on the blocklist or a forward declaration")]
41 UnacceptableParam(String),
42 #[error("Function {0} has a return reference parameter, but 0 or >1 input reference parameters, so the lifetime of the output reference cannot be deduced.")]
43 NotOneInputReference(String),
44 #[error("Encountered type not yet supported by autocxx: {0}")]
45 UnsupportedType(String),
46 #[error("Encountered type not yet known by autocxx: {0}")]
47 UnknownType(String),
48 #[error("Encountered mutable static data, not yet supported: {0}")]
49 StaticData(String),
50 #[error("Encountered typedef to itself - this is a known bindgen bug: {0}")]
51 InfinitelyRecursiveTypedef(QualifiedName),
52 #[error("Unexpected 'use' statement encountered: {}", .0.as_ref().map(|s| s.as_str()).unwrap_or("<unknown>"))]
53 UnexpectedUseStatement(Option<String>),
54 #[error("Type {} was parameterized over something complex which we don't yet support", .0.to_cpp_name())]
55 TemplatedTypeContainingNonPathArg(QualifiedName),
56 #[error("Pointer pointed to something unsupported")]
57 InvalidPointee,
58 #[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.")]
59 DidNotGenerateAnything(String),
60 #[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())]
61 TypeContainingForwardDeclaration(QualifiedName),
62 #[error("Found an attempt at using a type marked as blocked! ({})", .0.to_cpp_name())]
63 Blocked(QualifiedName),
64 #[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.")]
65 UnusedTemplateParam,
66 #[error("Names containing __ are reserved by C++ so not acceptable to cxx")]
67 TooManyUnderscores,
68 #[error("This item relies on a type not known to autocxx ({})", .0.to_cpp_name())]
69 UnknownDependentType(QualifiedName),
70 #[error("This item depends on some other type(s) which autocxx could not generate, some of them are: {}", .0.iter().join(", "))]
71 IgnoredDependent(HashSet<QualifiedName>),
72 #[error("The item name '{0}' is a reserved word in Rust.")]
73 ReservedName(String),
74 #[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(", "))]
75 DuplicateCxxBridgeName(Vec<String>),
76 #[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.")]
77 UnsupportedReceiver,
78 #[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())]
79 BoxContainingNonRustType(QualifiedName),
80 #[error("A qualified Rust type was found (i.e. one containing ::): {}. Rust types must always be a simple identifier.", .0.to_cpp_name())]
81 RustTypeWithAPath(QualifiedName),
82 #[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.")]
83 AbstractNestedType,
84 #[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.")]
85 NestedOpaqueTypedef,
86 #[error(
87 "This type is nested within another struct/class with protected or private visibility."
88 )]
89 NonPublicNestedType,
90 #[error("This function returns an rvalue reference (&&) which is not yet supported.")]
91 RValueReturn,
92 #[error("This method is private")]
93 PrivateMethod,
94 #[error("autocxx does not know how to generate bindings to operator=")]
95 AssignmentOperator,
96 #[error("This function was marked =delete")]
97 Deleted,
98 #[error("This structure has an rvalue reference field (&&) which is not yet supported.")]
99 RValueReferenceField,
100 #[error("This type was not on the allowlist, so we are not generating methods for it.")]
101 MethodOfNonAllowlistedType,
102 #[error("This type is templated, so we can't generate bindings. We will instead generate bindings for each instantiation.")]
103 MethodOfGenericType,
104 #[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.")]
105 DuplicateItemsFoundInParsing,
106 #[error(
107 "bindgen generated a move or copy constructor with an unexpected number of parameters."
108 )]
109 ConstructorWithOnlyOneParam,
110 #[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.")]
111 ConstructorWithMultipleParams,
112 #[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())]
113 InvalidTypeForCppPtr(QualifiedName),
114 #[error("A C++ std::vector was found containing some type that cxx can't accommodate as a vector element ({})", .0.to_cpp_name())]
115 InvalidTypeForCppVector(QualifiedName),
116 #[error("Variadic functions are not supported by cxx or autocxx.")]
117 Variadic,
118 #[error("A type had a template inside a std::vector, which is not supported.")]
119 GenericsWithinVector,
120 #[error("This typedef takes generic parameters, not yet supported by autocxx.")]
121 TypedefTakesGenericParameters,
122 #[error("This method belonged to an item in an anonymous namespace, not currently supported.")]
123 MethodInAnonymousNamespace,
124 #[error("We're unable to make a concrete version of this template, because we found an error handling the template.")]
125 ConcreteVersionOfIgnoredTemplate,
126 #[error("bindgen decided to call this type _bindgen_ty_N because it couldn't deduce the correct name for it. That means we can't generate C++ bindings to it.")]
127 BindgenTy,
128}
129
130/// Ensures that error contexts are always created using the constructors in this
131/// mod, therefore undergoing identifier sanitation.
132#[derive(Clone)]
133struct PhantomSanitized;
134
135/// The context of an error, e.g. whether it applies to a function or a method.
136/// This is used to generate suitable rustdoc in the output codegen so that
137/// the errors can be revealed in rust-analyzer-based IDEs, etc.
138#[derive(Clone)]
139pub(crate) struct ErrorContext(ErrorContextType, PhantomSanitized);
140
141/// All idents in this structure are guaranteed to be something we can safely codegen for.
142#[derive(Clone)]
143pub(crate) enum ErrorContextType {
144 Item(Ident),
145 SanitizedItem(Ident),
146 Method { self_ty: Ident, method: Ident },
147}
148
149impl ErrorContext {
150 pub(crate) fn new_for_item(id: Ident) -> Self {
151 match Self::sanitize_error_ident(&id) {
152 None => Self(ErrorContextType::Item(id), PhantomSanitized),
153 Some(sanitized) => Self(ErrorContextType::SanitizedItem(sanitized), PhantomSanitized),
154 }
155 }
156
157 pub(crate) fn new_for_method(self_ty: Ident, method: Ident) -> Self {
158 // If this IgnoredItem relates to a method on a self_ty which we can't represent,
159 // e.g. u8, then forget about trying to attach this error text to something within
160 // an impl block.
161 match Self::sanitize_error_ident(&self_ty) {
162 None => Self(
163 ErrorContextType::Method {
164 self_ty,
165 method: Self::sanitize_error_ident(&method).unwrap_or(method),
166 },
167 PhantomSanitized,
168 ),
169 Some(_) => Self(
170 ErrorContextType::SanitizedItem(make_ident(format!("{}_{}", self_ty, method))),
171 PhantomSanitized,
172 ),
173 }
174 }
175
176 /// Because errors may be generated for invalid types or identifiers,
177 /// we may need to scrub the name
178 fn sanitize_error_ident(id: &Ident) -> Option<Ident> {
179 let qn = QualifiedName::new(&Namespace::new(), id.clone());
180 if known_types().conflicts_with_built_in_type(&qn) {
181 Some(make_ident(format!("{}_autocxx_error", qn.get_final_item())))
182 } else {
183 None
184 }
185 }
186
187 pub(crate) fn get_type(&self) -> &ErrorContextType {
188 &self.0
189 }
190
191 pub(crate) fn into_type(self) -> ErrorContextType {
192 self.0
193 }
194}
195
196impl std::fmt::Display for ErrorContext {
197 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
198 match &self.0 {
199 ErrorContextType::Item(id) | ErrorContextType::SanitizedItem(id) => write!(f, "{}", id),
200 ErrorContextType::Method { self_ty, method } => write!(f, "{}::{}", self_ty, method),
201 }
202 }
203}
204
205#[derive(Clone)]
206pub(crate) struct ConvertErrorWithContext(pub(crate) ConvertError, pub(crate) Option<ErrorContext>);
207
208impl std::fmt::Debug for ConvertErrorWithContext {
209 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
210 write!(f, "{}", self.0)
211 }
212}
213
214impl std::fmt::Display for ConvertErrorWithContext {
215 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
216 write!(f, "{}", self.0)
217 }
218}