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-2025 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 #include "abg-ir-priv.h"
21 #include "abg-sptr-utils.h"
22 
24 // </headers defining libabigail's API>
25 
26 namespace abigail
27 {
28 namespace comparison
29 {
30 namespace filtering
31 {
32 
33 static bool
34 has_offset_changes(const string_decl_base_sptr_map& f_data_members,
35  const string_decl_base_sptr_map& s_data_members);
36 
37 static bool
38 type_diff_has_typedef_cv_qual_change_only(const diff *type_dif);
39 
40 static bool
41 type_diff_has_typedef_cv_qual_change_only(const type_base_sptr& f,
42  const type_base_sptr& s);
43 
44 static diff_category
45 has_harmful_change(const diff* d);
46 
47 static bool
48 has_harmful_enum_change(const diff* diff);
49 
50 static bool
51 has_harmless_enum_change(const type_base_sptr& f,
52  const type_base_sptr& s,
53  const diff_context_sptr& ctxt);
54 
55 static bool
56 type_size_changed_with_impact(const type_base* f,
57  const type_base* s);
58 
59 static bool
60 type_size_changed_with_impact(const decl_base* f,
61  const decl_base* s);
62 
63 using std::dynamic_pointer_cast;
64 
65 /// Walk the diff sub-trees of a a @ref corpus_diff and apply a filter
66 /// to the nodes visted. The filter categorizes each node, assigning
67 /// it into one or several categories.
68 ///
69 /// @param filter the filter to apply to the diff nodes
70 ///
71 /// @param d the corpus diff to apply the filter to.
72 void
74 {
75  bool s = d->context()->visiting_a_node_twice_is_forbidden();
76  d->context()->forbid_visiting_a_node_twice(true);
77  d->traverse(filter);
78  d->context()->forbid_visiting_a_node_twice(s);
79 }
80 
81 /// Walk a diff sub-tree and apply a filter to the nodes visted. The
82 /// filter categorizes each node, assigning it into one or several
83 /// categories.
84 ///
85 /// Note that this function makes sure to avoid visiting a node (or
86 /// any other node equivalent to it) more than once. This helps avoid
87 /// infinite loops for diff trees that involve type changes that
88 /// reference themselves.
89 ///
90 /// @param filter the filter to apply to the nodes of the sub-tree.
91 ///
92 /// @param d the diff sub-tree to walk and apply the filter to.
93 void
95 {
96  bool s = d->context()->visiting_a_node_twice_is_forbidden();
97  d->context()->forbid_visiting_a_node_twice(true);
98  d->context()->forget_visited_diffs();
99  d->traverse(filter);
100  d->context()->forbid_visiting_a_node_twice(s);
101 }
102 
103 /// Walk a diff sub-tree and apply a filter to the nodes visted. The
104 /// filter categorizes each node, assigning it into one or several
105 /// categories.
106 ///
107 /// Note that this function makes sure to avoid visiting a node (or
108 /// any other node equivalent to it) more than once. This helps avoid
109 /// infinite loops for diff trees that involve type changes that
110 /// reference themselves.
111 ///
112 /// @param filter the filter to apply to the nodes of the sub-tree.
113 ///
114 /// @param d the diff sub-tree to walk and apply the filter to.
115 void
117 {apply_filter(*filter, d);}
118 
119 /// Test if there is a class that is declaration-only among the two
120 /// classes in parameter.
121 ///
122 /// @param class1 the first class to consider.
123 ///
124 /// @param class2 the second class to consider.
125 ///
126 /// @return true if either classes are declaration-only, false
127 /// otherwise.
128 static bool
129 there_is_a_decl_only_class(const class_decl* class1, const class_decl* class2)
130 {
131  if ((class1 && class1->get_is_declaration_only())
132  || (class2 && class2->get_is_declaration_only()))
133  return true;
134  return false;
135 }
136 
137 /// Test if there is a class that is declaration-only among the two
138 /// classes in parameter.
139 ///
140 /// @param class1 the first class to consider.
141 ///
142 /// @param class2 the second class to consider.
143 ///
144 /// @return true if either classes are declaration-only, false
145 /// otherwise.
146 static bool
147 there_is_a_decl_only_class(const class_decl_sptr& class1,
148  const class_decl_sptr& class2)
149 {return there_is_a_decl_only_class(class1.get(), class2.get());}
150 
151 /// Test if there is a enum that is declaration-only among the two
152 /// enums in parameter.
153 ///
154 /// @param enum1 the first enum to consider.
155 ///
156 /// @param enum2 the second enum to consider.
157 ///
158 /// @return true if either enums are declaration-only, false
159 /// otherwise.
160 static bool
161 there_is_a_decl_only_enum(const enum_type_decl* enum1,
162  const enum_type_decl* enum2)
163 {
164  if ((enum1 && enum1->get_is_declaration_only())
165  || (enum2 && enum2->get_is_declaration_only()))
166  return true;
167  return false;
168 }
169 
170 /// Test if the diff involves a declaration-only class.
171 ///
172 /// @param diff the class diff to consider.
173 ///
174 /// @return true iff the diff involves a declaration-only class.
175 static bool
176 diff_involves_decl_only_class(const class_diff* diff)
177 {
178  if (diff && there_is_a_decl_only_class(diff->first_class_decl(),
179  diff->second_class_decl()))
180  return true;
181  return false;
182 }
183 
184 /// Tests if the size of a given type changed.
185 ///
186 /// @param f the first version of the type to consider.
187 ///
188 /// @param s the second version of the type to consider.
189 ///
190 /// @return true if the type size changed, false otherwise.
191 static bool
192 type_size_changed(const type_base* f, const type_base* s)
193 {
194  if (!f || !s
195  || f->get_size_in_bits() == 0
196  || s->get_size_in_bits() == 0
197  || there_is_a_decl_only_class(is_compatible_with_class_type(f),
199  || there_is_a_decl_only_enum(is_compatible_with_enum_type(f),
201  return false;
202 
203  return f->get_size_in_bits() != s->get_size_in_bits();
204 }
205 
206 /// Tests if the size of a given type changed.
207 ///
208 /// @param f the first version of the type to consider.
209 ///
210 /// @param s the second version of the type to consider.
211 ///
212 /// @return true if the type size changed, false otherwise.
213 static bool
214 type_size_changed(const type_base_sptr f, const type_base_sptr s)
215 {return type_size_changed(f.get(), s.get());}
216 
217 /// Detect if a type has offset changes.
218 ///
219 /// The type must be either a class or a union. This function returns
220 /// true iff the type has a data member which has an offset change.
221 ///
222 /// @param f the first version of the type to consider.
223 ///
224 /// @param s the second version of the type to consider.
225 ///
226 /// @return true iff the type has a data member which has an offset
227 /// change.
228 static bool
229 type_has_offset_changes(const type_base_sptr f, const type_base_sptr s)
230 {
231  if (!f || !s)
232  return false;
233 
234  class_or_union_sptr first = is_class_or_union_type(f);
235  class_or_union_sptr second = is_class_or_union_type(s);
236  if (!first || !second)
237  return false;
238 
239  // collect the data members
240  string_decl_base_sptr_map f_data_members, s_data_members;
241  collect_non_anonymous_data_members(first, f_data_members);
242  collect_non_anonymous_data_members(second, s_data_members);
243 
244  // detect offset changes
245  if (has_offset_changes(f_data_members, s_data_members))
246  return true;
247 
248  return false;
249 }
250 
251 /// Detect if a type has offset changes.
252 ///
253 /// The type must be either a class or a union. This function returns
254 /// true iff the type has a data member which has an offset change.
255 ///
256 /// @param f the first version of the type to consider.
257 ///
258 /// @param s the second version of the type to consider.
259 ///
260 /// @return true iff the type has a data member which has an offset
261 /// change.
262 static bool
263 type_has_offset_changes(const type_base* f, const type_base* s)
264 {
265  type_base_sptr first(const_cast<type_base*>(f), sptr_utils::noop_deleter());
266  type_base_sptr second(const_cast<type_base*>(s), sptr_utils::noop_deleter());
267 
268  return type_has_offset_changes(first, second);
269 }
270 
271 /// Detect if a type has offset changes.
272 ///
273 /// The type must be either a class or a union. This function returns
274 /// true iff the type has a data member which has an offset change.
275 ///
276 /// @param f the first version of the type to consider.
277 ///
278 /// @param s the second version of the type to consider.
279 ///
280 /// @return true iff the type has a data member which has an offset
281 /// change.
282 static bool
283 type_has_offset_changes(const decl_base_sptr f, const decl_base_sptr s)
284 {return type_has_offset_changes(is_type(f), is_type(s));}
285 
286 /// Test if a given type diff node carries a type size change.
287 ///
288 /// @param diff the diff tree node to test.
289 ///
290 /// @return true if @p diff carries a type size change.
291 static bool
292 has_type_size_change(const diff* diff)
293 {
294  if (!diff)
295  return false;
296 
297  if (const fn_parm_diff* fn_parm_d = is_fn_parm_diff(diff))
298  diff = fn_parm_d->type_diff().get();
299 
300  type_base_sptr f = is_type(diff->first_subject()),
301  s = is_type(diff->second_subject());
302 
303  if (!f || !s)
304  return false;
305 
306  return type_size_changed(f, s);
307 }
308 
309 /// Tests if the size of a given type changed and if its containing
310 /// type (if any) has a size change too, possibly as a consequence.
311 ///
312 /// Please note that the function also tests if the cause of the size
313 /// change does have an impact on the type itself in terms of data
314 /// member offset change.
315 ///
316 /// @param f the first version of the type to consider.
317 ///
318 /// @param s the second version of the type to consider.
319 ///
320 /// @param fs the scope of @p f.
321 ///
322 /// @param ss the scope of @p s.
323 ///
324 /// @return true if the type size changed and if that change did
325 /// impact the containing scope, false otherwise.
326 static bool
327 type_size_changed_with_impact(const type_base* f, const type_base *s,
328  const scope_decl* fs, const scope_decl* ss)
329 {
330  bool result = false;
331  if (type_size_changed(f, s))
332  {// Let's see if the type size has an impact on its scope.
333  if (is_type(ss))
334  // The scope is itself a type. Let's see if that type scope
335  // itself has a type size with an impact to its scope.
336  result = type_size_changed_with_impact (is_type(fs), is_type(ss));
337  else
338  {// The scope is not a type. So let's look at things in a
339  // more subtle way.
340  if (type_has_offset_changes(f, s))
341  // The type has an offset change so it looks like the size
342  // change is caused by something that did have an impact
343  // (in terms of ABI) on the type anyway.
344  result = true;
345 
346  if (// If the type itself is anonymous (and not named by a
347  // typedef), its size impact is going to be seen on the
348  // declaration of that type. And that would be tested
349  // separately anyway, for instance by
350  // has_harmful_change. So let's not consider that case
351  // here.
353  && !is_anonymous_type(s)
354  && type_size_changed(f, s))
355  result = true;
356  }
357  }
358  return result;
359 }
360 
361 /// Tests if the size of a given type changed and if its containing
362 /// type (if any) has a size change too, possibly as a consequence.
363 ///
364 /// Please note that the function also tests if the cause of the size
365 /// change does have an impact on the type itself in terms of data
366 /// member offset change.
367 ///
368 /// @param f the first version of the type to consider.
369 ///
370 /// @param s the second version of the type to consider.
371 ///
372 /// @return true if the type size changed and if that change did
373 /// impact the containing scope, false otherwise.
374 static bool
375 type_size_changed_with_impact(const type_base* f, const type_base *s)
376 {
377  const decl_base* first_type = get_type_declaration(f),
378  *second_type = get_type_declaration(s);
379 
380  if (!first_type || !second_type)
381  return false;
382 
383  scope_decl* fs = first_type->get_scope();
384  scope_decl* ss = second_type->get_scope();
385 
386  return type_size_changed_with_impact(f, s, fs, ss);
387 }
388 
389 /// Tests if the size of the type of a given decl changed and if its
390 /// containing type (if any) has a size change too, possibly as a
391 /// consequence.
392 ///
393 /// Please note that the function also tests if the cause of the size
394 /// change does have an impact on the type itself in terms of data
395 /// member offset change.
396 ///
397 /// Also, if we are looking at a type then test for the type directly.
398 ///
399 /// @param f the first declaration to consider.
400 ///
401 /// @param s the second declaration to consider.
402 ///
403 /// @return true if the type size changed and if that change did
404 /// impact the containing scope, false otherwise.
405 static bool
406 type_size_changed_with_impact(const decl_base* f, const decl_base *s)
407 {
408  if (!f || !s)
409  return false;
410 
411  if (is_type(f) && is_type(s))
412  return type_size_changed_with_impact(is_type(f), is_type(s));
413 
414  var_decl* f_var = is_var_decl(f);
415  var_decl* s_var = is_var_decl(s);
416 
417  if (!f_var || !s_var)
418  return false;
419 
420  scope_decl* fs = f->get_scope();
421  scope_decl* ss = s->get_scope();
422 
423  const type_base* first_type = f_var->get_type().get();
424  const type_base* second_type = s_var->get_type().get();
425 
426  return type_size_changed_with_impact(first_type, second_type, fs, ss);
427 }
428 
429 /// Tests if the size of a given type changed and if its containing
430 /// type (if any) has a size change too, possibly as a consequence.
431 ///
432 /// Please note that the function also tests if the cause of the size
433 /// change does have an impact on the type itself in terms of data
434 /// member offset change.
435 ///
436 /// @param f the first version of the type to consider.
437 ///
438 /// @param s the second version of the type to consider.
439 ///
440 /// @return true if the type size changed and if that change did
441 /// impact the containing scope, false otherwise.
442 static bool
443 type_size_changed_with_impact(const type_base_sptr& f, const type_base_sptr& s)
444 {return type_size_changed_with_impact(f.get(), s.get());}
445 
446 /// Tests if the size of a given type changed and if its containing
447 /// type (if any) has a size change too, possibly as a consequence.
448 ///
449 /// Please note that the function also tests if the cause of the size
450 /// change does have an impact on the type itself in terms of data
451 /// member offset change.
452 ///
453 /// @param f the declaration of the first version of the type to
454 /// consider.
455 ///
456 /// @param s the declaration of the second version of the type to
457 /// consider.
458 ///
459 /// @return true if the type size changed and if that change did
460 /// impact the containing scope, false otherwise.
461 static bool
462 type_size_changed_with_impact(const decl_base_sptr& f, const decl_base_sptr& s)
463 {return type_size_changed_with_impact(f.get(), s.get());}
464 
465 /// Tests if the diff node carries a type change in which the size
466 /// changed and the containing type (if any) has a size change too,
467 /// possibly as a consequence.
468 ///
469 /// Please note that the function also tests if the cause of the size
470 /// change does have an impact on the type itself in terms of data
471 /// member offset change.
472 ///
473 /// @param d the diff node to consider.
474 ///
475 /// @return true iff the diff node carries a type change in which the
476 /// size changed and containing type (if any) has a size change too,
477 /// possibly as a consequence.
478 static bool
479 has_type_size_change_with_impact(const diff* d)
480 {
481  if (!d)
482  return false;
483 
484  if (const fn_parm_diff* fn_parm_d = is_fn_parm_diff(d))
485  d = fn_parm_d->type_diff().get();
486 
487  if (is_type(d->first_subject()) || is_type(d->second_subject()))
488  return type_size_changed_with_impact(is_type(d->first_subject()),
489  is_type(d->second_subject()));
490 
491  return type_size_changed_with_impact(is_decl(d->first_subject()),
492  is_decl(d->second_subject()));
493 }
494 
495 /// Find a data member that is at a given offset.
496 ///
497 /// @param data_members the set of data member to consider.
498 ///
499 /// @param the offset to consider.
500 ///
501 /// @return the data member found at offset @p offset of nil if none
502 /// was found with that offset;
503 static var_decl_sptr
504 find_data_member_at_offset(const string_decl_base_sptr_map& data_members,
505  unsigned offset)
506 {
507  for (auto e : data_members)
508  {
509  var_decl_sptr dm = is_var_decl(e.second);
510  ABG_ASSERT(dm);
511  unsigned off = get_absolute_data_member_offset(dm);
512  if (offset == off)
513  return dm;
514  }
515  return var_decl_sptr();
516 }
517 
518 /// Test if a set of data members contains at least one data member
519 /// that has an offset change.
520 ///
521 /// @param f_data_members the first version of data members to
522 /// consider.
523 ///
524 /// @param s_data_members the second version of data members to
525 /// consider.
526 ///
527 /// @return true iff there is at least one data member which has an
528 /// offset change between the first version of data members and the
529 /// second version.
530 static bool
531 has_offset_changes(const string_decl_base_sptr_map& f_data_members,
532  const string_decl_base_sptr_map& s_data_members)
533 {
534  // Compare the offsets of the data members
535  for (auto entry : f_data_members)
536  {
537  var_decl_sptr f_member = is_var_decl(entry.second);
538  ABG_ASSERT(f_member);
539  unsigned f_offset = get_absolute_data_member_offset(f_member);
540  auto i = s_data_members.find(entry.first);
541  var_decl_sptr s_member;
542  if (i == s_data_members.end())
543  {
544  s_member = find_data_member_at_offset(s_data_members, f_offset);
545  if (!s_member)
546  // A data member was suppressed; that's bad; let's consider
547  // that as an offset change.
548  return true;
549  }
550 
551  if (!s_member)
552  s_member = is_var_decl(i->second);
553  ABG_ASSERT(s_member);
554  unsigned s_offset = get_absolute_data_member_offset(s_member);
555  if (f_offset != s_offset)
556  return true;
557  }
558  return false;
559 }
560 
561 /// Test if the local changes of a @ref class_diff are harmless.
562 ///
563 /// Harmful changes are basically:
564 /// 1/ name change (that changes the type altogether)
565 /// 2/ size change
566 /// 3/ offset change of any data member
567 ///
568 ///
569 /// Thus, this function tests that the class_diff carries none of the
570 /// 3 kinds of changes above.
571 ///
572 /// @param d the @ref class_diff to consider.
573 ///
574 /// @return true iff @p d has only harmless changes.
575 static bool
576 class_diff_has_only_harmless_changes(const class_diff* d)
577 {
578  if (!d || !d->has_changes())
579  return true;
580 
581  class_decl_sptr f = d->first_class_decl(), s = d->second_class_decl();
582 
583  if (f->get_qualified_name() != s->get_qualified_name())
584  return false;
585 
586  if (f->get_size_in_bits() != s->get_size_in_bits())
587  return false;
588 
589  // collect the data members
590  string_decl_base_sptr_map f_data_members, s_data_members;
591  collect_non_anonymous_data_members(f, f_data_members);
592  collect_non_anonymous_data_members(s, s_data_members);
593 
594  // detect offset changes
595  if (has_offset_changes(f_data_members, s_data_members))
596  return false;
597 
598  return true;
599 }
600 
601 /// Test if the local changes of a @ref class_diff are harmless.
602 ///
603 /// Harmful changes are basically:
604 /// 1/ name change (that changes the type altogether)
605 /// 2/ size change
606 /// 3/ offset change of any data member
607 ///
608 /// Thus, this function tests that the class_diff carries none of the
609 /// 3 kinds of changes above.
610 ///
611 /// @param d the @ref class_diff to consider.
612 ///
613 /// @return true iff @p d has only harmless changes.
614 static bool
615 class_diff_has_only_harmless_changes(diff* d)
616 {
617  if (const class_diff* class_dif = is_class_diff(d))
618  return class_diff_has_only_harmless_changes(class_dif);
619  return false;
620 }
621 
622 /// Tests if the access specifiers for a member declaration changed.
623 ///
624 /// @param f the declaration for the first version of the member
625 /// declaration to consider.
626 ///
627 /// @param s the declaration for the second version of the member
628 /// delcaration to consider.
629 ///
630 /// @return true iff the access specifier changed.
631 static bool
632 access_changed(const decl_base_sptr& f, const decl_base_sptr& s)
633 {
634  if (!is_member_decl(f)
635  || !is_member_decl(s))
636  return false;
637 
640 
641  if (sa != fa)
642  return true;
643 
644  return false;
645 }
646 
647 /// Test if there was a function or variable CRC change.
648 ///
649 /// @param f the first function or variable to consider.
650 ///
651 /// @param s the second function or variable to consider.
652 ///
653 /// @return true if the test is positive, false otherwise.
654 template <typename function_or_var_decl_sptr>
655 static bool
656 crc_changed(const function_or_var_decl_sptr& f,
657  const function_or_var_decl_sptr& s)
658 {
659  const auto& symbol_f = f->get_symbol();
660  const auto& symbol_s = s->get_symbol();
661  if (!symbol_f || !symbol_s)
662  return false;
663  return symbol_f->get_crc() != symbol_s->get_crc();
664 }
665 
666 /// Test if the current diff tree node carries a CRC change in either a
667 /// function or a variable.
668 ///
669 /// @param diff the diff tree node to consider.
670 ///
671 /// @return true if the test is positive, false otherwise.
672 static bool
673 crc_changed(const diff* diff)
674 {
675  if (const function_decl_diff* d =
676  dynamic_cast<const function_decl_diff*>(diff))
677  return crc_changed(d->first_function_decl(), d->second_function_decl());
678  if (const var_diff* d = dynamic_cast<const var_diff*>(diff))
679  return crc_changed(d->first_var(), d->second_var());
680  return false;
681 }
682 
683 /// Test if there was a function or variable namespace change.
684 ///
685 /// @param f the first function or variable to consider.
686 ///
687 /// @param s the second function or variable to consider.
688 ///
689 /// @return true if the test is positive, false otherwise.
690 template <typename function_or_var_decl_sptr>
691 static bool
692 namespace_changed(const function_or_var_decl_sptr& f,
693  const function_or_var_decl_sptr& s)
694 {
695  const auto& symbol_f = f->get_symbol();
696  const auto& symbol_s = s->get_symbol();
697  if (!symbol_f || !symbol_s)
698  return false;
699  return symbol_f->get_namespace() != symbol_s->get_namespace();
700 }
701 
702 /// Test if the current diff tree node carries a namespace change in
703 /// either a function or a variable.
704 ///
705 /// @param diff the diff tree node to consider.
706 ///
707 /// @return true if the test is positive, false otherwise.
708 static bool
709 namespace_changed(const diff* diff)
710 {
711  if (const function_decl_diff* d =
712  dynamic_cast<const function_decl_diff*>(diff))
713  return namespace_changed(d->first_function_decl(),
714  d->second_function_decl());
715  if (const var_diff* d = dynamic_cast<const var_diff*>(diff))
716  return namespace_changed(d->first_var(), d->second_var());
717  return false;
718 }
719 
720 /// Test if there was a function name change, but there there was no
721 /// change in name of the underlying symbol. IOW, if the name of a
722 /// function changed, but the symbol of the new function is equal to
723 /// the symbol of the old one, or is equal to an alians of the symbol
724 /// of the old function.
725 ///
726 /// @param f the first function to consider.
727 ///
728 /// @param s the second function to consider.
729 ///
730 /// @return true if the test is positive, false otherwise.
731 static bool
732 function_name_changed_but_not_symbol(const function_decl_sptr& f,
733  const function_decl_sptr& s)
734 {
735  if (!f || !s)
736  return false;
737  string fn = f->get_qualified_name(),
738  sn = s->get_qualified_name();
739 
740  if (fn != sn)
741  {
742  elf_symbol_sptr fs = f->get_symbol(), ss = s->get_symbol();
743  if (fs == ss)
744  return true;
745  if (!!fs != !!ss)
746  return false;
747  for (elf_symbol_sptr s = fs->get_next_alias();
748  s && !s->is_main_symbol();
749  s = s->get_next_alias())
750  if (*s == *ss)
751  return true;
752  }
753  return false;
754 }
755 
756 /// Test if the current diff tree node carries a function name change,
757 /// in which there there was no change in the name of the underlying
758 /// symbol. IOW, if the name of a function changed, but the symbol of
759 /// the new function is equal to the symbol of the old one, or is
760 /// equal to an alians of the symbol of the old function.
761 ///
762 /// @param diff the diff tree node to consider.
763 ///
764 /// @return true if the test is positive, false otherwise.
765 static bool
766 function_name_changed_but_not_symbol(const diff* diff)
767 {
768  if (const function_decl_diff* d =
769  dynamic_cast<const function_decl_diff*>(diff))
770  return function_name_changed_but_not_symbol(d->first_function_decl(),
771  d->second_function_decl());
772  return false;
773 }
774 
775 /// Tests if the offset of a given data member changed.
776 ///
777 /// @param f the declaration for the first version of the data member to
778 /// consider.
779 ///
780 /// @param s the declaration for the second version of the data member
781 /// to consider.
782 ///
783 /// @return true iff the offset of the data member changed.
784 static bool
785 data_member_offset_changed(decl_base_sptr f, decl_base_sptr s)
786 {
787  if (!is_member_decl(f)
788  || !is_member_decl(s))
789  return false;
790 
791  var_decl_sptr v0 = dynamic_pointer_cast<var_decl>(f),
792  v1 = dynamic_pointer_cast<var_decl>(s);
793  if (!v0 || !v1)
794  return false;
795 
797  return true;
798 
799  return false;
800 }
801 
802 /// Test if the size of a non-static data member changed accross two
803 /// versions.
804 ///
805 /// @param f the first version of the non-static data member.
806 ///
807 /// @param s the second version of the non-static data member.
808 static bool
809 non_static_data_member_type_size_changed_with_impact(const decl_base_sptr& f,
810  const decl_base_sptr& s)
811 {
812  if (!is_member_decl(f)
813  || !is_member_decl(s))
814  return false;
815 
816  var_decl_sptr fv = dynamic_pointer_cast<var_decl>(f),
817  sv = dynamic_pointer_cast<var_decl>(s);
818  if (!fv
819  || !sv
820  || get_member_is_static(fv)
821  || get_member_is_static(sv))
822  return false;
823 
824  return type_size_changed_with_impact(fv, sv);
825 }
826 
827 /// Test if the size of a static data member changed accross two
828 /// versions.
829 ///
830 /// @param f the first version of the static data member.
831 ///
832 /// @param s the second version of the static data member.
833 static bool
834 static_data_member_type_size_changed(const decl_base_sptr& f,
835  const decl_base_sptr& s)
836 {
837  if (!is_member_decl(f)
838  || !is_member_decl(s))
839  return false;
840 
841  var_decl_sptr fv = dynamic_pointer_cast<var_decl>(f),
842  sv = dynamic_pointer_cast<var_decl>(s);
843  if (!fv
844  || !sv
845  || !get_member_is_static(fv)
846  || !get_member_is_static(sv))
847  return false;
848 
849  return type_size_changed(fv->get_type(), sv->get_type());
850 }
851 
852 /// Test if two types are different but compatible.
853 ///
854 /// @param d1 the declaration of the first type to consider.
855 ///
856 /// @param d2 the declaration of the second type to consider.
857 ///
858 /// @return true if d1 and d2 are different but compatible.
859 static bool
860 is_compatible_change(const decl_base_sptr& d1, const decl_base_sptr& d2)
861 {
862  if ((d1 && d2)
863  && (d1 != d2)
864  && types_are_compatible(d1, d2))
865  return true;
866  return false;
867 }
868 
869 /// Test if a diff node carries a compatible type change.
870 ///
871 /// @param d the diff node to consider.
872 ///
873 /// @return true iff @p carries a compatible type change.
874 static bool
875 is_compatible_type_change(const diff* d)
876 {
877  if (!d)
878  return false;
879 
880  if (type_base_sptr t1 = is_type(d->first_subject()))
881  if (type_base_sptr t2 = is_type(d->second_subject()))
882  return types_are_compatible(t1, t2);
883 
884  return false;
885 }
886 
887 /// Test if a diff node carries a non-compatible change between two
888 /// types of different kinds.
889 ///
890 /// Note that a compatible change is a change whereby two types are
891 /// equal modulo a typedef. Said otherwise, a compatible change is a
892 /// change whereby one type is a typedef of the other.
893 ///
894 /// @param d the diff node to consider.
895 ///
896 /// @return true iff the diff node carries a non-compatible change
897 /// between two types of different kinds.
898 static bool
899 is_non_compatible_distinct_change(const diff *d)
900 {
901  if (const distinct_diff* dd = is_distinct_diff(d))
902  {
903  if (dd->compatible_child_diff()
904  || is_compatible_type_change(d)
906  || (!dd->first_subject() || !dd->second_subject()))
907  // The distinct diff node carries a compatible or benign
908  // change
909  return false;
910 
911  // If we reached this point, then the distinct diff node is
912  // likely to carry a non-compatible change.
913  return true;
914  }
915 
916  return false;
917 }
918 
919 /// Test if a diff node carries a changes in which two decls have
920 /// different names.
921 ///
922 /// @param d the diff node to consider.
923 ///
924 /// @return true iff d carries a change in which two decls have
925 /// different names.
926 static bool
927 decl_name_changed(const diff *d)
928 {return decl_name_changed(d->first_subject(), d->second_subject());}
929 
930 /// Test if two decls represents a harmless name change.
931 ///
932 /// For now, a harmless name change is considered only for a typedef,
933 /// enum or a data member.
934 ///
935 /// @param f the first decl to consider in the comparison.
936 ///
937 /// @param s the second decl to consider in the comparison.
938 ///
939 /// @param ctxt the diff context to use for fine grained comparison of
940 /// @p f and @p s.
941 ///
942 /// @return true iff decl @p s represents a harmless change over @p f.
943 bool
944 has_harmless_name_change(const decl_base_sptr& f,
945  const decl_base_sptr& s,
946  const diff_context_sptr& ctxt)
947 {
948  // So, a harmless name change is either ...
949  return (decl_name_changed(f, s)
950  && (// ... an anonymous decl name changed into another
951  // anonymous decl name ...
952  (f->get_is_anonymous() && s->get_is_anonymous())
953  ||
954  // ... an anonymous decl name changed harmlessly into
955  // another anonymous decl name ...
956  ((f->get_is_anonymous_or_has_anonymous_parent()
957  && s->get_is_anonymous_or_has_anonymous_parent())
958  && tools_utils::decl_names_equal(f->get_qualified_name(),
959  s->get_qualified_name()))
960  // ... Types are compatible (equal modulo a typedef or
961  // cv quals) ...
962  || (is_type(f)
963  && is_type(s)
965  // ... a harmless enum change ...
966  || has_harmless_enum_change(is_type(f), is_type(s), ctxt)
967  // ... a type replaced by a compatible anonymous union
968  // or struct ...
969  || (is_type(f) && is_type(s)
971  is_type(s)))
972  // ... a data member replaced by a compatible anonymous
973  // data member ...
974  || (is_data_member(f) && is_data_member(s)
976  is_decl(s)))
977  // ... or a data member name change, without having its
978  // type changed ...
979  || (is_data_member(f)
980  && is_data_member(s)
981  && (is_var_decl(f)->get_type()
982  == is_var_decl(s)->get_type()))));
983 }
984 
985 /// Test if two decls represent a harmful name change.
986 ///
987 /// A harmful name change is a name change that is not harmless, so
988 /// this function uses the function has_harmless_name_change.
989 ///
990 /// @param f the first decl to consider in the comparison.
991 ///
992 /// @param s the second decl to consider in the comparison.
993 ///
994 /// @param ctxt the diff context to use for comparison.
995 ///
996 /// @return true iff decl @p s represents a harmful name change over
997 /// @p f.
998 bool
999 has_harmful_name_change(const decl_base_sptr& f,
1000  const decl_base_sptr& s,
1001  const diff_context_sptr& ctxt)
1002 {return decl_name_changed(f, s) && ! has_harmless_name_change(f, s, ctxt);}
1003 
1004 /// Test if a diff node represents a harmful name change.
1005 ///
1006 /// A harmful name change is a name change that is not harmless, so
1007 /// this function uses the function has_harmless_name_change.
1008 ///
1009 /// @param f the first decl to consider in the comparison.
1010 ///
1011 /// @param s the second decl to consider in the comparison.
1012 ///
1013 /// @return true iff decl @p s represents a harmful name change over
1014 /// @p f.
1015 bool
1016 has_harmful_name_change(const diff* dif)
1017 {
1018  decl_base_sptr f = is_decl(dif->first_subject()),
1019  s = is_decl(dif->second_subject());
1020 
1021  return has_harmful_name_change(f, s, dif->context());
1022 }
1023 
1024 /// Test if a class_diff node has non-static members added or removed,
1025 /// with a possible impact on (type) scopes using the class.
1026 ///
1027 /// @param diff the diff node to consider.
1028 ///
1029 /// @return true iff the class_diff node has non-static members added
1030 /// or removed.
1031 static bool
1032 non_static_data_member_added_or_removed_with_impact(const class_diff* diff)
1033 {
1034  if (diff && !diff_involves_decl_only_class(diff))
1035  {
1036  for (string_decl_base_sptr_map::const_iterator i =
1037  diff->inserted_data_members().begin();
1038  i != diff->inserted_data_members().end();
1039  ++i)
1040  if (!get_member_is_static(i->second))
1041  {
1042  class_decl_sptr second_class = diff->second_class_decl();
1043  ABG_ASSERT(second_class);
1044  if (!is_anonymous_type(second_class))
1045  // The class that has data member added somewhere is
1046  // *NOT* anonymous so it means it can be reused
1047  // somewhere else but where it's currently declared. So
1048  // the data member addition might have a visible
1049  // external impact.
1050  return true;
1051  }
1052 
1053  for (string_decl_base_sptr_map::const_iterator i =
1054  diff->deleted_data_members().begin();
1055  i != diff->deleted_data_members().end();
1056  ++i)
1057  if (!get_member_is_static(i->second))
1058  return true;
1059  }
1060 
1061  return false;
1062 }
1063 
1064 /// Test if a class_diff node has non-static members added or removed,
1065 /// with a possible impact on (type) scopes using the class.
1066 ///
1067 /// @param diff the diff node to consider.
1068 ///
1069 /// @return true iff the class_diff node has members added or removed.
1070 static bool
1071 non_static_data_member_added_or_removed_with_impact(const diff* diff)
1072 {
1073  return non_static_data_member_added_or_removed_with_impact
1074  (is_class_diff(diff));
1075 }
1076 
1077 /// Test if a @ref class_or_union_diff has a data member replaced by
1078 /// an anonymous data member in a harmless way. That means, the new
1079 /// anonymous data member somehow contains the replaced data member
1080 /// and it doesn't break the layout of the containing class.
1081 ///
1082 /// @param diff the diff node to consider.
1083 ///
1084 /// @return true iff the @ref class_or_union_diff has a data member
1085 /// harmlessly replaced by an anonymous data member.
1086 bool
1088 {
1090 
1091  if (!c)
1092  return false;
1093  return !c->data_members_replaced_by_adms().empty();
1094 }
1095 
1096 /// Test if we are looking at two variables which types are both one
1097 /// dimension array, with one of them being of unknow size and the two
1098 /// variables having the same symbol size.
1099 ///
1100 /// This can happen in the case of these two declarations, for instance:
1101 ///
1102 /// unsigned int array[];
1103 ///
1104 /// and:
1105 ///
1106 /// unsigned int array[] ={0};
1107 ///
1108 /// In both cases, the size of the ELF symbol of the variable 'array'
1109 /// is 32 bits, but, at least in the first case
1110 bool
1112  const var_decl_sptr& var2)
1113 {
1114  type_base_sptr /*first type*/ft =
1115  peel_qualified_or_typedef_type(var1->get_type());
1116  type_base_sptr /*second type*/st =
1117  peel_qualified_or_typedef_type(var2->get_type());
1118 
1119  array_type_def_sptr /*first array type*/fat = is_array_type(ft);
1120  array_type_def_sptr /*second array type*/sat = is_array_type(st);
1121 
1122  // The types of the variables must be arrays.
1123  if (!fat || !sat)
1124  return false;
1125 
1126  // The arrays must have one dimension and at least one of them must
1127  // be of unknown size.
1128  if (fat->get_subranges().size() != 1
1129  || sat->get_subranges().size() != 1
1130  || (!fat->is_non_finite() && !sat->is_non_finite()))
1131  return false;
1132 
1133  // The variables must be equal modulo their type.
1134  if (!var_equals_modulo_types(*var1, *var2, nullptr))
1135  return false;
1136 
1137  // The symbols of the variables must be defined and of the same
1138  // non-zero size.
1139  if (!var1->get_symbol()
1140  || !var2->get_symbol()
1141  || var1->get_symbol()->get_size() != var2->get_symbol()->get_size())
1142  return false;
1143 
1144  return true;
1145 }
1146 
1147 /// Test if we are looking at a diff that carries a change of
1148 /// variables which types are both one dimension array, with one of
1149 /// them being of unknow size and the two variables having the same
1150 /// symbol size.
1151 ///
1152 /// This can happen in the case of these two declarations, for instance:
1153 ///
1154 /// unsigned int array[];
1155 ///
1156 /// and:
1157 ///
1158 /// unsigned int array[] ={0};
1159 ///
1160 /// In both cases, the size of the ELF symbol of the variable 'array'
1161 /// is 32 bits, but, at least in the first case
1162 bool
1164 {
1165  const var_diff* d = is_var_diff(diff);
1166 
1167  if (!d)
1168  return false;
1169 
1170  var_decl_sptr f = d->first_var(), s = d->second_var();
1171 
1173 }
1174 
1175 /// Test if a class with a fake flexible data member got changed into
1176 /// a class with a real fexible data member.
1177 ///
1178 /// A fake flexible array data member is a data member that is the
1179 /// last of the class/struct which type is an array of one element.
1180 /// This was used before C99 standardized flexible array data members.
1181 ///
1182 /// @param first the first version of the class to consider.
1183 ///
1184 /// @param second the second version of the class to consider.
1185 ///
1186 /// @return true iff @p first has a fake flexible array data member
1187 /// that got changed into @p second with a real flexible array data
1188 /// member.
1189 bool
1191  const class_decl_sptr& second)
1192 {
1194  && has_flexible_array_data_member(second))
1195  // A fake flexible array member has been changed into
1196  // a real flexible array ...
1197  return true;
1198  return false;
1199 }
1200 
1201 /// Test if a diff node carries a change from class with a fake
1202 /// flexible data member into a class with a real fexible data member.
1203 ///
1204 /// A fake flexible array data member is a data member that is the
1205 /// last of the class/struct which type is an array of one element.
1206 /// This was used before C99 standardized flexible array data members.
1207 ///
1208 /// @param the diff node to consider.
1209 ///
1210 /// @return true iff @p dif carries a change from class with a fake
1211 /// flexible data member into a class with a real fexible data member.
1212 /// member.
1213 bool
1215 {
1216  const class_diff* d = is_class_diff(dif);
1217  if (!d)
1218  return false;
1219 
1221  d->second_class_decl());
1222 }
1223 
1224 /// Test if a diff node carries a change where an lvalue reference
1225 /// changed into a rvalue reference, or vice versa.
1226 ///
1227 /// @param dif the diff node to consider.
1228 ///
1229 /// @return true iff @p dif carries a change where an lvalue reference
1230 /// changed into a rvalue reference, or vice versa.
1231 bool
1233 {
1234  const reference_diff* d = is_reference_diff(dif);
1235  if (!d)
1236  return false;
1237 
1238  if (d->first_reference()->is_lvalue() == d->second_reference()->is_lvalue())
1239  return false;
1240 
1241  return true;
1242 }
1243 
1244 /// Test if a class_diff node has static members added or removed.
1245 ///
1246 /// @param diff the diff node to consider.
1247 ///
1248 /// @return true iff the class_diff node has static members added
1249 /// or removed.
1250 static bool
1251 static_data_member_added_or_removed(const class_diff* diff)
1252 {
1253  if (diff && !diff_involves_decl_only_class(diff))
1254  {
1255  for (string_decl_base_sptr_map::const_iterator i =
1256  diff->inserted_data_members().begin();
1257  i != diff->inserted_data_members().end();
1258  ++i)
1259  if (get_member_is_static(i->second))
1260  return true;
1261 
1262  for (string_decl_base_sptr_map::const_iterator i =
1263  diff->deleted_data_members().begin();
1264  i != diff->deleted_data_members().end();
1265  ++i)
1266  if (get_member_is_static(i->second))
1267  return true;
1268  }
1269 
1270  return false;
1271 }
1272 
1273 /// Test if a class_diff node has a harmless "One Definition Rule"
1274 /// violation that will cause a diagnostic rule.
1275 ///
1276 /// The conditions this function looks for are:
1277 ///
1278 /// 1/ The two subject of the diff must be canonically different
1279 ///
1280 /// 2/ The two subjects of the diff must be structurally equal
1281 ///
1282 /// 3/ The canonical types of the subjects of the diff must be
1283 /// structurally different.
1284 ///
1285 /// These conditions makes the diff node appears as it carries changes
1286 /// (because of a ODR glitch present in the binary), but the glitch
1287 /// has no effect on the structural equality of the subjects of the
1288 /// diff. If we do not detect these conditions, we'd end up with a
1289 /// diagnostic glitch where the reporter thinks there is an ABI change
1290 /// (because of the canonical difference), but then it fails to give
1291 /// any detail about it, because there is no structural change.
1292 ///
1293 /// @param diff the diff node to consider.
1294 ///
1295 /// @return true iff the the diff node has a harmless "One Definition
1296 /// Rule" violation that cause an empty false positive.
1297 static bool
1298 class_diff_has_harmless_odr_violation_change(const diff* dif)
1299 {
1300  class_diff* d = dynamic_cast<class_diff*>(const_cast<diff*>(dif));
1301  if (!d || !d->has_changes())
1302  return false;
1303 
1304  class_decl_sptr first = d->first_class_decl();
1305  class_decl_sptr second = d->second_class_decl();
1306 
1307  if (first->get_qualified_name() == second->get_qualified_name()
1308  && first != second
1309  && first->get_corpus() == second->get_corpus())
1310  return true;
1311 
1312  return false;
1313 }
1314 
1315 /// Test if a class_diff node has static members added or
1316 /// removed.
1317 ///
1318 /// @param diff the diff node to consider.
1319 ///
1320 /// @return true iff the class_diff node has static members added
1321 /// or removed.
1322 static bool
1323 static_data_member_added_or_removed(const diff* diff)
1324 {
1325  return static_data_member_added_or_removed
1326  (dynamic_cast<const class_diff*>(diff));
1327 }
1328 
1329 /// Test if the class_diff node has a change involving virtual member
1330 /// functions.
1331 ///
1332 /// That means whether there is an added, removed or changed virtual
1333 /// member function.
1334 ///
1335 /// @param diff the class_diff node to consider.
1336 ///
1337 /// @return true iff the class_diff node contains changes involving
1338 /// virtual member functions.
1339 static bool
1340 has_virtual_mem_fn_change(const class_diff* diff)
1341 {
1342  if (!diff || diff_involves_decl_only_class(diff))
1343  return false;
1344 
1345  for (string_member_function_sptr_map::const_iterator i =
1346  diff->deleted_member_fns().begin();
1347  i != diff->deleted_member_fns().end();
1348  ++i)
1349  {
1350  if (get_member_function_is_virtual(i->second))
1351  {
1352  // Do not consider a virtual function that got deleted from
1353  // an offset and re-inserted at the same offset as a
1354  // "virtual member function change".
1355  string_member_function_sptr_map::const_iterator j =
1356  diff->inserted_member_fns().find(i->first);
1357  if (j != diff->inserted_member_fns().end()
1358  && (get_member_function_vtable_offset(i->second)
1359  == get_member_function_vtable_offset(j->second)))
1360  continue;
1361 
1362  return true;
1363  }
1364  }
1365 
1366  for (string_member_function_sptr_map::const_iterator i =
1367  diff->inserted_member_fns().begin();
1368  i != diff->inserted_member_fns().end();
1369  ++i)
1370  {
1371  if (get_member_function_is_virtual(i->second))
1372  {
1373  // Do not consider a virtual function that got deleted from
1374  // an offset and re-inserted at the same offset as a
1375  // "virtual member function change".
1376  string_member_function_sptr_map::const_iterator j =
1377  diff->deleted_member_fns().find(i->first);
1378  if (j != diff->deleted_member_fns().end()
1379  && (get_member_function_vtable_offset(i->second)
1380  == get_member_function_vtable_offset(j->second)))
1381  continue;
1382 
1383  return true;
1384  }
1385  }
1386 
1387  for (function_decl_diff_sptrs_type::const_iterator i =
1388  diff->changed_member_fns().begin();
1389  i != diff->changed_member_fns().end();
1390  ++i)
1391  if (get_member_function_is_virtual((*i)->first_function_decl())
1392  || get_member_function_is_virtual((*i)->second_function_decl()))
1393  {
1394  if (get_member_function_vtable_offset((*i)->first_function_decl())
1395  == get_member_function_vtable_offset((*i)->second_function_decl()))
1396  continue;
1397 
1398  return true;
1399  }
1400 
1401  return false;
1402 }
1403 
1404 /// Test if the function_decl_diff node has a change involving virtual
1405 /// member functions.
1406 ///
1407 /// That means whether there is an added, removed or changed virtual
1408 /// member function.
1409 ///
1410 /// @param diff the function_decl_diff node to consider.
1411 ///
1412 /// @return true iff the function_decl_diff node contains changes
1413 /// involving virtual member functions.
1414 bool
1415 has_virtual_mem_fn_change(const function_decl_diff* diff)
1416 {
1417  if (!diff)
1418  return false;
1419 
1421  sf = diff->second_function_decl();
1422 
1423  if (!is_member_function(ff)
1424  || !is_member_function(sf))
1425  return false;
1426 
1427  bool ff_is_virtual = get_member_function_is_virtual(ff),
1428  sf_is_virtual = get_member_function_is_virtual(sf);
1429 
1430  if (ff_is_virtual != sf_is_virtual)
1431  return true;
1432 
1433  size_t ff_vtable_offset = get_member_function_vtable_offset(ff),
1434  sf_vtable_offset = get_member_function_vtable_offset(sf);
1435 
1436  if (ff_vtable_offset != sf_vtable_offset)
1437  return true;
1438 
1439  return false;
1440 }
1441 
1442 /// Test if the class_diff node has a change involving virtual member
1443 /// functions.
1444 ///
1445 /// That means whether there is an added, removed or changed virtual
1446 /// member function.
1447 ///
1448 /// @param diff the class_diff node to consider.
1449 ///
1450 /// @return true iff the class_diff node contains changes involving
1451 /// virtual member functions.
1452 static bool
1453 has_virtual_mem_fn_change(const diff* diff)
1454 {
1455  return (has_virtual_mem_fn_change(dynamic_cast<const class_diff*>(diff))
1456  || has_virtual_mem_fn_change(dynamic_cast<const function_decl_diff*>(diff)));
1457 }
1458 
1459 /// Test if the class_diff has changes to non virtual member
1460 /// functions.
1461 ///
1462 ///@param diff the class_diff nod e to consider.
1463 ///
1464 /// @retrurn iff the class_diff node has changes to non virtual member
1465 /// functions.
1466 static bool
1467 has_non_virtual_mem_fn_change(const class_diff* diff)
1468 {
1469  if (!diff || diff_involves_decl_only_class(diff))
1470  return false;
1471 
1472  for (string_member_function_sptr_map::const_iterator i =
1473  diff->deleted_member_fns().begin();
1474  i != diff->deleted_member_fns().end();
1475  ++i)
1476  if (!get_member_function_is_virtual(i->second))
1477  return true;
1478 
1479  for (string_member_function_sptr_map::const_iterator i =
1480  diff->inserted_member_fns().begin();
1481  i != diff->inserted_member_fns().end();
1482  ++i)
1483  if (!get_member_function_is_virtual(i->second))
1484  return true;
1485 
1486  for (function_decl_diff_sptrs_type::const_iterator i =
1487  diff->changed_member_fns().begin();
1488  i != diff->changed_member_fns().end();
1489  ++i)
1490  if(!get_member_function_is_virtual((*i)->first_function_decl())
1491  && !get_member_function_is_virtual((*i)->second_function_decl()))
1492  return true;
1493 
1494  return false;
1495 }
1496 
1497 /// Test if the class_diff has changes to non virtual member
1498 /// functions.
1499 ///
1500 ///@param diff the class_diff nod e to consider.
1501 ///
1502 /// @retrurn iff the class_diff node has changes to non virtual member
1503 /// functions.
1504 static bool
1505 has_non_virtual_mem_fn_change(const diff* diff)
1506 {return has_non_virtual_mem_fn_change(dynamic_cast<const class_diff*>(diff));}
1507 
1508 /// Test if a class_diff carries a base class removal.
1509 ///
1510 /// @param diff the class_diff to consider.
1511 ///
1512 /// @return true iff @p diff carries a base classe removal.
1513 static bool
1514 base_classes_removed(const class_diff* diff)
1515 {
1516  if (!diff)
1517  return false;
1518  return diff->deleted_bases().size();
1519 }
1520 
1521 /// Test if a class_diff carries a base classes removal.
1522 ///
1523 /// @param diff the class_diff to consider.
1524 ///
1525 /// @return true iff @p diff carries a base class removal.
1526 static bool
1527 base_classes_removed(const diff* diff)
1528 {return base_classes_removed(dynamic_cast<const class_diff*>(diff));}
1529 
1530 /// Test if two classes that are decl-only (have the decl-only flag
1531 /// and carry no data members) but are different just by their size.
1532 ///
1533 /// In some weird DWARF representation, it happens that a decl-only
1534 /// class (with no data member) actually carries a non-zero size.
1535 /// That shouldn't happen, but hey, we need to deal with real life.
1536 /// So we need to detect that case first.
1537 ///
1538 /// @param first the first class or union to consider.
1539 ///
1540 /// @param seconf the second class or union to consider.
1541 ///
1542 /// @return true if the two classes are decl-only and differ in their
1543 /// size.
1544 bool
1546  const class_or_union& second)
1547 {
1548  if (first.get_qualified_name() != second.get_qualified_name())
1549  return false;
1550 
1551  if (!first.get_is_declaration_only() || !second.get_is_declaration_only())
1552  return false;
1553 
1554  bool f_is_empty = first.get_data_members().empty();
1555  bool s_is_empty = second.get_data_members().empty();
1556 
1557  return f_is_empty && s_is_empty;
1558 }
1559 
1560 /// Test if two classes that are decl-only (have the decl-only flag
1561 /// and carry no data members) but are different just by their size.
1562 ///
1563 /// In some weird DWARF representation, it happens that a decl-only
1564 /// class (with no data member) actually carries a non-zero size.
1565 /// That shouldn't happen, but hey, we need to deal with real life.
1566 /// So we need to detect that case first.
1567 ///
1568 /// @param first the first class or union to consider.
1569 ///
1570 /// @param seconf the second class or union to consider.
1571 ///
1572 /// @return true if the two classes are decl-only and differ in their
1573 /// size.
1574 bool
1575 is_decl_only_class_with_size_change(const class_or_union_sptr& first,
1576  const class_or_union_sptr& second)
1577 {
1578  if (!first || !second)
1579  return false;
1580 
1581  class_or_union_sptr f = look_through_decl_only_class(first);
1582  class_or_union_sptr s = look_through_decl_only_class(second);
1583 
1584  return is_decl_only_class_with_size_change(*f, *s);
1585 }
1586 
1587 /// Test if a diff node is for two classes that are decl-only (have
1588 /// the decl-only flag and carry no data members) but are different
1589 /// just by their size.
1590 ///
1591 /// In some weird DWARF representation, it happens that a decl-only
1592 /// class (with no data member) actually carries a non-zero size.
1593 /// That shouldn't happen, but hey, we need to deal with real life.
1594 /// So we need to detect that case first.
1595 ///
1596 /// @param diff the diff node to consider.
1597 ///
1598 /// @return true if the two classes are decl-only and differ in their
1599 /// size.
1600 bool
1602 {
1603  const class_or_union_diff *d = dynamic_cast<const class_or_union_diff*>(diff);
1604  if (!d)
1605  return false;
1606 
1607  class_or_union_sptr f =
1609  class_or_union_sptr s =
1611 
1613 }
1614 
1615 /// Test if two @ref decl_base_sptr are different just by the
1616 /// fact that one is decl-only and the other one is defined.
1617 ///
1618 /// @param first the first decl to consider.
1619 ///
1620 /// @param second the second decl to consider.
1621 ///
1622 /// @return true iff the two arguments are different just by the fact
1623 /// that one is decl-only and the other one is defined.
1624 bool
1625 has_decl_only_def_change(const decl_base_sptr& first,
1626  const decl_base_sptr& second)
1627 {
1628  if (!first || !second)
1629  return false;
1630 
1631  decl_base_sptr f =
1632  look_through_decl_only(first);
1633  decl_base_sptr s =
1634  look_through_decl_only(second);
1635 
1636  if (f->get_qualified_name() != s->get_qualified_name())
1637  return false;
1638 
1639  return f->get_is_declaration_only() != s->get_is_declaration_only();
1640 }
1641 
1642 /// Test if a diff carries a change in which the two decls are
1643 /// different by the fact that one is a decl-only and the other one is
1644 /// defined.
1645 ///
1646 /// @param diff the diff node to consider.
1647 ///
1648 /// @return true if the diff carries a change in which the two decls
1649 /// are different by the fact that one is a decl-only and the other
1650 /// one is defined.
1651 bool
1653 {
1654  if (!d)
1655  return false;
1656 
1657  decl_base_sptr f =
1659  decl_base_sptr s =
1661 
1662  return has_decl_only_def_change(f, s);
1663 }
1664 
1665 
1666 /// Test if two @ref class_or_union_sptr are different just by the
1667 /// fact that one is decl-only and the other one is defined.
1668 ///
1669 /// @param first the first class or union to consider.
1670 ///
1671 /// @param second the second class or union to consider.
1672 ///
1673 /// @return true iff the two arguments are different just by the fact
1674 /// that one is decl-only and the other one is defined.
1675 bool
1676 has_class_decl_only_def_change(const class_or_union_sptr& first,
1677  const class_or_union_sptr& second)
1678 {
1679  if (!first || !second)
1680  return false;
1681 
1682  class_or_union_sptr f =
1684  class_or_union_sptr s =
1686 
1687  if (f->get_qualified_name() != s->get_qualified_name())
1688  return false;
1689 
1690  return f->get_is_declaration_only() != s->get_is_declaration_only();
1691 }
1692 
1693 /// Test if two @ref enum_sptr are different just by the
1694 /// fact that one is decl-only and the other one is defined.
1695 ///
1696 /// @param first the first enum to consider.
1697 ///
1698 /// @param second the second enum to consider.
1699 ///
1700 /// @return true iff the two arguments are different just by the fact
1701 /// that one is decl-only and the other one is defined.
1702 bool
1704  const enum_type_decl_sptr& second)
1705 {
1706  if (!first || !second)
1707  return false;
1708 
1711 
1712  if (f->get_qualified_name() != s->get_qualified_name())
1713  return false;
1714 
1715  return f->get_is_declaration_only() != s->get_is_declaration_only();
1716 }
1717 
1718 /// Test if a class_or_union_diff carries a change in which the two
1719 /// classes are different by the fact that one is a decl-only and the
1720 /// other one is defined.
1721 ///
1722 /// @param diff the diff node to consider.
1723 ///
1724 /// @return true if the class_or_union_diff carries a change in which
1725 /// the two classes are different by the fact that one is a decl-only
1726 /// and the other one is defined.
1727 bool
1729 {
1730  const class_or_union_diff *d = dynamic_cast<const class_or_union_diff*>(diff);
1731  if (!d)
1732  return false;
1733 
1734  class_or_union_sptr f =
1736  class_or_union_sptr s =
1738 
1739  return has_class_decl_only_def_change(f, s);
1740 }
1741 
1742 /// Test if a enum_diff carries a change in which the two enums are
1743 /// different by the fact that one is a decl-only and the other one is
1744 /// defined.
1745 ///
1746 /// @param diff the diff node to consider.
1747 ///
1748 /// @return true if the enum_diff carries a change in which the two
1749 /// enums are different by the fact that one is a decl-only and the
1750 /// other one is defined.
1751 bool
1753 {
1754  const enum_diff *d = dynamic_cast<const enum_diff*>(diff);
1755  if (!d)
1756  return false;
1757 
1760 
1761  return has_enum_decl_only_def_change(f, s);
1762 }
1763 
1764 /// Test if a diff node carries a basic type name change.
1765 ///
1766 /// @param d the diff node to consider.
1767 ///
1768 /// @return true iff the diff node carries a basic type name change.
1769 bool
1771 {
1772  if (const type_decl_diff *dif = is_diff_of_basic_type(d))
1773  if (decl_name_changed(dif))
1774  return true;
1775 
1776  return false;
1777 }
1778 
1779 /// Test if a diff node carries a class or union type name change.
1780 ///
1781 /// @param d the diff node to consider.
1782 ///
1783 /// @return true iff the diff node carries a class or union type name
1784 /// change.
1785 bool
1787 {
1789  if (decl_name_changed(dif))
1790  return true;
1791 
1792  return false;
1793 }
1794 
1795 /// Test if a diff node carries a basic or class type name change.
1796 ///
1797 /// @param d the diff node to consider.
1798 ///
1799 /// @return true iff the diff node carries a basic or class type name
1800 /// change.
1801 bool
1803 {
1804  return (has_basic_type_name_change(d)
1806 }
1807 
1808 /// Test if a diff node carries a distinct type change or a
1809 /// pointer/reference/typedef to distinct type change.
1810 ///
1811 /// Note that a distinct type change is a change where the two
1812 /// subjects of the change are not of the same kind, e.g, a basic type
1813 /// that got changed into a qualified type.
1814 ///
1815 /// @param d the diff node to consider.
1816 ///
1817 /// @return true iff @p d is mostly a distinct diff.
1818 bool
1820 {
1821  if (is_distinct_diff(d))
1822  return true;
1823 
1824  // Let's consider that 'd' is a type diff ...
1825  diff * td = const_cast<type_diff_base*>(is_type_diff(d));
1826  if (!td)
1827  {
1828  // ... or a function parameter diff. In which case, let's get
1829  // its child type diff ...
1830  fn_parm_diff *pd = const_cast<fn_parm_diff*>(is_fn_parm_diff(d));
1831  if (pd)
1832  {
1833  td = const_cast<type_diff_base*>(is_type_diff(pd->type_diff().get()));
1834  if (!td)
1835  // if the diff of the fn_parm_diff is a a distinct diff
1836  // then handle it.
1837  td = const_cast<distinct_diff*>
1838  (is_distinct_diff(pd->type_diff().get()));
1839  }
1840  else
1841  return false;
1842  }
1843 
1844  // At this point, if we are not looking at a type diff we must have
1845  // bailed out already.
1846  ABG_ASSERT(td);
1847 
1848  type_base_sptr first = is_type(td->first_subject());
1849  type_base_sptr second = is_type(td->second_subject());
1850 
1852  second = peel_typedef_pointer_or_reference_type(second);
1853  ABG_ASSERT(first && second);
1854 
1855  return distinct_diff::entities_are_of_distinct_kinds(first, second);
1856 }
1857 
1858 /// Test if a diff node carries a non-anonymous data member to
1859 /// anonymous data member change, or vice-versa.
1860 ///
1861 /// @param d the diff node to consider.
1862 ///
1863 /// @return true iff @p d carries a non-anonymous to anonymous data
1864 /// member change, or vice-versa.
1865 bool
1867 {
1872  return true;
1873  return false;
1874 }
1875 
1876 /// Test if a diff node carries a non-anonymous data member to
1877 /// anonymous data member change, or vice-versa.
1878 ///
1879 /// @param d the diff node to consider.
1880 ///
1881 /// @return true iff @p d carries a non-anonymous to anonymous data
1882 /// member change, or vice-versa.
1883 bool
1885 {return has_anonymous_data_member_change(d.get());}
1886 
1887 /// Test if an enum_diff carries an enumerator insertion.
1888 ///
1889 /// @param diff the enum_diff to consider.
1890 ///
1891 /// @return true iff @p diff carries an enumerator insertion.
1892 static bool
1893 has_enumerator_insertion(const diff* diff)
1894 {
1895  if (const enum_diff* d = dynamic_cast<const enum_diff*>(diff))
1896  return !d->inserted_enumerators().empty();
1897  return false;
1898 }
1899 
1900 /// Test if an enum_diff carries an enumerator removal or an
1901 /// enumerator value change.
1902 ///
1903 /// @param diff the enum_diff to consider.
1904 ///
1905 /// @return true iff @p diff carries an enumerator removal or change.
1906 static bool
1907 has_enumerator_removal_or_value_change(const diff* diff)
1908 {
1909  if (const enum_diff* d = dynamic_cast<const enum_diff*>(diff))
1910  {
1911  if (!d->deleted_enumerators().empty())
1912  return true;
1913 
1914  for (auto& entry : d->changed_enumerators())
1915  {
1916  const changed_enumerator& change = entry.second;
1917  if (change.first.get_value() != change.second.get_value())
1918  return true;
1919  }
1920  }
1921  return false;
1922 }
1923 
1924 /// Test if a diff node carries an enumerator name or value change.
1925 ///
1926 /// @param diff the diff node to consider.
1927 ///
1928 /// @return true iff the diff node @p diff carries an enumerator name
1929 /// or value change.
1930 static bool
1931 has_enumerator_change(const diff* diff)
1932 {
1933  if (const enum_diff* d = dynamic_cast<const enum_diff*>(diff))
1934  return !d->changed_enumerators().empty();
1935  return false;
1936 }
1937 
1938 /// Test if an enum_diff carries a harmful change.
1939 ///
1940 /// For now, a harmful enum change is either a change that:
1941 ///
1942 /// - changes the size of the enum type
1943 ///
1944 /// - or removes (or changes) an existing enumerator value.
1945 ///
1946 /// @param diff the enum_diff to consider.
1947 ///
1948 /// @return true iff @p diff carries a harmful change.
1949 static bool
1950 has_harmful_enum_change(const diff* diff)
1951 {
1952  if (const enum_diff* d = dynamic_cast<const enum_diff*>(diff))
1953  if (has_type_size_change(d) || has_enumerator_removal_or_value_change(d))
1954  return true;
1955 
1956  return false;
1957 }
1958 
1959 /// Test if a diff node carries a harmless change of an enum into an
1960 /// integer (or vice-versa).
1961 ///
1962 /// The test takes into account the fact change we care about might be
1963 /// wrapped into a typedef or qualified type diff.
1964 ///
1965 /// @param diff the diff node to consider.
1966 ///
1967 /// @return true if @p diff is a harmless enum to integer change.
1968 bool
1970 {
1971  if (!diff)
1972  return false;
1973 
1975 
1976  if (const distinct_diff *d = is_distinct_diff(diff))
1977  {
1978  const enum_type_decl *enum_type = 0;
1979  const type_base *integer_type = 0;
1980 
1981  type_base *first_type =
1982  peel_qualified_or_typedef_type(is_type(d->first().get()));
1983  type_base *second_type =
1984  peel_qualified_or_typedef_type(is_type(d->second().get()));
1985 
1986  if (const enum_type_decl *e = is_enum_type(first_type))
1987  enum_type = e;
1988  else if (const enum_type_decl *e = is_enum_type(second_type))
1989  enum_type = e;
1990 
1991  if (const type_base * i = is_type_decl(first_type))
1992  integer_type = i;
1993  else if (const type_base *i = is_type_decl(second_type))
1994  integer_type = i;
1995 
1996  if (enum_type
1997  && integer_type
1998  && enum_type->get_size_in_bits() == integer_type->get_size_in_bits())
1999  return true;
2000  }
2001 
2002  return false;
2003 }
2004 
2005 /// Test if two types represent a harmless (that can be filtered out
2006 /// by default) enum type change.
2007 ///
2008 /// A harmless enum type change is either an enumerator insertion or
2009 /// an enumerator change that doesn't represents a harmful enum change
2010 /// at the same time. Note that a harmless enum to int change is a
2011 /// harmless enum change too.
2012 ///
2013 /// @param t1 the first version of the type to consider.
2014 ///
2015 /// @param t2 the second version of the type to consider.
2016 ///
2017 /// @param ctxt the diff context to use to compare @p t1 and @p t2.
2018 ///
2019 /// @return true iff {t1, t2} represents a harmless enum change.
2020 static bool
2021 has_harmless_enum_change(const type_base_sptr& t1,
2022  const type_base_sptr& t2,
2023  const diff_context_sptr& ctxt)
2024 {
2025  type_base_sptr f = peel_typedef_type(t1);
2026  type_base_sptr s = peel_typedef_type(t2);
2029 
2030  if (!e1 || !e2)
2031  return false;
2032 
2033  enum_diff_sptr dyf = compute_diff(e1, e2, ctxt);
2034  if (((has_enumerator_insertion(dyf.get()) || has_enumerator_change(dyf.get()))
2035  && !has_harmful_enum_change(dyf.get()))
2036  || has_harmless_enum_to_int_change(dyf.get()))
2037  return true;
2038 
2039  return false;
2040 }
2041 
2042 /// Test if two types represent a harmless (that can be filtered out
2043 /// by default) enum type change.
2044 ///
2045 /// A harmless enum type change is either an enumerator insertion or
2046 /// an enumerator change that doesn't represents a harmful enum change
2047 /// at the same time. Note that a harmless enum to int change is a
2048 /// harmless enum change too.
2049 ///
2050 /// @param t1 the first version of the type to consider.
2051 ///
2052 /// @param t2 the second version of the type to consider.
2053 ///
2054 /// @param ctxt the diff context to use to compare @p t1 and @p t2.
2055 ///
2056 /// @return true iff {t1, t2} represents a harmless enum change.
2057 static bool
2058 has_harmless_enum_change(const diff* d)
2059 {
2060  if (!d)
2061  return false;
2062 
2063  if (((has_enumerator_insertion(d) || has_enumerator_change(d))
2064  && !has_harmful_enum_change(d))
2066  return true;
2067 
2068  type_base_sptr f = is_type(d->first_subject());
2069  type_base_sptr s = is_type(d->second_subject());
2070  if (!f || !s)
2071  return false;
2072 
2073  return has_harmless_enum_change(f, s, d->context());
2074 }
2075 
2076 /// Test if an @ref fn_parm_diff node has a top cv qualifier change on
2077 /// the type of the function parameter.
2078 ///
2079 /// @param diff the diff node to consider. It should be a @ref
2080 /// fn_parm_diff, otherwise the function returns 'false' directly.
2081 ///
2082 /// @return true iff @p diff is a @ref fn_parm_diff node that has a
2083 /// top cv qualifier change on the type of the function parameter.
2084 static bool
2085 has_fn_parm_type_top_cv_qual_change(const diff* diff)
2086 {
2087  // is diff a "function parameter diff node?
2088  const fn_parm_diff* parm_diff = is_fn_parm_diff(diff);
2089 
2090  if (!parm_diff || !parm_diff->has_changes())
2091  // diff either carries no change or is not a function parameter
2092  // diff node. So bail out.
2093  return false;
2094 
2095  function_decl::parameter_sptr first_parm = parm_diff->first_parameter();
2096  function_decl::parameter_sptr second_parm = parm_diff->second_parameter();
2097 
2098  type_base_sptr first_parm_type = first_parm->get_type();
2099  type_base_sptr second_parm_type = second_parm->get_type();
2100 
2101  if (!is_qualified_type(first_parm_type)
2102  && !is_qualified_type(second_parm_type))
2103  // None of the parameter types is qualified.
2104  return false;
2105 
2106  qualified_type_def::CV cv_quals_1 = qualified_type_def::CV_NONE;
2107  qualified_type_def::CV cv_quals_2 = qualified_type_def::CV_NONE;
2108  type_base_sptr peeled_type_1 = first_parm_type;
2109  type_base_sptr peeled_type_2 = second_parm_type;
2110 
2111  if (qualified_type_def_sptr qtype1 = is_qualified_type(first_parm_type))
2112  {
2113  cv_quals_1 = qtype1->get_cv_quals();
2114  peeled_type_1 = peel_qualified_type(qtype1);
2115  }
2116 
2117  if (qualified_type_def_sptr qtype2 = is_qualified_type(second_parm_type))
2118  {
2119  cv_quals_2 = qtype2->get_cv_quals();
2120  peeled_type_2 = peel_qualified_type(qtype2);
2121  }
2122 
2123  if (peeled_type_1
2124  && peeled_type_2
2125  && get_type_name(peeled_type_1) == get_type_name(peeled_type_2)
2126  && cv_quals_1 != cv_quals_2)
2127  // The top-level CV qualifiers of the function type are different
2128  // and the un-qualified variant (peeled) of said function types
2129  // are equal. This means the only change the function types have
2130  // are about top-level CV qualifiers.
2131  return true;
2132 
2133  return false;
2134 }
2135 
2136 /// Test if a type diff only carries a CV qualifier-only change.
2137 ///
2138 /// @param type_dif the type dif to consider.
2139 ///
2140 /// @return true iff the type_diff carries a CV qualifier only change.
2141 static bool
2142 type_diff_has_typedef_cv_qual_change_only(const diff *type_dif)
2143 {
2144  if (!type_dif)
2145  return false;
2146 
2147  type_base_sptr f = is_type(type_dif->first_subject());
2148  type_base_sptr s = is_type(type_dif->second_subject());
2149 
2150  return type_diff_has_typedef_cv_qual_change_only(f, s);
2151 }
2152 
2153 /// Test if a type only carries a CV qualifier-only change.
2154 ///
2155 /// Note that for pointers and array types, the functions look at
2156 /// pointed-to types for comparison.
2157 ///
2158 /// @param f the first version of the type.
2159 ///
2160 /// @param s the second version of the type.
2161 ///
2162 /// @return true iff the change is only a qualifier change.
2163 static bool
2164 type_diff_has_typedef_cv_qual_change_only(const type_base_sptr& f,
2165  const type_base_sptr& s)
2166 {
2167  type_base_sptr a = f;
2168  type_base_sptr b = s;
2169 
2172 
2173  if (a && b && *a == *b)
2174  return true;
2175 
2176  if (is_pointer_type(a) && is_pointer_type(b))
2178 
2179  // If f and s are arrays, note that they can differ only by the cv
2180  // qualifier of the array element type. That cv qualifier is not
2181  // removed by peel_qualified_type. So we need to test this case
2182  // specifically.
2183  if (array_type_def_sptr f_a = is_array_type(a))
2184  if (array_type_def_sptr s_a = is_array_type(b))
2185  return equals_modulo_cv_qualifier(f_a, s_a);
2186 
2187  return false;
2188 }
2189 
2190 /// Test if an @ref fn_parm_diff node has a cv qualifier change on the
2191 /// type of the function parameter. That is, we are looking for
2192 /// changes like 'const char*' to 'char*'.
2193 ///
2194 /// @param diff the diff node to consider. It should be a @ref
2195 /// fn_parm_diff, otherwise the function returns 'false' directly.
2196 ///
2197 /// @return true iff @p diff is a @ref fn_parm_diff node that has a
2198 /// cv qualifier change on the type of the function parameter.
2199 static bool
2200 has_fn_parm_type_cv_qual_change(const diff* dif)
2201 {
2202  // is diff a "function parameter diff node?
2203  const fn_parm_diff* parm_diff = is_fn_parm_diff(dif);
2204 
2205  if (!parm_diff || !parm_diff->has_changes())
2206  // diff either carries no change or is not a function parameter
2207  // diff node. So bail out.
2208  return false;
2209 
2210  const diff *type_dif = parm_diff->type_diff().get();
2211  return type_diff_has_typedef_cv_qual_change_only(type_dif);
2212 }
2213 
2214 /// Test if a function type or decl diff node carries a CV
2215 /// qualifier-only change on its return type.
2216 ///
2217 /// @param dif the diff node to consider. Note that if this is
2218 /// neither a function type nor decl diff node, the function returns
2219 /// false.
2220 ///
2221 /// @return true iff @p dif is a function decl or type diff node which
2222 /// carries a CV qualifier-only change on its return type.
2223 static bool
2224 has_fn_return_type_cv_qual_change(const diff* dif)
2225 {
2226  const function_type_diff* fn_type_diff = is_function_type_diff(dif);
2227  if (!fn_type_diff)
2228  if (const function_decl_diff* fn_decl_diff = is_function_decl_diff(dif))
2229  fn_type_diff = fn_decl_diff->type_diff().get();
2230 
2231  if (!fn_type_diff)
2232  return false;
2233 
2234  const diff* return_type_diff = fn_type_diff->return_type_diff().get();
2235  return type_diff_has_typedef_cv_qual_change_only(return_type_diff);
2236 }
2237 
2238 /// Test if a function type or decl diff node carries a function
2239 /// parameter addition or removal.
2240 ///
2241 /// @param dif the diff node to consider. Note that if this is
2242 /// neither a function type nor decl diff node, the function returns
2243 /// false.
2244 ///
2245 /// @return true iff @p dif is a function decl or type diff node which
2246 /// carries a function parameter addition or removal.
2247 static bool
2248 has_added_or_removed_function_parameters(const diff *dif)
2249 {
2250  const function_type_diff *fn_type_diff = is_function_type_diff(dif);
2251  if (!fn_type_diff)
2252  if (const function_decl_diff* fn_decl_diff = is_function_decl_diff(dif))
2253  fn_type_diff = fn_decl_diff->type_diff().get();
2254 
2255  if (!fn_type_diff)
2256  return false;
2257 
2258  if (!(fn_type_diff->sorted_deleted_parms().empty()
2259  && fn_type_diff->sorted_added_parms().empty()))
2260  return true;
2261 
2262  return false;
2263 }
2264 
2265 /// Test if a diff node is a function diff node that carries either a
2266 /// return or a parameter type change that is deemed harmful.
2267 ///
2268 /// @param d the diff node to consider.
2269 ///
2270 /// @return the category of the change carried by @p or zero if
2271 /// doesn't carry any change.
2274 {
2275  const function_decl_diff* fn_decl_diff = nullptr;
2276  const function_type_diff* fn_type_diff = is_function_type_diff(d);
2277 
2278  if (!fn_type_diff)
2279  fn_decl_diff = is_function_decl_diff(d);
2280 
2281  if (!fn_decl_diff && !fn_type_diff)
2282  return NO_CHANGE_CATEGORY;
2283 
2284  diff_category category = NO_CHANGE_CATEGORY;
2285  if (fn_decl_diff)
2286  category = fn_decl_diff->get_local_category();
2287 
2288  if (is_harmful_category(category))
2289  return category;
2290 
2291  if (fn_decl_diff)
2292  fn_type_diff = fn_decl_diff->type_diff().get();
2293 
2294  if (!fn_type_diff)
2295  return NO_CHANGE_CATEGORY;
2296 
2297  diff_sptr return_type_diff = fn_type_diff->return_type_diff();
2298  if (return_type_diff && !has_void_to_non_void_change(return_type_diff))
2299  category = return_type_diff->get_local_category();
2300 
2301  if (is_harmful_category(category))
2302  return category;
2303 
2304  for (const auto& entry : fn_type_diff->subtype_changed_parms())
2305  {
2306  category = entry.second->get_local_category();
2307  if (is_harmful_category(category))
2308  return category;
2309  }
2310 
2311  return NO_CHANGE_CATEGORY;
2312 }
2313 
2314 /// Test if a diff node carries a change to the offset of a virtual
2315 /// function.
2316 ///
2317 /// @param d the diff node to consider.
2318 ///
2319 /// @return true iff @p carries a change to the offset of a virtual
2320 /// function.
2321 bool
2323 {
2324  const function_decl_diff* fn_diff = is_function_decl_diff(d);
2325  if (!fn_diff)
2326  return false;
2327 
2329  return true;
2330 
2331  return false;
2332 }
2333 
2334 /// Test if a diff node carries a change to the offset of a virtual
2335 /// function.
2336 ///
2337 /// @param d the diff node to consider.
2338 ///
2339 /// @return true iff @p carries a change to the offset of a virtual
2340 /// function.
2341 bool
2343 {return has_fn_with_virtual_offset_change(d.get());}
2344 
2345 
2346 /// Test if a diff node carries a harmful local change to a variable.
2347 ///
2348 /// @param d the diff node to consider.
2349 ///
2350 /// @return the @ref diff_category of the harmful local change or zero
2351 /// if the diff node carries no harmful local change.
2354 {
2355  const var_diff* vd = is_var_diff(d);
2357 
2359  return cat;
2360 
2361  cat = vd->get_local_category();
2362  if (is_harmful_category(cat))
2363  return cat;
2364 
2365  diff_sptr type_diff = vd->type_diff();
2366 
2367  cat = type_diff->get_local_category();
2368  if (is_harmful_category(cat))
2369  return cat;
2370 
2371  return NO_CHANGE_CATEGORY;
2372 }
2373 
2374 /// Test if diff node carries a harmful local change to a variable.
2375 ///
2376 /// @param d the diff node to consider.
2377 ///
2378 /// @return the @ref diff_category of the harmful local change or zero
2379 /// if the diff node carries no harmful local change.
2382 {return has_var_harmful_local_change(d.get());}
2383 
2384 /// Test if a diff node carries an incompatible ABI change.
2385 ///
2386 /// An incompatible ABI change is a harmful ABI change (i.e, one that
2387 /// cannot be filtered out) that definitely makes the new ABI
2388 /// incompatible with the previous one.
2389 ///
2390 /// @param d the diff node to consider.
2391 ///
2392 /// @return true iff @p d carries an incompatible ABI change.
2393 bool
2395 {
2399 }
2400 
2401 /// Test if a diff node carries an incompatible ABI change.
2402 ///
2403 /// An incompatible ABI change is a harmful ABI change (i.e, one that
2404 /// cannot be filtered out) that definitely makes the new ABI
2405 /// incompatible with the previous one.
2406 ///
2407 /// @param d the diff node to consider.
2408 ///
2409 /// @return true iff @p d carries an incompatible ABI change.
2410 bool
2412 {return has_incompatible_fn_or_var_change(d.get());}
2413 
2414 /// Test if a diff node carries a change where a type T is modified
2415 /// into an anonymous type T' of the same size which contains a data
2416 /// member of the same type as T.
2417 ///
2418 /// T and T' are thus said to be compatible.
2419 ///
2420 /// @param d the diff node to consider.
2421 ///
2422 /// @return true iff @p d carriesa change where a type T is modified
2423 /// into an anonymous type T' of the same size which contains a data
2424 /// member of the same type as T.
2425 bool
2428 
2429 /// Test if a diff node carries a change where a type T is modified
2430 /// into an anonymous type T' of the same size which contains a data
2431 /// member of the same type as T.
2432 ///
2433 /// T and T' are thus said to be compatible.
2434 ///
2435 /// @param d the diff node to consider.
2436 ///
2437 /// @return true iff @p d carriesa change where a type T is modified
2438 /// into an anonymous type T' of the same size which contains a data
2439 /// member of the same type as T.
2440 bool
2442 {
2443  type_base_sptr fs = is_type(d->first_subject());
2444  type_base_sptr ss = is_type(d->second_subject());
2445 
2447 }
2448 
2449 /// Test if a type 'F' is modified into an anonymous type 'S' of the
2450 /// same size which contains a data member of the same type as 'F'.
2451 ///
2452 /// F and S are thus said to be compatible.
2453 ///
2454 /// @param F the first type to consider.
2455 ///
2456 /// @param S the second version of type @p S to consider.
2457 ///
2458 /// @return true iff @p 'F' is modified into an anonymous type 'S' of
2459 /// the same size which contains a data member of the same type as
2460 /// 'F'.
2461 bool
2463  const type_base_sptr& s)
2464 
2465 {
2466  if (!f || !s)
2467  return false;
2468 
2469  if (is_anonymous_type(f) || !is_anonymous_type(s))
2470  return false;
2471 
2472  class_or_union_sptr second_cou = is_class_or_union_type(s);
2473  if (!second_cou)
2474  return false;
2475 
2476  if (f->get_size_in_bits() != second_cou->get_size_in_bits()
2477  || f->get_alignment_in_bits() != second_cou->get_alignment_in_bits())
2478  return false;
2479 
2480  string_decl_base_sptr_map non_anonymous_dms_in_second_class;
2482  non_anonymous_dms_in_second_class);
2483  for (const auto& entry : non_anonymous_dms_in_second_class)
2484  if (var_decl_sptr dm = is_data_member(entry.second))
2485  if (type_base_sptr t = dm->get_type())
2486  if (types_are_compatible(f, t))
2487  return true;
2488 
2489  return false;
2490 }
2491 
2492 /// Test if a diff node carries a change where a data member F is
2493 /// modified into an anonymous data member S that contains F at the
2494 /// same offset.
2495 ///
2496 /// F and S are thus said to be compatible.
2497 ///
2498 /// @param d the diff node to consider.
2499 ///
2500 /// @return true iff @p d carries a change where a data member F is
2501 /// modified into an anonymous data member S that contains F at the
2502 /// same offset.
2503 bool
2505 {
2509 }
2510 
2511 /// Test if a diff node carries a change where a data member F is
2512 /// modified into an anonymous data member S that contains F at the
2513 /// same offset.
2514 ///
2515 /// F and S are thus said to be compatible.
2516 ///
2517 /// @param d the diff node to consider.
2518 ///
2519 /// @return true iff @p d carries a change where a data member F is
2520 /// modified into an anonymous data member S that contains F at the
2521 /// same offset.
2522 bool
2525 
2526 /// Test if a data member F is modified into an anonymous data member
2527 /// S that contains F at the same offset.
2528 ///
2529 /// F and S are thus said to be compatible.
2530 ///
2531 /// @param f the first data member to consider.
2532 ///
2533 /// @param s the second data member to consider.
2534 ///
2535 /// @return true iff @p f is modified into @p s that contains F at the
2536 /// same offset.
2537 bool
2539  const decl_base_sptr& s)
2540 {
2541  var_decl_sptr f_dm = is_data_member(f);
2542  var_decl_sptr s_dm = is_data_member(s);
2543 
2544  if (!f_dm || !s_dm)
2545  return false;
2546 
2547  if (is_anonymous_data_member(f_dm)
2548  || !is_anonymous_data_member(s_dm)
2550  return false;
2551 
2552  string_decl_base_sptr_map non_anonymous_dms_in_second_dm;
2553  class_or_union_sptr cou = anonymous_data_member_to_class_or_union(s_dm);
2554  ABG_ASSERT(cou);
2555  collect_non_anonymous_data_members(cou, non_anonymous_dms_in_second_dm);
2556 
2557  for (const auto& entry : non_anonymous_dms_in_second_dm)
2558  if (var_decl_sptr dm = is_data_member(entry.second))
2559  if (types_are_compatible(f_dm->get_type(), dm->get_type()))
2560  return true;
2561 
2562  return false;
2563 }
2564 
2565 /// Test if a variable diff node carries a CV qualifier change on its type.
2566 ///
2567 /// @param dif the diff node to consider. Note that if it's not of
2568 /// var_diff type, the function returns false.
2569 ///
2570 /// @return true iff the @p dif carries a CV qualifier change on its
2571 /// type.
2572 static bool
2573 has_var_type_cv_qual_change(const diff* dif)
2574 {
2575  const var_diff *var_dif = is_var_diff(dif);
2576  if (!var_dif)
2577  return false;
2578 
2579  diff *type_dif = var_dif->type_diff().get();
2580  if (!type_dif)
2581  return false;
2582 
2583  return type_diff_has_typedef_cv_qual_change_only(type_dif);
2584 }
2585 
2586 /// Test if a type change is a "void pointer to pointer" change.
2587 ///
2588 /// @param f the first version of the type.
2589 ///
2590 /// @param s the second version of the type.
2591 ///
2592 /// @return true iff the type change is a "void pointer to pointer"
2593 /// change.
2594 static bool
2595 is_void_ptr_to_ptr(const type_base* f, const type_base* s)
2596 {
2598  && is_pointer_type(s)
2600  && ((f->get_size_in_bits() == 0)
2601  || (f->get_size_in_bits() == s->get_size_in_bits())))
2602  return true;
2603 
2604  return false;
2605 }
2606 
2607 /// Test if a pair of types represents a "void-to-non-void" change.
2608 ///
2609 /// The test looks through potential typedefs.
2610 ///
2611 /// @param f the first type to consider.
2612 ///
2613 /// @param s the second type to consider.
2614 ///
2615 /// @return true iff the pair of types represents a void-to-non-void
2616 /// type change.
2617 static bool
2618 is_void_to_non_void(const type_base* f, const type_base* s)
2619 {
2620  f = peel_typedef_type(f);
2621  s = peel_typedef_type(s);
2622 
2623  if (!f || !s)
2624  return false;
2625 
2626  const environment& env = f->get_environment();
2627  if (env.is_void_type(f) && !env.is_void_type(s))
2628  return true;
2629 
2630  return false;
2631 }
2632 
2633 /// Test if a pair of types represents a "void-to-non-void" change.
2634 ///
2635 /// The test looks through potential typedefs.
2636 ///
2637 /// @param f the first type to consider.
2638 ///
2639 /// @param s the second type to consider.
2640 ///
2641 /// @return true iff the pair of types represents a void-to-non-void
2642 /// type change.
2643 static bool
2644 is_void_to_non_void(const type_base_sptr& f, const type_base_sptr s)
2645 {return is_void_to_non_void(f.get(), s.get());}
2646 
2647 /// Test if a diff node carries a "void-to-non-void" type change
2648 ///
2649 /// The test looks through potential typedefs.
2650 ///
2651 /// @param f the first type to consider.
2652 ///
2653 /// @param s the second type to consider.
2654 ///
2655 /// @return true iff the pair of types represents a void-to-non-void
2656 /// type change.
2657 bool
2659 {
2660  type_base_sptr f = is_type(d->first_subject());
2661  type_base_sptr s = is_type(d->second_subject());
2662 
2663  return is_void_to_non_void(f, s);
2664 }
2665 
2666 /// Test if a diff node carries a "void-to-non-void" type change
2667 ///
2668 /// The test looks through potential typedefs.
2669 ///
2670 /// @param f the first type to consider.
2671 ///
2672 /// @param s the second type to consider.
2673 ///
2674 /// @return true iff the pair of types represents a void-to-non-void
2675 /// type change.
2676 bool
2678 {return has_void_to_non_void_change(d.get());}
2679 
2680 /// Test if a diff node carries a void* to pointer type change.
2681 ///
2682 /// Note that this function looks through typedef and qualifier types
2683 /// to find the void pointer.
2684 ///
2685 /// @param dif the diff node to consider.
2686 ///
2687 /// @return true iff @p dif carries a void* to pointer type change.
2688 bool
2690 {
2691  dif = peel_typedef_diff(dif);
2692 
2693  if (const distinct_diff *d = is_distinct_diff(dif))
2694  {
2695  const type_base *f = is_type(d->first().get());
2696  const type_base *s = is_type(d->second().get());
2697 
2700 
2701  if (is_void_ptr_to_ptr(f, s) || is_void_ptr_to_ptr(s, f))
2702  return true;
2703  }
2704  else if (const pointer_diff *d = is_pointer_diff(dif))
2705  {
2706  const type_base *f = is_type(d->first_pointer()).get();
2707  const type_base *s = is_type(d->second_pointer()).get();
2708 
2711 
2712  if (is_void_ptr_to_ptr(f, s) || is_void_ptr_to_ptr(s, f))
2713  return true;
2714  }
2715  else if (const qualified_type_diff *d = is_qualified_type_diff(dif))
2716  {
2717  const type_base *f = is_type(d->first_qualified_type()).get();
2718  const type_base *s = is_type(d->second_qualified_type()).get();
2719 
2722 
2723  if (is_void_ptr_to_ptr(f, s) || is_void_ptr_to_ptr(s, f))
2724  return true;
2725  }
2726 
2727  return false;
2728 }
2729 
2730 /// Test if a diff node carries a benign change to the size of a
2731 /// variable of type array.
2732 ///
2733 /// A benign size change is a change in size (from or to infinite) of
2734 /// the array as expressed by the debug info, but when the *ELF* size
2735 /// (what really matters) of the variable object hasn't changed. This
2736 /// happens when the debug info emitter did have trouble figuring out
2737 /// the actual size of the array.
2738 ///
2739 /// @param dif the diff node to consider.
2740 ///
2741 /// @return true iff @p dif contains the benign array type size change.
2742 bool
2744 {
2746 }
2747 
2748 /// Test if a union diff node does have changes that don't impact its
2749 /// size.
2750 ///
2751 /// @param d the union diff node to consider.
2752 ///
2753 /// @return true iff @p d is a diff node which has changes that don't
2754 /// impact its size.
2755 bool
2757 {
2758  if (is_union_diff(d)
2759  && d->has_changes()
2760  && !has_type_size_change(d))
2761  return true;
2762 
2763  return false;
2764 }
2765 
2766 /// Test if a diff node carries a change that is categorized as
2767 /// "harmful".
2768 ///
2769 /// A harmful change is a change that is not harmless. OK, that
2770 /// smells bit like a tasteless tautology, but bear with me please.
2771 ///
2772 /// A harmless change is a change that should be filtered out by
2773 /// default to avoid unnecessarily cluttering the change report.
2774 ///
2775 /// A harmful change is thus a change that SHOULD NOT be filtered out
2776 /// by default because it CAN represent an incompatible ABI change.
2777 ///
2778 /// An incompatbile ABI change is a harmful change that makes the new
2779 /// ABI incompatible with the previous one.
2780 ///
2781 /// @return the category of the harmful changes carried by the diff
2782 /// node or zero if the change carries no harmful change.
2783 static diff_category
2784 has_harmful_change(const diff* d)
2785 {
2786  diff_category category = NO_CHANGE_CATEGORY;
2787  decl_base_sptr f = is_decl(d->first_subject()),
2788  s = is_decl(d->second_subject());
2789 
2790  // Detect size or offset changes as well as data member addition
2791  // or removal.
2794  && (has_type_size_change_with_impact(d)
2795  || type_has_offset_changes(f, s)
2796  || data_member_offset_changed(f, s)
2797  || non_static_data_member_type_size_changed_with_impact(f, s)
2798  || non_static_data_member_added_or_removed_with_impact(d)
2799  || base_classes_removed(d)
2800  || has_harmful_enum_change(d)
2801  || crc_changed(d)
2802  || namespace_changed(d)))
2803  category |= SIZE_OR_OFFSET_CHANGE_CATEGORY;
2804 
2805  if (has_virtual_mem_fn_change(d))
2806  category |= VIRTUAL_MEMBER_CHANGE_CATEGORY;
2807 
2809  category |= REFERENCE_LVALUENESS_CHANGE_CATEGORY;
2810 
2811  if (has_added_or_removed_function_parameters(d))
2813 
2814  if (is_non_compatible_distinct_change(d))
2816 
2817  if (has_harmful_name_change(d))
2819 
2820  return category;
2821 }
2822 
2823 /// Detect if the changes carried by a given diff node are deemed
2824 /// harmless and do categorize the diff node accordingly.
2825 ///
2826 /// A harmless change is a change that ought to be filtered out by
2827 /// default from the change report. Filtering out harmless changes is
2828 /// to avoid unnecessarily cluttering the change report.
2829 ///
2830 /// A change is not harmless is a harmful node. Note that harmful
2831 /// diff nodes are categorized by @ref categorize_harmful_diff_node.
2832 ///
2833 /// @param d the diff node being visited.
2834 ///
2835 /// @param pre this is true iff the node is being visited *before* the
2836 /// children nodes of @p d.
2837 ///
2838 /// @return true iff the traversal shall keep going after the
2839 /// completion of this function.
2840 static bool
2841 categorize_harmless_diff_node(diff *d, bool pre)
2842 {
2843  if (!d->has_changes())
2844  return true;
2845 
2846  if (pre)
2847  {
2848  diff_category category = NO_CHANGE_CATEGORY;
2849 
2850  decl_base_sptr f = is_decl(d->first_subject()),
2851  s = is_decl(d->second_subject());
2852 
2856 
2857  if (access_changed(f, s))
2858  category |= ACCESS_CHANGE_CATEGORY;
2859 
2860  if (is_compatible_change(f, s))
2861  category |= COMPATIBLE_TYPE_CHANGE_CATEGORY;
2862 
2863  if (has_harmless_name_change(f, s, d->context())
2864  || class_diff_has_harmless_odr_violation_change(d))
2866 
2868  || class_diff_has_only_harmless_changes(d))
2870 
2871  if (has_non_virtual_mem_fn_change(d))
2873 
2874  if (static_data_member_added_or_removed(d)
2875  || static_data_member_type_size_changed(f, s))
2877 
2880 
2881  if (has_harmless_enum_change(d))
2882  category |= HARMLESS_ENUM_CHANGE_CATEGORY;
2883 
2884  if (function_name_changed_but_not_symbol(d))
2886 
2887  if (has_fn_parm_type_top_cv_qual_change(d))
2889 
2890  if (has_fn_parm_type_cv_qual_change(d))
2891  category |= FN_PARM_TYPE_CV_CHANGE_CATEGORY;
2892 
2893  if (has_fn_return_type_cv_qual_change(d))
2895 
2896  if (has_var_type_cv_qual_change(d))
2897  category |= VAR_TYPE_CV_CHANGE_CATEGORY;
2898 
2900  category |= VOID_PTR_TO_PTR_CHANGE_CATEGORY;
2901 
2904 
2905  if (category)
2906  {
2908  // Also update the category of the canonical node.
2909  if (diff * canonical = d->get_canonical_diff())
2910  canonical->add_to_local_and_inherited_categories(category);
2911  }
2912  }
2913 
2914  return true;
2915 }
2916 
2917 /// Detect if the changes carried by a given diff node are deemed
2918 /// harmful and do categorize the diff node accordingly.
2919 ///
2920 /// A harmful change is a change that is not harmless. OK, that
2921 /// smells bit like a tasteless tautology, but bear with me please.
2922 ///
2923 /// A harmless change is a change that should be filtered out by
2924 /// default to avoid unnecessarily cluttering the change report.
2925 ///
2926 /// A harmful change is thus a change that SHOULD NOT be filtered out
2927 /// by default because it CAN represent an incompatible ABI change.
2928 ///
2929 /// An incompatbile ABI change is a harmful change that makes the new
2930 /// ABI incompatible with the previous one.
2931 ///
2932 /// Note that harmless diff nodes are categorized by
2933 /// @ref categorize_harmless_diff_node.
2934 ///
2935 /// @param d the diff node being visited.
2936 ///
2937 /// @param pre this is true iff the node is being visited *before* the
2938 /// children nodes of @p d.
2939 ///
2940 /// @return true iff the traversal shall keep going after the
2941 /// completion of this function.
2942 static bool
2943 categorize_harmful_diff_node(diff *d, bool pre)
2944 {
2945  if (!d->has_changes())
2946  return true;
2947 
2948  if (pre)
2949  {
2950  diff_category category = NO_CHANGE_CATEGORY;
2951  category = has_harmful_change(d);
2952 
2953  if (category)
2954  {
2955  d->add_to_local_and_inherited_categories(category);
2956  // Update the category of the canonical diff node too.
2957  if (diff * canonical = d->get_canonical_diff())
2958  canonical->add_to_local_and_inherited_categories(category);
2959  }
2960  }
2961 
2962  return true;
2963 }
2964 
2965 /// The visiting code of the harmless_harmful_filter.
2966 ///
2967 /// @param d the diff node being visited.
2968 ///
2969 /// @param pre this is true iff the node is being visited *before* the
2970 /// children nodes of @p d.
2971 ///
2972 /// @return true iff the traversal shall keep going after the
2973 /// completion of this function.
2974 bool
2975 harmless_harmful_filter::visit(diff* d, bool pre)
2976 {
2977  return (categorize_harmless_diff_node(d, pre)
2978  && categorize_harmful_diff_node(d, pre));
2979 }
2980 
2981 /// Part of the visiting code of the harmless_harmful_filter.
2982 ///
2983 /// This function is called after the visiting of a given diff node.
2984 /// Note that when this function is called, the visiting might not
2985 /// have taken place *if* the node (or an equivalent node) has already
2986 /// been visited.
2987 ///
2988 /// @param d the diff node that has either been visited or skipped
2989 /// (because it has already been visited during this traversing).
2990 void
2991 harmless_harmful_filter::visit_end(diff* d)
2992 {
2993  if (d->context()->diff_has_been_visited(d))
2994  {
2995  // This node or one of its equivalent node has already been
2996  // visited. That means at this moment,
2997  // harmless_harmful_filter::visit() has *not* been called prior
2998  // to this harmless_harmful_filter::visit_end() is called. In
2999  // other words, only harmless_harmful_filter::visit_begin() and
3000  // harmless_harmful_filter::visit_end() are called.
3001  //
3002  // So let's update the category of this diff node from its
3003  // canonical node.
3004  if (diff* c = d->get_canonical_diff())
3005  d->add_to_local_and_inherited_categories(c->get_local_category());
3006  }
3007 }
3008 } // end namespace filtering
3009 } // end namespace comparison
3010 } // end namespace abigail
bool has_harmless_enum_to_int_change(const diff *diff)
Test if a diff node carries a harmless change of an enum into an integer (or vice-versa).
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:10807
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:6641
This means the diff node (or at least one of its descendant nodes) carries a change involving two com...
The base type of all declarations.
Definition: abg-ir.h:1584
const string_member_function_sptr_map & deleted_member_fns() const
This header declares filters for the diff trees resulting from comparing ABI Corpora.
The abstraction of a change between two ABI artifacts, a.k.a an artifact change.
const pointer_diff * is_pointer_diff(const diff *diff)
Test if a diff node is about differences between two pointers.
diff_category has_fn_return_or_parm_harmful_change(const diff *d)
Test if a diff node is a function diff node that carries either a return or a parameter type change t...
Abstraction of a diff between two qualified types.
class_decl_sptr second_class_decl() const
Getter of the second class involved in the diff.
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:10909
const string_fn_parm_diff_sptr_map & subtype_changed_parms() const
Getter for the map of function parameter changes of the current diff.
var_decl_sptr first_var() const
Getter for the first var_decl of the diff.
diff_category get_local_category() const
Getter for the local category of the current diff tree node.
bool is_data_member(const var_decl &v)
Test if a var_decl is a data member.
Definition: abg-ir.cc:5613
An abstraction of a diff between entities that are of a different kind (disctinct).
Utilities to ease the wrapping of C types into std::shared_ptr.
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:6368
A diff node in this category is a function (or function type) with at least one parameter added or re...
An abstraction helper for type declarations.
Definition: abg-ir.h:2002
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:5515
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...
shared_ptr< diff_context > diff_context_sptr
Convenience typedef for a shared pointer of diff_context.
Definition: abg-fwd.h:67
ssize_t get_member_function_vtable_offset(const function_decl &f)
Get the vtable offset of a member function.
Definition: abg-ir.cc:6578
const var_diff * is_var_diff(const diff *diff)
Test if a diff node is about differences between variables.
A non-compatible name change between two types.
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:253
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:5417
uint64_t get_absolute_data_member_offset(const var_decl &m)
Get the absolute offset of a data member.
Definition: abg-ir.cc:6273
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 ...
const fn_parm_diff * is_fn_parm_diff(const diff *diff)
Test if a diff node is about differences between two function parameters.
class_or_union * is_class_or_union_type(const type_or_decl_base *t)
Test if a type is a class_or_union.
Definition: abg-ir.cc:11396
const type_decl_diff * is_diff_of_basic_type(const diff *d)
Test if a diff node represents a diff between two basic types.
Abstracts a class declaration.
Definition: abg-ir.h:4173
Abstraction of a diff between two function_decl.
A diff node in this category is a function parameter type which top cv-qualifiers change...
bool has_void_to_non_void_change(const diff *d)
Test if a diff node carries a "void-to-non-void" type change.
bool is_type_to_compatible_anonymous_type_change(const diff_sptr &d)
Test if a diff node carries a change where a type T is modified into an anonymous type T' of the same...
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
const class_diff * is_class_diff(const diff *diff)
Test if a diff node is a class_diff node.
bool has_void_ptr_to_ptr_change(const diff *dif)
Test if a diff node carries a void* to pointer type change.
class_or_union * anonymous_data_member_to_class_or_union(const var_decl *d)
Get the class_or_union type of a given anonymous data member.
Definition: abg-ir.cc:6030
Abstraction of a diff between two enums.
unordered_map< string, decl_base_sptr > string_decl_base_sptr_map
Convenience typedef for a map which key is a string and which value is a decl_base_sptr.
Definition: abg-fwd.h:157
diff_category has_var_harmful_local_change(const diff *d)
Test if a diff node carries a harmful local change to a variable.
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:12059
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.
Abstracts a declaration for an enum type.
Definition: abg-ir.h:2784
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:6184
bool get_is_declaration_only() const
Test if a decl_base is a declaration-only decl.
Definition: abg-ir.cc:4996
bool has_strict_fam_conversion(const class_decl_sptr &first, const class_decl_sptr &second)
Test if a class with a fake flexible data member got changed into a class with a real fexible data me...
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_benign_array_of_unknown_size_change(const diff *dif)
Test if a diff node carries a benign change to the size of a variable of type array.
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:266
decl_base * is_decl(const type_or_decl_base *d)
Test if an ABI artifact is a declaration.
Definition: abg-ir.cc:10747
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:11100
shared_ptr< parameter > parameter_sptr
Convenience typedef for a shared pointer on a function_decl::parameter.
Definition: abg-ir.h:3183
virtual size_t get_size_in_bits() const
Getter for the size of the type.
Definition: abg-ir.cc:16492
var_decl_sptr second_var() const
Getter for the second var_decl of the diff.
This contains the private implementation of the suppression engine of libabigail. ...
Abstraction of a diff between two function parameters.
const enum_type_decl * is_compatible_with_enum_type(const type_base *t)
Test if a type is an enum. This function looks through typedefs.
Definition: abg-ir.cc:11051
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...
diff_sptr compute_diff(const decl_base_sptr first, const decl_base_sptr second, diff_context_sptr ctxt)
Compute the difference between two decls. The decls can represent either type declarations, or non-type declaration.
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...
class_decl_sptr first_class_decl() const
var_decl_sptr has_fake_flexible_array_data_member(const class_decl &klass)
Test if the last data member of a class is an array with one element.
Definition: abg-ir.cc:11276
The base type of class_decl and union_decl.
Definition: abg-ir.h:3976
The abstraction of a diff between two references.
const decl_base * get_type_declaration(const type_base *t)
Get the declaration for a given type.
Definition: abg-ir.cc:10229
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:7264
diff * get_canonical_diff() const
Getter for the canonical diff of the current instance of diff.
bool has_lvalue_reference_ness_change(const diff *dif)
Test if a diff node carries a change where an lvalue reference changed into a rvalue reference...
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:21550
The abstraction of a diff between two pointers.
bool is_data_member_to_compatible_anonymous_dm_change(const diff *d)
Test if a diff node carries a change where a data member F is modified into an anonymous data member ...
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:11967
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:924
#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:1743
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:7358
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:11948
array_type_def * is_array_type(const type_or_decl_base *type, bool look_through_qualifiers)
Test if a type is an array_type_def.
Definition: abg-ir.cc:12123
const type_base_sptr get_type() const
Getter of the type of the variable.
Definition: abg-ir.cc:21419
const string_member_function_sptr_map & inserted_member_fns() const
std::pair< enum_type_decl::enumerator, enum_type_decl::enumerator > changed_enumerator
Convenience typedef for a changed enumerator. The first element of the pair is the old enumerator and...
bool is_anonymous_type(const type_base *t)
Test whether a declaration is a type.
Definition: abg-ir.cc:10858
diff_sptr type_diff() const
Getter for the diff representing the changes on the type of the function parameter involved in the cu...
bool has_fn_with_virtual_offset_change(const diff *d)
Test if a diff node carries a change to the offset of a virtual function.
bool has_incompatible_fn_or_var_change(const diff *d)
Test if a diff node carries an incompatible ABI change.
bool collect_non_anonymous_data_members(const class_or_union *cou, string_decl_base_sptr_map &dms)
Collect all the non-anonymous data members of a class or union type.
Definition: abg-ir.cc:5815
shared_ptr< enum_type_decl > enum_type_decl_sptr
Convenience typedef for shared pointer to a enum_type_decl.
Definition: abg-fwd.h:172
shared_ptr< class_decl > class_decl_sptr
Convenience typedef for a shared pointer on a class_decl.
Definition: abg-fwd.h:190
virtual void get_qualified_name(interned_string &qualified_name, bool internal=false) const
Compute the qualified name of the decl.
Definition: abg-ir.cc:4823
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:8835
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:11838
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.
virtual bool traverse(ir_node_visitor &v)
This implements the ir_traversable_base::traverse pure virtual function.
Definition: abg-ir.cc:5236
bool is_harmful_category(diff_category c)
Test if an instance of diff_category (a category bit-field) is harmful or not.
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.
diff_sptr type_diff() const
Getter for the diff of the types of the 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:5575
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.
bool has_harmful_name_change(const decl_base_sptr &f, const decl_base_sptr &s, const diff_context_sptr &ctxt)
Test if two decls represent a harmful name change.
This means that a diff node in the sub-tree carries an addition or removal of a non-virtual member fu...
reference_type_def_sptr second_reference() const
Getter for the second reference of the diff.
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...
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:7409
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.
const class_decl * is_compatible_with_class_type(const type_base *t)
Test if a type is a class. This function looks through typedefs.
Definition: abg-ir.cc:11118
bool types_are_compatible(const type_base_sptr type1, const type_base_sptr type2)
Test if two types are equal modulo a typedef or CV qualifiers.
Definition: abg-ir.cc:10397
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:11918
const data_members & get_data_members() const
Get the data members of this class_or_union.
Definition: abg-ir.cc:24233
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.
reference_type_def_sptr first_reference() const
Getter for the first reference of the diff.
bool is_anonymous_data_member(const decl_base &d)
Test if a decl is an anonymous data member.
Definition: abg-ir.cc:5872
access_specifier
Access specifier for class members.
Definition: abg-ir.h:916
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.
This means that a diff node in the sub-tree carries a harmless union or class change.
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.
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:75
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_harmless_name_change(const decl_base_sptr &f, const decl_base_sptr &s, const diff_context_sptr &ctxt)
Test if two decls represents a harmless name change.
const distinct_diff * is_distinct_diff(const diff *diff)
Test if a diff node is about differences between two diff nodes of different kinds.
var_decl_sptr has_flexible_array_data_member(const class_decl &klass)
Test if the last data member of a class is an array with non-finite data member.
Definition: abg-ir.cc:11206
A change between two non-compatible types of different kinds.
shared_ptr< array_type_def > array_type_def_sptr
Convenience typedef for a shared pointer on a array_type_def.
Definition: abg-fwd.h:241
bool equals_modulo_cv_qualifier(const array_type_def *l, const array_type_def *r)
Test if two array types are equals modulo CV qualifiers.
Definition: abg-ir.cc:19932
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...
const pointer_type_def * is_pointer_type(const type_or_decl_base *t, bool look_through_qualifiers)
Test whether a type is a pointer_type_def.
Definition: abg-ir.cc:11479
Abstraction of a diff between two function types.
type_base_sptr peel_typedef_type(const type_base_sptr &type)
Return the leaf underlying type node of a typedef_decl node.
Definition: abg-ir.cc:7063
shared_ptr< filter_base > filter_base_sptr
Convenience typedef for a shared pointer to filter_base.
const diff_sptr return_type_diff() const
Getter for the diff of the return types of the two function types of the current diff.
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:11750
const diff_context_sptr context() const
Getter of the context of the current diff.
const enum_type_decl_sptr second_enum() const
CV
Bit field values representing the cv qualifiers of the underlying type.
Definition: abg-ir.h:2254
This means that a diff node in the sub-tree carries an addition or removal of a static data member...