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