libabigail
abg-reporter-priv.cc
1// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
2// -*- Mode: C++ -*-
3//
4// Copyright (C) 2017-2023 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 = o->get_qualified_name();
406 const string n_name = n->get_qualified_name();
407 const uint64_t o_size = get_var_size_in_bits(o);
408 const uint64_t n_size = get_var_size_in_bits(n);
409 const uint64_t o_offset = get_data_member_offset(o);
410 const uint64_t n_offset = get_data_member_offset(n);
411 const string o_pretty_representation =
412 o->get_pretty_representation(/*internal=*/false, /*qualified_name=*/false);
413 // no n_pretty_representation here as it's only needed in a couple of places
414 const bool show_size_offset_changes = ctxt->get_allowed_category()
416
417 // Has the main diff text been output?
418 bool emitted = false;
419 // Are we continuing on a new line? (implies emitted)
420 bool begin_with_and = false;
421 // Have we reported a size change already?
422 bool size_reported = false;
423
424 //----------------------------------------------------------------
425 // First we'll try to emit a report about the type change of this
426 // var_decl_diff.
427 //
428 // In the context of that type change report, we need to keep in
429 // mind that because we want to emit specific (useful) reports about
430 // anonymous data member changes, we'll try to detect the various
431 // scenarii that involve anonymous data member changes.
432 //
433 // Then, as a fallback method, we'll emit a more generic type change
434 // report for the other generic type changes.
435 //----------------------------------------------------------------
436
437 if (is_strict_anonymous_data_member_change)
438 {
439 const string n_pretty_representation =
440 n->get_pretty_representation(/*internal=*/false,
441 /*qualified_name=*/false);
442 const type_base_sptr o_type = o->get_type(), n_type = n->get_type();
443 if (o_pretty_representation != n_pretty_representation)
444 {
445 show_offset_or_size(indent + "anonymous data member at offset",
446 o_offset, *ctxt, out);
447
448 out << " changed from:\n"
449 << indent << " " << o_pretty_representation << "\n"
450 << indent << "to:\n"
451 << indent << " " << n_pretty_representation << "\n";
452
453 begin_with_and = true;
454 emitted = true;
455 }
456 else if (get_type_name(o_type) != get_type_name(n_type)
457 && is_decl(o_type) && is_decl(n_type)
458 && is_decl(o_type)->get_is_anonymous()
459 && is_decl(n_type)->get_is_anonymous())
460 {
461 out << indent << "while looking at anonymous data member '"
462 << o_pretty_representation << "':\n"
463 << indent << "the internal name of that anonymous data member"
464 " changed from:\n"
465 << indent << " " << get_type_name(o_type) << "\n"
466 << indent << "to:\n"
467 << indent << " " << get_type_name(n_type) << "\n"
468 << indent << " This is usually due to "
469 << "an anonymous member type being added or removed from "
470 << "the containing type\n";
471
472 begin_with_and = true;
473 emitted = true;
474 }
475 }
477 {
478 ABG_ASSERT(o_anon != n_anon);
479 // So we are looking at a non-anonymous data member change from
480 // or to an anonymous data member.
481 const string n_pretty_representation =
482 n->get_pretty_representation(/*internal=*/false,
483 /*qualified_name=*/false);
484 out << indent << (o_anon ? "anonymous " : "")
485 << "data member " << o_pretty_representation;
486 show_offset_or_size(" at offset", o_offset, *ctxt, out);
487 out << " became " << (n_anon ? "anonymous " : "")
488 << "data member '" << n_pretty_representation << "'\n";
489
490 begin_with_and = true;
491 emitted = true;
492 }
493
494 //
495 // If we haven't succeeded in emitting a specific type change report
496 // (mainly related to anonymous data data member changes) then let's
497 // try to emit a more generic report about the type change.
498 //
499 // This is the fallback method outlined in the comment at the
500 // beginning of this section.
501 //
502 if (!emitted)
503 if (const diff_sptr d = diff->type_diff())
504 {
505 if (ctxt->get_reporter()->diff_to_be_reported(d.get()))
506 {
507 if (local_only)
508 out << indent << "type '"
509 << get_pretty_representation(o->get_type())
510 << "' of '"
511 << (o_anon ?
512 string("anonymous data member")
513 : o->get_qualified_name())
514 << "' changed";
515 else
516 out << indent
517 << "type of '"<< (o_anon ? "anonymous data member ": "")
518 << o_pretty_representation << "' changed";
519
520 if (d->currently_reporting())
521 out << ", as being reported\n";
522 else if (d->reported_once())
523 out << ", as reported earlier\n";
524 else
525 {
526 out << ":\n";
527 d->report(out, indent + " ");
528 }
529
530 begin_with_and = true;
531 emitted = true;
532 size_reported = true;
533 }
534 }
535
536 //
537 // Okay, now we are done with report type changes. Let's report the
538 // other potential kinds of changes.
539 //
540
541 if (!filtering::has_anonymous_data_member_change(diff) && o_name != n_name)
542 {
544 && !(ctxt->get_allowed_category()
546 ;
547 else
548 {
549 if (begin_with_and)
550 {
551 out << indent << "and ";
552 begin_with_and = false;
553 }
554 else if (!emitted)
555 out << indent;
556 else
557 out << ", ";
558 out << "name of '" << o_name << "' changed to '" << n_name << "'";
559 report_loc_info(n, *ctxt, out);
560 emitted = true;
561 }
562 }
563
566 {
567 if (begin_with_and)
568 {
569 out << indent << "and ";
570 begin_with_and = false;
571 }
572 else if (!emitted)
573 out << indent << "'" << o_pretty_representation << "' ";
574 else
575 out << ", ";
577 out << "is no more laid out";
578 else
579 out << "now becomes laid out";
580 emitted = true;
581 }
582 if (show_size_offset_changes)
583 {
584 if (o_offset != n_offset)
585 {
586 if (begin_with_and)
587 {
588 out << indent << "and ";
589 begin_with_and = false;
590 }
591 else if (!emitted)
592 {
593 out << indent;
594 if (is_strict_anonymous_data_member_change)
595 out << "anonymous data member ";
596 out << "'" << o_pretty_representation << "' ";
597 }
598 else
599 out << ", ";
600
601 show_numerical_change("offset", o_offset, n_offset, *ctxt, out);
603 emitted = true;
604 }
605
606 if (!size_reported && o_size != n_size)
607 {
608 if (begin_with_and)
609 {
610 out << indent << "and ";
611 begin_with_and = false;
612 }
613 else if (!emitted)
614 {
615 out << indent;
616 if (is_strict_anonymous_data_member_change)
617 out << "anonymous data member ";
618 out << "'" << o_pretty_representation << "' ";
619 }
620 else
621 out << ", ";
622
623 show_numerical_change("size", o_size, n_size, *ctxt, out);
625 emitted = true;
626 }
627 }
628 if (o->get_binding() != n->get_binding())
629 {
630 if (begin_with_and)
631 {
632 out << indent << "and ";
633 begin_with_and = false;
634 }
635 else if (!emitted)
636 out << indent << "'" << o_pretty_representation << "' ";
637 else
638 out << ", ";
639 out << "elf binding changed from " << o->get_binding()
640 << " to " << n->get_binding();
641 emitted = true;
642 }
643 if (o->get_visibility() != n->get_visibility())
644 {
645 if (begin_with_and)
646 {
647 out << indent << "and ";
648 begin_with_and = false;
649 }
650 else if (!emitted)
651 out << indent << "'" << o_pretty_representation << "' ";
652 else
653 out << ", ";
654 out << "visibility changed from " << o->get_visibility()
655 << " to " << n->get_visibility();
656 emitted = true;
657 }
658 if ((ctxt->get_allowed_category() & ACCESS_CHANGE_CATEGORY)
661 {
662 if (begin_with_and)
663 {
664 out << indent << "and ";
665 begin_with_and = false;
666 }
667 else if (!emitted)
668 out << indent << "'" << o_pretty_representation << "' ";
669 else
670 out << ", ";
671
672 out << "access changed from '"
674 << "' to '"
675 << get_member_access_specifier(n) << "'";
676 emitted = true;
677 }
680 {
681 if (begin_with_and)
682 {
683 out << indent << "and ";
684 begin_with_and = false;
685 }
686 else if (!emitted)
687 out << indent << "'" << o_pretty_representation << "' ";
688 else
689 out << ", ";
690
692 out << "is no more static";
693 else
694 out << "now becomes static";
695 emitted = true;
696 }
697
698 if (begin_with_and)
699 // do nothing as begin_with_and implies emitted
700 ;
701 else if (!emitted)
702 // We appear to have fallen off the edge of the map.
703 out << indent << "'" << o_pretty_representation
704 << "' has *some* difference - please report as a bug";
705 else
706 {
707 ;// do nothing
708 }
709 emitted = true;
710
711 if (!begin_with_and)
712 out << "\n";
713}
714
715/// Represent the changes carried by an instance of @ref subrange_diff
716/// that represent a difference between two ranges.
717///
718/// @param diff diff the diff node to represent.
719///
720/// @param ctxt the diff context to use.
721///
722/// @param local_only if true, only display local changes.
723///
724/// @param out the output stream to send the representation to.
725///
726/// @param indent the indentation string to use for the change report.
727void
729 const diff_context_sptr ctxt,
730 ostream& out,
731 const string& indent,
732 bool local_only)
733{
736 string oor = o->get_pretty_representation();
737 string nr = n->get_pretty_representation();
738 string on = o->get_name();
739 string nn = n->get_name();
740 int64_t olb = o->get_lower_bound();
741 int64_t nlb = n->get_lower_bound();
742 int64_t oub = o->get_upper_bound();
743 int64_t nub = n->get_upper_bound();
744
745 if (on != nn)
746 {
747 out << indent << "name of range changed from '"
748 << on << "' to '" << nn << "'\n";
749 }
750
751 if (olb != nlb)
752 {
753 out << indent << "lower bound of range '"
754 << on
755 << "' change from '";
756 emit_num_value(olb, *ctxt, out);
757 out << "' to '";
758 emit_num_value(nlb, *ctxt, out);
759 out << "'\n";
760 }
761
762 if (oub != nub)
763 {
764 out << indent << "upper bound of range '"
765 << on
766 << "' 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 (!local_only)
774 {
776 if (dif && dif->to_be_reported())
777 {
778 // report range underlying type changes
779 out << indent << "underlying type of range '"
780 << oor << "' changed:\n";
781 dif->report(out, indent + " ");
782 }
783 }
784}
785
786/// Report the size and alignment changes of a type.
787///
788/// @param first the first type to consider.
789///
790/// @param second the second type to consider.
791///
792/// @param ctxt the content of the current diff.
793///
794/// @param out the output stream to report the change to.
795///
796/// @param indent the string to use for indentation.
797void
801 ostream& out,
802 const string& indent)
803{
804 type_base_sptr f = dynamic_pointer_cast<type_base>(first),
805 s = dynamic_pointer_cast<type_base>(second);
806
807 if (!s || !f)
808 return;
809
810 class_or_union_sptr first_class = is_class_or_union_type(first),
811 second_class = is_class_or_union_type(second);
812
813 if (filtering::has_class_decl_only_def_change(first_class, second_class))
814 // So these two classes differ only by the fact that one is the
815 // declaration-only form of the second. The declaration-only class
816 // is of unknown size (recorded as 0) and it is not meaningful to
817 // report a size change.
818 return;
819
820 unsigned fs = f->get_size_in_bits(), ss = s->get_size_in_bits(),
821 fa = f->get_alignment_in_bits(), sa = s->get_alignment_in_bits();
822 array_type_def_sptr first_array = is_array_type(is_type(first)),
823 second_array = is_array_type(is_type(second));
824 unsigned fdc = first_array ? first_array->get_dimension_count(): 0,
825 sdc = second_array ? second_array->get_dimension_count(): 0;
826
827 if (ctxt->get_allowed_category() & SIZE_OR_OFFSET_CHANGE_CATEGORY)
828 {
829 if (fs != ss || fdc != sdc)
830 {
831 if (first_array && second_array)
832 {
833 // We are looking at size or alignment changes between two
834 // arrays ...
835 out << indent << "array type size changed from ";
836 if (first_array->is_infinite())
837 out << "\'unknown\'";
838 else
839 emit_num_value(first_array->get_size_in_bits(), *ctxt, out);
840 out << " to ";
841 if (second_array->is_infinite())
842 out << "\'unknown\'";
843 else
844 emit_num_value(second_array->get_size_in_bits(), *ctxt, out);
845 out << "\n";
846
847 if (sdc != fdc)
848 {
849 out << indent + " "
850 << "number of dimensions changed from "
851 << fdc
852 << " to "
853 << sdc
854 << "\n";
855 }
856 array_type_def::subranges_type::const_iterator i, j;
857 for (i = first_array->get_subranges().begin(),
858 j = second_array->get_subranges().begin();
859 (i != first_array->get_subranges().end()
860 && j != second_array->get_subranges().end());
861 ++i, ++j)
862 {
863 if ((*i)->get_length() != (*j)->get_length())
864 {
865 out << indent
866 << "array type subrange "
867 << i - first_array->get_subranges().begin() + 1
868 << " changed length from ";
869
870 if ((*i)->is_infinite())
871 out << "\'unknown\'";
872 else
873 out << (*i)->get_length();
874
875 out << " to ";
876
877 if ((*j)->is_infinite())
878 out << "\'unknown\'";
879 else
880 out << (*j)->get_length();
881 out << "\n";
882 }
883 }
884 } // end if (first_array && second_array)
885 else if (fs != ss)
886 {
887 out << indent;
888 show_numerical_change("type size", fs, ss, *ctxt, out);
889 out << "\n";
890 }
891 } // end if (fs != ss || fdc != sdc)
892 else
893 if (ctxt->show_relative_offset_changes())
894 {
895 out << indent << "type size hasn't changed\n";
896 }
897 }
898 if ((ctxt->get_allowed_category() & SIZE_OR_OFFSET_CHANGE_CATEGORY)
899 && (fa != sa))
900 {
901 out << indent;
902 show_numerical_change("type alignment", fa, sa, *ctxt, out,
903 /*show_bits_or_bytes=*/false);
904 out << "\n";
905 }
906}
907
908/// @param tod the type or declaration to emit loc info about
909///
910/// @param ctxt the content of the current diff.
911///
912/// @param out the output stream to report the change to.
913///
914/// @return true iff something was reported.
915bool
917 const diff_context& ctxt,
918 ostream &out)
919{
920 if (!ctxt.show_locs())
921 return false;
922
923 decl_base_sptr decl = is_decl(tod);
924
925 if (!decl)
926 return false;
927
928 location loc;
930
931 if (tu && (loc = decl->get_location()))
932 {
933 string path;
934 unsigned line, column;
935
936 loc.expand(path, line, column);
937 //tu->get_loc_mgr().expand_location(loc, path, line, column);
938 path = basename(const_cast<char*>(path.c_str()));
939
940 out << " at " << path << ":" << line << ":" << column;
941
942 return true;
943 }
944 return false;
945}
946
947/// Report the name, size and alignment changes of a type.
948///
949/// @param first the first type to consider.
950///
951/// @param second the second type to consider.
952///
953/// @param ctxt the content of the current diff.
954///
955/// @param out the output stream to report the change to.
956///
957/// @param indent the string to use for indentation.
958void
960 decl_base_sptr second,
962 ostream& out,
963 const string& indent)
964{
965 string fn = first->get_qualified_name(),
966 sn = second->get_qualified_name();
967
968 if (fn != sn)
969 {
970 if (!(ctxt->get_allowed_category() & HARMLESS_DECL_NAME_CHANGE_CATEGORY)
971 && filtering::has_harmless_name_change(first, second))
972 // This is a harmless name change. but then
973 // HARMLESS_DECL_NAME_CHANGE_CATEGORY doesn't seem allowed.
974 ;
975 else
976 {
977 out << indent;
978 if (is_type(first))
979 out << "type";
980 else
981 out << "declaration";
982 out << " name changed from '" << fn << "' to '" << sn << "'";
983 out << "\n";
984 }
985 }
986
987 report_size_and_alignment_changes(first, second, ctxt, out, indent);
988}
989
990/// Output the header preceding the the report for
991/// insertion/deletion/change of a part of a class. This is a
992/// subroutine of class_diff::report.
993///
994/// @param out the output stream to output the report to.
995///
996/// @param number the number of insertion/deletion to refer to in the
997/// header.
998///
999/// @param num_filtered the number of filtered changes.
1000///
1001/// @param k the kind of diff (insertion/deletion/change) we want the
1002/// head to introduce.
1003///
1004/// @param section_name the name of the sub-part of the class to
1005/// report about.
1006///
1007/// @param indent the string to use as indentation prefix in the
1008/// header.
1009void
1011 size_t number,
1012 size_t num_filtered,
1013 diff_kind k,
1014 const string& section_name,
1015 const string& indent)
1016{
1017 size_t net_number = number - num_filtered;
1018 string change;
1019 char colon_or_semi_colon = ':';
1020
1021 switch (k)
1022 {
1023 case del_kind:
1024 change = (number > 1) ? "deletions" : "deletion";
1025 break;
1026 case ins_kind:
1027 change = (number > 1) ? "insertions" : "insertion";
1028 break;
1029 case subtype_change_kind:
1030 case change_kind:
1031 change = (number > 1) ? "changes" : "change";
1032 break;
1033 }
1034
1035 if (net_number == 0)
1036 {
1037 out << indent << "no " << section_name << " " << change;
1038 colon_or_semi_colon = ';';
1039 }
1040 else if (net_number == 1)
1041 out << indent << "1 " << section_name << " " << change;
1042 else
1043 out << indent << net_number << " " << section_name
1044 << " " << change;
1045
1046 if (num_filtered)
1047 out << " (" << num_filtered << " filtered)";
1048 out << colon_or_semi_colon << "\n";
1049}
1050
1051/// Output the header preceding the the report for
1052/// insertion/deletion/change of a part of a class. This is a
1053/// subroutine of class_diff::report.
1054///
1055/// @param out the output stream to output the report to.
1056///
1057/// @param k the kind of diff (insertion/deletion/change) we want the
1058/// head to introduce.
1059///
1060/// @param section_name the name of the sub-part of the class to
1061/// report about.
1062///
1063/// @param indent the string to use as indentation prefix in the
1064/// header.
1065void
1067 diff_kind k,
1068 const string& section_name,
1069 const string& indent)
1070{
1071 string change;
1072
1073 switch (k)
1074 {
1075 case del_kind:
1076 change = "deletions";
1077 break;
1078 case ins_kind:
1079 change = "insertions";
1080 break;
1081 case subtype_change_kind:
1082 case change_kind:
1083 change = "changes";
1084 break;
1085 }
1086
1087 out << indent << "there are " << section_name << " " << change << ":\n";
1088}
1089
1090/// Report the differences in access specifiers and static-ness for
1091/// class members.
1092///
1093/// @param decl1 the first class member to consider.
1094///
1095/// @param decl2 the second class member to consider.
1096///
1097/// @param out the output stream to send the report to.
1098///
1099/// @param indent the indentation string to use for the report.
1100///
1101/// @return true if something was reported, false otherwise.
1102bool
1103maybe_report_diff_for_member(const decl_base_sptr& decl1,
1104 const decl_base_sptr& decl2,
1105 const diff_context_sptr& ctxt,
1106 ostream& out,
1107 const string& indent)
1108
1109{
1110 bool reported = false;
1111 if (!is_member_decl(decl1) || !is_member_decl(decl2))
1112 return reported;
1113
1114 string decl1_repr = decl1->get_pretty_representation(),
1115 decl2_repr = decl2->get_pretty_representation();
1116
1117 if (get_member_is_static(decl1) != get_member_is_static(decl2))
1118 {
1119 bool lost = get_member_is_static(decl1);
1120 out << indent << "'" << decl1_repr << "' ";
1121 if (report_loc_info(decl2, *ctxt, out))
1122 out << " ";
1123 if (lost)
1124 out << "became non-static";
1125 else
1126 out << "became static";
1127 out << "\n";
1128 reported = true;
1129 }
1130 if ((ctxt->get_allowed_category() & ACCESS_CHANGE_CATEGORY)
1132 != get_member_access_specifier(decl2)))
1133 {
1134 out << indent << "'" << decl1_repr << "' access changed from '"
1136 << "' to '"
1138 << "'\n";
1139 reported = true;
1140 }
1141 return reported;
1142}
1143
1144/// Report the differences between two generic variables.
1145///
1146/// @param decl1 the first version of the variable.
1147///
1148/// @param decl2 the second version of the variable.
1149///
1150/// @param ctxt the context of the diff.
1151///
1152/// @param out the output stream to emit the change report to.
1153///
1154/// @param indent the indentation prefix to emit.
1155///
1156/// @return true if any text has been emitted to the output stream.
1157bool
1158maybe_report_diff_for_variable(const decl_base_sptr& decl1,
1159 const decl_base_sptr& decl2,
1160 const diff_context_sptr& ctxt,
1161 ostream& out,
1162 const string& indent)
1163{
1164 bool reported = false;
1165
1166 var_decl_sptr var1 = is_var_decl(decl1);
1167 var_decl_sptr var2 = is_var_decl(decl2);
1168
1169 if (!var1 || !var2)
1170 return reported;
1171
1173 {
1174 uint64_t var_size_in_bits = var1->get_symbol()->get_size() * 8;
1175
1176 out << indent;
1177 show_offset_or_size("size of variable symbol (",
1178 var_size_in_bits, *ctxt, out);
1179 out << ") hasn't changed\n"
1180 << indent << "but it does have a harmless type change\n";
1181 reported = true;
1182 }
1183
1184 return reported;
1185}
1186
1187/// Report the difference between two ELF symbols, if there is any.
1188///
1189/// @param symbol1 the first symbol to consider.
1190///
1191/// @param symbol2 the second symbol to consider.
1192///
1193/// @param ctxt the diff context.
1194///
1195/// @param the output stream to emit the report to.
1196///
1197/// @param indent the indentation string to use.
1198void
1200 const elf_symbol_sptr& symbol2,
1201 const diff_context_sptr& ctxt,
1202 ostream& out,
1203 const string& indent)
1204{
1205 if (!symbol1 || !symbol2 || symbol1 == symbol2)
1206 return;
1207
1208 if (symbol1->get_size() != symbol2->get_size())
1209 {
1210 out << indent;
1211 show_numerical_change("size of symbol",
1212 symbol1->get_size(),
1213 symbol2->get_size(),
1214 *ctxt, out,
1215 /*show_bits_or_bytes=*/false);
1216 out << "\n";
1217 }
1218
1219 if (symbol1->get_name() != symbol2->get_name())
1220 {
1221 out << indent << "symbol name changed from "
1222 << symbol1->get_name()
1223 << " to "
1224 << symbol2->get_name()
1225 << "\n";
1226 }
1227
1228 if (symbol1->get_type() != symbol2->get_type())
1229 {
1230 out << indent << "symbol type changed from '"
1231 << symbol1->get_type()
1232 << "' to '"
1233 << symbol2->get_type()
1234 << "'\n";
1235 }
1236
1237 if (symbol1->is_public() != symbol2->is_public())
1238 {
1239 out << indent << "symbol became ";
1240 if (symbol2->is_public())
1241 out << "exported";
1242 else
1243 out << "non-exported";
1244 out << "\n";
1245 }
1246
1247 if (symbol1->is_defined() != symbol2->is_defined())
1248 {
1249 out << indent << "symbol became ";
1250 if (symbol2->is_defined())
1251 out << "defined";
1252 else
1253 out << "undefined";
1254 out << "\n";
1255 }
1256
1257 if (symbol1->get_version() != symbol2->get_version())
1258 {
1259 out << indent << "symbol version changed from "
1260 << symbol1->get_version().str()
1261 << " to "
1262 << symbol2->get_version().str()
1263 << "\n";
1264 }
1265
1266 const abg_compat::optional<uint32_t>& crc1 = symbol1->get_crc();
1267 const abg_compat::optional<uint32_t>& crc2 = symbol2->get_crc();
1268 if (crc1 != crc2)
1269 {
1270 const std::string none = "(none)";
1271 out << indent << "CRC (modversions) changed from "
1272 << std::showbase << std::hex;
1273 if (crc1.has_value())
1274 out << crc1.value();
1275 else
1276 out << none;
1277 out << " to ";
1278 if (crc2.has_value())
1279 out << crc2.value();
1280 else
1281 out << none;
1282 out << std::noshowbase << std::dec
1283 << "\n";
1284 }
1285
1286 const abg_compat::optional<std::string>& ns1 = symbol1->get_namespace();
1287 const abg_compat::optional<std::string>& ns2 = symbol2->get_namespace();
1288 if (ns1 != ns2)
1289 {
1290 const std::string none = "(none)";
1291 out << indent << "namespace changed from ";
1292 if (ns1.has_value())
1293 out << "'" << ns1.value() << "'";
1294 else
1295 out << none;
1296 out << " to ";
1297 if (ns2.has_value())
1298 out << "'" << ns2.value() << "'";
1299 else
1300 out << none;
1301 out << "\n";
1302 }
1303}
1304
1305/// For a given symbol, emit a string made of its name and version.
1306/// The string also contains the list of symbols that alias this one.
1307///
1308/// @param out the output string to emit the resulting string to.
1309///
1310/// @param indent the indentation string to use before emitting the
1311/// resulting string.
1312///
1313/// @param symbol the symbol to emit the representation string for.
1314///
1315/// @param sym_map the symbol map to consider to look for aliases of
1316/// @p symbol.
1317void
1319 const string& indent,
1320 const elf_symbol& symbol,
1321 const string_elf_symbols_map_type& sym_map)
1322{
1323 out << indent << symbol.get_id_string();
1324 string aliases =
1325 symbol.get_aliases_id_string(sym_map,
1326 /*include_symbol_itself=*/false);
1327 if (!aliases.empty())
1328 out << ", aliases " << aliases;
1329}
1330
1331/// Report changes about types that are not reachable from global
1332/// functions and variables, in a given @param corpus_diff.
1333///
1334/// @param d the corpus_diff to consider.
1335///
1336/// @param s the statistics of the changes, after filters and
1337/// suppressions are reported. This is typically what is returned by
1338/// corpus_diff::apply_filters_and_suppressions_before_reporting().
1339///
1340/// @param indent the indendation string (usually a string of white
1341/// spaces) to use for indentation during the reporting.
1342///
1343/// @param out the output stream to emit the report to.
1344void
1346 const corpus_diff::diff_stats &s,
1347 const string& indent,
1348 ostream& out)
1349{
1350 const diff_context_sptr& ctxt = d.context();
1351
1352 if (!(ctxt->show_unreachable_types()
1353 && (!d.priv_->deleted_unreachable_types_.empty()
1354 || !d.priv_->added_unreachable_types_.empty()
1355 || !d.priv_->changed_unreachable_types_.empty())))
1356 // The user either doesn't want us to show changes about
1357 // unreachable types or there are not such changes.
1358 return;
1359
1360 // Handle removed unreachable types.
1362 out << indent
1363 << "1 removed type unreachable from any public interface:\n\n";
1364 else if (s.net_num_removed_unreachable_types() > 1)
1365 out << indent
1367 << " removed types unreachable from any public interface:\n\n";
1368
1369 vector<type_base_sptr> sorted_removed_unreachable_types;
1370 sort_string_type_base_sptr_map(d.priv_->deleted_unreachable_types_,
1371 sorted_removed_unreachable_types);
1372 bool emitted = false;
1373 for (vector<type_base_sptr>::const_iterator i =
1374 sorted_removed_unreachable_types.begin();
1375 i != sorted_removed_unreachable_types.end();
1376 ++i)
1377 {
1378 if (d.priv_->deleted_unreachable_type_is_suppressed((*i).get()))
1379 continue;
1380
1381 out << indent << " [D] '" << get_pretty_representation(*i) << "'";
1382 report_loc_info(*i, *ctxt, out);
1383 out << "\n";
1384 emitted = true;
1385 }
1386 if (emitted)
1387 out << "\n";
1388
1389 // Handle changed unreachable types!
1391 out << indent
1392 << "1 changed type unreachable from any public interface:\n\n";
1393 else if (s.net_num_changed_unreachable_types() > 1)
1394 out << indent
1396 << " changed types unreachable from any public interface:\n\n";
1397
1398 diff_sptrs_type sorted_diff_sptrs;
1399 sort_string_diff_sptr_map(d.priv_->changed_unreachable_types_,
1400 sorted_diff_sptrs);
1401 for (diff_sptrs_type::const_iterator i = sorted_diff_sptrs.begin();
1402 i != sorted_diff_sptrs.end();
1403 ++i)
1404 {
1405 diff_sptr diff = *i;
1406 if (!diff || !diff->to_be_reported())
1407 continue;
1408
1409 string repr = diff->first_subject()->get_pretty_representation();
1410
1411 out << indent << " [C] '" << repr << "' changed:\n";
1412 diff->report(out, indent + " ");
1413 // Extra spacing.
1414 out << "\n";
1415 }
1416 // Changed types have extra spacing already. No new line here.
1417
1418 // Handle added unreachable types.
1420 out << indent
1421 << "1 added type unreachable from any public interface:\n\n";
1422 else if (s.net_num_added_unreachable_types() > 1)
1423 out << indent
1425 << " added types unreachable from any public interface:\n\n";
1426
1427 vector<type_base_sptr> sorted_added_unreachable_types;
1428 sort_string_type_base_sptr_map(d.priv_->added_unreachable_types_,
1429 sorted_added_unreachable_types);
1430 emitted = false;
1431 for (vector<type_base_sptr>::const_iterator i =
1432 sorted_added_unreachable_types.begin();
1433 i != sorted_added_unreachable_types.end();
1434 ++i)
1435 {
1436 if (d.priv_->added_unreachable_type_is_suppressed((*i).get()))
1437 continue;
1438
1439 out << indent << " [A] '" << get_pretty_representation(*i) << "'";
1440 report_loc_info(*i, *ctxt, out);
1441 out << "\n";
1442 emitted = true;
1443 }
1444 if (emitted)
1445 out << "\n";
1446}
1447
1448/// If a given diff node impacts some public interfaces, then report
1449/// about those impacted interfaces on a given output stream.
1450///
1451/// @param d the diff node to get the impacted interfaces for.
1452///
1453/// @param out the output stream to report to.
1454///
1455/// @param indent the white space string to use for indentation.
1456void
1458 ostream &out,
1459 const string &indent)
1460{
1461 const diff_context_sptr &ctxt = d->context();
1462 const corpus_diff_sptr &corp_diff = ctxt->get_corpus_diff();
1463 if (!corp_diff)
1464 return;
1465
1466 if (!ctxt->show_impacted_interfaces())
1467 return;
1468
1469 const diff_maps &maps = corp_diff->get_leaf_diffs();
1470 artifact_sptr_set_type* impacted_artifacts =
1472 if (impacted_artifacts == 0)
1473 return;
1474
1475 if (impacted_artifacts->empty())
1476 return;
1477
1478 vector<type_or_decl_base_sptr> sorted_impacted_interfaces;
1479 sort_artifacts_set(*impacted_artifacts, sorted_impacted_interfaces);
1480
1481 size_t num_impacted_interfaces = impacted_artifacts->size();
1482 if (num_impacted_interfaces == 1)
1483 out << indent << "one impacted interface:\n";
1484 else
1485 out << indent << num_impacted_interfaces << " impacted interfaces:\n";
1486
1487 string cur_indent = indent + " ";
1488 vector<type_or_decl_base_sptr>::const_iterator it;
1489 for (it = sorted_impacted_interfaces.begin();
1490 it != sorted_impacted_interfaces.end();
1491 ++it)
1492 {
1493 out << cur_indent << get_pretty_representation(*it) << "\n";
1494 }
1495}
1496
1497/// If a given diff node impacts some public interfaces, then report
1498/// about those impacted interfaces on standard output.
1499///
1500/// @param d the diff node to get the impacted interfaces for.
1501///
1502/// @param out the output stream to report to.
1503///
1504/// @param indent the white space string to use for indentation.
1505void
1507 ostream &out,
1508 const string &indent)
1509{return maybe_report_interfaces_impacted_by_diff(d.get(), out, indent);}
1510
1511/// Tests if the diff node is to be reported.
1512///
1513/// @param p the diff to consider.
1514///
1515/// @return true iff the diff is to be reported.
1516bool
1518{return d && d->to_be_reported();}
1519
1520/// Report about data members replaced by an anonymous data member
1521/// without changing the overall bit-layout of the class or union in
1522/// an ABI-meaningful way.
1523///
1524/// @param d the diff to consider.
1525///
1526/// @param out the output stream to emit the change report to.
1527///
1528/// @param indent the indentation string to use.
1529void
1531 ostream &out,
1532 const string &indent)
1533{
1534 const diff_context_sptr& ctxt = d.context();
1535
1536 if ((ctxt->get_allowed_category() & HARMLESS_DATA_MEMBER_CHANGE_CATEGORY)
1537 && !d.data_members_replaced_by_adms().empty())
1538 {
1539 // Let's detect all the data members that are replaced by
1540 // members of the same anonymous data member and report them
1541 // in one go.
1542 for (changed_var_sptrs_type::const_iterator i =
1545 {
1546 // This contains the data members replaced by the same
1547 // anonymous data member.
1548 vector<var_decl_sptr> dms_replaced_by_same_anon_dm;
1549 dms_replaced_by_same_anon_dm.push_back(i->first);
1550 // This contains the anonymous data member that replaced the
1551 // data members in the variable above.
1552 var_decl_sptr anonymous_data_member = i->second;
1553 // Let's look forward to see if the subsequent data
1554 // members were replaced by members of
1555 // anonymous_data_member.
1556 for (++i;
1558 && *i->second == *anonymous_data_member;
1559 ++i)
1560 dms_replaced_by_same_anon_dm.push_back(i->first);
1561
1562 bool several_data_members_replaced =
1563 dms_replaced_by_same_anon_dm.size() > 1;
1564
1565 out << indent << "data member";
1566 if (several_data_members_replaced)
1567 out << "s";
1568
1569 bool first_data_member = true;
1570 for (vector<var_decl_sptr>::const_iterator it =
1571 dms_replaced_by_same_anon_dm.begin();
1572 it != dms_replaced_by_same_anon_dm.end();
1573 ++it)
1574 {
1575 string name = (*it)->get_qualified_name();
1576 if (!first_data_member)
1577 out << ",";
1578 out << " '" << name << "'";
1579 first_data_member = false;
1580 }
1581
1582 if (several_data_members_replaced)
1583 out << " were ";
1584 else
1585 out << " was ";
1586
1587 out << "replaced by anonymous data member:\n"
1588 << indent + " "
1589 << "'"
1590 << anonymous_data_member->get_pretty_representation()
1591 << "'\n";
1592 }
1593 }
1594}
1595
1596/// Report about the base classes of a class having been re-ordered.
1597///
1598/// @param d the class diff to consider.
1599///
1600/// @param out the output stream to report the change to.
1601///
1602/// @param indent the indentation string to use.
1603void
1605 ostream &out,
1606 const string &indent)
1607{
1608 if (d.moved_bases().empty())
1609 return;
1610
1612 second = d.second_class_decl();
1613
1614 ABG_ASSERT(!first->get_base_specifiers().empty());
1615 ABG_ASSERT(!second->get_base_specifiers().empty());
1616
1617 out << indent << "base classes of '"
1618 << first->get_pretty_representation()
1619 << "' are re-ordered from: ";
1620
1621 vector<class_decl_sptr> classes = {first, second};
1622 unsigned nb_classes_seen = 0;
1623 for (auto &klass : classes)
1624 {
1625 if (nb_classes_seen >= 1)
1626 out << " to: ";
1627 out << "'";
1628 bool needs_comma = false;
1629 for (auto &b : klass->get_base_specifiers())
1630 {
1631 if (needs_comma)
1632 out << ", ";
1633 if (b->get_is_virtual())
1634 out << "virtual ";
1635 out << b->get_base_class()->get_qualified_name();
1636 needs_comma = true;
1637 }
1638 out << "'";
1639 nb_classes_seen++;
1640 }
1641 if (nb_classes_seen)
1642 out << "\n";
1643}
1644} // Namespace comparison
1645} // 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:1612
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:2473
Abstraction of an elf symbol.
Definition: abg-ir.h:909
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:2500
const string & get_id_string() const
Get a string that is representative of a given elf_symbol.
Definition: abg-ir.cc:2430
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:393
This is the abstraction of the set of relevant artefacts (types, variable declarations,...
Definition: abg-ir.h:672
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:5717
ssize_t get_member_function_vtable_offset(const function_decl &f)
Get the vtable offset of a member function.
Definition: abg-ir.cc:6611
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:10248
bool is_anonymous_data_member(const decl_base &d)
Test if a decl is an anonymous data member.
Definition: abg-ir.cc:5946
shared_ptr< elf_symbol > elf_symbol_sptr
A convenience typedef for a shared pointer to elf_symbol.
Definition: abg-ir.h:874
change_kind
A bitfield that gives callers of abigail::ir::equals() some insight about how different two internal ...
Definition: abg-ir.h:1309
bool is_class_type(const type_or_decl_base &t)
Test whether a type is a class.
Definition: abg-ir.cc:10512
shared_ptr< array_type_def > array_type_def_sptr
Convenience typedef for a shared pointer on a array_type_def.
Definition: abg-fwd.h:237
array_type_def * is_array_type(const type_or_decl_base *type)
Test if a type is an array_type_def.
Definition: abg-ir.cc:11148
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:10586
shared_ptr< class_decl > class_decl_sptr
Convenience typedef for a shared pointer on a class_decl.
Definition: abg-fwd.h:190
uint64_t get_var_size_in_bits(const var_decl_sptr &v)
Get the size of a given variable.
Definition: abg-ir.cc:6349
shared_ptr< var_decl > var_decl_sptr
Convenience typedef for a shared pointer on a var_decl.
Definition: abg-fwd.h:249
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 get_data_member_is_laid_out(const var_decl &m)
Test whether a data member is laid out.
Definition: abg-ir.cc:6377
bool is_member_function(const function_decl &f)
Test whether a function_decl is a member function.
Definition: abg-ir.cc:6401
var_decl * is_var_decl(const type_or_decl_base *tod)
Tests if a declaration is a variable declaration.
Definition: abg-ir.cc:11084
decl_base * is_decl(const type_or_decl_base *d)
Test if an ABI artifact is a declaration.
Definition: abg-ir.cc:10188
access_specifier get_member_access_specifier(const decl_base &d)
Gets the access specifier for a class member.
Definition: abg-ir.cc:5657
uint64_t get_data_member_offset(const var_decl &m)
Get the offset of a data member.
Definition: abg-ir.cc:6217
bool get_member_function_is_virtual(const function_decl &f)
Test if a given member function is virtual.
Definition: abg-ir.cc:6674
bool is_union_type(const type_or_decl_base &t)
Test if a type is a union_decl.
Definition: abg-ir.cc:10605
bool is_data_member(const var_decl &v)
Test if a var_decl is a data member.
Definition: abg-ir.cc:5755
translation_unit * get_translation_unit(const decl_base &decl)
Return the translation unit a declaration belongs to.
Definition: abg-ir.cc:9964
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:8732
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:895
bool is_member_decl(const decl_base_sptr d)
Tests if a declaration is a class member.
Definition: abg-ir.cc:5559
Toplevel namespace for libabigail.