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