Skip to main content

duchess_reflect/
check.rs

1use std::collections::HashSet;
2
3use crate::{
4    class_info::{ClassInfo, ClassRef, Constructor, Flags, Method, RefType, RootMap, Type},
5    reflect::Reflector,
6};
7
8impl RootMap {
9    pub fn check(&self, reflector: &mut Reflector) -> syn::Result<()> {
10        let mut errors = vec![];
11
12        for class_name in &self.class_names() {
13            let ci = self.find_class(class_name).unwrap();
14            ci.check(self, reflector, &mut |e| errors.push(e))?;
15        }
16
17        match errors.into_iter().reduce(|mut error, next| {
18            error.combine(next);
19            error
20        }) {
21            Some(e) => Err(e),
22            None => Ok(()),
23        }
24    }
25}
26
27impl ClassInfo {
28    fn check(
29        &self,
30        root_map: &RootMap,
31        reflector: &mut Reflector,
32        push_error: &mut dyn FnMut(syn::Error),
33    ) -> syn::Result<()> {
34        let info = reflector.reflect(&self.name, self.span)?;
35
36        let mut push_error_message = |m: String| {
37            push_error(syn::Error::new(
38                self.span,
39                format!("error in class `{}`: {m}", self.name),
40            ));
41        };
42
43        if self.kind != info.kind {
44            push_error_message(format!(
45                "class `{}` should be type `{}`",
46                self.name,
47                format!("{:?}", info.kind).to_lowercase()
48            ));
49        }
50
51        // We always allow people to elide generics, in which case
52        // they are mirroring the "erased" version of the class.
53        //
54        // We need this (at minimum) to deal with `java.lang.Class`, since we
55        // don't want to mirror its parameter.
56        if !self.generics.is_empty() {
57            // But if there *are* generics, they must match exactly.
58            if self.generics != info.generics {
59                push_error_message(format!(
60                    "class `{}` should have generic parameters `<{}>`",
61                    self.name,
62                    info.generics
63                        .iter()
64                        .map(|g| g.to_string())
65                        .collect::<Vec<_>>()
66                        .join(", "),
67                ));
68            }
69        }
70
71        for cref in &self.extends {
72            if !info.extends.iter().any(|c| c == cref) {
73                let extends_list: String = info
74                    .extends
75                    .iter()
76                    .map(|c| format!("`{}`", c))
77                    .collect::<Vec<String>>()
78                    .join(", ");
79                push_error_message(format!(
80                    "declared interface `{cref}` not found in the reflected superclasses ({})",
81                    extends_list
82                ));
83            }
84
85            cref.check(root_map, &mut |m| {
86                push_error_message(format!("{m}, but is extended by `{}`", self.name))
87            });
88        }
89
90        // Check whether any extends declarations are duplicates
91        error_on_duplicates(self.extends.as_slice(), "extends", &mut push_error_message);
92
93        for cref in &self.implements {
94            if !info.implements.iter().any(|c| c == cref) {
95                let implements_list: String = info
96                    .implements
97                    .iter()
98                    .map(|c| format!("`{}`", c))
99                    .collect::<Vec<String>>()
100                    .join(", ");
101                push_error_message(format!(
102                    "declared interface `{cref}` not found in the reflected interfaces (`{}`)",
103                    implements_list
104                ));
105            }
106
107            cref.check(root_map, &mut |m| {
108                push_error_message(format!("{m}, but is implemented by `{}`", self.name));
109            });
110        }
111
112        // Check whether any implements declarations are duplicates
113        error_on_duplicates(
114            self.implements.as_slice(),
115            "implements",
116            &mut push_error_message,
117        );
118
119        for c in &self.constructors {
120            let c_method_sig = c.to_method_sig(self);
121
122            c.check(root_map, &mut |m| {
123                push_error_message(format!(
124                    "{m}, which appears in constructor {}",
125                    c_method_sig,
126                ));
127            });
128
129            if !info
130                .constructors
131                .iter()
132                .any(|info_c| info_c.to_method_sig(&info) == c_method_sig)
133            {
134                push_error_message(format!(
135                    "constructor {} does not match any constructors in the reflected class",
136                    c_method_sig,
137                ));
138            }
139        }
140
141        for m in &self.methods {
142            let m_method_sig = m.to_method_sig();
143
144            let mut push_method_error_message = |msg: String| {
145                push_error_message(format!(
146                    "{msg}, which appears in method `{}`",
147                    m.to_method_sig()
148                ));
149            };
150
151            m.check(root_map, &mut push_method_error_message);
152
153            if let Some(reflected_m) = info
154                .methods
155                .iter()
156                .find(|info_c| info_c.to_method_sig() == m_method_sig)
157            {
158                self.compare_flags(m.flags, reflected_m.flags, &mut push_method_error_message);
159            } else {
160                let same_names: Vec<_> = info
161                    .methods
162                    .iter()
163                    .filter(|info_c| info_c.name == m_method_sig.name)
164                    .map(|info_c| info_c.to_method_sig())
165                    .map(|info_c| info_c.to_string())
166                    .collect();
167                if same_names.is_empty() {
168                    push_error_message(format!(
169                        "no method named `{}` in the reflected class",
170                        m_method_sig,
171                    ));
172                } else {
173                    push_error_message(format!(
174                        "method `{}` does not match any of the methods in the reflected class: {}",
175                        m_method_sig,
176                        same_names.join(", "),
177                    ));
178                }
179            }
180        }
181
182        Ok(())
183    }
184
185    fn compare_flags(
186        &self,
187        flags: Flags,
188        reflected_flags: Flags,
189        push_error: &mut dyn FnMut(String),
190    ) {
191        if self.should_mirror_in_rust(flags.privacy)
192            != self.should_mirror_in_rust(reflected_flags.privacy)
193        {
194            push_error(format!(
195                "member declared as {} but it is {} in Java",
196                flags.privacy, reflected_flags.privacy,
197            ));
198        }
199
200        if flags.is_native && !reflected_flags.is_native {
201            push_error(format!(
202                "member declared as native but it is not native in Java",
203            ));
204        }
205
206        if !flags.is_native && reflected_flags.is_native {
207            push_error(format!(
208                "member not declared as native but it is native in Java",
209            ));
210        }
211    }
212}
213
214impl ClassRef {
215    fn check(&self, root_map: &RootMap, push_error: &mut dyn FnMut(String)) {
216        let (package_name, class_id) = self.name.split();
217        if let Some(package) = root_map.find_package(package_name) {
218            if let None = package.find_class(&class_id) {
219                push_error(format!(
220                    "class `{}` not in list of classes to be translated",
221                    self.name,
222                ))
223            }
224        }
225    }
226}
227
228impl Constructor {
229    fn check(&self, root_map: &RootMap, mut push_error: impl FnMut(String)) {
230        for ty in &self.argument_tys {
231            ty.check(root_map, &mut push_error);
232        }
233    }
234}
235
236impl Method {
237    fn check(&self, root_map: &RootMap, mut push_error: impl FnMut(String)) {
238        for ty in &self.argument_tys {
239            ty.check(root_map, &mut push_error);
240        }
241    }
242}
243
244impl Type {
245    fn check(&self, root_map: &RootMap, push_error: &mut impl FnMut(String)) {
246        match self {
247            Type::Ref(r) => r.check(root_map, push_error),
248            Type::Scalar(_) => (),
249            Type::Repeat(ty) => ty.check(root_map, push_error),
250        }
251    }
252}
253
254impl RefType {
255    fn check(&self, root_map: &RootMap, push_error: &mut impl FnMut(String)) {
256        match self {
257            RefType::Class(cref) => cref.check(root_map, push_error),
258            RefType::Array(array) => array.check(root_map, push_error),
259            RefType::TypeParameter(_) => (),
260            RefType::Extends(ty) => ty.check(root_map, push_error),
261            RefType::Super(ty) => ty.check(root_map, push_error),
262            RefType::Wildcard => (),
263        }
264    }
265}
266
267fn error_on_duplicates(
268    references: &[ClassRef],
269    ref_type: &str,
270    mut push_error: impl FnMut(String),
271) {
272    let mut seen = HashSet::with_capacity(references.len());
273    for class_ref in references.iter() {
274        if seen.contains(&(class_ref.name)) {
275            push_error(format!(
276                "duplicate reference in '{}' to '{}'",
277                ref_type, class_ref.name
278            ));
279        } else {
280            seen.insert(class_ref.name.clone());
281        }
282    }
283}