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 /// Emit a textual report about the a @ref fn_parm_diff instance.
526 ///
527 /// @param d the @ref fn_parm_diff to consider.
528 ///
529 /// @param out the output stream to emit the textual report to.
530 ///
531 /// @param indent the indentation string to use in the report.
532 void
533 default_reporter::report(const fn_parm_diff& d, ostream& out,
534  const string& indent) const
535 {
536  if (!d.to_be_reported())
537  return;
538 
539  function_decl::parameter_sptr f = d.first_parameter(),
540  s = d.second_parameter();
541 
542  // either the parameter has a sub-type change (if its type name
543  // hasn't changed) or it has a "grey" change (that is, a change that
544  // changes his type name w/o changing the signature of the
545  // function).
546  bool has_sub_type_change =
547  type_has_sub_type_changes(d.first_parameter()->get_type(),
548  d.second_parameter()->get_type());
549 
550  diff_sptr type_diff = d.type_diff();
551  ABG_ASSERT(type_diff->has_changes());
552 
553  out << indent;
554  if (f->get_is_artificial())
555  out << "implicit ";
556  out << "parameter " << f->get_index();
557  report_loc_info(f, *d.context(), out);
558  out << " of type '"
559  << f->get_type_pretty_representation();
560 
561  if (has_sub_type_change)
562  out << "' has sub-type changes:\n";
563  else
564  out << "' changed:\n";
565 
566  type_diff->report(out, indent + " ");
567 }
568 
569 /// For a @ref function_type_diff node, report the local changes
570 /// carried by the diff node.
571 ///
572 /// @param d the @ref function_type_diff node to consider.
573 ///
574 /// @param out the output stream to report to.
575 ///
576 /// @param indent the white space indentation string to use.
577 void
579  ostream& out,
580  const string& indent) const
581 
582 {
583  if (!d.to_be_reported())
584  return;
585 
588 
589  diff_context_sptr ctxt = d.context();
590 
591  // Report about the size of the function address
592  if (fft->get_size_in_bits() != sft->get_size_in_bits())
593  {
594  out << indent << "address size of function changed from "
595  << fft->get_size_in_bits()
596  << " bits to "
597  << sft->get_size_in_bits()
598  << " bits\n";
599  }
600 
601  // Report about the alignment of the function address
602  if (fft->get_alignment_in_bits()
603  != sft->get_alignment_in_bits())
604  {
605  out << indent << "address alignment of function changed from "
606  << fft->get_alignment_in_bits()
607  << " bits to "
608  << sft->get_alignment_in_bits()
609  << " bits\n";
610  }
611 
612  // Hmmh, the above was quick. Now report about function parameters;
613  // this shouldn't be as straightforward.
614 
615  // Report about the parameters that got removed.
616  for (vector<function_decl::parameter_sptr>::const_iterator i =
617  d.priv_->sorted_deleted_parms_.begin();
618  i != d.priv_->sorted_deleted_parms_.end();
619  ++i)
620  {
621  out << indent << "parameter " << (*i)->get_index()
622  << " of type '" << (*i)->get_type_pretty_representation()
623  << "' was removed\n";
624  }
625 
626  // Report about the parameters that got added
627  for (vector<function_decl::parameter_sptr>::const_iterator i =
628  d.priv_->sorted_added_parms_.begin();
629  i != d.priv_->sorted_added_parms_.end();
630  ++i)
631  {
632  out << indent << "parameter " << (*i)->get_index()
633  << " of type '" << (*i)->get_type_pretty_representation()
634  << "' was added\n";
635  }
636 }
637 
638 /// Build and emit a textual report about a @ref function_type_diff.
639 ///
640 /// @param d the @ref function_type_diff to consider.
641 ///
642 /// @param out the output stream.
643 ///
644 /// @param indent the indentation string to use.
645 void
646 default_reporter::report(const function_type_diff& d, ostream& out,
647  const string& indent) const
648 {
649  if (!d.to_be_reported())
650  return;
651 
654 
655  diff_context_sptr ctxt = d.context();
656  corpus_sptr fc = ctxt->get_first_corpus();
657  corpus_sptr sc = ctxt->get_second_corpus();
658 
659  // Report about return type differences.
660  if (d.priv_->return_type_diff_
661  && d.priv_->return_type_diff_->to_be_reported())
662  {
663  out << indent << "return type changed:\n";
664  d.priv_->return_type_diff_->report(out, indent + " ");
665  }
666 
667  // Report about the parameter types that have changed sub-types.
668  for (vector<fn_parm_diff_sptr>::const_iterator i =
669  d.priv_->sorted_subtype_changed_parms_.begin();
670  i != d.priv_->sorted_subtype_changed_parms_.end();
671  ++i)
672  {
673  diff_sptr dif = *i;
674  if (dif && dif->to_be_reported())
675  dif->report(out, indent);
676  }
677 
679  report_local_function_type_changes(d, out, indent);
680 }
681 
682 /// Report about the change carried by a @ref subrange_diff diff node
683 /// in a serialized form.
684 ///
685 /// @param d the diff node to consider.
686 ///
687 /// @param out the output stream to report to.
688 ///
689 /// @param indent the indentation string to use in the report.
690 void
691 default_reporter::report(const subrange_diff& d, std::ostream& out,
692  const std::string& indent) const
693 {
694  if (!diff_to_be_reported(&d))
695  return;
696 
697  RETURN_IF_BEING_REPORTED_OR_WAS_REPORTED_EARLIER3(d.first_subrange(),
698  d.second_subrange(),
699  "range type");
700 
701  represent(d, d.context(), out,indent, /*local_only=*/false);
702 }
703 
704 /// Report a @ref array_diff in a serialized form.
705 ///
706 /// @param d the @ref array_diff to consider.
707 ///
708 /// @param out the output stream to serialize the dif to.
709 ///
710 /// @param indent the string to use for indenting the report.
711 void
712 default_reporter::report(const array_diff& d, ostream& out,
713  const string& indent) const
714 {
715  if (!d.to_be_reported())
716  return;
717 
718  string name = d.first_array()->get_pretty_representation();
719  RETURN_IF_BEING_REPORTED_OR_WAS_REPORTED_EARLIER3(d.first_array(),
720  d.second_array(),
721  "array type");
722 
723  diff_sptr dif = d.element_type_diff();
724  if (dif->to_be_reported())
725  {
726  string fn = ir::get_pretty_representation(is_type(dif->first_subject()));
727  // report array element type changes
728  out << indent << "array element type '"
729  << fn << "' changed:\n";
730  dif->report(out, indent + " ");
731  }
732 
735  d.second_array(),
736  d.context(),
737  out, indent);
738 }
739 
740 /// Generates a report for an intance of @ref base_diff.
741 ///
742 /// @param d the @ref base_diff to consider.
743 ///
744 /// @param out the output stream to send the report to.
745 ///
746 /// @param indent the string to use for indentation.
747 void
748 default_reporter::report(const base_diff& d, ostream& out,
749  const string& indent) const
750 {
751  if (!d.to_be_reported())
752  return;
753 
754  class_decl::base_spec_sptr f = d.first_base(), s = d.second_base();
755  string repr = f->get_base_class()->get_pretty_representation();
756  bool emitted = false;
757 
758  if (!d.is_filtered_out_without_looking_at_allowed_changes())
759  {
760  if (f->get_is_static() != s->get_is_static())
761  {
762  if (f->get_is_static())
763  out << indent << "is no more static";
764  else
765  out << indent << "now becomes static";
766  emitted = true;
767  }
768 
769  if ((d.context()->get_allowed_category() & ACCESS_CHANGE_CATEGORY)
770  && (f->get_access_specifier() != s->get_access_specifier()))
771  {
772  if (emitted)
773  out << ", ";
774 
775  out << "has access changed from '"
776  << f->get_access_specifier()
777  << "' to '"
778  << s->get_access_specifier()
779  << "'";
780 
781  emitted = true;
782  }
783  }
784  if (class_diff_sptr dif = d.get_underlying_class_diff())
785  {
786  if (dif->to_be_reported())
787  {
788  if (emitted)
789  out << "\n";
790  dif->report(out, indent);
791  }
792  }
793 }
794 
795 /// Report the changes carried by a @ref scope_diff.
796 ///
797 /// @param d the @ref scope_diff to consider.
798 ///
799 /// @param out the out stream to report the changes to.
800 ///
801 /// @param indent the string to use for indentation.
802 void
803 default_reporter::report(const scope_diff& d, ostream& out,
804  const string& indent) const
805 {
806  if (!d.to_be_reported())
807  return;
808 
809  // Report changed types.
810  unsigned num_changed_types = d.changed_types().size();
811  if (num_changed_types == 0)
812  ;
813  else if (num_changed_types == 1)
814  out << indent << "1 changed type:\n";
815  else
816  out << indent << num_changed_types << " changed types:\n";
817 
818  for (diff_sptrs_type::const_iterator dif = d.changed_types().begin();
819  dif != d.changed_types().end();
820  ++dif)
821  {
822  if (!*dif)
823  continue;
824 
825  out << indent << " '"
826  << (*dif)->first_subject()->get_pretty_representation()
827  << "' changed:\n";
828  (*dif)->report(out, indent + " ");
829  }
830 
831  // Report changed decls
832  unsigned num_changed_decls = d.changed_decls().size();
833  if (num_changed_decls == 0)
834  ;
835  else if (num_changed_decls == 1)
836  out << indent << "1 changed declaration:\n";
837  else
838  out << indent << num_changed_decls << " changed declarations:\n";
839 
840  for (diff_sptrs_type::const_iterator dif= d.changed_decls().begin();
841  dif != d.changed_decls().end ();
842  ++dif)
843  {
844  if (!*dif)
845  continue;
846 
847  out << indent << " '"
848  << (*dif)->first_subject()->get_pretty_representation()
849  << "' was changed to '"
850  << (*dif)->second_subject()->get_pretty_representation() << "'";
851  report_loc_info((*dif)->second_subject(), *d.context(), out);
852  out << ":\n";
853 
854  (*dif)->report(out, indent + " ");
855  }
856 
857  // Report removed types/decls
858  for (string_decl_base_sptr_map::const_iterator i =
859  d.priv_->deleted_types_.begin();
860  i != d.priv_->deleted_types_.end();
861  ++i)
862  out << indent
863  << " '"
864  << i->second->get_pretty_representation()
865  << "' was removed\n";
866 
867  if (d.priv_->deleted_types_.size())
868  out << "\n";
869 
870  for (string_decl_base_sptr_map::const_iterator i =
871  d.priv_->deleted_decls_.begin();
872  i != d.priv_->deleted_decls_.end();
873  ++i)
874  out << indent
875  << " '"
876  << i->second->get_pretty_representation()
877  << "' was removed\n";
878 
879  if (d.priv_->deleted_decls_.size())
880  out << "\n";
881 
882  // Report added types/decls
883  bool emitted = false;
884  for (string_decl_base_sptr_map::const_iterator i =
885  d.priv_->inserted_types_.begin();
886  i != d.priv_->inserted_types_.end();
887  ++i)
888  {
889  // Do not report about type_decl as these are usually built-in
890  // types.
891  if (dynamic_pointer_cast<type_decl>(i->second))
892  continue;
893  out << indent
894  << " '"
895  << i->second->get_pretty_representation()
896  << "' was added\n";
897  emitted = true;
898  }
899 
900  if (emitted)
901  out << "\n";
902 
903  emitted = false;
904  for (string_decl_base_sptr_map::const_iterator i =
905  d.priv_->inserted_decls_.begin();
906  i != d.priv_->inserted_decls_.end();
907  ++i)
908  {
909  // Do not report about type_decl as these are usually built-in
910  // types.
911  if (dynamic_pointer_cast<type_decl>(i->second))
912  continue;
913  out << indent
914  << " '"
915  << i->second->get_pretty_representation()
916  << "' was added\n";
917  emitted = true;
918  }
919 
920  if (emitted)
921  out << "\n";
922 }
923 
924 /// Report the changes carried by a @ref class_or_union_diff node in a
925 /// textual format.
926 ///
927 /// @param d the @ref class_or_union_diff node to consider.
928 ///
929 /// @param out the output stream to write the textual report to.
930 ///
931 /// @param indent the number of white space to use as indentation.
932 void
933 default_reporter::report(const class_or_union_diff& d,
934  ostream& out,
935  const string& indent) const
936 {
937  if (!d.to_be_reported())
938  return;
939 
940  class_or_union_sptr first = d.first_class_or_union(),
941  second = d.second_class_or_union();
942 
943  const diff_context_sptr& ctxt = d.context();
944 
945  // Report class decl-only <-> definition change.
946  if (ctxt->get_allowed_category() & TYPE_DECL_ONLY_DEF_CHANGE_CATEGORY)
948  {
949  string was =
950  first->get_is_declaration_only()
951  ? " was a declaration-only type"
952  : " was a defined type";
953 
954  string is_now =
955  second->get_is_declaration_only()
956  ? " and is now a declaration-only type"
957  : " and is now a defined type";
958 
959  out << indent << "type " << first->get_pretty_representation()
960  << was << is_now << "\n";
961  return;
962  }
963 
964  // member functions
965  if (d.member_fns_changes())
966  {
967  // report deletions
968  int numdels = d.get_priv()->deleted_member_functions_.size();
969  size_t num_filtered =
970  d.get_priv()->count_filtered_deleted_mem_fns(ctxt);
971  if (numdels)
972  report_mem_header(out, numdels, num_filtered, del_kind,
973  "member function", indent);
974  for (class_or_union::member_functions::const_iterator i =
975  d.get_priv()->sorted_deleted_member_functions_.begin();
976  i != d.get_priv()->sorted_deleted_member_functions_.end();
977  ++i)
978  {
979  if (!(ctxt->get_allowed_category()
982  continue;
983 
984  method_decl_sptr mem_fun = *i;
985  out << indent << " ";
986  represent(*ctxt, mem_fun, out);
987  }
988 
989  // report insertions;
990  int numins = d.get_priv()->inserted_member_functions_.size();
991  num_filtered = d.get_priv()->count_filtered_inserted_mem_fns(ctxt);
992  if (numins)
993  report_mem_header(out, numins, num_filtered, ins_kind,
994  "member function", indent);
995  for (class_or_union::member_functions::const_iterator i =
996  d.get_priv()->sorted_inserted_member_functions_.begin();
997  i != d.get_priv()->sorted_inserted_member_functions_.end();
998  ++i)
999  {
1000  if (!(ctxt->get_allowed_category()
1003  continue;
1004 
1005  method_decl_sptr mem_fun = *i;
1006  out << indent << " ";
1007  represent(*ctxt, mem_fun, out);
1008  }
1009 
1010  // report member function with sub-types changes
1011  int numchanges = d.get_priv()->sorted_changed_member_functions_.size();
1012  num_filtered = d.get_priv()->count_filtered_changed_mem_fns(ctxt);
1013  if (numchanges)
1014  report_mem_header(out, numchanges, num_filtered, change_kind,
1015  "member function", indent);
1016  for (function_decl_diff_sptrs_type::const_iterator i =
1017  d.get_priv()->sorted_changed_member_functions_.begin();
1018  i != d.get_priv()->sorted_changed_member_functions_.end();
1019  ++i)
1020  {
1021  if (!(ctxt->get_allowed_category()
1024  ((*i)->first_function_decl()))
1026  ((*i)->second_function_decl())))
1027  continue;
1028 
1029  diff_sptr diff = *i;
1030  if (!diff || !diff->to_be_reported())
1031  continue;
1032 
1033  string repr =
1034  (*i)->first_function_decl()->get_pretty_representation();
1035  out << indent << " '" << repr << "' has some sub-type changes:\n";
1036  diff->report(out, indent + " ");
1037  }
1038  }
1039 
1040  // data members
1041  if (d.data_members_changes())
1042  {
1043  // report deletions
1044  int numdels = d.class_or_union_diff::get_priv()->
1045  get_deleted_non_static_data_members_number();
1046  if (numdels)
1047  {
1048  report_mem_header(out, numdels, 0, del_kind,
1049  "data member", indent);
1050  vector<decl_base_sptr> sorted_dms;
1052  (d.class_or_union_diff::get_priv()->deleted_data_members_,
1053  sorted_dms);
1054  for (vector<decl_base_sptr>::const_iterator i = sorted_dms.begin();
1055  i != sorted_dms.end();
1056  ++i)
1057  {
1058  var_decl_sptr data_mem =
1059  dynamic_pointer_cast<var_decl>(*i);
1060  ABG_ASSERT(data_mem);
1061  if (get_member_is_static(data_mem))
1062  continue;
1063  represent_data_member(data_mem, ctxt, out, indent + " ");
1064  }
1065  }
1066 
1067  //report insertions
1068  int numins =
1069  d.class_or_union_diff::get_priv()->inserted_data_members_.size();
1070  if (numins)
1071  {
1072  report_mem_header(out, numins, 0, ins_kind,
1073  "data member", indent);
1074  vector<decl_base_sptr> sorted_dms;
1076  (d.class_or_union_diff::get_priv()->inserted_data_members_,
1077  sorted_dms);
1078  for (vector<decl_base_sptr>::const_iterator i = sorted_dms.begin();
1079  i != sorted_dms.end();
1080  ++i)
1081  {
1082  var_decl_sptr data_mem =
1083  dynamic_pointer_cast<var_decl>(*i);
1084  ABG_ASSERT(data_mem);
1085  represent_data_member(data_mem, ctxt, out, indent + " ");
1086  }
1087  }
1088 
1089  // report change
1090  size_t num_changes =
1091  (d.sorted_subtype_changed_data_members().size()
1092  + d.sorted_changed_data_members().size());
1093 
1094  size_t num_changes_filtered =
1095  (d.count_filtered_subtype_changed_data_members()
1096  + d.count_filtered_changed_data_members());
1097 
1098  if (num_changes)
1099  {
1100  report_mem_header(out, num_changes, num_changes_filtered,
1101  change_kind, "data member", indent);
1102 
1103  for (var_diff_sptrs_type::const_iterator it =
1104  d.sorted_changed_data_members().begin();
1105  it != d.sorted_changed_data_members().end();
1106  ++it)
1107  if ((*it)->to_be_reported())
1108  represent(*it, ctxt, out, indent + " ");
1109 
1110  for (var_diff_sptrs_type::const_iterator it =
1111  d.sorted_subtype_changed_data_members().begin();
1112  it != d.sorted_subtype_changed_data_members().end();
1113  ++it)
1114  if ((*it)->to_be_reported())
1115  represent(*it, ctxt, out, indent + " ");
1116  }
1117 
1118  // Report about data members replaced by an anonymous union data
1119  // member.
1121  }
1122 
1123  // member types
1124  if (const edit_script& e = d.member_types_changes())
1125  {
1126  int numchanges =
1127  d.class_or_union_diff::get_priv()->sorted_changed_member_types_.size();
1128  int numdels =
1129  d.class_or_union_diff::get_priv()->deleted_member_types_.size();
1130 
1131  // report deletions
1132  if (numdels)
1133  {
1134  report_mem_header(out, numdels, 0, del_kind,
1135  "member type", indent);
1136 
1137  for (string_decl_base_sptr_map::const_iterator i =
1138  d.class_or_union_diff::get_priv()->deleted_member_types_.begin();
1139  i != d.class_or_union_diff::get_priv()->deleted_member_types_.end();
1140  ++i)
1141  {
1142  decl_base_sptr mem_type = i->second;
1143  out << indent << " '"
1144  << mem_type->get_pretty_representation()
1145  << "'\n";
1146  }
1147  out << "\n";
1148  }
1149  // report changes
1150  if (numchanges)
1151  {
1152  report_mem_header(out, numchanges, 0, change_kind,
1153  "member type", indent);
1154 
1155  for (diff_sptrs_type::const_iterator it =
1156  d.class_or_union_diff::get_priv()->sorted_changed_member_types_.begin();
1157  it != d.class_or_union_diff::get_priv()->sorted_changed_member_types_.end();
1158  ++it)
1159  {
1160  if (!(*it)->to_be_reported())
1161  continue;
1162 
1163  type_or_decl_base_sptr o = (*it)->first_subject();
1164  type_or_decl_base_sptr n = (*it)->second_subject();
1165  out << indent << " '"
1166  << o->get_pretty_representation()
1167  << "' changed ";
1168  report_loc_info(n, *ctxt, out);
1169  out << ":\n";
1170  (*it)->report(out, indent + " ");
1171  }
1172  out << "\n";
1173  }
1174 
1175  // report insertions
1176  int numins = e.num_insertions();
1177  ABG_ASSERT(numchanges <= numins);
1178  numins -= numchanges;
1179 
1180  if (numins)
1181  {
1182  report_mem_header(out, numins, 0, ins_kind,
1183  "member type", indent);
1184 
1185  for (vector<insertion>::const_iterator i = e.insertions().begin();
1186  i != e.insertions().end();
1187  ++i)
1188  {
1189  type_base_sptr mem_type;
1190  for (vector<unsigned>::const_iterator j =
1191  i->inserted_indexes().begin();
1192  j != i->inserted_indexes().end();
1193  ++j)
1194  {
1195  mem_type = second->get_member_types()[*j];
1196  if (!d.class_or_union_diff::get_priv()->
1197  member_type_has_changed(get_type_declaration(mem_type)))
1198  {
1199  out << indent << " '"
1200  << get_type_declaration(mem_type)->
1202  << "'\n";
1203  }
1204  }
1205  }
1206  out << "\n";
1207  }
1208  }
1209 
1210  // member function templates
1211  if (const edit_script& e = d.member_fn_tmpls_changes())
1212  {
1213  // report deletions
1214  int numdels = e.num_deletions();
1215  if (numdels)
1216  report_mem_header(out, numdels, 0, del_kind,
1217  "member function template", indent);
1218  for (vector<deletion>::const_iterator i = e.deletions().begin();
1219  i != e.deletions().end();
1220  ++i)
1221  {
1222  member_function_template_sptr mem_fn_tmpl =
1223  first->get_member_function_templates()[i->index()];
1224  out << indent << " '"
1225  << mem_fn_tmpl->as_function_tdecl()->get_pretty_representation()
1226  << "'\n";
1227  }
1228 
1229  // report insertions
1230  int numins = e.num_insertions();
1231  if (numins)
1232  report_mem_header(out, numins, 0, ins_kind,
1233  "member function template", indent);
1234  for (vector<insertion>::const_iterator i = e.insertions().begin();
1235  i != e.insertions().end();
1236  ++i)
1237  {
1238  member_function_template_sptr mem_fn_tmpl;
1239  for (vector<unsigned>::const_iterator j =
1240  i->inserted_indexes().begin();
1241  j != i->inserted_indexes().end();
1242  ++j)
1243  {
1244  mem_fn_tmpl = second->get_member_function_templates()[*j];
1245  out << indent << " '"
1246  << mem_fn_tmpl->as_function_tdecl()->
1248  << "'\n";
1249  }
1250  }
1251  }
1252 
1253  // member class templates.
1254  if (const edit_script& e = d.member_class_tmpls_changes())
1255  {
1256  // report deletions
1257  int numdels = e.num_deletions();
1258  if (numdels)
1259  report_mem_header(out, numdels, 0, del_kind,
1260  "member class template", indent);
1261  for (vector<deletion>::const_iterator i = e.deletions().begin();
1262  i != e.deletions().end();
1263  ++i)
1264  {
1265  member_class_template_sptr mem_cls_tmpl =
1266  first->get_member_class_templates()[i->index()];
1267  out << indent << " '"
1268  << mem_cls_tmpl->as_class_tdecl()->get_pretty_representation()
1269  << "'\n";
1270  }
1271 
1272  // report insertions
1273  int numins = e.num_insertions();
1274  if (numins)
1275  report_mem_header(out, numins, 0, ins_kind,
1276  "member class template", indent);
1277  for (vector<insertion>::const_iterator i = e.insertions().begin();
1278  i != e.insertions().end();
1279  ++i)
1280  {
1281  member_class_template_sptr mem_cls_tmpl;
1282  for (vector<unsigned>::const_iterator j =
1283  i->inserted_indexes().begin();
1284  j != i->inserted_indexes().end();
1285  ++j)
1286  {
1287  mem_cls_tmpl = second->get_member_class_templates()[*j];
1288  out << indent << " '"
1289  << mem_cls_tmpl->as_class_tdecl()
1290  ->get_pretty_representation()
1291  << "'\n";
1292  }
1293  }
1294  }
1295 }
1296 
1297 /// Produce a basic report about the changes carried by a @ref
1298 /// class_diff node.
1299 ///
1300 /// @param d the @ref class_diff node to consider.
1301 ///
1302 /// @param out the output stream to report the changes to.
1303 ///
1304 /// @param indent the string to use as an indentation prefix in the
1305 /// report.
1306 void
1307 default_reporter::report(const class_diff& d, ostream& out,
1308  const string& indent) const
1309 {
1310  if (!d.to_be_reported())
1311  return;
1312 
1313  string name = d.first_subject()->get_pretty_representation();
1314 
1315  RETURN_IF_BEING_REPORTED_OR_WAS_REPORTED_EARLIER(d.first_subject(),
1316  d.second_subject());
1317 
1318  d.currently_reporting(true);
1319 
1320  // Now report the changes about the differents parts of the type.
1321  class_decl_sptr first = d.first_class_decl(),
1322  second = d.second_class_decl();
1323 
1324  report_name_size_and_alignment_changes(first, second, d.context(),
1325  out, indent);
1326 
1327  const diff_context_sptr& ctxt = d.context();
1328  maybe_report_diff_for_member(first, second, ctxt, out, indent);
1329 
1330  // bases classes
1331  if (d.base_changes())
1332  {
1333  // Report deletions.
1334  int numdels = d.get_priv()->deleted_bases_.size();
1335  size_t numchanges = d.get_priv()->sorted_changed_bases_.size();
1336 
1337  if (numdels)
1338  {
1339  report_mem_header(out, numdels, 0, del_kind,
1340  "base class", indent);
1341 
1342  for (class_decl::base_specs::const_iterator i
1343  = d.get_priv()->sorted_deleted_bases_.begin();
1344  i != d.get_priv()->sorted_deleted_bases_.end();
1345  ++i)
1346  {
1347  if (i != d.get_priv()->sorted_deleted_bases_.begin())
1348  out << "\n";
1349 
1350  class_decl::base_spec_sptr base = *i;
1351 
1352  if (d.get_priv()->base_has_changed(base))
1353  continue;
1354  out << indent << " "
1355  << base->get_base_class()->get_pretty_representation();
1356  report_loc_info(base->get_base_class(), *d.context(), out);
1357  }
1358  out << "\n";
1359  }
1360 
1361  // Report changes.
1362  size_t num_filtered = d.get_priv()->count_filtered_bases();
1363  if (numchanges)
1364  {
1365  report_mem_header(out, numchanges, num_filtered, change_kind,
1366  "base class", indent);
1367  for (base_diff_sptrs_type::const_iterator it =
1368  d.get_priv()->sorted_changed_bases_.begin();
1369  it != d.get_priv()->sorted_changed_bases_.end();
1370  ++it)
1371  {
1372  base_diff_sptr diff = *it;
1373  if (!diff || !diff->to_be_reported())
1374  continue;
1375 
1376  class_decl::base_spec_sptr o = diff->first_base();
1377  out << indent << " '"
1378  << o->get_base_class()->get_pretty_representation() << "'";
1379  report_loc_info(o->get_base_class(), *d.context(), out);
1380  out << " changed:\n";
1381  diff->report(out, indent + " ");
1382  }
1383  }
1384 
1385  //Report insertions.
1386  int numins = d.get_priv()->inserted_bases_.size();
1387  if (numins)
1388  {
1389  report_mem_header(out, numins, 0, ins_kind,
1390  "base class", indent);
1391 
1392  for (class_decl::base_specs::const_iterator i =
1393  d.get_priv()->sorted_inserted_bases_.begin();
1394  i != d.get_priv()->sorted_inserted_bases_.end();
1395  ++i)
1396  {
1397  class_decl_sptr b = (*i)->get_base_class();
1398  out << indent << " " << b->get_pretty_representation();
1399  report_loc_info(b, *ctxt, out);
1400  out << "\n";
1401  }
1402  }
1403 
1404  // Report base classes re-organisation
1405  maybe_report_base_class_reordering(d, out, indent);
1406  }
1407 
1408  d.class_or_union_diff::report(out, indent);
1409 
1410  d.currently_reporting(false);
1411 
1412  d.reported_once(true);
1413 }
1414 
1415 /// Produce a basic report about the changes carried by a @ref
1416 /// union_diff node.
1417 ///
1418 /// @param d the @ref union_diff node to consider.
1419 ///
1420 /// @param out the output stream to report the changes to.
1421 ///
1422 /// @param indent the string to use as an indentation prefix in the
1423 /// report.
1424 void
1425 default_reporter::report(const union_diff& d, ostream& out,
1426  const string& indent) const
1427 {
1428  RETURN_IF_BEING_REPORTED_OR_WAS_REPORTED_EARLIER(d.first_subject(),
1429  d.second_subject());
1430 
1431  d.currently_reporting(true);
1432 
1433  // Now report the changes about the differents parts of the type.
1434  union_decl_sptr first = d.first_union_decl(), second = d.second_union_decl();
1435 
1436  report_name_size_and_alignment_changes(first, second, d.context(),
1437  out, indent);
1438 
1439  maybe_report_diff_for_member(first, second,d. context(), out, indent);
1440 
1441  d.class_or_union_diff::report(out, indent);
1442 
1443  if (d.context()->get_allowed_category() & HARMLESS_UNION_CHANGE_CATEGORY
1445  {
1446  // The user wants to see harmless changes and the union diff we
1447  // are looking at does carry some harmless changes. Let's show
1448  // the "before" and "after" carried by the diff node.
1449  out << indent << "type changed from:\n"
1450  << get_class_or_union_flat_representation(first, indent + " ",
1451  /*one_line=*/true,
1452  /*internal=*/false,
1453  /*qualified_names=*/false)
1454  << "\n"
1455  << indent << "to:\n"
1456  << get_class_or_union_flat_representation(second, indent + " ",
1457  /*one_line=*/true,
1458  /*internal=*/false,
1459  /*qualified_names=*/false)
1460  << "\n";
1461  }
1462 
1463  d.currently_reporting(false);
1464 
1465  d.reported_once(true);
1466 }
1467 
1468 /// Emit a report about the changes carried by a @ref distinct_diff
1469 /// node.
1470 ///
1471 /// @param d the @ref distinct_diff node to consider.
1472 ///
1473 /// @param out the output stream to send the diff report to.
1474 ///
1475 /// @param indent the indentation string to use in the report.
1476 void
1477 default_reporter::report(const distinct_diff& d, ostream& out,
1478  const string& indent) const
1479 {
1480  if (!d.to_be_reported())
1481  return;
1482 
1483  type_or_decl_base_sptr f = d.first(), s = d.second();
1484 
1485  string f_repr = f ? f->get_pretty_representation() : "'void'";
1486  string s_repr = s ? s->get_pretty_representation() : "'void'";
1487 
1488  diff_sptr diff = d.compatible_child_diff();
1489 
1490  string compatible = diff ? " to compatible type '": " to '";
1491 
1492  out << indent << "entity changed from '" << f_repr << "'"
1493  << compatible << s_repr << "'";
1494  report_loc_info(s, *d.context(), out);
1495  out << "\n";
1496 
1497  type_base_sptr fs = strip_typedef(is_type(f)),
1498  ss = strip_typedef(is_type(s));
1499 
1500  if (diff)
1501  diff->report(out, indent + " ");
1502  else
1503  report_size_and_alignment_changes(f, s, d.context(), out, indent);
1504 }
1505 
1506 /// Serialize a report of the changes encapsulated in the current
1507 /// instance of @ref function_decl_diff over to an output stream.
1508 ///
1509 /// @param d the @ref function_decl_diff node to consider.
1510 ///
1511 /// @param out the output stream to serialize the report to.
1512 ///
1513 /// @param indent the string to use an an indentation prefix.
1514 void
1515 default_reporter::report(const function_decl_diff& d, ostream& out,
1516  const string& indent) const
1517 {
1518  if (!d.to_be_reported())
1519  return;
1520 
1521  maybe_report_diff_for_member(d.first_function_decl(),
1522  d.second_function_decl(),
1523  d.context(), out, indent);
1524 
1525  function_decl_sptr ff = d.first_function_decl();
1526  function_decl_sptr sf = d.second_function_decl();
1527 
1528  diff_context_sptr ctxt = d.context();
1529  corpus_sptr fc = ctxt->get_first_corpus();
1530  corpus_sptr sc = ctxt->get_second_corpus();
1531 
1532 
1533  string qn1 = ff->get_qualified_name(), qn2 = sf->get_qualified_name(),
1534  linkage_names1, linkage_names2;
1535  elf_symbol_sptr s1 = ff->get_symbol(), s2 = sf->get_symbol();
1536 
1537  if (s1)
1538  linkage_names1 = s1->get_id_string();
1539  if (s2)
1540  linkage_names2 = s2->get_id_string();
1541 
1542  // If the symbols for ff and sf have aliases, get all the names of
1543  // the aliases;
1544  if (fc && s1)
1545  linkage_names1 =
1546  s1->get_aliases_id_string(fc->get_fun_symbol_map());
1547  if (sc && s2)
1548  linkage_names2 =
1549  s2->get_aliases_id_string(sc->get_fun_symbol_map());
1550 
1551  if (!d.is_filtered_out_without_looking_at_allowed_changes())
1552  {
1553  /// If the set of linkage names of the function have changed, report
1554  /// it.
1555  if (linkage_names1 != linkage_names2)
1556  {
1557  if (linkage_names1.empty())
1558  {
1559  out << indent << ff->get_pretty_representation()
1560  << " didn't have any linkage name, and it now has: '"
1561  << linkage_names2 << "'\n";
1562  }
1563  else if (linkage_names2.empty())
1564  {
1565  out << indent << ff->get_pretty_representation()
1566  << " did have linkage names '" << linkage_names1
1567  << "'\n"
1568  << indent << "but it doesn't have any linkage name anymore\n";
1569  }
1570  else
1571  out << indent << "linkage names of "
1572  << ff->get_pretty_representation()
1573  << "\n" << indent << "changed from '"
1574  << linkage_names1 << "' to '" << linkage_names2 << "'\n";
1575  }
1576 
1577  if (qn1 != qn2
1578  && d.type_diff()
1579  && d.type_diff()->to_be_reported())
1580  {
1581  // So the function has sub-type changes that are to be
1582  // reported. Let's see if the function name changed too; if it
1583  // did, then we'd report that change right before reporting the
1584  // sub-type changes.
1585  string frep1 = d.first_function_decl()->get_pretty_representation(),
1586  frep2 = d.second_function_decl()->get_pretty_representation();
1587  out << indent << "'" << frep1 << " {" << linkage_names1<< "}"
1588  << "' now becomes '"
1589  << frep2 << " {" << linkage_names2 << "}" << "'\n";
1590  }
1591 
1592  maybe_report_diff_for_symbol(ff->get_symbol(),
1593  sf->get_symbol(),
1594  d.context(), out, indent);
1595 
1596  // Now report about inline-ness changes
1597  if (ff->is_declared_inline() != sf->is_declared_inline())
1598  {
1599  out << indent;
1600  if (ff->is_declared_inline())
1601  out << sf->get_pretty_representation()
1602  << " is not declared inline anymore\n";
1603  else
1604  out << sf->get_pretty_representation()
1605  << " is now declared inline\n";
1606  }
1607 
1608  // Report about vtable offset changes.
1609  if (is_member_function(ff) && is_member_function(sf))
1610  {
1611  bool ff_is_virtual = get_member_function_is_virtual(ff),
1612  sf_is_virtual = get_member_function_is_virtual(sf);
1613  if (ff_is_virtual != sf_is_virtual)
1614  {
1615  out << indent;
1616  if (ff_is_virtual)
1617  out << ff->get_pretty_representation()
1618  << " is no more declared virtual\n";
1619  else
1620  out << ff->get_pretty_representation()
1621  << " is now declared virtual\n";
1622  }
1623 
1624  size_t ff_vtable_offset = get_member_function_vtable_offset(ff),
1625  sf_vtable_offset = get_member_function_vtable_offset(sf);
1626  if (ff_is_virtual && sf_is_virtual
1627  && (ff_vtable_offset != sf_vtable_offset))
1628  {
1629  out << indent
1630  << "the vtable offset of " << ff->get_pretty_representation()
1631  << " changed from " << ff_vtable_offset
1632  << " to " << sf_vtable_offset << "\n";
1633  }
1634 
1635  // the parent types (classe or union) of the two member
1636  // functions.
1637  class_or_union_sptr f =
1638  is_class_or_union_type(is_method_type(ff->get_type())->get_class_type());
1639  class_or_union_sptr s =
1640  is_class_or_union_type(is_method_type(sf->get_type())->get_class_type());
1641 
1644 
1645  // Detect if the virtual member function changes above
1646  // introduced a vtable change or not.
1647  bool vtable_added = false, vtable_removed = false;
1648  if (!f->get_is_declaration_only() && !s->get_is_declaration_only())
1649  {
1650  if (fc && sc)
1651  {
1652  vtable_added = !fc->has_vtable() && sc->has_vtable();
1653  vtable_removed = fc->has_vtable() && !sc->has_vtable();
1654  }
1655  }
1656  bool vtable_changed = ((ff_is_virtual != sf_is_virtual)
1657  || (ff_vtable_offset != sf_vtable_offset));
1658  bool incompatible_change = (ff_vtable_offset != sf_vtable_offset);
1659 
1660  if (vtable_added)
1661  out << indent
1662  << " note that a vtable was added to "
1663  << fc->get_pretty_representation()
1664  << "\n";
1665  else if (vtable_removed)
1666  out << indent
1667  << " note that the vtable was removed from "
1668  << fc->get_pretty_representation()
1669  << "\n";
1670  else if (vtable_changed)
1671  {
1672  out << indent;
1673  if (incompatible_change)
1674  out << " note that this is an ABI incompatible "
1675  "change to the vtable of ";
1676  else
1677  out << " note that this induces a change to the vtable of ";
1678  out << fc->get_pretty_representation()
1679  << "\n";
1680  }
1681 
1682  }
1683  }
1684 
1685  // Report about function type differences.
1686  if (d.type_diff() && d.type_diff()->to_be_reported())
1687  d.type_diff()->report(out, indent);
1688 }
1689 
1690 /// Report the changes carried by a @ref var_diff node in a serialized
1691 /// form.
1692 ///
1693 /// @param d the @ref var_diff node to consider.
1694 ///
1695 /// @param out the stream to serialize the diff to.
1696 ///
1697 /// @param indent the prefix to use for the indentation of this
1698 /// serialization.
1699 void
1700 default_reporter::report(const var_diff& d, ostream& out,
1701  const string& indent) const
1702 {
1703  if (!d.to_be_reported())
1704  return;
1705 
1706  decl_base_sptr first = d.first_var(), second = d.second_var();
1707  string n = first->get_pretty_representation();
1708 
1709  if (!d.is_filtered_out_without_looking_at_allowed_changes())
1710  {
1712  d.context(),
1713  out, indent);
1714 
1715  maybe_report_diff_for_symbol(d.first_var()->get_symbol(),
1716  d.second_var()->get_symbol(),
1717  d.context(), out, indent);
1718 
1719  maybe_report_diff_for_member(first, second, d.context(), out, indent);
1720 
1721  maybe_report_diff_for_variable(first, second, d.context(), out, indent);
1722  }
1723 
1724  if (diff_sptr dif = d.type_diff())
1725  {
1726  if (dif->to_be_reported())
1727  {
1728  RETURN_IF_BEING_REPORTED_OR_WAS_REPORTED_EARLIER2(dif, "type");
1729  out << indent << "type of variable changed:\n";
1730  dif->report(out, indent + " ");
1731  }
1732  }
1733 }
1734 
1735 /// Report the changes carried by a @ref translation_unit_diff node in
1736 /// a serialized form.
1737 ///
1738 /// @param d the @ref translation_unit_diff node to consider.
1739 ///
1740 /// @param out the output stream to serialize the report to.
1741 ///
1742 /// @param indent the prefix to use as indentation for the report.
1743 void
1744 default_reporter::report(const translation_unit_diff& d,
1745  ostream& out,
1746  const string& indent) const
1747 {
1748  static_cast<const scope_diff&>(d).report(out, indent);
1749 }
1750 
1751 /// Report the changes carried by a @ref corpus_diff node in a
1752 /// serialized form.
1753 ///
1754 /// @param d the @ref corpus_diff node to consider.
1755 ///
1756 /// @param out the output stream to serialize the report to.
1757 ///
1758 /// @param indent the prefix to use as indentation for the report.
1759 void
1760 default_reporter::report(const corpus_diff& d, ostream& out,
1761  const string& indent) const
1762 {
1763  const corpus_diff::diff_stats &s =
1764  const_cast<corpus_diff&>(d).
1765  apply_filters_and_suppressions_before_reporting();
1766 
1767  const diff_context_sptr& ctxt = d.context();
1768 
1769  d.priv_->emit_diff_stats(s, out, indent);
1770  if (ctxt->show_stats_only())
1771  return;
1772  out << "\n";
1773 
1774  if (ctxt->show_soname_change()
1775  && !d.priv_->sonames_equal_)
1776  out << indent << "SONAME changed from '"
1777  << d.first_corpus()->get_soname() << "' to '"
1778  << d.second_corpus()->get_soname() << "'\n\n";
1779 
1780  if (ctxt->show_architecture_change()
1781  && !d.priv_->architectures_equal_)
1782  out << indent << "architecture changed from '"
1783  << d.first_corpus()->get_architecture_name() << "' to '"
1784  << d.second_corpus()->get_architecture_name() << "'\n\n";
1785 
1786  /// Report removed/added/changed functions.
1787  if (ctxt->show_deleted_fns())
1788  {
1789  if (s.net_num_func_removed() == 1)
1790  out << indent << "1 Removed function:\n\n";
1791  else if (s.net_num_func_removed() > 1)
1792  out << indent << s.net_num_func_removed() << " Removed functions:\n\n";
1793 
1794  bool emitted = false;
1795  vector<function_decl*>sorted_deleted_fns;
1796  sort_string_function_ptr_map(d.priv_->deleted_fns_, sorted_deleted_fns);
1797  for (vector<function_decl*>::const_iterator i =
1798  sorted_deleted_fns.begin();
1799  i != sorted_deleted_fns.end();
1800  ++i)
1801  {
1802  if (d.priv_->deleted_function_is_suppressed(*i))
1803  continue;
1804 
1805  out << indent
1806  << " ";
1807  out << "[D] ";
1808  out << "'" << (*i)->get_pretty_representation() << "'";
1809  if (ctxt->show_linkage_names())
1810  {
1811  out << " {";
1812  show_linkage_name_and_aliases(out, "", *(*i)->get_symbol(),
1813  d.first_corpus()->get_fun_symbol_map());
1814  out << "}";
1815  }
1816  out << "\n";
1818  {
1819  class_decl_sptr c =
1820  is_class_type(is_method_type((*i)->get_type())->get_class_type());
1821  out << indent
1822  << " "
1823  << "note that this removes an entry from the vtable of "
1824  << c->get_pretty_representation()
1825  << "\n";
1826  }
1827  emitted = true;
1828  }
1829  if (emitted)
1830  out << "\n";
1831  }
1832 
1833  if (ctxt->show_added_fns())
1834  {
1835  if (s.net_num_func_added() == 1)
1836  out << indent << "1 Added function:\n\n";
1837  else if (s.net_num_func_added() > 1)
1838  out << indent << s.net_num_func_added()
1839  << " Added functions:\n\n";
1840  bool emitted = false;
1841  vector<function_decl*> sorted_added_fns;
1842  sort_string_function_ptr_map(d.priv_->added_fns_, sorted_added_fns);
1843  for (vector<function_decl*>::const_iterator i = sorted_added_fns.begin();
1844  i != sorted_added_fns.end();
1845  ++i)
1846  {
1847  if (d.priv_->added_function_is_suppressed(*i))
1848  continue;
1849 
1850  out
1851  << indent
1852  << " ";
1853  out << "[A] ";
1854  out << "'"
1855  << (*i)->get_pretty_representation()
1856  << "'";
1857  if (ctxt->show_linkage_names())
1858  {
1859  out << " {";
1861  (out, "", *(*i)->get_symbol(),
1862  d.second_corpus()->get_fun_symbol_map());
1863  out << "}";
1864  }
1865  out << "\n";
1867  {
1868  class_decl_sptr c =
1869  is_class_type(is_method_type((*i)->get_type())->get_class_type());
1870  out << indent
1871  << " "
1872  << "note that this adds a new entry to the vtable of "
1873  << c->get_pretty_representation()
1874  << "\n";
1875  }
1876  emitted = true;
1877  }
1878  if (emitted)
1879  out << "\n";
1880  }
1881 
1882  if (ctxt->show_changed_fns())
1883  {
1884  size_t num_changed = s.num_func_changed() - s.num_changed_func_filtered_out();
1885  if (num_changed == 1)
1886  out << indent << "1 function with some indirect sub-type change:\n\n";
1887  else if (num_changed > 1)
1888  out << indent << num_changed
1889  << " functions with some indirect sub-type change:\n\n";
1890 
1891  vector<function_decl_diff_sptr> sorted_changed_fns;
1892  sort_string_function_decl_diff_sptr_map(d.priv_->changed_fns_map_,
1893  sorted_changed_fns);
1894  for (vector<function_decl_diff_sptr>::const_iterator i =
1895  sorted_changed_fns.begin();
1896  i != sorted_changed_fns.end();
1897  ++i)
1898  {
1899  diff_sptr diff = *i;
1900  if (!diff)
1901  continue;
1902 
1903  if (diff->to_be_reported())
1904  {
1905  function_decl_sptr fn = (*i)->first_function_decl();
1906  out << indent << " [C] '"
1907  << fn->get_pretty_representation() << "'";
1908  report_loc_info((*i)->first_function_decl(), *ctxt, out);
1909  out << " has some indirect sub-type changes:\n";
1910  if (// The symbol of the function has aliases and the
1911  // function is not a cdtor (yeah because c++ cdtors
1912  // usually have several aliases).
1913  (fn->get_symbol()->has_aliases()
1914  && !(is_member_function(fn)
1916  && !(is_member_function(fn)
1918  || // We are in C and the name of the function is
1919  // different from the symbol name -- without
1920  // taking the possible symbol version into
1921  // account (this usually means the programmers
1922  // was playing tricks with symbol names and
1923  // versions).
1924  (is_c_language(get_translation_unit(fn)->get_language())
1925  && fn->get_name() != fn->get_symbol()->get_name()))
1926  {
1927  // As the name of the symbol of the function doesn't
1928  // seem to be obvious here, make sure to tell the
1929  // user about the name of the (function) symbol she
1930  // is looking at here.
1931  int number_of_aliases =
1932  fn->get_symbol()->get_number_of_aliases();
1933  if (number_of_aliases == 0)
1934  {
1935  out << indent << " "
1936  << "Please note that the exported symbol of "
1937  "this function is "
1938  << fn->get_symbol()->get_id_string()
1939  << "\n";
1940  }
1941  else
1942  {
1943  out << indent << " "
1944  << "Please note that the symbol of this function is "
1945  << fn->get_symbol()->get_id_string()
1946  << "\n and it aliases symbol";
1947  if (number_of_aliases > 1)
1948  out << "s";
1949  out << ": "
1950  << fn->get_symbol()->get_aliases_id_string(false)
1951  << "\n";
1952  }
1953  }
1954  diff->report(out, indent + " ");
1955  // Extra spacing.
1956  out << "\n";
1957  }
1958  }
1959  // Changed functions have extra spacing already. No new line here.
1960  }
1961 
1962  // Report removed/added/changed variables.
1963  if (ctxt->show_deleted_vars())
1964  {
1965  if (s.net_num_vars_removed() == 1)
1966  out << indent << "1 Removed variable:\n\n";
1967  else if (s.net_num_vars_removed() > 1)
1968  out << indent << s.net_num_vars_removed()
1969  << " Removed variables:\n\n";
1970  string n;
1971  bool emitted = false;
1972  vector<var_decl*> sorted_deleted_vars;
1973  sort_string_var_ptr_map(d.priv_->deleted_vars_, sorted_deleted_vars);
1974  for (vector<var_decl*>::const_iterator i =
1975  sorted_deleted_vars.begin();
1976  i != sorted_deleted_vars.end();
1977  ++i)
1978  {
1979  if (d.priv_->deleted_variable_is_suppressed(*i))
1980  continue;
1981 
1982  n = (*i)->get_pretty_representation();
1983 
1984  out << indent
1985  << " ";
1986  out << "[D] ";
1987  out << "'"
1988  << n
1989  << "'";
1990  if (ctxt->show_linkage_names())
1991  {
1992  out << " {";
1993  show_linkage_name_and_aliases(out, "", *(*i)->get_symbol(),
1994  d.first_corpus()->get_var_symbol_map());
1995  out << "}";
1996  }
1997  out << "\n";
1998  emitted = true;
1999  }
2000  if (emitted)
2001  out << "\n";
2002  }
2003 
2004  if (ctxt->show_added_vars())
2005  {
2006  if (s.net_num_vars_added() == 1)
2007  out << indent << "1 Added variable:\n\n";
2008  else if (s.net_num_vars_added() > 1)
2009  out << indent << s.net_num_vars_added()
2010  << " Added variables:\n\n";
2011  string n;
2012  bool emitted = false;
2013  vector<var_decl*> sorted_added_vars;
2014  sort_string_var_ptr_map(d.priv_->added_vars_, sorted_added_vars);
2015  for (vector<var_decl*>::const_iterator i =
2016  sorted_added_vars.begin();
2017  i != sorted_added_vars.end();
2018  ++i)
2019  {
2020  if (d.priv_->added_variable_is_suppressed(*i))
2021  continue;
2022 
2023  n = (*i)->get_pretty_representation();
2024 
2025  out << indent
2026  << " ";
2027  out << "[A] ";
2028  out << "'" << n << "'";
2029  if (ctxt->show_linkage_names())
2030  {
2031  out << " {";
2032  show_linkage_name_and_aliases(out, "", *(*i)->get_symbol(),
2033  d.second_corpus()->get_var_symbol_map());
2034  out << "}";
2035  }
2036  out << "\n";
2037  emitted = true;
2038  }
2039  if (emitted)
2040  out << "\n";
2041  }
2042 
2043  if (ctxt->show_changed_vars())
2044  {
2045  size_t num_changed =
2046  s.num_vars_changed() - s.num_changed_vars_filtered_out();
2047  if (num_changed == 1)
2048  out << indent << "1 Changed variable:\n\n";
2049  else if (num_changed > 1)
2050  out << indent << num_changed
2051  << " Changed variables:\n\n";
2052  string n1, n2;
2053 
2054  for (var_diff_sptrs_type::const_iterator i =
2055  d.priv_->sorted_changed_vars_.begin();
2056  i != d.priv_->sorted_changed_vars_.end();
2057  ++i)
2058  {
2059  diff_sptr diff = *i;
2060 
2061  if (!diff)
2062  continue;
2063 
2064  if (!diff->to_be_reported())
2065  continue;
2066 
2067  n1 = diff->first_subject()->get_pretty_representation();
2068  n2 = diff->second_subject()->get_pretty_representation();
2069 
2070  out << indent << " [C] '" << n1 << "' was changed";
2071  if (n1 != n2)
2072  out << " to '" << n2 << "'";
2073  report_loc_info(diff->second_subject(), *ctxt, out);
2074  out << ":\n";
2075  diff->report(out, indent + " ");
2076  // Extra spacing.
2077  out << "\n";
2078  }
2079  // Changed variables have extra spacing already. No new line here.
2080  }
2081 
2082  // Report removed function symbols not referenced by any debug info.
2083  if (ctxt->show_symbols_unreferenced_by_debug_info()
2084  && d.priv_->deleted_unrefed_fn_syms_.size())
2085  {
2086  if (s.net_num_removed_func_syms() == 1)
2087  out << indent
2088  << "1 Removed function symbol not referenced by debug info:\n\n";
2089  else if (s.net_num_removed_func_syms() > 0)
2090  out << indent
2091  << s.net_num_removed_func_syms()
2092  << " Removed function symbols not referenced by debug info:\n\n";
2093 
2094  bool emitted = false;
2095  vector<elf_symbol_sptr> sorted_deleted_unrefed_fn_syms;
2096  sort_string_elf_symbol_map(d.priv_->deleted_unrefed_fn_syms_,
2097  sorted_deleted_unrefed_fn_syms);
2098  for (vector<elf_symbol_sptr>::const_iterator i =
2099  sorted_deleted_unrefed_fn_syms.begin();
2100  i != sorted_deleted_unrefed_fn_syms.end();
2101  ++i)
2102  {
2103  if (d.priv_->deleted_unrefed_fn_sym_is_suppressed((*i).get()))
2104  continue;
2105 
2106  out << indent << " ";
2107  out << "[D] ";
2108 
2109  show_linkage_name_and_aliases(out, "", **i,
2110  d.first_corpus()->get_fun_symbol_map());
2111  out << "\n";
2112  emitted = true;
2113  }
2114  if (emitted)
2115  out << "\n";
2116  }
2117 
2118  // Report added function symbols not referenced by any debug info.
2119  if (ctxt->show_symbols_unreferenced_by_debug_info()
2120  && ctxt->show_added_symbols_unreferenced_by_debug_info()
2121  && d.priv_->added_unrefed_fn_syms_.size())
2122  {
2123  if (s.net_num_added_func_syms() == 1)
2124  out << indent
2125  << "1 Added function symbol not referenced by debug info:\n\n";
2126  else if (s.net_num_added_func_syms() > 0)
2127  out << indent
2128  << s.net_num_added_func_syms()
2129  << " Added function symbols not referenced by debug info:\n\n";
2130 
2131  bool emitted = false;
2132  vector<elf_symbol_sptr> sorted_added_unrefed_fn_syms;
2133  sort_string_elf_symbol_map(d.priv_->added_unrefed_fn_syms_,
2134  sorted_added_unrefed_fn_syms);
2135  for (vector<elf_symbol_sptr>::const_iterator i =
2136  sorted_added_unrefed_fn_syms.begin();
2137  i != sorted_added_unrefed_fn_syms.end();
2138  ++i)
2139  {
2140  if (d.priv_->added_unrefed_fn_sym_is_suppressed((*i).get()))
2141  continue;
2142 
2143  out << indent << " ";
2144  out << "[A] ";
2146  **i,
2147  d.second_corpus()->get_fun_symbol_map());
2148  out << "\n";
2149  emitted = true;
2150  }
2151  if (emitted)
2152  out << "\n";
2153  }
2154 
2155  // Report removed variable symbols not referenced by any debug info.
2156  if (ctxt->show_symbols_unreferenced_by_debug_info()
2157  && d.priv_->deleted_unrefed_var_syms_.size())
2158  {
2159  if (s.net_num_removed_var_syms() == 1)
2160  out << indent
2161  << "1 Removed variable symbol not referenced by debug info:\n\n";
2162  else if (s.net_num_removed_var_syms() > 0)
2163  out << indent
2164  << s.net_num_removed_var_syms()
2165  << " Removed variable symbols not referenced by debug info:\n\n";
2166 
2167  bool emitted = false;
2168  vector<elf_symbol_sptr> sorted_deleted_unrefed_var_syms;
2169  sort_string_elf_symbol_map(d.priv_->deleted_unrefed_var_syms_,
2170  sorted_deleted_unrefed_var_syms);
2171  for (vector<elf_symbol_sptr>::const_iterator i =
2172  sorted_deleted_unrefed_var_syms.begin();
2173  i != sorted_deleted_unrefed_var_syms.end();
2174  ++i)
2175  {
2176  if (d.priv_->deleted_unrefed_var_sym_is_suppressed((*i).get()))
2177  continue;
2178 
2179  out << indent << " ";
2180  out << "[D] ";
2181 
2183  (out, "", **i,
2184  d.first_corpus()->get_fun_symbol_map());
2185 
2186  out << "\n";
2187  emitted = true;
2188  }
2189  if (emitted)
2190  out << "\n";
2191  }
2192 
2193  // Report added variable symbols not referenced by any debug info.
2194  if (ctxt->show_symbols_unreferenced_by_debug_info()
2195  && ctxt->show_added_symbols_unreferenced_by_debug_info()
2196  && d.priv_->added_unrefed_var_syms_.size())
2197  {
2198  if (s.net_num_added_var_syms() == 1)
2199  out << indent
2200  << "1 Added variable symbol not referenced by debug info:\n\n";
2201  else if (s.net_num_added_var_syms() > 0)
2202  out << indent
2203  << s.net_num_added_var_syms()
2204  << " Added variable symbols not referenced by debug info:\n\n";
2205 
2206  bool emitted = false;
2207  vector<elf_symbol_sptr> sorted_added_unrefed_var_syms;
2208  sort_string_elf_symbol_map(d.priv_->added_unrefed_var_syms_,
2209  sorted_added_unrefed_var_syms);
2210  for (vector<elf_symbol_sptr>::const_iterator i =
2211  sorted_added_unrefed_var_syms.begin();
2212  i != sorted_added_unrefed_var_syms.end();
2213  ++i)
2214  {
2215  if (d.priv_->added_unrefed_var_sym_is_suppressed((*i).get()))
2216  continue;
2217 
2218  out << indent << " ";
2219  out << "[A] ";
2220  show_linkage_name_and_aliases(out, "", **i,
2221  d.second_corpus()->get_fun_symbol_map());
2222  out << "\n";
2223  emitted = true;
2224  }
2225  if (emitted)
2226  out << "\n";
2227  }
2228 
2229  // Report added/removed/changed types not reacheable from public
2230  // interfaces.
2231  maybe_report_unreachable_type_changes(d, s, indent, out);
2232 
2233  d.priv_->maybe_dump_diff_tree();
2234 }
2235 
2236 } // end namespace comparison
2237 }// end namespace libabigail
Testing (anding) against this mask means that a given IR artifact has local differences, with respect to the other artifact it was compared against. A local change is a change that is carried by the artifact itself (or its type), rather than by one off its sub-types.
Definition: abg-ir.h:1324
bool is_filtered_out_without_looking_at_allowed_changes() const
Test if this diff tree node is to be filtered out for reporting purposes, but without considering the...
size_t net_num_added_unreachable_types() const
Getter of the number of added types that are unreachable from public interfaces and that are *NOT* fi...
bool is_type(const type_or_decl_base &tod)
Test whether a declaration is a type.
Definition: abg-ir.cc:10469
bool get_member_function_is_virtual(const function_decl &f)
Test if a given member function is virtual.
Definition: abg-ir.cc:6723
void sort_enumerators(const string_enumerator_map &enumerators_map, enum_type_decl::enumerators &sorted)
Sort a map of enumerators by their value.
void report_local_reference_type_changes(const reference_diff &d, std::ostream &out, const std::string &indent) const
For a node, report the local changes carried by the diff node.
Abstraction of a diff between two qualified types.
size_t net_num_func_removed() const
Getter for the net number of function removed.
shared_ptr< class_diff > class_diff_sptr
Convenience typedef for a shared pointer on a class_diff type.
size_t net_num_vars_changed() const
Getter for the number of variables that have a change in their sub-types, minus the number of these v...
This means that a given IR artifact has changes in some of its sub-types, with respect to the other a...
Definition: abg-ir.h:1329
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:26982
size_t net_num_changed_unreachable_types() const
Getter of the number of changed types that are unreachable from public interfaces and that have *NOT*...
bool is_c_language(translation_unit::language l)
Test if a language enumerator designates the C language.
Definition: abg-ir.cc:1644
bool is_class_type(const type_or_decl_base &t)
Test whether a type is a class.
Definition: abg-ir.cc:10733
shared_ptr< function_type > function_type_sptr
Convenience typedef for a shared pointer on a function_type.
Definition: abg-fwd.h:205
bool is_member_function(const function_decl &f)
Test whether a function_decl is a member function.
Definition: abg-ir.cc:6450
bool report_local_qualified_type_changes(const qualified_type_diff &d, std::ostream &out, const std::string &indent) const
For a qualified_type_diff node, report the changes that are local.
const qualified_type_def_sptr first_qualified_type() const
Getter for the first qualified type of the diff.
void report_mem_header(ostream &out, size_t number, size_t num_filtered, diff_kind k, const string &section_name, const string &indent)
Output the header preceding the the report for insertion/deletion/change of a part of a class...
bool maybe_report_diff_for_variable(const decl_base_sptr &decl1, const decl_base_sptr &decl2, const diff_context_sptr &ctxt, ostream &out, const string &indent)
Report the differences between two generic variables.
void represent(const diff_context &ctxt, method_decl_sptr mem_fn, ostream &out)
Stream a string representation for a member function.
const array_type_def_sptr second_array() const
Getter for the second array of the diff.
void show_linkage_name_and_aliases(ostream &out, const string &indent, const elf_symbol &symbol, const string_elf_symbols_map_type &sym_map)
For a given symbol, emit a string made of its name and version. The string also contains the list of ...
This means that a diff node was marked as suppressed by a user-provided suppression specification...
const typedef_decl_sptr first_typedef_decl() const
Getter for the firt typedef_decl involved in the diff.
size_t net_num_vars_added() const
Getter for the net number of added variables.
shared_ptr< diff_context > diff_context_sptr
Convenience typedef for a shared pointer of diff_context.
Definition: abg-fwd.h:68
Abstraction of a diff between two typedef_decl.
ssize_t get_member_function_vtable_offset(const function_decl &f)
Get the vtable offset of a member function.
Definition: abg-ir.cc:6660
void sort_data_members(const string_decl_base_sptr_map &data_members, vector< decl_base_sptr > &sorted)
Sort a map of data members by the offset of their initial value.
This means the diff node (or at least one of its descendant nodes) carries access related changes...
const type_decl_sptr first_type_decl() const
Getter for the first subject of the type_decl_diff.
virtual bool diff_to_be_reported(const diff *d) const
Tests if the diff node is to be reported.
shared_ptr< var_decl > var_decl_sptr
Convenience typedef for a shared pointer on a var_decl.
Definition: abg-fwd.h:246
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:9482
The abstraction of a diff between two arrays.
size_t net_num_added_func_syms() const
Getter of the net number of added function symbols that are not referenced by any debug info...
size_t net_num_func_added() const
Getter for the net number of added functions.
class_or_union * is_class_or_union_type(const type_or_decl_base *t)
Test if a type is a class_or_union.
Definition: abg-ir.cc:10883
shared_ptr< typedef_decl > typedef_decl_sptr
Convenience typedef for a shared pointer on a typedef_decl.
Definition: abg-fwd.h:161
change_kind
A bitfield that gives callers of abigail::ir::equals() some insight about how different two internal ...
Definition: abg-ir.h:1308
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:27340
bool maybe_report_diff_for_member(const decl_base_sptr &decl1, const decl_base_sptr &decl2, const diff_context_sptr &ctxt, ostream &out, const string &indent)
Report the differences in access specifiers and static-ness for class members.
void report_local_function_type_changes(const function_type_diff &d, std::ostream &out, const std::string &indent) const
For a function_type_diff node, report the local changes carried by the diff node. ...
void sort_string_elf_symbol_map(const string_elf_symbol_map &map, vector< elf_symbol_sptr > &sorted)
Sort a map of string -> pointer to elf_symbol.
bool get_member_function_is_dtor(const function_decl &f)
Test whether a member function is a destructor.
Definition: abg-ir.cc:6536
Abstraction of a diff between two basic type declarations.
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.
bool soname_changed() const
Test if the soname of the underlying corpus has changed.
void report_underlying_changes_of_qualified_type(const qualified_type_diff &d, ostream &out, const string &indent) const
For a qualified_type_diff node, report the changes of its underlying type.
size_t net_num_removed_func_syms() const
Getter of the net number of removed function symbols that are not referenced by any debug info...
std::vector< enumerator > enumerators
Convenience typedef for a list of enumerator.
Definition: abg-ir.h:2699
The abstraction of the diff between two subrange types.
An abstraction of a diff between between two abi corpus.
vector< changed_enumerator > changed_enumerators_type
Convenience typedef for a vector of changed enumerators.
void maybe_report_interfaces_impacted_by_diff(const diff *d, ostream &out, const string &indent)
If a given diff node impacts some public interfaces, then report about those impacted interfaces on a...
diff_category
An enum for the different categories that a diff tree node falls into, regarding the kind of changes ...
Toplevel namespace for libabigail.
bool has_harmless_name_change(const decl_base_sptr &f, const decl_base_sptr &s)
Test if two decls represents a harmless name change.
shared_ptr< function_decl > function_decl_sptr
Convenience typedef for a shared pointer on a function_decl.
Definition: abg-fwd.h:263
bool reported_once() const
Tests if a report has already been emitted for the current diff.
void report_name_size_and_alignment_changes(decl_base_sptr first, decl_base_sptr second, diff_context_sptr ctxt, ostream &out, const string &indent)
Report the name, size and alignment changes of a type.
The declaration of the reporting types of libabigail's diff engine.
shared_ptr< parameter > parameter_sptr
Convenience typedef for a shared pointer on a function_decl::parameter.
Definition: abg-ir.h:3067
size_t net_num_vars_removed() const
Getter for the net number of removed variables.
shared_ptr< base_spec > base_spec_sptr
Convenience typedef.
Definition: abg-ir.h:4178
This means that a diff node was warked as being for a private type. That is, the diff node is meant t...
This means that a diff node in the sub-tree carries a harmless union change.
This means that a diff node in the sub-tree carries a harmless declaration name change. This is set only for name changes for data members and typedefs.
const array_type_def::subrange_sptr first_subrange() const
Getter of the first subrange of the current instance subrange_diff.
bool has_class_decl_only_def_change(const class_or_union_sptr &first, const class_or_union_sptr &second)
Test if two class_or_union_sptr are different just by the fact that one is decl-only and the other on...
diff_sptr leaf_underlying_type_diff() const
Getter for the diff between the most underlying non-qualified types of two qualified types...
size_t net_num_removed_var_syms() const
Getter of the net number of removed variable symbols that are not referenced by any debug info...
A diff node in this category is redundant. That means it's present as a child of a other nodes in the...
The abstraction of a diff between two references.
const decl_base * get_type_declaration(const type_base *t)
Get the declaration for a given type.
Definition: abg-ir.cc:10102
This means that a diff node in the sub-tree carries a type that was declaration-only and that is now ...
shared_ptr< type_or_decl_base > type_or_decl_base_sptr
A convenience typedef for a shared_ptr to type_or_decl_base.
Definition: abg-fwd.h:119
shared_ptr< type_decl > type_decl_sptr
Convenience typedef for a shared pointer on a type_decl.
Definition: abg-fwd.h:156
The abstraction of a diff between two pointers.
translation_unit * get_translation_unit(const decl_base &decl)
Return the translation unit a declaration belongs to.
Definition: abg-ir.cc:10185
size_t net_num_added_var_syms() const
Getter of the net number of added variable symbols that are not referenced by any debug info...
bool report_loc_info(const type_or_decl_base_sptr &tod, const diff_context &ctxt, ostream &out)
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
shared_ptr< elf_symbol > elf_symbol_sptr
A convenience typedef for a shared pointer to elf_symbol.
Definition: abg-ir.h:872
void maybe_report_data_members_replaced_by_anon_dm(const class_or_union_diff &d, ostream &out, const string &indent)
Report about data members replaced by an anonymous data member without changing the overall bit-layou...
#define ABG_ASSERT(cond)
This is a wrapper around the 'assert' glibc call. It allows for its argument to have side effects...
Definition: abg-fwd.h:1659
virtual bool diff_has_net_changes(const corpus_diff *d) const
Test if a given instance of corpus_diff carries changes whose reports are not suppressed by any suppr...
bool union_diff_has_harmless_changes(const diff *d)
Test if a union diff node does have changes that don't impact its size.
void report_size_and_alignment_changes(type_or_decl_base_sptr first, type_or_decl_base_sptr second, diff_context_sptr ctxt, ostream &out, const string &indent)
Report the size and alignment changes of a type.
type_base_sptr strip_typedef(const type_base_sptr type)
Recursively returns the the underlying type of a typedef. The return type should not be a typedef of ...
Definition: abg-ir.cc:6813
diff_sptr underlying_type_diff() const
Getter for the diff between the pointed-to types of the pointers of this diff.
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...
const diff_sptr & element_type_diff() const
Getter for the diff between the two types of array elements.
const type_decl_sptr second_type_decl() const
Getter for the second subject of the type_decl_diff.
const array_type_def::subrange_sptr second_subrange() const
Getter of the second subrange of the current instance subrange_diff.
bool get_member_function_is_ctor(const function_decl &f)
Test whether a member function is a constructor.
Definition: abg-ir.cc:6477
const function_type_sptr second_function_type() const
Getter for the second subject of the diff.
shared_ptr< enum_type_decl > enum_type_decl_sptr
Convenience typedef for shared pointer to a enum_type_decl.
Definition: abg-fwd.h:169
shared_ptr< base_diff > base_diff_sptr
Convenience typedef for a shared pointer to a base_diff type.
void sort_changed_enumerators(const string_changed_enumerator_map &enumerators_map, changed_enumerators_type &sorted)
Sort a map of changed enumerators.
shared_ptr< class_decl > class_decl_sptr
Convenience typedef for a shared pointer on a class_decl.
Definition: abg-fwd.h:187
bool to_be_reported() const
Test if this diff tree node should be reported.
const function_type_sptr first_function_type() const
Getter for the first subject of the diff.
const typedef_decl_sptr second_typedef_decl() const
Getter for the second typedef_decl involved in the diff.
bool get_member_is_static(const decl_base &d)
Gets a flag saying if a class member is static or not.
Definition: abg-ir.cc:5725
This means that a diff node in the sub-tree carries an addition or removal of a non-virtual member fu...
reference_type_def_sptr second_reference() const
Getter for the second reference of the diff.
The private data and functions of the abigail::ir::comparison types.
void report_non_type_typedef_changes(const typedef_diff &d, std::ostream &out, const std::string &indent) const
For a typedef_diff node, report the local changes to the typedef rather the changes to its underlying...
void sort_string_function_decl_diff_sptr_map(const string_function_decl_diff_sptr_map &map, function_decl_diff_sptrs_type &sorted)
Sort the values of a string_function_decl_diff_sptr_map map and store the result in a vector of funct...
size_t net_num_removed_unreachable_types() const
Getter of the number of removed types that are not reachable from public interfaces and that have *NO...
const qualified_type_def_sptr second_qualified_type() const
Getter for the second qualified type of the diff.
reference_type_def_sptr first_reference() const
Getter for the first reference of the diff.
void represent_data_member(var_decl_sptr d, const diff_context_sptr &ctxt, ostream &out, const string &indent)
Stream a string representation for a data member.
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:9260
const diff_sptr & underlying_type_diff() const
Getter for the diff between the two referred-to types.
bool equals(const decl_base &l, const decl_base &r, change_kind *k)
Compares two instances of decl_base.
Definition: abg-ir.cc:5276
void maybe_report_diff_for_symbol(const elf_symbol_sptr &symbol1, const elf_symbol_sptr &symbol2, const diff_context_sptr &ctxt, ostream &out, const string &indent)
Report the difference between two ELF symbols, if there is any.
void maybe_report_unreachable_type_changes(const corpus_diff &d, const corpus_diff::diff_stats &s, const string &indent, ostream &out)
Report changes about types that are not reachable from global functions and variables, in a given.
const diff_sptr underlying_type_diff() const
Getter for the diff between the two underlying types of the typedefs.
This is a document class that aims to capture statistics about the changes carried by a corpus_diff t...
void maybe_report_base_class_reordering(const class_diff &d, ostream &out, const string &indent)
Report about the base classes of a class having been re-ordered.
shared_ptr< diff > diff_sptr
Convenience typedef for a shared_ptr for the diff class.
Definition: abg-fwd.h:76
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 architecture_changed() const
Test if the architecture of the underlying corpus has changed.
method_type_sptr is_method_type(const type_or_decl_base_sptr &t)
Test whether a type is a method_type.
Definition: abg-ir.cc:11197
Abstraction of a diff between two function types.
string get_pretty_representation(diff *d)
Get a copy of the pretty representation of a diff node.
const array_type_def_sptr first_array() const
Getter for the first array of the diff.
const diff_context_sptr context() const
Getter of the context of the current diff.
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...