libabigail
abg-default-reporter.cc
Go to the documentation of this file.
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 
9 /// @file
10 ///
11 /// This is the implementation of the
12 /// abigail::comparison::default_reporter type.
13 
14 #include "abg-comparison-priv.h"
15 #include "abg-reporter.h"
16 #include "abg-reporter-priv.h"
17 
18 namespace abigail
19 {
20 namespace comparison
21 {
22 
23 /// Test if a given instance of @ref corpus_diff carries changes whose
24 /// reports are not suppressed by any suppression specification. In
25 /// effect, these are deemed incompatible ABI changes.
26 ///
27 /// @param d the @ref corpus_diff to consider
28 ///
29 /// @return true iff @p d carries subtype changes that are deemed
30 /// incompatible ABI changes.
31 bool
33 {
34  if (!d)
35  return false;
36 
37  const corpus_diff::diff_stats& stats = const_cast<corpus_diff*>(d)->
38  apply_filters_and_suppressions_before_reporting();
39 
40  // Logic here should match emit_diff_stats.
41  return (d->architecture_changed()
42  || d->soname_changed()
43  || stats.net_num_func_removed()
44  || stats.net_num_func_changed()
45  || stats.net_num_func_added()
46  || stats.net_num_vars_removed()
47  || stats.net_num_vars_changed()
48  || stats.net_num_vars_added()
52  || stats.net_num_removed_func_syms()
53  || stats.net_num_added_func_syms()
54  || stats.net_num_removed_var_syms()
55  || stats.net_num_added_var_syms());
56 }
57 
58 /// Ouputs a report of the differences between of the two type_decl
59 /// involved in the @ref type_decl_diff.
60 ///
61 /// @param d the @ref type_decl_diff to consider.
62 ///
63 /// @param out the output stream to emit the report to.
64 ///
65 /// @param indent the string to use for indentatino indent.
66 void
67 default_reporter::report(const type_decl_diff& d,
68  ostream& out,
69  const string& indent) const
70 {
71  if (!d.to_be_reported())
72  return;
73 
75 
76  string name = f->get_pretty_representation();
77 
79  out, indent);
80 
81  if (f->get_visibility() != s->get_visibility())
82  {
83  out << indent
84  << "visibility changed from '"
85  << f->get_visibility() << "' to '" << s->get_visibility()
86  << "\n";
87  }
88 
89  if (f->get_linkage_name() != s->get_linkage_name())
90  {
91  out << indent
92  << "mangled name changed from '"
93  << f->get_linkage_name() << "' to "
94  << s->get_linkage_name()
95  << "\n";
96  }
97 }
98 
99 /// Report the differences between the two enums.
100 ///
101 /// @param d the enum diff to consider.
102 ///
103 /// @param out the output stream to send the report to.
104 ///
105 /// @param indent the string to use for indentation.
106 void
107 default_reporter::report(const enum_diff& d, ostream& out,
108  const string& indent) const
109 {
110  if (!d.to_be_reported())
111  return;
112 
113  RETURN_IF_BEING_REPORTED_OR_WAS_REPORTED_EARLIER3(d.first_subject(),
114  d.second_subject(),
115  "enum type");
116 
117  string name = d.first_enum()->get_pretty_representation();
118 
119  enum_type_decl_sptr first = d.first_enum(), second = d.second_enum();
120 
121  const diff_context_sptr& ctxt = d.context();
122 
123  // Report enum decl-only <-> definition changes.
124  if (ctxt->get_allowed_category() & TYPE_DECL_ONLY_DEF_CHANGE_CATEGORY)
125  if (filtering::has_enum_decl_only_def_change(first, second))
126  {
127  string was =
128  first->get_is_declaration_only()
129  ? " was a declaration-only enum type"
130  : " was a defined enum type";
131 
132  string is_now =
133  second->get_is_declaration_only()
134  ? " and is now a declaration-only enum type"
135  : " and is now a defined enum type";
136 
137  out << indent << "enum type " << name << was << is_now << "\n";
138  return;
139  }
140 
141  report_name_size_and_alignment_changes(first, second, ctxt,
142  out, indent);
143  maybe_report_diff_for_member(first, second, ctxt, out, indent);
144 
145  //underlying type
146  d.underlying_type_diff()->report(out, indent);
147 
148  //report deletions/insertions/change of enumerators
149  unsigned numdels = d.deleted_enumerators().size();
150  unsigned numins = d.inserted_enumerators().size();
151  unsigned numchanges = d.changed_enumerators().size();
152 
153  if (numdels)
154  {
155  report_mem_header(out, numdels, 0, del_kind, "enumerator", indent);
156  enum_type_decl::enumerators sorted_deleted_enumerators;
157  sort_enumerators(d.deleted_enumerators(), sorted_deleted_enumerators);
158  for (enum_type_decl::enumerators::const_iterator i =
159  sorted_deleted_enumerators.begin();
160  i != sorted_deleted_enumerators.end();
161  ++i)
162  {
163  out << indent
164  << " '"
165  << (first->get_is_anonymous()
166  ? i->get_name()
167  : i->get_qualified_name())
168  << "' value '"
169  << i->get_value()
170  << "'";
171  out << "\n";
172  }
173  }
174  if (numins)
175  {
176  report_mem_header(out, numins, 0, ins_kind, "enumerator", indent);
177  enum_type_decl::enumerators sorted_inserted_enumerators;
178  sort_enumerators(d.inserted_enumerators(), sorted_inserted_enumerators);
179  for (enum_type_decl::enumerators::const_iterator i =
180  sorted_inserted_enumerators.begin();
181  i != sorted_inserted_enumerators.end();
182  ++i)
183  {
184  out << indent
185  << " '"
186  << (second->get_is_anonymous()
187  ? i->get_name()
188  :i->get_qualified_name())
189  << "' value '"
190  << i->get_value()
191  << "'";
192  out << "\n";
193  }
194  }
195  if (numchanges)
196  {
197  report_mem_header(out, numchanges, 0, change_kind, "enumerator", indent);
198  changed_enumerators_type sorted_changed_enumerators;
199  sort_changed_enumerators(d.changed_enumerators(),
200  sorted_changed_enumerators);
201  for (changed_enumerators_type::const_iterator i =
202  sorted_changed_enumerators.begin();
203  i != sorted_changed_enumerators.end();
204  ++i)
205  {
206  out << indent;
207  if (i->first.get_value() != i->second.get_value())
208  {
209  out << " '"
210  << (first->get_is_anonymous()
211  ? i->first.get_name()
212  : i->first.get_qualified_name())
213  << "' from value '"
214  << i->first.get_value() << "' to '"
215  << i->second.get_value() << "'";
216  }
217  else if (i->first.get_name() != i->second.get_name())
218  {
219  out << "from '"
220  << (first->get_is_anonymous()
221  ? i->first.get_name()
222  : i->first.get_qualified_name())
223  << " = " << i->first.get_value()
224  << "' to '"
225  << (second->get_is_anonymous()
226  ? i->second.get_name()
227  : i->second.get_qualified_name())
228  << " = " << i->second.get_value()
229  << "'";
230  }
231  else
232  {
233  out << "enumerator change from '"
234  << i->first.get_name()
235  << " = "
236  << i->first.get_value()
237  << "' to '"
238  << i->second.get_name()
239  << " = "
240  << i->second.get_value()
241  << "' could not be determined - please report as a bug";
242  }
243  report_loc_info(second, *ctxt, out);
244  out << "\n";
245  }
246  }
247 
248  if (ctxt->show_leaf_changes_only())
250 
251  d.reported_once(true);
252 }
253 
254 /// For a @ref typedef_diff node, report the local changes to the
255 /// typedef rather the changes to its underlying type.
256 ///
257 /// Note that changes to the underlying type are also considered
258 /// local.
259 ///
260 /// @param d the @ref typedef_diff node to consider.
261 ///
262 /// @param out the output stream to report to.
263 ///
264 /// @param indent the white space string to use for indentation.
265 void
267  ostream& out,
268  const string& indent) const
269 {
270  if (!d.to_be_reported())
271  return;
272 
274 
275  maybe_report_diff_for_member(f, s, d.context(), out, indent);
276 
278  && ((d.context()->get_allowed_category()
280  || d.context()->show_leaf_changes_only()))
281  || f->get_qualified_name() != s->get_qualified_name())
282  {
283  out << indent << "typedef name changed from "
284  << f->get_qualified_name()
285  << " to "
286  << s->get_qualified_name();
287  report_loc_info(s, *d.context(), out);
288  out << "\n";
289  }
290 }
291 
292 /// Reports the difference between the two subjects of the diff in a
293 /// serialized form.
294 ///
295 /// @param d @ref typedef_diff node to consider.
296 ///
297 /// @param out the output stream to emit the report to.
298 ///
299 /// @param indent the indentation string to use.
300 void
301 default_reporter::report(const typedef_diff& d,
302  ostream& out,
303  const string& indent) const
304 {
305  if (!d.to_be_reported())
306  return;
307 
309 
311  report_non_type_typedef_changes(d, out, indent);
312 
314  if (dif && dif->has_changes())
315  {
316  if (dif->to_be_reported())
317  {
318  RETURN_IF_BEING_REPORTED_OR_WAS_REPORTED_EARLIER2(dif,
319  "underlying type");
320  out << indent
321  << "underlying type '"
322  << dif->first_subject()->get_pretty_representation() << "'";
323  report_loc_info(dif->first_subject(), *d.context(), out);
324  out << " changed:\n";
325  dif->report(out, indent + " ");
326  }
327  else
328  {
329  // The typedef change is to be reported, so we'll report its
330  // underlying type change too (even if its redundant),
331  // unless it's suppressed. It makes sense in this
332  // particular case to emit the underlying type change
333  // because of the informative value underneath. We don't
334  // want to just know about the local changes of the typedef,
335  // but also about the changes on the underlying type.
336  diff_category c = dif->get_category();
338  {
339  out << indent
340  << "underlying type '"
341  << dif->first_subject()->get_pretty_representation() << "'";
342  report_loc_info(dif->first_subject(), *d.context(), out);
343  out << " changed:\n";
344  if (c & REDUNDANT_CATEGORY)
345  dif->set_category(c & ~REDUNDANT_CATEGORY);
346  dif->report(out, indent + " ");
347  if (c & REDUNDANT_CATEGORY)
348  dif->set_category(c | REDUNDANT_CATEGORY);
349  }
350  }
351  }
352 
353  d.reported_once(true);
354 }
355 
356 /// For a @ref qualified_type_diff node, report the changes that are
357 /// local.
358 ///
359 /// @param d the @ref qualified_type_diff node to consider.
360 ///
361 /// @param out the output stream to emit the report to.
362 ///
363 /// @param indent the white string to use for indentation.
364 ///
365 /// @return true iff a local change has been emitted. In this case,
366 /// the local change is a name change.
367 bool
369  ostream& out,
370  const string& indent) const
371 {
372  if (!d.to_be_reported())
373  return false;
374 
375  string fname = d.first_qualified_type()->get_pretty_representation(),
376  sname = d.second_qualified_type()->get_pretty_representation();
377 
378  if (fname != sname)
379  {
380  out << indent << "'" << fname << "' changed to '" << sname << "'\n";
381  return true;
382  }
383  return false;
384 }
385 
386 /// For a @ref qualified_type_diff node, report the changes of its
387 /// underlying type.
388 ///
389 /// @param d the @ref qualified_type_diff node to consider.
390 ///
391 /// @param out the output stream to emit the report to.
392 ///
393 /// @param indent the white string to use for indentation.
394 ///
395 /// @return true iff a local change has been emitted. In this case,
396 /// the local change is a name change.
397 void
399 (const qualified_type_diff& d, ostream& out, const string& indent) const
400 {
401  if (!d.to_be_reported())
402  return;
403 
405  ABG_ASSERT(dif);
406  ABG_ASSERT(dif->to_be_reported());
407  RETURN_IF_BEING_REPORTED_OR_WAS_REPORTED_EARLIER2(dif,
408  "unqualified "
409  "underlying type");
410 
411  string fltname = dif->first_subject()->get_pretty_representation();
412  out << indent << "in unqualified underlying type '" << fltname << "'";
413  report_loc_info(dif->second_subject(), *d.context(), out);
414  out << ":\n";
415  dif->report(out, indent + " ");
416 }
417 
418 /// Report a @ref qualified_type_diff in a serialized form.
419 ///
420 /// @param d the @ref qualified_type_diff node to consider.
421 ///
422 /// @param out the output stream to serialize to.
423 ///
424 /// @param indent the string to use to indent the lines of the report.
425 void
426 default_reporter::report(const qualified_type_diff& d, ostream& out,
427  const string& indent) const
428 {
429  if (!d.to_be_reported())
430  return;
431 
432  RETURN_IF_BEING_REPORTED_OR_WAS_REPORTED_EARLIER(d.first_qualified_type(),
434 
436  if (report_local_qualified_type_changes(d, out, indent))
437  // The local change was emitted and it's a name change. If the
438  // type name changed, the it means the type changed altogether.
439  // It makes a little sense to detail the changes in extenso here.
440  return;
441 
443 }
444 
445 /// Report the @ref pointer_diff in a serialized form.
446 ///
447 /// @param d the @ref pointer_diff node to consider.
448 ///
449 /// @param out the stream to serialize the diff to.
450 ///
451 /// @param indent the prefix to use for the indentation of this
452 /// serialization.
453 void
454 default_reporter::report(const pointer_diff& d, ostream& out,
455  const string& indent) const
456 {
457  if (!d.to_be_reported())
458  return;
459 
460  if (diff_sptr dif = d.underlying_type_diff())
461  {
462  RETURN_IF_BEING_REPORTED_OR_WAS_REPORTED_EARLIER2(dif, "pointed to type");
463  string repr = dif->first_subject()
464  ? dif->first_subject()->get_pretty_representation()
465  : string("void");
466 
467  out << indent
468  << "in pointed to type '" << repr << "'";
469  report_loc_info(dif->second_subject(), *d.context(), out);
470  out << ":\n";
471  dif->report(out, indent + " ");
472  }
473 }
474 
475 /// For a @reference_diff node, report the local changes carried by
476 /// the diff node.
477 ///
478 /// @param d the @reference_diff node to consider.
479 ///
480 /// @param out the output stream to report to.
481 ///
482 /// @param indent the white space indentation to use in the report.
483 void
485  ostream& out,
486  const string& indent) const
487 {
488  if (!d.to_be_reported())
489  return;
490 
492  ABG_ASSERT(f && s);
493 
494  string f_repr = f->get_pretty_representation(),
495  s_repr = s->get_pretty_representation();
496 
497  if (f->is_lvalue() != s->is_lvalue())
498  {
499  out << indent;
500  if (f->is_lvalue())
501  out << "lvalue reference type '" << f_repr
502  << " became an rvalue reference type: '"
503  << s_repr
504  << "'\n";
505  else
506  out << "rvalue reference type '" << f_repr
507  << " became an lvalue reference type: '"
508  << s_repr
509  << "'\n";
510  }
511  else if (!types_have_similar_structure(f->get_pointed_to_type().get(),
512  s->get_pointed_to_type().get()))
513  out << indent
514  << "reference type changed from: '"
515  << f_repr << "' to: '" << s_repr << "'\n";
516 }
517 
518 /// Report a @ref reference_diff in a serialized form.
519 ///
520 /// @param d the @ref reference_diff node to consider.
521 ///
522 /// @param out the output stream to serialize the dif to.
523 ///
524 /// @param indent the string to use for indenting the report.
525 void
526 default_reporter::report(const reference_diff& d, ostream& out,
527  const string& indent) const
528 {
529  if (!d.to_be_reported())
530  return;
531 
532  enum change_kind k = ir::NO_CHANGE_KIND;
533  equals(*d.first_reference(), *d.second_reference(), &k);
534 
536  if ((k & ALL_LOCAL_CHANGES_MASK) && !(k & SUBTYPE_CHANGE_KIND))
537  report_local_reference_type_changes(d, out, indent);
538 
539  if (k & SUBTYPE_CHANGE_KIND)
540  if (diff_sptr dif = d.underlying_type_diff())
541  {
542  RETURN_IF_BEING_REPORTED_OR_WAS_REPORTED_EARLIER2(dif,
543  "referenced type");
544 
545  out << indent
546  << "in referenced type '"
547  << dif->first_subject()->get_pretty_representation() << "'";
548  report_loc_info(dif->second_subject(), *d.context(), out);
549  out << ":\n";
550  dif->report(out, indent + " ");
551  }
552 }
553 
554 /// Report the local changes carried by a @ref ptr_to_mbr_diff diff
555 /// node.
556 ///
557 /// This is a subroutine of the method default_reporter::report() that
558 /// emits change report for @ref ptr_to_mbr_diff node.
559 ///
560 /// @param d the diff node to consider
561 ///
562 /// @param out the output stream to emit the report to.
563 ///
564 /// @param indent the indentation string (spaces) to use in the
565 /// report.
566 ///
567 /// @return truf iff a report was emitted to the output stream.
568 bool
570  std::ostream& out,
571  const std::string& indent) const
572 {
573  if (!d.to_be_reported())
574  return false;
575 
577  s = d.second_ptr_to_mbr_type();
578 
579  enum change_kind k = ir::NO_CHANGE_KIND;
581 
582  if (k & ALL_LOCAL_CHANGES_MASK)
583  {
584  string f_repr = f->get_pretty_representation(),
585  s_repr = s->get_pretty_representation();
586 
587  out << indent;
588  out << "pointer-to-member type changed from: '"
589  << f_repr << " to: '"<< s_repr << "'\n";
590  return true;
591  }
592  return false;
593 }
594 
595 
596 /// Emit a textual report about the changes carried by a @ref
597 /// ptr_to_mbr_diff diff node.
598 ///
599 /// @param out the output stream to emit the report to.
600 ///
601 /// @param indent the indentation string to use for the report.
602 void
603 default_reporter::report(const ptr_to_mbr_diff& d,
604  std::ostream& out,
605  const std::string& indent) const
606 {
607  if (!d.to_be_reported())
608  return;
609 
610  report_local_ptr_to_mbr_type_changes(d, out, indent);
611 
612  if (diff_sptr dif = d.member_type_diff())
613  {
614  RETURN_IF_BEING_REPORTED_OR_WAS_REPORTED_EARLIER2
615  (dif,"data member type of pointer-to-member");
616  if (dif->to_be_reported())
617  {
618  out << indent
619  << "in data member type '"
620  << dif->first_subject()->get_pretty_representation()
621  << "' of pointed-to-member type '"
622  << d.first_ptr_to_mbr_type()->get_pretty_representation()
623  << "'";
624  report_loc_info(dif->second_subject(), *d.context(), out);
625  out << ":\n";
626  dif->report(out, indent + " ");
627  }
628  }
629  if (diff_sptr dif = d.containing_type_diff())
630  {
631  RETURN_IF_BEING_REPORTED_OR_WAS_REPORTED_EARLIER2
632  (dif,"containing type of pointer-to-member");
633  if (dif->to_be_reported())
634  {
635  out << indent
636  << "in containing type '"
637  << dif->first_subject()->get_pretty_representation()
638  << "' of pointed-to-member type '"
639  << d.first_ptr_to_mbr_type()->get_pretty_representation()
640  << "'";
641  report_loc_info(dif->second_subject(), *d.context(), out);
642  out << ":\n";
643  dif->report(out, indent + " ");
644  }
645  }
646 }
647 
648 /// Emit a textual report about the a @ref fn_parm_diff instance.
649 ///
650 /// @param d the @ref fn_parm_diff to consider.
651 ///
652 /// @param out the output stream to emit the textual report to.
653 ///
654 /// @param indent the indentation string to use in the report.
655 void
656 default_reporter::report(const fn_parm_diff& d, ostream& out,
657  const string& indent) const
658 {
659  if (!d.to_be_reported())
660  return;
661 
663  s = d.second_parameter();
664 
665  // either the parameter has a sub-type change (if its type name
666  // hasn't changed) or it has a "grey" change (that is, a change that
667  // changes his type name w/o changing the signature of the
668  // function).
669  bool has_sub_type_change =
671  d.second_parameter()->get_type());
672 
673  diff_sptr type_diff = d.type_diff();
674  ABG_ASSERT(type_diff->has_changes());
675 
676  out << indent;
677  if (f->get_is_artificial())
678  out << "implicit ";
679  out << "parameter " << f->get_index();
680  report_loc_info(f, *d.context(), out);
681  out << " of type '"
682  << f->get_type_pretty_representation();
683 
684  if (has_sub_type_change)
685  out << "' has sub-type changes:\n";
686  else
687  out << "' changed:\n";
688 
689  type_diff->report(out, indent + " ");
690 }
691 
692 /// For a @ref function_type_diff node, report the local changes
693 /// carried by the diff node.
694 ///
695 /// @param d the @ref function_type_diff node to consider.
696 ///
697 /// @param out the output stream to report to.
698 ///
699 /// @param indent the white space indentation string to use.
700 void
702  ostream& out,
703  const string& indent) const
704 
705 {
706  if (!d.to_be_reported())
707  return;
708 
711 
712  diff_context_sptr ctxt = d.context();
713 
714  // Report about the size of the function address
715  if (fft->get_size_in_bits() != sft->get_size_in_bits())
716  {
717  out << indent << "address size of function changed from "
718  << fft->get_size_in_bits()
719  << " bits to "
720  << sft->get_size_in_bits()
721  << " bits\n";
722  }
723 
724  // Report about the alignment of the function address
725  if (fft->get_alignment_in_bits()
726  != sft->get_alignment_in_bits())
727  {
728  out << indent << "address alignment of function changed from "
729  << fft->get_alignment_in_bits()
730  << " bits to "
731  << sft->get_alignment_in_bits()
732  << " bits\n";
733  }
734 
735  // Hmmh, the above was quick. Now report about function parameters;
736  // this shouldn't be as straightforward.
737 
738  // Report about the parameters that got removed.
739  for (vector<function_decl::parameter_sptr>::const_iterator i =
740  d.priv_->sorted_deleted_parms_.begin();
741  i != d.priv_->sorted_deleted_parms_.end();
742  ++i)
743  {
744  out << indent << "parameter " << (*i)->get_index()
745  << " of type '" << (*i)->get_type_pretty_representation()
746  << "' was removed\n";
747  }
748 
749  // Report about the parameters that got added
750  for (vector<function_decl::parameter_sptr>::const_iterator i =
751  d.priv_->sorted_added_parms_.begin();
752  i != d.priv_->sorted_added_parms_.end();
753  ++i)
754  {
755  out << indent << "parameter " << (*i)->get_index()
756  << " of type '" << (*i)->get_type_pretty_representation()
757  << "' was added\n";
758  }
759 }
760 
761 /// Build and emit a textual report about a @ref function_type_diff.
762 ///
763 /// @param d the @ref function_type_diff to consider.
764 ///
765 /// @param out the output stream.
766 ///
767 /// @param indent the indentation string to use.
768 void
769 default_reporter::report(const function_type_diff& d, ostream& out,
770  const string& indent) const
771 {
772  if (!d.to_be_reported())
773  return;
774 
777 
778  diff_context_sptr ctxt = d.context();
779  corpus_sptr fc = ctxt->get_first_corpus();
780  corpus_sptr sc = ctxt->get_second_corpus();
781 
782  // Report about return type differences.
783  if (d.priv_->return_type_diff_
784  && d.priv_->return_type_diff_->to_be_reported())
785  {
786  out << indent << "return type changed:\n";
787  d.priv_->return_type_diff_->report(out, indent + " ");
788  }
789 
790  // Report about the parameter types that have changed sub-types.
791  for (vector<fn_parm_diff_sptr>::const_iterator i =
792  d.priv_->sorted_subtype_changed_parms_.begin();
793  i != d.priv_->sorted_subtype_changed_parms_.end();
794  ++i)
795  {
796  diff_sptr dif = *i;
797  if (dif && dif->to_be_reported())
798  dif->report(out, indent);
799  }
800 
802  report_local_function_type_changes(d, out, indent);
803 }
804 
805 /// Report about the change carried by a @ref subrange_diff diff node
806 /// in a serialized form.
807 ///
808 /// @param d the diff node to consider.
809 ///
810 /// @param out the output stream to report to.
811 ///
812 /// @param indent the indentation string to use in the report.
813 void
814 default_reporter::report(const subrange_diff& d, std::ostream& out,
815  const std::string& indent) const
816 {
817  if (!diff_to_be_reported(&d))
818  return;
819 
820  RETURN_IF_BEING_REPORTED_OR_WAS_REPORTED_EARLIER3(d.first_subrange(),
821  d.second_subrange(),
822  "range type");
823 
824  represent(d, d.context(), out,indent, /*local_only=*/false);
825 }
826 
827 /// Report a @ref array_diff in a serialized form.
828 ///
829 /// @param d the @ref array_diff to consider.
830 ///
831 /// @param out the output stream to serialize the dif to.
832 ///
833 /// @param indent the string to use for indenting the report.
834 void
835 default_reporter::report(const array_diff& d, ostream& out,
836  const string& indent) const
837 {
838  if (!d.to_be_reported())
839  return;
840 
841  string name = d.first_array()->get_pretty_representation();
842  RETURN_IF_BEING_REPORTED_OR_WAS_REPORTED_EARLIER3(d.first_array(),
843  d.second_array(),
844  "array type");
845 
846  diff_sptr dif = d.element_type_diff();
847  if (dif->to_be_reported())
848  {
849  string fn = ir::get_pretty_representation(is_type(dif->first_subject()));
850  // report array element type changes
851  out << indent << "array element type '"
852  << fn << "' changed:\n";
853  dif->report(out, indent + " ");
854  }
855 
858  d.second_array(),
859  d.context(),
860  out, indent);
861 
863  {
864  int subrange_index = 0;
865  for (const auto& subrange_diff : d.subrange_diffs())
866  {
867  ++subrange_index;
869  {
870  out << indent << "array subrange ";
871  if (d.subrange_diffs().size() > 1)
872  out << subrange_index << " ";
873  out << "changed: \n";
874  subrange_diff->report(out, indent + " ");
875  }
876  }
877  }
878 
879 }
880 
881 /// Generates a report for an intance of @ref base_diff.
882 ///
883 /// @param d the @ref base_diff to consider.
884 ///
885 /// @param out the output stream to send the report to.
886 ///
887 /// @param indent the string to use for indentation.
888 void
889 default_reporter::report(const base_diff& d, ostream& out,
890  const string& indent) const
891 {
892  if (!d.to_be_reported())
893  return;
894 
895  class_decl::base_spec_sptr f = d.first_base(), s = d.second_base();
896  string repr = f->get_base_class()->get_pretty_representation();
897  bool emitted = false;
898 
899  if (!d.is_filtered_out_without_looking_at_allowed_changes())
900  {
901  if (f->get_is_static() != s->get_is_static())
902  {
903  if (f->get_is_static())
904  out << indent << "is no more static";
905  else
906  out << indent << "now becomes static";
907  emitted = true;
908  }
909 
910  if ((d.context()->get_allowed_category() & ACCESS_CHANGE_CATEGORY)
911  && (f->get_access_specifier() != s->get_access_specifier()))
912  {
913  if (emitted)
914  out << ", ";
915 
916  out << "has access changed from '"
917  << f->get_access_specifier()
918  << "' to '"
919  << s->get_access_specifier()
920  << "'";
921 
922  emitted = true;
923  }
924  }
925  if (class_diff_sptr dif = d.get_underlying_class_diff())
926  {
927  if (dif->to_be_reported())
928  {
929  if (emitted)
930  out << "\n";
931  dif->report(out, indent);
932  }
933  }
934 }
935 
936 /// Report the changes carried by a @ref scope_diff.
937 ///
938 /// @param d the @ref scope_diff to consider.
939 ///
940 /// @param out the out stream to report the changes to.
941 ///
942 /// @param indent the string to use for indentation.
943 void
944 default_reporter::report(const scope_diff& d, ostream& out,
945  const string& indent) const
946 {
947  if (!d.to_be_reported())
948  return;
949 
950  // Report changed types.
951  unsigned num_changed_types = d.changed_types().size();
952  if (num_changed_types == 0)
953  ;
954  else if (num_changed_types == 1)
955  out << indent << "1 changed type:\n";
956  else
957  out << indent << num_changed_types << " changed types:\n";
958 
959  for (diff_sptrs_type::const_iterator dif = d.changed_types().begin();
960  dif != d.changed_types().end();
961  ++dif)
962  {
963  if (!*dif)
964  continue;
965 
966  out << indent << " '"
967  << (*dif)->first_subject()->get_pretty_representation()
968  << "' changed:\n";
969  (*dif)->report(out, indent + " ");
970  }
971 
972  // Report changed decls
973  unsigned num_changed_decls = d.changed_decls().size();
974  if (num_changed_decls == 0)
975  ;
976  else if (num_changed_decls == 1)
977  out << indent << "1 changed declaration:\n";
978  else
979  out << indent << num_changed_decls << " changed declarations:\n";
980 
981  for (diff_sptrs_type::const_iterator dif= d.changed_decls().begin();
982  dif != d.changed_decls().end ();
983  ++dif)
984  {
985  if (!*dif)
986  continue;
987 
988  out << indent << " '"
989  << (*dif)->first_subject()->get_pretty_representation()
990  << "' was changed to '"
991  << (*dif)->second_subject()->get_pretty_representation() << "'";
992  report_loc_info((*dif)->second_subject(), *d.context(), out);
993  out << ":\n";
994 
995  (*dif)->report(out, indent + " ");
996  }
997 
998  // Report removed types/decls
999  for (string_decl_base_sptr_map::const_iterator i =
1000  d.priv_->deleted_types_.begin();
1001  i != d.priv_->deleted_types_.end();
1002  ++i)
1003  out << indent
1004  << " '"
1005  << i->second->get_pretty_representation()
1006  << "' was removed\n";
1007 
1008  if (d.priv_->deleted_types_.size())
1009  out << "\n";
1010 
1011  for (string_decl_base_sptr_map::const_iterator i =
1012  d.priv_->deleted_decls_.begin();
1013  i != d.priv_->deleted_decls_.end();
1014  ++i)
1015  out << indent
1016  << " '"
1017  << i->second->get_pretty_representation()
1018  << "' was removed\n";
1019 
1020  if (d.priv_->deleted_decls_.size())
1021  out << "\n";
1022 
1023  // Report added types/decls
1024  bool emitted = false;
1025  for (string_decl_base_sptr_map::const_iterator i =
1026  d.priv_->inserted_types_.begin();
1027  i != d.priv_->inserted_types_.end();
1028  ++i)
1029  {
1030  // Do not report about type_decl as these are usually built-in
1031  // types.
1032  if (dynamic_pointer_cast<type_decl>(i->second))
1033  continue;
1034  out << indent
1035  << " '"
1036  << i->second->get_pretty_representation()
1037  << "' was added\n";
1038  emitted = true;
1039  }
1040 
1041  if (emitted)
1042  out << "\n";
1043 
1044  emitted = false;
1045  for (string_decl_base_sptr_map::const_iterator i =
1046  d.priv_->inserted_decls_.begin();
1047  i != d.priv_->inserted_decls_.end();
1048  ++i)
1049  {
1050  // Do not report about type_decl as these are usually built-in
1051  // types.
1052  if (dynamic_pointer_cast<type_decl>(i->second))
1053  continue;
1054  out << indent
1055  << " '"
1056  << i->second->get_pretty_representation()
1057  << "' was added\n";
1058  emitted = true;
1059  }
1060 
1061  if (emitted)
1062  out << "\n";
1063 }
1064 
1065 /// Report the changes carried by a @ref class_or_union_diff node in a
1066 /// textual format.
1067 ///
1068 /// @param d the @ref class_or_union_diff node to consider.
1069 ///
1070 /// @param out the output stream to write the textual report to.
1071 ///
1072 /// @param indent the number of white space to use as indentation.
1073 void
1074 default_reporter::report(const class_or_union_diff& d,
1075  ostream& out,
1076  const string& indent) const
1077 {
1078  if (!d.to_be_reported())
1079  return;
1080 
1081  class_or_union_sptr first = d.first_class_or_union(),
1082  second = d.second_class_or_union();
1083 
1084  const diff_context_sptr& ctxt = d.context();
1085 
1086  // Report class decl-only <-> definition change.
1087  if (ctxt->get_allowed_category() & TYPE_DECL_ONLY_DEF_CHANGE_CATEGORY)
1088  if (filtering::has_class_decl_only_def_change(first, second))
1089  {
1090  string was =
1091  first->get_is_declaration_only()
1092  ? " was a declaration-only type"
1093  : " was a defined type";
1094 
1095  string is_now =
1096  second->get_is_declaration_only()
1097  ? " and is now a declaration-only type"
1098  : " and is now a defined type";
1099 
1100  out << indent << "type " << first->get_pretty_representation()
1101  << was << is_now << "\n";
1102  return;
1103  }
1104 
1105  // member functions
1106  if (d.member_fns_changes())
1107  {
1108  // report deletions
1109  int numdels = d.get_priv()->deleted_member_functions_.size();
1110  size_t num_filtered =
1111  d.get_priv()->count_filtered_deleted_mem_fns(ctxt);
1112  if (numdels)
1113  report_mem_header(out, numdels, num_filtered, del_kind,
1114  "member function", indent);
1115  for (class_or_union::member_functions::const_iterator i =
1116  d.get_priv()->sorted_deleted_member_functions_.begin();
1117  i != d.get_priv()->sorted_deleted_member_functions_.end();
1118  ++i)
1119  {
1120  if (!(ctxt->get_allowed_category()
1123  continue;
1124 
1125  method_decl_sptr mem_fun = *i;
1126  out << indent << " ";
1127  represent(*ctxt, mem_fun, out);
1128  }
1129 
1130  // report insertions;
1131  int numins = d.get_priv()->inserted_member_functions_.size();
1132  num_filtered = d.get_priv()->count_filtered_inserted_mem_fns(ctxt);
1133  if (numins)
1134  report_mem_header(out, numins, num_filtered, ins_kind,
1135  "member function", indent);
1136  for (class_or_union::member_functions::const_iterator i =
1137  d.get_priv()->sorted_inserted_member_functions_.begin();
1138  i != d.get_priv()->sorted_inserted_member_functions_.end();
1139  ++i)
1140  {
1141  if (!(ctxt->get_allowed_category()
1144  continue;
1145 
1146  method_decl_sptr mem_fun = *i;
1147  out << indent << " ";
1148  represent(*ctxt, mem_fun, out);
1149  }
1150 
1151  // report member function with sub-types changes
1152  int numchanges = d.get_priv()->sorted_changed_member_functions_.size();
1153  num_filtered = d.get_priv()->count_filtered_changed_mem_fns(ctxt);
1154  if (numchanges)
1155  report_mem_header(out, numchanges, num_filtered, change_kind,
1156  "member function", indent);
1157  for (function_decl_diff_sptrs_type::const_iterator i =
1158  d.get_priv()->sorted_changed_member_functions_.begin();
1159  i != d.get_priv()->sorted_changed_member_functions_.end();
1160  ++i)
1161  {
1162  if (!(ctxt->get_allowed_category()
1165  ((*i)->first_function_decl()))
1167  ((*i)->second_function_decl())))
1168  continue;
1169 
1170  diff_sptr diff = *i;
1171  if (!diff || !diff->to_be_reported())
1172  continue;
1173 
1174  string repr =
1175  (*i)->first_function_decl()->get_pretty_representation();
1176  out << indent << " '" << repr << "' has some sub-type changes:\n";
1177  diff->report(out, indent + " ");
1178  }
1179  }
1180 
1181  // data members
1182  if (d.data_members_changes())
1183  {
1184  // report deletions
1185  int numdels = d.class_or_union_diff::get_priv()->
1186  get_deleted_non_static_data_members_number();
1187  if (numdels)
1188  {
1189  report_mem_header(out, numdels, 0, del_kind,
1190  "data member", indent);
1191  vector<decl_base_sptr> sorted_dms;
1193  (d.class_or_union_diff::get_priv()->deleted_data_members_,
1194  sorted_dms);
1195  for (vector<decl_base_sptr>::const_iterator i = sorted_dms.begin();
1196  i != sorted_dms.end();
1197  ++i)
1198  {
1199  var_decl_sptr data_mem =
1200  dynamic_pointer_cast<var_decl>(*i);
1201  ABG_ASSERT(data_mem);
1202  if (get_member_is_static(data_mem))
1203  continue;
1204  represent_data_member(data_mem, ctxt, out, indent + " ");
1205  }
1206  }
1207 
1208  //report insertions
1209  int numins =
1210  d.class_or_union_diff::get_priv()->inserted_data_members_.size();
1211  if (numins)
1212  {
1213  report_mem_header(out, numins, 0, ins_kind,
1214  "data member", indent);
1215  vector<decl_base_sptr> sorted_dms;
1217  (d.class_or_union_diff::get_priv()->inserted_data_members_,
1218  sorted_dms);
1219  for (vector<decl_base_sptr>::const_iterator i = sorted_dms.begin();
1220  i != sorted_dms.end();
1221  ++i)
1222  {
1223  var_decl_sptr data_mem =
1224  dynamic_pointer_cast<var_decl>(*i);
1225  ABG_ASSERT(data_mem);
1226  represent_data_member(data_mem, ctxt, out, indent + " ");
1227  }
1228  }
1229 
1230  // report change
1231  size_t num_changes =
1232  (d.sorted_subtype_changed_data_members().size()
1233  + d.sorted_changed_data_members().size());
1234 
1235  size_t num_changes_filtered =
1236  (d.count_filtered_subtype_changed_data_members()
1237  + d.count_filtered_changed_data_members());
1238 
1239  if (num_changes)
1240  {
1241  report_mem_header(out, num_changes, num_changes_filtered,
1242  change_kind, "data member", indent);
1243 
1244  for (var_diff_sptrs_type::const_iterator it =
1245  d.sorted_changed_data_members().begin();
1246  it != d.sorted_changed_data_members().end();
1247  ++it)
1248  if ((*it)->to_be_reported())
1249  represent(*it, ctxt, out, indent + " ");
1250 
1251  for (var_diff_sptrs_type::const_iterator it =
1252  d.sorted_subtype_changed_data_members().begin();
1253  it != d.sorted_subtype_changed_data_members().end();
1254  ++it)
1255  if ((*it)->to_be_reported())
1256  represent(*it, ctxt, out, indent + " ");
1257  }
1258 
1259  // Report about data members replaced by an anonymous union data
1260  // member.
1262  }
1263 
1264  // member types
1265  if (const edit_script& e = d.member_types_changes())
1266  {
1267  int numchanges =
1268  d.class_or_union_diff::get_priv()->sorted_changed_member_types_.size();
1269  int numdels =
1270  d.class_or_union_diff::get_priv()->deleted_member_types_.size();
1271 
1272  // report deletions
1273  if (numdels)
1274  {
1275  report_mem_header(out, numdels, 0, del_kind,
1276  "member type", indent);
1277 
1278  for (string_decl_base_sptr_map::const_iterator i =
1279  d.class_or_union_diff::get_priv()->deleted_member_types_.begin();
1280  i != d.class_or_union_diff::get_priv()->deleted_member_types_.end();
1281  ++i)
1282  {
1283  decl_base_sptr mem_type = i->second;
1284  out << indent << " '"
1285  << mem_type->get_pretty_representation()
1286  << "'\n";
1287  }
1288  out << "\n";
1289  }
1290  // report changes
1291  if (numchanges)
1292  {
1293  report_mem_header(out, numchanges, 0, change_kind,
1294  "member type", indent);
1295 
1296  for (diff_sptrs_type::const_iterator it =
1297  d.class_or_union_diff::get_priv()->sorted_changed_member_types_.begin();
1298  it != d.class_or_union_diff::get_priv()->sorted_changed_member_types_.end();
1299  ++it)
1300  {
1301  if (!(*it)->to_be_reported())
1302  continue;
1303 
1304  type_or_decl_base_sptr o = (*it)->first_subject();
1305  type_or_decl_base_sptr n = (*it)->second_subject();
1306  out << indent << " '"
1307  << o->get_pretty_representation()
1308  << "' changed ";
1309  report_loc_info(n, *ctxt, out);
1310  out << ":\n";
1311  (*it)->report(out, indent + " ");
1312  }
1313  out << "\n";
1314  }
1315 
1316  // report insertions
1317  int numins = e.num_insertions();
1318  ABG_ASSERT(numchanges <= numins);
1319  numins -= numchanges;
1320 
1321  if (numins)
1322  {
1323  report_mem_header(out, numins, 0, ins_kind,
1324  "member type", indent);
1325 
1326  for (vector<insertion>::const_iterator i = e.insertions().begin();
1327  i != e.insertions().end();
1328  ++i)
1329  {
1330  type_base_sptr mem_type;
1331  for (vector<unsigned>::const_iterator j =
1332  i->inserted_indexes().begin();
1333  j != i->inserted_indexes().end();
1334  ++j)
1335  {
1336  mem_type = second->get_member_types()[*j];
1337  if (!d.class_or_union_diff::get_priv()->
1338  member_type_has_changed(get_type_declaration(mem_type)))
1339  {
1340  out << indent << " '"
1341  << get_type_declaration(mem_type)->
1343  << "'\n";
1344  }
1345  }
1346  }
1347  out << "\n";
1348  }
1349  }
1350 
1351  // member function templates
1352  if (const edit_script& e = d.member_fn_tmpls_changes())
1353  {
1354  // report deletions
1355  int numdels = e.num_deletions();
1356  if (numdels)
1357  report_mem_header(out, numdels, 0, del_kind,
1358  "member function template", indent);
1359  for (vector<deletion>::const_iterator i = e.deletions().begin();
1360  i != e.deletions().end();
1361  ++i)
1362  {
1363  member_function_template_sptr mem_fn_tmpl =
1364  first->get_member_function_templates()[i->index()];
1365  out << indent << " '"
1366  << mem_fn_tmpl->as_function_tdecl()->get_pretty_representation()
1367  << "'\n";
1368  }
1369 
1370  // report insertions
1371  int numins = e.num_insertions();
1372  if (numins)
1373  report_mem_header(out, numins, 0, ins_kind,
1374  "member function template", indent);
1375  for (vector<insertion>::const_iterator i = e.insertions().begin();
1376  i != e.insertions().end();
1377  ++i)
1378  {
1379  member_function_template_sptr mem_fn_tmpl;
1380  for (vector<unsigned>::const_iterator j =
1381  i->inserted_indexes().begin();
1382  j != i->inserted_indexes().end();
1383  ++j)
1384  {
1385  mem_fn_tmpl = second->get_member_function_templates()[*j];
1386  out << indent << " '"
1387  << mem_fn_tmpl->as_function_tdecl()->
1389  << "'\n";
1390  }
1391  }
1392  }
1393 
1394  // member class templates.
1395  if (const edit_script& e = d.member_class_tmpls_changes())
1396  {
1397  // report deletions
1398  int numdels = e.num_deletions();
1399  if (numdels)
1400  report_mem_header(out, numdels, 0, del_kind,
1401  "member class template", indent);
1402  for (vector<deletion>::const_iterator i = e.deletions().begin();
1403  i != e.deletions().end();
1404  ++i)
1405  {
1406  member_class_template_sptr mem_cls_tmpl =
1407  first->get_member_class_templates()[i->index()];
1408  out << indent << " '"
1409  << mem_cls_tmpl->as_class_tdecl()->get_pretty_representation()
1410  << "'\n";
1411  }
1412 
1413  // report insertions
1414  int numins = e.num_insertions();
1415  if (numins)
1416  report_mem_header(out, numins, 0, ins_kind,
1417  "member class template", indent);
1418  for (vector<insertion>::const_iterator i = e.insertions().begin();
1419  i != e.insertions().end();
1420  ++i)
1421  {
1422  member_class_template_sptr mem_cls_tmpl;
1423  for (vector<unsigned>::const_iterator j =
1424  i->inserted_indexes().begin();
1425  j != i->inserted_indexes().end();
1426  ++j)
1427  {
1428  mem_cls_tmpl = second->get_member_class_templates()[*j];
1429  out << indent << " '"
1430  << mem_cls_tmpl->as_class_tdecl()
1431  ->get_pretty_representation()
1432  << "'\n";
1433  }
1434  }
1435  }
1436 }
1437 
1438 /// Produce a basic report about the changes carried by a @ref
1439 /// class_diff node.
1440 ///
1441 /// @param d the @ref class_diff node to consider.
1442 ///
1443 /// @param out the output stream to report the changes to.
1444 ///
1445 /// @param indent the string to use as an indentation prefix in the
1446 /// report.
1447 void
1448 default_reporter::report(const class_diff& d, ostream& out,
1449  const string& indent) const
1450 {
1451  if (!d.to_be_reported())
1452  return;
1453 
1454  string name = d.first_subject()->get_pretty_representation();
1455 
1456  RETURN_IF_BEING_REPORTED_OR_WAS_REPORTED_EARLIER(d.first_subject(),
1457  d.second_subject());
1458 
1459  d.currently_reporting(true);
1460 
1461  // Now report the changes about the differents parts of the type.
1462  class_decl_sptr first = d.first_class_decl(),
1463  second = d.second_class_decl();
1464 
1465  report_name_size_and_alignment_changes(first, second, d.context(),
1466  out, indent);
1467 
1468  const diff_context_sptr& ctxt = d.context();
1469  maybe_report_diff_for_member(first, second, ctxt, out, indent);
1470 
1471  // bases classes
1472  if (d.base_changes())
1473  {
1474  // Report deletions.
1475  int numdels = d.get_priv()->deleted_bases_.size();
1476  size_t numchanges = d.get_priv()->sorted_changed_bases_.size();
1477 
1478  if (numdels)
1479  {
1480  report_mem_header(out, numdels, 0, del_kind,
1481  "base class", indent);
1482 
1483  for (class_decl::base_specs::const_iterator i
1484  = d.get_priv()->sorted_deleted_bases_.begin();
1485  i != d.get_priv()->sorted_deleted_bases_.end();
1486  ++i)
1487  {
1488  if (i != d.get_priv()->sorted_deleted_bases_.begin())
1489  out << "\n";
1490 
1491  class_decl::base_spec_sptr base = *i;
1492 
1493  if (d.get_priv()->base_has_changed(base))
1494  continue;
1495  out << indent << " "
1496  << base->get_base_class()->get_pretty_representation();
1497  report_loc_info(base->get_base_class(), *d.context(), out);
1498  }
1499  out << "\n";
1500  }
1501 
1502  // Report changes.
1503  size_t num_filtered = d.get_priv()->count_filtered_bases();
1504  if (numchanges)
1505  {
1506  report_mem_header(out, numchanges, num_filtered, change_kind,
1507  "base class", indent);
1508  for (base_diff_sptrs_type::const_iterator it =
1509  d.get_priv()->sorted_changed_bases_.begin();
1510  it != d.get_priv()->sorted_changed_bases_.end();
1511  ++it)
1512  {
1513  base_diff_sptr diff = *it;
1514  if (!diff || !diff->to_be_reported())
1515  continue;
1516 
1517  class_decl::base_spec_sptr o = diff->first_base();
1518  out << indent << " '"
1519  << o->get_base_class()->get_pretty_representation() << "'";
1520  report_loc_info(o->get_base_class(), *d.context(), out);
1521  out << " changed:\n";
1522  diff->report(out, indent + " ");
1523  }
1524  }
1525 
1526  //Report insertions.
1527  int numins = d.get_priv()->inserted_bases_.size();
1528  if (numins)
1529  {
1530  report_mem_header(out, numins, 0, ins_kind,
1531  "base class", indent);
1532 
1533  for (class_decl::base_specs::const_iterator i =
1534  d.get_priv()->sorted_inserted_bases_.begin();
1535  i != d.get_priv()->sorted_inserted_bases_.end();
1536  ++i)
1537  {
1538  class_decl_sptr b = (*i)->get_base_class();
1539  out << indent << " " << b->get_pretty_representation();
1540  report_loc_info(b, *ctxt, out);
1541  out << "\n";
1542  }
1543  }
1544 
1545  // Report base classes re-organisation
1546  maybe_report_base_class_reordering(d, out, indent);
1547  }
1548 
1549  d.class_or_union_diff::report(out, indent);
1550 
1551  d.currently_reporting(false);
1552 
1553  d.reported_once(true);
1554 }
1555 
1556 /// Produce a basic report about the changes carried by a @ref
1557 /// union_diff node.
1558 ///
1559 /// @param d the @ref union_diff node to consider.
1560 ///
1561 /// @param out the output stream to report the changes to.
1562 ///
1563 /// @param indent the string to use as an indentation prefix in the
1564 /// report.
1565 void
1566 default_reporter::report(const union_diff& d, ostream& out,
1567  const string& indent) const
1568 {
1569  RETURN_IF_BEING_REPORTED_OR_WAS_REPORTED_EARLIER(d.first_subject(),
1570  d.second_subject());
1571 
1572  d.currently_reporting(true);
1573 
1574  // Now report the changes about the differents parts of the type.
1575  union_decl_sptr first = d.first_union_decl(), second = d.second_union_decl();
1576 
1577  report_name_size_and_alignment_changes(first, second, d.context(),
1578  out, indent);
1579 
1580  maybe_report_diff_for_member(first, second,d. context(), out, indent);
1581 
1582  d.class_or_union_diff::report(out, indent);
1583 
1584  if (d.context()->get_allowed_category() & HARMLESS_UNION_OR_CLASS_CHANGE_CATEGORY
1586  {
1587  // The user wants to see harmless changes and the union diff we
1588  // are looking at does carry some harmless changes. Let's show
1589  // the "before" and "after" carried by the diff node.
1590  out << indent << "type changed from:\n"
1591  << get_class_or_union_flat_representation(first, indent + " ",
1592  /*one_line=*/true,
1593  /*internal=*/false,
1594  /*qualified_names=*/false)
1595  << "\n"
1596  << indent << "to:\n"
1597  << get_class_or_union_flat_representation(second, indent + " ",
1598  /*one_line=*/true,
1599  /*internal=*/false,
1600  /*qualified_names=*/false)
1601  << "\n";
1602  }
1603 
1604  d.currently_reporting(false);
1605 
1606  d.reported_once(true);
1607 }
1608 
1609 /// Emit a report about the changes carried by a @ref distinct_diff
1610 /// node.
1611 ///
1612 /// @param d the @ref distinct_diff node to consider.
1613 ///
1614 /// @param out the output stream to send the diff report to.
1615 ///
1616 /// @param indent the indentation string to use in the report.
1617 void
1618 default_reporter::report(const distinct_diff& d, ostream& out,
1619  const string& indent) const
1620 {
1621  if (!d.to_be_reported())
1622  return;
1623 
1624  type_or_decl_base_sptr f = d.first(), s = d.second();
1625 
1626  string f_repr = f ? f->get_pretty_representation() : "'void'";
1627  string s_repr = s ? s->get_pretty_representation() : "'void'";
1628 
1629  diff_sptr diff = d.compatible_child_diff();
1630 
1631  string compatible = diff ? " to compatible type '": " to '";
1632 
1633  out << indent << "entity changed from '" << f_repr << "'"
1634  << compatible << s_repr << "'";
1635  report_loc_info(s, *d.context(), out);
1636  out << "\n";
1637 
1638  type_base_sptr fs = strip_typedef(is_type(f)),
1639  ss = strip_typedef(is_type(s));
1640 
1641  report_size_and_alignment_changes(f, s, d.context(), out, indent);
1642 }
1643 
1644 /// Serialize a report of the changes encapsulated in the current
1645 /// instance of @ref function_decl_diff over to an output stream.
1646 ///
1647 /// @param d the @ref function_decl_diff node to consider.
1648 ///
1649 /// @param out the output stream to serialize the report to.
1650 ///
1651 /// @param indent the string to use an an indentation prefix.
1652 void
1653 default_reporter::report(const function_decl_diff& d, ostream& out,
1654  const string& indent) const
1655 {
1656  if (!d.to_be_reported())
1657  return;
1658 
1659  maybe_report_diff_for_member(d.first_function_decl(),
1660  d.second_function_decl(),
1661  d.context(), out, indent);
1662 
1663  function_decl_sptr ff = d.first_function_decl();
1664  function_decl_sptr sf = d.second_function_decl();
1665 
1666  diff_context_sptr ctxt = d.context();
1667  corpus_sptr fc = ctxt->get_first_corpus();
1668  corpus_sptr sc = ctxt->get_second_corpus();
1669 
1670 
1671  string qn1 = ff->get_qualified_name(), qn2 = sf->get_qualified_name(),
1672  linkage_names1, linkage_names2;
1673  elf_symbol_sptr s1 = ff->get_symbol(), s2 = sf->get_symbol();
1674 
1675  if (s1)
1676  linkage_names1 = s1->get_id_string();
1677  if (s2)
1678  linkage_names2 = s2->get_id_string();
1679 
1680  // If the symbols for ff and sf have aliases, get all the names of
1681  // the aliases;
1682  if (fc && s1)
1683  linkage_names1 =
1684  s1->get_aliases_id_string(fc->get_fun_symbol_map());
1685  if (sc && s2)
1686  linkage_names2 =
1687  s2->get_aliases_id_string(sc->get_fun_symbol_map());
1688 
1689  if (!d.is_filtered_out_without_looking_at_allowed_changes())
1690  {
1691  /// If the set of linkage names of the function have changed, report
1692  /// it.
1693  if (linkage_names1 != linkage_names2)
1694  {
1695  if (linkage_names1.empty())
1696  {
1697  out << indent << ff->get_pretty_representation()
1698  << " didn't have any linkage name, and it now has: '"
1699  << linkage_names2 << "'\n";
1700  }
1701  else if (linkage_names2.empty())
1702  {
1703  out << indent << ff->get_pretty_representation()
1704  << " did have linkage names '" << linkage_names1
1705  << "'\n"
1706  << indent << "but it doesn't have any linkage name anymore\n";
1707  }
1708  else
1709  out << indent << "linkage names of "
1710  << ff->get_pretty_representation()
1711  << "\n" << indent << "changed from '"
1712  << linkage_names1 << "' to '" << linkage_names2 << "'\n";
1713  }
1714 
1715  if (qn1 != qn2
1716  && d.type_diff()
1717  && d.type_diff()->to_be_reported())
1718  {
1719  // So the function has sub-type changes that are to be
1720  // reported. Let's see if the function name changed too; if it
1721  // did, then we'd report that change right before reporting the
1722  // sub-type changes.
1723  string frep1 = d.first_function_decl()->get_pretty_representation(),
1724  frep2 = d.second_function_decl()->get_pretty_representation();
1725  out << indent << "'" << frep1 << " {" << linkage_names1<< "}"
1726  << "' now becomes '"
1727  << frep2 << " {" << linkage_names2 << "}" << "'\n";
1728  }
1729 
1730  maybe_report_diff_for_symbol(ff->get_symbol(),
1731  sf->get_symbol(),
1732  d.context(), out, indent);
1733 
1734  // Now report about inline-ness changes
1735  if (ff->is_declared_inline() != sf->is_declared_inline())
1736  {
1737  out << indent;
1738  if (ff->is_declared_inline())
1739  out << sf->get_pretty_representation()
1740  << " is not declared inline anymore\n";
1741  else
1742  out << sf->get_pretty_representation()
1743  << " is now declared inline\n";
1744  }
1745 
1746  // Report about vtable offset changes.
1747  if (is_member_function(ff) && is_member_function(sf))
1748  {
1749  bool ff_is_virtual = get_member_function_is_virtual(ff),
1750  sf_is_virtual = get_member_function_is_virtual(sf);
1751  if (ff_is_virtual != sf_is_virtual)
1752  {
1753  out << indent;
1754  if (ff_is_virtual)
1755  out << ff->get_pretty_representation()
1756  << " is no more declared virtual\n";
1757  else
1758  out << ff->get_pretty_representation()
1759  << " is now declared virtual\n";
1760  }
1761 
1762  size_t ff_vtable_offset = get_member_function_vtable_offset(ff),
1763  sf_vtable_offset = get_member_function_vtable_offset(sf);
1764  if (ff_is_virtual && sf_is_virtual
1765  && (ff_vtable_offset != sf_vtable_offset))
1766  {
1767  out << indent
1768  << "the vtable offset of " << ff->get_pretty_representation()
1769  << " changed from " << ff_vtable_offset
1770  << " to " << sf_vtable_offset << "\n";
1771  }
1772 
1773  // the parent types (classe or union) of the two member
1774  // functions.
1775  class_or_union_sptr f =
1776  is_class_or_union_type(is_method_type(ff->get_type())->get_class_type());
1777  class_or_union_sptr s =
1778  is_class_or_union_type(is_method_type(sf->get_type())->get_class_type());
1779 
1782 
1783  // Detect if the virtual member function changes above
1784  // introduced a vtable change or not.
1785  bool vtable_added = false, vtable_removed = false;
1786  if (!f->get_is_declaration_only() && !s->get_is_declaration_only())
1787  {
1788  if (fc && sc)
1789  {
1790  vtable_added = !fc->has_vtable() && sc->has_vtable();
1791  vtable_removed = fc->has_vtable() && !sc->has_vtable();
1792  }
1793  }
1794  bool vtable_changed = ((ff_is_virtual != sf_is_virtual)
1795  || (ff_vtable_offset != sf_vtable_offset));
1796  bool incompatible_change = (ff_vtable_offset != sf_vtable_offset);
1797 
1798  if (vtable_added)
1799  out << indent
1800  << " note that a vtable was added to "
1801  << fc->get_pretty_representation()
1802  << "\n";
1803  else if (vtable_removed)
1804  out << indent
1805  << " note that the vtable was removed from "
1806  << fc->get_pretty_representation()
1807  << "\n";
1808  else if (vtable_changed)
1809  {
1810  out << indent;
1811  if (incompatible_change)
1812  out << " note that this is an ABI incompatible "
1813  "change to the vtable of ";
1814  else
1815  out << " note that this induces a change to the vtable of ";
1816  out << fc->get_pretty_representation()
1817  << "\n";
1818  }
1819 
1820  }
1821  }
1822 
1823  // Report about function type differences.
1824  if (d.type_diff() && d.type_diff()->to_be_reported())
1825  d.type_diff()->report(out, indent);
1826 }
1827 
1828 /// Report the changes carried by a @ref var_diff node in a serialized
1829 /// form.
1830 ///
1831 /// @param d the @ref var_diff node to consider.
1832 ///
1833 /// @param out the stream to serialize the diff to.
1834 ///
1835 /// @param indent the prefix to use for the indentation of this
1836 /// serialization.
1837 void
1838 default_reporter::report(const var_diff& d, ostream& out,
1839  const string& indent) const
1840 {
1841  if (!d.to_be_reported())
1842  return;
1843 
1844  decl_base_sptr first = d.first_var(), second = d.second_var();
1845  string n = first->get_pretty_representation();
1846 
1847  if (!d.is_filtered_out_without_looking_at_allowed_changes())
1848  {
1850  d.context(),
1851  out, indent);
1852 
1853  maybe_report_diff_for_symbol(d.first_var()->get_symbol(),
1854  d.second_var()->get_symbol(),
1855  d.context(), out, indent);
1856 
1857  maybe_report_diff_for_member(first, second, d.context(), out, indent);
1858 
1859  maybe_report_diff_for_variable(first, second, d.context(), out, indent);
1860  }
1861 
1862  if (diff_sptr dif = d.type_diff())
1863  {
1864  if (dif->to_be_reported())
1865  {
1866  RETURN_IF_BEING_REPORTED_OR_WAS_REPORTED_EARLIER2(dif, "type");
1867  out << indent << "type of variable changed:\n";
1868  dif->report(out, indent + " ");
1869  }
1870  }
1871 }
1872 
1873 /// Report the changes carried by a @ref translation_unit_diff node in
1874 /// a serialized form.
1875 ///
1876 /// @param d the @ref translation_unit_diff node to consider.
1877 ///
1878 /// @param out the output stream to serialize the report to.
1879 ///
1880 /// @param indent the prefix to use as indentation for the report.
1881 void
1882 default_reporter::report(const translation_unit_diff& d,
1883  ostream& out,
1884  const string& indent) const
1885 {
1886  static_cast<const scope_diff&>(d).report(out, indent);
1887 }
1888 
1889 /// Report the changes carried by a @ref corpus_diff node in a
1890 /// serialized form.
1891 ///
1892 /// @param d the @ref corpus_diff node to consider.
1893 ///
1894 /// @param out the output stream to serialize the report to.
1895 ///
1896 /// @param indent the prefix to use as indentation for the report.
1897 void
1898 default_reporter::report(const corpus_diff& d, ostream& out,
1899  const string& indent) const
1900 {
1901  const corpus_diff::diff_stats &s =
1902  const_cast<corpus_diff&>(d).
1903  apply_filters_and_suppressions_before_reporting();
1904 
1905  const diff_context_sptr& ctxt = d.context();
1906 
1907  d.priv_->emit_diff_stats(s, out, indent);
1908  if (ctxt->show_stats_only())
1909  return;
1910  out << "\n";
1911 
1912  if (ctxt->show_soname_change()
1913  && !d.priv_->sonames_equal_)
1914  out << indent << "SONAME changed from '"
1915  << d.first_corpus()->get_soname() << "' to '"
1916  << d.second_corpus()->get_soname() << "'\n\n";
1917 
1918  if (ctxt->show_architecture_change()
1919  && !d.priv_->architectures_equal_)
1920  out << indent << "architecture changed from '"
1921  << d.first_corpus()->get_architecture_name() << "' to '"
1922  << d.second_corpus()->get_architecture_name() << "'\n\n";
1923 
1924  /// Report removed/added/changed functions.
1925  if (ctxt->show_deleted_fns())
1926  {
1927  if (s.net_num_func_removed() == 1)
1928  out << indent << "1 Removed function:\n\n";
1929  else if (s.net_num_func_removed() > 1)
1930  out << indent << s.net_num_func_removed() << " Removed functions:\n\n";
1931 
1932  bool emitted = false;
1933  corpus::functions sorted_deleted_fns;
1934  sort_string_function_ptr_map(d.priv_->deleted_fns_, sorted_deleted_fns);
1935  for (auto f : sorted_deleted_fns)
1936  {
1937  if (d.priv_->deleted_function_is_suppressed(f))
1938  continue;
1939 
1940  out << indent
1941  << " ";
1942  out << "[D] ";
1943  out << "'" << (f)->get_pretty_representation() << "'";
1944  if (ctxt->show_linkage_names())
1945  {
1946  out << " {";
1947  show_linkage_name_and_aliases(out, "", *(f)->get_symbol(),
1948  d.first_corpus()->get_fun_symbol_map());
1949  out << "}";
1950  }
1951  out << "\n";
1953  {
1954  class_decl_sptr c =
1955  is_class_type(is_method_type(f->get_type())->get_class_type());
1956  out << indent
1957  << " "
1958  << "note that this removes an entry from the vtable of "
1959  << c->get_pretty_representation()
1960  << "\n";
1961  }
1962  emitted = true;
1963  }
1964  if (emitted)
1965  out << "\n";
1966  }
1967 
1968  if (size_t num_changed = s.num_func_with_incompatible_changes())
1969  {
1970  if (num_changed == 1)
1971  out << indent << "1 function with incompatible sub-type changes:\n\n";
1972  else if (num_changed > 1)
1973  out << indent << num_changed
1974  << " functions with incompatible sub-type changes:\n\n";
1975 
1976  sort_function_decl_diffs(const_cast<corpus_diff&>(d).
1977  incompatible_changed_functions());
1978  for (auto& fn_diff : d.incompatible_changed_functions())
1979  if (fn_diff)
1980  emit_changed_fn_report(ctxt, fn_diff, out, indent);
1981  }
1982 
1983  if (ctxt->show_added_fns())
1984  {
1985  if (s.net_num_func_added() == 1)
1986  out << indent << "1 Added function:\n\n";
1987  else if (s.net_num_func_added() > 1)
1988  out << indent << s.net_num_func_added()
1989  << " Added functions:\n\n";
1990  bool emitted = false;
1991  corpus::functions sorted_added_fns;
1992  sort_string_function_ptr_map(d.priv_->added_fns_, sorted_added_fns);
1993  for (auto f : sorted_added_fns)
1994  {
1995  if (d.priv_->added_function_is_suppressed(f))
1996  continue;
1997 
1998  out
1999  << indent
2000  << " ";
2001  out << "[A] ";
2002  out << "'"
2003  << f->get_pretty_representation()
2004  << "'";
2005  if (ctxt->show_linkage_names())
2006  {
2007  out << " {";
2009  (out, "", *f->get_symbol(),
2010  d.second_corpus()->get_fun_symbol_map());
2011  out << "}";
2012  }
2013  out << "\n";
2015  {
2016  class_decl_sptr c =
2017  is_class_type(is_method_type(f->get_type())->get_class_type());
2018  out << indent
2019  << " "
2020  << "note that this adds a new entry to the vtable of "
2021  << c->get_pretty_representation()
2022  << "\n";
2023  }
2024  emitted = true;
2025  }
2026  if (emitted)
2027  out << "\n";
2028  }
2029 
2030  if (ctxt->show_changed_fns())
2031  if (size_t num_changed = s.net_num_non_incompatible_func_changed())
2032  {
2033  if (num_changed == 1)
2034  out << indent << "1 function with some indirect sub-type change:\n\n";
2035  else if (num_changed > 1)
2036  out << indent << num_changed
2037  << " functions with some indirect sub-type change:\n\n";
2038 
2039  vector<function_decl_diff_sptr> sorted_changed_fns;
2040  sort_string_function_decl_diff_sptr_map(d.priv_->changed_fns_map_,
2041  sorted_changed_fns);
2042  for (auto& fn_diff : sorted_changed_fns)
2043  if (fn_diff && !filtering::has_incompatible_fn_or_var_change(fn_diff))
2044  emit_changed_fn_report(ctxt, fn_diff, out, indent,
2045  /*indirect_changed_subtypes=*/true,
2046  /*emit_redundant_fns=*/false);
2047  // Changed functions have extra spacing already. No new line here.
2048  }
2049 
2050  // Report removed/added/changed variables.
2051  if (ctxt->show_deleted_vars())
2052  {
2053  if (s.net_num_vars_removed() == 1)
2054  out << indent << "1 Removed variable:\n\n";
2055  else if (s.net_num_vars_removed() > 1)
2056  out << indent << s.net_num_vars_removed()
2057  << " Removed variables:\n\n";
2058  string n;
2059  bool emitted = false;
2060  corpus::variables sorted_deleted_vars;
2061  sort_string_var_ptr_map(d.priv_->deleted_vars_, sorted_deleted_vars);
2062  for (auto v : sorted_deleted_vars)
2063  {
2064  if (d.priv_->deleted_variable_is_suppressed(v))
2065  continue;
2066 
2067  n = v->get_pretty_representation();
2068 
2069  out << indent
2070  << " ";
2071  out << "[D] ";
2072  out << "'"
2073  << n
2074  << "'";
2075  if (ctxt->show_linkage_names())
2076  {
2077  out << " {";
2078  show_linkage_name_and_aliases(out, "", *v->get_symbol(),
2079  d.first_corpus()->get_var_symbol_map());
2080  out << "}";
2081  }
2082  out << "\n";
2083  emitted = true;
2084  }
2085  if (emitted)
2086  out << "\n";
2087  }
2088 
2089  if (size_t num_changed = s.num_var_with_incompatible_changes())
2090  {
2091  if (num_changed == 1)
2092  out << indent << "1 variable with incompatible sub-type changes:\n\n";
2093  else if (num_changed > 1)
2094  out << indent << num_changed
2095  << " variables with incompatible sub-type changes:\n\n";
2096 
2097  sort_var_diffs(const_cast<corpus_diff&>(d).
2098  incompatible_changed_variables());
2099  for (auto& var_diff : d.incompatible_changed_variables())
2100  if (var_diff)
2101  emit_changed_var_report(ctxt, var_diff, out, indent);
2102  }
2103 
2104  if (ctxt->show_added_vars())
2105  {
2106  if (s.net_num_vars_added() == 1)
2107  out << indent << "1 Added variable:\n\n";
2108  else if (s.net_num_vars_added() > 1)
2109  out << indent << s.net_num_vars_added()
2110  << " Added variables:\n\n";
2111  string n;
2112  bool emitted = false;
2113  corpus::variables sorted_added_vars;
2114  sort_string_var_ptr_map(d.priv_->added_vars_, sorted_added_vars);
2115  for (auto v : sorted_added_vars)
2116  {
2117  if (d.priv_->added_variable_is_suppressed(v))
2118  continue;
2119 
2120  n = v->get_pretty_representation();
2121 
2122  out << indent
2123  << " ";
2124  out << "[A] ";
2125  out << "'" << n << "'";
2126  if (ctxt->show_linkage_names())
2127  {
2128  out << " {";
2129  show_linkage_name_and_aliases(out, "", *v->get_symbol(),
2130  d.second_corpus()->get_var_symbol_map());
2131  out << "}";
2132  }
2133  out << "\n";
2134  emitted = true;
2135  }
2136  if (emitted)
2137  out << "\n";
2138  }
2139 
2140  if (ctxt->show_changed_vars())
2141  if (size_t num_changed = s.net_num_non_incompatible_var_changed())
2142  {
2143  if (num_changed == 1)
2144  out << indent << "1 Changed variable:\n\n";
2145  else if (num_changed > 1)
2146  out << indent << num_changed
2147  << " Changed variables:\n\n";
2148  string n1, n2;
2149 
2150  for (auto& var_diff : d.priv_->sorted_changed_vars_)
2151  if (var_diff
2153  emit_changed_var_report(ctxt, var_diff, out, indent);
2154  // Changed variables have extra spacing already. No new line here.
2155  }
2156 
2157  // Report removed function symbols not referenced by any debug info.
2158  if (ctxt->show_symbols_unreferenced_by_debug_info()
2159  && d.priv_->deleted_unrefed_fn_syms_.size())
2160  {
2161  if (s.net_num_removed_func_syms() == 1)
2162  out << indent
2163  << "1 Removed function symbol not referenced by debug info:\n\n";
2164  else if (s.net_num_removed_func_syms() > 0)
2165  out << indent
2166  << s.net_num_removed_func_syms()
2167  << " Removed function symbols not referenced by debug info:\n\n";
2168 
2169  bool emitted = false;
2170  vector<elf_symbol_sptr> sorted_deleted_unrefed_fn_syms;
2171  sort_string_elf_symbol_map(d.priv_->deleted_unrefed_fn_syms_,
2172  sorted_deleted_unrefed_fn_syms);
2173  for (vector<elf_symbol_sptr>::const_iterator i =
2174  sorted_deleted_unrefed_fn_syms.begin();
2175  i != sorted_deleted_unrefed_fn_syms.end();
2176  ++i)
2177  {
2178  if (d.priv_->deleted_unrefed_fn_sym_is_suppressed((*i).get()))
2179  continue;
2180 
2181  out << indent << " ";
2182  out << "[D] ";
2183 
2184  show_linkage_name_and_aliases(out, "", **i,
2185  d.first_corpus()->get_fun_symbol_map());
2186  out << "\n";
2187  emitted = true;
2188  }
2189  if (emitted)
2190  out << "\n";
2191  }
2192 
2193  // Report added function symbols not referenced by any debug info.
2194  if (ctxt->show_symbols_unreferenced_by_debug_info()
2195  && ctxt->show_added_symbols_unreferenced_by_debug_info()
2196  && d.priv_->added_unrefed_fn_syms_.size())
2197  {
2198  if (s.net_num_added_func_syms() == 1)
2199  out << indent
2200  << "1 Added function symbol not referenced by debug info:\n\n";
2201  else if (s.net_num_added_func_syms() > 0)
2202  out << indent
2203  << s.net_num_added_func_syms()
2204  << " Added function symbols not referenced by debug info:\n\n";
2205 
2206  bool emitted = false;
2207  vector<elf_symbol_sptr> sorted_added_unrefed_fn_syms;
2208  sort_string_elf_symbol_map(d.priv_->added_unrefed_fn_syms_,
2209  sorted_added_unrefed_fn_syms);
2210  for (vector<elf_symbol_sptr>::const_iterator i =
2211  sorted_added_unrefed_fn_syms.begin();
2212  i != sorted_added_unrefed_fn_syms.end();
2213  ++i)
2214  {
2215  if (d.priv_->added_unrefed_fn_sym_is_suppressed((*i).get()))
2216  continue;
2217 
2218  out << indent << " ";
2219  out << "[A] ";
2221  **i,
2222  d.second_corpus()->get_fun_symbol_map());
2223  out << "\n";
2224  emitted = true;
2225  }
2226  if (emitted)
2227  out << "\n";
2228  }
2229 
2230  // Report removed variable symbols not referenced by any debug info.
2231  if (ctxt->show_symbols_unreferenced_by_debug_info()
2232  && d.priv_->deleted_unrefed_var_syms_.size())
2233  {
2234  if (s.net_num_removed_var_syms() == 1)
2235  out << indent
2236  << "1 Removed variable symbol not referenced by debug info:\n\n";
2237  else if (s.net_num_removed_var_syms() > 0)
2238  out << indent
2239  << s.net_num_removed_var_syms()
2240  << " Removed variable symbols not referenced by debug info:\n\n";
2241 
2242  bool emitted = false;
2243  vector<elf_symbol_sptr> sorted_deleted_unrefed_var_syms;
2244  sort_string_elf_symbol_map(d.priv_->deleted_unrefed_var_syms_,
2245  sorted_deleted_unrefed_var_syms);
2246  for (vector<elf_symbol_sptr>::const_iterator i =
2247  sorted_deleted_unrefed_var_syms.begin();
2248  i != sorted_deleted_unrefed_var_syms.end();
2249  ++i)
2250  {
2251  if (d.priv_->deleted_unrefed_var_sym_is_suppressed((*i).get()))
2252  continue;
2253 
2254  out << indent << " ";
2255  out << "[D] ";
2256 
2258  (out, "", **i,
2259  d.first_corpus()->get_fun_symbol_map());
2260 
2261  out << "\n";
2262  emitted = true;
2263  }
2264  if (emitted)
2265  out << "\n";
2266  }
2267 
2268  // Report added variable symbols not referenced by any debug info.
2269  if (ctxt->show_symbols_unreferenced_by_debug_info()
2270  && ctxt->show_added_symbols_unreferenced_by_debug_info()
2271  && d.priv_->added_unrefed_var_syms_.size())
2272  {
2273  if (s.net_num_added_var_syms() == 1)
2274  out << indent
2275  << "1 Added variable symbol not referenced by debug info:\n\n";
2276  else if (s.net_num_added_var_syms() > 0)
2277  out << indent
2278  << s.net_num_added_var_syms()
2279  << " Added variable symbols not referenced by debug info:\n\n";
2280 
2281  bool emitted = false;
2282  vector<elf_symbol_sptr> sorted_added_unrefed_var_syms;
2283  sort_string_elf_symbol_map(d.priv_->added_unrefed_var_syms_,
2284  sorted_added_unrefed_var_syms);
2285  for (vector<elf_symbol_sptr>::const_iterator i =
2286  sorted_added_unrefed_var_syms.begin();
2287  i != sorted_added_unrefed_var_syms.end();
2288  ++i)
2289  {
2290  if (d.priv_->added_unrefed_var_sym_is_suppressed((*i).get()))
2291  continue;
2292 
2293  out << indent << " ";
2294  out << "[A] ";
2295  show_linkage_name_and_aliases(out, "", **i,
2296  d.second_corpus()->get_fun_symbol_map());
2297  out << "\n";
2298  emitted = true;
2299  }
2300  if (emitted)
2301  out << "\n";
2302  }
2303 
2304  // Report added/removed/changed types not reacheable from public
2305  // interfaces.
2306  maybe_report_unreachable_type_changes(d, s, indent, out);
2307 
2308  d.priv_->maybe_dump_diff_tree();
2309 }
2310 
2311 } // end namespace comparison
2312 }// end namespace libabigail
Testing (anding) against this mask means that a given IR artifact has local differences, with respect to the other artifact it was compared against. A local change is a change that is carried by the artifact itself (or its type), rather than by one off its sub-types.
Definition: abg-ir.h:1376
bool is_filtered_out_without_looking_at_allowed_changes() const
Test if this diff tree node is to be filtered out for reporting purposes, but without considering the...
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 get_member_function_is_virtual(const function_decl &f)
Test if a given member function is virtual.
Definition: abg-ir.cc:6641
void sort_enumerators(const string_enumerator_map &enumerators_map, enum_type_decl::enumerators &sorted)
Sort a map of enumerators by their value.
ptr_to_mbr_type_sptr second_ptr_to_mbr_type() const
Getter of the second pointer-to-member subject of the current diff node.
void report_local_reference_type_changes(const reference_diff &d, std::ostream &out, const std::string &indent) const
For a node, report the local changes carried by the diff node.
Abstraction of a diff between two qualified types.
size_t net_num_func_removed() const
Getter for the net number of function removed.
shared_ptr< class_diff > class_diff_sptr
Convenience typedef for a shared pointer on a class_diff type.
size_t net_num_vars_changed() const
Getter for the number of variables that have a change in their sub-types, minus the number of these v...
This means that a given IR artifact has changes in some of its sub-types, with respect to the other a...
Definition: abg-ir.h:1381
bool type_has_sub_type_changes(const type_base_sptr t_v1, const type_base_sptr t_v2)
Tests if the change of a given type effectively comes from just its sub-types. That is...
Definition: abg-ir.cc:28457
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_class_type(const type_or_decl_base &t)
Test whether a type is a class.
Definition: abg-ir.cc:11165
const vector< subrange_diff_sptr > & subrange_diffs() const
Getter for the diffs between the array subranges.
shared_ptr< function_type > function_type_sptr
Convenience typedef for a shared pointer on a function_type.
Definition: abg-fwd.h:208
bool is_member_function(const function_decl &f)
Test whether a function_decl is a member function.
Definition: abg-ir.cc:6368
void sort_var_diffs(var_diff_sptrs_type &var_diffs)
Sort a vector of var_diff_sptr.
bool report_local_qualified_type_changes(const qualified_type_diff &d, std::ostream &out, const std::string &indent) const
For a qualified_type_diff node, report the changes that are local.
const qualified_type_def_sptr first_qualified_type() const
Getter for the first qualified type of the diff.
const function_decl::parameter_sptr second_parameter() const
Getter for the second subject of this diff node.
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...
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.
void sort_function_decl_diffs(function_decl_diff_sptrs_type &fn_diffs)
Sort a vector of function_decl_diff_sptr.
const array_type_def_sptr second_array() const
Getter for the second array of the diff.
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 ...
This means that a diff node was marked as suppressed by a user-provided suppression specification...
const typedef_decl_sptr first_typedef_decl() const
Getter for the firt typedef_decl involved in the diff.
size_t net_num_vars_added() const
Getter for the net number of added variables.
shared_ptr< diff_context > diff_context_sptr
Convenience typedef for a shared pointer of diff_context.
Definition: abg-fwd.h:67
Abstraction of a diff between two typedef_decl.
ssize_t get_member_function_vtable_offset(const function_decl &f)
Get the vtable offset of a member function.
Definition: abg-ir.cc:6578
void sort_data_members(const string_decl_base_sptr_map &data_members, vector< decl_base_sptr > &sorted)
Sort a map of data members by the offset of their initial value.
This means the diff node (or at least one of its descendant nodes) carries access related changes...
const type_decl_sptr first_type_decl() const
Getter for the first subject of the type_decl_diff.
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
string get_class_or_union_flat_representation(const class_or_union &cou, const string &indent, bool one_line, bool internal, bool qualified_names)
Get the flat representation of an instance of class_or_union type.
Definition: abg-ir.cc:9508
void sort_string_var_ptr_map(const string_var_ptr_map &map, vector< var_decl_sptr > &sorted)
Sort a map of string -> pointer to var_decl.
The abstraction of a diff between two arrays.
size_t net_num_added_func_syms() const
Getter of the net number of added function symbols that are not referenced by any debug info...
size_t net_num_func_added() const
Getter for the net number of added functions.
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
shared_ptr< typedef_decl > typedef_decl_sptr
Convenience typedef for a shared pointer on a typedef_decl.
Definition: abg-fwd.h:164
change_kind
A bitfield that gives callers of abigail::ir::equals() some insight about how different two internal ...
Definition: abg-ir.h:1360
bool types_have_similar_structure(const type_base_sptr &first, const type_base_sptr &second, bool indirect_type)
Test if two types have similar structures, even though they are (or can be) different.
Definition: abg-ir.cc:28849
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.
void report_local_function_type_changes(const function_type_diff &d, std::ostream &out, const std::string &indent) const
For a function_type_diff node, report the local changes carried by the diff node. ...
void sort_string_elf_symbol_map(const string_elf_symbol_map &map, vector< elf_symbol_sptr > &sorted)
Sort a map of string -> pointer to elf_symbol.
Abstraction of a diff between two basic type declarations.
bool soname_changed() const
Test if the soname of the underlying corpus has changed.
void report_underlying_changes_of_qualified_type(const qualified_type_diff &d, ostream &out, const string &indent) const
For a qualified_type_diff node, report the changes of its underlying type.
size_t net_num_removed_func_syms() const
Getter of the net number of removed function symbols that are not referenced by any debug info...
std::vector< enumerator > enumerators
Convenience typedef for a list of enumerator.
Definition: abg-ir.h:2798
The abstraction of the diff between two subrange types.
vector< var_decl_sptr > variables
Convenience typedef for std::vector
Definition: abg-corpus.h:37
An abstraction of a diff between between two abi corpus.
vector< changed_enumerator > changed_enumerators_type
Convenience typedef for a vector of changed enumerators.
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...
diff_category
An enum for the different categories that a diff tree node falls into, regarding the kind of changes ...
bool report_local_ptr_to_mbr_type_changes(const ptr_to_mbr_diff &d, std::ostream &out, const std::string &indent="") const
Report the local changes carried by a ptr_to_mbr_diff diff node.
Toplevel namespace for libabigail.
shared_ptr< ptr_to_mbr_type > ptr_to_mbr_type_sptr
Convenience typedef for a shared pointer to a ptr_to_mbr_type.
Definition: abg-fwd.h:237
shared_ptr< function_decl > function_decl_sptr
Convenience typedef for a shared pointer on a function_decl.
Definition: abg-fwd.h:266
bool reported_once() const
Tests if a report has already been emitted for the current diff.
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.
The declaration of the reporting types of libabigail's diff engine.
shared_ptr< parameter > parameter_sptr
Convenience typedef for a shared pointer on a function_decl::parameter.
Definition: abg-ir.h:3183
size_t net_num_vars_removed() const
Getter for the net number of removed variables.
shared_ptr< base_spec > base_spec_sptr
Convenience typedef.
Definition: abg-ir.h:4188
This means that a diff node was warked as being for a private type. That is, the diff node is meant t...
Abstraction of a diff between two function parameters.
const function_decl::parameter_sptr first_parameter() const
Getter for the first subject of this diff node.
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.
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...
diff_sptr leaf_underlying_type_diff() const
Getter for the diff between the most underlying non-qualified types of two qualified types...
size_t net_num_removed_var_syms() const
Getter of the net number of removed variable symbols that are not referenced by any debug info...
The abstraction of a diff between two ptr_to_mbr_type.
A diff node in this category is redundant. That means it's present as a child of a other nodes in the...
The abstraction of a diff between two references.
const decl_base * get_type_declaration(const type_base *t)
Get the declaration for a given type.
Definition: abg-ir.cc:10229
This means that a diff node in the sub-tree carries a type that was declaration-only and that is now ...
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
shared_ptr< type_decl > type_decl_sptr
Convenience typedef for a shared pointer on a type_decl.
Definition: abg-fwd.h:159
ptr_to_mbr_type_sptr first_ptr_to_mbr_type() const
Getter of the first pointer-to-member subject of the current diff node.
The abstraction of a diff between two pointers.
size_t net_num_added_var_syms() const
Getter of the net number of added variable symbols that are not referenced by any debug info...
bool report_loc_info(const type_or_decl_base_sptr &tod, const diff_context &ctxt, ostream &out)
const diff_sptr containing_type_diff() const
Getter of the diff node carrying changes to the containing type of first subject of the current diff ...
shared_ptr< reference_type_def > reference_type_def_sptr
Convenience typedef for a shared pointer on a reference_type_def.
Definition: abg-fwd.h:232
shared_ptr< elf_symbol > elf_symbol_sptr
A convenience typedef for a shared pointer to elf_symbol.
Definition: abg-ir.h:924
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...
#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
virtual bool diff_has_net_changes(const corpus_diff *d) const
Test if a given instance of corpus_diff carries changes whose reports are not suppressed by any suppr...
bool union_diff_has_harmless_changes(const diff *d)
Test if a union diff node does have changes that don't impact its size.
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.
type_base_sptr strip_typedef(const type_base_sptr type)
Recursively returns the the underlying type of a typedef. The return type should not be a typedef of ...
Definition: abg-ir.cc:6780
diff_sptr underlying_type_diff() const
Getter for the diff between the pointed-to types of the pointers of this diff.
diff_sptr type_diff() const
Getter for the diff representing the changes on the type of the function parameter involved in the cu...
size_t net_num_func_changed() const
Getter for the number of functions that have a change in their sub-types, minus the number of these f...
bool has_incompatible_fn_or_var_change(const diff *d)
Test if a diff node carries an incompatible ABI change.
const diff_sptr & element_type_diff() const
Getter for the diff between the two types of array elements.
const type_decl_sptr second_type_decl() const
Getter for the second subject of the type_decl_diff.
const array_type_def::subrange_sptr second_subrange() const
Getter of the second subrange of the current instance subrange_diff.
const function_type_sptr second_function_type() const
Getter for the second subject of the diff.
shared_ptr< enum_type_decl > enum_type_decl_sptr
Convenience typedef for shared pointer to a enum_type_decl.
Definition: abg-fwd.h:172
shared_ptr< base_diff > base_diff_sptr
Convenience typedef for a shared pointer to a base_diff type.
void sort_changed_enumerators(const string_changed_enumerator_map &enumerators_map, changed_enumerators_type &sorted)
Sort a map of changed enumerators.
shared_ptr< class_decl > class_decl_sptr
Convenience typedef for a shared pointer on a class_decl.
Definition: abg-fwd.h:190
bool to_be_reported() const
Test if this diff tree node should be reported.
const function_type_sptr first_function_type() const
Getter for the first subject of the diff.
const typedef_decl_sptr second_typedef_decl() const
Getter for the second typedef_decl involved in the diff.
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
This means that a diff node in the sub-tree carries an addition or removal of a non-virtual member fu...
reference_type_def_sptr second_reference() const
Getter for the second reference of the diff.
The private data and functions of the abigail::ir::comparison types.
void report_non_type_typedef_changes(const typedef_diff &d, std::ostream &out, const std::string &indent) const
For a typedef_diff node, report the local changes to the typedef rather the changes to its underlying...
void sort_string_function_decl_diff_sptr_map(const string_function_decl_diff_sptr_map &map, function_decl_diff_sptrs_type &sorted)
Sort the values of a string_function_decl_diff_sptr_map map and store the result in a vector of funct...
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...
const qualified_type_def_sptr second_qualified_type() const
Getter for the second qualified type of the diff.
reference_type_def_sptr first_reference() const
Getter for the first reference of the 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.
virtual void report(ostream &, const string &indent="") const
Report about the changes carried by this node.
string get_pretty_representation(const type_or_decl_base *tod, bool internal)
Build and return a copy of the pretty representation of an ABI artifact that could be either a type o...
Definition: abg-ir.cc:9286
This means that a diff node in the sub-tree carries a harmless union or class change.
const diff_sptr & underlying_type_diff() const
Getter for the diff between the two referred-to types.
bool equals(const decl_base &l, const decl_base &r, change_kind *k)
Compares two instances of decl_base.
Definition: abg-ir.cc:5138
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 diff_sptr underlying_type_diff() const
Getter for the diff between the two underlying types of the typedefs.
This is a document class that aims to capture statistics about the changes carried by a corpus_diff t...
void sort_string_function_ptr_map(const string_function_ptr_map &map, vector< const function_decl * > &sorted)
Sort an instance of string_function_ptr_map map and stuff a resulting sorted vector of pointers to fu...
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_enum_decl_only_def_change(const enum_type_decl_sptr &first, const enum_type_decl_sptr &second)
Test if two enum_sptr are different just by the fact that one is decl-only and the other one is defin...
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.
bool any_subrange_diff_to_be_reported() const
Test if any subrange diff is to be reported.
bool architecture_changed() const
Test if the architecture of the underlying corpus has changed.
method_type_sptr is_method_type(const type_or_decl_base_sptr &t)
Test whether a type is a method_type.
Definition: abg-ir.cc:11888
Abstraction of a diff between two function types.
string get_pretty_representation(diff *d)
Get a copy of the pretty representation of a diff node.
const array_type_def_sptr first_array() const
Getter for the first array of the diff.
const diff_context_sptr context() const
Getter of the context of the current diff.
const diff_sptr member_type_diff() const
Getter of the diff node carrying changes to the member type of first subject of the current diff node...
vector< const function_decl * > functions
Convenience typedef for std::vector
Definition: abg-corpus.h:31