libabigail
abg-reporter-priv.cc
1// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
2// -*- Mode: C++ -*-
3//
4// Copyright (C) 2017-2025 Red Hat, Inc.
5//
6// Author: Dodji Seketeli
7
8#include <libgen.h>
9#include <algorithm>
10#include "abg-comparison-priv.h"
11#include "abg-reporter-priv.h"
12
13namespace abigail
14{
15
16namespace comparison
17{
18
19/// Convert a number in bits into a number in bytes.
20///
21/// @param bits the number in bits to convert.
22///
23/// @return the number @p bits converted into bytes.
24uint64_t
26{return bits / 8;}
27
28/// Emit a numerical value to an output stream.
29///
30/// Depending on the current @ref diff_context, the number is going to
31/// be emitted either in decimal or hexadecimal base.
32///
33/// @param value the value to emit.
34///
35/// @param ctxt the current diff context.
36///
37/// @param out the output stream to emit the numerical value to.
38void
39emit_num_value(uint64_t value, const diff_context& ctxt, ostream& out)
40{
41 if (ctxt.show_hex_values())
42 out << std::hex << std::showbase ;
43 else
44 out << std::dec;
45 out << value << std::dec << std::noshowbase;
46}
47
48/// Convert a bits value into a byte value if the current diff context
49/// instructs us to do so.
50///
51/// @param bits the bits value to convert.
52///
53/// @param ctxt the current diff context to consider.
54///
55/// @return the resulting bits or bytes value, depending on what the
56/// diff context instructs us to do.
57uint64_t
58maybe_convert_bits_to_bytes(uint64_t bits, const diff_context& ctxt)
59{
61 return bits;
62 return convert_bits_to_bytes(bits);
63}
64
65/// Emit a message showing the numerical change between two values, to
66/// a given output stream.
67///
68/// The function emits a message like
69///
70/// "XXX changes from old_bits to new_bits (in bits)"
71///
72/// or
73///
74/// "XXX changes from old_bits to new_bits (in bytes)"
75///
76/// Depending on if the current diff context instructs us to emit the
77/// change in bits or bytes. XXX is the string content of the @p what
78/// parameter.
79///
80/// @param what the string that tells us what the change represents.
81/// This is the "XXX" we refer to in the explanation above.
82///
83/// @param old_bits the initial value (which changed) in bits.
84///
85/// @param new_bits the final value (resulting from the change or @p
86/// old_bits) in bits.
87///
88/// @param ctxt the current diff context to consider.
89///
90/// @param out the output stream to send the change message to.
91///
92/// @param show_bits_or_byte if this is true, then the message is
93/// going to precise if the changed value is in bits or bytes.
94/// Otherwise, no mention of that is made.
95void
96show_numerical_change(const string& what,
97 uint64_t old_bits,
98 uint64_t new_bits,
99 const diff_context& ctxt,
100 ostream& out,
101 bool show_bits_or_byte)
102{
103 bool can_convert_bits_to_bytes = (old_bits % 8 == 0 && new_bits % 8 == 0);
104 uint64_t o = can_convert_bits_to_bytes
105 ? maybe_convert_bits_to_bytes(old_bits, ctxt)
106 : old_bits;
107 uint64_t n = can_convert_bits_to_bytes
108 ? maybe_convert_bits_to_bytes(new_bits, ctxt)
109 : new_bits;
110 string bits_or_bytes =
111 (!can_convert_bits_to_bytes || ctxt.show_offsets_sizes_in_bits ())
112 ? "bits"
113 : "bytes";
114
115 out << what << " changed from ";
116 emit_num_value(o, ctxt, out);
117 out << " to ";
118 emit_num_value(n, ctxt, out);
119 if (show_bits_or_byte)
120 {
121 out << " (in ";
122 out << bits_or_bytes;
123 out << ")";
124 }
125}
126
127/// Emit a message showing the value of a numerical value representing
128/// a size or an offset, preceded by a string. The message is ended
129/// by a part which says if the value is in bits or bytes.
130///
131/// @param what the string prefix of the message to emit.
132///
133/// @param value the numerical value to emit.
134///
135/// @param ctxt the diff context to take into account.
136///
137/// @param out the output stream to emit the message to.
138void
139show_offset_or_size(const string& what,
140 uint64_t value,
141 const diff_context& ctxt,
142 ostream& out)
143{
144 uint64_t v = value;
145 bool can_convert_bits_to_bytes = (value % 8 == 0);
146 if (can_convert_bits_to_bytes)
147 v = maybe_convert_bits_to_bytes(v, ctxt);
148 string bits_or_bytes =
149 (!can_convert_bits_to_bytes || ctxt.show_offsets_sizes_in_bits())
150 ? "bits"
151 : "bytes";
152
153 if (!what.empty())
154 out << what << " ";
155 emit_num_value(v, ctxt, out);
156 out << " (in " << bits_or_bytes << ")";
157}
158
159/// Emit a message showing the value of a numerical value representing
160/// a size or an offset. The message is ended by a part which says if
161/// the value is in bits or bytes.
162///
163/// @param value the numerical value to emit.
164///
165/// @param ctxt the diff context to take into account.
166///
167/// @param out the output stream to emit the message to.
168void
169show_offset_or_size(uint64_t value,
170 const diff_context& ctxt,
171 ostream& out)
172{show_offset_or_size("", value, ctxt, out);}
173
174/// Stream a string representation for a member function.
175///
176/// @param ctxt the current diff context.
177///
178/// @param mem_fn the member function to stream
179///
180/// @param out the output stream to send the representation to
181void
183 method_decl_sptr mem_fn,
184 ostream& out)
185{
186 if (!mem_fn || !is_member_function(mem_fn))
187 return;
188
189 method_decl_sptr meth =
190 dynamic_pointer_cast<method_decl>(mem_fn);
191 ABG_ASSERT(meth);
192
193 out << "'" << mem_fn->get_pretty_representation() << "'";
194 report_loc_info(meth, ctxt, out);
196 {
197
198 ssize_t voffset = get_member_function_vtable_offset(mem_fn);
199 ssize_t biggest_voffset =
200 is_class_type(meth->get_type()->get_class_type())->
201 get_biggest_vtable_offset();
202 if (voffset > -1)
203 {
204 out << ", virtual at voffset ";
206 ctxt, out);
207 out << "/";
208 emit_num_value(biggest_voffset, ctxt, out);
209 }
210 }
211
212 if (ctxt.show_linkage_names()
213 && (mem_fn->get_symbol()))
214 {
215 out << " {"
216 << mem_fn->get_symbol()->get_id_string()
217 << "}";
218 }
219 out << "\n";
220}
221
222/// Stream a string representation for a data member.
223///
224/// @param d the data member to stream
225///
226/// @param ctxt the current diff context.
227///
228/// @param out the output stream to send the representation to
229///
230/// @param indent the indentation string to use for the change report.
231void
233 const diff_context_sptr& ctxt,
234 ostream& out,
235 const string& indent)
236{
237 if (!is_data_member(d)
239 return;
240
241 out << indent
242 << "'"
243 << d->get_pretty_representation(/*internal=*/false,
244 /*qualified_name=*/false)
245 << "'";
246 if (!get_member_is_static(d))
247 {
248 // Do not emit offset information for data member of a union
249 // type because all data members of a union are supposed to be
250 // at offset 0.
251 if (!is_union_type(d->get_scope()))
252 show_offset_or_size(", at offset",
254 *ctxt, out);
255 report_loc_info(d, *ctxt, out);
256 }
257 out << "\n";
258}
259
260/// If a given @ref var_diff node carries a data member change in
261/// which the offset of the data member actually changed, then emit a
262/// string (to an output stream) that represents that offset change.
263///
264/// For instance, if the offset of the data member increased by 32
265/// bits then the string emitted is going to be "by +32 bits".
266///
267/// If, on the other hand, the offset of the data member decreased by
268/// 64 bits then the string emitted is going to be "by -64 bits".
269///
270/// This function is a sub-routine used by the reporting system.
271///
272/// @param diff the diff node that potentially carries the data member
273/// change.
274///
275/// @param ctxt the context in which the diff is being reported.
276///
277/// @param out the output stream to emit the string to.
278void
280 diff_context& ctxt,
281 ostream& out)
282{
284 return;
285
286 var_decl_sptr o = diff->first_var();
287 var_decl_sptr n = diff->second_var();
288
289 uint64_t first_offset = get_data_member_offset(o),
290 second_offset = get_data_member_offset(n);
291
292 string sign;
293 uint64_t change = 0;
294 if (first_offset < second_offset)
295 {
296 sign = "+";
297 change = second_offset - first_offset;
298 }
299 else if (first_offset > second_offset)
300 {
301 sign = "-";
302 change = first_offset - second_offset;
303 }
304 else
305 return;
306
307 if (!ctxt.show_offsets_sizes_in_bits())
308 change = convert_bits_to_bytes(change);
309
310 string bits_or_bytes = ctxt.show_offsets_sizes_in_bits()
311 ? "bits"
312 : "bytes";
313
314 out << " (by " << sign;
315 emit_num_value(change, ctxt, out);
316 out << " " << bits_or_bytes << ")";
317}
318
319/// If a given @ref var_diff node carries a hange in which the size of
320/// the variable actually changed, then emit a string (to an output
321/// stream) that represents that size change.
322///
323/// For instance, if the size of the variable increased by 32 bits
324/// then the string emitted is going to be "by +32 bits".
325///
326/// If, on the other hand, the size of the variable decreased by 64
327/// bits then the string emitted is going to be "by -64 bits".
328///
329/// This function is a sub-routine used by the reporting system.
330///
331/// @param diff the diff node that potentially carries the variable
332/// change.
333///
334/// @param ctxt the context in which the diff is being reported.
335///
336/// @param out the output stream to emit the string to.
337void
339 diff_context& ctxt,
340 ostream& out)
341{
343 return;
344
345 var_decl_sptr o = diff->first_var();
346 var_decl_sptr n = diff->second_var();
347
348 uint64_t first_size = get_var_size_in_bits(o),
349 second_size = get_var_size_in_bits(n);
350
351 string sign;
352 uint64_t change = 0;
353 if (first_size < second_size)
354 {
355 sign = "+";
356 change = second_size - first_size;
357 }
358 else if (first_size > second_size)
359 {
360 sign = "-";
361 change = first_size - second_size;
362 }
363 else
364 return;
365
366 if (!ctxt.show_offsets_sizes_in_bits())
367 change = convert_bits_to_bytes(change);
368
369 string bits_or_bytes = ctxt.show_offsets_sizes_in_bits()
370 ? "bits"
371 : "bytes";
372
373 out << " (by " << sign;
374 emit_num_value(change, ctxt, out);
375 out << " " << bits_or_bytes << ")";
376}
377
378/// Represent the changes carried by an instance of @ref var_diff that
379/// represent a difference between two class data members.
380///
381/// @param diff diff the diff node to represent.
382///
383/// @param ctxt the diff context to use.
384///
385/// @param local_only if true, only display local changes.
386///
387/// @param out the output stream to send the representation to.
388///
389/// @param indent the indentation string to use for the change report.
390void
393 ostream& out,
394 const string& indent,
395 bool local_only)
396{
397 if (!ctxt->get_reporter()->diff_to_be_reported(diff.get()))
398 return;
399
400 const var_decl_sptr o = diff->first_var();
401 const var_decl_sptr n = diff->second_var();
402 const bool o_anon = !!is_anonymous_data_member(o);
403 const bool n_anon = !!is_anonymous_data_member(n);
404 const bool is_strict_anonymous_data_member_change = o_anon && n_anon;
405 const string o_name = (is_data_member_of_anonymous_class_or_union(o)
406 ? o->get_name()
407 : o->get_qualified_name());
408 const string n_name = (is_data_member_of_anonymous_class_or_union(n)
409 ? n->get_name()
410 : n->get_qualified_name());
411 const uint64_t o_size = get_var_size_in_bits(o);
412 const uint64_t n_size = get_var_size_in_bits(n);
413 const uint64_t o_offset = get_data_member_offset(o);
414 const uint64_t n_offset = get_data_member_offset(n);
415 const string o_pretty_representation =
416 o->get_pretty_representation(/*internal=*/false, /*qualified_name=*/false);
417 // no n_pretty_representation here as it's only needed in a couple of places
418 const bool show_size_offset_changes = ctxt->get_allowed_category()
420
421 // Has the main diff text been output?
422 bool emitted = false;
423 // Are we continuing on a new line? (implies emitted)
424 bool begin_with_and = false;
425 // Have we reported a size change already?
426 bool size_reported = false;
427
428 //----------------------------------------------------------------
429 // First we'll try to emit a report about the type change of this
430 // var_decl_diff.
431 //
432 // In the context of that type change report, we need to keep in
433 // mind that because we want to emit specific (useful) reports about
434 // anonymous data member changes, we'll try to detect the various
435 // scenarii that involve anonymous data member changes.
436 //
437 // Then, as a fallback method, we'll emit a more generic type change
438 // report for the other generic type changes.
439 //----------------------------------------------------------------
440
441 if (is_strict_anonymous_data_member_change)
442 {
443 const string n_pretty_representation =
444 n->get_pretty_representation(/*internal=*/false,
445 /*qualified_name=*/false);
446 const type_base_sptr o_type = o->get_type(), n_type = n->get_type();
447 if (o_pretty_representation != n_pretty_representation)
448 {
449 show_offset_or_size(indent + "anonymous data member at offset",
450 o_offset, *ctxt, out);
451
452 out << " changed from:\n"
453 << indent << " " << o_pretty_representation << "\n"
454 << indent << "to:\n"
455 << indent << " " << n_pretty_representation << "\n";
456
457 begin_with_and = true;
458 emitted = true;
459 }
460 else if (get_type_name(o_type) != get_type_name(n_type)
461 && is_decl(o_type) && is_decl(n_type)
462 && is_decl(o_type)->get_is_anonymous()
463 && is_decl(n_type)->get_is_anonymous())
464 {
465 out << indent << "while looking at anonymous data member '"
466 << o_pretty_representation << "':\n"
467 << indent << "the internal name of that anonymous data member"
468 " changed from:\n"
469 << indent << " " << get_type_name(o_type) << "\n"
470 << indent << "to:\n"
471 << indent << " " << get_type_name(n_type) << "\n"
472 << indent << " This is usually due to "
473 << "an anonymous member type being added or removed from "
474 << "the containing type\n";
475
476 begin_with_and = true;
477 emitted = true;
478 }
479 }
481 {
482 ABG_ASSERT(o_anon != n_anon);
483 // So we are looking at a non-anonymous data member change from
484 // or to an anonymous data member.
485 const string n_pretty_representation =
486 n->get_pretty_representation(/*internal=*/false,
487 /*qualified_name=*/false);
488 out << indent << (o_anon ? "anonymous " : "")
489 << "data member " << o_pretty_representation;
490 show_offset_or_size(" at offset", o_offset, *ctxt, out);
491 out << " became " << (n_anon ? "anonymous " : "")
492 << "data member '" << n_pretty_representation << "'\n";
493
494 begin_with_and = true;
495 emitted = true;
496 }
497
498 //
499 // If we haven't succeeded in emitting a specific type change report
500 // (mainly related to anonymous data data member changes) then let's
501 // try to emit a more generic report about the type change.
502 //
503 // This is the fallback method outlined in the comment at the
504 // beginning of this section.
505 //
506 if (!emitted)
507 if (const diff_sptr d = diff->type_diff())
508 {
509 if (ctxt->get_reporter()->diff_to_be_reported(d.get()))
510 {
511 if (local_only)
512 out << indent << "type '"
513 << get_pretty_representation(o->get_type())
514 << "' of '"
515 << (o_anon ?
516 string("anonymous data member")
517 : o->get_qualified_name())
518 << "' changed";
519 else
520 out << indent
521 << "type of '"<< (o_anon ? "anonymous data member ": "")
522 << o_pretty_representation << "' changed";
523
524 if (d->currently_reporting())
525 out << ", as being reported\n";
526 else if (d->reported_once())
527 out << ", as reported earlier\n";
528 else
529 {
530 out << ":\n";
531 d->report(out, indent + " ");
532 }
533
534 begin_with_and = true;
535 emitted = true;
536 size_reported = true;
537 }
538 }
539
540 //
541 // Okay, now we are done with report type changes. Let's report the
542 // other potential kinds of changes.
543 //
544
545 if (!filtering::has_anonymous_data_member_change(diff) && o_name != n_name)
546 {
548 && !(ctxt->get_allowed_category()
550 ;
551 else
552 {
553 if (begin_with_and)
554 {
555 out << indent << "and ";
556 begin_with_and = false;
557 }
558 else if (!emitted)
559 out << indent;
560 else
561 out << ", ";
562 out << "name of '" << o_name << "' changed to '" << n_name << "'";
563 report_loc_info(n, *ctxt, out);
564 emitted = true;
565 }
566 }
567
570 {
571 if (begin_with_and)
572 {
573 out << indent << "and ";
574 begin_with_and = false;
575 }
576 else if (!emitted)
577 out << indent << "'" << o_pretty_representation << "' ";
578 else
579 out << ", ";
581 out << "is no more laid out";
582 else
583 out << "now becomes laid out";
584 emitted = true;
585 }
586 if (show_size_offset_changes)
587 {
588 if (o_offset != n_offset)
589 {
590 if (begin_with_and)
591 {
592 out << indent << "and ";
593 begin_with_and = false;
594 }
595 else if (!emitted)
596 {
597 out << indent;
598 if (is_strict_anonymous_data_member_change)
599 out << "anonymous data member ";
600 out << "'" << o_pretty_representation << "' ";
601 }
602 else
603 out << ", ";
604
605 show_numerical_change("offset", o_offset, n_offset, *ctxt, out);
607 emitted = true;
608 }
609
610 if (!size_reported && o_size != n_size)
611 {
612 if (begin_with_and)
613 {
614 out << indent << "and ";
615 begin_with_and = false;
616 }
617 else if (!emitted)
618 {
619 out << indent;
620 if (is_strict_anonymous_data_member_change)
621 out << "anonymous data member ";
622 out << "'" << o_pretty_representation << "' ";
623 }
624 else
625 out << ", ";
626
627 show_numerical_change("size", o_size, n_size, *ctxt, out);
629 emitted = true;
630 }
631 }
632 if (o->get_binding() != n->get_binding())
633 {
634 if (begin_with_and)
635 {
636 out << indent << "and ";
637 begin_with_and = false;
638 }
639 else if (!emitted)
640 out << indent << "'" << o_pretty_representation << "' ";
641 else
642 out << ", ";
643 out << "elf binding changed from " << o->get_binding()
644 << " to " << n->get_binding();
645 emitted = true;
646 }
647 if (o->get_visibility() != n->get_visibility())
648 {
649 if (begin_with_and)
650 {
651 out << indent << "and ";
652 begin_with_and = false;
653 }
654 else if (!emitted)
655 out << indent << "'" << o_pretty_representation << "' ";
656 else
657 out << ", ";
658 out << "visibility changed from " << o->get_visibility()
659 << " to " << n->get_visibility();
660 emitted = true;
661 }
662 if ((ctxt->get_allowed_category() & ACCESS_CHANGE_CATEGORY)
665 {
666 if (begin_with_and)
667 {
668 out << indent << "and ";
669 begin_with_and = false;
670 }
671 else if (!emitted)
672 out << indent << "'" << o_pretty_representation << "' ";
673 else
674 out << ", ";
675
676 out << "access changed from '"
678 << "' to '"
679 << get_member_access_specifier(n) << "'";
680 emitted = true;
681 }
684 {
685 if (begin_with_and)
686 {
687 out << indent << "and ";
688 begin_with_and = false;
689 }
690 else if (!emitted)
691 out << indent << "'" << o_pretty_representation << "' ";
692 else
693 out << ", ";
694
696 out << "is no more static";
697 else
698 out << "now becomes static";
699 emitted = true;
700 }
701
702 if (begin_with_and)
703 // do nothing as begin_with_and implies emitted
704 ;
705 else if (!emitted)
706 // We appear to have fallen off the edge of the map.
707 out << indent << "'" << o_pretty_representation
708 << "' has *some* difference - please report as a bug";
709 else
710 {
711 ;// do nothing
712 }
713 emitted = true;
714
715 if (!begin_with_and)
716 out << "\n";
717}
718
719/// Represent the changes carried by an instance of @ref subrange_diff
720/// that represent a difference between two ranges.
721///
722/// @param diff diff the diff node to represent.
723///
724/// @param ctxt the diff context to use.
725///
726/// @param local_only if true, only display local changes.
727///
728/// @param out the output stream to send the representation to.
729///
730/// @param indent the indentation string to use for the change report.
731void
733 const diff_context_sptr ctxt,
734 ostream& out,
735 const string& indent,
736 bool local_only)
737{
740 string oor = o->get_pretty_representation();
741 string nr = n->get_pretty_representation();
742 string on = o->get_name();
743 string nn = n->get_name();
744 int64_t olb = o->get_lower_bound();
745 int64_t nlb = n->get_lower_bound();
746 int64_t oub = o->get_upper_bound();
747 int64_t nub = n->get_upper_bound();
748
749 if (on != nn)
750 {
751 out << indent << "name of range changed from '"
752 << on << "' to '" << nn << "'\n";
753 }
754
755 if (olb != nlb)
756 {
757 out << indent << "lower bound of '" << oor << "' change from '";
758 emit_num_value(olb, *ctxt, out);
759 out << "' to '";
760 emit_num_value(nlb, *ctxt, out);
761 out << "'\n";
762 }
763
764 if (oub != nub)
765 {
766 out << indent << "upper bound of '" << oor << "' change from '";
767 emit_num_value(oub, *ctxt, out);
768 out << "' to '";
769 emit_num_value(nub, *ctxt, out);
770 out << "'\n";
771 }
772
773 if (o->is_non_finite() != n->is_non_finite())
774 {
775 out << indent
776 << (o->is_non_finite()
777 ? string("unknown sized")
778 : string("known sized"))
779 << string(" range '")
780 << oor
781 << string("' changed to ")
782 << (n->is_non_finite()
783 ? string("unknown sized")
784 : string("known sized"))
785 << string(" range '")
786 << nr
787 << "'\n";
788 }
789
790 if (!local_only)
791 {
793 if (dif && dif->to_be_reported())
794 {
795 // report range underlying type changes
796 out << indent << "underlying type of range '"
797 << oor << "' changed:\n";
798 dif->report(out, indent + " ");
799 }
800 }
801}
802
803/// Report the size and alignment changes of a type.
804///
805/// @param first the first type to consider.
806///
807/// @param second the second type to consider.
808///
809/// @param ctxt the content of the current diff.
810///
811/// @param out the output stream to report the change to.
812///
813/// @param indent the string to use for indentation.
814void
818 ostream& out,
819 const string& indent)
820{
821 type_base_sptr f = dynamic_pointer_cast<type_base>(first),
822 s = dynamic_pointer_cast<type_base>(second);
823
824 if (!s || !f)
825 return;
826
827 class_or_union_sptr first_class = is_class_or_union_type(first),
828 second_class = is_class_or_union_type(second);
829
830 if (filtering::has_class_decl_only_def_change(first_class, second_class))
831 // So these two classes differ only by the fact that one is the
832 // declaration-only form of the second. The declaration-only class
833 // is of unknown size (recorded as 0) and it is not meaningful to
834 // report a size change.
835 return;
836
837 unsigned fs = f->get_size_in_bits(), ss = s->get_size_in_bits(),
838 fa = f->get_alignment_in_bits(), sa = s->get_alignment_in_bits();
839 array_type_def_sptr first_array = is_array_type(is_type(first)),
840 second_array = is_array_type(is_type(second));
841 unsigned fdc = first_array ? first_array->get_dimension_count(): 0,
842 sdc = second_array ? second_array->get_dimension_count(): 0;
843
844 if (ctxt->get_allowed_category() & SIZE_OR_OFFSET_CHANGE_CATEGORY)
845 {
846 if (fs != ss || fdc != sdc)
847 {
848 if (first_array && second_array)
849 {
850 // We are looking at size or alignment changes between two
851 // arrays ...
852 out << indent << "array type size changed from ";
853 if (first_array->is_non_finite())
854 out << "\'unknown\'";
855 else
856 emit_num_value(first_array->get_size_in_bits(), *ctxt, out);
857 out << " to ";
858 if (second_array->is_non_finite())
859 out << "\'unknown\'";
860 else
861 emit_num_value(second_array->get_size_in_bits(), *ctxt, out);
862 out << "\n";
863
864 if (sdc != fdc)
865 {
866 out << indent + " "
867 << "number of dimensions changed from "
868 << fdc
869 << " to "
870 << sdc
871 << "\n";
872 }
873 array_type_def::subranges_type::const_iterator i, j;
874 for (i = first_array->get_subranges().begin(),
875 j = second_array->get_subranges().begin();
876 (i != first_array->get_subranges().end()
877 && j != second_array->get_subranges().end());
878 ++i, ++j)
879 {
880 if ((*i)->get_length() != (*j)->get_length())
881 {
882 out << indent
883 << "array type subrange "
884 << i - first_array->get_subranges().begin() + 1
885 << " changed length from ";
886
887 if ((*i)->is_non_finite())
888 out << "\'unknown\'";
889 else
890 out << (*i)->get_length();
891
892 out << " to ";
893
894 if ((*j)->is_non_finite())
895 out << "\'unknown\'";
896 else
897 out << (*j)->get_length();
898 out << "\n";
899 }
900 }
901 } // end if (first_array && second_array)
902 else if (fs != ss)
903 {
904 out << indent;
905 show_numerical_change("type size", fs, ss, *ctxt, out);
906 out << "\n";
907 }
908 } // end if (fs != ss || fdc != sdc)
909 else
910 if (ctxt->show_relative_offset_changes())
911 {
912 out << indent << "type size hasn't changed\n";
913 }
914 }
915 if ((ctxt->get_allowed_category() & SIZE_OR_OFFSET_CHANGE_CATEGORY)
916 && (fa != sa))
917 {
918 out << indent;
919 show_numerical_change("type alignment", fa, sa, *ctxt, out,
920 /*show_bits_or_bytes=*/false);
921 out << "\n";
922 }
923}
924
925/// @param tod the type or declaration to emit loc info about
926///
927/// @param ctxt the content of the current diff.
928///
929/// @param out the output stream to report the change to.
930///
931/// @return true iff something was reported.
932bool
934 const diff_context& ctxt,
935 ostream &out)
936{
937 if (!ctxt.show_locs())
938 return false;
939
940 decl_base_sptr decl = is_decl(tod);
941
942 if (!decl)
943 return false;
944
945 location loc;
947
948 if (tu && (loc = decl->get_location()))
949 {
950 string path;
951 unsigned line, column;
952
953 loc.expand(path, line, column);
954 //tu->get_loc_mgr().expand_location(loc, path, line, column);
955 path = basename(const_cast<char*>(path.c_str()));
956
957 out << " at " << path << ":" << line << ":" << column;
958
959 return true;
960 }
961 return false;
962}
963
964/// Report the name, size and alignment changes of a type.
965///
966/// @param first the first type to consider.
967///
968/// @param second the second type to consider.
969///
970/// @param ctxt the content of the current diff.
971///
972/// @param out the output stream to report the change to.
973///
974/// @param indent the string to use for indentation.
975void
977 decl_base_sptr second,
979 ostream& out,
980 const string& indent)
981{
982 string fn = first->get_qualified_name(),
983 sn = second->get_qualified_name();
984
985 if (!(first->get_is_anonymous() && second->get_is_anonymous())
986 && fn != sn)
987 {
988 if (!(ctxt->get_allowed_category() & HARMLESS_DECL_NAME_CHANGE_CATEGORY)
989 && filtering::has_harmless_name_change(first, second, ctxt))
990 // This is a harmless name change. but then
991 // HARMLESS_DECL_NAME_CHANGE_CATEGORY doesn't seem allowed.
992 ;
993 else
994 {
995 out << indent;
996 if (is_type(first))
997 out << "type";
998 else
999 out << "declaration";
1000 out << " name changed from '" << fn << "' to '" << sn << "'";
1001 out << "\n";
1002 }
1003 }
1004
1005 report_size_and_alignment_changes(first, second, ctxt, out, indent);
1006}
1007
1008/// Output the header preceding the the report for
1009/// insertion/deletion/change of a part of a class. This is a
1010/// subroutine of class_diff::report.
1011///
1012/// @param out the output stream to output the report to.
1013///
1014/// @param number the number of insertion/deletion to refer to in the
1015/// header.
1016///
1017/// @param num_filtered the number of filtered changes.
1018///
1019/// @param k the kind of diff (insertion/deletion/change) we want the
1020/// head to introduce.
1021///
1022/// @param section_name the name of the sub-part of the class to
1023/// report about.
1024///
1025/// @param indent the string to use as indentation prefix in the
1026/// header.
1027void
1029 size_t number,
1030 size_t num_filtered,
1031 diff_kind k,
1032 const string& section_name,
1033 const string& indent)
1034{
1035 size_t net_number = number - num_filtered;
1036 string change;
1037 char colon_or_semi_colon = ':';
1038
1039 switch (k)
1040 {
1041 case del_kind:
1042 change = (number > 1) ? "deletions" : "deletion";
1043 break;
1044 case ins_kind:
1045 change = (number > 1) ? "insertions" : "insertion";
1046 break;
1047 case subtype_change_kind:
1048 case change_kind:
1049 change = (number > 1) ? "changes" : "change";
1050 break;
1051 }
1052
1053 if (net_number == 0)
1054 {
1055 out << indent << "no " << section_name << " " << change;
1056 colon_or_semi_colon = ';';
1057 }
1058 else if (net_number == 1)
1059 out << indent << "1 " << section_name << " " << change;
1060 else
1061 out << indent << net_number << " " << section_name
1062 << " " << change;
1063
1064 if (num_filtered)
1065 out << " (" << num_filtered << " filtered)";
1066 out << colon_or_semi_colon << "\n";
1067}
1068
1069/// Output the header preceding the the report for
1070/// insertion/deletion/change of a part of a class. This is a
1071/// subroutine of class_diff::report.
1072///
1073/// @param out the output stream to output the report to.
1074///
1075/// @param k the kind of diff (insertion/deletion/change) we want the
1076/// head to introduce.
1077///
1078/// @param section_name the name of the sub-part of the class to
1079/// report about.
1080///
1081/// @param indent the string to use as indentation prefix in the
1082/// header.
1083void
1085 diff_kind k,
1086 const string& section_name,
1087 const string& indent)
1088{
1089 string change;
1090
1091 switch (k)
1092 {
1093 case del_kind:
1094 change = "deletions";
1095 break;
1096 case ins_kind:
1097 change = "insertions";
1098 break;
1099 case subtype_change_kind:
1100 case change_kind:
1101 change = "changes";
1102 break;
1103 }
1104
1105 out << indent << "there are " << section_name << " " << change << ":\n";
1106}
1107
1108/// Report the differences in access specifiers and static-ness for
1109/// class members.
1110///
1111/// @param decl1 the first class member to consider.
1112///
1113/// @param decl2 the second class member to consider.
1114///
1115/// @param out the output stream to send the report to.
1116///
1117/// @param indent the indentation string to use for the report.
1118///
1119/// @return true if something was reported, false otherwise.
1120bool
1121maybe_report_diff_for_member(const decl_base_sptr& decl1,
1122 const decl_base_sptr& decl2,
1123 const diff_context_sptr& ctxt,
1124 ostream& out,
1125 const string& indent)
1126
1127{
1128 bool reported = false;
1129 if (!is_member_decl(decl1) || !is_member_decl(decl2))
1130 return reported;
1131
1132 string decl1_repr = decl1->get_pretty_representation(),
1133 decl2_repr = decl2->get_pretty_representation();
1134
1135 if (get_member_is_static(decl1) != get_member_is_static(decl2))
1136 {
1137 bool lost = get_member_is_static(decl1);
1138 out << indent << "'" << decl1_repr << "' ";
1139 if (report_loc_info(decl2, *ctxt, out))
1140 out << " ";
1141 if (lost)
1142 out << "became non-static";
1143 else
1144 out << "became static";
1145 out << "\n";
1146 reported = true;
1147 }
1148 if ((ctxt->get_allowed_category() & ACCESS_CHANGE_CATEGORY)
1150 != get_member_access_specifier(decl2)))
1151 {
1152 out << indent << "'" << decl1_repr << "' access changed from '"
1154 << "' to '"
1156 << "'\n";
1157 reported = true;
1158 }
1159 return reported;
1160}
1161
1162/// Report the differences between two generic variables.
1163///
1164/// @param decl1 the first version of the variable.
1165///
1166/// @param decl2 the second version of the variable.
1167///
1168/// @param ctxt the context of the diff.
1169///
1170/// @param out the output stream to emit the change report to.
1171///
1172/// @param indent the indentation prefix to emit.
1173///
1174/// @return true if any text has been emitted to the output stream.
1175bool
1176maybe_report_diff_for_variable(const decl_base_sptr& decl1,
1177 const decl_base_sptr& decl2,
1178 const diff_context_sptr& ctxt,
1179 ostream& out,
1180 const string& indent)
1181{
1182 bool reported = false;
1183
1184 var_decl_sptr var1 = is_var_decl(decl1);
1185 var_decl_sptr var2 = is_var_decl(decl2);
1186
1187 if (!var1 || !var2)
1188 return reported;
1189
1191 {
1192 uint64_t var_size_in_bits = var1->get_symbol()->get_size() * 8;
1193
1194 out << indent;
1195 show_offset_or_size("size of variable symbol (",
1196 var_size_in_bits, *ctxt, out);
1197 out << ") hasn't changed\n"
1198 << indent << "but it does have a harmless type change\n";
1199 reported = true;
1200 }
1201
1202 return reported;
1203}
1204
1205/// Report the difference between two ELF symbols, if there is any.
1206///
1207/// @param symbol1 the first symbol to consider.
1208///
1209/// @param symbol2 the second symbol to consider.
1210///
1211/// @param ctxt the diff context.
1212///
1213/// @param the output stream to emit the report to.
1214///
1215/// @param indent the indentation string to use.
1216void
1218 const elf_symbol_sptr& symbol2,
1219 const diff_context_sptr& ctxt,
1220 ostream& out,
1221 const string& indent)
1222{
1223 if (!symbol1 || !symbol2 || symbol1 == symbol2)
1224 return;
1225
1226 if (symbol1->get_size() != symbol2->get_size())
1227 {
1228 out << indent;
1229 show_numerical_change("size of symbol",
1230 symbol1->get_size(),
1231 symbol2->get_size(),
1232 *ctxt, out,
1233 /*show_bits_or_bytes=*/false);
1234 out << "\n";
1235 }
1236
1237 if (symbol1->get_name() != symbol2->get_name())
1238 {
1239 out << indent << "symbol name changed from "
1240 << symbol1->get_name()
1241 << " to "
1242 << symbol2->get_name()
1243 << "\n";
1244 }
1245
1246 if (symbol1->get_type() != symbol2->get_type())
1247 {
1248 out << indent << "symbol type changed from '"
1249 << symbol1->get_type()
1250 << "' to '"
1251 << symbol2->get_type()
1252 << "'\n";
1253 }
1254
1255 if (symbol1->is_public() != symbol2->is_public())
1256 {
1257 out << indent << "symbol became ";
1258 if (symbol2->is_public())
1259 out << "exported";
1260 else
1261 out << "non-exported";
1262 out << "\n";
1263 }
1264
1265 if (symbol1->is_defined() != symbol2->is_defined())
1266 {
1267 out << indent << "symbol became ";
1268 if (symbol2->is_defined())
1269 out << "defined";
1270 else
1271 out << "undefined";
1272 out << "\n";
1273 }
1274
1275 if (symbol1->get_version() != symbol2->get_version())
1276 {
1277 out << indent << "symbol version changed from "
1278 << symbol1->get_version().str()
1279 << " to "
1280 << symbol2->get_version().str()
1281 << "\n";
1282 }
1283
1284 const abg_compat::optional<uint32_t>& crc1 = symbol1->get_crc();
1285 const abg_compat::optional<uint32_t>& crc2 = symbol2->get_crc();
1286 if (crc1 != crc2)
1287 {
1288 const std::string none = "(none)";
1289 out << indent << "CRC (modversions) changed from "
1290 << std::showbase << std::hex;
1291 if (crc1.has_value())
1292 out << crc1.value();
1293 else
1294 out << none;
1295 out << " to ";
1296 if (crc2.has_value())
1297 out << crc2.value();
1298 else
1299 out << none;
1300 out << std::noshowbase << std::dec
1301 << "\n";
1302 }
1303
1304 const abg_compat::optional<std::string>& ns1 = symbol1->get_namespace();
1305 const abg_compat::optional<std::string>& ns2 = symbol2->get_namespace();
1306 if (ns1 != ns2)
1307 {
1308 const std::string none = "(none)";
1309 out << indent << "namespace changed from ";
1310 if (ns1.has_value())
1311 out << "'" << ns1.value() << "'";
1312 else
1313 out << none;
1314 out << " to ";
1315 if (ns2.has_value())
1316 out << "'" << ns2.value() << "'";
1317 else
1318 out << none;
1319 out << "\n";
1320 }
1321}
1322
1323/// For a given symbol, emit a string made of its name and version.
1324/// The string also contains the list of symbols that alias this one.
1325///
1326/// @param out the output string to emit the resulting string to.
1327///
1328/// @param indent the indentation string to use before emitting the
1329/// resulting string.
1330///
1331/// @param symbol the symbol to emit the representation string for.
1332///
1333/// @param sym_map the symbol map to consider to look for aliases of
1334/// @p symbol.
1335void
1337 const string& indent,
1338 const elf_symbol& symbol,
1339 const string_elf_symbols_map_type& sym_map)
1340{
1341 out << indent << symbol.get_id_string();
1342 string aliases =
1343 symbol.get_aliases_id_string(sym_map,
1344 /*include_symbol_itself=*/false);
1345 if (!aliases.empty())
1346 out << ", aliases " << aliases;
1347}
1348
1349/// Report changes about types that are not reachable from global
1350/// functions and variables, in a given @param corpus_diff.
1351///
1352/// @param d the corpus_diff to consider.
1353///
1354/// @param s the statistics of the changes, after filters and
1355/// suppressions are reported. This is typically what is returned by
1356/// corpus_diff::apply_filters_and_suppressions_before_reporting().
1357///
1358/// @param indent the indendation string (usually a string of white
1359/// spaces) to use for indentation during the reporting.
1360///
1361/// @param out the output stream to emit the report to.
1362void
1364 const corpus_diff::diff_stats &s,
1365 const string& indent,
1366 ostream& out)
1367{
1368 const diff_context_sptr& ctxt = d.context();
1369
1370 if (!(ctxt->show_unreachable_types()
1371 && (!d.priv_->deleted_unreachable_types_.empty()
1372 || !d.priv_->added_unreachable_types_.empty()
1373 || !d.priv_->changed_unreachable_types_.empty())))
1374 // The user either doesn't want us to show changes about
1375 // unreachable types or there are not such changes.
1376 return;
1377
1378 // Handle removed unreachable types.
1380 out << indent
1381 << "1 removed type unreachable from any public interface:\n\n";
1382 else if (s.net_num_removed_unreachable_types() > 1)
1383 out << indent
1385 << " removed types unreachable from any public interface:\n\n";
1386
1387 vector<type_base_sptr> sorted_removed_unreachable_types;
1388 sort_string_type_base_sptr_map(d.priv_->deleted_unreachable_types_,
1389 sorted_removed_unreachable_types);
1390 bool emitted = false;
1391 for (vector<type_base_sptr>::const_iterator i =
1392 sorted_removed_unreachable_types.begin();
1393 i != sorted_removed_unreachable_types.end();
1394 ++i)
1395 {
1396 if (d.priv_->deleted_unreachable_type_is_suppressed((*i).get()))
1397 continue;
1398
1399 out << indent << " [D] '" << get_pretty_representation(*i) << "'";
1400 report_loc_info(*i, *ctxt, out);
1401 out << "\n";
1402 emitted = true;
1403 }
1404 if (emitted)
1405 out << "\n";
1406
1407 // Handle changed unreachable types!
1409 out << indent
1410 << "1 changed type unreachable from any public interface:\n\n";
1411 else if (s.net_num_changed_unreachable_types() > 1)
1412 out << indent
1414 << " changed types unreachable from any public interface:\n\n";
1415
1416 diff_sptrs_type sorted_diff_sptrs;
1417 sort_string_diff_sptr_map(d.priv_->changed_unreachable_types_,
1418 sorted_diff_sptrs);
1419 for (diff_sptrs_type::const_iterator i = sorted_diff_sptrs.begin();
1420 i != sorted_diff_sptrs.end();
1421 ++i)
1422 {
1423 diff_sptr diff = *i;
1424 if (!diff || !diff->to_be_reported())
1425 continue;
1426
1427 string repr = diff->first_subject()->get_pretty_representation();
1428
1429 out << indent << " [C] '" << repr << "' changed:\n";
1430 diff->report(out, indent + " ");
1431 // Extra spacing.
1432 out << "\n";
1433 }
1434 // Changed types have extra spacing already. No new line here.
1435
1436 // Handle added unreachable types.
1438 out << indent
1439 << "1 added type unreachable from any public interface:\n\n";
1440 else if (s.net_num_added_unreachable_types() > 1)
1441 out << indent
1443 << " added types unreachable from any public interface:\n\n";
1444
1445 vector<type_base_sptr> sorted_added_unreachable_types;
1446 sort_string_type_base_sptr_map(d.priv_->added_unreachable_types_,
1447 sorted_added_unreachable_types);
1448 emitted = false;
1449 for (vector<type_base_sptr>::const_iterator i =
1450 sorted_added_unreachable_types.begin();
1451 i != sorted_added_unreachable_types.end();
1452 ++i)
1453 {
1454 if (d.priv_->added_unreachable_type_is_suppressed((*i).get()))
1455 continue;
1456
1457 out << indent << " [A] '" << get_pretty_representation(*i) << "'";
1458 report_loc_info(*i, *ctxt, out);
1459 out << "\n";
1460 emitted = true;
1461 }
1462 if (emitted)
1463 out << "\n";
1464}
1465
1466/// If a given diff node impacts some public interfaces, then report
1467/// about those impacted interfaces on a given output stream.
1468///
1469/// @param d the diff node to get the impacted interfaces for.
1470///
1471/// @param out the output stream to report to.
1472///
1473/// @param indent the white space string to use for indentation.
1474void
1476 ostream &out,
1477 const string &indent)
1478{
1479 const diff_context_sptr &ctxt = d->context();
1480 const corpus_diff_sptr &corp_diff = ctxt->get_corpus_diff();
1481 if (!corp_diff)
1482 return;
1483
1484 if (!ctxt->show_impacted_interfaces())
1485 return;
1486
1487 const diff_maps &maps = corp_diff->get_leaf_diffs();
1488 artifact_sptr_set_type* impacted_artifacts =
1490 if (impacted_artifacts == 0)
1491 return;
1492
1493 if (impacted_artifacts->empty())
1494 return;
1495
1496 vector<type_or_decl_base_sptr> sorted_impacted_interfaces;
1497 sort_artifacts_set(*impacted_artifacts, sorted_impacted_interfaces);
1498
1499 size_t num_impacted_interfaces = impacted_artifacts->size();
1500 if (num_impacted_interfaces == 1)
1501 out << indent << "one impacted interface:\n";
1502 else
1503 out << indent << num_impacted_interfaces << " impacted interfaces:\n";
1504
1505 string cur_indent = indent + " ";
1506 vector<type_or_decl_base_sptr>::const_iterator it;
1507 for (it = sorted_impacted_interfaces.begin();
1508 it != sorted_impacted_interfaces.end();
1509 ++it)
1510 {
1511 out << cur_indent << get_pretty_representation(*it) << "\n";
1512 }
1513}
1514
1515/// If a given diff node impacts some public interfaces, then report
1516/// about those impacted interfaces on standard output.
1517///
1518/// @param d the diff node to get the impacted interfaces for.
1519///
1520/// @param out the output stream to report to.
1521///
1522/// @param indent the white space string to use for indentation.
1523void
1525 ostream &out,
1526 const string &indent)
1527{return maybe_report_interfaces_impacted_by_diff(d.get(), out, indent);}
1528
1529/// Tests if the diff node is to be reported.
1530///
1531/// @param p the diff to consider.
1532///
1533/// @return true iff the diff is to be reported.
1534bool
1536{return d && d->to_be_reported();}
1537
1538/// Report about data members replaced by an anonymous data member
1539/// without changing the overall bit-layout of the class or union in
1540/// an ABI-meaningful way.
1541///
1542/// @param d the diff to consider.
1543///
1544/// @param out the output stream to emit the change report to.
1545///
1546/// @param indent the indentation string to use.
1547void
1549 ostream &out,
1550 const string &indent)
1551{
1552 const diff_context_sptr& ctxt = d.context();
1553
1554 if ((ctxt->get_allowed_category() & HARMLESS_DATA_MEMBER_CHANGE_CATEGORY)
1555 && !d.data_members_replaced_by_adms().empty())
1556 {
1557 // Let's detect all the data members that are replaced by
1558 // members of the same anonymous data member and report them
1559 // in one go.
1560 for (changed_var_sptrs_type::const_iterator i =
1563 {
1564 // This contains the data members replaced by the same
1565 // anonymous data member.
1566 vector<var_decl_sptr> dms_replaced_by_same_anon_dm;
1567 dms_replaced_by_same_anon_dm.push_back(i->first);
1568 // This contains the anonymous data member that replaced the
1569 // data members in the variable above.
1570 var_decl_sptr anonymous_data_member = i->second;
1571 // Let's look forward to see if the subsequent data
1572 // members were replaced by members of
1573 // anonymous_data_member.
1574 for (++i;
1576 && *i->second == *anonymous_data_member;
1577 ++i)
1578 dms_replaced_by_same_anon_dm.push_back(i->first);
1579
1580 bool several_data_members_replaced =
1581 dms_replaced_by_same_anon_dm.size() > 1;
1582
1583 out << indent << "data member";
1584 if (several_data_members_replaced)
1585 out << "s";
1586
1587 bool first_data_member = true;
1588 for (vector<var_decl_sptr>::const_iterator it =
1589 dms_replaced_by_same_anon_dm.begin();
1590 it != dms_replaced_by_same_anon_dm.end();
1591 ++it)
1592 {
1593 string name = (*it)->get_qualified_name();
1594 if (!first_data_member)
1595 out << ",";
1596 out << " '" << name << "'";
1597 first_data_member = false;
1598 }
1599
1600 if (several_data_members_replaced)
1601 out << " were ";
1602 else
1603 out << " was ";
1604
1605 out << "replaced by anonymous data member:\n"
1606 << indent + " "
1607 << "'"
1608 << anonymous_data_member->get_pretty_representation()
1609 << "'\n";
1610 }
1611 }
1612}
1613
1614/// Report about the base classes of a class having been re-ordered.
1615///
1616/// @param d the class diff to consider.
1617///
1618/// @param out the output stream to report the change to.
1619///
1620/// @param indent the indentation string to use.
1621void
1623 ostream &out,
1624 const string &indent)
1625{
1626 if (d.moved_bases().empty())
1627 return;
1628
1630 second = d.second_class_decl();
1631
1632 ABG_ASSERT(!first->get_base_specifiers().empty());
1633 ABG_ASSERT(!second->get_base_specifiers().empty());
1634
1635 out << indent << "base classes of '"
1636 << first->get_pretty_representation()
1637 << "' are re-ordered from: ";
1638
1639 vector<class_decl_sptr> classes = {first, second};
1640 unsigned nb_classes_seen = 0;
1641 for (auto &klass : classes)
1642 {
1643 if (nb_classes_seen >= 1)
1644 out << " to: ";
1645 out << "'";
1646 bool needs_comma = false;
1647 for (auto &b : klass->get_base_specifiers())
1648 {
1649 if (needs_comma)
1650 out << ", ";
1651 if (b->get_is_virtual())
1652 out << "virtual ";
1653 out << b->get_base_class()->get_qualified_name();
1654 needs_comma = true;
1655 }
1656 out << "'";
1657 nb_classes_seen++;
1658 }
1659 if (nb_classes_seen)
1660 out << "\n";
1661}
1662} // Namespace comparison
1663} // end namespace abigail
The private data and functions of the abigail::ir::comparison types.
#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 type abstracts changes for a class_decl.
class_decl_sptr first_class_decl() const
const vector< class_decl::base_spec_sptr > & moved_bases() const
Getter for the vector of bases that "moved". That is, the vector of base types which position changed...
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.
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.
const changed_var_sptrs_type & ordered_data_members_replaced_by_adms() const
Get an ordered vector of of data members that got replaced by anonymous data members.
This is a document class that aims to capture statistics about the changes carried by a corpus_diff t...
size_t net_num_added_unreachable_types() const
Getter of the number of added types that are unreachable from public interfaces and that are *NOT* fi...
size_t net_num_removed_unreachable_types() const
Getter of the number of removed types that are not reachable from public interfaces and that have *NO...
size_t net_num_changed_unreachable_types() const
Getter of the number of changed types that are unreachable from public interfaces and that have *NOT*...
An abstraction of a diff between between two abi corpus.
const diff_context_sptr context() const
Getter of the diff context of this diff.
The context of the diff. This type holds various bits of information that is going to be used through...
bool show_offsets_sizes_in_bits() const
Get the flag that indicates if diff reports using this context should show sizes and offsets in bits,...
void show_relative_offset_changes(bool f)
Set a flag saying if offset changes should be reported in a relative way. That is,...
bool show_hex_values() const
Get the flag that indicates if the diff reports using this context should show sizes and offsets in a...
This type contains maps. Each map associates a type name to a diff of that type. Not all kinds of dif...
artifact_sptr_set_type * lookup_impacted_interfaces(const diff *d) const
Lookup the interfaces that are impacted by a given leaf diff node.
The abstraction of a change between two ABI artifacts, a.k.a an artifact change.
type_or_decl_base_sptr first_subject() const
Getter of the first subject of the diff.
virtual void report(ostream &out, const string &indent="") const =0
Pure interface to report the diff in a serialized form that is legible for the user.
const diff_context_sptr context() const
Getter of the context of the current diff.
bool to_be_reported() const
Test if this diff tree node should be reported.
virtual bool diff_to_be_reported(const diff *d) const
Tests if the diff node is to be reported.
The abstraction of the diff between two subrange types.
const array_type_def::subrange_sptr second_subrange() const
Getter of the second subrange of the current instance subrange_diff.
const array_type_def::subrange_sptr first_subrange() const
Getter of the first subrange of the current instance subrange_diff.
const diff_sptr underlying_type_diff() const
Getter of the diff node of the underlying types of the current subrange_diff diff node.
shared_ptr< subrange_type > subrange_sptr
Convenience typedef for a shared pointer on a function_decl::subrange.
Definition: abg-ir.h:2566
Abstraction of an elf symbol.
Definition: abg-ir.h:961
string get_aliases_id_string(const string_elf_symbols_map_type &symtab, bool include_symbol_itself=true) const
Return a comma separated list of the id of the current symbol as well as the id string of its aliases...
Definition: abg-ir.cc:2686
const string & get_id_string() const
Get a string that is representative of a given elf_symbol.
Definition: abg-ir.cc:2616
The source location of a token.
Definition: abg-ir.h:307
void expand(std::string &path, unsigned &line, unsigned &column) const
Expand the current location into a tripplet file path, line and column number.
Definition: abg-ir.cc:452
This is the abstraction of the set of relevant artefacts (types, variable declarations,...
Definition: abg-ir.h:695
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_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 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 has_anonymous_data_member_change(const diff *d)
Test if a diff node carries a non-anonymous data member to anonymous data member change,...
shared_ptr< diff > diff_sptr
Convenience typedef for a shared_ptr for the diff class.
Definition: abg-fwd.h:78
void show_linkage_name_and_aliases(ostream &out, const string &indent, const elf_symbol &symbol, const string_elf_symbols_map_type &sym_map)
For a given symbol, emit a string made of its name and version. The string also contains the list of ...
void maybe_report_interfaces_impacted_by_diff(const diff *d, ostream &out, const string &indent)
If a given diff node impacts some public interfaces, then report about those impacted interfaces on a...
@ 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....
@ SIZE_OR_OFFSET_CHANGE_CATEGORY
This means the diff node (or at least one of its descendant nodes) carries a change that modifies the...
@ HARMLESS_DECL_NAME_CHANGE_CATEGORY
This means that a diff node in the sub-tree carries a harmless declaration name change....
void maybe_report_diff_for_symbol(const elf_symbol_sptr &symbol1, const elf_symbol_sptr &symbol2, const diff_context_sptr &ctxt, ostream &out, const string &indent)
Report the difference between two ELF symbols, if there is any.
void maybe_show_relative_offset_change(const var_diff_sptr &diff, diff_context &ctxt, ostream &out)
If a given var_diff node carries a data member change in which the offset of the data member actually...
uint64_t convert_bits_to_bytes(size_t bits)
Convert a number in bits into a number in bytes.
vector< diff_sptr > diff_sptrs_type
Convenience typedef for a vector of diff_sptr.
void maybe_report_unreachable_type_changes(const corpus_diff &d, const corpus_diff::diff_stats &s, const string &indent, ostream &out)
Report changes about types that are not reachable from global functions and variables,...
shared_ptr< diff_context > diff_context_sptr
Convenience typedef for a shared pointer of diff_context.
Definition: abg-fwd.h:70
void report_size_and_alignment_changes(type_or_decl_base_sptr first, type_or_decl_base_sptr second, diff_context_sptr ctxt, ostream &out, const string &indent)
Report the size and alignment changes of a type.
bool report_loc_info(const type_or_decl_base_sptr &tod, const diff_context &ctxt, ostream &out)
shared_ptr< var_diff > var_diff_sptr
Convenience typedef for a shared pointer to a var_diff type.
void show_numerical_change(const string &what, uint64_t old_bits, uint64_t new_bits, const diff_context &ctxt, ostream &out, bool show_bits_or_byte)
Emit a message showing the numerical change between two values, to a given output stream.
bool maybe_report_diff_for_variable(const decl_base_sptr &decl1, const decl_base_sptr &decl2, const diff_context_sptr &ctxt, ostream &out, const string &indent)
Report the differences between two generic variables.
void maybe_report_base_class_reordering(const class_diff &d, ostream &out, const string &indent)
Report about the base classes of a class having been re-ordered.
void represent_data_member(var_decl_sptr d, const diff_context_sptr &ctxt, ostream &out, const string &indent)
Stream a string representation for a data member.
void maybe_show_relative_size_change(const var_diff_sptr &diff, diff_context &ctxt, ostream &out)
If a given var_diff node carries a hange in which the size of the variable actually changed,...
string get_pretty_representation(diff *d)
Get a copy of the pretty representation of a diff node.
diff_kind
Represent the kind of difference we want report_mem_header() to report.
void sort_artifacts_set(const artifact_sptr_set_type &set, vector< type_or_decl_base_sptr > &sorted)
Sort the set of ABI artifacts contained in a artifact_sptr_set_type.
void maybe_report_data_members_replaced_by_anon_dm(const class_or_union_diff &d, ostream &out, const string &indent)
Report about data members replaced by an anonymous data member without changing the overall bit-layou...
bool maybe_report_diff_for_member(const decl_base_sptr &decl1, const decl_base_sptr &decl2, const diff_context_sptr &ctxt, ostream &out, const string &indent)
Report the differences in access specifiers and static-ness for class members.
void report_mem_header(ostream &out, size_t number, size_t num_filtered, diff_kind k, const string &section_name, const string &indent)
Output the header preceding the the report for insertion/deletion/change of a part of a class....
void sort_string_type_base_sptr_map(string_type_base_sptr_map &map, vector< type_base_sptr > &sorted)
Sort a map of string to type_base_sptr entities.
void show_offset_or_size(const string &what, uint64_t value, const diff_context &ctxt, ostream &out)
Emit a message showing the value of a numerical value representing a size or an offset,...
void emit_num_value(uint64_t value, const diff_context &ctxt, ostream &out)
Emit a numerical value to an output stream.
void report_name_size_and_alignment_changes(decl_base_sptr first, decl_base_sptr second, diff_context_sptr ctxt, ostream &out, const string &indent)
Report the name, size and alignment changes of a type.
void sort_string_diff_sptr_map(const string_diff_sptr_map &map, diff_sptrs_type &sorted)
Sort a map ofg string -> diff_sptr into a vector of diff_sptr. The diff_sptr are sorted lexicographic...
shared_ptr< corpus_diff > corpus_diff_sptr
A convenience typedef for a shared pointer to corpus_diff.
void represent(const diff_context &ctxt, method_decl_sptr mem_fn, ostream &out)
Stream a string representation for a member function.
uint64_t maybe_convert_bits_to_bytes(uint64_t bits, const diff_context &ctxt)
Convert a bits value into a byte value if the current diff context instructs us to do so.
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
ssize_t get_member_function_vtable_offset(const function_decl &f)
Get the vtable offset of a member function.
Definition: abg-ir.cc:6578
unordered_set< type_or_decl_base_sptr, type_or_decl_hash, type_or_decl_equal > artifact_sptr_set_type
A convenience typedef for a hash set of type_or_decl_base_sptr.
Definition: abg-ir.h:559
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
change_kind
A bitfield that gives callers of abigail::ir::equals() some insight about how different two internal ...
Definition: abg-ir.h:1361
bool is_class_type(const type_or_decl_base &t)
Test whether a type is a class.
Definition: abg-ir.cc:11091
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
uint64_t get_var_size_in_bits(const var_decl_sptr &v)
Get the size of a given variable.
Definition: abg-ir.cc:6316
shared_ptr< var_decl > var_decl_sptr
Convenience typedef for a shared pointer on a var_decl.
Definition: abg-fwd.h:256
shared_ptr< type_or_decl_base > type_or_decl_base_sptr
A convenience typedef for a shared_ptr to type_or_decl_base.
Definition: abg-fwd.h:120
bool is_data_member_of_anonymous_class_or_union(const var_decl &d)
Test if a var_decl is a data member belonging to an anonymous type.
Definition: abg-ir.cc:5990
bool get_data_member_is_laid_out(const var_decl &m)
Test whether a data member is laid out.
Definition: abg-ir.cc:6344
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
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
translation_unit * get_translation_unit(const type_or_decl_base &t)
Return the translation unit a declaration belongs to.
Definition: abg-ir.cc:10461
bool is_union_type(const type_or_decl_base &t)
Test if a type is a union_decl.
Definition: abg-ir.cc:11371
bool is_data_member(const var_decl &v)
Test if a var_decl is a data member.
Definition: abg-ir.cc:5613
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
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
std::unordered_map< string, elf_symbols > string_elf_symbols_map_type
Convenience typedef for a map which key is a string and which value is a vector of elf_symbol.
Definition: abg-ir.h:947
bool is_member_decl(const decl_base_sptr d)
Tests if a declaration is a class member.
Definition: abg-ir.cc:5417
Toplevel namespace for libabigail.