libabigail
abg-comp-filter.cc
Go to the documentation of this file.
1 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
2 // -*- Mode: C++ -*-
3 //
4 // Copyright (C) 2013-2023 Red Hat, Inc.
5 //
6 // Author: Dodji Seketeli
7 
8 /// @file
9 ///
10 /// This file contains definitions of diff objects filtering
11 /// facilities.
12 
13 #include "abg-internal.h"
14 #include <memory>
15 // <headers defining libabigail's API go under here>
16 ABG_BEGIN_EXPORT_DECLARATIONS
17 
18 #include "abg-comp-filter.h"
19 #include "abg-tools-utils.h"
20 
21 ABG_END_EXPORT_DECLARATIONS
22 // </headers defining libabigail's API>
23 
24 namespace abigail
25 {
26 namespace comparison
27 {
28 namespace filtering
29 {
30 
31 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
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 
1171  function_decl_sptr ff = diff->first_function_decl(),
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
This header declares filters for the diff trees resulting from comparing ABI Corpora.
#define ABG_ASSERT(cond)
This is a wrapper around the 'assert' glibc call. It allows for its argument to have side effects,...
Definition: abg-fwd.h:1714
This type abstracts changes for a class_decl.
class_decl_sptr first_class_decl() const
class_decl_sptr second_class_decl() const
Getter of the second class involved in the diff.
This is the base class of class_diff and union_diff.
class_or_union_sptr first_class_or_union() const
const string_decl_base_sptr_map & data_members_replaced_by_adms() const
Get the map of data members that got replaced by anonymous data members.
class_or_union_sptr second_class_or_union() const
The abstraction of a change between two ABI artifacts, a.k.a an artifact change.
type_or_decl_base_sptr second_subject() const
Getter of the second subject of the diff.
type_or_decl_base_sptr first_subject() const
Getter of the first subject of the diff.
diff * get_canonical_diff() const
Getter for the canonical diff of the current instance of diff.
void add_to_local_and_inherited_categories(diff_category c)
Adds the current diff tree node to the categories resulting from the local and inherited changes of t...
virtual bool has_changes() const =0
Pure interface to get the length of the changes encapsulated by this diff. A length of zero means tha...
An abstraction of a diff between entities that are of a different kind (disctinct).
static bool entities_are_of_distinct_kinds(type_or_decl_base_sptr first, type_or_decl_base_sptr second)
Test if the two arguments are of different kind, or that are both NULL.
Abstraction of a diff between two enums.
const enum_type_decl_sptr first_enum() const
const enum_type_decl_sptr second_enum() const
Abstraction of a diff between two function parameters.
diff_sptr type_diff() const
Getter for the diff representing the changes on the type of the function parameter involved in the cu...
Abstraction of a diff between two function_decl.
Abstraction of a diff between two basic type declarations.
The base class of diff between types.
Abstracts a diff between two instances of var_decl.
var_decl_sptr first_var() const
Getter for the first var_decl of the diff.
var_decl_sptr second_var() const
Getter for the second var_decl of the diff.
The base type of class_decl and union_decl.
Definition: abg-ir.h:4029
const data_members & get_data_members() const
Get the data members of this class_or_union.
Definition: abg-ir.cc:23515
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
bool get_is_declaration_only() const
Test if a decl_base is a declaration-only decl.
Definition: abg-ir.cc:5194
shared_ptr< parameter > parameter_sptr
Convenience typedef for a shared pointer on a function_decl::parameter.
Definition: abg-ir.h:3131
CV
Bit field values representing the cv qualifiers of the underlying type.
Definition: abg-ir.h:2245
virtual size_t get_size_in_bits() const
Getter for the size of the type.
Definition: abg-ir.cc:16123
bool has_basic_type_name_change(const diff *d)
Test if a diff node carries a basic type name change.
bool has_enum_decl_only_def_change(const enum_type_decl_sptr &first, const enum_type_decl_sptr &second)
Test if two enum_sptr are different just by the fact that one is decl-only and the other one is defin...
bool has_class_decl_only_def_change(const class_or_union_sptr &first, const class_or_union_sptr &second)
Test if two class_or_union_sptr are different just by the fact that one is decl-only and the other on...
bool has_data_member_replaced_by_anon_dm(const diff *diff)
Test if a class_or_union_diff has a data member replaced by an anonymous data member in a harmless wa...
bool has_harmless_name_change(const decl_base_sptr &f, const decl_base_sptr &s)
Test if two decls represents a harmless name change.
bool is_var_1_dim_unknown_size_array_change(const var_decl_sptr &var1, const var_decl_sptr &var2)
Test if we are looking at two variables which types are both one dimension array, with one of them be...
bool is_decl_only_class_with_size_change(const class_or_union &first, const class_or_union &second)
Test if two classes that are decl-only (have the decl-only flag and carry no data members) but are di...
shared_ptr< filter_base > filter_base_sptr
Convenience typedef for a shared pointer to filter_base.
bool has_harmful_name_change(const decl_base_sptr &f, const decl_base_sptr &s)
Test if two decls represents a harmful name change.
bool has_class_or_union_type_name_change(const diff *d)
Test if a diff node carries a class or union type name change.
bool has_decl_only_def_change(const decl_base_sptr &first, const decl_base_sptr &second)
Test if two decl_base_sptr are different just by the fact that one is decl-only and the other one is ...
bool has_basic_or_class_type_name_change(const diff *d)
Test if a diff node carries a basic or class type name change.
bool has_anonymous_data_member_change(const diff *d)
Test if a diff node carries a non-anonymous data member to anonymous data member change,...
void apply_filter(filter_base &filter, corpus_diff_sptr d)
Walk the diff sub-trees of a a corpus_diff and apply a filter to the nodes visted....
bool is_mostly_distinct_diff(const diff *d)
Test if a diff node carries a distinct type change or a pointer/reference/typedef to distinct type ch...
bool union_diff_has_harmless_changes(const diff *d)
Test if a union diff node does have changes that don't impact its size.
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...
shared_ptr< diff > diff_sptr
Convenience typedef for a shared_ptr for the diff class.
Definition: abg-fwd.h:76
diff_category
An enum for the different categories that a diff tree node falls into, regarding the kind of changes ...
@ ACCESS_CHANGE_CATEGORY
This means the diff node (or at least one of its descendant nodes) carries access related changes,...
@ HARMLESS_DATA_MEMBER_CHANGE_CATEGORY
This means that a diff node in the sub-tree carries a harmless data member change....
@ VIRTUAL_MEMBER_CHANGE_CATEGORY
This means that a diff node in the sub-tree carries an incompatible change to a vtable.
@ SIZE_OR_OFFSET_CHANGE_CATEGORY
This means the diff node (or at least one of its descendant nodes) carries a change that modifies the...
@ NON_VIRT_MEM_FUN_CHANGE_CATEGORY
This means that a diff node in the sub-tree carries an addition or removal of a non-virtual member fu...
@ HARMLESS_ENUM_CHANGE_CATEGORY
This means that a diff node in the sub-tree carries an addition of enumerator to an enum type.
@ FN_PARM_ADD_REMOVE_CHANGE_CATEGORY
A diff node in this category is a function (or function type) with at least one parameter added or re...
@ VOID_PTR_TO_PTR_CHANGE_CATEGORY
A diff node in this category carries a change from void pointer to non-void pointer.
@ COMPATIBLE_TYPE_CHANGE_CATEGORY
This means the diff node (or at least one of its descendant nodes) carries a change involving two com...
@ TYPE_DECL_ONLY_DEF_CHANGE_CATEGORY
This means that a diff node in the sub-tree carries a type that was declaration-only and that is now ...
@ STATIC_DATA_MEMBER_CHANGE_CATEGORY
This means that a diff node in the sub-tree carries an addition or removal of a static data member.
@ HARMLESS_UNION_OR_CLASS_CHANGE_CATEGORY
This means that a diff node in the sub-tree carries a harmless union or class change.
@ HARMLESS_DECL_NAME_CHANGE_CATEGORY
This means that a diff node in the sub-tree carries a harmless declaration name change....
@ NO_CHANGE_CATEGORY
This means the diff node does not carry any (meaningful) change, or that it carries changes that have...
@ BENIGN_INFINITE_ARRAY_CHANGE_CATEGORY
A diff node in this category carries a change in the size of the array type of a global variable,...
@ VAR_TYPE_CV_CHANGE_CATEGORY
A diff node in this category is for a variable which type holds a cv-qualifier change.
@ FN_PARM_TYPE_CV_CHANGE_CATEGORY
A diff node in this category has a function parameter type with a cv-qualifiers change.
@ FN_PARM_TYPE_TOP_CV_CHANGE_CATEGORY
A diff node in this category is a function parameter type which top cv-qualifiers change.
@ FN_RETURN_TYPE_CV_CHANGE_CATEGORY
A diff node in this category is a function return type with a cv-qualifier change.
@ HARMLESS_SYMBOL_ALIAS_CHANGE_CATEGORY
This means that a diff node in the sub-tree carries an a symbol alias change that is harmless.
const class_or_union_diff * is_diff_of_class_or_union_type(const diff *d)
Test if a diff node represents a diff between two class or union types.
const pointer_diff * is_pointer_diff(const diff *diff)
Test if a diff node is about differences between two pointers.
shared_ptr< diff_context > diff_context_sptr
Convenience typedef for a shared pointer of diff_context.
Definition: abg-fwd.h:68
const diff * peel_typedef_diff(const diff *dif)
If a diff node is about changes between two typedef types, get the diff node about changes between th...
const function_decl_diff * is_function_decl_diff(const diff *diff)
Test if a diff node is about differences between functions.
const function_type_diff * is_function_type_diff(const diff *diff)
Test if a diff node is a function_type_diff node.
const distinct_diff * is_distinct_diff(const diff *diff)
Test if a diff node is about differences between two diff nodes of different kinds.
const diff * peel_reference_diff(const diff *dif)
If a diff node is about changes between two reference types, get the diff node about changes between ...
const fn_parm_diff * is_fn_parm_diff(const diff *diff)
Test if a diff node is about differences between two function parameters.
const union_diff * is_union_diff(const diff *diff)
Test if a diff node is a union_diff node.
const diff * peel_pointer_diff(const diff *dif)
If a diff node is about changes between two pointer types, get the diff node about changes between th...
const class_or_union_diff * is_class_or_union_diff(const diff *d)
Test if a diff node is a class_or_union_diff node.
const class_diff * is_class_diff(const diff *diff)
Test if a diff node is a class_diff node.
const type_decl_diff * is_diff_of_basic_type(const diff *d)
Test if a diff node represents a diff between two basic types.
const qualified_type_diff * is_qualified_type_diff(const diff *diff)
Test if a diff node is about differences between two qualified types.
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,...
const var_diff * is_var_diff(const diff *diff)
Test if a diff node is about differences between variables.
const type_diff_base * is_type_diff(const diff *diff)
Test if a diff node is about differences between types.
const reference_diff * is_reference_diff(const diff *diff)
Test if a diff node is about differences between two references.
shared_ptr< corpus_diff > corpus_diff_sptr
A convenience typedef for a shared pointer to corpus_diff.
const diff * peel_typedef_or_qualified_type_diff(const diff *dif)
If a diff node is about changes between two typedefs or qualified types, get the diff node about chan...
bool enum_has_non_name_change(const enum_type_decl &l, const enum_type_decl &r, change_kind *k)
Test if two enums differ, but not by a name change.
Definition: abg-ir.cc:19851
const type_base * peel_qualified_type(const type_base *type)
Return the leaf underlying type of a qualified type.
Definition: abg-ir.cc:7456
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
shared_ptr< function_decl > function_decl_sptr
Convenience typedef for a shared pointer on a function_decl.
Definition: abg-fwd.h:267
access_specifier
Access specifier for class members.
Definition: abg-ir.h:879
ssize_t get_member_function_vtable_offset(const function_decl &f)
Get the vtable offset of a member function.
Definition: abg-ir.cc:6819
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_type(const type_or_decl_base &tod)
Test whether a declaration is a type.
Definition: abg-ir.cc:10665
bool is_anonymous_data_member(const decl_base &d)
Test if a decl is an anonymous data member.
Definition: abg-ir.cc:6113
shared_ptr< elf_symbol > elf_symbol_sptr
A convenience typedef for a shared pointer to elf_symbol.
Definition: abg-ir.h:886
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
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< array_type_def > array_type_def_sptr
Convenience typedef for a shared pointer on a array_type_def.
Definition: abg-fwd.h:242
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
shared_ptr< class_decl > class_decl_sptr
Convenience typedef for a shared pointer on a class_decl.
Definition: abg-fwd.h:191
type_base_sptr peel_typedef_pointer_or_reference_type(const type_base_sptr type)
Return the leaf underlying or pointed-to type node of a typedef_decl, pointer_type_def,...
Definition: abg-ir.cc:7601
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
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
typedef_decl_sptr is_typedef(const type_or_decl_base_sptr t)
Test whether a type is a typedef.
Definition: abg-ir.cc:10825
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
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
shared_ptr< var_decl > var_decl_sptr
Convenience typedef for a shared pointer on a var_decl.
Definition: abg-fwd.h:254
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
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
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
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 is_member_function(const function_decl &f)
Test whether a function_decl is a member function.
Definition: abg-ir.cc:6609
var_decl * is_var_decl(const type_or_decl_base *tod)
Tests if a declaration is a variable declaration.
Definition: abg-ir.cc:11841
decl_base * is_decl(const type_or_decl_base *d)
Test if an ABI artifact is a declaration.
Definition: abg-ir.cc:10605
access_specifier get_member_access_specifier(const decl_base &d)
Gets the access specifier for a class member.
Definition: abg-ir.cc:5756
shared_ptr< enum_type_decl > enum_type_decl_sptr
Convenience typedef for shared pointer to a enum_type_decl.
Definition: abg-fwd.h:173
uint64_t get_data_member_offset(const var_decl &m)
Get the offset of a data member.
Definition: abg-ir.cc:6425
bool get_member_function_is_virtual(const function_decl &f)
Test if a given member function is virtual.
Definition: abg-ir.cc:6882
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
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
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
bool is_data_member(const var_decl &v)
Test if a var_decl is a data member.
Definition: abg-ir.cc:5854
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
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
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
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
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
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
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
bool is_member_decl(const decl_base_sptr d)
Tests if a declaration is a class member.
Definition: abg-ir.cc:5658
bool decl_names_equal(const string &l, const string &r)
Compare two fully qualified decl names by taking into account that they might have compontents that a...
Toplevel namespace for libabigail.
The base class for the diff tree node filter.