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