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 
13 namespace abigail
14 {
15 
16 namespace 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.
24 uint64_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.
38 void
39 emit_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.
57 uint64_t
58 maybe_convert_bits_to_bytes(uint64_t bits, const diff_context& ctxt)
59 {
60  if (ctxt.show_offsets_sizes_in_bits())
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.
95 void
96 show_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.
138 void
139 show_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.
168 void
169 show_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
181 void
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);
195  if (get_member_function_is_virtual(mem_fn))
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.
231 void
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.
278 void
280  diff_context& ctxt,
281  ostream& out)
282 {
283  if (!ctxt.show_relative_offset_changes())
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.
337 void
339  diff_context& ctxt,
340  ostream& out)
341 {
342  if (!ctxt.show_relative_offset_changes())
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.
390 void
392  diff_context_sptr ctxt,
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  }
678  if (get_member_is_static(o)
679  != get_member_is_static(n))
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 
691  if (get_member_is_static(o))
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.
727 void
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.
797 void
799  type_or_decl_base_sptr second,
800  diff_context_sptr ctxt,
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.
915 bool
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.
958 void
960  decl_base_sptr second,
961  diff_context_sptr ctxt,
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.
1009 void
1010 report_mem_header(ostream& out,
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.
1065 void
1066 report_mem_header(ostream& out,
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.
1102 bool
1103 maybe_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)
1131  && (get_member_access_specifier(decl1)
1132  != get_member_access_specifier(decl2)))
1133  {
1134  out << indent << "'" << decl1_repr << "' access changed from '"
1135  << get_member_access_specifier(decl1)
1136  << "' to '"
1137  << get_member_access_specifier(decl2)
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.
1157 bool
1158 maybe_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.
1198 void
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.
1317 void
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.
1344 void
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.
1361  if (s.net_num_removed_unreachable_types() == 1)
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!
1390  if (s.net_num_changed_unreachable_types() == 1)
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.
1419  if (s.net_num_added_unreachable_types() == 1)
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.
1456 void
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.
1505 void
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.
1516 bool
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.
1529 void
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 =
1544  i != d.ordered_data_members_replaced_by_adms().end();)
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.
1603 void
1605  ostream &out,
1606  const string &indent)
1607 {
1608  if (d.moved_bases().empty())
1609  return;
1610 
1611  class_decl_sptr first = d.first_class_decl(),
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:1589
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:2460
Abstraction of an elf symbol.
Definition: abg-ir.h:900
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:290
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:663
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:76
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:68
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:5648
ssize_t get_member_function_vtable_offset(const function_decl &f)
Get the vtable offset of a member function.
Definition: abg-ir.cc:6558
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:541
bool is_type(const type_or_decl_base &tod)
Test whether a declaration is a type.
Definition: abg-ir.cc:10194
bool is_anonymous_data_member(const decl_base &d)
Test if a decl is an anonymous data member.
Definition: abg-ir.cc:5877
shared_ptr< elf_symbol > elf_symbol_sptr
A convenience typedef for a shared pointer to elf_symbol.
Definition: abg-ir.h:863
change_kind
A bitfield that gives callers of abigail::ir::equals() some insight about how different two internal ...
Definition: abg-ir.h:1300
bool is_class_type(const type_or_decl_base &t)
Test whether a type is a class.
Definition: abg-ir.cc:10449
shared_ptr< array_type_def > array_type_def_sptr
Convenience typedef for a shared pointer on a array_type_def.
Definition: abg-fwd.h:234
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:10954
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:10509
shared_ptr< class_decl > class_decl_sptr
Convenience typedef for a shared pointer on a class_decl.
Definition: abg-fwd.h:187
uint64_t get_var_size_in_bits(const var_decl_sptr &v)
Get the size of a given variable.
Definition: abg-ir.cc:6296
shared_ptr< var_decl > var_decl_sptr
Convenience typedef for a shared pointer on a var_decl.
Definition: abg-fwd.h:246
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:119
bool get_data_member_is_laid_out(const var_decl &m)
Test whether a data member is laid out.
Definition: abg-ir.cc:6324
bool is_member_function(const function_decl &f)
Test whether a function_decl is a member function.
Definition: abg-ir.cc:6348
var_decl * is_var_decl(const type_or_decl_base *tod)
Tests if a declaration is a variable declaration.
Definition: abg-ir.cc:10890
decl_base * is_decl(const type_or_decl_base *d)
Test if an ABI artifact is a declaration.
Definition: abg-ir.cc:10134
access_specifier get_member_access_specifier(const decl_base &d)
Gets the access specifier for a class member.
Definition: abg-ir.cc:5588
uint64_t get_data_member_offset(const var_decl &m)
Get the offset of a data member.
Definition: abg-ir.cc:6164
bool get_member_function_is_virtual(const function_decl &f)
Test if a given member function is virtual.
Definition: abg-ir.cc:6621
bool is_union_type(const type_or_decl_base &t)
Test if a type is a union_decl.
Definition: abg-ir.cc:10528
bool is_data_member(const var_decl &v)
Test if a var_decl is a data member.
Definition: abg-ir.cc:5686
translation_unit * get_translation_unit(const decl_base &decl)
Return the translation unit a declaration belongs to.
Definition: abg-ir.cc:9910
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:8678
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:886
bool is_member_decl(const decl_base_sptr d)
Tests if a declaration is a class member.
Definition: abg-ir.cc:5490
Toplevel namespace for libabigail.