libabigail
abg-reporter-priv.cc
1// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
2// -*- Mode: C++ -*-
3//
4// Copyright (C) 2017-2024 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 range '"
758 << on
759 << "' change from '";
760 emit_num_value(olb, *ctxt, out);
761 out << "' to '";
762 emit_num_value(nlb, *ctxt, out);
763 out << "'\n";
764 }
765
766 if (oub != nub)
767 {
768 out << indent << "upper bound of range '"
769 << on
770 << "' change from '";
771 emit_num_value(oub, *ctxt, out);
772 out << "' to '";
773 emit_num_value(nub, *ctxt, out);
774 out << "'\n";
775 }
776
777 if (!local_only)
778 {
780 if (dif && dif->to_be_reported())
781 {
782 // report range underlying type changes
783 out << indent << "underlying type of range '"
784 << oor << "' changed:\n";
785 dif->report(out, indent + " ");
786 }
787 }
788}
789
790/// Report the size and alignment changes of a type.
791///
792/// @param first the first type to consider.
793///
794/// @param second the second type to consider.
795///
796/// @param ctxt the content of the current diff.
797///
798/// @param out the output stream to report the change to.
799///
800/// @param indent the string to use for indentation.
801void
805 ostream& out,
806 const string& indent)
807{
808 type_base_sptr f = dynamic_pointer_cast<type_base>(first),
809 s = dynamic_pointer_cast<type_base>(second);
810
811 if (!s || !f)
812 return;
813
814 class_or_union_sptr first_class = is_class_or_union_type(first),
815 second_class = is_class_or_union_type(second);
816
817 if (filtering::has_class_decl_only_def_change(first_class, second_class))
818 // So these two classes differ only by the fact that one is the
819 // declaration-only form of the second. The declaration-only class
820 // is of unknown size (recorded as 0) and it is not meaningful to
821 // report a size change.
822 return;
823
824 unsigned fs = f->get_size_in_bits(), ss = s->get_size_in_bits(),
825 fa = f->get_alignment_in_bits(), sa = s->get_alignment_in_bits();
826 array_type_def_sptr first_array = is_array_type(is_type(first)),
827 second_array = is_array_type(is_type(second));
828 unsigned fdc = first_array ? first_array->get_dimension_count(): 0,
829 sdc = second_array ? second_array->get_dimension_count(): 0;
830
831 if (ctxt->get_allowed_category() & SIZE_OR_OFFSET_CHANGE_CATEGORY)
832 {
833 if (fs != ss || fdc != sdc)
834 {
835 if (first_array && second_array)
836 {
837 // We are looking at size or alignment changes between two
838 // arrays ...
839 out << indent << "array type size changed from ";
840 if (first_array->is_non_finite())
841 out << "\'unknown\'";
842 else
843 emit_num_value(first_array->get_size_in_bits(), *ctxt, out);
844 out << " to ";
845 if (second_array->is_non_finite())
846 out << "\'unknown\'";
847 else
848 emit_num_value(second_array->get_size_in_bits(), *ctxt, out);
849 out << "\n";
850
851 if (sdc != fdc)
852 {
853 out << indent + " "
854 << "number of dimensions changed from "
855 << fdc
856 << " to "
857 << sdc
858 << "\n";
859 }
860 array_type_def::subranges_type::const_iterator i, j;
861 for (i = first_array->get_subranges().begin(),
862 j = second_array->get_subranges().begin();
863 (i != first_array->get_subranges().end()
864 && j != second_array->get_subranges().end());
865 ++i, ++j)
866 {
867 if ((*i)->get_length() != (*j)->get_length())
868 {
869 out << indent
870 << "array type subrange "
871 << i - first_array->get_subranges().begin() + 1
872 << " changed length from ";
873
874 if ((*i)->is_non_finite())
875 out << "\'unknown\'";
876 else
877 out << (*i)->get_length();
878
879 out << " to ";
880
881 if ((*j)->is_non_finite())
882 out << "\'unknown\'";
883 else
884 out << (*j)->get_length();
885 out << "\n";
886 }
887 }
888 } // end if (first_array && second_array)
889 else if (fs != ss)
890 {
891 out << indent;
892 show_numerical_change("type size", fs, ss, *ctxt, out);
893 out << "\n";
894 }
895 } // end if (fs != ss || fdc != sdc)
896 else
897 if (ctxt->show_relative_offset_changes())
898 {
899 out << indent << "type size hasn't changed\n";
900 }
901 }
902 if ((ctxt->get_allowed_category() & SIZE_OR_OFFSET_CHANGE_CATEGORY)
903 && (fa != sa))
904 {
905 out << indent;
906 show_numerical_change("type alignment", fa, sa, *ctxt, out,
907 /*show_bits_or_bytes=*/false);
908 out << "\n";
909 }
910}
911
912/// @param tod the type or declaration to emit loc info about
913///
914/// @param ctxt the content of the current diff.
915///
916/// @param out the output stream to report the change to.
917///
918/// @return true iff something was reported.
919bool
921 const diff_context& ctxt,
922 ostream &out)
923{
924 if (!ctxt.show_locs())
925 return false;
926
927 decl_base_sptr decl = is_decl(tod);
928
929 if (!decl)
930 return false;
931
932 location loc;
934
935 if (tu && (loc = decl->get_location()))
936 {
937 string path;
938 unsigned line, column;
939
940 loc.expand(path, line, column);
941 //tu->get_loc_mgr().expand_location(loc, path, line, column);
942 path = basename(const_cast<char*>(path.c_str()));
943
944 out << " at " << path << ":" << line << ":" << column;
945
946 return true;
947 }
948 return false;
949}
950
951/// Report the name, size and alignment changes of a type.
952///
953/// @param first the first type to consider.
954///
955/// @param second the second type to consider.
956///
957/// @param ctxt the content of the current diff.
958///
959/// @param out the output stream to report the change to.
960///
961/// @param indent the string to use for indentation.
962void
964 decl_base_sptr second,
966 ostream& out,
967 const string& indent)
968{
969 string fn = first->get_qualified_name(),
970 sn = second->get_qualified_name();
971
972 if (!(first->get_is_anonymous() && second->get_is_anonymous())
973 && fn != sn)
974 {
975 if (!(ctxt->get_allowed_category() & HARMLESS_DECL_NAME_CHANGE_CATEGORY)
976 && filtering::has_harmless_name_change(first, second))
977 // This is a harmless name change. but then
978 // HARMLESS_DECL_NAME_CHANGE_CATEGORY doesn't seem allowed.
979 ;
980 else
981 {
982 out << indent;
983 if (is_type(first))
984 out << "type";
985 else
986 out << "declaration";
987 out << " name changed from '" << fn << "' to '" << sn << "'";
988 out << "\n";
989 }
990 }
991
992 report_size_and_alignment_changes(first, second, ctxt, out, indent);
993}
994
995/// Output the header preceding the the report for
996/// insertion/deletion/change of a part of a class. This is a
997/// subroutine of class_diff::report.
998///
999/// @param out the output stream to output the report to.
1000///
1001/// @param number the number of insertion/deletion to refer to in the
1002/// header.
1003///
1004/// @param num_filtered the number of filtered changes.
1005///
1006/// @param k the kind of diff (insertion/deletion/change) we want the
1007/// head to introduce.
1008///
1009/// @param section_name the name of the sub-part of the class to
1010/// report about.
1011///
1012/// @param indent the string to use as indentation prefix in the
1013/// header.
1014void
1016 size_t number,
1017 size_t num_filtered,
1018 diff_kind k,
1019 const string& section_name,
1020 const string& indent)
1021{
1022 size_t net_number = number - num_filtered;
1023 string change;
1024 char colon_or_semi_colon = ':';
1025
1026 switch (k)
1027 {
1028 case del_kind:
1029 change = (number > 1) ? "deletions" : "deletion";
1030 break;
1031 case ins_kind:
1032 change = (number > 1) ? "insertions" : "insertion";
1033 break;
1034 case subtype_change_kind:
1035 case change_kind:
1036 change = (number > 1) ? "changes" : "change";
1037 break;
1038 }
1039
1040 if (net_number == 0)
1041 {
1042 out << indent << "no " << section_name << " " << change;
1043 colon_or_semi_colon = ';';
1044 }
1045 else if (net_number == 1)
1046 out << indent << "1 " << section_name << " " << change;
1047 else
1048 out << indent << net_number << " " << section_name
1049 << " " << change;
1050
1051 if (num_filtered)
1052 out << " (" << num_filtered << " filtered)";
1053 out << colon_or_semi_colon << "\n";
1054}
1055
1056/// Output the header preceding the the report for
1057/// insertion/deletion/change of a part of a class. This is a
1058/// subroutine of class_diff::report.
1059///
1060/// @param out the output stream to output the report to.
1061///
1062/// @param k the kind of diff (insertion/deletion/change) we want the
1063/// head to introduce.
1064///
1065/// @param section_name the name of the sub-part of the class to
1066/// report about.
1067///
1068/// @param indent the string to use as indentation prefix in the
1069/// header.
1070void
1072 diff_kind k,
1073 const string& section_name,
1074 const string& indent)
1075{
1076 string change;
1077
1078 switch (k)
1079 {
1080 case del_kind:
1081 change = "deletions";
1082 break;
1083 case ins_kind:
1084 change = "insertions";
1085 break;
1086 case subtype_change_kind:
1087 case change_kind:
1088 change = "changes";
1089 break;
1090 }
1091
1092 out << indent << "there are " << section_name << " " << change << ":\n";
1093}
1094
1095/// Report the differences in access specifiers and static-ness for
1096/// class members.
1097///
1098/// @param decl1 the first class member to consider.
1099///
1100/// @param decl2 the second class member to consider.
1101///
1102/// @param out the output stream to send the report to.
1103///
1104/// @param indent the indentation string to use for the report.
1105///
1106/// @return true if something was reported, false otherwise.
1107bool
1108maybe_report_diff_for_member(const decl_base_sptr& decl1,
1109 const decl_base_sptr& decl2,
1110 const diff_context_sptr& ctxt,
1111 ostream& out,
1112 const string& indent)
1113
1114{
1115 bool reported = false;
1116 if (!is_member_decl(decl1) || !is_member_decl(decl2))
1117 return reported;
1118
1119 string decl1_repr = decl1->get_pretty_representation(),
1120 decl2_repr = decl2->get_pretty_representation();
1121
1122 if (get_member_is_static(decl1) != get_member_is_static(decl2))
1123 {
1124 bool lost = get_member_is_static(decl1);
1125 out << indent << "'" << decl1_repr << "' ";
1126 if (report_loc_info(decl2, *ctxt, out))
1127 out << " ";
1128 if (lost)
1129 out << "became non-static";
1130 else
1131 out << "became static";
1132 out << "\n";
1133 reported = true;
1134 }
1135 if ((ctxt->get_allowed_category() & ACCESS_CHANGE_CATEGORY)
1137 != get_member_access_specifier(decl2)))
1138 {
1139 out << indent << "'" << decl1_repr << "' access changed from '"
1141 << "' to '"
1143 << "'\n";
1144 reported = true;
1145 }
1146 return reported;
1147}
1148
1149/// Report the differences between two generic variables.
1150///
1151/// @param decl1 the first version of the variable.
1152///
1153/// @param decl2 the second version of the variable.
1154///
1155/// @param ctxt the context of the diff.
1156///
1157/// @param out the output stream to emit the change report to.
1158///
1159/// @param indent the indentation prefix to emit.
1160///
1161/// @return true if any text has been emitted to the output stream.
1162bool
1163maybe_report_diff_for_variable(const decl_base_sptr& decl1,
1164 const decl_base_sptr& decl2,
1165 const diff_context_sptr& ctxt,
1166 ostream& out,
1167 const string& indent)
1168{
1169 bool reported = false;
1170
1171 var_decl_sptr var1 = is_var_decl(decl1);
1172 var_decl_sptr var2 = is_var_decl(decl2);
1173
1174 if (!var1 || !var2)
1175 return reported;
1176
1178 {
1179 uint64_t var_size_in_bits = var1->get_symbol()->get_size() * 8;
1180
1181 out << indent;
1182 show_offset_or_size("size of variable symbol (",
1183 var_size_in_bits, *ctxt, out);
1184 out << ") hasn't changed\n"
1185 << indent << "but it does have a harmless type change\n";
1186 reported = true;
1187 }
1188
1189 return reported;
1190}
1191
1192/// Report the difference between two ELF symbols, if there is any.
1193///
1194/// @param symbol1 the first symbol to consider.
1195///
1196/// @param symbol2 the second symbol to consider.
1197///
1198/// @param ctxt the diff context.
1199///
1200/// @param the output stream to emit the report to.
1201///
1202/// @param indent the indentation string to use.
1203void
1205 const elf_symbol_sptr& symbol2,
1206 const diff_context_sptr& ctxt,
1207 ostream& out,
1208 const string& indent)
1209{
1210 if (!symbol1 || !symbol2 || symbol1 == symbol2)
1211 return;
1212
1213 if (symbol1->get_size() != symbol2->get_size())
1214 {
1215 out << indent;
1216 show_numerical_change("size of symbol",
1217 symbol1->get_size(),
1218 symbol2->get_size(),
1219 *ctxt, out,
1220 /*show_bits_or_bytes=*/false);
1221 out << "\n";
1222 }
1223
1224 if (symbol1->get_name() != symbol2->get_name())
1225 {
1226 out << indent << "symbol name changed from "
1227 << symbol1->get_name()
1228 << " to "
1229 << symbol2->get_name()
1230 << "\n";
1231 }
1232
1233 if (symbol1->get_type() != symbol2->get_type())
1234 {
1235 out << indent << "symbol type changed from '"
1236 << symbol1->get_type()
1237 << "' to '"
1238 << symbol2->get_type()
1239 << "'\n";
1240 }
1241
1242 if (symbol1->is_public() != symbol2->is_public())
1243 {
1244 out << indent << "symbol became ";
1245 if (symbol2->is_public())
1246 out << "exported";
1247 else
1248 out << "non-exported";
1249 out << "\n";
1250 }
1251
1252 if (symbol1->is_defined() != symbol2->is_defined())
1253 {
1254 out << indent << "symbol became ";
1255 if (symbol2->is_defined())
1256 out << "defined";
1257 else
1258 out << "undefined";
1259 out << "\n";
1260 }
1261
1262 if (symbol1->get_version() != symbol2->get_version())
1263 {
1264 out << indent << "symbol version changed from "
1265 << symbol1->get_version().str()
1266 << " to "
1267 << symbol2->get_version().str()
1268 << "\n";
1269 }
1270
1271 const abg_compat::optional<uint32_t>& crc1 = symbol1->get_crc();
1272 const abg_compat::optional<uint32_t>& crc2 = symbol2->get_crc();
1273 if (crc1 != crc2)
1274 {
1275 const std::string none = "(none)";
1276 out << indent << "CRC (modversions) changed from "
1277 << std::showbase << std::hex;
1278 if (crc1.has_value())
1279 out << crc1.value();
1280 else
1281 out << none;
1282 out << " to ";
1283 if (crc2.has_value())
1284 out << crc2.value();
1285 else
1286 out << none;
1287 out << std::noshowbase << std::dec
1288 << "\n";
1289 }
1290
1291 const abg_compat::optional<std::string>& ns1 = symbol1->get_namespace();
1292 const abg_compat::optional<std::string>& ns2 = symbol2->get_namespace();
1293 if (ns1 != ns2)
1294 {
1295 const std::string none = "(none)";
1296 out << indent << "namespace changed from ";
1297 if (ns1.has_value())
1298 out << "'" << ns1.value() << "'";
1299 else
1300 out << none;
1301 out << " to ";
1302 if (ns2.has_value())
1303 out << "'" << ns2.value() << "'";
1304 else
1305 out << none;
1306 out << "\n";
1307 }
1308}
1309
1310/// For a given symbol, emit a string made of its name and version.
1311/// The string also contains the list of symbols that alias this one.
1312///
1313/// @param out the output string to emit the resulting string to.
1314///
1315/// @param indent the indentation string to use before emitting the
1316/// resulting string.
1317///
1318/// @param symbol the symbol to emit the representation string for.
1319///
1320/// @param sym_map the symbol map to consider to look for aliases of
1321/// @p symbol.
1322void
1324 const string& indent,
1325 const elf_symbol& symbol,
1326 const string_elf_symbols_map_type& sym_map)
1327{
1328 out << indent << symbol.get_id_string();
1329 string aliases =
1330 symbol.get_aliases_id_string(sym_map,
1331 /*include_symbol_itself=*/false);
1332 if (!aliases.empty())
1333 out << ", aliases " << aliases;
1334}
1335
1336/// Report changes about types that are not reachable from global
1337/// functions and variables, in a given @param corpus_diff.
1338///
1339/// @param d the corpus_diff to consider.
1340///
1341/// @param s the statistics of the changes, after filters and
1342/// suppressions are reported. This is typically what is returned by
1343/// corpus_diff::apply_filters_and_suppressions_before_reporting().
1344///
1345/// @param indent the indendation string (usually a string of white
1346/// spaces) to use for indentation during the reporting.
1347///
1348/// @param out the output stream to emit the report to.
1349void
1351 const corpus_diff::diff_stats &s,
1352 const string& indent,
1353 ostream& out)
1354{
1355 const diff_context_sptr& ctxt = d.context();
1356
1357 if (!(ctxt->show_unreachable_types()
1358 && (!d.priv_->deleted_unreachable_types_.empty()
1359 || !d.priv_->added_unreachable_types_.empty()
1360 || !d.priv_->changed_unreachable_types_.empty())))
1361 // The user either doesn't want us to show changes about
1362 // unreachable types or there are not such changes.
1363 return;
1364
1365 // Handle removed unreachable types.
1367 out << indent
1368 << "1 removed type unreachable from any public interface:\n\n";
1369 else if (s.net_num_removed_unreachable_types() > 1)
1370 out << indent
1372 << " removed types unreachable from any public interface:\n\n";
1373
1374 vector<type_base_sptr> sorted_removed_unreachable_types;
1375 sort_string_type_base_sptr_map(d.priv_->deleted_unreachable_types_,
1376 sorted_removed_unreachable_types);
1377 bool emitted = false;
1378 for (vector<type_base_sptr>::const_iterator i =
1379 sorted_removed_unreachable_types.begin();
1380 i != sorted_removed_unreachable_types.end();
1381 ++i)
1382 {
1383 if (d.priv_->deleted_unreachable_type_is_suppressed((*i).get()))
1384 continue;
1385
1386 out << indent << " [D] '" << get_pretty_representation(*i) << "'";
1387 report_loc_info(*i, *ctxt, out);
1388 out << "\n";
1389 emitted = true;
1390 }
1391 if (emitted)
1392 out << "\n";
1393
1394 // Handle changed unreachable types!
1396 out << indent
1397 << "1 changed type unreachable from any public interface:\n\n";
1398 else if (s.net_num_changed_unreachable_types() > 1)
1399 out << indent
1401 << " changed types unreachable from any public interface:\n\n";
1402
1403 diff_sptrs_type sorted_diff_sptrs;
1404 sort_string_diff_sptr_map(d.priv_->changed_unreachable_types_,
1405 sorted_diff_sptrs);
1406 for (diff_sptrs_type::const_iterator i = sorted_diff_sptrs.begin();
1407 i != sorted_diff_sptrs.end();
1408 ++i)
1409 {
1410 diff_sptr diff = *i;
1411 if (!diff || !diff->to_be_reported())
1412 continue;
1413
1414 string repr = diff->first_subject()->get_pretty_representation();
1415
1416 out << indent << " [C] '" << repr << "' changed:\n";
1417 diff->report(out, indent + " ");
1418 // Extra spacing.
1419 out << "\n";
1420 }
1421 // Changed types have extra spacing already. No new line here.
1422
1423 // Handle added unreachable types.
1425 out << indent
1426 << "1 added type unreachable from any public interface:\n\n";
1427 else if (s.net_num_added_unreachable_types() > 1)
1428 out << indent
1430 << " added types unreachable from any public interface:\n\n";
1431
1432 vector<type_base_sptr> sorted_added_unreachable_types;
1433 sort_string_type_base_sptr_map(d.priv_->added_unreachable_types_,
1434 sorted_added_unreachable_types);
1435 emitted = false;
1436 for (vector<type_base_sptr>::const_iterator i =
1437 sorted_added_unreachable_types.begin();
1438 i != sorted_added_unreachable_types.end();
1439 ++i)
1440 {
1441 if (d.priv_->added_unreachable_type_is_suppressed((*i).get()))
1442 continue;
1443
1444 out << indent << " [A] '" << get_pretty_representation(*i) << "'";
1445 report_loc_info(*i, *ctxt, out);
1446 out << "\n";
1447 emitted = true;
1448 }
1449 if (emitted)
1450 out << "\n";
1451}
1452
1453/// If a given diff node impacts some public interfaces, then report
1454/// about those impacted interfaces on a given output stream.
1455///
1456/// @param d the diff node to get the impacted interfaces for.
1457///
1458/// @param out the output stream to report to.
1459///
1460/// @param indent the white space string to use for indentation.
1461void
1463 ostream &out,
1464 const string &indent)
1465{
1466 const diff_context_sptr &ctxt = d->context();
1467 const corpus_diff_sptr &corp_diff = ctxt->get_corpus_diff();
1468 if (!corp_diff)
1469 return;
1470
1471 if (!ctxt->show_impacted_interfaces())
1472 return;
1473
1474 const diff_maps &maps = corp_diff->get_leaf_diffs();
1475 artifact_sptr_set_type* impacted_artifacts =
1477 if (impacted_artifacts == 0)
1478 return;
1479
1480 if (impacted_artifacts->empty())
1481 return;
1482
1483 vector<type_or_decl_base_sptr> sorted_impacted_interfaces;
1484 sort_artifacts_set(*impacted_artifacts, sorted_impacted_interfaces);
1485
1486 size_t num_impacted_interfaces = impacted_artifacts->size();
1487 if (num_impacted_interfaces == 1)
1488 out << indent << "one impacted interface:\n";
1489 else
1490 out << indent << num_impacted_interfaces << " impacted interfaces:\n";
1491
1492 string cur_indent = indent + " ";
1493 vector<type_or_decl_base_sptr>::const_iterator it;
1494 for (it = sorted_impacted_interfaces.begin();
1495 it != sorted_impacted_interfaces.end();
1496 ++it)
1497 {
1498 out << cur_indent << get_pretty_representation(*it) << "\n";
1499 }
1500}
1501
1502/// If a given diff node impacts some public interfaces, then report
1503/// about those impacted interfaces on standard output.
1504///
1505/// @param d the diff node to get the impacted interfaces for.
1506///
1507/// @param out the output stream to report to.
1508///
1509/// @param indent the white space string to use for indentation.
1510void
1512 ostream &out,
1513 const string &indent)
1514{return maybe_report_interfaces_impacted_by_diff(d.get(), out, indent);}
1515
1516/// Tests if the diff node is to be reported.
1517///
1518/// @param p the diff to consider.
1519///
1520/// @return true iff the diff is to be reported.
1521bool
1523{return d && d->to_be_reported();}
1524
1525/// Report about data members replaced by an anonymous data member
1526/// without changing the overall bit-layout of the class or union in
1527/// an ABI-meaningful way.
1528///
1529/// @param d the diff to consider.
1530///
1531/// @param out the output stream to emit the change report to.
1532///
1533/// @param indent the indentation string to use.
1534void
1536 ostream &out,
1537 const string &indent)
1538{
1539 const diff_context_sptr& ctxt = d.context();
1540
1541 if ((ctxt->get_allowed_category() & HARMLESS_DATA_MEMBER_CHANGE_CATEGORY)
1542 && !d.data_members_replaced_by_adms().empty())
1543 {
1544 // Let's detect all the data members that are replaced by
1545 // members of the same anonymous data member and report them
1546 // in one go.
1547 for (changed_var_sptrs_type::const_iterator i =
1550 {
1551 // This contains the data members replaced by the same
1552 // anonymous data member.
1553 vector<var_decl_sptr> dms_replaced_by_same_anon_dm;
1554 dms_replaced_by_same_anon_dm.push_back(i->first);
1555 // This contains the anonymous data member that replaced the
1556 // data members in the variable above.
1557 var_decl_sptr anonymous_data_member = i->second;
1558 // Let's look forward to see if the subsequent data
1559 // members were replaced by members of
1560 // anonymous_data_member.
1561 for (++i;
1563 && *i->second == *anonymous_data_member;
1564 ++i)
1565 dms_replaced_by_same_anon_dm.push_back(i->first);
1566
1567 bool several_data_members_replaced =
1568 dms_replaced_by_same_anon_dm.size() > 1;
1569
1570 out << indent << "data member";
1571 if (several_data_members_replaced)
1572 out << "s";
1573
1574 bool first_data_member = true;
1575 for (vector<var_decl_sptr>::const_iterator it =
1576 dms_replaced_by_same_anon_dm.begin();
1577 it != dms_replaced_by_same_anon_dm.end();
1578 ++it)
1579 {
1580 string name = (*it)->get_qualified_name();
1581 if (!first_data_member)
1582 out << ",";
1583 out << " '" << name << "'";
1584 first_data_member = false;
1585 }
1586
1587 if (several_data_members_replaced)
1588 out << " were ";
1589 else
1590 out << " was ";
1591
1592 out << "replaced by anonymous data member:\n"
1593 << indent + " "
1594 << "'"
1595 << anonymous_data_member->get_pretty_representation()
1596 << "'\n";
1597 }
1598 }
1599}
1600
1601/// Report about the base classes of a class having been re-ordered.
1602///
1603/// @param d the class diff to consider.
1604///
1605/// @param out the output stream to report the change to.
1606///
1607/// @param indent the indentation string to use.
1608void
1610 ostream &out,
1611 const string &indent)
1612{
1613 if (d.moved_bases().empty())
1614 return;
1615
1617 second = d.second_class_decl();
1618
1619 ABG_ASSERT(!first->get_base_specifiers().empty());
1620 ABG_ASSERT(!second->get_base_specifiers().empty());
1621
1622 out << indent << "base classes of '"
1623 << first->get_pretty_representation()
1624 << "' are re-ordered from: ";
1625
1626 vector<class_decl_sptr> classes = {first, second};
1627 unsigned nb_classes_seen = 0;
1628 for (auto &klass : classes)
1629 {
1630 if (nb_classes_seen >= 1)
1631 out << " to: ";
1632 out << "'";
1633 bool needs_comma = false;
1634 for (auto &b : klass->get_base_specifiers())
1635 {
1636 if (needs_comma)
1637 out << ", ";
1638 if (b->get_is_virtual())
1639 out << "virtual ";
1640 out << b->get_base_class()->get_qualified_name();
1641 needs_comma = true;
1642 }
1643 out << "'";
1644 nb_classes_seen++;
1645 }
1646 if (nb_classes_seen)
1647 out << "\n";
1648}
1649} // Namespace comparison
1650} // 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:1714
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:2537
Abstraction of an elf symbol.
Definition: abg-ir.h:923
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:2597
const string & get_id_string() const
Get a string that is representative of a given elf_symbol.
Definition: abg-ir.cc:2527
The source location of a token.
Definition: abg-ir.h:299
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:462
This is the abstraction of the set of relevant artefacts (types, variable declarations,...
Definition: abg-ir.h:686
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_harmless_name_change(const decl_base_sptr &f, const decl_base_sptr &s)
Test if two decls represents a harmless name change.
bool is_var_1_dim_unknown_size_array_change(const var_decl_sptr &var1, const var_decl_sptr &var2)
Test if we are looking at two variables which types are both one dimension array, with one of them be...
bool 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:79
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:71
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:5561
ssize_t get_member_function_vtable_offset(const function_decl &f)
Get the vtable offset of a member function.
Definition: abg-ir.cc:6564
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:550
bool is_type(const type_or_decl_base &tod)
Test whether a declaration is a type.
Definition: abg-ir.cc:10410
bool is_anonymous_data_member(const decl_base &d)
Test if a decl is an anonymous data member.
Definition: abg-ir.cc:5858
shared_ptr< elf_symbol > elf_symbol_sptr
A convenience typedef for a shared pointer to elf_symbol.
Definition: abg-ir.h:888
change_kind
A bitfield that gives callers of abigail::ir::equals() some insight about how different two internal ...
Definition: abg-ir.h:1323
bool is_class_type(const type_or_decl_base &t)
Test whether a type is a class.
Definition: abg-ir.cc:10692
shared_ptr< array_type_def > array_type_def_sptr
Convenience typedef for a shared pointer on a array_type_def.
Definition: abg-fwd.h:245
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:10923
shared_ptr< class_decl > class_decl_sptr
Convenience typedef for a shared pointer on a class_decl.
Definition: abg-fwd.h:194
uint64_t get_var_size_in_bits(const var_decl_sptr &v)
Get the size of a given variable.
Definition: abg-ir.cc:6302
shared_ptr< var_decl > var_decl_sptr
Convenience typedef for a shared pointer on a var_decl.
Definition: abg-fwd.h:257
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:121
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:5976
bool get_data_member_is_laid_out(const var_decl &m)
Test whether a data member is laid out.
Definition: abg-ir.cc:6330
bool is_member_function(const function_decl &f)
Test whether a function_decl is a member function.
Definition: abg-ir.cc:6354
var_decl * is_var_decl(const type_or_decl_base *tod)
Tests if a declaration is a variable declaration.
Definition: abg-ir.cc:11586
decl_base * is_decl(const type_or_decl_base *d)
Test if an ABI artifact is a declaration.
Definition: abg-ir.cc:10350
access_specifier get_member_access_specifier(const decl_base &d)
Gets the access specifier for a class member.
Definition: abg-ir.cc:5501
uint64_t get_data_member_offset(const var_decl &m)
Get the offset of a data member.
Definition: abg-ir.cc:6170
bool get_member_function_is_virtual(const function_decl &f)
Test if a given member function is virtual.
Definition: abg-ir.cc:6627
bool is_union_type(const type_or_decl_base &t)
Test if a type is a union_decl.
Definition: abg-ir.cc:10972
bool is_data_member(const var_decl &v)
Test if a var_decl is a data member.
Definition: abg-ir.cc:5599
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:11650
translation_unit * get_translation_unit(const decl_base &decl)
Return the translation unit a declaration belongs to.
Definition: abg-ir.cc:10126
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:8754
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:909
bool is_member_decl(const decl_base_sptr d)
Tests if a declaration is a class member.
Definition: abg-ir.cc:5403
Toplevel namespace for libabigail.