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);
607  emitted = true;
608  }
609 
610  if (!size_reported && o_size != n_size)
611  {
612  if (begin_with_and)
613  {
614  out << indent << "and ";
615  begin_with_and = false;
616  }
617  else if (!emitted)
618  {
619  out << indent;
620  if (is_strict_anonymous_data_member_change)
621  out << "anonymous data member ";
622  out << "'" << o_pretty_representation << "' ";
623  }
624  else
625  out << ", ";
626 
627  show_numerical_change("size", o_size, n_size, *ctxt, out);
629  emitted = true;
630  }
631  }
632  if (o->get_binding() != n->get_binding())
633  {
634  if (begin_with_and)
635  {
636  out << indent << "and ";
637  begin_with_and = false;
638  }
639  else if (!emitted)
640  out << indent << "'" << o_pretty_representation << "' ";
641  else
642  out << ", ";
643  out << "elf binding changed from " << o->get_binding()
644  << " to " << n->get_binding();
645  emitted = true;
646  }
647  if (o->get_visibility() != n->get_visibility())
648  {
649  if (begin_with_and)
650  {
651  out << indent << "and ";
652  begin_with_and = false;
653  }
654  else if (!emitted)
655  out << indent << "'" << o_pretty_representation << "' ";
656  else
657  out << ", ";
658  out << "visibility changed from " << o->get_visibility()
659  << " to " << n->get_visibility();
660  emitted = true;
661  }
662  if ((ctxt->get_allowed_category() & ACCESS_CHANGE_CATEGORY)
665  {
666  if (begin_with_and)
667  {
668  out << indent << "and ";
669  begin_with_and = false;
670  }
671  else if (!emitted)
672  out << indent << "'" << o_pretty_representation << "' ";
673  else
674  out << ", ";
675 
676  out << "access changed from '"
678  << "' to '"
679  << get_member_access_specifier(n) << "'";
680  emitted = true;
681  }
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_non_finite())
841  out << "\'unknown\'";
842  else
843  emit_num_value(first_array->get_size_in_bits(), *ctxt, out);
844  out << " to ";
845  if (second_array->is_non_finite())
846  out << "\'unknown\'";
847  else
848  emit_num_value(second_array->get_size_in_bits(), *ctxt, out);
849  out << "\n";
850 
851  if (sdc != fdc)
852  {
853  out << indent + " "
854  << "number of dimensions changed from "
855  << fdc
856  << " to "
857  << sdc
858  << "\n";
859  }
860  array_type_def::subranges_type::const_iterator i, j;
861  for (i = first_array->get_subranges().begin(),
862  j = second_array->get_subranges().begin();
863  (i != first_array->get_subranges().end()
864  && j != second_array->get_subranges().end());
865  ++i, ++j)
866  {
867  if ((*i)->get_length() != (*j)->get_length())
868  {
869  out << indent
870  << "array type subrange "
871  << i - first_array->get_subranges().begin() + 1
872  << " changed length from ";
873 
874  if ((*i)->is_non_finite())
875  out << "\'unknown\'";
876  else
877  out << (*i)->get_length();
878 
879  out << " to ";
880 
881  if ((*j)->is_non_finite())
882  out << "\'unknown\'";
883  else
884  out << (*j)->get_length();
885  out << "\n";
886  }
887  }
888  } // end if (first_array && second_array)
889  else if (fs != ss)
890  {
891  out << indent;
892  show_numerical_change("type size", fs, ss, *ctxt, out);
893  out << "\n";
894  }
895  } // end if (fs != ss || fdc != sdc)
896  else
897  if (ctxt->show_relative_offset_changes())
898  {
899  out << indent << "type size hasn't changed\n";
900  }
901  }
902  if ((ctxt->get_allowed_category() & SIZE_OR_OFFSET_CHANGE_CATEGORY)
903  && (fa != sa))
904  {
905  out << indent;
906  show_numerical_change("type alignment", fa, sa, *ctxt, out,
907  /*show_bits_or_bytes=*/false);
908  out << "\n";
909  }
910 }
911 
912 /// @param tod the type or declaration to emit loc info about
913 ///
914 /// @param ctxt the content of the current diff.
915 ///
916 /// @param out the output stream to report the change to.
917 ///
918 /// @return true iff something was reported.
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
The private data and functions of the abigail::ir::comparison types.
#define ABG_ASSERT(cond)
This is a wrapper around the 'assert' glibc call. It allows for its argument to have side effects,...
Definition: abg-fwd.h:1714
This type abstracts changes for a class_decl.
class_decl_sptr first_class_decl() const
const vector< class_decl::base_spec_sptr > & moved_bases() const
Getter for the vector of bases that "moved". That is, the vector of base types which position changed...
class_decl_sptr second_class_decl() const
Getter of the second class involved in the diff.
This is the base class of class_diff and union_diff.
const string_decl_base_sptr_map & data_members_replaced_by_adms() const
Get the map of data members that got replaced by anonymous data members.
const changed_var_sptrs_type & ordered_data_members_replaced_by_adms() const
Get an ordered vector of of data members that got replaced by anonymous data members.
This is a document class that aims to capture statistics about the changes carried by a corpus_diff t...
size_t net_num_added_unreachable_types() const
Getter of the number of added types that are unreachable from public interfaces and that are *NOT* fi...
size_t net_num_removed_unreachable_types() const
Getter of the number of removed types that are not reachable from public interfaces and that have *NO...
size_t net_num_changed_unreachable_types() const
Getter of the number of changed types that are unreachable from public interfaces and that have *NOT*...
An abstraction of a diff between between two abi corpus.
const diff_context_sptr context() const
Getter of the diff context of this diff.
The context of the diff. This type holds various bits of information that is going to be used through...
bool show_offsets_sizes_in_bits() const
Get the flag that indicates if diff reports using this context should show sizes and offsets in bits,...
void show_relative_offset_changes(bool f)
Set a flag saying if offset changes should be reported in a relative way. That is,...
bool show_hex_values() const
Get the flag that indicates if the diff reports using this context should show sizes and offsets in a...
This type contains maps. Each map associates a type name to a diff of that type. Not all kinds of dif...
artifact_sptr_set_type * lookup_impacted_interfaces(const diff *d) const
Lookup the interfaces that are impacted by a given leaf diff node.
The abstraction of a change between two ABI artifacts, a.k.a an artifact change.
type_or_decl_base_sptr first_subject() const
Getter of the first subject of the diff.
virtual void report(ostream &out, const string &indent="") const =0
Pure interface to report the diff in a serialized form that is legible for the user.
const diff_context_sptr context() const
Getter of the context of the current diff.
bool to_be_reported() const
Test if this diff tree node should be reported.
virtual bool diff_to_be_reported(const diff *d) const
Tests if the diff node is to be reported.
The abstraction of the diff between two subrange types.
const array_type_def::subrange_sptr second_subrange() const
Getter of the second subrange of the current instance subrange_diff.
const array_type_def::subrange_sptr first_subrange() const
Getter of the first subrange of the current instance subrange_diff.
const diff_sptr underlying_type_diff() const
Getter of the diff node of the underlying types of the current subrange_diff diff node.
shared_ptr< subrange_type > subrange_sptr
Convenience typedef for a shared pointer on a function_decl::subrange.
Definition: abg-ir.h:2533
Abstraction of an elf symbol.
Definition: abg-ir.h:923
string get_aliases_id_string(const string_elf_symbols_map_type &symtab, bool include_symbol_itself=true) const
Return a comma separated list of the id of the current symbol as well as the id string of its aliases...
Definition: abg-ir.cc:2598
const string & get_id_string() const
Get a string that is representative of a given elf_symbol.
Definition: abg-ir.cc:2528
The source location of a token.
Definition: abg-ir.h:299
void expand(std::string &path, unsigned &line, unsigned &column) const
Expand the current location into a tripplet file path, line and column number.
Definition: abg-ir.cc:463
This is the abstraction of the set of relevant artefacts (types, variable declarations,...
Definition: abg-ir.h:686
bool has_class_decl_only_def_change(const class_or_union_sptr &first, const class_or_union_sptr &second)
Test if two class_or_union_sptr are different just by the fact that one is decl-only and the other on...
bool has_harmless_name_change(const decl_base_sptr &f, const decl_base_sptr &s)
Test if two decls represents a harmless name change.
bool is_var_1_dim_unknown_size_array_change(const var_decl_sptr &var1, const var_decl_sptr &var2)
Test if we are looking at two variables which types are both one dimension array, with one of them be...
bool has_anonymous_data_member_change(const diff *d)
Test if a diff node carries a non-anonymous data member to anonymous data member change,...
shared_ptr< diff > diff_sptr
Convenience typedef for a shared_ptr for the diff class.
Definition: abg-fwd.h:76
void show_linkage_name_and_aliases(ostream &out, const string &indent, const elf_symbol &symbol, const string_elf_symbols_map_type &sym_map)
For a given symbol, emit a string made of its name and version. The string also contains the list of ...
void maybe_report_interfaces_impacted_by_diff(const diff *d, ostream &out, const string &indent)
If a given diff node impacts some public interfaces, then report about those impacted interfaces on a...
@ ACCESS_CHANGE_CATEGORY
This means the diff node (or at least one of its descendant nodes) carries access related changes,...
@ HARMLESS_DATA_MEMBER_CHANGE_CATEGORY
This means that a diff node in the sub-tree carries a harmless data member change....
@ SIZE_OR_OFFSET_CHANGE_CATEGORY
This means the diff node (or at least one of its descendant nodes) carries a change that modifies the...
@ HARMLESS_DECL_NAME_CHANGE_CATEGORY
This means that a diff node in the sub-tree carries a harmless declaration name change....
void maybe_report_diff_for_symbol(const elf_symbol_sptr &symbol1, const elf_symbol_sptr &symbol2, const diff_context_sptr &ctxt, ostream &out, const string &indent)
Report the difference between two ELF symbols, if there is any.
void maybe_show_relative_offset_change(const var_diff_sptr &diff, diff_context &ctxt, ostream &out)
If a given var_diff node carries a data member change in which the offset of the data member actually...
uint64_t convert_bits_to_bytes(size_t bits)
Convert a number in bits into a number in bytes.
vector< diff_sptr > diff_sptrs_type
Convenience typedef for a vector of diff_sptr.
void maybe_report_unreachable_type_changes(const corpus_diff &d, const corpus_diff::diff_stats &s, const string &indent, ostream &out)
Report changes about types that are not reachable from global functions and variables,...
shared_ptr< diff_context > diff_context_sptr
Convenience typedef for a shared pointer of diff_context.
Definition: abg-fwd.h:68
void report_size_and_alignment_changes(type_or_decl_base_sptr first, type_or_decl_base_sptr second, diff_context_sptr ctxt, ostream &out, const string &indent)
Report the size and alignment changes of a type.
bool report_loc_info(const type_or_decl_base_sptr &tod, const diff_context &ctxt, ostream &out)
shared_ptr< var_diff > var_diff_sptr
Convenience typedef for a shared pointer to a var_diff type.
void show_numerical_change(const string &what, uint64_t old_bits, uint64_t new_bits, const diff_context &ctxt, ostream &out, bool show_bits_or_byte)
Emit a message showing the numerical change between two values, to a given output stream.
bool maybe_report_diff_for_variable(const decl_base_sptr &decl1, const decl_base_sptr &decl2, const diff_context_sptr &ctxt, ostream &out, const string &indent)
Report the differences between two generic variables.
void maybe_report_base_class_reordering(const class_diff &d, ostream &out, const string &indent)
Report about the base classes of a class having been re-ordered.
void represent_data_member(var_decl_sptr d, const diff_context_sptr &ctxt, ostream &out, const string &indent)
Stream a string representation for a data member.
void maybe_show_relative_size_change(const var_diff_sptr &diff, diff_context &ctxt, ostream &out)
If a given var_diff node carries a hange in which the size of the variable actually changed,...
string get_pretty_representation(diff *d)
Get a copy of the pretty representation of a diff node.
diff_kind
Represent the kind of difference we want report_mem_header() to report.
void sort_artifacts_set(const artifact_sptr_set_type &set, vector< type_or_decl_base_sptr > &sorted)
Sort the set of ABI artifacts contained in a artifact_sptr_set_type.
void maybe_report_data_members_replaced_by_anon_dm(const class_or_union_diff &d, ostream &out, const string &indent)
Report about data members replaced by an anonymous data member without changing the overall bit-layou...
bool maybe_report_diff_for_member(const decl_base_sptr &decl1, const decl_base_sptr &decl2, const diff_context_sptr &ctxt, ostream &out, const string &indent)
Report the differences in access specifiers and static-ness for class members.
void report_mem_header(ostream &out, size_t number, size_t num_filtered, diff_kind k, const string &section_name, const string &indent)
Output the header preceding the the report for insertion/deletion/change of a part of a class....
void sort_string_type_base_sptr_map(string_type_base_sptr_map &map, vector< type_base_sptr > &sorted)
Sort a map of string to type_base_sptr entities.
void show_offset_or_size(const string &what, uint64_t value, const diff_context &ctxt, ostream &out)
Emit a message showing the value of a numerical value representing a size or an offset,...
void emit_num_value(uint64_t value, const diff_context &ctxt, ostream &out)
Emit a numerical value to an output stream.
void report_name_size_and_alignment_changes(decl_base_sptr first, decl_base_sptr second, diff_context_sptr ctxt, ostream &out, const string &indent)
Report the name, size and alignment changes of a type.
void sort_string_diff_sptr_map(const string_diff_sptr_map &map, diff_sptrs_type &sorted)
Sort a map ofg string -> diff_sptr into a vector of diff_sptr. The diff_sptr are sorted lexicographic...
shared_ptr< corpus_diff > corpus_diff_sptr
A convenience typedef for a shared pointer to corpus_diff.
void represent(const diff_context &ctxt, method_decl_sptr mem_fn, ostream &out)
Stream a string representation for a member function.
uint64_t maybe_convert_bits_to_bytes(uint64_t bits, const diff_context &ctxt)
Convert a bits value into a byte value if the current diff context instructs us to do so.
bool get_member_is_static(const decl_base &d)
Gets a flag saying if a class member is static or not.
Definition: abg-ir.cc:5816
ssize_t get_member_function_vtable_offset(const function_decl &f)
Get the vtable offset of a member function.
Definition: abg-ir.cc:6819
unordered_set< type_or_decl_base_sptr, type_or_decl_hash, type_or_decl_equal > artifact_sptr_set_type
A convenience typedef for a hash set of type_or_decl_base_sptr.
Definition: abg-ir.h:550
bool is_type(const type_or_decl_base &tod)
Test whether a declaration is a type.
Definition: abg-ir.cc:10665
bool is_anonymous_data_member(const decl_base &d)
Test if a decl is an anonymous data member.
Definition: abg-ir.cc:6113
shared_ptr< elf_symbol > elf_symbol_sptr
A convenience typedef for a shared pointer to elf_symbol.
Definition: abg-ir.h:886
change_kind
A bitfield that gives callers of abigail::ir::equals() some insight about how different two internal ...
Definition: abg-ir.h:1323
bool is_class_type(const type_or_decl_base &t)
Test whether a type is a class.
Definition: abg-ir.cc:10947
shared_ptr< array_type_def > array_type_def_sptr
Convenience typedef for a shared pointer on a array_type_def.
Definition: abg-fwd.h:242
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:11178
shared_ptr< class_decl > class_decl_sptr
Convenience typedef for a shared pointer on a class_decl.
Definition: abg-fwd.h:191
uint64_t get_var_size_in_bits(const var_decl_sptr &v)
Get the size of a given variable.
Definition: abg-ir.cc:6557
shared_ptr< var_decl > var_decl_sptr
Convenience typedef for a shared pointer on a var_decl.
Definition: abg-fwd.h:254
shared_ptr< type_or_decl_base > type_or_decl_base_sptr
A convenience typedef for a shared_ptr to type_or_decl_base.
Definition: abg-fwd.h:119
bool 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:6231
bool get_data_member_is_laid_out(const var_decl &m)
Test whether a data member is laid out.
Definition: abg-ir.cc:6585
bool is_member_function(const function_decl &f)
Test whether a function_decl is a member function.
Definition: abg-ir.cc:6609
var_decl * is_var_decl(const type_or_decl_base *tod)
Tests if a declaration is a variable declaration.
Definition: abg-ir.cc:11841
decl_base * is_decl(const type_or_decl_base *d)
Test if an ABI artifact is a declaration.
Definition: abg-ir.cc:10605
access_specifier get_member_access_specifier(const decl_base &d)
Gets the access specifier for a class member.
Definition: abg-ir.cc:5756
uint64_t get_data_member_offset(const var_decl &m)
Get the offset of a data member.
Definition: abg-ir.cc:6425
bool get_member_function_is_virtual(const function_decl &f)
Test if a given member function is virtual.
Definition: abg-ir.cc:6882
bool is_union_type(const type_or_decl_base &t)
Test if a type is a union_decl.
Definition: abg-ir.cc:11227
bool is_data_member(const var_decl &v)
Test if a var_decl is a data member.
Definition: abg-ir.cc:5854
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:11905
translation_unit * get_translation_unit(const decl_base &decl)
Return the translation unit a declaration belongs to.
Definition: abg-ir.cc:10381
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:9009
std::unordered_map< string, elf_symbols > string_elf_symbols_map_type
Convenience typedef for a map which key is a string and which value is a vector of elf_symbol.
Definition: abg-ir.h:909
bool is_member_decl(const decl_base_sptr d)
Tests if a declaration is a class member.
Definition: abg-ir.cc:5658
Toplevel namespace for libabigail.