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