Use closure names, not ids for CreateCl
[hiphop-php.git] / hphp / hack / src / hackc / sem_diff / sem_diff.rs
blobc8fef97c70ce8a895b0a294e81ba520a36532544
1 use anyhow::Result;
2 use ffi::Pair;
3 use ffi::Str;
4 use ffi::Triple;
5 use hhbc::FatalOp;
6 use hhbc::HackCUnit;
7 use hhbc::HhasAttribute;
8 use hhbc::HhasBody;
9 use hhbc::HhasClass;
10 use hhbc::HhasConstant;
11 use hhbc::HhasFunction;
12 use hhbc::HhasMethod;
13 use hhbc::HhasModule;
14 use hhbc::HhasParam;
15 use hhbc::HhasPos;
16 use hhbc::HhasSymbolRefs;
17 use hhbc::HhasTypedef;
19 use crate::code_path::CodePath;
20 use crate::helpers::*;
22 /// Compare two HackCUnits semantically.
23 ///
24 /// For a semantic comparison we don't care about any bytecode differences as
25 /// long as:
26 ///
27 ///   1. Instructs with side-effects exist and are in the same order.
28 ///
29 ///   2. Instructs that can mutate COW datatypes (dict, vec, keyset, string)
30 ///   have the same COW behavior (if a datatype would have been mutated in-place
31 ///   in a_unit it should be mutated in-place in b_unit).
32 ///
33 ///   3. An exception thrown from an instruction will be handled the same way
34 ///
35 /// In general most of the HackCUnit is compared using Eq - although structs are
36 /// destructured so an error can report where the difference occurred.
37 ///
38 /// The "interesting" bit happens in `body::compare_bodies()`.
39 ///
40 pub fn sem_diff_unit<'arena>(a_unit: &HackCUnit<'arena>, b_unit: &HackCUnit<'arena>) -> Result<()> {
41     let HackCUnit {
42         adata: a_adata,
43         functions: a_functions,
44         classes: a_classes,
45         modules: a_modules,
46         typedefs: a_typedefs,
47         file_attributes: a_file_attributes,
48         module_use: a_module_use,
49         symbol_refs: a_symbol_refs,
50         constants: a_constants,
51         fatal: a_fatal,
52     } = a_unit;
53     let HackCUnit {
54         adata: b_adata,
55         functions: b_functions,
56         classes: b_classes,
57         modules: b_modules,
58         typedefs: b_typedefs,
59         file_attributes: b_file_attributes,
60         module_use: b_module_use,
61         symbol_refs: b_symbol_refs,
62         constants: b_constants,
63         fatal: b_fatal,
64     } = b_unit;
66     let path = CodePath::name("HackCUnit");
68     sem_diff_map_t(&path.qualified("adata"), a_adata, b_adata, sem_diff_eq)?;
70     sem_diff_map_t(
71         &path.qualified("typedefs"),
72         a_typedefs,
73         b_typedefs,
74         sem_diff_typedef,
75     )?;
77     sem_diff_attributes(
78         &path.qualified("file_attributes"),
79         a_file_attributes,
80         b_file_attributes,
81     )?;
83     sem_diff_option(
84         &path.qualified("fatal"),
85         a_fatal.as_ref().into_option(),
86         b_fatal.as_ref().into_option(),
87         sem_diff_fatal,
88     )?;
90     sem_diff_map_t(
91         &path.qualified("constants"),
92         a_constants,
93         b_constants,
94         sem_diff_constant,
95     )?;
97     sem_diff_symbol_refs(&path.qualified("symbol_refs"), a_symbol_refs, b_symbol_refs)?;
99     sem_diff_map_t(
100         &path.qualified("modules"),
101         a_modules,
102         b_modules,
103         sem_diff_module,
104     )?;
106     sem_diff_option(
107         &path.qualified("module_use"),
108         a_module_use.as_ref().into_option(),
109         b_module_use.as_ref().into_option(),
110         sem_diff_eq,
111     )?;
113     sem_diff_map_t(
114         &path.qualified("functions"),
115         a_functions.as_arena_ref(),
116         b_functions.as_arena_ref(),
117         sem_diff_function,
118     )?;
120     sem_diff_map_t(
121         &path.qualified("classes"),
122         a_classes.as_arena_ref(),
123         b_classes.as_arena_ref(),
124         sem_diff_class,
125     )?;
127     Ok(())
130 fn sem_diff_attribute(
131     path: &CodePath<'_>,
132     a: &HhasAttribute<'_>,
133     b: &HhasAttribute<'_>,
134 ) -> Result<()> {
135     let HhasAttribute {
136         name: a_name,
137         arguments: a_arguments,
138     } = a;
139     let HhasAttribute {
140         name: b_name,
141         arguments: b_arguments,
142     } = b;
143     sem_diff_eq(&path.qualified("name"), a_name, b_name)?;
144     sem_diff_slice(
145         &path.qualified("arguments"),
146         a_arguments,
147         b_arguments,
148         sem_diff_eq,
149     )?;
150     Ok(())
153 fn sem_diff_attributes(
154     path: &CodePath<'_>,
155     a: &[HhasAttribute<'_>],
156     b: &[HhasAttribute<'_>],
157 ) -> Result<()> {
158     sem_diff_slice(path, a, b, sem_diff_attribute)
161 fn sem_diff_body<'arena>(
162     path: &CodePath<'_>,
163     a: &'arena HhasBody<'arena>,
164     b: &'arena HhasBody<'arena>,
165 ) -> Result<()> {
166     let HhasBody {
167         body_instrs: _,
168         decl_vars: _,
169         num_iters: a_num_iters,
170         is_memoize_wrapper: a_is_memoize_wrapper,
171         is_memoize_wrapper_lsb: a_is_memoize_wrapper_lsb,
172         upper_bounds: a_upper_bounds,
173         shadowed_tparams: a_shadowed_tparams,
174         params: a_params,
175         return_type_info: a_return_type_info,
176         doc_comment: a_doc_comment,
177     } = a;
178     let HhasBody {
179         body_instrs: _,
180         decl_vars: _,
181         num_iters: b_num_iters,
182         is_memoize_wrapper: b_is_memoize_wrapper,
183         is_memoize_wrapper_lsb: b_is_memoize_wrapper_lsb,
184         upper_bounds: b_upper_bounds,
185         shadowed_tparams: b_shadowed_tparams,
186         params: b_params,
187         return_type_info: b_return_type_info,
188         doc_comment: b_doc_comment,
189     } = b;
191     sem_diff_eq(&path.qualified("num_iters"), a_num_iters, b_num_iters)?;
192     sem_diff_slice(
193         &path.qualified("params"),
194         a_params,
195         b_params,
196         sem_diff_param,
197     )?;
198     sem_diff_eq(
199         &path.qualified("is_memoize_wrapper"),
200         a_is_memoize_wrapper,
201         b_is_memoize_wrapper,
202     )?;
203     sem_diff_eq(
204         &path.qualified("is_memoize_wrapper_lsb"),
205         a_is_memoize_wrapper_lsb,
206         b_is_memoize_wrapper_lsb,
207     )?;
208     sem_diff_eq(&path.qualified("doc_comment"), a_doc_comment, b_doc_comment)?;
209     sem_diff_eq(
210         &path.qualified("return_type_info"),
211         a_return_type_info,
212         b_return_type_info,
213     )?;
214     sem_diff_eq(
215         &path.qualified("upper_bounds"),
216         a_upper_bounds,
217         b_upper_bounds,
218     )?;
219     sem_diff_eq(
220         &path.qualified("shadowed_tparams"),
221         a_shadowed_tparams,
222         b_shadowed_tparams,
223     )?;
225     // Don't bother comparing decl_vars - when we use named vars later we'll
226     // compare the names to ensure we're using the same ones.
227     // sem_diff_set_t(&path.qualified("decl_vars"), a_decl_vars, b_decl_vars)?;
229     // This compares the instrs themselves.
230     crate::body::compare_bodies(path, a, b)
233 fn sem_diff_param<'arena>(
234     path: &CodePath<'_>,
235     a: &'arena HhasParam<'arena>,
236     b: &'arena HhasParam<'arena>,
237 ) -> Result<()> {
238     let HhasParam {
239         name: a_name,
240         is_variadic: a_is_variadic,
241         is_inout: a_is_inout,
242         is_readonly: a_is_readonly,
243         user_attributes: a_user_attributes,
244         type_info: a_type_info,
245         default_value: a_default_value,
246     } = a;
247     let HhasParam {
248         name: b_name,
249         is_variadic: b_is_variadic,
250         is_inout: b_is_inout,
251         is_readonly: b_is_readonly,
252         user_attributes: b_user_attributes,
253         type_info: b_type_info,
254         default_value: b_default_value,
255     } = b;
257     sem_diff_eq(&path.qualified("name"), a_name, b_name)?;
258     sem_diff_eq(&path.qualified("is_variadic"), a_is_variadic, b_is_variadic)?;
259     sem_diff_eq(&path.qualified("is_inout"), a_is_inout, b_is_inout)?;
260     sem_diff_eq(&path.qualified("is_readonly"), a_is_readonly, b_is_readonly)?;
261     sem_diff_eq(
262         &path.qualified("user_attributes"),
263         a_user_attributes,
264         b_user_attributes,
265     )?;
266     sem_diff_eq(&path.qualified("type_info"), a_type_info, b_type_info)?;
267     sem_diff_option(
268         &path.qualified("default_value"),
269         a_default_value.as_ref().into_option(),
270         b_default_value.as_ref().into_option(),
271         |path, Pair(_, a_text), Pair(_, b_text)| sem_diff_eq(path, a_text, b_text),
272     )?;
274     Ok(())
277 fn sem_diff_class<'arena>(
278     path: &CodePath<'_>,
279     a: &'arena HhasClass<'arena>,
280     b: &'arena HhasClass<'arena>,
281 ) -> Result<()> {
282     let HhasClass {
283         attributes: a_attributes,
284         base: a_base,
285         implements: a_implements,
286         enum_includes: a_enum_includes,
287         name: a_name,
288         span: a_span,
289         uses: a_uses,
290         enum_type: a_enum_type,
291         methods: a_methods,
292         properties: a_properties,
293         constants: a_constants,
294         type_constants: a_type_constants,
295         ctx_constants: a_ctx_constants,
296         requirements: a_requirements,
297         upper_bounds: a_upper_bounds,
298         doc_comment: a_doc_comment,
299         flags: a_flags,
300     } = a;
301     let HhasClass {
302         attributes: b_attributes,
303         base: b_base,
304         implements: b_implements,
305         enum_includes: b_enum_includes,
306         name: b_name,
307         span: b_span,
308         uses: b_uses,
309         enum_type: b_enum_type,
310         methods: b_methods,
311         properties: b_properties,
312         constants: b_constants,
313         type_constants: b_type_constants,
314         ctx_constants: b_ctx_constants,
315         requirements: b_requirements,
316         upper_bounds: b_upper_bounds,
317         doc_comment: b_doc_comment,
318         flags: b_flags,
319     } = b;
321     sem_diff_eq(&path.qualified("name"), a_name, b_name)?;
322     sem_diff_attributes(&path.qualified("attributes"), a_attributes, b_attributes)?;
323     sem_diff_eq(&path.qualified("base"), a_base, b_base)?;
325     sem_diff_eq(&path.qualified("implements"), a_implements, b_implements)?;
326     sem_diff_eq(
327         &path.qualified("enum_includes"),
328         a_enum_includes,
329         b_enum_includes,
330     )?;
331     sem_diff_eq(&path.qualified("span"), a_span, b_span)?;
332     sem_diff_eq(&path.qualified("uses"), a_uses, b_uses)?;
333     sem_diff_option(
334         &path.qualified("enum_type"),
335         a_enum_type.as_ref().into_option(),
336         b_enum_type.as_ref().into_option(),
337         sem_diff_eq,
338     )?;
339     sem_diff_map_t(
340         &path.qualified("properties"),
341         a_properties,
342         b_properties,
343         sem_diff_eq,
344     )?;
345     sem_diff_map_t(
346         &path.qualified("constants"),
347         a_constants,
348         b_constants,
349         sem_diff_constant,
350     )?;
351     sem_diff_map_t(
352         &path.qualified("type_constants"),
353         a_type_constants,
354         b_type_constants,
355         sem_diff_eq,
356     )?;
357     sem_diff_map_t(
358         &path.qualified("ctx_constants"),
359         a_ctx_constants,
360         b_ctx_constants,
361         sem_diff_eq,
362     )?;
363     sem_diff_map_t(
364         &path.qualified("requirements"),
365         a_requirements,
366         b_requirements,
367         |path, a, b| sem_diff_eq(path, &a.1, &b.1),
368     )?;
369     sem_diff_map_t(
370         &path.qualified("upper_bounds"),
371         a_upper_bounds,
372         b_upper_bounds,
373         |path, a, b| sem_diff_slice(path, &a.1, &b.1, sem_diff_eq),
374     )?;
375     sem_diff_eq(&path.qualified("doc_comment"), a_doc_comment, b_doc_comment)?;
376     sem_diff_eq(&path.qualified("flags"), a_flags, b_flags)?;
378     sem_diff_map_t(
379         &path.qualified("methods"),
380         a_methods,
381         b_methods,
382         sem_diff_method,
383     )?;
385     Ok(())
388 fn sem_diff_constant(
389     path: &CodePath<'_>,
390     a: &HhasConstant<'_>,
391     b: &HhasConstant<'_>,
392 ) -> Result<()> {
393     let HhasConstant {
394         name: a_name,
395         value: a_value,
396         is_abstract: a_is_abstract,
397     } = a;
398     let HhasConstant {
399         name: b_name,
400         value: b_value,
401         is_abstract: b_is_abstract,
402     } = b;
403     sem_diff_eq(&path.qualified("name"), a_name, b_name)?;
404     sem_diff_option(
405         &path.qualified("value"),
406         a_value.as_ref().into_option(),
407         b_value.as_ref().into_option(),
408         sem_diff_eq,
409     )?;
410     sem_diff_eq(&path.qualified("is_abstract"), a_is_abstract, b_is_abstract)?;
411     Ok(())
414 fn sem_diff_fatal(
415     path: &CodePath<'_>,
416     a: &Triple<FatalOp, HhasPos, Str<'_>>,
417     b: &Triple<FatalOp, HhasPos, Str<'_>>,
418 ) -> Result<()> {
419     sem_diff_eq(&path.index(0), &a.0, &b.0)?;
420     sem_diff_eq(&path.index(1), &a.1, &b.1)?;
421     sem_diff_eq(&path.index(2), &a.2, &b.2)?;
422     Ok(())
425 fn sem_diff_function<'arena>(
426     path: &CodePath<'_>,
427     a: &'arena HhasFunction<'arena>,
428     b: &'arena HhasFunction<'arena>,
429 ) -> Result<()> {
430     let HhasFunction {
431         attributes: a_attributes,
432         name: a_name,
433         body: a_body,
434         span: a_span,
435         coeffects: a_coeffects,
436         flags: a_flags,
437         attrs: a_attrs,
438     } = a;
439     let HhasFunction {
440         attributes: b_attributes,
441         name: b_name,
442         body: b_body,
443         span: b_span,
444         coeffects: b_coeffects,
445         flags: b_flags,
446         attrs: b_attrs,
447     } = b;
449     sem_diff_eq(&path.qualified("name"), a_name, b_name)?;
450     sem_diff_attributes(&path.qualified("attributes"), a_attributes, b_attributes)?;
451     sem_diff_body(&path.qualified("body"), a_body, b_body)?;
452     sem_diff_eq(&path.qualified("span"), a_span, b_span)?;
453     sem_diff_eq(&path.qualified("coeffects"), a_coeffects, b_coeffects)?;
454     sem_diff_eq(&path.qualified("flags"), a_flags, b_flags)?;
455     sem_diff_eq(&path.qualified("attrs"), a_attrs, b_attrs)?;
457     Ok(())
460 fn sem_diff_method<'arena>(
461     path: &CodePath<'_>,
462     a: &'arena HhasMethod<'arena>,
463     b: &'arena HhasMethod<'arena>,
464 ) -> Result<()> {
465     let HhasMethod {
466         attributes: a_attributes,
467         visibility: a_visibility,
468         name: a_name,
469         body: a_body,
470         span: a_span,
471         coeffects: a_coeffects,
472         flags: a_flags,
473         attrs: a_attrs,
474     } = a;
475     let HhasMethod {
476         attributes: b_attributes,
477         visibility: b_visibility,
478         name: b_name,
479         body: b_body,
480         span: b_span,
481         coeffects: b_coeffects,
482         flags: b_flags,
483         attrs: b_attrs,
484     } = b;
485     sem_diff_attributes(&path.qualified("attributes"), a_attributes, b_attributes)?;
486     sem_diff_eq(&path.qualified("visibility"), a_visibility, b_visibility)?;
487     sem_diff_eq(&path.qualified("name"), a_name, b_name)?;
488     sem_diff_body(&path.qualified("body"), a_body, b_body)?;
489     sem_diff_eq(&path.qualified("span"), a_span, b_span)?;
490     sem_diff_eq(&path.qualified("coeffects"), a_coeffects, b_coeffects)?;
491     sem_diff_eq(&path.qualified("flags"), a_flags, b_flags)?;
492     sem_diff_eq(&path.qualified("attrs"), a_attrs, b_attrs)?;
493     Ok(())
496 fn sem_diff_module<'arena>(
497     path: &CodePath<'_>,
498     a: &HhasModule<'arena>,
499     b: &HhasModule<'arena>,
500 ) -> Result<()> {
501     let HhasModule {
502         attributes: a_attributes,
503         name: a_name,
504         span: a_span,
505     } = a;
506     let HhasModule {
507         attributes: b_attributes,
508         name: b_name,
509         span: b_span,
510     } = b;
512     sem_diff_eq(&path.qualified("name"), a_name, b_name)?;
513     sem_diff_attributes(&path.qualified("attributes"), a_attributes, b_attributes)?;
514     sem_diff_eq(&path.qualified("span"), a_span, b_span)?;
515     Ok(())
518 fn sem_diff_symbol_refs<'arena>(
519     path: &CodePath<'_>,
520     a: &HhasSymbolRefs<'arena>,
521     b: &HhasSymbolRefs<'arena>,
522 ) -> Result<()> {
523     let HhasSymbolRefs {
524         includes: a_includes,
525         constants: a_constants,
526         functions: a_functions,
527         classes: a_classes,
528     } = a;
529     let HhasSymbolRefs {
530         includes: b_includes,
531         constants: b_constants,
532         functions: b_functions,
533         classes: b_classes,
534     } = b;
536     sem_diff_slice(
537         &path.qualified("includes"),
538         a_includes,
539         b_includes,
540         sem_diff_eq,
541     )?;
542     sem_diff_slice(
543         &path.qualified("constants"),
544         a_constants,
545         b_constants,
546         sem_diff_eq,
547     )?;
548     sem_diff_slice(
549         &path.qualified("functions"),
550         a_functions,
551         b_functions,
552         sem_diff_eq,
553     )?;
554     sem_diff_slice(
555         &path.qualified("classes"),
556         a_classes,
557         b_classes,
558         sem_diff_eq,
559     )?;
560     Ok(())
563 fn sem_diff_typedef<'arena>(
564     path: &CodePath<'_>,
565     a: &HhasTypedef<'arena>,
566     b: &HhasTypedef<'arena>,
567 ) -> Result<()> {
568     let HhasTypedef {
569         name: a_name,
570         attributes: a_attributes,
571         type_info: a_type_info,
572         type_structure: a_type_structure,
573         span: a_span,
574         attrs: a_attrs,
575     } = a;
576     let HhasTypedef {
577         name: b_name,
578         attributes: b_attributes,
579         type_info: b_type_info,
580         type_structure: b_type_structure,
581         span: b_span,
582         attrs: b_attrs,
583     } = b;
585     sem_diff_eq(&path.qualified("name"), a_name, b_name)?;
586     sem_diff_attributes(&path.qualified("attributes"), a_attributes, b_attributes)?;
587     sem_diff_eq(&path.qualified("type_info"), a_type_info, b_type_info)?;
588     sem_diff_eq(
589         &path.qualified("type_structure"),
590         a_type_structure,
591         b_type_structure,
592     )?;
593     sem_diff_eq(&path.qualified("span"), a_span, b_span)?;
594     sem_diff_eq(&path.qualified("attrs"), a_attrs, b_attrs)?;
595     Ok(())