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