libabigail
abg-comp-filter.cc
Go to the documentation of this file.
1 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
2 // -*- Mode: C++ -*-
3 //
4 // Copyright (C) 2013-2023 Red Hat, Inc.
5 //
6 // Author: Dodji Seketeli
7 
8 /// @file
9 ///
10 /// This file contains definitions of diff objects filtering
11 /// facilities.
12 
13 #include "abg-internal.h"
14 #include <memory>
15 // <headers defining libabigail's API go under here>
16 ABG_BEGIN_EXPORT_DECLARATIONS
17 
18 #include "abg-comp-filter.h"
19 #include "abg-tools-utils.h"
20 
22 // </headers defining libabigail's API>
23 
24 namespace abigail
25 {
26 namespace comparison
27 {
28 namespace filtering
29 {
30 
31 static bool
32 has_offset_changes(const string_decl_base_sptr_map& f_data_members,
33  const string_decl_base_sptr_map& s_data_members);
34 
35 using std::dynamic_pointer_cast;
36 
37 /// Walk the diff sub-trees of a a @ref corpus_diff and apply a filter
38 /// to the nodes visted. The filter categorizes each node, assigning
39 /// it into one or several categories.
40 ///
41 /// @param filter the filter to apply to the diff nodes
42 ///
43 /// @param d the corpus diff to apply the filter to.
44 void
46 {
47  bool s = d->context()->visiting_a_node_twice_is_forbidden();
48  d->context()->forbid_visiting_a_node_twice(true);
49  d->traverse(filter);
50  d->context()->forbid_visiting_a_node_twice(s);
51 }
52 
53 /// Walk a diff sub-tree and apply a filter to the nodes visted. The
54 /// filter categorizes each node, assigning it into one or several
55 /// categories.
56 ///
57 /// Note that this function makes sure to avoid visiting a node (or
58 /// any other node equivalent to it) more than once. This helps avoid
59 /// infinite loops for diff trees that involve type changes that
60 /// reference themselves.
61 ///
62 /// @param filter the filter to apply to the nodes of the sub-tree.
63 ///
64 /// @param d the diff sub-tree to walk and apply the filter to.
65 void
67 {
68  bool s = d->context()->visiting_a_node_twice_is_forbidden();
69  d->context()->forbid_visiting_a_node_twice(true);
70  d->context()->forget_visited_diffs();
71  d->traverse(filter);
72  d->context()->forbid_visiting_a_node_twice(s);
73 }
74 
75 /// Walk a diff sub-tree and apply a filter to the nodes visted. The
76 /// filter categorizes each node, assigning it into one or several
77 /// categories.
78 ///
79 /// Note that this function makes sure to avoid visiting a node (or
80 /// any other node equivalent to it) more than once. This helps avoid
81 /// infinite loops for diff trees that involve type changes that
82 /// reference themselves.
83 ///
84 /// @param filter the filter to apply to the nodes of the sub-tree.
85 ///
86 /// @param d the diff sub-tree to walk and apply the filter to.
87 void
89 {apply_filter(*filter, d);}
90 
91 /// Test if there is a class that is declaration-only among the two
92 /// classes in parameter.
93 ///
94 /// @param class1 the first class to consider.
95 ///
96 /// @param class2 the second class to consider.
97 ///
98 /// @return true if either classes are declaration-only, false
99 /// otherwise.
100 static bool
101 there_is_a_decl_only_class(const class_decl_sptr& class1,
102  const class_decl_sptr& class2)
103 {
104  if ((class1 && class1->get_is_declaration_only())
105  || (class2 && class2->get_is_declaration_only()))
106  return true;
107  return false;
108 }
109 
110 /// Test if there is a enum that is declaration-only among the two
111 /// enums in parameter.
112 ///
113 /// @param enum1 the first enum to consider.
114 ///
115 /// @param enum2 the second enum to consider.
116 ///
117 /// @return true if either enums are declaration-only, false
118 /// otherwise.
119 static bool
120 there_is_a_decl_only_enum(const enum_type_decl_sptr& enum1,
121  const enum_type_decl_sptr& enum2)
122 {
123  if ((enum1 && enum1->get_is_declaration_only())
124  || (enum2 && enum2->get_is_declaration_only()))
125  return true;
126  return false;
127 }
128 
129 /// Test if the diff involves a declaration-only class.
130 ///
131 /// @param diff the class diff to consider.
132 ///
133 /// @return true iff the diff involves a declaration-only class.
134 static bool
135 diff_involves_decl_only_class(const class_diff* diff)
136 {
137  if (diff && there_is_a_decl_only_class(diff->first_class_decl(),
138  diff->second_class_decl()))
139  return true;
140  return false;
141 }
142 
143 /// Tests if the size of a given type changed.
144 ///
145 /// @param f the first version of the type to consider.
146 ///
147 /// @param s the second version of the type to consider.
148 ///
149 /// @return true if the type size changed, false otherwise.
150 static bool
151 type_size_changed(const type_base_sptr f, const type_base_sptr s)
152 {
153  if (!f || !s
154  || f->get_size_in_bits() == 0
155  || s->get_size_in_bits() == 0
156  || there_is_a_decl_only_class(is_compatible_with_class_type(f),
158  || there_is_a_decl_only_enum(is_compatible_with_enum_type(f),
160  return false;
161 
162  return f->get_size_in_bits() != s->get_size_in_bits();
163 }
164 
165 /// Detect if a type has offset changes.
166 ///
167 /// The type must be either a class or a union. This function returns
168 /// true iff the type has a data member which has an offset change.
169 ///
170 /// @param f the first version of the type to consider.
171 ///
172 /// @param s the second version of the type to consider.
173 ///
174 /// @return true iff the type has a data member which has an offset
175 /// change.
176 static bool
177 type_has_offset_changes(const type_base_sptr f, const type_base_sptr s)
178 {
179  if (!f || !s)
180  return false;
181 
182  class_or_union_sptr first = is_class_or_union_type(f);
183  class_or_union_sptr second = is_class_or_union_type(s);
184  if (!first || !second)
185  return false;
186 
187  // collect the data members
188  string_decl_base_sptr_map f_data_members, s_data_members;
189  collect_non_anonymous_data_members(first, f_data_members);
190  collect_non_anonymous_data_members(second, s_data_members);
191 
192  // detect offset changes
193  if (has_offset_changes(f_data_members, s_data_members))
194  return true;
195 
196  return false;
197 }
198 
199 /// Detect if a type has offset changes.
200 ///
201 /// The type must be either a class or a union. This function returns
202 /// true iff the type has a data member which has an offset change.
203 ///
204 /// @param f the first version of the type to consider.
205 ///
206 /// @param s the second version of the type to consider.
207 ///
208 /// @return true iff the type has a data member which has an offset
209 /// change.
210 static bool
211 type_has_offset_changes(const decl_base_sptr f, const decl_base_sptr s)
212 {return type_has_offset_changes(is_type(f), is_type(s));}
213 
214 /// Tests if the size of a given type changed.
215 ///
216 /// @param f the declaration of the first version of the type to
217 /// consider.
218 ///
219 /// @param s the declaration of the second version of the type to
220 /// consider.
221 ///
222 /// @return true if the type size changed, false otherwise.
223 static bool
224 type_size_changed(const decl_base_sptr f, const decl_base_sptr s)
225 {return type_size_changed(is_type(f), is_type(s));}
226 
227 /// Test if a given type diff node carries a type size change.
228 ///
229 /// @param diff the diff tree node to test.
230 ///
231 /// @return true if @p diff carries a type size change.
232 static bool
233 has_type_size_change(const diff* diff)
234 {
235  if (!diff)
236  return false;
237 
238  if (const fn_parm_diff* fn_parm_d = is_fn_parm_diff(diff))
239  diff = fn_parm_d->type_diff().get();
240 
241  type_base_sptr f = is_type(diff->first_subject()),
242  s = is_type(diff->second_subject());
243 
244  if (!f || !s)
245  return false;
246 
247  return type_size_changed(f, s);
248 }
249 
250 /// Find a data member that is at a given offset.
251 ///
252 /// @param data_members the set of data member to consider.
253 ///
254 /// @param the offset to consider.
255 ///
256 /// @return the data member found at offset @p offset of nil if none
257 /// was found with that offset;
258 static var_decl_sptr
259 find_data_member_at_offset(const string_decl_base_sptr_map& data_members,
260  unsigned offset)
261 {
262  for (auto e : data_members)
263  {
264  var_decl_sptr dm = is_var_decl(e.second);
265  ABG_ASSERT(dm);
266  unsigned off = get_absolute_data_member_offset(dm);
267  if (offset == off)
268  return dm;
269  }
270  return var_decl_sptr();
271 }
272 
273 /// Test if a set of data members contains at least one data member
274 /// that has an offset change.
275 ///
276 /// @param f_data_members the first version of data members to
277 /// consider.
278 ///
279 /// @param s_data_members the second version of data members to
280 /// consider.
281 ///
282 /// @return true iff there is at least one data member which has an
283 /// offset change between the first version of data members and the
284 /// second version.
285 static bool
286 has_offset_changes(const string_decl_base_sptr_map& f_data_members,
287  const string_decl_base_sptr_map& s_data_members)
288 {
289  // Compare the offsets of the data members
290  for (auto entry : f_data_members)
291  {
292  var_decl_sptr f_member = is_var_decl(entry.second);
293  ABG_ASSERT(f_member);
294  unsigned f_offset = get_absolute_data_member_offset(f_member);
295  auto i = s_data_members.find(entry.first);
296  var_decl_sptr s_member;
297  if (i == s_data_members.end())
298  {
299  s_member = find_data_member_at_offset(s_data_members, f_offset);
300  if (!s_member)
301  // A data member was suppressed; that's bad; let's consider
302  // that as an offset change.
303  return true;
304  }
305 
306  if (!s_member)
307  s_member = is_var_decl(i->second);
308  ABG_ASSERT(s_member);
309  unsigned s_offset = get_absolute_data_member_offset(s_member);
310  if (f_offset != s_offset)
311  return true;
312  }
313  return false;
314 }
315 
316 /// Test if a set of data members contains at least one data member
317 /// that has a sub-type change.
318 ///
319 /// @param f_data_members the first version of data members to
320 /// consider.
321 ///
322 /// @param s_data_members the second version of data members to
323 /// consider.
324 ///
325 /// @return true iff there is at least one data member which has a
326 /// sub-type change between the first version of data members and the
327 /// second version.
328 static bool
329 has_subtype_changes(const string_decl_base_sptr_map& f_data_members,
330  const string_decl_base_sptr_map& s_data_members,
331  diff_context_sptr ctxt)
332 {
333  // Now compare the offsets of the data members collected.
334  for (auto entry : f_data_members)
335  {
336  var_decl_sptr f_member = is_var_decl(entry.second);
337  ABG_ASSERT(f_member);
338 
339  var_decl_sptr s_member;
340  auto i = s_data_members.find(entry.first);
341  if (i == s_data_members.end())
342  {
343  unsigned offset = get_data_member_offset(f_member);
344  s_member = find_data_member_at_offset(s_data_members, offset);
345  if (!s_member)
346  // A data member was suppressed; that's bad; let's consider
347  // that as a sub-type change.
348  return true;
349  }
350 
351  if (!s_member)
352  s_member = is_var_decl(i->second);
353  ABG_ASSERT(s_member);
354  diff_sptr d =compute_diff(f_member->get_type(), s_member->get_type(), ctxt);
355  if (d->has_changes())
356  return true;
357  }
358  return false;
359 }
360 
361 /// Test if the changes of a @ref class_diff are harmless.
362 ///
363 /// Potentially harmful changes are basically:
364 /// 1/ name change (that changes the type altogether)
365 /// 2/ size change
366 /// 3/ offset change of any data member
367 /// 4/ any subtype change.
368 ///
369 /// Thus, this function tests that the class_diff carries none of the
370 /// 4 kinds of changes above.
371 ///
372 /// @param d the @ref class_diff to consider.
373 ///
374 /// @return true iff @p d has only harmless changes.
375 static bool
376 class_diff_has_only_harmless_changes(const class_diff* d)
377 {
378  if (!d || !d->has_changes())
379  return true;
380 
381  class_decl_sptr f = d->first_class_decl(), s = d->second_class_decl();
382 
383  if (f->get_qualified_name() != s->get_qualified_name())
384  return false;
385 
386  if (f->get_size_in_bits() != s->get_size_in_bits())
387  return false;
388 
389  // collect the data members
390  string_decl_base_sptr_map f_data_members, s_data_members;
391  collect_non_anonymous_data_members(f, f_data_members);
392  collect_non_anonymous_data_members(s, s_data_members);
393 
394  // detect offset changes
395  if (has_offset_changes(f_data_members, s_data_members))
396  return false;
397 
398  // detect subtype changes
399  if (has_subtype_changes(f_data_members, s_data_members, d->context()))
400  return false;
401 
402  return true;
403 }
404 
405 /// Test if the changes of a @ref class_diff are harmless.
406 ///
407 /// Potentially harmful changes are basically:
408 /// 1/ name change (that changes the type altogether)
409 /// 2/ size change
410 /// 3/ offset change of any data member
411 /// 4/ any subtype change.
412 ///
413 /// Thus, this function tests that the class_diff carries none of the
414 /// 4 kinds of changes above.
415 ///
416 /// @param d the @ref class_diff to consider.
417 ///
418 /// @return true iff @p d has only harmless changes.
419 static bool
420 class_diff_has_only_harmless_changes(diff* d)
421 {
422  if (const class_diff* class_dif = is_class_diff(d))
423  return class_diff_has_only_harmless_changes(class_dif);
424  return false;
425 }
426 
427 /// Tests if the access specifiers for a member declaration changed.
428 ///
429 /// @param f the declaration for the first version of the member
430 /// declaration to consider.
431 ///
432 /// @param s the declaration for the second version of the member
433 /// delcaration to consider.
434 ///
435 /// @return true iff the access specifier changed.
436 static bool
437 access_changed(const decl_base_sptr& f, const decl_base_sptr& s)
438 {
439  if (!is_member_decl(f)
440  || !is_member_decl(s))
441  return false;
442 
445 
446  if (sa != fa)
447  return true;
448 
449  return false;
450 }
451 
452 /// Test if there was a function or variable CRC change.
453 ///
454 /// @param f the first function or variable to consider.
455 ///
456 /// @param s the second function or variable to consider.
457 ///
458 /// @return true if the test is positive, false otherwise.
459 template <typename function_or_var_decl_sptr>
460 static bool
461 crc_changed(const function_or_var_decl_sptr& f,
462  const function_or_var_decl_sptr& s)
463 {
464  const auto& symbol_f = f->get_symbol();
465  const auto& symbol_s = s->get_symbol();
466  if (!symbol_f || !symbol_s)
467  return false;
468  return symbol_f->get_crc() != symbol_s->get_crc();
469 }
470 
471 /// Test if the current diff tree node carries a CRC change in either a
472 /// function or a variable.
473 ///
474 /// @param diff the diff tree node to consider.
475 ///
476 /// @return true if the test is positive, false otherwise.
477 static bool
478 crc_changed(const diff* diff)
479 {
480  if (const function_decl_diff* d =
481  dynamic_cast<const function_decl_diff*>(diff))
482  return crc_changed(d->first_function_decl(), d->second_function_decl());
483  if (const var_diff* d = dynamic_cast<const var_diff*>(diff))
484  return crc_changed(d->first_var(), d->second_var());
485  return false;
486 }
487 
488 /// Test if there was a function or variable namespace change.
489 ///
490 /// @param f the first function or variable to consider.
491 ///
492 /// @param s the second function or variable to consider.
493 ///
494 /// @return true if the test is positive, false otherwise.
495 template <typename function_or_var_decl_sptr>
496 static bool
497 namespace_changed(const function_or_var_decl_sptr& f,
498  const function_or_var_decl_sptr& s)
499 {
500  const auto& symbol_f = f->get_symbol();
501  const auto& symbol_s = s->get_symbol();
502  if (!symbol_f || !symbol_s)
503  return false;
504  return symbol_f->get_namespace() != symbol_s->get_namespace();
505 }
506 
507 /// Test if the current diff tree node carries a namespace change in
508 /// either a function or a variable.
509 ///
510 /// @param diff the diff tree node to consider.
511 ///
512 /// @return true if the test is positive, false otherwise.
513 static bool
514 namespace_changed(const diff* diff)
515 {
516  if (const function_decl_diff* d =
517  dynamic_cast<const function_decl_diff*>(diff))
518  return namespace_changed(d->first_function_decl(),
519  d->second_function_decl());
520  if (const var_diff* d = dynamic_cast<const var_diff*>(diff))
521  return namespace_changed(d->first_var(), d->second_var());
522  return false;
523 }
524 
525 /// Test if there was a function name change, but there there was no
526 /// change in name of the underlying symbol. IOW, if the name of a
527 /// function changed, but the symbol of the new function is equal to
528 /// the symbol of the old one, or is equal to an alians of the symbol
529 /// of the old function.
530 ///
531 /// @param f the first function to consider.
532 ///
533 /// @param s the second function to consider.
534 ///
535 /// @return true if the test is positive, false otherwise.
536 static bool
537 function_name_changed_but_not_symbol(const function_decl_sptr& f,
538  const function_decl_sptr& s)
539 {
540  if (!f || !s)
541  return false;
542  string fn = f->get_qualified_name(),
543  sn = s->get_qualified_name();
544 
545  if (fn != sn)
546  {
547  elf_symbol_sptr fs = f->get_symbol(), ss = s->get_symbol();
548  if (fs == ss)
549  return true;
550  if (!!fs != !!ss)
551  return false;
552  for (elf_symbol_sptr s = fs->get_next_alias();
553  s && !s->is_main_symbol();
554  s = s->get_next_alias())
555  if (*s == *ss)
556  return true;
557  }
558  return false;
559 }
560 
561 /// Test if the current diff tree node carries a function name change,
562 /// in which there there was no change in the name of the underlying
563 /// symbol. IOW, if the name of a function changed, but the symbol of
564 /// the new function is equal to the symbol of the old one, or is
565 /// equal to an alians of the symbol of the old function.
566 ///
567 /// @param diff the diff tree node to consider.
568 ///
569 /// @return true if the test is positive, false otherwise.
570 static bool
571 function_name_changed_but_not_symbol(const diff* diff)
572 {
573  if (const function_decl_diff* d =
574  dynamic_cast<const function_decl_diff*>(diff))
575  return function_name_changed_but_not_symbol(d->first_function_decl(),
576  d->second_function_decl());
577  return false;
578 }
579 
580 /// Tests if the offset of a given data member changed.
581 ///
582 /// @param f the declaration for the first version of the data member to
583 /// consider.
584 ///
585 /// @param s the declaration for the second version of the data member
586 /// to consider.
587 ///
588 /// @return true iff the offset of the data member changed.
589 static bool
590 data_member_offset_changed(decl_base_sptr f, decl_base_sptr s)
591 {
592  if (!is_member_decl(f)
593  || !is_member_decl(s))
594  return false;
595 
596  var_decl_sptr v0 = dynamic_pointer_cast<var_decl>(f),
597  v1 = dynamic_pointer_cast<var_decl>(s);
598  if (!v0 || !v1)
599  return false;
600 
602  return true;
603 
604  return false;
605 }
606 
607 /// Test if the size of a non-static data member changed accross two
608 /// versions.
609 ///
610 /// @param f the first version of the non-static data member.
611 ///
612 /// @param s the second version of the non-static data member.
613 static bool
614 non_static_data_member_type_size_changed(const decl_base_sptr& f,
615  const decl_base_sptr& s)
616 {
617  if (!is_member_decl(f)
618  || !is_member_decl(s))
619  return false;
620 
621  var_decl_sptr fv = dynamic_pointer_cast<var_decl>(f),
622  sv = dynamic_pointer_cast<var_decl>(s);
623  if (!fv
624  || !sv
625  || get_member_is_static(fv)
626  || get_member_is_static(sv))
627  return false;
628 
629  return type_size_changed(fv->get_type(), sv->get_type());
630 }
631 
632 /// Test if the size of a static data member changed accross two
633 /// versions.
634 ///
635 /// @param f the first version of the static data member.
636 ///
637 /// @param s the second version of the static data member.
638 static bool
639 static_data_member_type_size_changed(const decl_base_sptr& f,
640  const decl_base_sptr& s)
641 {
642  if (!is_member_decl(f)
643  || !is_member_decl(s))
644  return false;
645 
646  var_decl_sptr fv = dynamic_pointer_cast<var_decl>(f),
647  sv = dynamic_pointer_cast<var_decl>(s);
648  if (!fv
649  || !sv
650  || !get_member_is_static(fv)
651  || !get_member_is_static(sv))
652  return false;
653 
654  return type_size_changed(fv->get_type(), sv->get_type());
655 }
656 
657 /// Test if two types are different but compatible.
658 ///
659 /// @param d1 the declaration of the first type to consider.
660 ///
661 /// @param d2 the declaration of the second type to consider.
662 ///
663 /// @return true if d1 and d2 are different but compatible.
664 static bool
665 is_compatible_change(const decl_base_sptr& d1, const decl_base_sptr& d2)
666 {
667  if ((d1 && d2)
668  && (d1 != d2)
669  && types_are_compatible(d1, d2))
670  return true;
671  return false;
672 }
673 
674 /// Test if two decls have different names.
675 ///
676 /// @param d1 the first declaration to consider.
677 ///
678 /// @param d2 the second declaration to consider.
679 ///
680 /// @return true if d1 and d2 have different names.
681 static bool
682 decl_name_changed(const type_or_decl_base* a1, const type_or_decl_base *a2)
683 {
684  string d1_name, d2_name;
685 
686  const decl_base *d1 = dynamic_cast<const decl_base*>(a1);
687  if (d1 == 0)
688  return false;
689 
690  const decl_base *d2 = dynamic_cast<const decl_base*>(a2);
691  if (d2 == 0)
692  return false;
693 
694  if (d1)
695  d1_name = d1->get_qualified_name();
696  if (d2)
697  d2_name = d2->get_qualified_name();
698 
699  return d1_name != d2_name;
700 }
701 
702 /// Test if two decls have different names.
703 ///
704 /// @param d1 the first declaration to consider.
705 ///
706 /// @param d2 the second declaration to consider.
707 ///
708 /// @return true if d1 and d2 have different names.
709 static bool
710 decl_name_changed(const type_or_decl_base_sptr& d1,
711  const type_or_decl_base_sptr& d2)
712 {return decl_name_changed(d1.get(), d2.get());}
713 
714 /// Test if a diff nodes carries a changes in which two decls have
715 /// different names.
716 ///
717 /// @param d the diff node to consider.
718 ///
719 /// @return true iff d carries a changes in which two decls have
720 /// different names.
721 static bool
722 decl_name_changed(const diff *d)
723 {return decl_name_changed(d->first_subject(), d->second_subject());}
724 
725 /// Test if two decls represents a harmless name change.
726 ///
727 /// For now, a harmless name change is considered only for a typedef,
728 /// enum or a data member.
729 ///
730 /// @param f the first decl to consider in the comparison.
731 ///
732 /// @param s the second decl to consider in the comparison.
733 ///
734 /// @return true iff decl @p s represents a harmless change over @p f.
735 bool
736 has_harmless_name_change(const decl_base_sptr& f, const decl_base_sptr& s)
737 {
738  // So, a harmless name change is either ...
739  return (decl_name_changed(f, s)
740  && (// ... an anonymous decl name changed into another
741  // anonymous decl name ...
742  (f->get_is_anonymous() && s->get_is_anonymous())
743  ||
744  // ... an anonymous decl name changed harmlessly into
745  // another anonymous decl name ...
746  ((f->get_is_anonymous_or_has_anonymous_parent()
747  && s->get_is_anonymous_or_has_anonymous_parent())
748  && tools_utils::decl_names_equal(f->get_qualified_name(),
749  s->get_qualified_name()))
750  // ... a typedef name change, without having the
751  // underlying type changed ...
752  || (is_typedef(f)
753  && is_typedef(s)
754  && (is_typedef(f)->get_underlying_type()
755  == is_typedef(s)->get_underlying_type()))
756  // .. or a data member name change, without having its
757  // type changed ...
758  || (is_data_member(f)
759  && is_data_member(s)
760  && (is_var_decl(f)->get_type()
761  == is_var_decl(s)->get_type()))
762  // .. an enum name change without having any other part
763  // of the enum to change.
764  || (is_enum_type(f)
765  && is_enum_type(s)
767  *is_enum_type(s),
768  0))));
769 }
770 
771 /// Test if two decls represents a harmful name change.
772 ///
773 /// A harmful name change is a name change that is not harmless, so
774 /// this function uses the function has_harmless_name_change.
775 ///
776 /// @param f the first decl to consider in the comparison.
777 ///
778 /// @param s the second decl to consider in the comparison.
779 ///
780 /// @return true iff decl @p s represents a harmful name change over
781 /// @p f.
782 bool
783 has_harmful_name_change(const decl_base_sptr& f, const decl_base_sptr& s)
784 {return decl_name_changed(f, s) && ! has_harmless_name_change(f, s);}
785 
786 /// Test if a diff node represents a harmful name change.
787 ///
788 /// A harmful name change is a name change that is not harmless, so
789 /// this function uses the function has_harmless_name_change.
790 ///
791 /// @param f the first decl to consider in the comparison.
792 ///
793 /// @param s the second decl to consider in the comparison.
794 ///
795 /// @return true iff decl @p s represents a harmful name change over
796 /// @p f.
797 bool
798 has_harmful_name_change(const diff* dif)
799 {
800  decl_base_sptr f = is_decl(dif->first_subject()),
801  s = is_decl(dif->second_subject());
802 
803  return has_harmful_name_change(f, s);
804 }
805 
806 /// Test if a class_diff node has non-static members added or
807 /// removed.
808 ///
809 /// @param diff the diff node to consider.
810 ///
811 /// @return true iff the class_diff node has non-static members added
812 /// or removed.
813 static bool
814 non_static_data_member_added_or_removed(const class_diff* diff)
815 {
816  if (diff && !diff_involves_decl_only_class(diff))
817  {
818  for (string_decl_base_sptr_map::const_iterator i =
819  diff->inserted_data_members().begin();
820  i != diff->inserted_data_members().end();
821  ++i)
822  if (!get_member_is_static(i->second))
823  return true;
824 
825  for (string_decl_base_sptr_map::const_iterator i =
826  diff->deleted_data_members().begin();
827  i != diff->deleted_data_members().end();
828  ++i)
829  if (!get_member_is_static(i->second))
830  return true;
831  }
832 
833  return false;
834 }
835 
836 /// Test if a class_diff node has members added or removed.
837 ///
838 /// @param diff the diff node to consider.
839 ///
840 /// @return true iff the class_diff node has members added or removed.
841 static bool
842 non_static_data_member_added_or_removed(const diff* diff)
843 {
844  return non_static_data_member_added_or_removed
845  (dynamic_cast<const class_diff*>(diff));
846 }
847 
848 /// Test if a @ref class_or_union_diff has a data member replaced by
849 /// an anonymous data member in a harmless way. That means, the new
850 /// anonymous data member somehow contains the replaced data member
851 /// and it doesn't break the layout of the containing class.
852 ///
853 /// @param diff the diff node to consider.
854 ///
855 /// @return true iff the @ref class_or_union_diff has a data member
856 /// harmlessly replaced by an anonymous data member.
857 bool
859 {
861 
862  if (!c)
863  return false;
864  return !c->data_members_replaced_by_adms().empty();
865 }
866 
867 /// Test if we are looking at two variables which types are both one
868 /// dimension array, with one of them being of unknow size and the two
869 /// variables having the same symbol size.
870 ///
871 /// This can happen in the case of these two declarations, for instance:
872 ///
873 /// unsigned int array[];
874 ///
875 /// and:
876 ///
877 /// unsigned int array[] ={0};
878 ///
879 /// In both cases, the size of the ELF symbol of the variable 'array'
880 /// is 32 bits, but, at least in the first case
881 bool
883  const var_decl_sptr& var2)
884 {
885  type_base_sptr /*first type*/ft =
886  peel_qualified_or_typedef_type(var1->get_type());
887  type_base_sptr /*second type*/st =
888  peel_qualified_or_typedef_type(var2->get_type());
889 
890  array_type_def_sptr /*first array type*/fat = is_array_type(ft);
891  array_type_def_sptr /*second array type*/sat = is_array_type(st);
892 
893  // The types of the variables must be arrays.
894  if (!fat || !sat)
895  return false;
896 
897  // The arrays must have one dimension and at least one of them must
898  // be of unknown size.
899  if (fat->get_subranges().size() != 1
900  || sat->get_subranges().size() != 1
901  || (!fat->is_non_finite() && !sat->is_non_finite()))
902  return false;
903 
904  // The variables must be equal modulo their type.
905  if (!var_equals_modulo_types(*var1, *var2, nullptr))
906  return false;
907 
908  // The symbols of the variables must be defined and of the same
909  // non-zero size.
910  if (!var1->get_symbol()
911  || !var2->get_symbol()
912  || var1->get_symbol()->get_size() != var2->get_symbol()->get_size())
913  return false;
914 
915  return true;
916 }
917 
918 /// Test if we are looking at a diff that carries a change of
919 /// variables which types are both one dimension array, with one of
920 /// them being of unknow size and the two variables having the same
921 /// symbol size.
922 ///
923 /// This can happen in the case of these two declarations, for instance:
924 ///
925 /// unsigned int array[];
926 ///
927 /// and:
928 ///
929 /// unsigned int array[] ={0};
930 ///
931 /// In both cases, the size of the ELF symbol of the variable 'array'
932 /// is 32 bits, but, at least in the first case
933 bool
935 {
936  const var_diff* d = is_var_diff(diff);
937 
938  if (!d)
939  return false;
940 
941  var_decl_sptr f = d->first_var(), s = d->second_var();
942 
944 }
945 
946 /// Test if a class with a fake flexible data member got changed into
947 /// a class with a real fexible data member.
948 ///
949 /// A fake flexible array data member is a data member that is the
950 /// last of the class/struct which type is an array of one element.
951 /// This was used before C99 standardized flexible array data members.
952 ///
953 /// @param first the first version of the class to consider.
954 ///
955 /// @param second the second version of the class to consider.
956 ///
957 /// @return true iff @p first has a fake flexible array data member
958 /// that got changed into @p second with a real flexible array data
959 /// member.
960 bool
962  const class_decl_sptr& second)
963 {
966  // A fake flexible array member has been changed into
967  // a real flexible array ...
968  return true;
969  return false;
970 }
971 
972 /// Test if a diff node carries a change from class with a fake
973 /// flexible data member into a class with a real fexible data member.
974 ///
975 /// A fake flexible array data member is a data member that is the
976 /// last of the class/struct which type is an array of one element.
977 /// This was used before C99 standardized flexible array data members.
978 ///
979 /// @param the diff node to consider.
980 ///
981 /// @return true iff @p dif carries a change from class with a fake
982 /// flexible data member into a class with a real fexible data member.
983 /// member.
984 bool
986 {
987  const class_diff* d = is_class_diff(dif);
988  if (!d)
989  return false;
990 
992  d->second_class_decl());
993 }
994 
995 /// Test if a class_diff node has static members added or removed.
996 ///
997 /// @param diff the diff node to consider.
998 ///
999 /// @return true iff the class_diff node has static members added
1000 /// or removed.
1001 static bool
1002 static_data_member_added_or_removed(const class_diff* diff)
1003 {
1004  if (diff && !diff_involves_decl_only_class(diff))
1005  {
1006  for (string_decl_base_sptr_map::const_iterator i =
1007  diff->inserted_data_members().begin();
1008  i != diff->inserted_data_members().end();
1009  ++i)
1010  if (get_member_is_static(i->second))
1011  return true;
1012 
1013  for (string_decl_base_sptr_map::const_iterator i =
1014  diff->deleted_data_members().begin();
1015  i != diff->deleted_data_members().end();
1016  ++i)
1017  if (get_member_is_static(i->second))
1018  return true;
1019  }
1020 
1021  return false;
1022 }
1023 
1024 /// Test if a class_diff node has a harmless "One Definition Rule"
1025 /// violation that will cause a diagnostic rule.
1026 ///
1027 /// The conditions this function looks for are:
1028 ///
1029 /// 1/ The two subject of the diff must be canonically different
1030 ///
1031 /// 2/ The two subjects of the diff must be structurally equal
1032 ///
1033 /// 3/ The canonical types of the subjects of the diff must be
1034 /// structurally different.
1035 ///
1036 /// These conditions makes the diff node appears as it carries changes
1037 /// (because of a ODR glitch present in the binary), but the glitch
1038 /// has no effect on the structural equality of the subjects of the
1039 /// diff. If we do not detect these conditions, we'd end up with a
1040 /// diagnostic glitch where the reporter thinks there is an ABI change
1041 /// (because of the canonical difference), but then it fails to give
1042 /// any detail about it, because there is no structural change.
1043 ///
1044 /// @param diff the diff node to consider.
1045 ///
1046 /// @return true iff the the diff node has a harmless "One Definition
1047 /// Rule" violation that cause an empty false positive.
1048 static bool
1049 class_diff_has_harmless_odr_violation_change(const diff* dif)
1050 {
1051  class_diff* d = dynamic_cast<class_diff*>(const_cast<diff*>(dif));
1052  if (!d || !d->has_changes())
1053  return false;
1054 
1055  class_decl_sptr first = d->first_class_decl();
1056  class_decl_sptr second = d->second_class_decl();
1057 
1058  if (first->get_qualified_name() == second->get_qualified_name()
1059  && first != second
1060  && first->get_corpus() == second->get_corpus())
1061  return true;
1062 
1063  return false;
1064 }
1065 
1066 /// Test if a class_diff node has static members added or
1067 /// removed.
1068 ///
1069 /// @param diff the diff node to consider.
1070 ///
1071 /// @return true iff the class_diff node has static members added
1072 /// or removed.
1073 static bool
1074 static_data_member_added_or_removed(const diff* diff)
1075 {
1076  return static_data_member_added_or_removed
1077  (dynamic_cast<const class_diff*>(diff));
1078 }
1079 
1080 /// Test if the class_diff node has a change involving virtual member
1081 /// functions.
1082 ///
1083 /// That means whether there is an added, removed or changed virtual
1084 /// member function.
1085 ///
1086 /// @param diff the class_diff node to consider.
1087 ///
1088 /// @return true iff the class_diff node contains changes involving
1089 /// virtual member functions.
1090 static bool
1091 has_virtual_mem_fn_change(const class_diff* diff)
1092 {
1093  if (!diff || diff_involves_decl_only_class(diff))
1094  return false;
1095 
1096  for (string_member_function_sptr_map::const_iterator i =
1097  diff->deleted_member_fns().begin();
1098  i != diff->deleted_member_fns().end();
1099  ++i)
1100  {
1101  if (get_member_function_is_virtual(i->second))
1102  {
1103  // Do not consider a virtual function that got deleted from
1104  // an offset and re-inserted at the same offset as a
1105  // "virtual member function change".
1106  string_member_function_sptr_map::const_iterator j =
1107  diff->inserted_member_fns().find(i->first);
1108  if (j != diff->inserted_member_fns().end()
1109  && (get_member_function_vtable_offset(i->second)
1110  == get_member_function_vtable_offset(j->second)))
1111  continue;
1112 
1113  return true;
1114  }
1115  }
1116 
1117  for (string_member_function_sptr_map::const_iterator i =
1118  diff->inserted_member_fns().begin();
1119  i != diff->inserted_member_fns().end();
1120  ++i)
1121  {
1122  if (get_member_function_is_virtual(i->second))
1123  {
1124  // Do not consider a virtual function that got deleted from
1125  // an offset and re-inserted at the same offset as a
1126  // "virtual member function change".
1127  string_member_function_sptr_map::const_iterator j =
1128  diff->deleted_member_fns().find(i->first);
1129  if (j != diff->deleted_member_fns().end()
1130  && (get_member_function_vtable_offset(i->second)
1131  == get_member_function_vtable_offset(j->second)))
1132  continue;
1133 
1134  return true;
1135  }
1136  }
1137 
1138  for (function_decl_diff_sptrs_type::const_iterator i =
1139  diff->changed_member_fns().begin();
1140  i != diff->changed_member_fns().end();
1141  ++i)
1142  if (get_member_function_is_virtual((*i)->first_function_decl())
1143  || get_member_function_is_virtual((*i)->second_function_decl()))
1144  {
1145  if (get_member_function_vtable_offset((*i)->first_function_decl())
1146  == get_member_function_vtable_offset((*i)->second_function_decl()))
1147  continue;
1148 
1149  return true;
1150  }
1151 
1152  return false;
1153 }
1154 
1155 /// Test if the function_decl_diff node has a change involving virtual
1156 /// member functions.
1157 ///
1158 /// That means whether there is an added, removed or changed virtual
1159 /// member function.
1160 ///
1161 /// @param diff the function_decl_diff node to consider.
1162 ///
1163 /// @return true iff the function_decl_diff node contains changes
1164 /// involving virtual member functions.
1165 bool
1166 has_virtual_mem_fn_change(const function_decl_diff* diff)
1167 {
1168  if (!diff)
1169  return false;
1170 
1172  sf = diff->second_function_decl();
1173 
1174  if (!is_member_function(ff)
1175  || !is_member_function(sf))
1176  return false;
1177 
1178  bool ff_is_virtual = get_member_function_is_virtual(ff),
1179  sf_is_virtual = get_member_function_is_virtual(sf);
1180 
1181  if (ff_is_virtual != sf_is_virtual)
1182  return true;
1183 
1184  size_t ff_vtable_offset = get_member_function_vtable_offset(ff),
1185  sf_vtable_offset = get_member_function_vtable_offset(sf);
1186 
1187  if (ff_vtable_offset != sf_vtable_offset)
1188  return true;
1189 
1190  return false;
1191 }
1192 
1193 /// Test if the class_diff node has a change involving virtual member
1194 /// functions.
1195 ///
1196 /// That means whether there is an added, removed or changed virtual
1197 /// member function.
1198 ///
1199 /// @param diff the class_diff node to consider.
1200 ///
1201 /// @return true iff the class_diff node contains changes involving
1202 /// virtual member functions.
1203 static bool
1204 has_virtual_mem_fn_change(const diff* diff)
1205 {
1206  return (has_virtual_mem_fn_change(dynamic_cast<const class_diff*>(diff))
1207  || has_virtual_mem_fn_change(dynamic_cast<const function_decl_diff*>(diff)));
1208 }
1209 
1210 /// Test if the class_diff has changes to non virtual member
1211 /// functions.
1212 ///
1213 ///@param diff the class_diff nod e to consider.
1214 ///
1215 /// @retrurn iff the class_diff node has changes to non virtual member
1216 /// functions.
1217 static bool
1218 has_non_virtual_mem_fn_change(const class_diff* diff)
1219 {
1220  if (!diff || diff_involves_decl_only_class(diff))
1221  return false;
1222 
1223  for (string_member_function_sptr_map::const_iterator i =
1224  diff->deleted_member_fns().begin();
1225  i != diff->deleted_member_fns().end();
1226  ++i)
1227  if (!get_member_function_is_virtual(i->second))
1228  return true;
1229 
1230  for (string_member_function_sptr_map::const_iterator i =
1231  diff->inserted_member_fns().begin();
1232  i != diff->inserted_member_fns().end();
1233  ++i)
1234  if (!get_member_function_is_virtual(i->second))
1235  return true;
1236 
1237  for (function_decl_diff_sptrs_type::const_iterator i =
1238  diff->changed_member_fns().begin();
1239  i != diff->changed_member_fns().end();
1240  ++i)
1241  if(!get_member_function_is_virtual((*i)->first_function_decl())
1242  && !get_member_function_is_virtual((*i)->second_function_decl()))
1243  return true;
1244 
1245  return false;
1246 }
1247 
1248 /// Test if the class_diff has changes to non virtual member
1249 /// functions.
1250 ///
1251 ///@param diff the class_diff nod e to consider.
1252 ///
1253 /// @retrurn iff the class_diff node has changes to non virtual member
1254 /// functions.
1255 static bool
1256 has_non_virtual_mem_fn_change(const diff* diff)
1257 {return has_non_virtual_mem_fn_change(dynamic_cast<const class_diff*>(diff));}
1258 
1259 /// Test if a class_diff carries a base class removal.
1260 ///
1261 /// @param diff the class_diff to consider.
1262 ///
1263 /// @return true iff @p diff carries a base classe removal.
1264 static bool
1265 base_classes_removed(const class_diff* diff)
1266 {
1267  if (!diff)
1268  return false;
1269  return diff->deleted_bases().size();
1270 }
1271 
1272 /// Test if a class_diff carries a base classes removal.
1273 ///
1274 /// @param diff the class_diff to consider.
1275 ///
1276 /// @return true iff @p diff carries a base class removal.
1277 static bool
1278 base_classes_removed(const diff* diff)
1279 {return base_classes_removed(dynamic_cast<const class_diff*>(diff));}
1280 
1281 /// Test if two classes that are decl-only (have the decl-only flag
1282 /// and carry no data members) but are different just by their size.
1283 ///
1284 /// In some weird DWARF representation, it happens that a decl-only
1285 /// class (with no data member) actually carries a non-zero size.
1286 /// That shouldn't happen, but hey, we need to deal with real life.
1287 /// So we need to detect that case first.
1288 ///
1289 /// @param first the first class or union to consider.
1290 ///
1291 /// @param seconf the second class or union to consider.
1292 ///
1293 /// @return true if the two classes are decl-only and differ in their
1294 /// size.
1295 bool
1297  const class_or_union& second)
1298 {
1299  if (first.get_qualified_name() != second.get_qualified_name())
1300  return false;
1301 
1302  if (!first.get_is_declaration_only() || !second.get_is_declaration_only())
1303  return false;
1304 
1305  bool f_is_empty = first.get_data_members().empty();
1306  bool s_is_empty = second.get_data_members().empty();
1307 
1308  return f_is_empty && s_is_empty;
1309 }
1310 
1311 /// Test if two classes that are decl-only (have the decl-only flag
1312 /// and carry no data members) but are different just by their size.
1313 ///
1314 /// In some weird DWARF representation, it happens that a decl-only
1315 /// class (with no data member) actually carries a non-zero size.
1316 /// That shouldn't happen, but hey, we need to deal with real life.
1317 /// So we need to detect that case first.
1318 ///
1319 /// @param first the first class or union to consider.
1320 ///
1321 /// @param seconf the second class or union to consider.
1322 ///
1323 /// @return true if the two classes are decl-only and differ in their
1324 /// size.
1325 bool
1326 is_decl_only_class_with_size_change(const class_or_union_sptr& first,
1327  const class_or_union_sptr& second)
1328 {
1329  if (!first || !second)
1330  return false;
1331 
1332  class_or_union_sptr f = look_through_decl_only_class(first);
1333  class_or_union_sptr s = look_through_decl_only_class(second);
1334 
1335  return is_decl_only_class_with_size_change(*f, *s);
1336 }
1337 
1338 /// Test if a diff node is for two classes that are decl-only (have
1339 /// the decl-only flag and carry no data members) but are different
1340 /// just by their size.
1341 ///
1342 /// In some weird DWARF representation, it happens that a decl-only
1343 /// class (with no data member) actually carries a non-zero size.
1344 /// That shouldn't happen, but hey, we need to deal with real life.
1345 /// So we need to detect that case first.
1346 ///
1347 /// @param diff the diff node to consider.
1348 ///
1349 /// @return true if the two classes are decl-only and differ in their
1350 /// size.
1351 bool
1353 {
1354  const class_or_union_diff *d = dynamic_cast<const class_or_union_diff*>(diff);
1355  if (!d)
1356  return false;
1357 
1358  class_or_union_sptr f =
1360  class_or_union_sptr s =
1362 
1364 }
1365 
1366 /// Test if two @ref decl_base_sptr are different just by the
1367 /// fact that one is decl-only and the other one is defined.
1368 ///
1369 /// @param first the first decl to consider.
1370 ///
1371 /// @param second the second decl to consider.
1372 ///
1373 /// @return true iff the two arguments are different just by the fact
1374 /// that one is decl-only and the other one is defined.
1375 bool
1376 has_decl_only_def_change(const decl_base_sptr& first,
1377  const decl_base_sptr& second)
1378 {
1379  if (!first || !second)
1380  return false;
1381 
1382  decl_base_sptr f =
1383  look_through_decl_only(first);
1384  decl_base_sptr s =
1385  look_through_decl_only(second);
1386 
1387  if (f->get_qualified_name() != s->get_qualified_name())
1388  return false;
1389 
1390  return f->get_is_declaration_only() != s->get_is_declaration_only();
1391 }
1392 
1393 /// Test if a diff carries a change in which the two decls are
1394 /// different by the fact that one is a decl-only and the other one is
1395 /// defined.
1396 ///
1397 /// @param diff the diff node to consider.
1398 ///
1399 /// @return true if the diff carries a change in which the two decls
1400 /// are different by the fact that one is a decl-only and the other
1401 /// one is defined.
1402 bool
1404 {
1405  if (!d)
1406  return false;
1407 
1408  decl_base_sptr f =
1410  decl_base_sptr s =
1412 
1413  return has_decl_only_def_change(f, s);
1414 }
1415 
1416 
1417 /// Test if two @ref class_or_union_sptr are different just by the
1418 /// fact that one is decl-only and the other one is defined.
1419 ///
1420 /// @param first the first class or union to consider.
1421 ///
1422 /// @param second the second class or union to consider.
1423 ///
1424 /// @return true iff the two arguments are different just by the fact
1425 /// that one is decl-only and the other one is defined.
1426 bool
1427 has_class_decl_only_def_change(const class_or_union_sptr& first,
1428  const class_or_union_sptr& second)
1429 {
1430  if (!first || !second)
1431  return false;
1432 
1433  class_or_union_sptr f =
1435  class_or_union_sptr s =
1437 
1438  if (f->get_qualified_name() != s->get_qualified_name())
1439  return false;
1440 
1441  return f->get_is_declaration_only() != s->get_is_declaration_only();
1442 }
1443 
1444 /// Test if two @ref enum_sptr are different just by the
1445 /// fact that one is decl-only and the other one is defined.
1446 ///
1447 /// @param first the first enum to consider.
1448 ///
1449 /// @param second the second enum to consider.
1450 ///
1451 /// @return true iff the two arguments are different just by the fact
1452 /// that one is decl-only and the other one is defined.
1453 bool
1455  const enum_type_decl_sptr& second)
1456 {
1457  if (!first || !second)
1458  return false;
1459 
1462 
1463  if (f->get_qualified_name() != s->get_qualified_name())
1464  return false;
1465 
1466  return f->get_is_declaration_only() != s->get_is_declaration_only();
1467 }
1468 
1469 /// Test if a class_or_union_diff carries a change in which the two
1470 /// classes are different by the fact that one is a decl-only and the
1471 /// other one is defined.
1472 ///
1473 /// @param diff the diff node to consider.
1474 ///
1475 /// @return true if the class_or_union_diff carries a change in which
1476 /// the two classes are different by the fact that one is a decl-only
1477 /// and the other one is defined.
1478 bool
1480 {
1481  const class_or_union_diff *d = dynamic_cast<const class_or_union_diff*>(diff);
1482  if (!d)
1483  return false;
1484 
1485  class_or_union_sptr f =
1487  class_or_union_sptr s =
1489 
1490  return has_class_decl_only_def_change(f, s);
1491 }
1492 
1493 /// Test if a enum_diff carries a change in which the two enums are
1494 /// different by the fact that one is a decl-only and the other one is
1495 /// defined.
1496 ///
1497 /// @param diff the diff node to consider.
1498 ///
1499 /// @return true if the enum_diff carries a change in which the two
1500 /// enums are different by the fact that one is a decl-only and the
1501 /// other one is defined.
1502 bool
1504 {
1505  const enum_diff *d = dynamic_cast<const enum_diff*>(diff);
1506  if (!d)
1507  return false;
1508 
1511 
1512  return has_enum_decl_only_def_change(f, s);
1513 }
1514 
1515 /// Test if a diff node carries a basic type name change.
1516 ///
1517 /// @param d the diff node to consider.
1518 ///
1519 /// @return true iff the diff node carries a basic type name change.
1520 bool
1522 {
1523  if (const type_decl_diff *dif = is_diff_of_basic_type(d))
1524  if (decl_name_changed(dif))
1525  return true;
1526 
1527  return false;
1528 }
1529 
1530 /// Test if a diff node carries a class or union type name change.
1531 ///
1532 /// @param d the diff node to consider.
1533 ///
1534 /// @return true iff the diff node carries a class or union type name
1535 /// change.
1536 bool
1538 {
1540  if (decl_name_changed(dif))
1541  return true;
1542 
1543  return false;
1544 }
1545 
1546 /// Test if a diff node carries a basic or class type name change.
1547 ///
1548 /// @param d the diff node to consider.
1549 ///
1550 /// @return true iff the diff node carries a basic or class type name
1551 /// change.
1552 bool
1554 {
1555  return (has_basic_type_name_change(d)
1557 }
1558 
1559 /// Test if a diff node carries a distinct type change or a
1560 /// pointer/reference/typedef to distinct type change.
1561 ///
1562 /// Note that a distinct type change is a change where the two
1563 /// subjects of the change are not of the same kind, e.g, a basic type
1564 /// that got changed into a qualified type.
1565 ///
1566 /// @param d the diff node to consider.
1567 ///
1568 /// @return true iff @p d is mostly a distinct diff.
1569 bool
1571 {
1572  if (is_distinct_diff(d))
1573  return true;
1574 
1575  // Let's consider that 'd' is a type diff ...
1576  diff * td = const_cast<type_diff_base*>(is_type_diff(d));
1577  if (!td)
1578  {
1579  // ... or a function parameter diff. In which case, let's get
1580  // its child type diff ...
1581  fn_parm_diff *pd = const_cast<fn_parm_diff*>(is_fn_parm_diff(d));
1582  if (pd)
1583  {
1584  td = const_cast<type_diff_base*>(is_type_diff(pd->type_diff().get()));
1585  if (!td)
1586  // if the diff of the fn_parm_diff is a a distinct diff
1587  // then handle it.
1588  td = const_cast<distinct_diff*>
1589  (is_distinct_diff(pd->type_diff().get()));
1590  }
1591  else
1592  return false;
1593  }
1594 
1595  // At this point, if we are not looking at a type diff we must have
1596  // bailed out already.
1597  ABG_ASSERT(td);
1598 
1599  type_base_sptr first = is_type(td->first_subject());
1600  type_base_sptr second = is_type(td->second_subject());
1601 
1603  second = peel_typedef_pointer_or_reference_type(second);
1604  ABG_ASSERT(first && second);
1605 
1606  return distinct_diff::entities_are_of_distinct_kinds(first, second);
1607 }
1608 
1609 /// Test if a diff node carries a non-anonymous data member to
1610 /// anonymous data member change, or vice-versa.
1611 ///
1612 /// @param d the diff node to consider.
1613 ///
1614 /// @return true iff @p d carries a non-anonymous to anonymous data
1615 /// member change, or vice-versa.
1616 bool
1618 {
1621  return true;
1622  return false;
1623 }
1624 
1625 /// Test if a diff node carries a non-anonymous data member to
1626 /// anonymous data member change, or vice-versa.
1627 ///
1628 /// @param d the diff node to consider.
1629 ///
1630 /// @return true iff @p d carries a non-anonymous to anonymous data
1631 /// member change, or vice-versa.
1632 bool
1634 {return has_anonymous_data_member_change(d.get());}
1635 
1636 /// Test if an enum_diff carries an enumerator insertion.
1637 ///
1638 /// @param diff the enum_diff to consider.
1639 ///
1640 /// @return true iff @p diff carries an enumerator insertion.
1641 static bool
1642 has_enumerator_insertion(const diff* diff)
1643 {
1644  if (const enum_diff* d = dynamic_cast<const enum_diff*>(diff))
1645  return !d->inserted_enumerators().empty();
1646  return false;
1647 }
1648 
1649 /// Test if an enum_diff carries an enumerator removal.
1650 ///
1651 /// @param diff the enum_diff to consider.
1652 ///
1653 /// @return true iff @p diff carries an enumerator removal or change.
1654 static bool
1655 has_enumerator_removal_or_change(const diff* diff)
1656 {
1657  if (const enum_diff* d = dynamic_cast<const enum_diff*>(diff))
1658  return (!d->deleted_enumerators().empty()
1659  || !d->changed_enumerators().empty());
1660  return false;
1661 }
1662 
1663 /// Test if an enum_diff carries a harmful change.
1664 ///
1665 /// @param diff the enum_diff to consider.
1666 ///
1667 /// @return true iff @p diff carries a harmful change.
1668 static bool
1669 has_harmful_enum_change(const diff* diff)
1670 {
1671  if (const enum_diff* d = dynamic_cast<const enum_diff*>(diff))
1672  return (has_enumerator_removal_or_change(d)
1673  || has_type_size_change(d));
1674  return false;
1675 }
1676 
1677 /// Test if a diff node carries a harmless change of an enum into an
1678 /// integer (or vice-versa).
1679 ///
1680 /// The test takes into account the fact change we care about might be
1681 /// wrapped into a typedef or qualified type diff.
1682 ///
1683 /// @param diff the diff node to consider.
1684 ///
1685 /// @return true if @p diff is a harmless enum to integer change.
1686 static bool
1687 has_harmless_enum_to_int_change(const diff* diff)
1688 {
1689  if (!diff)
1690  return false;
1691 
1693 
1694  if (const distinct_diff *d = is_distinct_diff(diff))
1695  {
1696  const enum_type_decl *enum_type = 0;
1697  const type_base *integer_type = 0;
1698 
1699  type_base *first_type =
1700  peel_qualified_or_typedef_type(is_type(d->first().get()));
1701  type_base *second_type =
1702  peel_qualified_or_typedef_type(is_type(d->second().get()));
1703 
1704  if (const enum_type_decl *e = is_enum_type(first_type))
1705  enum_type = e;
1706  else if (const enum_type_decl *e = is_enum_type(second_type))
1707  enum_type = e;
1708 
1709  if (const type_base * i = is_type_decl(first_type))
1710  integer_type = i;
1711  else if (const type_base *i = is_type_decl(second_type))
1712  integer_type = i;
1713 
1714  if (enum_type
1715  && integer_type
1716  && enum_type->get_size_in_bits() == integer_type->get_size_in_bits())
1717  return true;
1718  }
1719 
1720  return false;
1721 }
1722 
1723 /// Test if an @ref fn_parm_diff node has a top cv qualifier change on
1724 /// the type of the function parameter.
1725 ///
1726 /// @param diff the diff node to consider. It should be a @ref
1727 /// fn_parm_diff, otherwise the function returns 'false' directly.
1728 ///
1729 /// @return true iff @p diff is a @ref fn_parm_diff node that has a
1730 /// top cv qualifier change on the type of the function parameter.
1731 static bool
1732 has_fn_parm_type_top_cv_qual_change(const diff* diff)
1733 {
1734  // is diff a "function parameter diff node?
1735  const fn_parm_diff* parm_diff = is_fn_parm_diff(diff);
1736 
1737  if (!parm_diff || !parm_diff->has_changes())
1738  // diff either carries no change or is not a function parameter
1739  // diff node. So bail out.
1740  return false;
1741 
1742  function_decl::parameter_sptr first_parm = parm_diff->first_parameter();
1743  function_decl::parameter_sptr second_parm = parm_diff->second_parameter();
1744 
1745  type_base_sptr first_parm_type = first_parm->get_type();
1746  type_base_sptr second_parm_type = second_parm->get_type();
1747 
1748  if (!is_qualified_type(first_parm_type)
1749  && !is_qualified_type(second_parm_type))
1750  // None of the parameter types is qualified.
1751  return false;
1752 
1753  qualified_type_def::CV cv_quals_1 = qualified_type_def::CV_NONE;
1754  qualified_type_def::CV cv_quals_2 = qualified_type_def::CV_NONE;
1755  type_base_sptr peeled_type_1 = first_parm_type;
1756  type_base_sptr peeled_type_2 = second_parm_type;
1757 
1758  if (qualified_type_def_sptr qtype1 = is_qualified_type(first_parm_type))
1759  {
1760  cv_quals_1 = qtype1->get_cv_quals();
1761  peeled_type_1 = peel_qualified_type(qtype1);
1762  }
1763 
1764  if (qualified_type_def_sptr qtype2 = is_qualified_type(second_parm_type))
1765  {
1766  cv_quals_2 = qtype2->get_cv_quals();
1767  peeled_type_2 = peel_qualified_type(qtype2);
1768  }
1769 
1770  if (peeled_type_1
1771  && peeled_type_2
1772  && get_type_name(peeled_type_1) == get_type_name(peeled_type_2)
1773  && cv_quals_1 != cv_quals_2)
1774  // The top-level CV qualifiers of the function type are different
1775  // and the un-qualified variant (peeled) of said function types
1776  // are equal. This means the only change the function types have
1777  // are about top-level CV qualifiers.
1778  return true;
1779 
1780  return false;
1781 }
1782 
1783 /// Test if a type diff only carries a CV qualifier-only change.
1784 ///
1785 /// @param type_dif the type dif to consider.
1786 ///
1787 /// @return true iff the type_diff carries a CV qualifier only change.
1788 static bool
1789 type_diff_has_cv_qual_change_only(const diff *type_dif)
1790 {
1791  if (!is_type_diff(type_dif))
1792  return false;
1793 
1794  if (is_pointer_diff(type_dif))
1795  type_dif = peel_pointer_diff(type_dif);
1796  else if (is_reference_diff(type_dif))
1797  type_dif = peel_reference_diff(type_dif);
1798 
1799  const type_base *f = 0;
1800  const type_base *s = 0;
1801  if (const distinct_diff *d = is_distinct_diff(type_dif))
1802  {
1803  if (is_qualified_type(d->first()) == is_qualified_type(d->second()))
1804  return false;
1805  else
1806  {
1807  f = is_type(d->first()).get();
1808  s = is_type(d->second()).get();
1809  }
1810  }
1811  else if (const qualified_type_diff *d = is_qualified_type_diff(type_dif))
1812  {
1813  f = is_type(d->first_qualified_type()).get();
1814  s = is_type(d->second_qualified_type()).get();
1815  }
1816  else
1817  return false;
1818 
1819  f = peel_qualified_type(f);
1820  s = peel_qualified_type(s);
1821 
1822  // If f and s are arrays, note that they can differ only by the cv
1823  // qualifier of the array element type. That cv qualifier is not
1824  // removed by peel_qualified_type. So we need to test this case
1825  // specifically.
1826  if (array_type_def *f_a = is_array_type(f))
1827  if (array_type_def *s_a = is_array_type(s))
1828  return equals_modulo_cv_qualifier(f_a, s_a);
1829 
1830  return *f == *s;
1831 }
1832 
1833 /// Test if an @ref fn_parm_diff node has a cv qualifier change on the
1834 /// type of the function parameter. That is, we are looking for
1835 /// changes like 'const char*' to 'char*'.
1836 ///
1837 /// @param diff the diff node to consider. It should be a @ref
1838 /// fn_parm_diff, otherwise the function returns 'false' directly.
1839 ///
1840 /// @return true iff @p diff is a @ref fn_parm_diff node that has a
1841 /// cv qualifier change on the type of the function parameter.
1842 static bool
1843 has_fn_parm_type_cv_qual_change(const diff* dif)
1844 {
1845  // is diff a "function parameter diff node?
1846  const fn_parm_diff* parm_diff = is_fn_parm_diff(dif);
1847 
1848  if (!parm_diff || !parm_diff->has_changes())
1849  // diff either carries no change or is not a function parameter
1850  // diff node. So bail out.
1851  return false;
1852 
1853  const diff *type_dif = parm_diff->type_diff().get();
1854  return type_diff_has_cv_qual_change_only(type_dif);
1855 }
1856 
1857 /// Test if a function type or decl diff node carries a CV
1858 /// qualifier-only change on its return type.
1859 ///
1860 /// @param dif the diff node to consider. Note that if this is
1861 /// neither a function type nor decl diff node, the function returns
1862 /// false.
1863 ///
1864 /// @return true iff @p dif is a function decl or type diff node which
1865 /// carries a CV qualifier-only change on its return type.
1866 static bool
1867 has_fn_return_type_cv_qual_change(const diff* dif)
1868 {
1869  const function_type_diff* fn_type_diff = is_function_type_diff(dif);
1870  if (!fn_type_diff)
1871  if (const function_decl_diff* fn_decl_diff = is_function_decl_diff(dif))
1872  fn_type_diff = fn_decl_diff->type_diff().get();
1873 
1874  if (!fn_type_diff)
1875  return false;
1876 
1877  const diff* return_type_diff = fn_type_diff->return_type_diff().get();
1878  return type_diff_has_cv_qual_change_only(return_type_diff);
1879 }
1880 
1881 /// Test if a function type or decl diff node carries a function
1882 /// parameter addition or removal.
1883 ///
1884 /// @param dif the diff node to consider. Note that if this is
1885 /// neither a function type nor decl diff node, the function returns
1886 /// false.
1887 ///
1888 /// @return true iff @p dif is a function decl or type diff node which
1889 /// carries a function parameter addition or removal.
1890 static bool
1891 has_added_or_removed_function_parameters(const diff *dif)
1892 {
1893  const function_type_diff *fn_type_diff = is_function_type_diff(dif);
1894  if (!fn_type_diff)
1895  if (const function_decl_diff* fn_decl_diff = is_function_decl_diff(dif))
1896  fn_type_diff = fn_decl_diff->type_diff().get();
1897 
1898  if (!fn_type_diff)
1899  return false;
1900 
1901  if (!(fn_type_diff->sorted_deleted_parms().empty()
1902  && fn_type_diff->sorted_added_parms().empty()))
1903  return true;
1904 
1905  return false;
1906 }
1907 
1908 /// Test if a variable diff node carries a CV qualifier change on its type.
1909 ///
1910 /// @param dif the diff node to consider. Note that if it's not of
1911 /// var_diff type, the function returns false.
1912 ///
1913 /// @return true iff the @p dif carries a CV qualifier change on its
1914 /// type.
1915 static bool
1916 has_var_type_cv_qual_change(const diff* dif)
1917 {
1918  const var_diff *var_dif = is_var_diff(dif);
1919  if (!var_dif)
1920  return false;
1921 
1922  diff *type_dif = var_dif->type_diff().get();
1923  if (!type_dif)
1924  return false;
1925 
1926  return type_diff_has_cv_qual_change_only(type_dif);
1927 }
1928 
1929 /// Test if a diff node carries a void* to pointer type change.
1930 ///
1931 /// Note that this function looks through typedef and qualifier types
1932 /// to find the void pointer.
1933 ///
1934 /// @param dif the diff node to consider.
1935 ///
1936 /// @return true iff @p dif carries a void* to pointer type change.
1937 static bool
1938 has_void_ptr_to_ptr_change(const diff* dif)
1939 {
1940  dif = peel_typedef_diff(dif);
1941 
1942  if (const distinct_diff *d = is_distinct_diff(dif))
1943  {
1944  const type_base *f = is_type(d->first().get());
1945  const type_base *s = is_type(d->second().get());
1946 
1949 
1951  && is_pointer_type(s)
1953  && f->get_size_in_bits() == s->get_size_in_bits())
1954  return true;
1955  }
1956  else if (const pointer_diff *d = is_pointer_diff(dif))
1957  {
1958  const type_base *f = is_type(d->first_pointer()).get();
1959  const type_base *s = is_type(d->second_pointer()).get();
1960 
1963 
1965  && is_pointer_type(s)
1967  && f->get_size_in_bits() == s->get_size_in_bits())
1968  return true;
1969  }
1970  else if (const qualified_type_diff *d = is_qualified_type_diff(dif))
1971  {
1972  const type_base *f = is_type(d->first_qualified_type()).get();
1973  const type_base *s = is_type(d->second_qualified_type()).get();
1974 
1977 
1979  && is_pointer_type(s)
1981  && f->get_size_in_bits() == s->get_size_in_bits())
1982  return true;
1983  }
1984 
1985  return false;
1986 }
1987 
1988 /// Test if a diff node carries a benign change to the size of a
1989 /// variable of type array.
1990 ///
1991 /// A benign size change is a change in size (from or to infinite) of
1992 /// the array as expressed by the debug info, but when the *ELF* size
1993 /// (what really matters) of the variable object hasn't changed. This
1994 /// happens when the debug info emitter did have trouble figuring out
1995 /// the actual size of the array.
1996 ///
1997 /// @param dif the diff node to consider.
1998 ///
1999 /// @return true iff @p dif contains the benign array type size change.
2000 static bool
2001 has_benign_array_of_unknown_size_change(const diff* dif)
2002 {
2004 }
2005 
2006 /// Test if a union diff node does have changes that don't impact its
2007 /// size.
2008 ///
2009 /// @param d the union diff node to consider.
2010 ///
2011 /// @return true iff @p d is a diff node which has changes that don't
2012 /// impact its size.
2013 bool
2015 {
2016  if (is_union_diff(d)
2017  && d->has_changes()
2018  && !has_type_size_change(d))
2019  return true;
2020 
2021  return false;
2022 }
2023 
2024 /// Detect if the changes carried by a given diff node are deemed
2025 /// harmless and do categorize the diff node accordingly.
2026 ///
2027 /// @param d the diff node being visited.
2028 ///
2029 /// @param pre this is true iff the node is being visited *before* the
2030 /// children nodes of @p d.
2031 ///
2032 /// @return true iff the traversal shall keep going after the
2033 /// completion of this function.
2034 static bool
2035 categorize_harmless_diff_node(diff *d, bool pre)
2036 {
2037  if (!d->has_changes())
2038  return true;
2039 
2040  if (pre)
2041  {
2042  diff_category category = NO_CHANGE_CATEGORY;
2043 
2044  decl_base_sptr f = is_decl(d->first_subject()),
2045  s = is_decl(d->second_subject());
2046 
2050 
2051  if (access_changed(f, s))
2052  category |= ACCESS_CHANGE_CATEGORY;
2053 
2054  if (is_compatible_change(f, s))
2055  category |= COMPATIBLE_TYPE_CHANGE_CATEGORY;
2056 
2057  if (has_harmless_name_change(f, s)
2058  || class_diff_has_harmless_odr_violation_change(d))
2060 
2062  || class_diff_has_only_harmless_changes(d))
2064 
2065  if (has_non_virtual_mem_fn_change(d))
2067 
2068  if (static_data_member_added_or_removed(d)
2069  || static_data_member_type_size_changed(f, s))
2071 
2074 
2075  if ((has_enumerator_insertion(d)
2076  && !has_harmful_enum_change(d))
2077  || has_harmless_enum_to_int_change(d))
2078  category |= HARMLESS_ENUM_CHANGE_CATEGORY;
2079 
2080  if (function_name_changed_but_not_symbol(d))
2082 
2083  if (has_fn_parm_type_top_cv_qual_change(d))
2085 
2086  if (has_fn_parm_type_cv_qual_change(d))
2087  category |= FN_PARM_TYPE_CV_CHANGE_CATEGORY;
2088 
2089  if (has_fn_return_type_cv_qual_change(d))
2091 
2092  if (has_var_type_cv_qual_change(d))
2093  category |= VAR_TYPE_CV_CHANGE_CATEGORY;
2094 
2095  if (has_void_ptr_to_ptr_change(d))
2096  category |= VOID_PTR_TO_PTR_CHANGE_CATEGORY;
2097 
2098  if (has_benign_array_of_unknown_size_change(d))
2100 
2101  if (category)
2102  {
2104  // Also update the category of the canonical node.
2105  if (diff * canonical = d->get_canonical_diff())
2106  canonical->add_to_local_and_inherited_categories(category);
2107  }
2108  }
2109 
2110  return true;
2111 }
2112 
2113 /// Detect if the changes carried by a given diff node are deemed
2114 /// harmful and do categorize the diff node accordingly.
2115 ///
2116 /// @param d the diff node being visited.
2117 ///
2118 /// @param pre this is true iff the node is being visited *before* the
2119 /// children nodes of @p d.
2120 ///
2121 /// @return true iff the traversal shall keep going after the
2122 /// completion of this function.
2123 static bool
2124 categorize_harmful_diff_node(diff *d, bool pre)
2125 {
2126  if (!d->has_changes())
2127  return true;
2128 
2129  if (pre)
2130  {
2131  diff_category category = NO_CHANGE_CATEGORY;
2132  decl_base_sptr f = is_decl(d->first_subject()),
2133  s = is_decl(d->second_subject());
2134 
2135  // Detect size or offset changes as well as data member addition
2136  // or removal.
2137  //
2138  // TODO: be more specific -- not all size changes are harmful.
2141  && (type_size_changed(f, s)
2142  || type_has_offset_changes(f, s)
2143  || data_member_offset_changed(f, s)
2144  || non_static_data_member_type_size_changed(f, s)
2145  || non_static_data_member_added_or_removed(d)
2146  || base_classes_removed(d)
2147  || has_harmful_enum_change(d)
2148  || crc_changed(d)
2149  || namespace_changed(d)))
2150  category |= SIZE_OR_OFFSET_CHANGE_CATEGORY;
2151 
2152  if (has_virtual_mem_fn_change(d))
2153  category |= VIRTUAL_MEMBER_CHANGE_CATEGORY;
2154 
2155  if (has_added_or_removed_function_parameters(d))
2157 
2158  if (category)
2159  {
2160  d->add_to_local_and_inherited_categories(category);
2161  // Update the category of the canonical diff node too.
2162  if (diff * canonical = d->get_canonical_diff())
2163  canonical->add_to_local_and_inherited_categories(category);
2164  }
2165  }
2166 
2167  return true;
2168 }
2169 
2170 /// The visiting code of the harmless_harmful_filter.
2171 ///
2172 /// @param d the diff node being visited.
2173 ///
2174 /// @param pre this is true iff the node is being visited *before* the
2175 /// children nodes of @p d.
2176 ///
2177 /// @return true iff the traversal shall keep going after the
2178 /// completion of this function.
2179 bool
2180 harmless_harmful_filter::visit(diff* d, bool pre)
2181 {
2182  return (categorize_harmless_diff_node(d, pre)
2183  && categorize_harmful_diff_node(d, pre));
2184 }
2185 
2186 /// Part of the visiting code of the harmless_harmful_filter.
2187 ///
2188 /// This function is called after the visiting of a given diff node.
2189 /// Note that when this function is called, the visiting might not
2190 /// have taken place *if* the node (or an equivalent node) has already
2191 /// been visited.
2192 ///
2193 /// @param d the diff node that has either been visited or skipped
2194 /// (because it has already been visited during this traversing).
2195 void
2196 harmless_harmful_filter::visit_end(diff* d)
2197 {
2198  if (d->context()->diff_has_been_visited(d))
2199  {
2200  // This node or one of its equivalent node has already been
2201  // visited. That means at this moment,
2202  // harmless_harmful_filter::visit() has *not* been called prior
2203  // to this harmless_harmful_filter::visit_end() is called. In
2204  // other words, only harmless_harmful_filter::visit_begin() and
2205  // harmless_harmful_filter::visit_end() are called.
2206  //
2207  // So let's update the category of this diff node from its
2208  // canonical node.
2209  if (diff* c = d->get_canonical_diff())
2210  d->add_to_local_and_inherited_categories(c->get_local_category());
2211  }
2212 }
2213 } // end namespace filtering
2214 } // end namespace comparison
2215 } // end namespace abigail
const string_decl_base_sptr_map & inserted_data_members() const
Getter for the data members that got inserted.
bool is_type(const type_or_decl_base &tod)
Test whether a declaration is a type.
Definition: abg-ir.cc:10665
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:6882
This means the diff node (or at least one of its descendant nodes) carries a change involving two com...
const string_member_function_sptr_map & deleted_member_fns() const
This header declares filters for the diff trees resulting from comparing ABI Corpora.
const pointer_diff * is_pointer_diff(const diff *diff)
Test if a diff node is about differences between two pointers.
class_decl_sptr is_compatible_with_class_type(const type_base_sptr &t)
Test if a type is a class. This function looks through typedefs.
Definition: abg-ir.cc:10915
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:10767
var_decl_sptr first_var() const
Getter for the first var_decl of the diff.
bool is_data_member(const var_decl &v)
Test if a var_decl is a data member.
Definition: abg-ir.cc:5854
An abstraction of a diff between entities that are of a different kind (disctinct).
const qualified_type_diff * is_qualified_type_diff(const diff *diff)
Test if a diff node is about differences between two qualified types.
bool is_member_function(const function_decl &f)
Test whether a function_decl is a member function.
Definition: abg-ir.cc:6609
A diff node in this category is a function (or function type) with at least one parameter added or re...
This means the diff node (or at least one of its descendant nodes) carries a change that modifies the...
This type abstracts changes for a class_decl.
access_specifier get_member_access_specifier(const decl_base &d)
Gets the access specifier for a class member.
Definition: abg-ir.cc:5756
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:68
ssize_t get_member_function_vtable_offset(const function_decl &f)
Get the vtable offset of a member function.
Definition: abg-ir.cc:6819
const var_diff * is_var_diff(const diff *diff)
Test if a diff node is about differences between variables.
This means that a diff node in the sub-tree carries an addition of enumerator to an enum type...
bool is_mostly_distinct_diff(const diff *d)
Test if a diff node carries a distinct type change or a pointer/reference/typedef to distinct type ch...
const diff * peel_typedef_diff(const diff *dif)
If a diff node is about changes between two typedef types, get the diff node about changes between th...
This means the diff node (or at least one of its descendant nodes) carries access related changes...
A diff node in this category carries a change from void pointer to non-void pointer.
shared_ptr< var_decl > var_decl_sptr
Convenience typedef for a shared pointer on a var_decl.
Definition: abg-fwd.h:254
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:5658
uint64_t get_absolute_data_member_offset(const var_decl &m)
Get the absolute offset of a data member.
Definition: abg-ir.cc:6514
bool has_decl_only_def_change(const decl_base_sptr &first, const decl_base_sptr &second)
Test if two decl_base_sptr are different just by the fact that one is decl-only and the other one is ...
bool enum_has_non_name_change(const enum_type_decl &l, const enum_type_decl &r, change_kind *k)
Test if two enums differ, but not by a name change.
Definition: abg-ir.cc:19851
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:11178
const type_decl_diff * is_diff_of_basic_type(const diff *d)
Test if a diff node represents a diff between two basic types.
Abstraction of a diff between two function_decl.
A diff node in this category is a function parameter type which top cv-qualifiers change...
This means that a diff node in the sub-tree carries an incompatible change to a vtable.
const enum_type_decl_sptr first_enum() const
const class_diff * is_class_diff(const diff *diff)
Test if a diff node is a class_diff node.
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:158
enum_type_decl_sptr is_compatible_with_enum_type(const type_base_sptr &t)
Test if a type is an enum. This function looks through typedefs.
Definition: abg-ir.cc:10864
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:11841
This is the base class of class_diff and union_diff.
const string_decl_base_sptr_map & deleted_data_members() const
Getter for the data members that got deleted.
typedef_decl_sptr is_typedef(const type_or_decl_base_sptr t)
Test whether a type is a typedef.
Definition: abg-ir.cc:10825
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:6425
bool get_is_declaration_only() const
Test if a decl_base is a declaration-only decl.
Definition: abg-ir.cc:5194
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_harmless_name_change(const decl_base_sptr &f, const decl_base_sptr &s)
Test if two decls represents a harmless name change.
bool has_basic_type_name_change(const diff *d)
Test if a diff node carries a basic type name change.
shared_ptr< function_decl > function_decl_sptr
Convenience typedef for a shared pointer on a function_decl.
Definition: abg-fwd.h:267
decl_base * is_decl(const type_or_decl_base *d)
Test if an ABI artifact is a declaration.
Definition: abg-ir.cc:10605
bool has_harmful_name_change(const decl_base_sptr &f, const decl_base_sptr &s)
Test if two decls represents a harmful name change.
const enum_type_decl * is_enum_type(const type_or_decl_base *d)
Test if a decl is an enum_type_decl.
Definition: abg-ir.cc:10897
virtual size_t get_size_in_bits() const
Getter for the size of the type.
Definition: abg-ir.cc:16123
shared_ptr< parameter > parameter_sptr
Convenience typedef for a shared pointer on a function_decl::parameter.
Definition: abg-ir.h:3131
var_decl_sptr second_var() const
Getter for the second var_decl of the diff.
Abstraction of a diff between two function parameters.
This means that a diff node in the sub-tree carries a harmless 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:11058
The base type of class_decl and union_decl.
Definition: abg-ir.h:4028
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:7456
diff * get_canonical_diff() const
Getter for the canonical diff of the current instance of diff.
bool var_equals_modulo_types(const var_decl &l, const var_decl &r, change_kind *k)
Compares two instances of var_decl without taking their type into account.
Definition: abg-ir.cc:20890
shared_ptr< type_or_decl_base > type_or_decl_base_sptr
A convenience typedef for a shared_ptr to type_or_decl_base.
Definition: abg-fwd.h:119
void apply_filter(filter_base &filter, corpus_diff_sptr d)
Walk the diff sub-trees of a a corpus_diff and apply a filter to the nodes visted. The filter categorizes each node, assigning it into one or several categories.
const function_decl_diff * is_function_decl_diff(const diff *diff)
Test if a diff node is about differences between functions.
decl_base_sptr look_through_decl_only(const decl_base &d)
If a decl is decl-only get its definition. Otherwise, just return nil.
Definition: abg-ir.cc:11749
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:886
#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:1714
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:7550
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:11730
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:11905
const type_base_sptr get_type() const
Getter of the type of the variable.
Definition: abg-ir.cc:20759
const string_member_function_sptr_map & inserted_member_fns() const
diff_sptr type_diff() const
Getter for the diff representing the changes on the type of the function parameter involved in the cu...
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:6056
shared_ptr< enum_type_decl > enum_type_decl_sptr
Convenience typedef for shared pointer to a enum_type_decl.
Definition: abg-fwd.h:173
shared_ptr< class_decl > class_decl_sptr
Convenience typedef for a shared pointer on a class_decl.
Definition: abg-fwd.h:191
virtual void get_qualified_name(interned_string &qualified_name, bool internal=false) const
Compute the qualified name of the decl.
Definition: abg-ir.cc:5063
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:9009
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:11620
static bool entities_are_of_distinct_kinds(type_or_decl_base_sptr first, type_or_decl_base_sptr second)
Test if the two arguments are of different kind, or that are both NULL.
const string_decl_base_sptr_map & data_members_replaced_by_adms() const
Get the map of data members that got replaced by anonymous data members.
Abstracts a diff between two instances of var_decl.
bool get_member_is_static(const decl_base &d)
Gets a flag saying if a class member is static or not.
Definition: abg-ir.cc:5816
const class_or_union_diff * is_class_or_union_diff(const diff *d)
Test if a diff node is a class_or_union_diff node.
This means that a diff node in the sub-tree carries an addition or removal of a non-virtual member fu...
const reference_diff * is_reference_diff(const diff *diff)
Test if a diff node is about differences between two references.
This means that a diff node in the sub-tree carries an a symbol alias change that is harmless...
const diff * peel_reference_diff(const diff *dif)
If a diff node is about changes between two reference types, get the diff node about changes between ...
type_base_sptr peel_typedef_pointer_or_reference_type(const type_base_sptr type)
Return the leaf underlying or pointed-to type node of a typedef_decl, pointer_type_def, reference_type_def, or array_type_def node.
Definition: abg-ir.cc:7601
const type_diff_base * is_type_diff(const diff *diff)
Test if a diff node is about differences between types.
shared_ptr< corpus_diff > corpus_diff_sptr
A convenience typedef for a shared pointer to corpus_diff.
bool types_are_compatible(const type_base_sptr type1, const type_base_sptr type2)
Test if two types are equal modulo a typedef.
Definition: abg-ir.cc:10332
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:11700
const data_members & get_data_members() const
Get the data members of this class_or_union.
Definition: abg-ir.cc:23515
const class_or_union_diff * is_diff_of_class_or_union_type(const diff *d)
Test if a diff node represents a diff between two class or union types.
bool is_anonymous_data_member(const decl_base &d)
Test if a decl is an anonymous data member.
Definition: abg-ir.cc:6113
access_specifier
Access specifier for class members.
Definition: abg-ir.h:878
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:76
bool has_enum_decl_only_def_change(const enum_type_decl_sptr &first, const enum_type_decl_sptr &second)
Test if two enum_sptr are different just by the fact that one is decl-only and the other one is defin...
const distinct_diff * is_distinct_diff(const diff *diff)
Test if a diff node is about differences between two diff nodes of different kinds.
const diff * peel_pointer_diff(const diff *dif)
If a diff node is about changes between two pointer types, get the diff node about changes between th...
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:10988
shared_ptr< array_type_def > array_type_def_sptr
Convenience typedef for a shared pointer on a array_type_def.
Definition: abg-fwd.h:242
bool equals_modulo_cv_qualifier(const array_type_def *l, const array_type_def *r)
Test if two variables are equals modulo CV qualifiers.
Definition: abg-ir.cc:19436
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:11261
shared_ptr< filter_base > filter_base_sptr
Convenience typedef for a shared pointer to filter_base.
const type_base * is_void_pointer_type_equivalent(const type_base *type)
Test if a type is equivalent to a pointer to void type.
Definition: abg-ir.cc:11532
const enum_type_decl_sptr second_enum() const
CV
Bit field values representing the cv qualifiers of the underlying type.
Definition: abg-ir.h:2244
This means that a diff node in the sub-tree carries an addition or removal of a static data member...