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