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