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