libabigail
abg-comp-filter.cc
Go to the documentation of this file.
1 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
2 // -*- Mode: C++ -*-
3 //
4 // Copyright (C) 2013-2023 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 
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
42 {
43  bool s = d->context()->visiting_a_node_twice_is_forbidden();
44  d->context()->forbid_visiting_a_node_twice(true);
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
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
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
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
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
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
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),
154  || there_is_a_decl_only_enum(is_compatible_with_enum_type(f),
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
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
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
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 
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
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();
234  const auto& symbol_s = s->get_symbol();
235  if (!symbol_f || !symbol_s)
236  return false;
237  return symbol_f->get_crc() != symbol_s->get_crc();
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
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 or variable namespace change.
258 ///
259 /// @param f the first function or variable to consider.
260 ///
261 /// @param s the second function or variable to consider.
262 ///
263 /// @return true if the test is positive, false otherwise.
264 template <typename function_or_var_decl_sptr>
265 static bool
266 namespace_changed(const function_or_var_decl_sptr& f,
267  const function_or_var_decl_sptr& s)
268 {
269  const auto& symbol_f = f->get_symbol();
270  const auto& symbol_s = s->get_symbol();
271  if (!symbol_f || !symbol_s)
272  return false;
273  return symbol_f->get_namespace() != symbol_s->get_namespace();
274 }
275 
276 /// Test if the current diff tree node carries a namespace change in
277 /// either a function or a variable.
278 ///
279 /// @param diff the diff tree node to consider.
280 ///
281 /// @return true if the test is positive, false otherwise.
282 static bool
283 namespace_changed(const diff* diff)
284 {
285  if (const function_decl_diff* d =
286  dynamic_cast<const function_decl_diff*>(diff))
287  return namespace_changed(d->first_function_decl(),
288  d->second_function_decl());
289  if (const var_diff* d = dynamic_cast<const var_diff*>(diff))
290  return namespace_changed(d->first_var(), d->second_var());
291  return false;
292 }
293 
294 /// Test if there was a function name change, but there there was no
295 /// change in name of the underlying symbol. IOW, if the name of a
296 /// function changed, but the symbol of the new function is equal to
297 /// the symbol of the old one, or is equal to an alians of the symbol
298 /// of the old function.
299 ///
300 /// @param f the first function to consider.
301 ///
302 /// @param s the second function to consider.
303 ///
304 /// @return true if the test is positive, false otherwise.
305 static bool
306 function_name_changed_but_not_symbol(const function_decl_sptr& f,
307  const function_decl_sptr& s)
308 {
309  if (!f || !s)
310  return false;
311  string fn = f->get_qualified_name(),
312  sn = s->get_qualified_name();
313 
314  if (fn != sn)
315  {
316  elf_symbol_sptr fs = f->get_symbol(), ss = s->get_symbol();
317  if (fs == ss)
318  return true;
319  if (!!fs != !!ss)
320  return false;
321  for (elf_symbol_sptr s = fs->get_next_alias();
322  s && !s->is_main_symbol();
323  s = s->get_next_alias())
324  if (*s == *ss)
325  return true;
326  }
327  return false;
328 }
329 
330 /// Test if the current diff tree node carries a function name change,
331 /// in which there there was no change in the name of the underlying
332 /// symbol. IOW, if the name of a function changed, but the symbol of
333 /// the new function is equal to the symbol of the old one, or is
334 /// equal to an alians of the symbol of the old function.
335 ///
336 /// @param diff the diff tree node to consider.
337 ///
338 /// @return true if the test is positive, false otherwise.
339 static bool
340 function_name_changed_but_not_symbol(const diff* diff)
341 {
342  if (const function_decl_diff* d =
343  dynamic_cast<const function_decl_diff*>(diff))
344  return function_name_changed_but_not_symbol(d->first_function_decl(),
345  d->second_function_decl());
346  return false;
347 }
348 
349 /// Tests if the offset of a given data member changed.
350 ///
351 /// @param f the declaration for the first version of the data member to
352 /// consider.
353 ///
354 /// @param s the declaration for the second version of the data member
355 /// to consider.
356 ///
357 /// @return true iff the offset of the data member changed.
358 static bool
359 data_member_offset_changed(decl_base_sptr f, decl_base_sptr s)
360 {
361  if (!is_member_decl(f)
362  || !is_member_decl(s))
363  return false;
364 
365  var_decl_sptr v0 = dynamic_pointer_cast<var_decl>(f),
366  v1 = dynamic_pointer_cast<var_decl>(s);
367  if (!v0 || !v1)
368  return false;
369 
371  return true;
372 
373  return false;
374 }
375 
376 /// Test if the size of a non-static data member changed accross two
377 /// versions.
378 ///
379 /// @param f the first version of the non-static data member.
380 ///
381 /// @param s the second version of the non-static data member.
382 static bool
383 non_static_data_member_type_size_changed(const decl_base_sptr& f,
384  const decl_base_sptr& s)
385 {
386  if (!is_member_decl(f)
387  || !is_member_decl(s))
388  return false;
389 
390  var_decl_sptr fv = dynamic_pointer_cast<var_decl>(f),
391  sv = dynamic_pointer_cast<var_decl>(s);
392  if (!fv
393  || !sv
394  || get_member_is_static(fv)
395  || get_member_is_static(sv))
396  return false;
397 
398  return type_size_changed(fv->get_type(), sv->get_type());
399 }
400 
401 /// Test if the size of a static data member changed accross two
402 /// versions.
403 ///
404 /// @param f the first version of the static data member.
405 ///
406 /// @param s the second version of the static data member.
407 static bool
408 static_data_member_type_size_changed(const decl_base_sptr& f,
409  const decl_base_sptr& s)
410 {
411  if (!is_member_decl(f)
412  || !is_member_decl(s))
413  return false;
414 
415  var_decl_sptr fv = dynamic_pointer_cast<var_decl>(f),
416  sv = dynamic_pointer_cast<var_decl>(s);
417  if (!fv
418  || !sv
419  || !get_member_is_static(fv)
420  || !get_member_is_static(sv))
421  return false;
422 
423  return type_size_changed(fv->get_type(), sv->get_type());
424 }
425 
426 /// Test if two types are different but compatible.
427 ///
428 /// @param d1 the declaration of the first type to consider.
429 ///
430 /// @param d2 the declaration of the second type to consider.
431 ///
432 /// @return true if d1 and d2 are different but compatible.
433 static bool
434 is_compatible_change(const decl_base_sptr& d1, const decl_base_sptr& d2)
435 {
436  if ((d1 && d2)
437  && (d1 != d2)
438  && types_are_compatible(d1, d2))
439  return true;
440  return false;
441 }
442 
443 /// Test if two decls have different names.
444 ///
445 /// @param d1 the first declaration to consider.
446 ///
447 /// @param d2 the second declaration to consider.
448 ///
449 /// @return true if d1 and d2 have different names.
450 static bool
451 decl_name_changed(const type_or_decl_base* a1, const type_or_decl_base *a2)
452 {
453  string d1_name, d2_name;
454 
455  const decl_base *d1 = dynamic_cast<const decl_base*>(a1);
456  if (d1 == 0)
457  return false;
458 
459  const decl_base *d2 = dynamic_cast<const decl_base*>(a2);
460  if (d2 == 0)
461  return false;
462 
463  if (d1)
464  d1_name = d1->get_qualified_name();
465  if (d2)
466  d2_name = d2->get_qualified_name();
467 
468  return d1_name != d2_name;
469 }
470 
471 /// Test if two decls have different names.
472 ///
473 /// @param d1 the first declaration to consider.
474 ///
475 /// @param d2 the second declaration to consider.
476 ///
477 /// @return true if d1 and d2 have different names.
478 static bool
479 decl_name_changed(const type_or_decl_base_sptr& d1,
480  const type_or_decl_base_sptr& d2)
481 {return decl_name_changed(d1.get(), d2.get());}
482 
483 /// Test if a diff nodes carries a changes in which two decls have
484 /// different names.
485 ///
486 /// @param d the diff node to consider.
487 ///
488 /// @return true iff d carries a changes in which two decls have
489 /// different names.
490 static bool
491 decl_name_changed(const diff *d)
492 {return decl_name_changed(d->first_subject(), d->second_subject());}
493 
494 /// Test if two decls represents a harmless name change.
495 ///
496 /// For now, a harmless name change is considered only for a typedef,
497 /// enum or a data member.
498 ///
499 /// @param f the first decl to consider in the comparison.
500 ///
501 /// @param s the second decl to consider in the comparison.
502 ///
503 /// @return true iff decl @p s represents a harmless change over @p f.
504 bool
505 has_harmless_name_change(const decl_base_sptr& f, const decl_base_sptr& s)
506 {
507  // So, a harmless name change is either ...
508  return (decl_name_changed(f, s)
509  && (// ... an anonymous decl name changed into another
510  // anonymous decl name ...
511  (f->get_is_anonymous() && s->get_is_anonymous())
512  ||
513  // ... an anonymous decl name changed harmlessly into
514  // another anonymous decl name ...
515  ((f->get_is_anonymous_or_has_anonymous_parent()
516  && s->get_is_anonymous_or_has_anonymous_parent())
517  && tools_utils::decl_names_equal(f->get_qualified_name(),
518  s->get_qualified_name()))
519  // ... a typedef name change, without having the
520  // underlying type changed ...
521  || (is_typedef(f)
522  && is_typedef(s)
523  && (is_typedef(f)->get_underlying_type()
524  == is_typedef(s)->get_underlying_type()))
525  // .. or a data member name change, without having its
526  // type changed ...
527  || (is_data_member(f)
528  && is_data_member(s)
529  && (is_var_decl(f)->get_type()
530  == is_var_decl(s)->get_type()))
531  // .. an enum name change without having any other part
532  // of the enum to change.
533  || (is_enum_type(f)
534  && is_enum_type(s)
536  *is_enum_type(s),
537  0))));
538 }
539 
540 /// Test if two decls represents a harmful name change.
541 ///
542 /// A harmful name change is a name change that is not harmless, so
543 /// this function uses the function has_harmless_name_change.
544 ///
545 /// @param f the first decl to consider in the comparison.
546 ///
547 /// @param s the second decl to consider in the comparison.
548 ///
549 /// @return true iff decl @p s represents a harmful name change over
550 /// @p f.
551 bool
552 has_harmful_name_change(const decl_base_sptr& f, const decl_base_sptr& s)
553 {return decl_name_changed(f, s) && ! has_harmless_name_change(f, s);}
554 
555 /// Test if a diff node represents a harmful name change.
556 ///
557 /// A harmful name change is a name change that is not harmless, so
558 /// this function uses the function has_harmless_name_change.
559 ///
560 /// @param f the first decl to consider in the comparison.
561 ///
562 /// @param s the second decl to consider in the comparison.
563 ///
564 /// @return true iff decl @p s represents a harmful name change over
565 /// @p f.
566 bool
567 has_harmful_name_change(const diff* dif)
568 {
569  decl_base_sptr f = is_decl(dif->first_subject()),
570  s = is_decl(dif->second_subject());
571 
572  return has_harmful_name_change(f, s);
573 }
574 
575 /// Test if a class_diff node has non-static members added or
576 /// removed.
577 ///
578 /// @param diff the diff node to consider.
579 ///
580 /// @return true iff the class_diff node has non-static members added
581 /// or removed.
582 static bool
583 non_static_data_member_added_or_removed(const class_diff* diff)
584 {
585  if (diff && !diff_involves_decl_only_class(diff))
586  {
587  for (string_decl_base_sptr_map::const_iterator i =
588  diff->inserted_data_members().begin();
589  i != diff->inserted_data_members().end();
590  ++i)
591  if (!get_member_is_static(i->second))
592  return true;
593 
594  for (string_decl_base_sptr_map::const_iterator i =
595  diff->deleted_data_members().begin();
596  i != diff->deleted_data_members().end();
597  ++i)
598  if (!get_member_is_static(i->second))
599  return true;
600  }
601 
602  return false;
603 }
604 
605 /// Test if a class_diff node has members added or removed.
606 ///
607 /// @param diff the diff node to consider.
608 ///
609 /// @return true iff the class_diff node has members added or removed.
610 static bool
611 non_static_data_member_added_or_removed(const diff* diff)
612 {
613  return non_static_data_member_added_or_removed
614  (dynamic_cast<const class_diff*>(diff));
615 }
616 
617 /// Test if a @ref class_or_union_diff has a data member replaced by
618 /// an anonymous data member in a harmless way. That means, the new
619 /// anonymous data member somehow contains the replaced data member
620 /// and it doesn't break the layout of the containing class.
621 ///
622 /// @param diff the diff node to consider.
623 ///
624 /// @return true iff the @ref class_or_union_diff has a data member
625 /// harmlessly replaced by an anonymous data member.
626 bool
628 {
630 
631  if (!c)
632  return false;
633  return !c->data_members_replaced_by_adms().empty();
634 }
635 
636 /// Test if we are looking at two variables which types are both one
637 /// dimension array, with one of them being of unknow size and the two
638 /// variables having the same symbol size.
639 ///
640 /// This can happen in the case of these two declarations, for instance:
641 ///
642 /// unsigned int array[];
643 ///
644 /// and:
645 ///
646 /// unsigned int array[] ={0};
647 ///
648 /// In both cases, the size of the ELF symbol of the variable 'array'
649 /// is 32 bits, but, at least in the first case
650 bool
652  const var_decl_sptr& var2)
653 {
654  type_base_sptr /*first type*/ft =
655  peel_qualified_or_typedef_type(var1->get_type());
656  type_base_sptr /*second type*/st =
657  peel_qualified_or_typedef_type(var2->get_type());
658 
659  array_type_def_sptr /*first array type*/fat = is_array_type(ft);
660  array_type_def_sptr /*second array type*/sat = is_array_type(st);
661 
662  // The types of the variables must be arrays.
663  if (!fat || !sat)
664  return false;
665 
666  // The arrays must have one dimension and at least one of them must
667  // be of unknown size.
668  if (fat->get_subranges().size() != 1
669  || sat->get_subranges().size() != 1
670  || (!fat->is_infinite() && !sat->is_infinite()))
671  return false;
672 
673  // The variables must be equal modulo their type.
674  if (!var_equals_modulo_types(*var1, *var2, nullptr))
675  return false;
676 
677  // The symbols of the variables must be defined and of the same
678  // non-zero size.
679  if (!var1->get_symbol()
680  || !var2->get_symbol()
681  || var1->get_symbol()->get_size() != var2->get_symbol()->get_size())
682  return false;
683 
684  return true;
685 }
686 
687 /// Test if we are looking at a diff that carries a change of
688 /// variables which types are both one dimension array, with one of
689 /// them being of unknow size and the two variables having the same
690 /// symbol size.
691 ///
692 /// This can happen in the case of these two declarations, for instance:
693 ///
694 /// unsigned int array[];
695 ///
696 /// and:
697 ///
698 /// unsigned int array[] ={0};
699 ///
700 /// In both cases, the size of the ELF symbol of the variable 'array'
701 /// is 32 bits, but, at least in the first case
702 bool
704 {
705  const var_diff* d = is_var_diff(diff);
706 
707  if (!d)
708  return false;
709 
710  var_decl_sptr f = d->first_var(), s = d->second_var();
711 
713 }
714 
715 /// Test if a class_diff node has static members added or removed.
716 ///
717 /// @param diff the diff node to consider.
718 ///
719 /// @return true iff the class_diff node has static members added
720 /// or removed.
721 static bool
722 static_data_member_added_or_removed(const class_diff* diff)
723 {
724  if (diff && !diff_involves_decl_only_class(diff))
725  {
726  for (string_decl_base_sptr_map::const_iterator i =
727  diff->inserted_data_members().begin();
728  i != diff->inserted_data_members().end();
729  ++i)
730  if (get_member_is_static(i->second))
731  return true;
732 
733  for (string_decl_base_sptr_map::const_iterator i =
734  diff->deleted_data_members().begin();
735  i != diff->deleted_data_members().end();
736  ++i)
737  if (get_member_is_static(i->second))
738  return true;
739  }
740 
741  return false;
742 }
743 
744 /// Test if a class_diff node has a harmless "One Definition Rule"
745 /// violation that will cause a diagnostic rule.
746 ///
747 /// The conditions this function looks for are:
748 ///
749 /// 1/ The two subject of the diff must be canonically different
750 ///
751 /// 2/ The two subjects of the diff must be structurally equal
752 ///
753 /// 3/ The canonical types of the subjects of the diff must be
754 /// structurally different.
755 ///
756 /// These conditions makes the diff node appears as it carries changes
757 /// (because of a ODR glitch present in the binary), but the glitch
758 /// has no effect on the structural equality of the subjects of the
759 /// diff. If we do not detect these conditions, we'd end up with a
760 /// diagnostic glitch where the reporter thinks there is an ABI change
761 /// (because of the canonical difference), but then it fails to give
762 /// any detail about it, because there is no structural change.
763 ///
764 /// @param diff the diff node to consider.
765 ///
766 /// @return true iff the the diff node has a harmless "One Definition
767 /// Rule" violation that cause an empty false positive.
768 static bool
769 class_diff_has_harmless_odr_violation_change(const diff* dif)
770 {
771  class_diff* d = dynamic_cast<class_diff*>(const_cast<diff*>(dif));
772  if (!d || !d->has_changes())
773  return false;
774 
775  class_decl_sptr first = d->first_class_decl();
776  class_decl_sptr second = d->second_class_decl();
777 
778  if (first->get_qualified_name() == second->get_qualified_name()
779  && first != second
780  && first->get_corpus() == second->get_corpus())
781  return true;
782 
783  return false;
784 }
785 
786 /// Test if a class_diff node has static members added or
787 /// removed.
788 ///
789 /// @param diff the diff node to consider.
790 ///
791 /// @return true iff the class_diff node has static members added
792 /// or removed.
793 static bool
794 static_data_member_added_or_removed(const diff* diff)
795 {
796  return static_data_member_added_or_removed
797  (dynamic_cast<const class_diff*>(diff));
798 }
799 
800 /// Test if the class_diff node has a change involving virtual member
801 /// functions.
802 ///
803 /// That means whether there is an added, removed or changed virtual
804 /// member function.
805 ///
806 /// @param diff the class_diff node to consider.
807 ///
808 /// @return true iff the class_diff node contains changes involving
809 /// virtual member functions.
810 static bool
811 has_virtual_mem_fn_change(const class_diff* diff)
812 {
813  if (!diff || diff_involves_decl_only_class(diff))
814  return false;
815 
816  for (string_member_function_sptr_map::const_iterator i =
817  diff->deleted_member_fns().begin();
818  i != diff->deleted_member_fns().end();
819  ++i)
820  {
821  if (get_member_function_is_virtual(i->second))
822  {
823  // Do not consider a virtual function that got deleted from
824  // an offset and re-inserted at the same offset as a
825  // "virtual member function change".
826  string_member_function_sptr_map::const_iterator j =
827  diff->inserted_member_fns().find(i->first);
828  if (j != diff->inserted_member_fns().end()
829  && (get_member_function_vtable_offset(i->second)
830  == get_member_function_vtable_offset(j->second)))
831  continue;
832 
833  return true;
834  }
835  }
836 
837  for (string_member_function_sptr_map::const_iterator i =
838  diff->inserted_member_fns().begin();
839  i != diff->inserted_member_fns().end();
840  ++i)
841  {
842  if (get_member_function_is_virtual(i->second))
843  {
844  // Do not consider a virtual function that got deleted from
845  // an offset and re-inserted at the same offset as a
846  // "virtual member function change".
847  string_member_function_sptr_map::const_iterator j =
848  diff->deleted_member_fns().find(i->first);
849  if (j != diff->deleted_member_fns().end()
850  && (get_member_function_vtable_offset(i->second)
851  == get_member_function_vtable_offset(j->second)))
852  continue;
853 
854  return true;
855  }
856  }
857 
858  for (function_decl_diff_sptrs_type::const_iterator i =
859  diff->changed_member_fns().begin();
860  i != diff->changed_member_fns().end();
861  ++i)
862  if (get_member_function_is_virtual((*i)->first_function_decl())
863  || get_member_function_is_virtual((*i)->second_function_decl()))
864  {
865  if (get_member_function_vtable_offset((*i)->first_function_decl())
866  == get_member_function_vtable_offset((*i)->second_function_decl()))
867  continue;
868 
869  return true;
870  }
871 
872  return false;
873 }
874 
875 /// Test if the function_decl_diff node has a change involving virtual
876 /// member functions.
877 ///
878 /// That means whether there is an added, removed or changed virtual
879 /// member function.
880 ///
881 /// @param diff the function_decl_diff node to consider.
882 ///
883 /// @return true iff the function_decl_diff node contains changes
884 /// involving virtual member functions.
885 bool
886 has_virtual_mem_fn_change(const function_decl_diff* diff)
887 {
888  if (!diff)
889  return false;
890 
892  sf = diff->second_function_decl();
893 
894  if (!is_member_function(ff)
895  || !is_member_function(sf))
896  return false;
897 
898  bool ff_is_virtual = get_member_function_is_virtual(ff),
899  sf_is_virtual = get_member_function_is_virtual(sf);
900 
901  if (ff_is_virtual != sf_is_virtual)
902  return true;
903 
904  size_t ff_vtable_offset = get_member_function_vtable_offset(ff),
905  sf_vtable_offset = get_member_function_vtable_offset(sf);
906 
907  if (ff_vtable_offset != sf_vtable_offset)
908  return true;
909 
910  return false;
911 }
912 
913 /// Test if the class_diff node has a change involving virtual member
914 /// functions.
915 ///
916 /// That means whether there is an added, removed or changed virtual
917 /// member function.
918 ///
919 /// @param diff the class_diff node to consider.
920 ///
921 /// @return true iff the class_diff node contains changes involving
922 /// virtual member functions.
923 static bool
924 has_virtual_mem_fn_change(const diff* diff)
925 {
926  return (has_virtual_mem_fn_change(dynamic_cast<const class_diff*>(diff))
927  || has_virtual_mem_fn_change(dynamic_cast<const function_decl_diff*>(diff)));
928 }
929 
930 /// Test if the class_diff has changes to non virtual member
931 /// functions.
932 ///
933 ///@param diff the class_diff nod e to consider.
934 ///
935 /// @retrurn iff the class_diff node has changes to non virtual member
936 /// functions.
937 static bool
938 has_non_virtual_mem_fn_change(const class_diff* diff)
939 {
940  if (!diff || diff_involves_decl_only_class(diff))
941  return false;
942 
943  for (string_member_function_sptr_map::const_iterator i =
944  diff->deleted_member_fns().begin();
945  i != diff->deleted_member_fns().end();
946  ++i)
947  if (!get_member_function_is_virtual(i->second))
948  return true;
949 
950  for (string_member_function_sptr_map::const_iterator i =
951  diff->inserted_member_fns().begin();
952  i != diff->inserted_member_fns().end();
953  ++i)
954  if (!get_member_function_is_virtual(i->second))
955  return true;
956 
957  for (function_decl_diff_sptrs_type::const_iterator i =
958  diff->changed_member_fns().begin();
959  i != diff->changed_member_fns().end();
960  ++i)
961  if(!get_member_function_is_virtual((*i)->first_function_decl())
962  && !get_member_function_is_virtual((*i)->second_function_decl()))
963  return true;
964 
965  return false;
966 }
967 
968 /// Test if the class_diff has changes to non virtual member
969 /// functions.
970 ///
971 ///@param diff the class_diff nod e to consider.
972 ///
973 /// @retrurn iff the class_diff node has changes to non virtual member
974 /// functions.
975 static bool
976 has_non_virtual_mem_fn_change(const diff* diff)
977 {return has_non_virtual_mem_fn_change(dynamic_cast<const class_diff*>(diff));}
978 
979 /// Test if a class_diff carries base classes adding or removals.
980 ///
981 /// @param diff the class_diff to consider.
982 ///
983 /// @return true iff @p diff carries base classes adding or removals.
984 static bool
985 base_classes_added_or_removed(const class_diff* diff)
986 {
987  if (!diff)
988  return false;
989  return diff->deleted_bases().size() || diff->inserted_bases().size();
990 }
991 
992 /// Test if a class_diff carries base classes adding or removals.
993 ///
994 /// @param diff the class_diff to consider.
995 ///
996 /// @return true iff @p diff carries base classes adding or removals.
997 static bool
998 base_classes_added_or_removed(const diff* diff)
999 {return base_classes_added_or_removed(dynamic_cast<const class_diff*>(diff));}
1000 
1001 /// Test if two classes that are decl-only (have the decl-only flag
1002 /// and carry no data members) but are different just by their size.
1003 ///
1004 /// In some weird DWARF representation, it happens that a decl-only
1005 /// class (with no data member) actually carries a non-zero size.
1006 /// That shouldn't happen, but hey, we need to deal with real life.
1007 /// So we need to detect that case first.
1008 ///
1009 /// @param first the first class or union to consider.
1010 ///
1011 /// @param seconf the second class or union to consider.
1012 ///
1013 /// @return true if the two classes are decl-only and differ in their
1014 /// size.
1015 bool
1017  const class_or_union& second)
1018 {
1019  if (first.get_qualified_name() != second.get_qualified_name())
1020  return false;
1021 
1022  if (!first.get_is_declaration_only() || !second.get_is_declaration_only())
1023  return false;
1024 
1025  bool f_is_empty = first.get_data_members().empty();
1026  bool s_is_empty = second.get_data_members().empty();
1027 
1028  return f_is_empty && s_is_empty;
1029 }
1030 
1031 /// Test if two classes that are decl-only (have the decl-only flag
1032 /// and carry no data members) but are different just by their size.
1033 ///
1034 /// In some weird DWARF representation, it happens that a decl-only
1035 /// class (with no data member) actually carries a non-zero size.
1036 /// That shouldn't happen, but hey, we need to deal with real life.
1037 /// So we need to detect that case first.
1038 ///
1039 /// @param first the first class or union to consider.
1040 ///
1041 /// @param seconf the second class or union to consider.
1042 ///
1043 /// @return true if the two classes are decl-only and differ in their
1044 /// size.
1045 bool
1046 is_decl_only_class_with_size_change(const class_or_union_sptr& first,
1047  const class_or_union_sptr& second)
1048 {
1049  if (!first || !second)
1050  return false;
1051 
1052  class_or_union_sptr f = look_through_decl_only_class(first);
1053  class_or_union_sptr s = look_through_decl_only_class(second);
1054 
1055  return is_decl_only_class_with_size_change(*f, *s);
1056 }
1057 
1058 /// Test if a diff node is for two classes that are decl-only (have
1059 /// the decl-only flag and carry no data members) but are different
1060 /// just by their size.
1061 ///
1062 /// In some weird DWARF representation, it happens that a decl-only
1063 /// class (with no data member) actually carries a non-zero size.
1064 /// That shouldn't happen, but hey, we need to deal with real life.
1065 /// So we need to detect that case first.
1066 ///
1067 /// @param diff the diff node to consider.
1068 ///
1069 /// @return true if the two classes are decl-only and differ in their
1070 /// size.
1071 bool
1073 {
1074  const class_or_union_diff *d = dynamic_cast<const class_or_union_diff*>(diff);
1075  if (!d)
1076  return false;
1077 
1078  class_or_union_sptr f =
1080  class_or_union_sptr s =
1082 
1084 }
1085 
1086 /// Test if two @ref decl_base_sptr are different just by the
1087 /// fact that one is decl-only and the other one is defined.
1088 ///
1089 /// @param first the first decl to consider.
1090 ///
1091 /// @param second the second decl to consider.
1092 ///
1093 /// @return true iff the two arguments are different just by the fact
1094 /// that one is decl-only and the other one is defined.
1095 bool
1096 has_decl_only_def_change(const decl_base_sptr& first,
1097  const decl_base_sptr& second)
1098 {
1099  if (!first || !second)
1100  return false;
1101 
1102  decl_base_sptr f =
1103  look_through_decl_only(first);
1104  decl_base_sptr s =
1105  look_through_decl_only(second);
1106 
1107  if (f->get_qualified_name() != s->get_qualified_name())
1108  return false;
1109 
1110  return f->get_is_declaration_only() != s->get_is_declaration_only();
1111 }
1112 
1113 /// Test if a diff carries a change in which the two decls are
1114 /// different by the fact that one is a decl-only and the other one is
1115 /// defined.
1116 ///
1117 /// @param diff the diff node to consider.
1118 ///
1119 /// @return true if the diff carries a change in which the two decls
1120 /// are different by the fact that one is a decl-only and the other
1121 /// one is defined.
1122 bool
1124 {
1125  if (!d)
1126  return false;
1127 
1128  decl_base_sptr f =
1130  decl_base_sptr s =
1132 
1133  return has_decl_only_def_change(f, s);
1134 }
1135 
1136 
1137 /// Test if two @ref class_or_union_sptr are different just by the
1138 /// fact that one is decl-only and the other one is defined.
1139 ///
1140 /// @param first the first class or union to consider.
1141 ///
1142 /// @param second the second class or union to consider.
1143 ///
1144 /// @return true iff the two arguments are different just by the fact
1145 /// that one is decl-only and the other one is defined.
1146 bool
1147 has_class_decl_only_def_change(const class_or_union_sptr& first,
1148  const class_or_union_sptr& second)
1149 {
1150  if (!first || !second)
1151  return false;
1152 
1153  class_or_union_sptr f =
1155  class_or_union_sptr s =
1157 
1158  if (f->get_qualified_name() != s->get_qualified_name())
1159  return false;
1160 
1161  return f->get_is_declaration_only() != s->get_is_declaration_only();
1162 }
1163 
1164 /// Test if two @ref enum_sptr are different just by the
1165 /// fact that one is decl-only and the other one is defined.
1166 ///
1167 /// @param first the first enum to consider.
1168 ///
1169 /// @param second the second enum to consider.
1170 ///
1171 /// @return true iff the two arguments are different just by the fact
1172 /// that one is decl-only and the other one is defined.
1173 bool
1175  const enum_type_decl_sptr& second)
1176 {
1177  if (!first || !second)
1178  return false;
1179 
1182 
1183  if (f->get_qualified_name() != s->get_qualified_name())
1184  return false;
1185 
1186  return f->get_is_declaration_only() != s->get_is_declaration_only();
1187 }
1188 
1189 /// Test if a class_or_union_diff carries a change in which the two
1190 /// classes are different by the fact that one is a decl-only and the
1191 /// other one is defined.
1192 ///
1193 /// @param diff the diff node to consider.
1194 ///
1195 /// @return true if the class_or_union_diff carries a change in which
1196 /// the two classes are different by the fact that one is a decl-only
1197 /// and the other one is defined.
1198 bool
1200 {
1201  const class_or_union_diff *d = dynamic_cast<const class_or_union_diff*>(diff);
1202  if (!d)
1203  return false;
1204 
1205  class_or_union_sptr f =
1207  class_or_union_sptr s =
1209 
1210  return has_class_decl_only_def_change(f, s);
1211 }
1212 
1213 /// Test if a enum_diff carries a change in which the two enums are
1214 /// different by the fact that one is a decl-only and the other one is
1215 /// defined.
1216 ///
1217 /// @param diff the diff node to consider.
1218 ///
1219 /// @return true if the enum_diff carries a change in which the two
1220 /// enums are different by the fact that one is a decl-only and the
1221 /// other one is defined.
1222 bool
1224 {
1225  const enum_diff *d = dynamic_cast<const enum_diff*>(diff);
1226  if (!d)
1227  return false;
1228 
1231 
1232  return has_enum_decl_only_def_change(f, s);
1233 }
1234 
1235 /// Test if a diff node carries a basic type name change.
1236 ///
1237 /// @param d the diff node to consider.
1238 ///
1239 /// @return true iff the diff node carries a basic type name change.
1240 bool
1242 {
1243  if (const type_decl_diff *dif = is_diff_of_basic_type(d))
1244  if (decl_name_changed(dif))
1245  return true;
1246 
1247  return false;
1248 }
1249 
1250 /// Test if a diff node carries a class or union type name change.
1251 ///
1252 /// @param d the diff node to consider.
1253 ///
1254 /// @return true iff the diff node carries a class or union type name
1255 /// change.
1256 bool
1258 {
1260  if (decl_name_changed(dif))
1261  return true;
1262 
1263  return false;
1264 }
1265 
1266 /// Test if a diff node carries a basic or class type name change.
1267 ///
1268 /// @param d the diff node to consider.
1269 ///
1270 /// @return true iff the diff node carries a basic or class type name
1271 /// change.
1272 bool
1274 {
1275  return (has_basic_type_name_change(d)
1277 }
1278 
1279 /// Test if a diff node carries a distinct type change or a
1280 /// pointer/reference/typedef to distinct type change.
1281 ///
1282 /// Note that a distinct type change is a change where the two
1283 /// subjects of the change are not of the same kind, e.g, a basic type
1284 /// that got changed into a qualified type.
1285 ///
1286 /// @param d the diff node to consider.
1287 ///
1288 /// @return true iff @p d is mostly a distinct diff.
1289 bool
1291 {
1292  if (is_distinct_diff(d))
1293  return true;
1294 
1295  // Let's consider that 'd' is a type diff ...
1296  diff * td = const_cast<type_diff_base*>(is_type_diff(d));
1297  if (!td)
1298  {
1299  // ... or a function parameter diff. In which case, let's get
1300  // its child type diff ...
1301  fn_parm_diff *pd = const_cast<fn_parm_diff*>(is_fn_parm_diff(d));
1302  if (pd)
1303  {
1304  td = const_cast<type_diff_base*>(is_type_diff(pd->type_diff().get()));
1305  if (!td)
1306  // if the diff of the fn_parm_diff is a a distinct diff
1307  // then handle it.
1308  td = const_cast<distinct_diff*>
1309  (is_distinct_diff(pd->type_diff().get()));
1310  }
1311  else
1312  return false;
1313  }
1314 
1315  // At this point, if we are not looking at a type diff we must have
1316  // bailed out already.
1317  ABG_ASSERT(td);
1318 
1319  type_base_sptr first = is_type(td->first_subject());
1320  type_base_sptr second = is_type(td->second_subject());
1321 
1323  second = peel_typedef_pointer_or_reference_type(second);
1324  ABG_ASSERT(first && second);
1325 
1326  return distinct_diff::entities_are_of_distinct_kinds(first, second);
1327 }
1328 
1329 /// Test if a diff node carries a non-anonymous data member to
1330 /// anonymous data member change, or vice-versa.
1331 ///
1332 /// @param d the diff node to consider.
1333 ///
1334 /// @return true iff @p d carries a non-anonymous to anonymous data
1335 /// member change, or vice-versa.
1336 bool
1338 {
1341  return true;
1342  return false;
1343 }
1344 
1345 /// Test if a diff node carries a non-anonymous data member to
1346 /// anonymous data member change, or vice-versa.
1347 ///
1348 /// @param d the diff node to consider.
1349 ///
1350 /// @return true iff @p d carries a non-anonymous to anonymous data
1351 /// member change, or vice-versa.
1352 bool
1354 {return has_anonymous_data_member_change(d.get());}
1355 
1356 /// Test if an enum_diff carries an enumerator insertion.
1357 ///
1358 /// @param diff the enum_diff to consider.
1359 ///
1360 /// @return true iff @p diff carries an enumerator insertion.
1361 static bool
1362 has_enumerator_insertion(const diff* diff)
1363 {
1364  if (const enum_diff* d = dynamic_cast<const enum_diff*>(diff))
1365  return !d->inserted_enumerators().empty();
1366  return false;
1367 }
1368 
1369 /// Test if an enum_diff carries an enumerator removal.
1370 ///
1371 /// @param diff the enum_diff to consider.
1372 ///
1373 /// @return true iff @p diff carries an enumerator removal or change.
1374 static bool
1375 has_enumerator_removal_or_change(const diff* diff)
1376 {
1377  if (const enum_diff* d = dynamic_cast<const enum_diff*>(diff))
1378  return (!d->deleted_enumerators().empty()
1379  || !d->changed_enumerators().empty());
1380  return false;
1381 }
1382 
1383 /// Test if an enum_diff carries a harmful change.
1384 ///
1385 /// @param diff the enum_diff to consider.
1386 ///
1387 /// @return true iff @p diff carries a harmful change.
1388 static bool
1389 has_harmful_enum_change(const diff* diff)
1390 {
1391  if (const enum_diff* d = dynamic_cast<const enum_diff*>(diff))
1392  return (has_enumerator_removal_or_change(d)
1393  || has_type_size_change(d));
1394  return false;
1395 }
1396 
1397 /// Test if a diff node carries a harmless change of an enum into an
1398 /// integer (or vice-versa).
1399 ///
1400 /// The test takes into account the fact change we care about might be
1401 /// wrapped into a typedef or qualified type diff.
1402 ///
1403 /// @param diff the diff node to consider.
1404 ///
1405 /// @return true if @p diff is a harmless enum to integer change.
1406 static bool
1407 has_harmless_enum_to_int_change(const diff* diff)
1408 {
1409  if (!diff)
1410  return false;
1411 
1413 
1414  if (const distinct_diff *d = is_distinct_diff(diff))
1415  {
1416  const enum_type_decl *enum_type = 0;
1417  const type_base *integer_type = 0;
1418 
1419  type_base *first_type =
1420  peel_qualified_or_typedef_type(is_type(d->first().get()));
1421  type_base *second_type =
1422  peel_qualified_or_typedef_type(is_type(d->second().get()));
1423 
1424  if (const enum_type_decl *e = is_enum_type(first_type))
1425  enum_type = e;
1426  else if (const enum_type_decl *e = is_enum_type(second_type))
1427  enum_type = e;
1428 
1429  if (const type_base * i = is_type_decl(first_type))
1430  integer_type = i;
1431  else if (const type_base *i = is_type_decl(second_type))
1432  integer_type = i;
1433 
1434  if (enum_type
1435  && integer_type
1436  && enum_type->get_size_in_bits() == integer_type->get_size_in_bits())
1437  return true;
1438  }
1439 
1440  return false;
1441 }
1442 
1443 /// Test if an @ref fn_parm_diff node has a top cv qualifier change on
1444 /// the type of the function parameter.
1445 ///
1446 /// @param diff the diff node to consider. It should be a @ref
1447 /// fn_parm_diff, otherwise the function returns 'false' directly.
1448 ///
1449 /// @return true iff @p diff is a @ref fn_parm_diff node that has a
1450 /// top cv qualifier change on the type of the function parameter.
1451 static bool
1452 has_fn_parm_type_top_cv_qual_change(const diff* diff)
1453 {
1454  // is diff a "function parameter diff node?
1455  const fn_parm_diff* parm_diff = is_fn_parm_diff(diff);
1456 
1457  if (!parm_diff || !parm_diff->has_changes())
1458  // diff either carries no change or is not a function parameter
1459  // diff node. So bail out.
1460  return false;
1461 
1462  function_decl::parameter_sptr first_parm = parm_diff->first_parameter();
1463  function_decl::parameter_sptr second_parm = parm_diff->second_parameter();
1464 
1465  type_base_sptr first_parm_type = first_parm->get_type();
1466  type_base_sptr second_parm_type = second_parm->get_type();
1467 
1468  if (!is_qualified_type(first_parm_type)
1469  && !is_qualified_type(second_parm_type))
1470  // None of the parameter types is qualified.
1471  return false;
1472 
1473  qualified_type_def::CV cv_quals_1 = qualified_type_def::CV_NONE;
1474  qualified_type_def::CV cv_quals_2 = qualified_type_def::CV_NONE;
1475  type_base_sptr peeled_type_1 = first_parm_type;
1476  type_base_sptr peeled_type_2 = second_parm_type;
1477 
1478  if (qualified_type_def_sptr qtype1 = is_qualified_type(first_parm_type))
1479  {
1480  cv_quals_1 = qtype1->get_cv_quals();
1481  peeled_type_1 = peel_qualified_type(qtype1);
1482  }
1483 
1484  if (qualified_type_def_sptr qtype2 = is_qualified_type(second_parm_type))
1485  {
1486  cv_quals_2 = qtype2->get_cv_quals();
1487  peeled_type_2 = peel_qualified_type(qtype2);
1488  }
1489 
1490  if (peeled_type_1
1491  && peeled_type_2
1492  && get_type_name(peeled_type_1) == get_type_name(peeled_type_2)
1493  && cv_quals_1 != cv_quals_2)
1494  // The top-level CV qualifiers of the function type are different
1495  // and the un-qualified variant (peeled) of said function types
1496  // are equal. This means the only change the function types have
1497  // are about top-level CV qualifiers.
1498  return true;
1499 
1500  return false;
1501 }
1502 
1503 /// Test if a type diff only carries a CV qualifier-only change.
1504 ///
1505 /// @param type_dif the type dif to consider.
1506 ///
1507 /// @return true iff the type_diff carries a CV qualifier only change.
1508 static bool
1509 type_diff_has_cv_qual_change_only(const diff *type_dif)
1510 {
1511  if (!is_type_diff(type_dif))
1512  return false;
1513 
1514  if (is_pointer_diff(type_dif))
1515  type_dif = peel_pointer_diff(type_dif);
1516  else if (is_reference_diff(type_dif))
1517  type_dif = peel_reference_diff(type_dif);
1518 
1519  const type_base *f = 0;
1520  const type_base *s = 0;
1521  if (const distinct_diff *d = is_distinct_diff(type_dif))
1522  {
1523  if (is_qualified_type(d->first()) == is_qualified_type(d->second()))
1524  return false;
1525  else
1526  {
1527  f = is_type(d->first()).get();
1528  s = is_type(d->second()).get();
1529  }
1530  }
1531  else if (const qualified_type_diff *d = is_qualified_type_diff(type_dif))
1532  {
1533  f = is_type(d->first_qualified_type()).get();
1534  s = is_type(d->second_qualified_type()).get();
1535  }
1536  else
1537  return false;
1538 
1539  f = peel_qualified_type(f);
1540  s = peel_qualified_type(s);
1541 
1542  // If f and s are arrays, note that they can differ only by the cv
1543  // qualifier of the array element type. That cv qualifier is not
1544  // removed by peel_qualified_type. So we need to test this case
1545  // specifically.
1546  if (array_type_def *f_a = is_array_type(f))
1547  if (array_type_def *s_a = is_array_type(s))
1548  return equals_modulo_cv_qualifier(f_a, s_a);
1549 
1550  return *f == *s;
1551 }
1552 
1553 /// Test if an @ref fn_parm_diff node has a cv qualifier change on the
1554 /// type of the function parameter. That is, we are looking for
1555 /// changes like 'const char*' to 'char*'.
1556 ///
1557 /// @param diff the diff node to consider. It should be a @ref
1558 /// fn_parm_diff, otherwise the function returns 'false' directly.
1559 ///
1560 /// @return true iff @p diff is a @ref fn_parm_diff node that has a
1561 /// cv qualifier change on the type of the function parameter.
1562 static bool
1563 has_fn_parm_type_cv_qual_change(const diff* dif)
1564 {
1565  // is diff a "function parameter diff node?
1566  const fn_parm_diff* parm_diff = is_fn_parm_diff(dif);
1567 
1568  if (!parm_diff || !parm_diff->has_changes())
1569  // diff either carries no change or is not a function parameter
1570  // diff node. So bail out.
1571  return false;
1572 
1573  const diff *type_dif = parm_diff->type_diff().get();
1574  return type_diff_has_cv_qual_change_only(type_dif);
1575 }
1576 
1577 /// Test if a function type or decl diff node carries a CV
1578 /// qualifier-only change on its return type.
1579 ///
1580 /// @param dif the diff node to consider. Note that if this is
1581 /// neither a function type nor decl diff node, the function returns
1582 /// false.
1583 ///
1584 /// @return true iff @p dif is a function decl or type diff node which
1585 /// carries a CV qualifier-only change on its return type.
1586 static bool
1587 has_fn_return_type_cv_qual_change(const diff* dif)
1588 {
1589  const function_type_diff* fn_type_diff = is_function_type_diff(dif);
1590  if (!fn_type_diff)
1591  if (const function_decl_diff* fn_decl_diff = is_function_decl_diff(dif))
1592  fn_type_diff = fn_decl_diff->type_diff().get();
1593 
1594  if (!fn_type_diff)
1595  return false;
1596 
1597  const diff* return_type_diff = fn_type_diff->return_type_diff().get();
1598  return type_diff_has_cv_qual_change_only(return_type_diff);
1599 }
1600 
1601 /// Test if a function type or decl diff node carries a function
1602 /// parameter addition or removal.
1603 ///
1604 /// @param dif the diff node to consider. Note that if this is
1605 /// neither a function type nor decl diff node, the function returns
1606 /// false.
1607 ///
1608 /// @return true iff @p dif is a function decl or type diff node which
1609 /// carries a function parameter addition or removal.
1610 static bool
1611 has_added_or_removed_function_parameters(const diff *dif)
1612 {
1613  const function_type_diff *fn_type_diff = is_function_type_diff(dif);
1614  if (!fn_type_diff)
1615  if (const function_decl_diff* fn_decl_diff = is_function_decl_diff(dif))
1616  fn_type_diff = fn_decl_diff->type_diff().get();
1617 
1618  if (!fn_type_diff)
1619  return false;
1620 
1621  if (!(fn_type_diff->sorted_deleted_parms().empty()
1622  && fn_type_diff->sorted_added_parms().empty()))
1623  return true;
1624 
1625  return false;
1626 }
1627 
1628 /// Test if a variable diff node carries a CV qualifier change on its type.
1629 ///
1630 /// @param dif the diff node to consider. Note that if it's not of
1631 /// var_diff type, the function returns false.
1632 ///
1633 /// @return true iff the @p dif carries a CV qualifier change on its
1634 /// type.
1635 static bool
1636 has_var_type_cv_qual_change(const diff* dif)
1637 {
1638  const var_diff *var_dif = is_var_diff(dif);
1639  if (!var_dif)
1640  return false;
1641 
1642  diff *type_dif = var_dif->type_diff().get();
1643  if (!type_dif)
1644  return false;
1645 
1646  return type_diff_has_cv_qual_change_only(type_dif);
1647 }
1648 
1649 /// Test if a diff node carries a void* to pointer type change.
1650 ///
1651 /// Note that this function looks through typedef and qualifier types
1652 /// to find the void pointer.
1653 ///
1654 /// @param dif the diff node to consider.
1655 ///
1656 /// @return true iff @p dif carries a void* to pointer type change.
1657 static bool
1658 has_void_ptr_to_ptr_change(const diff* dif)
1659 {
1660  dif = peel_typedef_diff(dif);
1661 
1662  if (const distinct_diff *d = is_distinct_diff(dif))
1663  {
1664  const type_base *f = is_type(d->first().get());
1665  const type_base *s = is_type(d->second().get());
1666 
1669 
1671  && is_pointer_type(s)
1673  && f->get_size_in_bits() == s->get_size_in_bits())
1674  return true;
1675  }
1676  else if (const pointer_diff *d = is_pointer_diff(dif))
1677  {
1678  const type_base *f = is_type(d->first_pointer()).get();
1679  const type_base *s = is_type(d->second_pointer()).get();
1680 
1683 
1685  && is_pointer_type(s)
1687  && f->get_size_in_bits() == s->get_size_in_bits())
1688  return true;
1689  }
1690  else if (const qualified_type_diff *d = is_qualified_type_diff(dif))
1691  {
1692  const type_base *f = is_type(d->first_qualified_type()).get();
1693  const type_base *s = is_type(d->second_qualified_type()).get();
1694 
1697 
1699  && is_pointer_type(s)
1701  && f->get_size_in_bits() == s->get_size_in_bits())
1702  return true;
1703  }
1704 
1705  return false;
1706 }
1707 
1708 /// Test if a diff node carries a benign change to the size of a
1709 /// variable of type array.
1710 ///
1711 /// A benign size change is a change in size (from or to infinite) of
1712 /// the array as expressed by the debug info, but when the *ELF* size
1713 /// (what really matters) of the variable object hasn't changed. This
1714 /// happens when the debug info emitter did have trouble figuring out
1715 /// the actual size of the array.
1716 ///
1717 /// @param dif the diff node to consider.
1718 ///
1719 /// @return true iff @p dif contains the benign array type size change.
1720 static bool
1721 has_benign_array_of_unknown_size_change(const diff* dif)
1722 {
1724 }
1725 
1726 /// Test if a union diff node does have changes that don't impact its
1727 /// size.
1728 ///
1729 /// @param d the union diff node to consider.
1730 ///
1731 /// @return true iff @p d is a diff node which has changes that don't
1732 /// impact its size.
1733 bool
1735 {
1736  if (is_union_diff(d)
1737  && d->has_changes()
1738  && !has_type_size_change(d))
1739  return true;
1740 
1741  return false;
1742 }
1743 
1744 /// Detect if the changes carried by a given diff node are deemed
1745 /// harmless and do categorize the diff node accordingly.
1746 ///
1747 /// @param d the diff node being visited.
1748 ///
1749 /// @param pre this is true iff the node is being visited *before* the
1750 /// children nodes of @p d.
1751 ///
1752 /// @return true iff the traversal shall keep going after the
1753 /// completion of this function.
1754 static bool
1755 categorize_harmless_diff_node(diff *d, bool pre)
1756 {
1757  if (!d->has_changes())
1758  return true;
1759 
1760  if (pre)
1761  {
1762  diff_category category = NO_CHANGE_CATEGORY;
1763 
1764  decl_base_sptr f = is_decl(d->first_subject()),
1765  s = is_decl(d->second_subject());
1766 
1770 
1771  if (access_changed(f, s))
1772  category |= ACCESS_CHANGE_CATEGORY;
1773 
1774  if (is_compatible_change(f, s))
1775  category |= COMPATIBLE_TYPE_CHANGE_CATEGORY;
1776 
1777  if (has_harmless_name_change(f, s)
1778  || class_diff_has_harmless_odr_violation_change(d))
1780 
1782  category |= HARMLESS_UNION_CHANGE_CATEGORY;
1783 
1784  if (has_non_virtual_mem_fn_change(d))
1786 
1787  if (static_data_member_added_or_removed(d)
1788  || static_data_member_type_size_changed(f, s))
1790 
1793 
1794  if ((has_enumerator_insertion(d)
1795  && !has_harmful_enum_change(d))
1796  || has_harmless_enum_to_int_change(d))
1797  category |= HARMLESS_ENUM_CHANGE_CATEGORY;
1798 
1799  if (function_name_changed_but_not_symbol(d))
1801 
1802  if (has_fn_parm_type_top_cv_qual_change(d))
1804 
1805  if (has_fn_parm_type_cv_qual_change(d))
1806  category |= FN_PARM_TYPE_CV_CHANGE_CATEGORY;
1807 
1808  if (has_fn_return_type_cv_qual_change(d))
1810 
1811  if (has_var_type_cv_qual_change(d))
1812  category |= VAR_TYPE_CV_CHANGE_CATEGORY;
1813 
1814  if (has_void_ptr_to_ptr_change(d))
1815  category |= VOID_PTR_TO_PTR_CHANGE_CATEGORY;
1816 
1817  if (has_benign_array_of_unknown_size_change(d))
1819 
1820  if (category)
1821  {
1823  // Also update the category of the canonical node.
1824  if (diff * canonical = d->get_canonical_diff())
1825  canonical->add_to_local_and_inherited_categories(category);
1826  }
1827  }
1828 
1829  return true;
1830 }
1831 
1832 /// Detect if the changes carried by a given diff node are deemed
1833 /// harmful and do categorize the diff node accordingly.
1834 ///
1835 /// @param d the diff node being visited.
1836 ///
1837 /// @param pre this is true iff the node is being visited *before* the
1838 /// children nodes of @p d.
1839 ///
1840 /// @return true iff the traversal shall keep going after the
1841 /// completion of this function.
1842 static bool
1843 categorize_harmful_diff_node(diff *d, bool pre)
1844 {
1845  if (!d->has_changes())
1846  return true;
1847 
1848  if (pre)
1849  {
1850  diff_category category = NO_CHANGE_CATEGORY;
1851  decl_base_sptr f = is_decl(d->first_subject()),
1852  s = is_decl(d->second_subject());
1853 
1854  // Detect size or offset changes as well as data member addition
1855  // or removal.
1856  //
1857  // TODO: be more specific -- not all size changes are harmful.
1860  && (type_size_changed(f, s)
1861  || data_member_offset_changed(f, s)
1862  || non_static_data_member_type_size_changed(f, s)
1863  || non_static_data_member_added_or_removed(d)
1864  || base_classes_added_or_removed(d)
1865  || has_harmful_enum_change(d)
1866  || crc_changed(d)
1867  || namespace_changed(d)))
1868  category |= SIZE_OR_OFFSET_CHANGE_CATEGORY;
1869 
1870  if (has_virtual_mem_fn_change(d))
1871  category |= VIRTUAL_MEMBER_CHANGE_CATEGORY;
1872 
1873  if (has_added_or_removed_function_parameters(d))
1875 
1876  if (category)
1877  {
1878  d->add_to_local_and_inherited_categories(category);
1879  // Update the category of the canonical diff node too.
1880  if (diff * canonical = d->get_canonical_diff())
1881  canonical->add_to_local_and_inherited_categories(category);
1882  }
1883  }
1884 
1885  return true;
1886 }
1887 
1888 /// The visiting code of the harmless_harmful_filter.
1889 ///
1890 /// @param d the diff node being visited.
1891 ///
1892 /// @param pre this is true iff the node is being visited *before* the
1893 /// children nodes of @p d.
1894 ///
1895 /// @return true iff the traversal shall keep going after the
1896 /// completion of this function.
1897 bool
1898 harmless_harmful_filter::visit(diff* d, bool pre)
1899 {
1900  return (categorize_harmless_diff_node(d, pre)
1901  && categorize_harmful_diff_node(d, pre));
1902 }
1903 
1904 /// Part of the visiting code of the harmless_harmful_filter.
1905 ///
1906 /// This function is called after the visiting of a given diff node.
1907 /// Note that when this function is called, the visiting might not
1908 /// have taken place *if* the node (or an equivalent node) has already
1909 /// been visited.
1910 ///
1911 /// @param d the diff node that has either been visited or skipped
1912 /// (because it has already been visited during this traversing).
1913 void
1914 harmless_harmful_filter::visit_end(diff* d)
1915 {
1916  if (d->context()->diff_has_been_visited(d))
1917  {
1918  // This node or one of its equivalent node has already been
1919  // visited. That means at this moment,
1920  // harmless_harmful_filter::visit() has *not* been called prior
1921  // to this harmless_harmful_filter::visit_end() is called. In
1922  // other words, only harmless_harmful_filter::visit_begin() and
1923  // harmless_harmful_filter::visit_end() are called.
1924  //
1925  // So let's update the category of this diff node from its
1926  // canonical node.
1927  if (diff* c = d->get_canonical_diff())
1928  d->add_to_local_and_inherited_categories(c->get_local_category());
1929  }
1930 }
1931 } // end namespace filtering
1932 } // end namespace comparison
1933 } // end namespace abigail
const string_decl_base_sptr_map & inserted_data_members() const
Getter for the data members that got inserted.
bool is_type(const type_or_decl_base &tod)
Test whether a declaration is a type.
Definition: abg-ir.cc:10469
bool decl_names_equal(const string &l, const string &r)
Compare two fully qualified decl names by taking into account that they might have compontents that a...
bool get_member_function_is_virtual(const function_decl &f)
Test if a given member function is virtual.
Definition: abg-ir.cc:6723
This means the diff node (or at least one of its descendant nodes) carries a change involving two com...
const string_member_function_sptr_map & deleted_member_fns() const
This header declares filters for the diff trees resulting from comparing ABI Corpora.
const pointer_diff * is_pointer_diff(const diff *diff)
Test if a diff node is about differences between two pointers.
class_decl_sptr is_compatible_with_class_type(const type_base_sptr &t)
Test if a type is a class. This function looks through typedefs.
Definition: abg-ir.cc:10701
const function_decl_sptr first_function_decl() const
const type_decl * is_type_decl(const type_or_decl_base *t)
Test whether a type is a type_decl (a builtin type).
Definition: abg-ir.cc:10553
var_decl_sptr first_var() const
Getter for the first var_decl of the diff.
bool is_data_member(const var_decl &v)
Test if a var_decl is a data member.
Definition: abg-ir.cc:5763
An abstraction of a diff between entities that are of a different kind (disctinct).
const qualified_type_diff * is_qualified_type_diff(const diff *diff)
Test if a diff node is about differences between two qualified types.
bool is_member_function(const function_decl &f)
Test whether a function_decl is a member function.
Definition: abg-ir.cc:6450
A diff node in this category is a function (or function type) with at least one parameter added or re...
This means the diff node (or at least one of its descendant nodes) carries a change that modifies the...
This type abstracts changes for a class_decl.
access_specifier get_member_access_specifier(const decl_base &d)
Gets the access specifier for a class member.
Definition: abg-ir.cc:5665
type_or_decl_base_sptr first_subject() const
Getter of the first subject of the diff.
This means the diff node does not carry any (meaningful) change, or that it carries changes that have...
ssize_t get_member_function_vtable_offset(const function_decl &f)
Get the vtable offset of a member function.
Definition: abg-ir.cc:6660
const var_diff * is_var_diff(const diff *diff)
Test if a diff node is about differences between variables.
This means that a diff node in the sub-tree carries an addition of enumerator to an enum type...
bool is_mostly_distinct_diff(const diff *d)
Test if a diff node carries a distinct type change or a pointer/reference/typedef to distinct type ch...
const diff * peel_typedef_diff(const diff *dif)
If a diff node is about changes between two typedef types, get the diff node about changes between th...
This means the diff node (or at least one of its descendant nodes) carries access related changes...
A diff node in this category carries a change from void pointer to non-void pointer.
shared_ptr< var_decl > var_decl_sptr
Convenience typedef for a shared pointer on a var_decl.
Definition: abg-fwd.h:246
class_or_union_sptr first_class_or_union() const
bool is_member_decl(const decl_base_sptr d)
Tests if a declaration is a class member.
Definition: abg-ir.cc:5567
bool has_decl_only_def_change(const decl_base_sptr &first, const decl_base_sptr &second)
Test if two decl_base_sptr are different just by the fact that one is decl-only and the other one is ...
bool enum_has_non_name_change(const enum_type_decl &l, const enum_type_decl &r, change_kind *k)
Test if two enums differ, but not by a name change.
Definition: abg-ir.cc:19052
const fn_parm_diff * is_fn_parm_diff(const diff *diff)
Test if a diff node is about differences between two function parameters.
const type_decl_diff * is_diff_of_basic_type(const diff *d)
Test if a diff node represents a diff between two basic types.
Abstraction of a diff between two function_decl.
A diff node in this category is a function parameter type which top cv-qualifiers change...
This means that a diff node in the sub-tree carries an incompatible change to a vtable.
const enum_type_decl_sptr first_enum() const
Abstraction of a diff between two enums.
enum_type_decl_sptr is_compatible_with_enum_type(const type_base_sptr &t)
Test if a type is an enum. This function looks through typedefs.
Definition: abg-ir.cc:10650
The base class of diff between types.
Abstraction of a diff between two basic type declarations.
const function_decl_sptr second_function_decl() const
bool has_anonymous_data_member_change(const diff *d)
Test if a diff node carries a non-anonymous data member to anonymous data member change, or vice-versa.
var_decl * is_var_decl(const type_or_decl_base *tod)
Tests if a declaration is a variable declaration.
Definition: abg-ir.cc:11368
This is the base class of class_diff and union_diff.
const string_decl_base_sptr_map & deleted_data_members() const
Getter for the data members that got deleted.
typedef_decl_sptr is_typedef(const type_or_decl_base_sptr t)
Test whether a type is a typedef.
Definition: abg-ir.cc:10611
class_or_union_sptr second_class_or_union() const
uint64_t get_data_member_offset(const var_decl &m)
Get the offset of a data member.
Definition: abg-ir.cc:6266
bool get_is_declaration_only() const
Test if a decl_base is a declaration-only decl.
Definition: abg-ir.cc:5103
diff_category
An enum for the different categories that a diff tree node falls into, regarding the kind of changes ...
Toplevel namespace for libabigail.
const diff * peel_typedef_or_qualified_type_diff(const diff *dif)
If a diff node is about changes between two typedefs or qualified types, get the diff node about chan...
bool has_harmless_name_change(const decl_base_sptr &f, const decl_base_sptr &s)
Test if two decls represents a harmless name change.
bool has_basic_type_name_change(const diff *d)
Test if a diff node carries a basic type name change.
shared_ptr< function_decl > function_decl_sptr
Convenience typedef for a shared pointer on a function_decl.
Definition: abg-fwd.h:263
decl_base * is_decl(const type_or_decl_base *d)
Test if an ABI artifact is a declaration.
Definition: abg-ir.cc:10409
bool has_harmful_name_change(const decl_base_sptr &f, const decl_base_sptr &s)
Test if two decls represents a harmful name change.
const enum_type_decl * is_enum_type(const type_or_decl_base *d)
Test if a decl is an enum_type_decl.
Definition: abg-ir.cc:10683
virtual size_t get_size_in_bits() const
Getter for the size of the type.
Definition: abg-ir.cc:15569
shared_ptr< parameter > parameter_sptr
Convenience typedef for a shared pointer on a function_decl::parameter.
Definition: abg-ir.h:3067
var_decl_sptr second_var() const
Getter for the second var_decl of the diff.
Abstraction of a diff between two function parameters.
This means that a diff node in the sub-tree carries a harmless union change.
This means that a diff node in the sub-tree carries a harmless declaration name change. This is set only for name changes for data members and typedefs.
This means that a diff node in the sub-tree carries a harmless data member change. An example of harmless data member change is an anonymous data member that replaces a given data member without locally changing the layout.
bool has_class_decl_only_def_change(const class_or_union_sptr &first, const class_or_union_sptr &second)
Test if two class_or_union_sptr are different just by the fact that one is decl-only and the other on...
bool has_data_member_replaced_by_anon_dm(const diff *diff)
Test if a class_or_union_diff has a data member replaced by an anonymous data member in a harmless wa...
The base type of class_decl and union_decl.
Definition: abg-ir.h:3961
This means that a diff node in the sub-tree carries a type that was declaration-only and that is now ...
const type_base * peel_qualified_type(const type_base *type)
Return the leaf underlying type of a qualified type.
Definition: abg-ir.cc:7297
diff * get_canonical_diff() const
Getter for the canonical diff of the current instance of diff.
bool var_equals_modulo_types(const var_decl &l, const var_decl &r, change_kind *k)
Compares two instances of var_decl without taking their type into account.
Definition: abg-ir.cc:20091
shared_ptr< type_or_decl_base > type_or_decl_base_sptr
A convenience typedef for a shared_ptr to type_or_decl_base.
Definition: abg-fwd.h:119
void apply_filter(filter_base &filter, corpus_diff_sptr d)
Walk the diff sub-trees of a a corpus_diff and apply a filter to the nodes visted. The filter categorizes each node, assigning it into one or several categories.
const function_decl_diff * is_function_decl_diff(const diff *diff)
Test if a diff node is about differences between functions.
decl_base_sptr look_through_decl_only(const decl_base &d)
If a decl is decl-only get its definition. Otherwise, just return nil.
Definition: abg-ir.cc:11276
A diff node in this category is for a variable which type holds a cv-qualifier change.
shared_ptr< elf_symbol > elf_symbol_sptr
A convenience typedef for a shared pointer to elf_symbol.
Definition: abg-ir.h:872
#define ABG_ASSERT(cond)
This is a wrapper around the 'assert' glibc call. It allows for its argument to have side effects...
Definition: abg-fwd.h:1659
bool union_diff_has_harmless_changes(const diff *d)
Test if a union diff node does have changes that don't impact its size.
A diff node in this category has a function parameter type with a cv-qualifiers change.
type_base * peel_qualified_or_typedef_type(const type_base *type)
Return the leaf underlying type of a qualified or typedef type.
Definition: abg-ir.cc:7342
enum_type_decl_sptr look_through_decl_only_enum(const enum_type_decl &the_enum)
If an enum is a decl-only enum, get its definition. Otherwise, just return the initial enum...
Definition: abg-ir.cc:11257
const type_base_sptr get_type() const
Getter of the type of the variable.
Definition: abg-ir.cc:19960
const string_member_function_sptr_map & inserted_member_fns() const
diff_sptr type_diff() const
Getter for the diff representing the changes on the type of the function parameter involved in the cu...
array_type_def * is_array_type(const type_or_decl_base *type)
Test if a type is an array_type_def.
Definition: abg-ir.cc:11432
shared_ptr< enum_type_decl > enum_type_decl_sptr
Convenience typedef for shared pointer to a enum_type_decl.
Definition: abg-fwd.h:169
shared_ptr< class_decl > class_decl_sptr
Convenience typedef for a shared pointer on a class_decl.
Definition: abg-fwd.h:187
virtual void get_qualified_name(interned_string &qualified_name, bool internal=false) const
Compute the qualified name of the decl.
Definition: abg-ir.cc:4972
interned_string get_type_name(const type_base_sptr &t, bool qualified, bool internal)
Get the name of a given type and return a copy of it.
Definition: abg-ir.cc:8781
qualified_type_def * is_qualified_type(const type_or_decl_base *t)
Test whether a type is a reference_type_def.
Definition: abg-ir.cc:11147
static bool entities_are_of_distinct_kinds(type_or_decl_base_sptr first, type_or_decl_base_sptr second)
Test if the two arguments are of different kind, or that are both NULL.
const string_decl_base_sptr_map & data_members_replaced_by_adms() const
Get the map of data members that got replaced by anonymous data members.
Abstracts a diff between two instances of var_decl.
bool get_member_is_static(const decl_base &d)
Gets a flag saying if a class member is static or not.
Definition: abg-ir.cc:5725
const class_or_union_diff * is_class_or_union_diff(const diff *d)
Test if a diff node is a class_or_union_diff node.
This means that a diff node in the sub-tree carries an addition or removal of a non-virtual member fu...
const reference_diff * is_reference_diff(const diff *diff)
Test if a diff node is about differences between two references.
This means that a diff node in the sub-tree carries an a symbol alias change that is harmless...
const diff * peel_reference_diff(const diff *dif)
If a diff node is about changes between two reference types, get the diff node about changes between ...
type_base_sptr peel_typedef_pointer_or_reference_type(const type_base_sptr type)
Return the leaf underlying or pointed-to type node of a typedef_decl, pointer_type_def, reference_type_def, or array_type_def node.
Definition: abg-ir.cc:7393
const type_diff_base * is_type_diff(const diff *diff)
Test if a diff node is about differences between types.
shared_ptr< corpus_diff > corpus_diff_sptr
A convenience typedef for a shared pointer to corpus_diff.
bool types_are_compatible(const type_base_sptr type1, const type_base_sptr type2)
Test if two types are equal modulo a typedef.
Definition: abg-ir.cc:10136
class_or_union * look_through_decl_only_class(class_or_union *the_class)
If a class (or union) is a decl-only class, get its definition. Otherwise, just return the initial cl...
Definition: abg-ir.cc:11227
const data_members & get_data_members() const
Get the data members of this class_or_union.
Definition: abg-ir.cc:22694
const class_or_union_diff * is_diff_of_class_or_union_type(const diff *d)
Test if a diff node represents a diff between two class or union types.
bool is_anonymous_data_member(const decl_base &d)
Test if a decl is an anonymous data member.
Definition: abg-ir.cc:5954
access_specifier
Access specifier for class members.
Definition: abg-ir.h:864
void add_to_local_and_inherited_categories(diff_category c)
Adds the current diff tree node to the categories resulting from the local and inherited changes of t...
const function_type_diff * is_function_type_diff(const diff *diff)
Test if a diff node is a function_type_diff node.
const function_decl_diff_sptrs_type & changed_member_fns() const
Getter for the virtual members functions that have had a change in a sub-type, without having a chang...
virtual bool has_changes() const =0
Pure interface to get the length of the changes encapsulated by this diff. A length of zero means tha...
A diff node in this category carries a change in the size of the array type of a global variable...
bool has_class_or_union_type_name_change(const diff *d)
Test if a diff node carries a class or union type name change.
bool is_var_1_dim_unknown_size_array_change(const var_decl_sptr &var1, const var_decl_sptr &var2)
Test if we are looking at two variables which types are both one dimension array, with one of them be...
The base class for the diff tree node filter.
type_or_decl_base_sptr second_subject() const
Getter of the second subject of the diff.
bool has_basic_or_class_type_name_change(const diff *d)
Test if a diff node carries a basic or class type name change.
const union_diff * is_union_diff(const diff *diff)
Test if a diff node is a union_diff node.
pointer_type_def * is_pointer_type(type_or_decl_base *t)
Test whether a type is a pointer_type_def.
Definition: abg-ir.cc:10962
A diff node in this category is a function return type with a cv-qualifier change.
shared_ptr< diff > diff_sptr
Convenience typedef for a shared_ptr for the diff class.
Definition: abg-fwd.h:76
bool has_enum_decl_only_def_change(const enum_type_decl_sptr &first, const enum_type_decl_sptr &second)
Test if two enum_sptr are different just by the fact that one is decl-only and the other one is defin...
const distinct_diff * is_distinct_diff(const diff *diff)
Test if a diff node is about differences between two diff nodes of different kinds.
const diff * peel_pointer_diff(const diff *dif)
If a diff node is about changes between two pointer types, get the diff node about changes between th...
shared_ptr< array_type_def > array_type_def_sptr
Convenience typedef for a shared pointer on a array_type_def.
Definition: abg-fwd.h:234
bool equals_modulo_cv_qualifier(const array_type_def *l, const array_type_def *r)
Test if two variables are equals modulo CV qualifiers.
Definition: abg-ir.cc:18640
bool is_decl_only_class_with_size_change(const class_or_union &first, const class_or_union &second)
Test if two classes that are decl-only (have the decl-only flag and carry no data members) but are di...
shared_ptr< filter_base > filter_base_sptr
Convenience typedef for a shared pointer to filter_base.
const type_base * is_void_pointer_type_equivalent(const type_base *type)
Test if a type is equivalent to a pointer to void type.
Definition: abg-ir.cc:11059
const enum_type_decl_sptr second_enum() const
CV
Bit field values representing the cv qualifiers of the underlying type.
Definition: abg-ir.h:2231
This means that a diff node in the sub-tree carries an addition or removal of a static data member...