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