1use crate::syntax::map::UnorderedMap;
2use crate::syntax::set::{OrderedSet as Set, UnorderedSet};
3use crate::syntax::{Api, Enum, ExternFn, NamedType, Pair, Struct, Type};
4use proc_macro2::Ident;
5use std::fmt::{self, Display};
6
7#[derive(Copy, Clone)]
8pub(crate) enum TrivialReason<'a> {
9 StructField(&'a Struct),
10 FunctionArgument(&'a ExternFn),
11 FunctionReturn(&'a ExternFn),
12 BoxTarget,
13 VecElement,
14 SliceElement { mutable: bool },
15 UnpinnedMut(&'a ExternFn),
16}
17
18pub(crate) fn required_trivial_reasons<'a>(
19 apis: &'a [Api],
20 all: &Set<&'a Type>,
21 structs: &UnorderedMap<&'a Ident, &'a Struct>,
22 enums: &UnorderedMap<&'a Ident, &'a Enum>,
23 cxx: &UnorderedSet<&'a Ident>,
24) -> UnorderedMap<&'a Ident, Vec<TrivialReason<'a>>> {
25 let mut required_trivial = UnorderedMap::new();
26
27 let mut insist_extern_types_are_trivial = |ident: &'a NamedType, reason| {
28 if cxx.contains(&ident.rust)
29 && !structs.contains_key(&ident.rust)
30 && !enums.contains_key(&ident.rust)
31 {
32 required_trivial
33 .entry(&ident.rust)
34 .or_insert_with(Vec::new)
35 .push(reason);
36 }
37 };
38
39 for api in apis {
40 match api {
41 Api::Struct(strct) => {
42 for field in &strct.fields {
43 if let Type::Ident(ident) = &field.ty {
44 let reason = TrivialReason::StructField(strct);
45 insist_extern_types_are_trivial(ident, reason);
46 }
47 }
48 }
49 Api::CxxFunction(efn) | Api::RustFunction(efn) => {
50 if let Some(receiver) = &efn.receiver {
51 if receiver.mutable && !receiver.pinned {
52 let reason = TrivialReason::UnpinnedMut(efn);
53 insist_extern_types_are_trivial(&receiver.ty, reason);
54 }
55 }
56 for arg in &efn.args {
57 match &arg.ty {
58 Type::Ident(ident) => {
59 let reason = TrivialReason::FunctionArgument(efn);
60 insist_extern_types_are_trivial(ident, reason);
61 }
62 Type::Ref(ty) => {
63 if ty.mutable && !ty.pinned {
64 if let Type::Ident(ident) = &ty.inner {
65 let reason = TrivialReason::UnpinnedMut(efn);
66 insist_extern_types_are_trivial(ident, reason);
67 }
68 }
69 }
70 _ => {}
71 }
72 }
73 if let Some(ret) = &efn.ret {
74 match ret {
75 Type::Ident(ident) => {
76 let reason = TrivialReason::FunctionReturn(efn);
77 insist_extern_types_are_trivial(ident, reason);
78 }
79 Type::Ref(ty) => {
80 if ty.mutable && !ty.pinned {
81 if let Type::Ident(ident) = &ty.inner {
82 let reason = TrivialReason::UnpinnedMut(efn);
83 insist_extern_types_are_trivial(ident, reason);
84 }
85 }
86 }
87 _ => {}
88 }
89 }
90 }
91 _ => {}
92 }
93 }
94
95 for ty in all {
96 match ty {
97 Type::RustBox(ty) => {
98 if let Type::Ident(ident) = &ty.inner {
99 let reason = TrivialReason::BoxTarget;
100 insist_extern_types_are_trivial(ident, reason);
101 }
102 }
103 Type::RustVec(ty) => {
104 if let Type::Ident(ident) = &ty.inner {
105 let reason = TrivialReason::VecElement;
106 insist_extern_types_are_trivial(ident, reason);
107 }
108 }
109 Type::SliceRef(ty) => {
110 if let Type::Ident(ident) = &ty.inner {
111 let reason = TrivialReason::SliceElement {
112 mutable: ty.mutable,
113 };
114 insist_extern_types_are_trivial(ident, reason);
115 }
116 }
117 _ => {}
118 }
119 }
120
121 required_trivial
122}
123
124pub(crate) fn as_what<'a>(name: &'a Pair, reasons: &'a [TrivialReason]) -> impl Display + 'a {
128 struct Description<'a> {
129 name: &'a Pair,
130 reasons: &'a [TrivialReason<'a>],
131 }
132
133 impl<'a> Display for Description<'a> {
134 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
135 let mut field_of = Set::new();
136 let mut argument_of = Set::new();
137 let mut return_of = Set::new();
138 let mut box_target = false;
139 let mut vec_element = false;
140 let mut slice_shared_element = false;
141 let mut slice_mut_element = false;
142 let mut unpinned_mut = Set::new();
143
144 for reason in self.reasons {
145 match reason {
146 TrivialReason::StructField(strct) => {
147 field_of.insert(&strct.name.rust);
148 }
149 TrivialReason::FunctionArgument(efn) => {
150 argument_of.insert(&efn.name.rust);
151 }
152 TrivialReason::FunctionReturn(efn) => {
153 return_of.insert(&efn.name.rust);
154 }
155 TrivialReason::BoxTarget => box_target = true,
156 TrivialReason::VecElement => vec_element = true,
157 TrivialReason::SliceElement { mutable } => {
158 if *mutable {
159 slice_mut_element = true;
160 } else {
161 slice_shared_element = true;
162 }
163 }
164 TrivialReason::UnpinnedMut(efn) => {
165 unpinned_mut.insert(&efn.name.rust);
166 }
167 }
168 }
169
170 let mut clauses = Vec::new();
171 if !field_of.is_empty() {
172 clauses.push(Clause::Set {
173 article: "a",
174 desc: "field of",
175 set: &field_of,
176 });
177 }
178 if !argument_of.is_empty() {
179 clauses.push(Clause::Set {
180 article: "an",
181 desc: "argument of",
182 set: &argument_of,
183 });
184 }
185 if !return_of.is_empty() {
186 clauses.push(Clause::Set {
187 article: "a",
188 desc: "return value of",
189 set: &return_of,
190 });
191 }
192 if box_target {
193 clauses.push(Clause::Ty1 {
194 article: "type",
195 desc: "Box",
196 param: self.name,
197 });
198 }
199 if vec_element {
200 clauses.push(Clause::Ty1 {
201 article: "a",
202 desc: "vector element in Vec",
203 param: self.name,
204 });
205 }
206 if slice_shared_element || slice_mut_element {
207 clauses.push(Clause::Slice {
208 article: "a",
209 desc: "slice element in",
210 shared: slice_shared_element,
211 mutable: slice_mut_element,
212 param: self.name,
213 });
214 }
215 if !unpinned_mut.is_empty() {
216 clauses.push(Clause::Set {
217 article: "a",
218 desc: "non-pinned mutable reference in signature of",
219 set: &unpinned_mut,
220 });
221 }
222
223 for (i, clause) in clauses.iter().enumerate() {
224 if i == 0 {
225 write!(f, "{} ", clause.article())?;
226 } else if i + 1 < clauses.len() {
227 write!(f, ", ")?;
228 } else {
229 write!(f, " or ")?;
230 }
231 clause.fmt(f)?;
232 }
233
234 Ok(())
235 }
236 }
237
238 enum Clause<'a> {
239 Set {
240 article: &'a str,
241 desc: &'a str,
242 set: &'a Set<&'a Ident>,
243 },
244 Ty1 {
245 article: &'a str,
246 desc: &'a str,
247 param: &'a Pair,
248 },
249 Slice {
250 article: &'a str,
251 desc: &'a str,
252 shared: bool,
253 mutable: bool,
254 param: &'a Pair,
255 },
256 }
257
258 impl<'a> Clause<'a> {
259 fn article(&self) -> &'a str {
260 match self {
261 Clause::Set { article, .. }
262 | Clause::Ty1 { article, .. }
263 | Clause::Slice { article, .. } => article,
264 }
265 }
266
267 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
268 match self {
269 Clause::Set {
270 article: _,
271 desc,
272 set,
273 } => {
274 write!(f, "{} ", desc)?;
275 for (i, ident) in set.iter().take(3).enumerate() {
276 if i > 0 {
277 write!(f, ", ")?;
278 }
279 write!(f, "`{}`", ident)?;
280 }
281 Ok(())
282 }
283 Clause::Ty1 {
284 article: _,
285 desc,
286 param,
287 } => write!(f, "{}<{}>", desc, param.rust),
288 Clause::Slice {
289 article: _,
290 desc,
291 shared,
292 mutable,
293 param,
294 } => {
295 write!(f, "{} ", desc)?;
296 if *shared {
297 write!(f, "&[{}]", param.rust)?;
298 }
299 if *shared && *mutable {
300 write!(f, " and ")?;
301 }
302 if *mutable {
303 write!(f, "&mut [{}]", param.rust)?;
304 }
305 Ok(())
306 }
307 }
308 }
309 }
310
311 Description { name, reasons }
312}