libabigail
abg-reporter-priv.cc
1 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
2 // -*- Mode: C++ -*-
3 //
4 // Copyright (C) 2017-2025 Red Hat, Inc.
5 //
6 // Author: Dodji Seketeli
7 
8 #include <libgen.h>
9 #include <algorithm>
10 #include "abg-comparison-priv.h"
11 #include "abg-reporter-priv.h"
12 
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 '" << oor << "' change from '";
758  emit_num_value(olb, *ctxt, out);
759  out << "' to '";
760  emit_num_value(nlb, *ctxt, out);
761  out << "'\n";
762  }
763 
764  if (oub != nub)
765  {
766  out << indent << "upper bound of '" << oor << "' change from '";
767  emit_num_value(oub, *ctxt, out);
768  out << "' to '";
769  emit_num_value(nub, *ctxt, out);
770  out << "'\n";
771  }
772 
773  if (o->is_non_finite() != n->is_non_finite())
774  {
775  out << indent
776  << (o->is_non_finite()
777  ? string("unknown sized")
778  : string("known sized"))
779  << string(" range '")
780  << oor
781  << string("' changed to ")
782  << (n->is_non_finite()
783  ? string("unknown sized")
784  : string("known sized"))
785  << string(" range '")
786  << nr
787  << "'\n";
788  }
789 
790  if (!local_only)
791  {
793  if (dif && dif->to_be_reported())
794  {
795  // report range underlying type changes
796  out << indent << "underlying type of range '"
797  << oor << "' changed:\n";
798  dif->report(out, indent + " ");
799  }
800  }
801 }
802 
803 /// Report the size and alignment changes of a type.
804 ///
805 /// @param first the first type to consider.
806 ///
807 /// @param second the second type to consider.
808 ///
809 /// @param ctxt the content of the current diff.
810 ///
811 /// @param out the output stream to report the change to.
812 ///
813 /// @param indent the string to use for indentation.
814 void
816  type_or_decl_base_sptr second,
817  diff_context_sptr ctxt,
818  ostream& out,
819  const string& indent)
820 {
821  type_base_sptr f = dynamic_pointer_cast<type_base>(first),
822  s = dynamic_pointer_cast<type_base>(second);
823 
824  if (!s || !f)
825  return;
826 
827  class_or_union_sptr first_class = is_class_or_union_type(first),
828  second_class = is_class_or_union_type(second);
829 
830  if (filtering::has_class_decl_only_def_change(first_class, second_class))
831  // So these two classes differ only by the fact that one is the
832  // declaration-only form of the second. The declaration-only class
833  // is of unknown size (recorded as 0) and it is not meaningful to
834  // report a size change.
835  return;
836 
837  unsigned fs = f->get_size_in_bits(), ss = s->get_size_in_bits(),
838  fa = f->get_alignment_in_bits(), sa = s->get_alignment_in_bits();
839  array_type_def_sptr first_array = is_array_type(is_type(first)),
840  second_array = is_array_type(is_type(second));
841  unsigned fdc = first_array ? first_array->get_dimension_count(): 0,
842  sdc = second_array ? second_array->get_dimension_count(): 0;
843 
844  if (ctxt->get_allowed_category() & SIZE_OR_OFFSET_CHANGE_CATEGORY)
845  {
846  if (fs != ss || fdc != sdc)
847  {
848  if (first_array && second_array)
849  {
850  // We are looking at size or alignment changes between two
851  // arrays ...
852  out << indent << "array type size changed from ";
853  if (first_array->is_non_finite())
854  out << "\'unknown\'";
855  else
856  emit_num_value(first_array->get_size_in_bits(), *ctxt, out);
857  out << " to ";
858  if (second_array->is_non_finite())
859  out << "\'unknown\'";
860  else
861  emit_num_value(second_array->get_size_in_bits(), *ctxt, out);
862  out << "\n";
863 
864  if (sdc != fdc)
865  {
866  out << indent + " "
867  << "number of dimensions changed from "
868  << fdc
869  << " to "
870  << sdc
871  << "\n";
872  }
873  array_type_def::subranges_type::const_iterator i, j;
874  for (i = first_array->get_subranges().begin(),
875  j = second_array->get_subranges().begin();
876  (i != first_array->get_subranges().end()
877  && j != second_array->get_subranges().end());
878  ++i, ++j)
879  {
880  if ((*i)->get_length() != (*j)->get_length())
881  {
882  out << indent
883  << "array type subrange "
884  << i - first_array->get_subranges().begin() + 1
885  << " changed length from ";
886 
887  if ((*i)->is_non_finite())
888  out << "\'unknown\'";
889  else
890  out << (*i)->get_length();
891 
892  out << " to ";
893 
894  if ((*j)->is_non_finite())
895  out << "\'unknown\'";
896  else
897  out << (*j)->get_length();
898  out << "\n";
899  }
900  }
901  } // end if (first_array && second_array)
902  else if (fs != ss)
903  {
904  out << indent;
905  show_numerical_change("type size", fs, ss, *ctxt, out);
906  out << "\n";
907  }
908  } // end if (fs != ss || fdc != sdc)
909  else
910  if (ctxt->show_relative_offset_changes())
911  {
912  out << indent << "type size hasn't changed\n";
913  }
914  }
915  if ((ctxt->get_allowed_category() & SIZE_OR_OFFSET_CHANGE_CATEGORY)
916  && (fa != sa))
917  {
918  out << indent;
919  show_numerical_change("type alignment", fa, sa, *ctxt, out,
920  /*show_bits_or_bytes=*/false);
921  out << "\n";
922  }
923 }
924 
925 /// @param tod the type or declaration to emit loc info about
926 ///
927 /// @param ctxt the content of the current diff.
928 ///
929 /// @param out the output stream to report the change to.
930 ///
931 /// @return true iff something was reported.
932 bool
934  const diff_context& ctxt,
935  ostream &out)
936 {
937  if (!ctxt.show_locs())
938  return false;
939 
940  decl_base_sptr decl = is_decl(tod);
941 
942  if (!decl)
943  return false;
944 
945  location loc;
947 
948  if (tu && (loc = decl->get_location()))
949  {
950  string path;
951  unsigned line, column;
952 
953  loc.expand(path, line, column);
954  //tu->get_loc_mgr().expand_location(loc, path, line, column);
955  path = basename(const_cast<char*>(path.c_str()));
956 
957  out << " at " << path << ":" << line << ":" << column;
958 
959  return true;
960  }
961  return false;
962 }
963 
964 /// Report the name, size and alignment changes of a type.
965 ///
966 /// @param first the first type to consider.
967 ///
968 /// @param second the second type to consider.
969 ///
970 /// @param ctxt the content of the current diff.
971 ///
972 /// @param out the output stream to report the change to.
973 ///
974 /// @param indent the string to use for indentation.
975 void
977  decl_base_sptr second,
978  diff_context_sptr ctxt,
979  ostream& out,
980  const string& indent)
981 {
982  string fn = first->get_qualified_name(),
983  sn = second->get_qualified_name();
984 
985  if (!(first->get_is_anonymous() && second->get_is_anonymous())
986  && fn != sn)
987  {
988  if (!(ctxt->get_allowed_category() & HARMLESS_DECL_NAME_CHANGE_CATEGORY)
989  && filtering::has_harmless_name_change(first, second, ctxt))
990  // This is a harmless name change. but then
991  // HARMLESS_DECL_NAME_CHANGE_CATEGORY doesn't seem allowed.
992  ;
993  else
994  {
995  out << indent;
996  if (is_type(first))
997  out << "type";
998  else
999  out << "declaration";
1000  out << " name changed from '" << fn << "' to '" << sn << "'";
1001  out << "\n";
1002  }
1003  }
1004 
1005  report_size_and_alignment_changes(first, second, ctxt, out, indent);
1006 }
1007 
1008 /// Output the header preceding the the report for
1009 /// insertion/deletion/change of a part of a class. This is a
1010 /// subroutine of class_diff::report.
1011 ///
1012 /// @param out the output stream to output the report to.
1013 ///
1014 /// @param number the number of insertion/deletion to refer to in the
1015 /// header.
1016 ///
1017 /// @param num_filtered the number of filtered changes.
1018 ///
1019 /// @param k the kind of diff (insertion/deletion/change) we want the
1020 /// head to introduce.
1021 ///
1022 /// @param section_name the name of the sub-part of the class to
1023 /// report about.
1024 ///
1025 /// @param indent the string to use as indentation prefix in the
1026 /// header.
1027 void
1028 report_mem_header(ostream& out,
1029  size_t number,
1030  size_t num_filtered,
1031  diff_kind k,
1032  const string& section_name,
1033  const string& indent)
1034 {
1035  size_t net_number = number - num_filtered;
1036  string change;
1037  char colon_or_semi_colon = ':';
1038 
1039  switch (k)
1040  {
1041  case del_kind:
1042  change = (number > 1) ? "deletions" : "deletion";
1043  break;
1044  case ins_kind:
1045  change = (number > 1) ? "insertions" : "insertion";
1046  break;
1047  case subtype_change_kind:
1048  case change_kind:
1049  change = (number > 1) ? "changes" : "change";
1050  break;
1051  }
1052 
1053  if (net_number == 0)
1054  {
1055  out << indent << "no " << section_name << " " << change;
1056  colon_or_semi_colon = ';';
1057  }
1058  else if (net_number == 1)
1059  out << indent << "1 " << section_name << " " << change;
1060  else
1061  out << indent << net_number << " " << section_name
1062  << " " << change;
1063 
1064  if (num_filtered)
1065  out << " (" << num_filtered << " filtered)";
1066  out << colon_or_semi_colon << "\n";
1067 }
1068 
1069 /// Output the header preceding the the report for
1070 /// insertion/deletion/change of a part of a class. This is a
1071 /// subroutine of class_diff::report.
1072 ///
1073 /// @param out the output stream to output the report to.
1074 ///
1075 /// @param k the kind of diff (insertion/deletion/change) we want the
1076 /// head to introduce.
1077 ///
1078 /// @param section_name the name of the sub-part of the class to
1079 /// report about.
1080 ///
1081 /// @param indent the string to use as indentation prefix in the
1082 /// header.
1083 void
1084 report_mem_header(ostream& out,
1085  diff_kind k,
1086  const string& section_name,
1087  const string& indent)
1088 {
1089  string change;
1090 
1091  switch (k)
1092  {
1093  case del_kind:
1094  change = "deletions";
1095  break;
1096  case ins_kind:
1097  change = "insertions";
1098  break;
1099  case subtype_change_kind:
1100  case change_kind:
1101  change = "changes";
1102  break;
1103  }
1104 
1105  out << indent << "there are " << section_name << " " << change << ":\n";
1106 }
1107 
1108 /// Emit a report about a changed function.
1109 ///
1110 /// @param ctxt the diff context to use for the report.
1111 ///
1112 /// @param fn_diff the function_diff node to emit the report for.
1113 ///
1114 /// @param out the output stream to emit the report to.
1115 ///
1116 /// @param indent the indentation string to use.
1117 ///
1118 /// @param indirect_changed_subtypes if true, this means there are
1119 /// indirect sub-types changes. Indirect means it's a pointed-to-type
1120 /// that changed.
1121 ///
1122 /// @param emit_redundant_fn_changes if true, the function reports
1123 /// about changes carried by @fn_diff even if they are redundant.
1124 void
1126  const function_decl_diff_sptr& fn_diff,
1127  ostream& out, const string indent,
1128  bool indirect_changed_subtypes,
1129  bool emit_redundant_fn_changes)
1130 {
1131  bool saved_show_redundant_changes = ctxt->show_redundant_changes();
1132  ctxt->show_redundant_changes(emit_redundant_fn_changes);
1133 
1134  if (fn_diff->to_be_reported())
1135  {
1136  function_decl_sptr fn = fn_diff->first_function_decl();
1137  out << indent << " [C] '"
1138  << fn->get_pretty_representation() << "'";
1139  report_loc_info(fn_diff->first_function_decl(), *ctxt, out);
1140 
1141  out << " has some";
1142  if (indirect_changed_subtypes)
1143  out << " indirect";
1144  out << " sub-type changes:\n";
1145 
1146  if ((fn->get_symbol()->has_aliases()
1147  && !(is_member_function(fn)
1149  && !(is_member_function(fn)
1151  || (is_c_language(get_translation_unit(fn)->get_language())
1152  && fn->get_name() != fn->get_linkage_name()))
1153  {
1154  int number_of_aliases =
1155  fn->get_symbol()->get_number_of_aliases();
1156  if (number_of_aliases == 0)
1157  {
1158  out << indent << " "
1159  << "Please note that the exported symbol of "
1160  "this function is "
1161  << fn->get_symbol()->get_id_string()
1162  << "\n";
1163  }
1164  else
1165  {
1166  out << indent << " "
1167  << "Please note that the symbol of this function is "
1168  << fn->get_symbol()->get_id_string()
1169  << "\n and it aliases symbol";
1170  if (number_of_aliases > 1)
1171  out << "s";
1172  out << ": "
1173  << fn->get_symbol()->get_aliases_id_string(false)
1174  << "\n";
1175  }
1176  }
1177  fn_diff->report(out, indent + " ");
1178  // Extra spacing.
1179  out << "\n";
1180  }
1181 
1182  ctxt->show_redundant_changes(saved_show_redundant_changes);
1183 }
1184 
1185 /// Emit a report about a changed variable.
1186 ///
1187 /// @param ctxt the diff context to use for the report.
1188 ///
1189 /// @param fn_diff the var_diff node to emit the report for.
1190 ///
1191 /// @param out the output stream to emit the report to.
1192 ///
1193 /// @param indent the indentation string to use.
1194 ///
1195 /// @param indirect_changed_subtypes if true, this means there are
1196 /// indirect sub-types changes. Indirect means it's a pointed-to-type
1197 /// that changed.
1198 ///
1199 /// @param emit_redundant_var_changes if true, the function reports
1200 /// about changes carried by @fn_diff even if they are redundant.
1201 void
1203  const var_diff_sptr& var_diff,
1204  ostream& out, const string indent,
1205  bool emit_redundant_var_changes)
1206 {
1207  diff_sptr diff = var_diff;
1208  if (!diff)
1209  return;
1210 
1211  bool saved_show_redundant_changes = ctxt->show_redundant_changes();
1212  ctxt->show_redundant_changes(emit_redundant_var_changes);
1213 
1214  if (diff->to_be_reported())
1215  {
1216  string n1 = diff->first_subject()->get_pretty_representation();
1217  string n2 = diff->second_subject()->get_pretty_representation();
1218 
1219  out << indent << " [C] '" << n1 << "' was changed";
1220  if (n1 != n2)
1221  out << " to '" << n2 << "'";
1222  report_loc_info(diff->second_subject(), *ctxt, out);
1223  out << ":\n";
1224  diff->report(out, indent + " ");
1225  // Extra spacing.
1226  out << "\n";
1227  }
1228 
1229  ctxt->show_redundant_changes(saved_show_redundant_changes);
1230 }
1231 
1232 /// Report the differences in access specifiers and static-ness for
1233 /// class members.
1234 ///
1235 /// @param decl1 the first class member to consider.
1236 ///
1237 /// @param decl2 the second class member to consider.
1238 ///
1239 /// @param out the output stream to send the report to.
1240 ///
1241 /// @param indent the indentation string to use for the report.
1242 ///
1243 /// @return true if something was reported, false otherwise.
1244 bool
1245 maybe_report_diff_for_member(const decl_base_sptr& decl1,
1246  const decl_base_sptr& decl2,
1247  const diff_context_sptr& ctxt,
1248  ostream& out,
1249  const string& indent)
1250 
1251 {
1252  bool reported = false;
1253  if (!is_member_decl(decl1) || !is_member_decl(decl2))
1254  return reported;
1255 
1256  string decl1_repr = decl1->get_pretty_representation(),
1257  decl2_repr = decl2->get_pretty_representation();
1258 
1259  if (get_member_is_static(decl1) != get_member_is_static(decl2))
1260  {
1261  bool lost = get_member_is_static(decl1);
1262  out << indent << "'" << decl1_repr << "' ";
1263  if (report_loc_info(decl2, *ctxt, out))
1264  out << " ";
1265  if (lost)
1266  out << "became non-static";
1267  else
1268  out << "became static";
1269  out << "\n";
1270  reported = true;
1271  }
1272  if ((ctxt->get_allowed_category() & ACCESS_CHANGE_CATEGORY)
1273  && (get_member_access_specifier(decl1)
1274  != get_member_access_specifier(decl2)))
1275  {
1276  out << indent << "'" << decl1_repr << "' access changed from '"
1277  << get_member_access_specifier(decl1)
1278  << "' to '"
1279  << get_member_access_specifier(decl2)
1280  << "'\n";
1281  reported = true;
1282  }
1283  return reported;
1284 }
1285 
1286 /// Report the differences between two generic variables.
1287 ///
1288 /// @param decl1 the first version of the variable.
1289 ///
1290 /// @param decl2 the second version of the variable.
1291 ///
1292 /// @param ctxt the context of the diff.
1293 ///
1294 /// @param out the output stream to emit the change report to.
1295 ///
1296 /// @param indent the indentation prefix to emit.
1297 ///
1298 /// @return true if any text has been emitted to the output stream.
1299 bool
1300 maybe_report_diff_for_variable(const decl_base_sptr& decl1,
1301  const decl_base_sptr& decl2,
1302  const diff_context_sptr& ctxt,
1303  ostream& out,
1304  const string& indent)
1305 {
1306  bool reported = false;
1307 
1308  var_decl_sptr var1 = is_var_decl(decl1);
1309  var_decl_sptr var2 = is_var_decl(decl2);
1310 
1311  if (!var1 || !var2)
1312  return reported;
1313 
1315  {
1316  uint64_t var_size_in_bits = var1->get_symbol()->get_size() * 8;
1317 
1318  out << indent;
1319  show_offset_or_size("size of variable symbol (",
1320  var_size_in_bits, *ctxt, out);
1321  out << ") hasn't changed\n"
1322  << indent << "but it does have a harmless type change\n";
1323  reported = true;
1324  }
1325 
1326  return reported;
1327 }
1328 
1329 /// Report the difference between two ELF symbols, if there is any.
1330 ///
1331 /// @param symbol1 the first symbol to consider.
1332 ///
1333 /// @param symbol2 the second symbol to consider.
1334 ///
1335 /// @param ctxt the diff context.
1336 ///
1337 /// @param the output stream to emit the report to.
1338 ///
1339 /// @param indent the indentation string to use.
1340 void
1342  const elf_symbol_sptr& symbol2,
1343  const diff_context_sptr& ctxt,
1344  ostream& out,
1345  const string& indent)
1346 {
1347  if (!symbol1 || !symbol2 || symbol1 == symbol2)
1348  return;
1349 
1350  if (symbol1->get_size() != symbol2->get_size())
1351  {
1352  out << indent;
1353  show_numerical_change("size of symbol",
1354  symbol1->get_size(),
1355  symbol2->get_size(),
1356  *ctxt, out,
1357  /*show_bits_or_bytes=*/false);
1358  out << "\n";
1359  }
1360 
1361  if (symbol1->get_name() != symbol2->get_name())
1362  {
1363  out << indent << "symbol name changed from "
1364  << symbol1->get_name()
1365  << " to "
1366  << symbol2->get_name()
1367  << "\n";
1368  }
1369 
1370  if (symbol1->get_type() != symbol2->get_type())
1371  {
1372  out << indent << "symbol type changed from '"
1373  << symbol1->get_type()
1374  << "' to '"
1375  << symbol2->get_type()
1376  << "'\n";
1377  }
1378 
1379  if (symbol1->is_public() != symbol2->is_public())
1380  {
1381  out << indent << "symbol became ";
1382  if (symbol2->is_public())
1383  out << "exported";
1384  else
1385  out << "non-exported";
1386  out << "\n";
1387  }
1388 
1389  if (symbol1->is_defined() != symbol2->is_defined())
1390  {
1391  out << indent << "symbol became ";
1392  if (symbol2->is_defined())
1393  out << "defined";
1394  else
1395  out << "undefined";
1396  out << "\n";
1397  }
1398 
1399  if (symbol1->get_version() != symbol2->get_version())
1400  {
1401  out << indent << "symbol version changed from "
1402  << symbol1->get_version().str()
1403  << " to "
1404  << symbol2->get_version().str()
1405  << "\n";
1406  }
1407 
1408  const abg_compat::optional<uint32_t>& crc1 = symbol1->get_crc();
1409  const abg_compat::optional<uint32_t>& crc2 = symbol2->get_crc();
1410  if (crc1 != crc2)
1411  {
1412  const std::string none = "(none)";
1413  out << indent << "CRC (modversions) changed from "
1414  << std::showbase << std::hex;
1415  if (crc1.has_value())
1416  out << crc1.value();
1417  else
1418  out << none;
1419  out << " to ";
1420  if (crc2.has_value())
1421  out << crc2.value();
1422  else
1423  out << none;
1424  out << std::noshowbase << std::dec
1425  << "\n";
1426  }
1427 
1428  const abg_compat::optional<std::string>& ns1 = symbol1->get_namespace();
1429  const abg_compat::optional<std::string>& ns2 = symbol2->get_namespace();
1430  if (ns1 != ns2)
1431  {
1432  const std::string none = "(none)";
1433  out << indent << "namespace changed from ";
1434  if (ns1.has_value())
1435  out << "'" << ns1.value() << "'";
1436  else
1437  out << none;
1438  out << " to ";
1439  if (ns2.has_value())
1440  out << "'" << ns2.value() << "'";
1441  else
1442  out << none;
1443  out << "\n";
1444  }
1445 }
1446 
1447 /// For a given symbol, emit a string made of its name and version.
1448 /// The string also contains the list of symbols that alias this one.
1449 ///
1450 /// @param out the output string to emit the resulting string to.
1451 ///
1452 /// @param indent the indentation string to use before emitting the
1453 /// resulting string.
1454 ///
1455 /// @param symbol the symbol to emit the representation string for.
1456 ///
1457 /// @param sym_map the symbol map to consider to look for aliases of
1458 /// @p symbol.
1459 void
1461  const string& indent,
1462  const elf_symbol& symbol,
1463  const string_elf_symbols_map_type& sym_map)
1464 {
1465  out << indent << symbol.get_id_string();
1466  string aliases =
1467  symbol.get_aliases_id_string(sym_map,
1468  /*include_symbol_itself=*/false);
1469  if (!aliases.empty())
1470  out << ", aliases " << aliases;
1471 }
1472 
1473 /// Report changes about types that are not reachable from global
1474 /// functions and variables, in a given @param corpus_diff.
1475 ///
1476 /// @param d the corpus_diff to consider.
1477 ///
1478 /// @param s the statistics of the changes, after filters and
1479 /// suppressions are reported. This is typically what is returned by
1480 /// corpus_diff::apply_filters_and_suppressions_before_reporting().
1481 ///
1482 /// @param indent the indendation string (usually a string of white
1483 /// spaces) to use for indentation during the reporting.
1484 ///
1485 /// @param out the output stream to emit the report to.
1486 void
1488  const corpus_diff::diff_stats &s,
1489  const string& indent,
1490  ostream& out)
1491 {
1492  const diff_context_sptr& ctxt = d.context();
1493 
1494  if (!(ctxt->show_unreachable_types()
1495  && (!d.priv_->deleted_unreachable_types_.empty()
1496  || !d.priv_->added_unreachable_types_.empty()
1497  || !d.priv_->changed_unreachable_types_.empty())))
1498  // The user either doesn't want us to show changes about
1499  // unreachable types or there are not such changes.
1500  return;
1501 
1502  // Handle removed unreachable types.
1503  if (s.net_num_removed_unreachable_types() == 1)
1504  out << indent
1505  << "1 removed type unreachable from any public interface:\n\n";
1506  else if (s.net_num_removed_unreachable_types() > 1)
1507  out << indent
1509  << " removed types unreachable from any public interface:\n\n";
1510 
1511  vector<type_base_sptr> sorted_removed_unreachable_types;
1512  sort_string_type_base_sptr_map(d.priv_->deleted_unreachable_types_,
1513  sorted_removed_unreachable_types);
1514  bool emitted = false;
1515  for (vector<type_base_sptr>::const_iterator i =
1516  sorted_removed_unreachable_types.begin();
1517  i != sorted_removed_unreachable_types.end();
1518  ++i)
1519  {
1520  if (d.priv_->deleted_unreachable_type_is_suppressed((*i).get()))
1521  continue;
1522 
1523  out << indent << " [D] '" << get_pretty_representation(*i) << "'";
1524  report_loc_info(*i, *ctxt, out);
1525  out << "\n";
1526  emitted = true;
1527  }
1528  if (emitted)
1529  out << "\n";
1530 
1531  // Handle changed unreachable types!
1532  if (s.net_num_changed_unreachable_types() == 1)
1533  out << indent
1534  << "1 changed type unreachable from any public interface:\n\n";
1535  else if (s.net_num_changed_unreachable_types() > 1)
1536  out << indent
1538  << " changed types unreachable from any public interface:\n\n";
1539 
1540  diff_sptrs_type sorted_diff_sptrs;
1541  sort_string_diff_sptr_map(d.priv_->changed_unreachable_types_,
1542  sorted_diff_sptrs);
1543  for (diff_sptrs_type::const_iterator i = sorted_diff_sptrs.begin();
1544  i != sorted_diff_sptrs.end();
1545  ++i)
1546  {
1547  diff_sptr diff = *i;
1548  if (!diff || !diff->to_be_reported())
1549  continue;
1550 
1551  string repr = diff->first_subject()->get_pretty_representation();
1552 
1553  out << indent << " [C] '" << repr << "' changed:\n";
1554  diff->report(out, indent + " ");
1555  // Extra spacing.
1556  out << "\n";
1557  }
1558  // Changed types have extra spacing already. No new line here.
1559 
1560  // Handle added unreachable types.
1561  if (s.net_num_added_unreachable_types() == 1)
1562  out << indent
1563  << "1 added type unreachable from any public interface:\n\n";
1564  else if (s.net_num_added_unreachable_types() > 1)
1565  out << indent
1567  << " added types unreachable from any public interface:\n\n";
1568 
1569  vector<type_base_sptr> sorted_added_unreachable_types;
1570  sort_string_type_base_sptr_map(d.priv_->added_unreachable_types_,
1571  sorted_added_unreachable_types);
1572  emitted = false;
1573  for (vector<type_base_sptr>::const_iterator i =
1574  sorted_added_unreachable_types.begin();
1575  i != sorted_added_unreachable_types.end();
1576  ++i)
1577  {
1578  if (d.priv_->added_unreachable_type_is_suppressed((*i).get()))
1579  continue;
1580 
1581  out << indent << " [A] '" << get_pretty_representation(*i) << "'";
1582  report_loc_info(*i, *ctxt, out);
1583  out << "\n";
1584  emitted = true;
1585  }
1586  if (emitted)
1587  out << "\n";
1588 }
1589 
1590 /// If a given diff node impacts some public interfaces, then report
1591 /// about those impacted interfaces on a given output stream.
1592 ///
1593 /// @param d the diff node to get the impacted interfaces for.
1594 ///
1595 /// @param out the output stream to report to.
1596 ///
1597 /// @param indent the white space string to use for indentation.
1598 void
1600  ostream &out,
1601  const string &indent)
1602 {
1603  const diff_context_sptr &ctxt = d->context();
1604  const corpus_diff_sptr &corp_diff = ctxt->get_corpus_diff();
1605  if (!corp_diff)
1606  return;
1607 
1608  if (!ctxt->show_impacted_interfaces())
1609  return;
1610 
1611  const diff_maps &maps = corp_diff->get_leaf_diffs();
1612  artifact_sptr_set_type* impacted_artifacts =
1614  if (impacted_artifacts == 0)
1615  return;
1616 
1617  if (impacted_artifacts->empty())
1618  return;
1619 
1620  vector<type_or_decl_base_sptr> sorted_impacted_interfaces;
1621  sort_artifacts_set(*impacted_artifacts, sorted_impacted_interfaces);
1622 
1623  size_t num_impacted_interfaces = impacted_artifacts->size();
1624  if (num_impacted_interfaces == 1)
1625  out << indent << "one impacted interface:\n";
1626  else
1627  out << indent << num_impacted_interfaces << " impacted interfaces:\n";
1628 
1629  string cur_indent = indent + " ";
1630  vector<type_or_decl_base_sptr>::const_iterator it;
1631  for (it = sorted_impacted_interfaces.begin();
1632  it != sorted_impacted_interfaces.end();
1633  ++it)
1634  {
1635  out << cur_indent << get_pretty_representation(*it) << "\n";
1636  }
1637 }
1638 
1639 /// If a given diff node impacts some public interfaces, then report
1640 /// about those impacted interfaces on standard output.
1641 ///
1642 /// @param d the diff node to get the impacted interfaces for.
1643 ///
1644 /// @param out the output stream to report to.
1645 ///
1646 /// @param indent the white space string to use for indentation.
1647 void
1649  ostream &out,
1650  const string &indent)
1651 {return maybe_report_interfaces_impacted_by_diff(d.get(), out, indent);}
1652 
1653 /// Tests if the diff node is to be reported.
1654 ///
1655 /// @param p the diff to consider.
1656 ///
1657 /// @return true iff the diff is to be reported.
1658 bool
1660 {return d && d->to_be_reported();}
1661 
1662 /// Report about data members replaced by an anonymous data member
1663 /// without changing the overall bit-layout of the class or union in
1664 /// an ABI-meaningful way.
1665 ///
1666 /// @param d the diff to consider.
1667 ///
1668 /// @param out the output stream to emit the change report to.
1669 ///
1670 /// @param indent the indentation string to use.
1671 void
1673  ostream &out,
1674  const string &indent)
1675 {
1676  const diff_context_sptr& ctxt = d.context();
1677 
1678  if ((ctxt->get_allowed_category() & HARMLESS_DATA_MEMBER_CHANGE_CATEGORY)
1679  && !d.data_members_replaced_by_adms().empty())
1680  {
1681  // Let's detect all the data members that are replaced by
1682  // members of the same anonymous data member and report them
1683  // in one go.
1684  for (changed_var_sptrs_type::const_iterator i =
1686  i != d.ordered_data_members_replaced_by_adms().end();)
1687  {
1688  // This contains the data members replaced by the same
1689  // anonymous data member.
1690  vector<var_decl_sptr> dms_replaced_by_same_anon_dm;
1691  dms_replaced_by_same_anon_dm.push_back(i->first);
1692  // This contains the anonymous data member that replaced the
1693  // data members in the variable above.
1694  var_decl_sptr anonymous_data_member = i->second;
1695  // Let's look forward to see if the subsequent data
1696  // members were replaced by members of
1697  // anonymous_data_member.
1698  for (++i;
1700  && *i->second == *anonymous_data_member;
1701  ++i)
1702  dms_replaced_by_same_anon_dm.push_back(i->first);
1703 
1704  bool several_data_members_replaced =
1705  dms_replaced_by_same_anon_dm.size() > 1;
1706 
1707  out << indent << "data member";
1708  if (several_data_members_replaced)
1709  out << "s";
1710 
1711  bool first_data_member = true;
1712  for (vector<var_decl_sptr>::const_iterator it =
1713  dms_replaced_by_same_anon_dm.begin();
1714  it != dms_replaced_by_same_anon_dm.end();
1715  ++it)
1716  {
1717  string name = (*it)->get_qualified_name();
1718  if (!first_data_member)
1719  out << ",";
1720  out << " '" << name << "'";
1721  first_data_member = false;
1722  }
1723 
1724  if (several_data_members_replaced)
1725  out << " were ";
1726  else
1727  out << " was ";
1728 
1729  out << "replaced by anonymous data member:\n"
1730  << indent + " "
1731  << "'"
1732  << anonymous_data_member->get_pretty_representation()
1733  << "'\n";
1734  }
1735  }
1736 }
1737 
1738 /// Report about the base classes of a class having been re-ordered.
1739 ///
1740 /// @param d the class diff to consider.
1741 ///
1742 /// @param out the output stream to report the change to.
1743 ///
1744 /// @param indent the indentation string to use.
1745 void
1747  ostream &out,
1748  const string &indent)
1749 {
1750  if (d.moved_bases().empty())
1751  return;
1752 
1753  class_decl_sptr first = d.first_class_decl(),
1754  second = d.second_class_decl();
1755 
1756  ABG_ASSERT(!first->get_base_specifiers().empty());
1757  ABG_ASSERT(!second->get_base_specifiers().empty());
1758 
1759  out << indent << "base classes of '"
1760  << first->get_pretty_representation()
1761  << "' are re-ordered from: ";
1762 
1763  vector<class_decl_sptr> classes = {first, second};
1764  unsigned nb_classes_seen = 0;
1765  for (auto &klass : classes)
1766  {
1767  if (nb_classes_seen >= 1)
1768  out << " to: ";
1769  out << "'";
1770  bool needs_comma = false;
1771  for (auto &b : klass->get_base_specifiers())
1772  {
1773  if (needs_comma)
1774  out << ", ";
1775  if (b->get_is_virtual())
1776  out << "virtual ";
1777  out << b->get_base_class()->get_qualified_name();
1778  needs_comma = true;
1779  }
1780  out << "'";
1781  nb_classes_seen++;
1782  }
1783  if (nb_classes_seen)
1784  out << "\n";
1785 }
1786 } // Namespace comparison
1787 } // 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:10807
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:6641
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_c_language(translation_unit::language l)
Test if a language enumerator designates the C language.
Definition: abg-ir.cc:1792
bool is_data_member(const var_decl &v)
Test if a var_decl is a data member.
Definition: abg-ir.cc:5613
bool is_class_type(const type_or_decl_base &t)
Test whether a type is a class.
Definition: abg-ir.cc:11165
bool is_member_function(const function_decl &f)
Test whether a function_decl is a member function.
Definition: abg-ir.cc:6368
An abstraction helper for type declarations.
Definition: abg-ir.h:2002
This means the diff node (or at least one of its descendant nodes) carries a change that modifies the...
translation_unit * get_translation_unit(const type_or_decl_base &t)
Return the translation unit a declaration belongs to.
Definition: abg-ir.cc:10503
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:5515
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:694
shared_ptr< diff_context > diff_context_sptr
Convenience typedef for a shared pointer of diff_context.
Definition: abg-fwd.h:67
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:6578
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:253
bool is_member_decl(const decl_base_sptr d)
Tests if a declaration is a class member.
Definition: abg-ir.cc:5417
Abstraction of the declaration of a method.
Definition: abg-ir.h:3886
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:11396
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:1360
void emit_changed_var_report(const diff_context_sptr &ctxt, const var_diff_sptr &var_diff, ostream &out, const string indent, bool emit_redundant_var_changes)
Emit a report about a changed variable.
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 get_member_function_is_dtor(const function_decl &f)
Test whether a member function is a destructor.
Definition: abg-ir.cc:6454
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:12059
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:2686
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:5990
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:452
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:6184
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:559
shared_ptr< function_decl > function_decl_sptr
Convenience typedef for a shared pointer on a function_decl.
Definition: abg-fwd.h:266
decl_base * is_decl(const type_or_decl_base *d)
Test if an ABI artifact is a declaration.
Definition: abg-ir.cc:10747
std::unordered_map< string, elf_symbols > string_elf_symbols_map_type
Convenience typedef for a map which key is a string and which value is a vector of elf_symbol...
Definition: abg-ir.h:947
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.
void emit_changed_fn_report(const diff_context_sptr &ctxt, const function_decl_diff_sptr &fn_diff, ostream &out, const string indent, bool indirect_changed_subtypes, bool emit_redundant_fn_changes)
Emit a report about a changed function.
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...
shared_ptr< function_decl_diff > function_decl_diff_sptr
Convenience typedef for a shared pointer to a function_decl type.
The source location of a token.
Definition: abg-ir.h:306
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:118
Abstraction of an elf symbol.
Definition: abg-ir.h:960
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:924
#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:1743
bool get_data_member_is_laid_out(const var_decl &m)
Test whether a data member is laid out.
Definition: abg-ir.cc:6344
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:11445
array_type_def * is_array_type(const type_or_decl_base *type, bool look_through_qualifiers)
Test if a type is an array_type_def.
Definition: abg-ir.cc:12123
This type contains maps. Each map associates a type name to a diff of that type. Not all kinds of dif...
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...
bool get_member_function_is_ctor(const function_decl &f)
Test whether a member function is a constructor.
Definition: abg-ir.cc:6395
shared_ptr< class_decl > class_decl_sptr
Convenience typedef for a shared pointer on a class_decl.
Definition: abg-fwd.h:190
interned_string get_type_name(const type_base_sptr &t, bool qualified, bool internal)
Get the name of a given type and return a copy of it.
Definition: abg-ir.cc:8835
uint64_t get_var_size_in_bits(const var_decl_sptr &v)
Get the size of a given variable.
Definition: abg-ir.cc:6316
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...
Abstracts a diff between two instances of var_decl.
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:5575
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:5872
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:2616
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:75
bool has_harmless_name_change(const decl_base_sptr &f, const decl_base_sptr &s, const diff_context_sptr &ctxt)
Test if two decls represents a harmless name change.
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:2562
shared_ptr< array_type_def > array_type_def_sptr
Convenience typedef for a shared pointer on a array_type_def.
Definition: abg-fwd.h:241
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.