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