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