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 
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
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
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 
891  function_decl_sptr ff = diff->first_function_decl(),
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 
1670  if (is_void_pointer_type(f)
1671  && is_pointer_type(s)
1672  && !is_void_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 
1684  if (is_void_pointer_type(f)
1685  && is_pointer_type(s)
1686  && !is_void_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 
1698  if (is_void_pointer_type(f)
1699  && is_pointer_type(s)
1700  && !is_void_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
This header declares filters for the diff trees resulting from comparing ABI Corpora.
#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:1589
This type abstracts changes for a class_decl.
This is the base class of class_diff and union_diff.
class_or_union_sptr first_class_or_union() const
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.
class_or_union_sptr second_class_or_union() const
The abstraction of a change between two ABI artifacts, a.k.a an artifact change.
type_or_decl_base_sptr second_subject() const
Getter of the second subject of the diff.
type_or_decl_base_sptr first_subject() const
Getter of the first subject of the diff.
diff * get_canonical_diff() const
Getter for the canonical diff of the current instance of diff.
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...
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...
An abstraction of a diff between entities that are of a different kind (disctinct).
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.
Abstraction of a diff between two enums.
const enum_type_decl_sptr first_enum() const
const enum_type_decl_sptr second_enum() const
Abstraction of a diff between two function parameters.
diff_sptr type_diff() const
Getter for the diff representing the changes on the type of the function parameter involved in the cu...
Abstraction of a diff between two function_decl.
Abstraction of a diff between two basic type declarations.
The base class of diff between types.
Abstracts a diff between two instances of var_decl.
var_decl_sptr first_var() const
Getter for the first var_decl of the diff.
var_decl_sptr second_var() const
Getter for the second var_decl of the diff.
The base type of class_decl and union_decl.
Definition: abg-ir.h:3939
const data_members & get_data_members() const
Get the data members of this class_or_union.
Definition: abg-ir.cc:22027
virtual void get_qualified_name(interned_string &qualified_name, bool internal=false) const
Compute the qualified name of the decl.
Definition: abg-ir.cc:4896
bool get_is_declaration_only() const
Test if a decl_base is a declaration-only decl.
Definition: abg-ir.cc:5027
shared_ptr< parameter > parameter_sptr
Convenience typedef for a shared pointer on a function_decl::parameter.
Definition: abg-ir.h:3044
CV
Bit field values representing the cv qualifiers of the underlying type.
Definition: abg-ir.h:2223
virtual size_t get_size_in_bits() const
Getter for the size of the type.
Definition: abg-ir.cc:15066
bool has_basic_type_name_change(const diff *d)
Test if a diff node carries a basic type name change.
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...
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...
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 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...
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.
bool has_harmful_name_change(const decl_base_sptr &f, const decl_base_sptr &s)
Test if two decls represents a harmful name change.
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 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 has_basic_or_class_type_name_change(const diff *d)
Test if a diff node carries a basic or class type name change.
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,...
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....
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...
bool union_diff_has_harmless_changes(const diff *d)
Test if a union diff node does have changes that don't impact its size.
shared_ptr< diff > diff_sptr
Convenience typedef for a shared_ptr for the diff class.
Definition: abg-fwd.h:76
diff_category
An enum for the different categories that a diff tree node falls into, regarding the kind of changes ...
@ ACCESS_CHANGE_CATEGORY
This means the diff node (or at least one of its descendant nodes) carries access related changes,...
@ HARMLESS_DATA_MEMBER_CHANGE_CATEGORY
This means that a diff node in the sub-tree carries a harmless data member change....
@ VIRTUAL_MEMBER_CHANGE_CATEGORY
This means that a diff node in the sub-tree carries an incompatible change to a vtable.
@ SIZE_OR_OFFSET_CHANGE_CATEGORY
This means the diff node (or at least one of its descendant nodes) carries a change that modifies the...
@ NON_VIRT_MEM_FUN_CHANGE_CATEGORY
This means that a diff node in the sub-tree carries an addition or removal of a non-virtual member fu...
@ HARMLESS_ENUM_CHANGE_CATEGORY
This means that a diff node in the sub-tree carries an addition of enumerator to an enum type.
@ FN_PARM_ADD_REMOVE_CHANGE_CATEGORY
A diff node in this category is a function (or function type) with at least one parameter added or re...
@ VOID_PTR_TO_PTR_CHANGE_CATEGORY
A diff node in this category carries a change from void pointer to non-void pointer.
@ COMPATIBLE_TYPE_CHANGE_CATEGORY
This means the diff node (or at least one of its descendant nodes) carries a change involving two com...
@ TYPE_DECL_ONLY_DEF_CHANGE_CATEGORY
This means that a diff node in the sub-tree carries a type that was declaration-only and that is now ...
@ STATIC_DATA_MEMBER_CHANGE_CATEGORY
This means that a diff node in the sub-tree carries an addition or removal of a static data member.
@ HARMLESS_DECL_NAME_CHANGE_CATEGORY
This means that a diff node in the sub-tree carries a harmless declaration name change....
@ NO_CHANGE_CATEGORY
This means the diff node does not carry any (meaningful) change, or that it carries changes that have...
@ HARMLESS_UNION_CHANGE_CATEGORY
This means that a diff node in the sub-tree carries a harmless union change.
@ BENIGN_INFINITE_ARRAY_CHANGE_CATEGORY
A diff node in this category carries a change in the size of the array type of a global variable,...
@ VAR_TYPE_CV_CHANGE_CATEGORY
A diff node in this category is for a variable which type holds a cv-qualifier change.
@ FN_PARM_TYPE_CV_CHANGE_CATEGORY
A diff node in this category has a function parameter type with a cv-qualifiers change.
@ FN_PARM_TYPE_TOP_CV_CHANGE_CATEGORY
A diff node in this category is a function parameter type which top cv-qualifiers change.
@ FN_RETURN_TYPE_CV_CHANGE_CATEGORY
A diff node in this category is a function return type with a cv-qualifier change.
@ HARMLESS_SYMBOL_ALIAS_CHANGE_CATEGORY
This means that a diff node in the sub-tree carries an a symbol alias change that is harmless.
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.
const pointer_diff * is_pointer_diff(const diff *diff)
Test if a diff node is about differences between two pointers.
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...
const function_decl_diff * is_function_decl_diff(const diff *diff)
Test if a diff node is about differences between functions.
const function_type_diff * is_function_type_diff(const diff *diff)
Test if a diff node is a function_type_diff node.
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_reference_diff(const diff *dif)
If a diff node is about changes between two reference types, get the diff node about changes between ...
const fn_parm_diff * is_fn_parm_diff(const diff *diff)
Test if a diff node is about differences between two function parameters.
const union_diff * is_union_diff(const diff *diff)
Test if a diff node is a union_diff node.
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...
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.
const type_decl_diff * is_diff_of_basic_type(const diff *d)
Test if a diff node represents a diff between two basic types.
const qualified_type_diff * is_qualified_type_diff(const diff *diff)
Test if a diff node is about differences between two qualified types.
const var_diff * is_var_diff(const diff *diff)
Test if a diff node is about differences between variables.
const type_diff_base * is_type_diff(const diff *diff)
Test if a diff node is about differences between types.
const reference_diff * is_reference_diff(const diff *diff)
Test if a diff node is about differences between two references.
shared_ptr< corpus_diff > corpus_diff_sptr
A convenience typedef for a shared pointer to corpus_diff.
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 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:18438
const type_base * peel_qualified_type(const type_base *type)
Return the leaf underlying type of a qualified type.
Definition: abg-ir.cc:7195
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:5648
shared_ptr< function_decl > function_decl_sptr
Convenience typedef for a shared pointer on a function_decl.
Definition: abg-fwd.h:263
access_specifier
Access specifier for class members.
Definition: abg-ir.h:856
ssize_t get_member_function_vtable_offset(const function_decl &f)
Get the vtable offset of a member function.
Definition: abg-ir.cc:6558
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:18058
bool is_type(const type_or_decl_base &tod)
Test whether a declaration is a type.
Definition: abg-ir.cc:10194
bool is_anonymous_data_member(const decl_base &d)
Test if a decl is an anonymous data member.
Definition: abg-ir.cc:5877
shared_ptr< elf_symbol > elf_symbol_sptr
A convenience typedef for a shared pointer to elf_symbol.
Definition: abg-ir.h:863
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:10417
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
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:10954
shared_ptr< class_decl > class_decl_sptr
Convenience typedef for a shared pointer on a class_decl.
Definition: abg-fwd.h:187
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,...
Definition: abg-ir.cc:7291
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:10278
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:10798
typedef_decl_sptr is_typedef(const type_or_decl_base_sptr t)
Test whether a type is a typedef.
Definition: abg-ir.cc:10336
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:19442
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:10399
shared_ptr< var_decl > var_decl_sptr
Convenience typedef for a shared pointer on a var_decl.
Definition: abg-fwd.h:246
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
pointer_type_def * is_pointer_type(type_or_decl_base *t)
Test whether a type is a pointer_type_def.
Definition: abg-ir.cc:10558
bool is_member_function(const function_decl &f)
Test whether a function_decl is a member function.
Definition: abg-ir.cc:6348
var_decl * is_var_decl(const type_or_decl_base *tod)
Tests if a declaration is a variable declaration.
Definition: abg-ir.cc:10890
decl_base * is_decl(const type_or_decl_base *d)
Test if an ABI artifact is a declaration.
Definition: abg-ir.cc:10134
access_specifier get_member_access_specifier(const decl_base &d)
Gets the access specifier for a class member.
Definition: abg-ir.cc:5588
shared_ptr< enum_type_decl > enum_type_decl_sptr
Convenience typedef for shared pointer to a enum_type_decl.
Definition: abg-fwd.h:169
uint64_t get_data_member_offset(const var_decl &m)
Get the offset of a data member.
Definition: abg-ir.cc:6164
bool get_member_function_is_virtual(const function_decl &f)
Test if a given member function is virtual.
Definition: abg-ir.cc:6621
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:10749
bool is_data_member(const var_decl &v)
Test if a var_decl is a data member.
Definition: abg-ir.cc:5686
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:9861
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:8678
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:10669
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:10779
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:10366
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:7240
bool is_member_decl(const decl_base_sptr d)
Tests if a declaration is a class member.
Definition: abg-ir.cc:5490
const type_base * is_void_pointer_type(const type_base *type)
Test if a type is a pointer to void type.
Definition: abg-ir.cc:10632
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...
Toplevel namespace for libabigail.
The base class for the diff tree node filter.