• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
2 // -*- Mode: C++ -*-
3 //
4 // Copyright (C) 2013-2020 Red Hat, Inc.
5 //
6 // Author: Dodji Seketeli
7 
8 /// @file
9 ///
10 /// This file contains definitions of diff objects filtering
11 /// facilities.
12 
13 #include "abg-internal.h"
14 #include <memory>
15 // <headers defining libabigail's API go under here>
16 ABG_BEGIN_EXPORT_DECLARATIONS
17 
18 #include "abg-comp-filter.h"
19 #include "abg-tools-utils.h"
20 
21 ABG_END_EXPORT_DECLARATIONS
22 // </headers defining libabigail's API>
23 
24 namespace abigail
25 {
26 namespace comparison
27 {
28 namespace filtering
29 {
30 
31 using std::dynamic_pointer_cast;
32 
33 /// Walk the diff sub-trees of a a @ref corpus_diff and apply a filter
34 /// to the nodes visted.  The filter categorizes each node, assigning
35 /// it into one or several categories.
36 ///
37 /// @param filter the filter to apply to the diff nodes
38 ///
39 /// @param d the corpus diff to apply the filter to.
40 void
apply_filter(filter_base & filter,corpus_diff_sptr d)41 apply_filter(filter_base& filter, corpus_diff_sptr d)
42 {
43   bool s = d->context()->visiting_a_node_twice_is_forbidden();
44   d->context()->forbid_visiting_a_node_twice(false);
45   d->traverse(filter);
46   d->context()->forbid_visiting_a_node_twice(s);
47 }
48 
49 /// Walk a diff sub-tree and apply a filter to the nodes visted.  The
50 /// filter categorizes each node, assigning it into one or several
51 /// categories.
52 ///
53 /// Note that this function makes sure to avoid visiting a node (or
54 /// any other node equivalent to it) more than once.  This helps avoid
55 /// infinite loops for diff trees that involve type changes that
56 /// reference themselves.
57 ///
58 /// @param filter the filter to apply to the nodes of the sub-tree.
59 ///
60 /// @param d the diff sub-tree to walk and apply the filter to.
61 void
apply_filter(filter_base & filter,diff_sptr d)62 apply_filter(filter_base& filter, diff_sptr d)
63 {
64   bool s = d->context()->visiting_a_node_twice_is_forbidden();
65   d->context()->forbid_visiting_a_node_twice(true);
66   d->context()->forget_visited_diffs();
67   d->traverse(filter);
68   d->context()->forbid_visiting_a_node_twice(s);
69 }
70 
71 /// Walk a diff sub-tree and apply a filter to the nodes visted.  The
72 /// filter categorizes each node, assigning it into one or several
73 /// categories.
74 ///
75 /// Note that this function makes sure to avoid visiting a node (or
76 /// any other node equivalent to it) more than once.  This helps avoid
77 /// infinite loops for diff trees that involve type changes that
78 /// reference themselves.
79 ///
80 /// @param filter the filter to apply to the nodes of the sub-tree.
81 ///
82 /// @param d the diff sub-tree to walk and apply the filter to.
83 void
apply_filter(filter_base_sptr filter,diff_sptr d)84 apply_filter(filter_base_sptr filter, diff_sptr d)
85 {apply_filter(*filter, d);}
86 
87 /// Test if there is a class that is declaration-only among the two
88 /// classes in parameter.
89 ///
90 /// @param class1 the first class to consider.
91 ///
92 /// @param class2 the second class to consider.
93 ///
94 /// @return true if either classes are declaration-only, false
95 /// otherwise.
96 static bool
there_is_a_decl_only_class(const class_decl_sptr & class1,const class_decl_sptr & class2)97 there_is_a_decl_only_class(const class_decl_sptr& class1,
98 			   const class_decl_sptr& class2)
99 {
100   if ((class1 && class1->get_is_declaration_only())
101       || (class2 && class2->get_is_declaration_only()))
102     return true;
103   return false;
104 }
105 
106 /// Test if there is a enum that is declaration-only among the two
107 /// enums in parameter.
108 ///
109 /// @param enum1 the first enum to consider.
110 ///
111 /// @param enum2 the second enum to consider.
112 ///
113 /// @return true if either enums are declaration-only, false
114 /// otherwise.
115 static bool
there_is_a_decl_only_enum(const enum_type_decl_sptr & enum1,const enum_type_decl_sptr & enum2)116 there_is_a_decl_only_enum(const enum_type_decl_sptr& enum1,
117 			  const enum_type_decl_sptr& enum2)
118 {
119   if ((enum1 && enum1->get_is_declaration_only())
120       || (enum2 && enum2->get_is_declaration_only()))
121     return true;
122   return false;
123 }
124 
125 /// Test if the diff involves a declaration-only class.
126 ///
127 /// @param diff the class diff to consider.
128 ///
129 /// @return true iff the diff involves a declaration-only class.
130 static bool
diff_involves_decl_only_class(const class_diff * diff)131 diff_involves_decl_only_class(const class_diff* diff)
132 {
133   if (diff && there_is_a_decl_only_class(diff->first_class_decl(),
134 					 diff->second_class_decl()))
135     return true;
136   return false;
137 }
138 
139 /// Tests if the size of a given type changed.
140 ///
141 /// @param f the first version of the type to consider.
142 ///
143 /// @param s the second version of the type to consider.
144 ///
145 /// @return true if the type size changed, false otherwise.
146 static bool
type_size_changed(const type_base_sptr f,const type_base_sptr s)147 type_size_changed(const type_base_sptr f, const type_base_sptr s)
148 {
149   if (!f || !s
150       || f->get_size_in_bits() == 0
151       || s->get_size_in_bits() == 0
152       || there_is_a_decl_only_class(is_compatible_with_class_type(f),
153 				    is_compatible_with_class_type(s))
154       || there_is_a_decl_only_enum(is_compatible_with_enum_type(f),
155 				   is_compatible_with_enum_type(s)))
156     return false;
157 
158   return f->get_size_in_bits() != s->get_size_in_bits();
159 }
160 
161 /// Tests if the size of a given type changed.
162 ///
163 /// @param f the declaration of the first version of the type to
164 /// consider.
165 ///
166 /// @param s the declaration of the second version of the type to
167 /// consider.
168 ///
169 /// @return true if the type size changed, false otherwise.
170 static bool
type_size_changed(const decl_base_sptr f,const decl_base_sptr s)171 type_size_changed(const decl_base_sptr f, const decl_base_sptr s)
172 {return type_size_changed(is_type(f), is_type(s));}
173 
174 /// Test if a given type diff node carries a type size change.
175 ///
176 /// @param diff the diff tree node to test.
177 ///
178 /// @return true if @p diff carries a type size change.
179 static bool
has_type_size_change(const diff * diff)180 has_type_size_change(const diff* diff)
181 {
182   if (!diff)
183     return false;
184 
185   if (const fn_parm_diff* fn_parm_d = is_fn_parm_diff(diff))
186     diff = fn_parm_d->type_diff().get();
187 
188   type_base_sptr f = is_type(diff->first_subject()),
189     s = is_type(diff->second_subject());
190 
191   if (!f || !s)
192     return false;
193 
194   return type_size_changed(f, s);
195 }
196 /// Tests if the access specifiers for a member declaration changed.
197 ///
198 /// @param f the declaration for the first version of the member
199 /// declaration to consider.
200 ///
201 /// @param s the declaration for the second version of the member
202 /// delcaration to consider.
203 ///
204 /// @return true iff the access specifier changed.
205 static bool
access_changed(const decl_base_sptr & f,const decl_base_sptr & s)206 access_changed(const decl_base_sptr& f, const decl_base_sptr& s)
207 {
208   if (!is_member_decl(f)
209       || !is_member_decl(s))
210     return false;
211 
212   access_specifier fa = get_member_access_specifier(f),
213     sa = get_member_access_specifier(s);
214 
215   if (sa != fa)
216     return true;
217 
218   return false;
219 }
220 
221 /// Test if there was a function or variable CRC change.
222 ///
223 /// @param f the first function or variable to consider.
224 ///
225 /// @param s the second function or variable to consider.
226 ///
227 /// @return true if the test is positive, false otherwise.
228 template <typename function_or_var_decl_sptr>
229 static bool
crc_changed(const function_or_var_decl_sptr & f,const function_or_var_decl_sptr & s)230 crc_changed(const function_or_var_decl_sptr& f,
231 	    const function_or_var_decl_sptr& s)
232 {
233   const auto symbol_f  = f->get_symbol(), symbol_s = s->get_symbol();
234   if (!symbol_f || !symbol_s)
235     return false;
236   const auto crc_f = symbol_f->get_crc(), crc_s = symbol_s->get_crc();
237   return (crc_f != 0 && crc_s != 0 && crc_f != crc_s);
238 }
239 
240 /// Test if the current diff tree node carries a CRC change in either a
241 /// function or a variable.
242 ///
243 /// @param diff the diff tree node to consider.
244 ///
245 /// @return true if the test is positive, false otherwise.
246 static bool
crc_changed(const diff * diff)247 crc_changed(const diff* diff)
248 {
249   if (const function_decl_diff* d =
250 	dynamic_cast<const function_decl_diff*>(diff))
251     return crc_changed(d->first_function_decl(), d->second_function_decl());
252   if (const var_diff* d = dynamic_cast<const var_diff*>(diff))
253     return crc_changed(d->first_var(), d->second_var());
254   return false;
255 }
256 
257 /// Test if there was a function name change, but there there was no
258 /// change in name of the underlying symbol.  IOW, if the name of a
259 /// function changed, but the symbol of the new function is equal to
260 /// the symbol of the old one, or is equal to an alians of the symbol
261 /// of the old function.
262 ///
263 /// @param f the first function to consider.
264 ///
265 /// @param s the second function to consider.
266 ///
267 /// @return true if the test is positive, false otherwise.
268 static bool
function_name_changed_but_not_symbol(const function_decl_sptr & f,const function_decl_sptr & s)269 function_name_changed_but_not_symbol(const function_decl_sptr& f,
270 				     const function_decl_sptr& s)
271 {
272   if (!f || !s)
273     return false;
274   string fn = f->get_qualified_name(),
275     sn = s->get_qualified_name();
276 
277   if (fn != sn)
278     {
279       elf_symbol_sptr fs = f->get_symbol(), ss = s->get_symbol();
280       if (fs == ss)
281 	return true;
282       if (!!fs != !!ss)
283 	return false;
284       for (elf_symbol_sptr s = fs->get_next_alias();
285 	   s && !s->is_main_symbol();
286 	   s = s->get_next_alias())
287 	if (*s == *ss)
288 	  return true;
289     }
290   return false;
291 }
292 
293 /// Test if the current diff tree node carries a function name change,
294 /// in which there there was no change in the name of the underlying
295 /// symbol.  IOW, if the name of a function changed, but the symbol of
296 /// the new function is equal to the symbol of the old one, or is
297 /// equal to an alians of the symbol of the old function.
298 ///
299 /// @param diff the diff tree node to consider.
300 ///
301 /// @return true if the test is positive, false otherwise.
302 static bool
function_name_changed_but_not_symbol(const diff * diff)303 function_name_changed_but_not_symbol(const diff* diff)
304 {
305   if (const function_decl_diff* d =
306       dynamic_cast<const function_decl_diff*>(diff))
307     return function_name_changed_but_not_symbol(d->first_function_decl(),
308 						d->second_function_decl());
309   return false;
310 }
311 
312 /// Tests if the offset of a given data member changed.
313 ///
314 /// @param f the declaration for the first version of the data member to
315 /// consider.
316 ///
317 /// @param s the declaration for the second version of the data member
318 /// to consider.
319 ///
320 /// @return true iff the offset of the data member changed.
321 static bool
data_member_offset_changed(decl_base_sptr f,decl_base_sptr s)322 data_member_offset_changed(decl_base_sptr f, decl_base_sptr s)
323 {
324   if (!is_member_decl(f)
325       || !is_member_decl(s))
326     return false;
327 
328   var_decl_sptr v0 = dynamic_pointer_cast<var_decl>(f),
329     v1 = dynamic_pointer_cast<var_decl>(s);
330   if (!v0 || !v1)
331     return false;
332 
333   if (get_data_member_offset(v0) != get_data_member_offset(v1))
334     return true;
335 
336   return false;
337 }
338 
339 /// Test if the size of a non-static data member changed accross two
340 /// versions.
341 ///
342 /// @param f the first version of the non-static data member.
343 ///
344 /// @param s the second version of the non-static data member.
345 static bool
non_static_data_member_type_size_changed(const decl_base_sptr & f,const decl_base_sptr & s)346 non_static_data_member_type_size_changed(const decl_base_sptr& f,
347 					 const decl_base_sptr& s)
348 {
349   if (!is_member_decl(f)
350       || !is_member_decl(s))
351     return false;
352 
353   var_decl_sptr fv = dynamic_pointer_cast<var_decl>(f),
354     sv = dynamic_pointer_cast<var_decl>(s);
355   if (!fv
356       || !sv
357       || get_member_is_static(fv)
358       || get_member_is_static(sv))
359     return false;
360 
361   return type_size_changed(fv->get_type(), sv->get_type());
362 }
363 
364 /// Test if the size of a static data member changed accross two
365 /// versions.
366 ///
367 /// @param f the first version of the static data member.
368 ///
369 /// @param s the second version of the static data member.
370 static bool
static_data_member_type_size_changed(const decl_base_sptr & f,const decl_base_sptr & s)371 static_data_member_type_size_changed(const decl_base_sptr& f,
372 				     const decl_base_sptr& s)
373 {
374   if (!is_member_decl(f)
375       || !is_member_decl(s))
376     return false;
377 
378   var_decl_sptr fv = dynamic_pointer_cast<var_decl>(f),
379     sv = dynamic_pointer_cast<var_decl>(s);
380   if (!fv
381       || !sv
382       || !get_member_is_static(fv)
383       || !get_member_is_static(sv))
384     return false;
385 
386   return type_size_changed(fv->get_type(), sv->get_type());
387 }
388 
389 /// Test if two types are different but compatible.
390 ///
391 /// @param d1 the declaration of the first type to consider.
392 ///
393 /// @param d2 the declaration of the second type to consider.
394 ///
395 /// @return true if d1 and d2 are different but compatible.
396 static bool
is_compatible_change(const decl_base_sptr & d1,const decl_base_sptr & d2)397 is_compatible_change(const decl_base_sptr& d1, const decl_base_sptr& d2)
398 {
399   if ((d1 && d2)
400       && (d1 != d2)
401       && types_are_compatible(d1, d2))
402     return true;
403   return false;
404 }
405 
406 /// Test if two decls have different names.
407 ///
408 /// @param d1 the first declaration to consider.
409 ///
410 /// @param d2 the second declaration to consider.
411 ///
412 /// @return true if d1 and d2 have different names.
413 static bool
decl_name_changed(const type_or_decl_base * a1,const type_or_decl_base * a2)414 decl_name_changed(const type_or_decl_base* a1, const type_or_decl_base *a2)
415 {
416   string d1_name, d2_name;
417 
418   const decl_base *d1 = dynamic_cast<const decl_base*>(a1);
419   if (d1 == 0)
420     return false;
421 
422   const decl_base *d2 = dynamic_cast<const decl_base*>(a2);
423   if (d2 == 0)
424     return false;
425 
426   if (d1)
427     d1_name = d1->get_qualified_name();
428   if (d2)
429     d2_name = d2->get_qualified_name();
430 
431   return d1_name != d2_name;
432 }
433 
434 /// Test if two decls have different names.
435 ///
436 /// @param d1 the first declaration to consider.
437 ///
438 /// @param d2 the second declaration to consider.
439 ///
440 /// @return true if d1 and d2 have different names.
441 static bool
decl_name_changed(const type_or_decl_base_sptr & d1,const type_or_decl_base_sptr & d2)442 decl_name_changed(const type_or_decl_base_sptr& d1,
443 		  const type_or_decl_base_sptr& d2)
444 {return decl_name_changed(d1.get(), d2.get());}
445 
446 /// Test if a diff nodes carries a changes in which two decls have
447 /// different names.
448 ///
449 /// @param d the diff node to consider.
450 ///
451 /// @return true iff d carries a changes in which two decls have
452 /// different names.
453 static bool
decl_name_changed(const diff * d)454 decl_name_changed(const diff *d)
455 {return decl_name_changed(d->first_subject(), d->second_subject());}
456 
457 /// Test if two decls represents a harmless name change.
458 ///
459 /// For now, a harmless name change is considered only for a typedef,
460 /// enum or a data member.
461 ///
462 /// @param f the first decl to consider in the comparison.
463 ///
464 /// @param s the second decl to consider in the comparison.
465 ///
466 /// @return true iff decl @p s represents a harmless change over @p f.
467 bool
has_harmless_name_change(const decl_base_sptr & f,const decl_base_sptr & s)468 has_harmless_name_change(const decl_base_sptr& f, const decl_base_sptr& s)
469 {
470   // So, a harmless name change is either ...
471   return (decl_name_changed(f, s)
472 	  && (// ... an anonymous decl name changed into another
473 	      // anonymous decl name ...
474 	      (f->get_is_anonymous() && s->get_is_anonymous())
475 	      ||
476 	      // ... an anonymous decl name changed harmlessly into
477 	      // another anonymous decl name ...
478 	      ((f->get_is_anonymous_or_has_anonymous_parent()
479 		&& s->get_is_anonymous_or_has_anonymous_parent())
480 	       && tools_utils::decl_names_equal(f->get_qualified_name(),
481 						s->get_qualified_name()))
482 	      // ... a typedef name change, without having the
483 	      // underlying type changed ...
484 	      || (is_typedef(f)
485 		  && is_typedef(s)
486 		  && (is_typedef(f)->get_underlying_type()
487 		   == is_typedef(s)->get_underlying_type()))
488 	      // .. or a data member name change, without having its
489 	      // type changed ...
490 	      || (is_data_member(f)
491 		  && is_data_member(s)
492 		  && (is_var_decl(f)->get_type()
493 		      == is_var_decl(s)->get_type()))
494 	      // .. an enum name change without having any other part
495 	      // of the enum to change.
496 	      || (is_enum_type(f)
497 		  && is_enum_type(s)
498 		  && !enum_has_non_name_change(*is_enum_type(f),
499 					       *is_enum_type(s),
500 					       0))));
501 }
502 
503 /// Test if two decls represents a harmful name change.
504 ///
505 /// A harmful name change is a name change that is not harmless, so
506 /// this function uses the function has_harmless_name_change.
507 ///
508 /// @param f the first decl to consider in the comparison.
509 ///
510 /// @param s the second decl to consider in the comparison.
511 ///
512 /// @return true iff decl @p s represents a harmful name change over
513 /// @p f.
514 bool
has_harmful_name_change(const decl_base_sptr & f,const decl_base_sptr & s)515 has_harmful_name_change(const decl_base_sptr& f, const decl_base_sptr& s)
516 {return decl_name_changed(f, s) && ! has_harmless_name_change(f, s);}
517 
518 /// Test if a diff node represents a harmful name change.
519 ///
520 /// A harmful name change is a name change that is not harmless, so
521 /// this function uses the function has_harmless_name_change.
522 ///
523 /// @param f the first decl to consider in the comparison.
524 ///
525 /// @param s the second decl to consider in the comparison.
526 ///
527 /// @return true iff decl @p s represents a harmful name change over
528 /// @p f.
529 bool
has_harmful_name_change(const diff * dif)530 has_harmful_name_change(const diff* dif)
531 {
532   decl_base_sptr f = is_decl(dif->first_subject()),
533     s = is_decl(dif->second_subject());
534 
535   return has_harmful_name_change(f, s);
536 }
537 
538 /// Test if a class_diff node has non-static members added or
539 /// removed.
540 ///
541 /// @param diff the diff node to consider.
542 ///
543 /// @return true iff the class_diff node has non-static members added
544 /// or removed.
545 static bool
non_static_data_member_added_or_removed(const class_diff * diff)546 non_static_data_member_added_or_removed(const class_diff* diff)
547 {
548   if (diff && !diff_involves_decl_only_class(diff))
549     {
550       for (string_decl_base_sptr_map::const_iterator i =
551 	     diff->inserted_data_members().begin();
552 	   i != diff->inserted_data_members().end();
553 	   ++i)
554 	if (!get_member_is_static(i->second))
555 	  return true;
556 
557       for (string_decl_base_sptr_map::const_iterator i =
558 	     diff->deleted_data_members().begin();
559 	   i != diff->deleted_data_members().end();
560 	   ++i)
561 	if (!get_member_is_static(i->second))
562 	  return true;
563     }
564 
565   return false;
566 }
567 
568 /// Test if a class_diff node has members added or removed.
569 ///
570 /// @param diff the diff node to consider.
571 ///
572 /// @return true iff the class_diff node has members added or removed.
573 static bool
non_static_data_member_added_or_removed(const diff * diff)574 non_static_data_member_added_or_removed(const diff* diff)
575 {
576   return non_static_data_member_added_or_removed
577     (dynamic_cast<const class_diff*>(diff));
578 }
579 
580 /// Test if a @ref class_or_union_diff has a data member replaced by
581 /// an anonymous data member in a harmless way.  That means, the new
582 /// anonymous data member somehow contains the replaced data member
583 /// and it doesn't break the layout of the containing class.
584 ///
585 /// @param diff the diff node to consider.
586 ///
587 /// @return true iff the @ref class_or_union_diff has a data member
588 /// harmlessly replaced by an anonymous data member.
589 bool
has_data_member_replaced_by_anon_dm(const diff * diff)590 has_data_member_replaced_by_anon_dm(const diff* diff)
591 {
592   const class_or_union_diff *c = is_class_or_union_diff(diff);
593 
594   if (!c)
595     return false;
596   return !c->data_members_replaced_by_adms().empty();
597 }
598 
599 /// Test if a class_diff node has static members added or removed.
600 ///
601 /// @param diff the diff node to consider.
602 ///
603 /// @return true iff the class_diff node has static members added
604 /// or removed.
605 static bool
static_data_member_added_or_removed(const class_diff * diff)606 static_data_member_added_or_removed(const class_diff* diff)
607 {
608   if (diff && !diff_involves_decl_only_class(diff))
609     {
610       for (string_decl_base_sptr_map::const_iterator i =
611 	     diff->inserted_data_members().begin();
612 	   i != diff->inserted_data_members().end();
613 	   ++i)
614 	if (get_member_is_static(i->second))
615 	  return true;
616 
617       for (string_decl_base_sptr_map::const_iterator i =
618 	     diff->deleted_data_members().begin();
619 	   i != diff->deleted_data_members().end();
620 	   ++i)
621 	if (get_member_is_static(i->second))
622 	  return true;
623     }
624 
625   return false;
626 }
627 
628 /// Test if a class_diff node has a harmless "One Definition Rule"
629 /// violation that will cause a diagnostic rule.
630 ///
631 /// The conditions this function looks for are:
632 ///
633 ///  1/ The two subject of the diff must be canonically different
634 ///
635 ///  2/ The two subjects of the diff must be structurally equal
636 ///
637 ///  3/ The canonical types of the subjects of the diff must be
638 ///  structurally different.
639 ///
640 /// These conditions makes the diff node appears as it carries changes
641 /// (because of a ODR glitch present in the binary), but the glitch
642 /// has no effect on the structural equality of the subjects of the
643 /// diff.  If we do not detect these conditions, we'd end up with a
644 /// diagnostic glitch where the reporter thinks there is an ABI change
645 /// (because of the canonical difference), but then it fails to give
646 /// any detail about it, because there is no structural change.
647 ///
648 /// @param diff the diff node to consider.
649 ///
650 /// @return true iff the the diff node has a harmless "One Definition
651 /// Rule" violation that cause an empty false positive.
652 static bool
class_diff_has_harmless_odr_violation_change(const diff * dif)653 class_diff_has_harmless_odr_violation_change(const diff* dif)
654 {
655   class_diff* d =  dynamic_cast<class_diff*>(const_cast<diff*>(dif));
656   if (!d || !d->has_changes())
657     return false;
658 
659   class_decl_sptr first = d->first_class_decl();
660   class_decl_sptr second = d->second_class_decl();
661 
662   if (equals(*first, *second, 0))
663     {
664       class_decl_sptr fc = is_class_type(first->get_canonical_type());
665       class_decl_sptr sc = is_class_type(second->get_canonical_type());
666 
667       if (!equals(*fc, *sc, 0))
668 	return true;
669     }
670 
671   return false;
672 }
673 
674 /// Test if a class_diff node has static members added or
675 /// removed.
676 ///
677 /// @param diff the diff node to consider.
678 ///
679 /// @return true iff the class_diff node has static members added
680 /// or removed.
681 static bool
static_data_member_added_or_removed(const diff * diff)682 static_data_member_added_or_removed(const diff* diff)
683 {
684   return static_data_member_added_or_removed
685     (dynamic_cast<const class_diff*>(diff));
686 }
687 
688 /// Test if the class_diff node has a change involving virtual member
689 /// functions.
690 ///
691 /// That means whether there is an added, removed or changed virtual
692 /// member function.
693 ///
694 /// @param diff the class_diff node to consider.
695 ///
696 /// @return true iff the class_diff node contains changes involving
697 /// virtual member functions.
698 static bool
has_virtual_mem_fn_change(const class_diff * diff)699 has_virtual_mem_fn_change(const class_diff* diff)
700 {
701   if (!diff || diff_involves_decl_only_class(diff))
702     return false;
703 
704   for (string_member_function_sptr_map::const_iterator i =
705 	 diff->deleted_member_fns().begin();
706        i != diff->deleted_member_fns().end();
707        ++i)
708     {
709       if (get_member_function_is_virtual(i->second))
710 	{
711 	  // Do not consider a virtual function that got deleted from
712 	  // an offset and re-inserted at the same offset as a
713 	  // "virtual member function change".
714 	  string_member_function_sptr_map::const_iterator j =
715 	    diff->inserted_member_fns().find(i->first);
716 	  if (j != diff->inserted_member_fns().end()
717 	      && (get_member_function_vtable_offset(i->second)
718 		  == get_member_function_vtable_offset(j->second)))
719 	    continue;
720 
721 	  return true;
722 	}
723     }
724 
725   for (string_member_function_sptr_map::const_iterator i =
726 	 diff->inserted_member_fns().begin();
727        i != diff->inserted_member_fns().end();
728        ++i)
729     {
730       if (get_member_function_is_virtual(i->second))
731 	{
732 	  // Do not consider a virtual function that got deleted from
733 	  // an offset and re-inserted at the same offset as a
734 	  // "virtual member function change".
735 	  string_member_function_sptr_map::const_iterator j =
736 	    diff->deleted_member_fns().find(i->first);
737 	  if (j != diff->deleted_member_fns().end()
738 	      && (get_member_function_vtable_offset(i->second)
739 		  == get_member_function_vtable_offset(j->second)))
740 	    continue;
741 
742 	  return true;
743 	}
744     }
745 
746   for (function_decl_diff_sptrs_type::const_iterator i =
747 	 diff->changed_member_fns().begin();
748        i != diff->changed_member_fns().end();
749        ++i)
750     if (get_member_function_is_virtual((*i)->first_function_decl())
751 	|| get_member_function_is_virtual((*i)->second_function_decl()))
752       {
753 	if (get_member_function_vtable_offset((*i)->first_function_decl())
754 	    == get_member_function_vtable_offset((*i)->second_function_decl()))
755 	  continue;
756 
757 	return true;
758       }
759 
760   return false;
761 }
762 
763 /// Test if the function_decl_diff node has a change involving virtual
764 /// member functions.
765 ///
766 /// That means whether there is an added, removed or changed virtual
767 /// member function.
768 ///
769 /// @param diff the function_decl_diff node to consider.
770 ///
771 /// @return true iff the function_decl_diff node contains changes
772 /// involving virtual member functions.
773 bool
has_virtual_mem_fn_change(const function_decl_diff * diff)774 has_virtual_mem_fn_change(const function_decl_diff* diff)
775 {
776   if (!diff)
777     return false;
778 
779   function_decl_sptr ff = diff->first_function_decl(),
780     sf = diff->second_function_decl();
781 
782   if (!is_member_function(ff)
783       || !is_member_function(sf))
784     return false;
785 
786   bool ff_is_virtual = get_member_function_is_virtual(ff),
787     sf_is_virtual = get_member_function_is_virtual(sf);
788 
789   if (ff_is_virtual != sf_is_virtual)
790     return true;
791 
792   size_t ff_vtable_offset = get_member_function_vtable_offset(ff),
793     sf_vtable_offset = get_member_function_vtable_offset(sf);
794 
795   if (ff_vtable_offset != sf_vtable_offset)
796     return true;
797 
798   return false;
799 }
800 
801 /// Test if the class_diff node has a change involving virtual member
802 /// functions.
803 ///
804 /// That means whether there is an added, removed or changed virtual
805 /// member function.
806 ///
807 /// @param diff the class_diff node to consider.
808 ///
809 /// @return true iff the class_diff node contains changes involving
810 /// virtual member functions.
811 static bool
has_virtual_mem_fn_change(const diff * diff)812 has_virtual_mem_fn_change(const diff* diff)
813 {
814   return (has_virtual_mem_fn_change(dynamic_cast<const class_diff*>(diff))
815 	  || has_virtual_mem_fn_change(dynamic_cast<const function_decl_diff*>(diff)));
816 }
817 
818 /// Test if the class_diff has changes to non virtual member
819 /// functions.
820 ///
821 ///@param diff the class_diff nod e to consider.
822 ///
823 /// @retrurn iff the class_diff node has changes to non virtual member
824 /// functions.
825 static bool
has_non_virtual_mem_fn_change(const class_diff * diff)826 has_non_virtual_mem_fn_change(const class_diff* diff)
827 {
828   if (!diff || diff_involves_decl_only_class(diff))
829     return false;
830 
831   for (string_member_function_sptr_map::const_iterator i =
832 	 diff->deleted_member_fns().begin();
833        i != diff->deleted_member_fns().end();
834        ++i)
835     if (!get_member_function_is_virtual(i->second))
836       return true;
837 
838   for (string_member_function_sptr_map::const_iterator i =
839 	 diff->inserted_member_fns().begin();
840        i != diff->inserted_member_fns().end();
841        ++i)
842     if (!get_member_function_is_virtual(i->second))
843       return true;
844 
845   for (function_decl_diff_sptrs_type::const_iterator i =
846 	 diff->changed_member_fns().begin();
847        i != diff->changed_member_fns().end();
848        ++i)
849     if(!get_member_function_is_virtual((*i)->first_function_decl())
850        && !get_member_function_is_virtual((*i)->second_function_decl()))
851       return true;
852 
853   return false;
854 }
855 
856 /// Test if the class_diff has changes to non virtual member
857 /// functions.
858 ///
859 ///@param diff the class_diff nod e to consider.
860 ///
861 /// @retrurn iff the class_diff node has changes to non virtual member
862 /// functions.
863 static bool
has_non_virtual_mem_fn_change(const diff * diff)864 has_non_virtual_mem_fn_change(const diff* diff)
865 {return has_non_virtual_mem_fn_change(dynamic_cast<const class_diff*>(diff));}
866 
867 /// Test if a class_diff carries base classes adding or removals.
868 ///
869 /// @param diff the class_diff to consider.
870 ///
871 /// @return true iff @p diff carries base classes adding or removals.
872 static bool
base_classes_added_or_removed(const class_diff * diff)873 base_classes_added_or_removed(const class_diff* diff)
874 {
875   if (!diff)
876     return false;
877   return diff->deleted_bases().size() || diff->inserted_bases().size();
878 }
879 
880 /// Test if a class_diff carries base classes adding or removals.
881 ///
882 /// @param diff the class_diff to consider.
883 ///
884 /// @return true iff @p diff carries base classes adding or removals.
885 static bool
base_classes_added_or_removed(const diff * diff)886 base_classes_added_or_removed(const diff* diff)
887 {return base_classes_added_or_removed(dynamic_cast<const class_diff*>(diff));}
888 
889 /// Test if two classes that are decl-only (have the decl-only flag
890 /// and carry no data members) but are different just by their size.
891 ///
892 /// In some weird DWARF representation, it happens that a decl-only
893 /// class (with no data member) actually carries a non-zero size.
894 /// That shouldn't happen, but hey, we need to deal with real life.
895 /// So we need to detect that case first.
896 ///
897 /// @param first the first class or union to consider.
898 ///
899 /// @param seconf the second class or union to consider.
900 ///
901 /// @return true if the two classes are decl-only and differ in their
902 /// size.
903 bool
is_decl_only_class_with_size_change(const class_or_union & first,const class_or_union & second)904 is_decl_only_class_with_size_change(const class_or_union& first,
905 				    const class_or_union& second)
906 {
907   if (first.get_qualified_name() != second.get_qualified_name())
908     return false;
909 
910   if (!first.get_is_declaration_only() || !second.get_is_declaration_only())
911     return false;
912 
913   bool f_is_empty = first.get_data_members().empty();
914   bool s_is_empty = second.get_data_members().empty();
915 
916   return f_is_empty && s_is_empty;
917 }
918 
919 /// Test if two classes that are decl-only (have the decl-only flag
920 /// and carry no data members) but are different just by their size.
921 ///
922 /// In some weird DWARF representation, it happens that a decl-only
923 /// class (with no data member) actually carries a non-zero size.
924 /// That shouldn't happen, but hey, we need to deal with real life.
925 /// So we need to detect that case first.
926 ///
927 /// @param first the first class or union to consider.
928 ///
929 /// @param seconf the second class or union to consider.
930 ///
931 /// @return true if the two classes are decl-only and differ in their
932 /// size.
933 bool
is_decl_only_class_with_size_change(const class_or_union_sptr & first,const class_or_union_sptr & second)934 is_decl_only_class_with_size_change(const class_or_union_sptr& first,
935 				    const class_or_union_sptr& second)
936 {
937   if (!first || !second)
938     return false;
939 
940   class_or_union_sptr f = look_through_decl_only_class(first);
941   class_or_union_sptr s = look_through_decl_only_class(second);
942 
943   return is_decl_only_class_with_size_change(*f, *s);
944 }
945 
946 /// Test if a diff node is for two classes that are decl-only (have
947 /// the decl-only flag and carry no data members) but are different
948 /// just by their size.
949 ///
950 /// In some weird DWARF representation, it happens that a decl-only
951 /// class (with no data member) actually carries a non-zero size.
952 /// That shouldn't happen, but hey, we need to deal with real life.
953 /// So we need to detect that case first.
954 ///
955 /// @param diff the diff node to consider.
956 ///
957 /// @return true if the two classes are decl-only and differ in their
958 /// size.
959 bool
is_decl_only_class_with_size_change(const diff * diff)960 is_decl_only_class_with_size_change(const diff *diff)
961 {
962   const class_or_union_diff *d = dynamic_cast<const class_or_union_diff*>(diff);
963   if (!d)
964     return false;
965 
966   class_or_union_sptr f =
967     look_through_decl_only_class(d->first_class_or_union());
968   class_or_union_sptr s =
969     look_through_decl_only_class(d->second_class_or_union());
970 
971   return is_decl_only_class_with_size_change(f, s);
972 }
973 
974 /// Test if two @ref decl_base_sptr are different just by the
975 /// fact that one is decl-only and the other one is defined.
976 ///
977 /// @param first the first decl to consider.
978 ///
979 /// @param second the second decl to consider.
980 ///
981 /// @return true iff the two arguments are different just by the fact
982 /// that one is decl-only and the other one is defined.
983 bool
has_decl_only_def_change(const decl_base_sptr & first,const decl_base_sptr & second)984 has_decl_only_def_change(const decl_base_sptr& first,
985 			 const decl_base_sptr& second)
986 {
987   if (!first || !second)
988     return false;
989 
990   decl_base_sptr f =
991     look_through_decl_only(first);
992   decl_base_sptr s =
993     look_through_decl_only(second);
994 
995   if (f->get_qualified_name() != s->get_qualified_name())
996     return false;
997 
998   return f->get_is_declaration_only() != s->get_is_declaration_only();
999 }
1000 
1001 /// Test if a diff carries a change in which the two decls are
1002 /// different by the fact that one is a decl-only and the other one is
1003 /// defined.
1004 ///
1005 /// @param diff the diff node to consider.
1006 ///
1007 /// @return true if the diff carries a change in which the two decls
1008 /// are different by the fact that one is a decl-only and the other
1009 /// one is defined.
1010 bool
has_decl_only_def_change(const diff * d)1011 has_decl_only_def_change(const diff *d)
1012 {
1013   if (!d)
1014     return false;
1015 
1016   decl_base_sptr f =
1017     look_through_decl_only(is_decl(d->first_subject()));
1018   decl_base_sptr s =
1019     look_through_decl_only(is_decl(d->second_subject()));
1020 
1021   return has_decl_only_def_change(f, s);
1022 }
1023 
1024 
1025 /// Test if two @ref class_or_union_sptr are different just by the
1026 /// fact that one is decl-only and the other one is defined.
1027 ///
1028 /// @param first the first class or union to consider.
1029 ///
1030 /// @param second the second class or union to consider.
1031 ///
1032 /// @return true iff the two arguments are different just by the fact
1033 /// that one is decl-only and the other one is defined.
1034 bool
has_class_decl_only_def_change(const class_or_union_sptr & first,const class_or_union_sptr & second)1035 has_class_decl_only_def_change(const class_or_union_sptr& first,
1036 			       const class_or_union_sptr& second)
1037 {
1038   if (!first || !second)
1039     return false;
1040 
1041   class_or_union_sptr f =
1042     look_through_decl_only_class(first);
1043   class_or_union_sptr s =
1044     look_through_decl_only_class(second);
1045 
1046   if (f->get_qualified_name() != s->get_qualified_name())
1047     return false;
1048 
1049   return f->get_is_declaration_only() != s->get_is_declaration_only();
1050 }
1051 
1052 /// Test if two @ref enum_sptr are different just by the
1053 /// fact that one is decl-only and the other one is defined.
1054 ///
1055 /// @param first the first enum to consider.
1056 ///
1057 /// @param second the second enum to consider.
1058 ///
1059 /// @return true iff the two arguments are different just by the fact
1060 /// that one is decl-only and the other one is defined.
1061 bool
has_enum_decl_only_def_change(const enum_type_decl_sptr & first,const enum_type_decl_sptr & second)1062 has_enum_decl_only_def_change(const enum_type_decl_sptr& first,
1063 			      const enum_type_decl_sptr& second)
1064 {
1065   if (!first || !second)
1066     return false;
1067 
1068   enum_type_decl_sptr f = look_through_decl_only_enum(first);
1069   enum_type_decl_sptr s = look_through_decl_only_enum(second);
1070 
1071   if (f->get_qualified_name() != s->get_qualified_name())
1072     return false;
1073 
1074   return f->get_is_declaration_only() != s->get_is_declaration_only();
1075 }
1076 
1077 /// Test if a class_or_union_diff carries a change in which the two
1078 /// classes are different by the fact that one is a decl-only and the
1079 /// other one is defined.
1080 ///
1081 /// @param diff the diff node to consider.
1082 ///
1083 /// @return true if the class_or_union_diff carries a change in which
1084 /// the two classes are different by the fact that one is a decl-only
1085 /// and the other one is defined.
1086 bool
has_class_decl_only_def_change(const diff * diff)1087 has_class_decl_only_def_change(const diff *diff)
1088 {
1089   const class_or_union_diff *d = dynamic_cast<const class_or_union_diff*>(diff);
1090   if (!d)
1091     return false;
1092 
1093   class_or_union_sptr f =
1094     look_through_decl_only_class(d->first_class_or_union());
1095   class_or_union_sptr s =
1096     look_through_decl_only_class(d->second_class_or_union());
1097 
1098   return has_class_decl_only_def_change(f, s);
1099 }
1100 
1101 /// Test if a enum_diff carries a change in which the two enums are
1102 /// different by the fact that one is a decl-only and the other one is
1103 /// defined.
1104 ///
1105 /// @param diff the diff node to consider.
1106 ///
1107 /// @return true if the enum_diff carries a change in which the two
1108 /// enums are different by the fact that one is a decl-only and the
1109 /// other one is defined.
1110 bool
has_enum_decl_only_def_change(const diff * diff)1111 has_enum_decl_only_def_change(const diff *diff)
1112 {
1113   const enum_diff *d = dynamic_cast<const enum_diff*>(diff);
1114   if (!d)
1115     return false;
1116 
1117   enum_type_decl_sptr f = look_through_decl_only_enum(d->first_enum());
1118   enum_type_decl_sptr s = look_through_decl_only_enum(d->second_enum());
1119 
1120   return has_enum_decl_only_def_change(f, s);
1121 }
1122 
1123 /// Test if a diff node carries a basic type name change.
1124 ///
1125 /// @param d the diff node to consider.
1126 ///
1127 /// @return true iff the diff node carries a basic type name change.
1128 bool
has_basic_type_name_change(const diff * d)1129 has_basic_type_name_change(const diff *d)
1130 {
1131   if (const type_decl_diff *dif = is_diff_of_basic_type(d))
1132     if (decl_name_changed(dif))
1133       return true;
1134 
1135   return false;
1136 }
1137 
1138 /// Test if a diff node carries a class or union type name change.
1139 ///
1140 /// @param d the diff node to consider.
1141 ///
1142 /// @return true iff the diff node carries a class or union type name
1143 /// change.
1144 bool
has_class_or_union_type_name_change(const diff * d)1145 has_class_or_union_type_name_change(const diff *d)
1146 {
1147   if (const class_or_union_diff *dif = is_diff_of_class_or_union_type(d))
1148     if (decl_name_changed(dif))
1149       return true;
1150 
1151   return false;
1152 }
1153 
1154 /// Test if a diff node carries a basic or class type name change.
1155 ///
1156 /// @param d the diff node to consider.
1157 ///
1158 /// @return true iff the diff node carries a basic or class type name
1159 /// change.
1160 bool
has_basic_or_class_type_name_change(const diff * d)1161 has_basic_or_class_type_name_change(const diff *d)
1162 {
1163   return (has_basic_type_name_change(d)
1164 	  || has_class_or_union_type_name_change(d));
1165 }
1166 
1167 /// Test if a diff node carries a distinct type change or a
1168 /// pointer/reference/typedef to distinct type change.
1169 ///
1170 /// Note that a distinct type change is a change where the two
1171 /// subjects of the change are not of the same kind, e.g, a basic type
1172 /// that got changed into a qualified type.
1173 ///
1174 /// @param d the diff node to consider.
1175 ///
1176 /// @return true iff @p d is mostly a distinct diff.
1177 bool
is_mostly_distinct_diff(const diff * d)1178 is_mostly_distinct_diff(const diff *d)
1179 {
1180   if (is_distinct_diff(d))
1181     return true;
1182 
1183   // Let's consider that 'd' is a type diff ...
1184   diff * td = const_cast<type_diff_base*>(is_type_diff(d));
1185   if (!td)
1186     {
1187       // ... or a function parameter diff.  In which case, let's get
1188       // its child type diff ...
1189       fn_parm_diff *pd = const_cast<fn_parm_diff*>(is_fn_parm_diff(d));
1190       if (pd)
1191 	{
1192 	  td = const_cast<type_diff_base*>(is_type_diff(pd->type_diff().get()));
1193 	  if (!td)
1194 	    // if the diff of the fn_parm_diff is a a distinct diff
1195 	    // then handle it.
1196 	    td = const_cast<distinct_diff*>
1197 	      (is_distinct_diff(pd->type_diff().get()));
1198 	}
1199       else
1200 	return false;
1201     }
1202 
1203   // At this point, if we are not looking at a type diff we must have
1204   // bailed out already.
1205   ABG_ASSERT(td);
1206 
1207   type_base_sptr first = is_type(td->first_subject());
1208   type_base_sptr second = is_type(td->second_subject());
1209 
1210   first = peel_typedef_pointer_or_reference_type(first);
1211   second = peel_typedef_pointer_or_reference_type(second);
1212   ABG_ASSERT(first && second);
1213 
1214   return distinct_diff::entities_are_of_distinct_kinds(first, second);
1215 }
1216 
1217 /// Test if a diff node carries a non-anonymous data member to
1218 /// anonymous data member change, or vice-versa.
1219 ///
1220 /// @param d the diff node to consider.
1221 ///
1222 /// @return true iff @p d carries a non-anonymous to anonymous data
1223 /// member change, or vice-versa.
1224 bool
has_anonymous_data_member_change(const diff * d)1225 has_anonymous_data_member_change(const diff *d)
1226 {
1227   if (is_anonymous_data_member(d->first_subject())
1228       || is_anonymous_data_member(d->second_subject()))
1229     return true;
1230   return false;
1231 }
1232 
1233 /// Test if a diff node carries a non-anonymous data member to
1234 /// anonymous data member change, or vice-versa.
1235 ///
1236 /// @param d the diff node to consider.
1237 ///
1238 /// @return true iff @p d carries a non-anonymous to anonymous data
1239 /// member change, or vice-versa.
1240 bool
has_anonymous_data_member_change(const diff_sptr & d)1241 has_anonymous_data_member_change(const diff_sptr &d)
1242 {return has_anonymous_data_member_change(d.get());}
1243 
1244 /// Test if an enum_diff carries an enumerator insertion.
1245 ///
1246 /// @param diff the enum_diff to consider.
1247 ///
1248 /// @return true iff @p diff carries an enumerator insertion.
1249 static bool
has_enumerator_insertion(const diff * diff)1250 has_enumerator_insertion(const diff* diff)
1251 {
1252   if (const enum_diff* d = dynamic_cast<const enum_diff*>(diff))
1253     return !d->inserted_enumerators().empty();
1254   return false;
1255 }
1256 
1257 /// Test if an enum_diff carries an enumerator removal.
1258 ///
1259 /// @param diff the enum_diff to consider.
1260 ///
1261 /// @return true iff @p diff carries an enumerator removal or change.
1262 static bool
has_enumerator_removal_or_change(const diff * diff)1263 has_enumerator_removal_or_change(const diff* diff)
1264 {
1265   if (const enum_diff* d = dynamic_cast<const enum_diff*>(diff))
1266     return (!d->deleted_enumerators().empty()
1267 	    || !d->changed_enumerators().empty());
1268   return false;
1269 }
1270 
1271 /// Test if an enum_diff carries a harmful change.
1272 ///
1273 /// @param diff the enum_diff to consider.
1274 ///
1275 /// @return true iff @p diff carries a harmful change.
1276 static bool
has_harmful_enum_change(const diff * diff)1277 has_harmful_enum_change(const diff* diff)
1278 {
1279   if (const enum_diff* d = dynamic_cast<const enum_diff*>(diff))
1280     return (has_enumerator_removal_or_change(d)
1281 	    || has_type_size_change(d));
1282   return false;
1283 }
1284 
1285 /// Test if a diff node carries a harmless change of an enum into an
1286 /// integer (or vice-versa).
1287 ///
1288 /// The test takes into account the fact change we care about might be
1289 /// wrapped into a typedef or qualified type diff.
1290 ///
1291 /// @param diff the diff node to consider.
1292 ///
1293 /// @return true if @p diff is a harmless enum to integer change.
1294 static bool
has_harmless_enum_to_int_change(const diff * diff)1295 has_harmless_enum_to_int_change(const diff* diff)
1296 {
1297   if (!diff)
1298     return false;
1299 
1300   diff = peel_typedef_or_qualified_type_diff(diff);
1301 
1302   if (const distinct_diff *d = is_distinct_diff(diff))
1303     {
1304       const enum_type_decl *enum_type = 0;
1305       const type_base *integer_type = 0;
1306 
1307       type_base *first_type =
1308 	peel_qualified_or_typedef_type(is_type(d->first().get()));
1309       type_base *second_type =
1310 	peel_qualified_or_typedef_type(is_type(d->second().get()));
1311 
1312       if (const enum_type_decl *e = is_enum_type(first_type))
1313 	enum_type = e;
1314       else if (const enum_type_decl *e = is_enum_type(second_type))
1315 	enum_type = e;
1316 
1317       if (const type_base * i = is_type_decl(first_type))
1318 	integer_type = i;
1319       else if (const type_base *i = is_type_decl(second_type))
1320 	integer_type = i;
1321 
1322       if (enum_type
1323 	  && integer_type
1324 	  && enum_type->get_size_in_bits() == integer_type->get_size_in_bits())
1325 	return true;
1326     }
1327 
1328   return false;
1329 }
1330 
1331 /// Test if an @ref fn_parm_diff node has a top cv qualifier change on
1332 /// the type of the function parameter.
1333 ///
1334 /// @param diff the diff node to consider.  It should be a @ref
1335 /// fn_parm_diff, otherwise the function returns 'false' directly.
1336 ///
1337 /// @return true iff @p diff is a @ref fn_parm_diff node that has a
1338 /// top cv qualifier change on the type of the function parameter.
1339 static bool
has_fn_parm_type_top_cv_qual_change(const diff * diff)1340 has_fn_parm_type_top_cv_qual_change(const diff* diff)
1341 {
1342   // is diff a "function parameter diff node?
1343   const fn_parm_diff* parm_diff = is_fn_parm_diff(diff);
1344 
1345   if (!parm_diff || !parm_diff->has_changes())
1346     // diff either carries no change or is not a function parameter
1347     // diff node.  So bail out.
1348     return false;
1349 
1350   function_decl::parameter_sptr first_parm = parm_diff->first_parameter();
1351   function_decl::parameter_sptr second_parm = parm_diff->second_parameter();
1352 
1353   type_base_sptr first_parm_type = first_parm->get_type();
1354   type_base_sptr second_parm_type = second_parm->get_type();
1355 
1356   if (!is_qualified_type(first_parm_type)
1357       && !is_qualified_type(second_parm_type))
1358     // None of the parameter types is qualified.
1359     return false;
1360 
1361   qualified_type_def::CV cv_quals_1 = qualified_type_def::CV_NONE;
1362   qualified_type_def::CV cv_quals_2 = qualified_type_def::CV_NONE;
1363   type_base_sptr peeled_type_1 = first_parm_type;
1364   type_base_sptr peeled_type_2 = second_parm_type;
1365 
1366   if (qualified_type_def_sptr qtype1 = is_qualified_type(first_parm_type))
1367     {
1368       cv_quals_1 = qtype1->get_cv_quals();
1369       peeled_type_1 = peel_qualified_type(qtype1);
1370     }
1371 
1372   if (qualified_type_def_sptr qtype2 = is_qualified_type(second_parm_type))
1373     {
1374       cv_quals_2 = qtype2->get_cv_quals();
1375       peeled_type_2 = peel_qualified_type(qtype2);
1376     }
1377 
1378   if (peeled_type_1
1379       && peeled_type_2
1380       && get_type_name(peeled_type_1) == get_type_name(peeled_type_2)
1381       && cv_quals_1 != cv_quals_2)
1382     // The top-level CV qualifiers of the function type are different
1383     // and the un-qualified variant (peeled) of said function types
1384     // are equal.  This means the only change the function types have
1385     // are about top-level CV qualifiers.
1386     return true;
1387 
1388   return false;
1389 }
1390 
1391 /// Test if a type diff only carries a CV qualifier-only change.
1392 ///
1393 /// @param type_dif the type dif to consider.
1394 ///
1395 /// @return true iff the type_diff carries a CV qualifier only change.
1396 static bool
type_diff_has_cv_qual_change_only(const diff * type_dif)1397 type_diff_has_cv_qual_change_only(const diff *type_dif)
1398 {
1399   if (!is_type_diff(type_dif))
1400     return false;
1401 
1402   if (is_pointer_diff(type_dif))
1403     type_dif = peel_pointer_diff(type_dif);
1404   else if (is_reference_diff(type_dif))
1405     type_dif = peel_reference_diff(type_dif);
1406 
1407   const type_base *f = 0;
1408   const type_base *s = 0;
1409   if (const distinct_diff *d = is_distinct_diff(type_dif))
1410     {
1411       if (is_qualified_type(d->first()) == is_qualified_type(d->second()))
1412 	return false;
1413       else
1414 	{
1415 	  f = is_type(d->first()).get();
1416 	  s = is_type(d->second()).get();
1417 	}
1418     }
1419   else if (const qualified_type_diff *d = is_qualified_type_diff(type_dif))
1420     {
1421       f = is_type(d->first_qualified_type()).get();
1422       s = is_type(d->second_qualified_type()).get();
1423     }
1424   else
1425     return false;
1426 
1427   f = peel_qualified_type(f);
1428   s = peel_qualified_type(s);
1429 
1430   // If f and s are arrays, note that they can differ only by the cv
1431   // qualifier of the array element type.  That cv qualifier is not
1432   // removed by peel_qualified_type.  So we need to test this case
1433   // specifically.
1434   if (array_type_def *f_a = is_array_type(f))
1435     if (array_type_def *s_a = is_array_type(s))
1436       return equals_modulo_cv_qualifier(f_a, s_a);
1437 
1438   return *f == *s;
1439 }
1440 
1441 /// Test if an @ref fn_parm_diff node has a cv qualifier change on the
1442 /// type of the function parameter. That is, we are looking for
1443 /// changes like 'const char*' to 'char*'.
1444 ///
1445 /// @param diff the diff node to consider.  It should be a @ref
1446 /// fn_parm_diff, otherwise the function returns 'false' directly.
1447 ///
1448 /// @return true iff @p diff is a @ref fn_parm_diff node that has a
1449 /// cv qualifier change on the type of the function parameter.
1450 static bool
has_fn_parm_type_cv_qual_change(const diff * dif)1451 has_fn_parm_type_cv_qual_change(const diff* dif)
1452 {
1453   // is diff a "function parameter diff node?
1454   const fn_parm_diff* parm_diff = is_fn_parm_diff(dif);
1455 
1456   if (!parm_diff || !parm_diff->has_changes())
1457     // diff either carries no change or is not a function parameter
1458     // diff node.  So bail out.
1459     return false;
1460 
1461   const diff *type_dif = parm_diff->type_diff().get();
1462   return type_diff_has_cv_qual_change_only(type_dif);
1463 }
1464 
1465 /// Test if a function type or decl diff node carries a CV
1466 /// qualifier-only change on its return type.
1467 ///
1468 /// @param dif the diff node to consider.  Note that if this is
1469 /// neither a function type nor decl diff node, the function returns
1470 /// false.
1471 ///
1472 /// @return true iff @p dif is a function decl or type diff node which
1473 /// carries a CV qualifier-only change on its return type.
1474 static bool
has_fn_return_type_cv_qual_change(const diff * dif)1475 has_fn_return_type_cv_qual_change(const diff* dif)
1476 {
1477   const function_type_diff* fn_type_diff = is_function_type_diff(dif);
1478   if (!fn_type_diff)
1479     if (const function_decl_diff* fn_decl_diff = is_function_decl_diff(dif))
1480       fn_type_diff = fn_decl_diff->type_diff().get();
1481 
1482   if (!fn_type_diff)
1483     return false;
1484 
1485   const diff* return_type_diff = fn_type_diff->return_type_diff().get();
1486   return type_diff_has_cv_qual_change_only(return_type_diff);
1487 }
1488 
1489 /// Test if a function type or decl diff node carries a function
1490 /// parameter addition or removal.
1491 ///
1492 /// @param dif the diff node to consider.  Note that if this is
1493 /// neither a function type nor decl diff node, the function returns
1494 /// false.
1495 ///
1496 /// @return true iff @p dif is a function decl or type diff node which
1497 /// carries a function parameter addition or removal.
1498 static bool
has_added_or_removed_function_parameters(const diff * dif)1499 has_added_or_removed_function_parameters(const diff *dif)
1500 {
1501   const function_type_diff *fn_type_diff = is_function_type_diff(dif);
1502     if (!fn_type_diff)
1503     if (const function_decl_diff* fn_decl_diff = is_function_decl_diff(dif))
1504       fn_type_diff = fn_decl_diff->type_diff().get();
1505 
1506   if (!fn_type_diff)
1507     return false;
1508 
1509   if (!(fn_type_diff->sorted_deleted_parms().empty()
1510 	&& fn_type_diff->sorted_added_parms().empty()))
1511     return true;
1512 
1513   return false;
1514 }
1515 
1516 /// Test if a variable diff node carries a CV qualifier change on its type.
1517 ///
1518 /// @param dif the diff node to consider.  Note that if it's not of
1519 /// var_diff type, the function returns false.
1520 ///
1521 /// @return true iff the @p dif carries a CV qualifier change on its
1522 /// type.
1523 static bool
has_var_type_cv_qual_change(const diff * dif)1524 has_var_type_cv_qual_change(const diff* dif)
1525 {
1526   const var_diff *var_dif = is_var_diff(dif);
1527   if (!var_dif)
1528     return false;
1529 
1530   {
1531     // Make sure the variable diff does carry a type change at least
1532     change_kind ch_kind = NO_CHANGE_KIND;
1533     if (equals(*var_dif->first_var(), *var_dif->second_var(), &ch_kind))
1534       return false;
1535 
1536     if (!(ch_kind & LOCAL_TYPE_CHANGE_KIND || ch_kind & SUBTYPE_CHANGE_KIND))
1537       return false;
1538   }
1539 
1540   diff *type_dif = var_dif->type_diff().get();
1541   ABG_ASSERT(type_dif);
1542 
1543   return type_diff_has_cv_qual_change_only(type_dif);
1544 }
1545 
1546 /// Test if a diff node carries a void* to pointer type change.
1547 ///
1548 /// Note that this function looks through typedef and qualifier types
1549 /// to find the void pointer.
1550 ///
1551 /// @param dif the diff node to consider.
1552 ///
1553 /// @return true iff @p dif carries a void* to pointer type change.
1554 static bool
has_void_ptr_to_ptr_change(const diff * dif)1555 has_void_ptr_to_ptr_change(const diff* dif)
1556 {
1557   dif = peel_typedef_diff(dif);
1558 
1559   if (const distinct_diff *d = is_distinct_diff(dif))
1560     {
1561       const type_base *f = is_type(d->first().get());
1562       const type_base *s = is_type(d->second().get());
1563 
1564       f = peel_qualified_or_typedef_type(f);
1565       s = peel_qualified_or_typedef_type(s);
1566 
1567       if (is_void_pointer_type(f)
1568 	  && is_pointer_type(s)
1569 	  && !is_void_pointer_type(s)
1570 	  && f->get_size_in_bits() == s->get_size_in_bits())
1571 	return true;
1572     }
1573   else if (const pointer_diff *d = is_pointer_diff(dif))
1574     {
1575       const type_base *f = is_type(d->first_pointer()).get();
1576       const type_base *s = is_type(d->second_pointer()).get();
1577 
1578       f = peel_qualified_or_typedef_type(f);
1579       s = peel_qualified_or_typedef_type(s);
1580 
1581       if (is_void_pointer_type(f)
1582 	  && is_pointer_type(s)
1583 	  && !is_void_pointer_type(s)
1584 	  && f->get_size_in_bits() == s->get_size_in_bits())
1585 	return true;
1586     }
1587   else if (const qualified_type_diff *d = is_qualified_type_diff(dif))
1588     {
1589       const type_base *f = is_type(d->first_qualified_type()).get();
1590       const type_base *s = is_type(d->second_qualified_type()).get();
1591 
1592       f = peel_qualified_or_typedef_type(f);
1593       s = peel_qualified_or_typedef_type(s);
1594 
1595       if (is_void_pointer_type(f)
1596 	  && is_pointer_type(s)
1597 	  && !is_void_pointer_type(s)
1598 	  && f->get_size_in_bits() == s->get_size_in_bits())
1599 	return true;
1600     }
1601 
1602   return false;
1603 }
1604 
1605 /// Test if a diff node carries a benign change to the size of a
1606 /// variable of type array.
1607 ///
1608 /// A benign size change is a change in size (from or to infinite) of
1609 /// the array as expressed by the debug info, but when the *ELF* size
1610 /// (what really matters) of the variable object hasn't changed.  This
1611 /// happens when the debug info emitter did have trouble figuring out
1612 /// the actual size of the array.
1613 ///
1614 /// @param dif the diff node to consider.
1615 ///
1616 /// @return true iff @p dif contains the benign array type size change.
1617 static bool
has_benign_infinite_array_change(const diff * dif)1618 has_benign_infinite_array_change(const diff* dif)
1619 {
1620   if (const var_diff* var_dif = is_var_diff(dif))
1621     {
1622       if (!var_dif->first_var()->get_symbol()
1623 	  || var_dif->second_var()->get_symbol())
1624 	return false;
1625 
1626       if (var_dif->first_var()->get_symbol()->get_size()
1627 	  != var_dif->second_var()->get_symbol()->get_size())
1628 	return false;
1629 
1630       const diff *d = var_dif->type_diff().get();
1631       if (!d)
1632 	return false;
1633       d = peel_qualified_diff(d);
1634       if (const array_diff *a = is_array_diff(d))
1635 	{
1636 	  array_type_def_sptr f = a->first_array(), s = a->second_array();
1637 	  if (f->is_infinite() != s->is_infinite())
1638 	    return true;
1639 	}
1640     }
1641   return false;
1642 }
1643 
1644 /// Test if a union diff node does have changes that don't impact its
1645 /// size.
1646 ///
1647 /// @param d the union diff node to consider.
1648 ///
1649 /// @return true iff @p d is a diff node which has changes that don't
1650 /// impact its size.
1651 bool
union_diff_has_harmless_changes(const diff * d)1652 union_diff_has_harmless_changes(const diff *d)
1653 {
1654   if (is_union_diff(d)
1655       && d->has_changes()
1656       && !has_type_size_change(d))
1657     return true;
1658 
1659   return false;
1660 }
1661 
1662 /// Detect if the changes carried by a given diff node are deemed
1663 /// harmless and do categorize the diff node accordingly.
1664 ///
1665 /// @param d the diff node being visited.
1666 ///
1667 /// @param pre this is true iff the node is being visited *before* the
1668 /// children nodes of @p d.
1669 ///
1670 /// @return true iff the traversal shall keep going after the
1671 /// completion of this function.
1672 static bool
categorize_harmless_diff_node(diff * d,bool pre)1673 categorize_harmless_diff_node(diff *d, bool pre)
1674 {
1675   if (!d->has_changes())
1676     return true;
1677 
1678   if (pre)
1679     {
1680       diff_category category = NO_CHANGE_CATEGORY;
1681 
1682       decl_base_sptr f = is_decl(d->first_subject()),
1683 	s = is_decl(d->second_subject());
1684 
1685       if (has_class_decl_only_def_change(d)
1686 	  || has_enum_decl_only_def_change(d))
1687 	category |= TYPE_DECL_ONLY_DEF_CHANGE_CATEGORY;
1688 
1689       if (access_changed(f, s))
1690 	category |= ACCESS_CHANGE_CATEGORY;
1691 
1692       if (is_compatible_change(f, s))
1693 	category |= COMPATIBLE_TYPE_CHANGE_CATEGORY;
1694 
1695       if (has_harmless_name_change(f, s)
1696 	  || class_diff_has_harmless_odr_violation_change(d))
1697 	category |= HARMLESS_DECL_NAME_CHANGE_CATEGORY;
1698 
1699       if (union_diff_has_harmless_changes(d))
1700 	category |= HARMLESS_UNION_CHANGE_CATEGORY;
1701 
1702       if (has_non_virtual_mem_fn_change(d))
1703 	category |= NON_VIRT_MEM_FUN_CHANGE_CATEGORY;
1704 
1705       if (static_data_member_added_or_removed(d)
1706 	  || static_data_member_type_size_changed(f, s))
1707 	category |= STATIC_DATA_MEMBER_CHANGE_CATEGORY;
1708 
1709       if (has_data_member_replaced_by_anon_dm(d))
1710 	category |= HARMLESS_DATA_MEMBER_CHANGE_CATEGORY;
1711 
1712       if ((has_enumerator_insertion(d)
1713 	   && !has_harmful_enum_change(d))
1714 	  || has_harmless_enum_to_int_change(d))
1715 	category |= HARMLESS_ENUM_CHANGE_CATEGORY;
1716 
1717       if (function_name_changed_but_not_symbol(d))
1718 	category |= HARMLESS_SYMBOL_ALIAS_CHANGE_CATEGORY;
1719 
1720       if (has_fn_parm_type_top_cv_qual_change(d))
1721 	category |= FN_PARM_TYPE_TOP_CV_CHANGE_CATEGORY;
1722 
1723       if (has_fn_parm_type_cv_qual_change(d))
1724 	category |= FN_PARM_TYPE_CV_CHANGE_CATEGORY;
1725 
1726       if (has_fn_return_type_cv_qual_change(d))
1727 	category |= FN_RETURN_TYPE_CV_CHANGE_CATEGORY;
1728 
1729       if (has_var_type_cv_qual_change(d))
1730 	category |= VAR_TYPE_CV_CHANGE_CATEGORY;
1731 
1732       if (has_void_ptr_to_ptr_change(d))
1733 	category |= VOID_PTR_TO_PTR_CHANGE_CATEGORY;
1734 
1735       if (has_benign_infinite_array_change(d))
1736 	category |= BENIGN_INFINITE_ARRAY_CHANGE_CATEGORY;
1737 
1738       if (category)
1739 	{
1740 	  d->add_to_local_and_inherited_categories(category);
1741 	  // Also update the category of the canonical node.
1742 	  if (diff * canonical = d->get_canonical_diff())
1743 	    canonical->add_to_local_and_inherited_categories(category);
1744 	}
1745     }
1746 
1747   return true;
1748 }
1749 
1750 /// Detect if the changes carried by a given diff node are deemed
1751 /// harmful and do categorize the diff node accordingly.
1752 ///
1753 /// @param d the diff node being visited.
1754 ///
1755 /// @param pre this is true iff the node is being visited *before* the
1756 /// children nodes of @p d.
1757 ///
1758 /// @return true iff the traversal shall keep going after the
1759 /// completion of this function.
1760 static bool
categorize_harmful_diff_node(diff * d,bool pre)1761 categorize_harmful_diff_node(diff *d, bool pre)
1762 {
1763   if (!d->has_changes())
1764     return true;
1765 
1766   if (pre)
1767     {
1768       diff_category category = NO_CHANGE_CATEGORY;
1769       decl_base_sptr f = is_decl(d->first_subject()),
1770 	s = is_decl(d->second_subject());
1771 
1772       // Detect size or offset changes as well as data member addition
1773       // or removal.
1774       //
1775       // TODO: be more specific -- not all size changes are harmful.
1776       if (!has_class_decl_only_def_change(d)
1777 	  && !has_enum_decl_only_def_change(d)
1778 	  && (type_size_changed(f, s)
1779 	      || data_member_offset_changed(f, s)
1780 	      || non_static_data_member_type_size_changed(f, s)
1781 	      || non_static_data_member_added_or_removed(d)
1782 	      || base_classes_added_or_removed(d)
1783 	      || has_harmful_enum_change(d)
1784 	      || crc_changed(d)))
1785 	category |= SIZE_OR_OFFSET_CHANGE_CATEGORY;
1786 
1787       if (has_virtual_mem_fn_change(d))
1788 	category |= VIRTUAL_MEMBER_CHANGE_CATEGORY;
1789 
1790       if (has_added_or_removed_function_parameters(d))
1791 	category |= FN_PARM_ADD_REMOVE_CHANGE_CATEGORY;
1792 
1793       if (category)
1794 	{
1795 	  d->add_to_local_and_inherited_categories(category);
1796 	  // Update the category of the canonical diff node too.
1797 	  if (diff * canonical = d->get_canonical_diff())
1798 	    canonical->add_to_local_and_inherited_categories(category);
1799 	}
1800     }
1801 
1802   return true;
1803 }
1804 
1805 /// The visiting code of the harmless_harmful_filter.
1806 ///
1807 /// @param d the diff node being visited.
1808 ///
1809 /// @param pre this is true iff the node is being visited *before* the
1810 /// children nodes of @p d.
1811 ///
1812 /// @return true iff the traversal shall keep going after the
1813 /// completion of this function.
1814 bool
visit(diff * d,bool pre)1815 harmless_harmful_filter::visit(diff* d, bool pre)
1816 {
1817   return (categorize_harmless_diff_node(d, pre)
1818 	  && categorize_harmful_diff_node(d, pre));
1819 }
1820 
1821 /// Part of the visiting code of the harmless_harmful_filter.
1822 ///
1823 /// This function is called after the visiting of a given diff node.
1824 /// Note that when this function is called, the visiting might not
1825 /// have taken place *if* the node (or an equivalent node) has already
1826 /// been visited.
1827 ///
1828 /// @param d the diff node that has either been visited or skipped
1829 /// (because it has already been visited during this traversing).
1830 void
visit_end(diff * d)1831 harmless_harmful_filter::visit_end(diff* d)
1832 {
1833   if (d->context()->diff_has_been_visited(d))
1834     {
1835       // This node or one of its equivalent node has already been
1836       // visited.  That means at this moment,
1837       // harmless_harmful_filter::visit() has *not* been called prior
1838       // to this harmless_harmful_filter::visit_end() is called.  In
1839       // other words, only harmless_harmful_filter::visit_begin() and
1840       // harmless_harmful_filter::visit_end() are called.
1841       //
1842       // So let's update the category of this diff node from its
1843       // canonical node.
1844       if (diff* c = d->get_canonical_diff())
1845 	d->add_to_local_and_inherited_categories(c->get_local_category());
1846     }
1847 }
1848 } // end namespace filtering
1849 } // end namespace comparison
1850 } // end namespace abigail
1851