1 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
2 // -*- Mode: C++ -*-
3 //
4 // Copyright (C) 2017-2022 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 diff node is to be reported by the current instance of
24 /// @ref leaf_reporter.
25 ///
26 /// A node is said to be reported by the current instance of @ref
27 /// leaf_reporter if the node carries local changes and if the node's
28 /// reporting hasn't been suppressed.
29 bool
diff_to_be_reported(const diff * d) const30 leaf_reporter::diff_to_be_reported(const diff *d) const
31 {return d && d->to_be_reported() && d->has_local_changes();}
32
33 /// Test if a given instance of @ref corpus_diff carries changes whose
34 /// reports are not suppressed by any suppression specification. In
35 /// effect, these are deemed incompatible ABI changes.
36 ///
37 /// @param d the @ref corpus_diff to consider
38 ///
39 /// @return true iff @p d carries subtype changes that are deemed
40 /// incompatible ABI changes.
41 bool
diff_has_net_changes(const corpus_diff * d) const42 leaf_reporter::diff_has_net_changes(const corpus_diff *d) const
43 {
44 if (!d)
45 return false;
46
47 const corpus_diff::diff_stats& stats = const_cast<corpus_diff*>(d)->
48 apply_filters_and_suppressions_before_reporting();
49
50 // Logic here should match emit_diff_stats.
51 return (d->architecture_changed()
52 || d->soname_changed()
53 || stats.net_num_func_removed()
54 || stats.net_num_leaf_type_changes()
55 || stats.net_num_leaf_func_changes()
56 || stats.net_num_func_added()
57 || stats.net_num_vars_removed()
58 || stats.net_num_leaf_var_changes()
59 || stats.net_num_vars_added()
60 || stats.net_num_removed_unreachable_types()
61 || stats.net_num_changed_unreachable_types()
62 || stats.net_num_added_unreachable_types()
63 || stats.net_num_removed_func_syms()
64 || stats.net_num_added_func_syms()
65 || stats.net_num_removed_var_syms()
66 || stats.net_num_added_var_syms());
67 }
68
69 /// Report the changes carried by the diffs contained in an instance
70 /// of @ref string_diff_ptr_map.
71 ///
72 /// @param mapp the set of diffs to report for.
73 ///
74 /// @param out the output stream to report the diffs to.
75 ///
76 /// @param indent the string to use for indentation.
77 static void
report_diffs(const reporter_base & r,const string_diff_ptr_map & mapp,ostream & out,const string & indent)78 report_diffs(const reporter_base& r,
79 const string_diff_ptr_map& mapp,
80 ostream& out,
81 const string& indent)
82 {
83 diff_ptrs_type sorted_diffs;
84 sort_string_diff_ptr_map(mapp, sorted_diffs);
85
86 bool started_to_emit = false;
87 for (diff_ptrs_type::const_iterator i = sorted_diffs.begin();
88 i != sorted_diffs.end();
89 ++i)
90 {
91 if (const var_diff *d = is_var_diff(*i))
92 if (is_data_member(d->first_var()))
93 continue;
94
95 if (r.diff_to_be_reported((*i)->get_canonical_diff()))
96 {
97 if (started_to_emit)
98 out << "\n";
99
100 string n = (*i)->first_subject()->get_pretty_representation();
101
102 out << indent << "'" << n;
103
104 report_loc_info((*i)->first_subject(),
105 *(*i)->context(), out);
106
107 out << "' changed:\n";
108
109 (*i)->get_canonical_diff()->report(out, indent + " ");
110 started_to_emit = true;
111 }
112 }
113 }
114
115 /// Report the type changes carried by an instance of @ref diff_maps.
116 ///
117 /// @param maps the set of diffs to report.
118 ///
119 /// @param out the output stream to report the diffs to.
120 ///
121 /// @param indent the string to use for indentation.
122 static void
report_type_changes_from_diff_maps(const leaf_reporter & reporter,const diff_maps & maps,ostream & out,const string & indent)123 report_type_changes_from_diff_maps(const leaf_reporter& reporter,
124 const diff_maps& maps,
125 ostream& out,
126 const string& indent)
127 {
128 // basic types
129 report_diffs(reporter, maps.get_type_decl_diff_map(), out, indent);
130
131 // enums
132 report_diffs(reporter, maps.get_enum_diff_map(), out, indent);
133
134 // classes
135 report_diffs(reporter, maps.get_class_diff_map(), out, indent);
136
137 // unions
138 report_diffs(reporter, maps.get_union_diff_map(), out, indent);
139
140 // typedefs
141 report_diffs(reporter, maps.get_typedef_diff_map(), out, indent);
142
143 // arrays
144 report_diffs(reporter, maps.get_array_diff_map(), out, indent);
145
146 // It doesn't make sense to report function type changes, does it?
147 // report_diffs(reporter, maps.get_function_type_diff_map(), out, indent);
148
149 // distinct diffs
150 report_diffs(reporter, maps.get_distinct_diff_map(), out, indent);
151
152 // function parameter diffs
153 report_diffs(reporter, maps.get_fn_parm_diff_map(), out, indent);
154 }
155
156 /// Report the changes carried by an instance of @ref diff_maps.
157 ///
158 /// @param maps the set of diffs to report.
159 ///
160 /// @param out the output stream to report the diffs to.
161 ///
162 /// @param indent the string to use for indentation.
163 void
report_changes_from_diff_maps(const diff_maps & maps,ostream & out,const string & indent) const164 leaf_reporter::report_changes_from_diff_maps(const diff_maps& maps,
165 ostream& out,
166 const string& indent) const
167 {
168 report_type_changes_from_diff_maps(*this, maps, out, indent);
169
170 // function decls
171 report_diffs(*this, maps.get_function_decl_diff_map(), out, indent);
172
173 // var decl
174 report_diffs(*this, maps.get_var_decl_diff_map(), out, indent);
175 }
176
177 /// Report the changes carried by a @ref typedef_diff node.
178 ///
179 /// @param out the output stream to report to.
180 ///
181 /// @param indent the white space string to use for indentation.
182 void
report(const typedef_diff & d,ostream & out,const string & indent) const183 leaf_reporter::report(const typedef_diff& d,
184 ostream& out,
185 const string& indent) const
186 {
187 if (!diff_to_be_reported(&d))
188 return;
189
190 // all changes carried by a typedef_diff are considered local, so
191 // let's just call the default reporter here.
192 default_reporter::report(d, out, indent);
193
194 maybe_report_interfaces_impacted_by_diff(&d, out, indent);
195 }
196
197 /// Report the changes carried by a @ref qualified_type_diff node.
198 ///
199 /// @param out the output stream to report to.
200 ///
201 /// @param indent the white space string to use for indentation.
202 void
report(const qualified_type_diff & d,ostream & out,const string & indent) const203 leaf_reporter::report(const qualified_type_diff& d, ostream& out,
204 const string& indent) const
205 {
206 if (!diff_to_be_reported(&d))
207 return;
208
209 report_local_qualified_type_changes(d, out, indent);
210 }
211
212 /// Report the changes carried by a @ref pointer_diff node.
213 ///
214 /// Note that this function does nothing because a @ref pointer_diff
215 /// node never carries local changes.
216 void
report(const pointer_diff & d,ostream & out,const string & indent) const217 leaf_reporter::report(const pointer_diff &d,
218 ostream& out,
219 const string& indent) const
220 {
221 // Changes that modify the representation of a pointed-to type is
222 // considered local to the pointer type.
223 if (!diff_to_be_reported(&d))
224 return;
225
226 out << indent
227 << "pointer type changed from: '"
228 << d.first_pointer()->get_pretty_representation()
229 << "' to: '"
230 << d.second_pointer()->get_pretty_representation()
231 << "'\n";
232 }
233
234 /// Report the changes carried by a @ref reference_diff node.
235 ///
236 /// @param out the output stream to report to.
237 ///
238 /// @param indent the white space string to use for indentation.
239 void
report(const reference_diff & d,ostream & out,const string & indent) const240 leaf_reporter::report(const reference_diff& d,
241 ostream& out,
242 const string& indent) const
243 {
244 if (!diff_to_be_reported(&d))
245 return;
246
247 report_local_reference_type_changes(d, out, indent);
248 }
249
250 /// Report the changes carried by a @ref fn_parm_diff node.
251 ///
252 /// @param out the output stream to report to.
253 ///
254 /// @param indent the white space string to use for indentation.
255 void
report(const fn_parm_diff & d,ostream & out,const string & indent) const256 leaf_reporter::report(const fn_parm_diff& d,
257 ostream& out,
258 const string& indent) const
259 {
260 if (!diff_to_be_reported(&d))
261 return;
262
263 ABG_ASSERT(diff_to_be_reported(d.type_diff().get()));
264
265 function_decl::parameter_sptr f = d.first_parameter();
266
267 out << indent
268 << "parameter " << f->get_index();
269
270 report_loc_info(f, *d.context(), out);
271
272 out << " of type '"
273 << f->get_type_pretty_representation()
274 << "' changed:\n";
275 d.type_diff()->report(out, indent + " ");
276 }
277
278 /// Report the changes carried by a @ref function_type_diff node.
279 ///
280 /// @param out the output stream to report to.
281 ///
282 /// @param indent the white space string to use for indentation.
283 void
report(const function_type_diff & d,ostream & out,const string & indent) const284 leaf_reporter::report(const function_type_diff& d,
285 ostream& out,
286 const string& indent) const
287 {
288 if (!diff_to_be_reported(&d))
289 return;
290
291 report_local_function_type_changes(d, out, indent);
292
293 if (diff_to_be_reported(d.priv_->return_type_diff_.get()))
294 {
295 out << indent << "return type changed:\n";
296 d.priv_->return_type_diff_->report(out, indent + " ");
297 }
298
299 // Hmmh, the above was quick. Now report about function parameters;
300 //
301 // Report about the parameter types that have changed sub-types.
302 for (vector<fn_parm_diff_sptr>::const_iterator i =
303 d.priv_->sorted_subtype_changed_parms_.begin();
304 i != d.priv_->sorted_subtype_changed_parms_.end();
305 ++i)
306 {
307 diff_sptr dif = *i;
308 if (diff_to_be_reported(dif.get()))
309 dif->report(out, indent);
310 }
311 }
312
313 /// Report the changes carried by a @ref scope_diff node.
314 ///
315 /// @param out the output stream to report to.
316 ///
317 /// @param indent the white space string to use for indentation.
318 void
report(const scope_diff & d,ostream & out,const string & indent) const319 leaf_reporter::report(const scope_diff& d,
320 ostream& out,
321 const string& indent) const
322 {
323 if (!d.to_be_reported())
324 return;
325
326 // Report changed types.
327 unsigned num_changed_types = d.changed_types().size();
328 if (num_changed_types)
329 out << indent << "changed types:\n";
330
331 for (diff_sptrs_type::const_iterator dif = d.changed_types().begin();
332 dif != d.changed_types().end();
333 ++dif)
334 {
335 if (!*dif || !diff_to_be_reported((*dif).get()))
336 continue;
337
338 out << indent << " '"
339 << (*dif)->first_subject()->get_pretty_representation()
340 << "' changed:\n";
341 (*dif)->report(out, indent + " ");
342 }
343
344 // Report changed decls
345 unsigned num_changed_decls = d.changed_decls().size();
346 if (num_changed_decls)
347 out << indent << "changed declarations:\n";
348
349 for (diff_sptrs_type::const_iterator dif= d.changed_decls().begin();
350 dif != d.changed_decls().end ();
351 ++dif)
352 {
353 if (!*dif || !diff_to_be_reported((*dif).get()))
354 continue;
355
356 out << indent << " '"
357 << (*dif)->first_subject()->get_pretty_representation()
358 << "' was changed to '"
359 << (*dif)->second_subject()->get_pretty_representation() << "'";
360 report_loc_info((*dif)->second_subject(), *d.context(), out);
361 out << ":\n";
362
363 (*dif)->report(out, indent + " ");
364 }
365
366 // Report removed types/decls
367 for (string_decl_base_sptr_map::const_iterator i =
368 d.priv_->deleted_types_.begin();
369 i != d.priv_->deleted_types_.end();
370 ++i)
371 out << indent
372 << " '"
373 << i->second->get_pretty_representation()
374 << "' was removed\n";
375
376 if (d.priv_->deleted_types_.size())
377 out << "\n";
378
379 for (string_decl_base_sptr_map::const_iterator i =
380 d.priv_->deleted_decls_.begin();
381 i != d.priv_->deleted_decls_.end();
382 ++i)
383 out << indent
384 << " '"
385 << i->second->get_pretty_representation()
386 << "' was removed\n";
387
388 if (d.priv_->deleted_decls_.size())
389 out << "\n";
390
391 // Report added types/decls
392 bool emitted = false;
393 for (string_decl_base_sptr_map::const_iterator i =
394 d.priv_->inserted_types_.begin();
395 i != d.priv_->inserted_types_.end();
396 ++i)
397 {
398 // Do not report about type_decl as these are usually built-in
399 // types.
400 if (dynamic_pointer_cast<type_decl>(i->second))
401 continue;
402 out << indent
403 << " '"
404 << i->second->get_pretty_representation()
405 << "' was added\n";
406 emitted = true;
407 }
408
409 if (emitted)
410 out << "\n";
411
412 emitted = false;
413 for (string_decl_base_sptr_map::const_iterator i =
414 d.priv_->inserted_decls_.begin();
415 i != d.priv_->inserted_decls_.end();
416 ++i)
417 {
418 // Do not report about type_decl as these are usually built-in
419 // types.
420 if (dynamic_pointer_cast<type_decl>(i->second))
421 continue;
422 out << indent
423 << " '"
424 << i->second->get_pretty_representation()
425 << "' was added\n";
426 emitted = true;
427 }
428
429 if (emitted)
430 out << "\n";
431 }
432
433 /// Report the changes carried by a @ref array_diff node.
434 ///
435 /// @param out the output stream to report to.
436 ///
437 /// @param indent the white space string to use for indentation.
438 void
report(const array_diff & d,ostream & out,const string & indent) const439 leaf_reporter::report(const array_diff& d,
440 ostream& out,
441 const string& indent) const
442 {
443 if (!diff_to_be_reported(&d))
444 return;
445
446 RETURN_IF_BEING_REPORTED_OR_WAS_REPORTED_EARLIER3(d.first_array(),
447 d.second_array(),
448 "array type");
449
450 report_name_size_and_alignment_changes(d.first_array(),
451 d.second_array(),
452 d.context(),
453 out, indent);
454
455 diff_sptr dif = d.element_type_diff();
456 if (diff_to_be_reported(dif.get()))
457 {
458 string fn = ir::get_pretty_representation(is_type(dif->first_subject()));
459 // report array element type changes
460 out << indent << "array element type '"
461 << fn << "' changed: \n";
462 dif->report(out, indent + " ");
463 }
464
465 maybe_report_interfaces_impacted_by_diff(&d, out, indent);
466 }
467
468 /// Report the changes carried by a @ref class_or_union_diff node.
469 ///
470 /// @param out the output stream to report to.
471 ///
472 /// @param indent the white space string to use for indentation.
473 void
report(const class_or_union_diff & d,ostream & out,const string & indent) const474 leaf_reporter::report(const class_or_union_diff& d,
475 ostream& out,
476 const string& indent) const
477 {
478 if (!diff_to_be_reported(&d))
479 return;
480
481 class_or_union_sptr first = d.first_class_or_union(),
482 second = d.second_class_or_union();
483
484 const diff_context_sptr& ctxt = d.context();
485
486 // Report class decl-only -> definition change.
487 if (ctxt->get_allowed_category() & TYPE_DECL_ONLY_DEF_CHANGE_CATEGORY)
488 if (filtering::has_class_decl_only_def_change(first, second))
489 {
490 string was =
491 first->get_is_declaration_only()
492 ? " was a declaration-only type"
493 : " was a defined type";
494
495 string is_now =
496 second->get_is_declaration_only()
497 ? " and is now a declaration-only type"
498 : " and is now a defined type";
499
500 out << indent << "type " << first->get_pretty_representation()
501 << was << is_now << "\n";
502 return;
503 }
504
505 // member functions
506 if (d.member_fns_changes())
507 {
508 // report deletions
509 int numdels = d.get_priv()->deleted_member_functions_.size();
510 size_t num_filtered =
511 d.get_priv()->count_filtered_deleted_mem_fns(ctxt);
512 if (numdels)
513 report_mem_header(out, numdels, num_filtered, del_kind,
514 "member function", indent);
515 for (string_member_function_sptr_map::const_iterator i =
516 d.get_priv()->deleted_member_functions_.begin();
517 i != d.get_priv()->deleted_member_functions_.end();
518 ++i)
519 {
520 if (!(ctxt->get_allowed_category()
521 & NON_VIRT_MEM_FUN_CHANGE_CATEGORY)
522 && !get_member_function_is_virtual(i->second))
523 continue;
524
525 method_decl_sptr mem_fun = i->second;
526 out << indent << " ";
527 represent(*ctxt, mem_fun, out);
528 }
529
530 // report insertions;
531 int numins = d.get_priv()->inserted_member_functions_.size();
532 num_filtered = d.get_priv()->count_filtered_inserted_mem_fns(ctxt);
533 if (numins)
534 report_mem_header(out, numins, num_filtered, ins_kind,
535 "member function", indent);
536 for (string_member_function_sptr_map::const_iterator i =
537 d.get_priv()->inserted_member_functions_.begin();
538 i != d.get_priv()->inserted_member_functions_.end();
539 ++i)
540 {
541 if (!(ctxt->get_allowed_category()
542 & NON_VIRT_MEM_FUN_CHANGE_CATEGORY)
543 && !get_member_function_is_virtual(i->second))
544 continue;
545
546 method_decl_sptr mem_fun = i->second;
547 out << indent << " ";
548 represent(*ctxt, mem_fun, out);
549 }
550
551 // report member function with sub-types changes
552 int numchanges = d.get_priv()->sorted_changed_member_functions_.size();
553 if (numchanges)
554 report_mem_header(out, change_kind, "member function", indent);
555 for (function_decl_diff_sptrs_type::const_iterator i =
556 d.get_priv()->sorted_changed_member_functions_.begin();
557 i != d.get_priv()->sorted_changed_member_functions_.end();
558 ++i)
559 {
560 if (!(ctxt->get_allowed_category()
561 & NON_VIRT_MEM_FUN_CHANGE_CATEGORY)
562 && !(get_member_function_is_virtual
563 ((*i)->first_function_decl()))
564 && !(get_member_function_is_virtual
565 ((*i)->second_function_decl())))
566 continue;
567
568 diff_sptr diff = *i;
569 if (!diff_to_be_reported(diff.get()))
570 continue;
571
572 string repr =
573 (*i)->first_function_decl()->get_pretty_representation();
574 out << indent << " '" << repr << "' has some changes:\n";
575 diff->report(out, indent + " ");
576 }
577 }
578
579 // data members
580 if (d.data_members_changes())
581 {
582 // report deletions
583 int numdels = d.class_or_union_diff::get_priv()->
584 get_deleted_non_static_data_members_number();
585 if (numdels)
586 {
587 report_mem_header(out, numdels, 0, del_kind,
588 "data member", indent);
589 vector<decl_base_sptr> sorted_dms;
590 sort_data_members
591 (d.class_or_union_diff::get_priv()->deleted_data_members_,
592 sorted_dms);
593 for (vector<decl_base_sptr>::const_iterator i = sorted_dms.begin();
594 i != sorted_dms.end();
595 ++i)
596 {
597 var_decl_sptr data_mem =
598 dynamic_pointer_cast<var_decl>(*i);
599 ABG_ASSERT(data_mem);
600 if (get_member_is_static(data_mem))
601 continue;
602 represent_data_member(data_mem, ctxt, out, indent + " ");
603 }
604 }
605
606 //report insertions
607 int numins =
608 d.class_or_union_diff::get_priv()->inserted_data_members_.size();
609 if (numins)
610 {
611 report_mem_header(out, numins, 0, ins_kind,
612 "data member", indent);
613 vector<decl_base_sptr> sorted_dms;
614 sort_data_members
615 (d.class_or_union_diff::get_priv()->inserted_data_members_,
616 sorted_dms);
617 for (vector<decl_base_sptr>::const_iterator i = sorted_dms.begin();
618 i != sorted_dms.end();
619 ++i)
620 {
621 var_decl_sptr data_mem =
622 dynamic_pointer_cast<var_decl>(*i);
623 ABG_ASSERT(data_mem);
624 represent_data_member(data_mem, ctxt, out, indent + " ");
625 }
626 }
627
628 // report changes
629 size_t numchanges = (d.sorted_changed_data_members().size()
630 + d.sorted_subtype_changed_data_members().size());
631
632 size_t num_filtered =
633 (d.count_filtered_changed_data_members(/*local_only=*/true)
634 + d.count_filtered_subtype_changed_data_members(/*local_only=*/true));
635
636 ABG_ASSERT(numchanges >= num_filtered);
637 size_t net_numchanges = numchanges - num_filtered;
638
639 if (net_numchanges)
640 {
641 report_mem_header(out, change_kind, "data member", indent);
642
643 for (var_diff_sptrs_type::const_iterator it =
644 d.sorted_changed_data_members().begin();
645 it != d.sorted_changed_data_members().end();
646 ++it)
647 if (diff_to_be_reported((*it).get()))
648 represent(*it, ctxt, out, indent + " ", /*local_only=*/true);
649
650 for (var_diff_sptrs_type::const_iterator it =
651 d.sorted_subtype_changed_data_members().begin();
652 it != d.sorted_subtype_changed_data_members().end();
653 ++it)
654 if (diff_to_be_reported((*it).get()))
655 represent(*it, ctxt, out, indent + " ", /*local_only=*/true);
656 }
657
658 // Report about data members replaced by an anonymous union data
659 // member.
660 maybe_report_data_members_replaced_by_anon_dm(d, out, indent);
661 }
662 }
663
664 /// Report the changes carried by a @ref class_diff node.
665 ///
666 /// @param out the output stream to report to.
667 ///
668 /// @param indent the white space string to use for indentation.
669 void
report(const class_diff & d,ostream & out,const string & indent) const670 leaf_reporter::report(const class_diff& d,
671 ostream& out,
672 const string& indent) const
673 {
674 if (!diff_to_be_reported(&d))
675 return;
676
677 RETURN_IF_BEING_REPORTED_OR_WAS_REPORTED_EARLIER(d.first_subject(),
678 d.second_subject());
679
680 string name = d.first_subject()->get_pretty_representation();
681
682 // Now report the changes about the differents parts of the type.
683 class_decl_sptr first = d.first_class_decl(),
684 second = d.second_class_decl();
685
686 report_name_size_and_alignment_changes(first, second, d.context(),
687 out, indent);
688
689 const diff_context_sptr& ctxt = d.context();
690 maybe_report_diff_for_member(first, second, ctxt, out, indent);
691
692 d.class_or_union_diff::report(out, indent);
693
694 maybe_report_base_class_reordering(d, out, indent);
695
696 maybe_report_interfaces_impacted_by_diff(&d, out, indent);
697
698 d.reported_once(true);
699 }
700
701 /// Report the changes carried by a @ref union_diff node.
702 ///
703 /// @param out the output stream to report to.
704 ///
705 /// @param indent the white space string to use for indentation.
706 void
report(const union_diff & d,ostream & out,const string & indent) const707 leaf_reporter::report(const union_diff& d,
708 ostream& out,
709 const string& indent) const
710 {
711 if (!diff_to_be_reported(&d))
712 return;
713
714 RETURN_IF_BEING_REPORTED_OR_WAS_REPORTED_EARLIER(d.first_subject(),
715 d.second_subject());
716
717 // Now report the changes about the differents parts of the type.
718 union_decl_sptr first = d.first_union_decl(), second = d.second_union_decl();
719
720 report_name_size_and_alignment_changes(first, second, d.context(),
721 out, indent);
722
723 maybe_report_diff_for_member(first, second,d. context(), out, indent);
724
725 d.class_or_union_diff::report(out, indent);
726
727 maybe_report_interfaces_impacted_by_diff(&d, out, indent);
728 }
729
730 /// Report the changes carried by a @ref distinct_diff node.
731 ///
732 /// @param out the output stream to report to.
733 ///
734 /// @param indent the white space string to use for indentation.
735 void
report(const distinct_diff & d,ostream & out,const string & indent) const736 leaf_reporter::report(const distinct_diff& d,
737 ostream& out,
738 const string& indent) const
739 {
740 if (!diff_to_be_reported(&d))
741 return;
742
743 type_or_decl_base_sptr f = d.first(), s = d.second();
744
745 string f_repr = f ? f->get_pretty_representation() : "'void'";
746 string s_repr = s ? s->get_pretty_representation() : "'void'";
747
748 diff_sptr diff = d.compatible_child_diff();
749
750 string compatible = diff ? " to compatible type '": " to '";
751
752 out << indent << "entity changed from '" << f_repr << "'"
753 << compatible << s_repr << "'";
754 report_loc_info(s, *d.context(), out);
755 out << "\n";
756
757 report_size_and_alignment_changes(f, s, d.context(), out, indent);
758 maybe_report_interfaces_impacted_by_diff(&d, out, indent);
759 }
760
761 /// Report the changes carried by a @ref function_decl_diff node.
762 ///
763 /// @param out the output stream to report to.
764 ///
765 /// @param indent the white space string to use for indentation.
766 void
report(const function_decl_diff & d,ostream & out,const string & indent) const767 leaf_reporter::report(const function_decl_diff& d,
768 ostream& out,
769 const string& indent) const
770 {
771 if (!diff_to_be_reported(&d))
772 return;
773
774 maybe_report_diff_for_member(d.first_function_decl(),
775 d.second_function_decl(),
776 d.context(), out, indent);
777
778 function_decl_sptr ff = d.first_function_decl();
779 function_decl_sptr sf = d.second_function_decl();
780
781 diff_context_sptr ctxt = d.context();
782 corpus_sptr fc = ctxt->get_corpus_diff()->first_corpus();
783 corpus_sptr sc = ctxt->get_corpus_diff()->second_corpus();
784
785 string qn1 = ff->get_qualified_name(), qn2 = sf->get_qualified_name(),
786 linkage_names1, linkage_names2;
787 elf_symbol_sptr s1 = ff->get_symbol(), s2 = sf->get_symbol();
788
789 if (s1)
790 linkage_names1 = s1->get_id_string();
791 if (s2)
792 linkage_names2 = s2->get_id_string();
793
794 // If the symbols for ff and sf have aliases, get all the names of
795 // the aliases;
796 if (fc && s1)
797 linkage_names1 =
798 s1->get_aliases_id_string(fc->get_fun_symbol_map());
799 if (sc && s2)
800 linkage_names2 =
801 s2->get_aliases_id_string(sc->get_fun_symbol_map());
802
803 /// If the set of linkage names of the function have changed, report
804 /// it.
805 if (linkage_names1 != linkage_names2)
806 {
807 if (linkage_names1.empty())
808 {
809 out << indent << ff->get_pretty_representation()
810 << " didn't have any linkage name, and it now has: '"
811 << linkage_names2 << "'\n";
812 }
813 else if (linkage_names2.empty())
814 {
815 out << indent << ff->get_pretty_representation()
816 << " did have linkage names '" << linkage_names1
817 << "'\n"
818 << indent << "but it doesn't have any linkage name anymore\n";
819 }
820 else
821 out << indent << "linkage names of "
822 << ff->get_pretty_representation()
823 << "\n" << indent << "changed from '"
824 << linkage_names1 << "' to '" << linkage_names2 << "'\n";
825 }
826
827 if (qn1 != qn2
828 && diff_to_be_reported(d.type_diff().get()))
829 {
830 // So the function has sub-type changes that are to be
831 // reported. Let's see if the function name changed too; if it
832 // did, then we'd report that change right before reporting the
833 // sub-type changes.
834 string frep1 = d.first_function_decl()->get_pretty_representation(),
835 frep2 = d.second_function_decl()->get_pretty_representation();
836 out << indent << "'" << frep1 << " {" << linkage_names1<< "}"
837 << "' now becomes '"
838 << frep2 << " {" << linkage_names2 << "}" << "'\n";
839 }
840
841 maybe_report_diff_for_symbol(ff->get_symbol(),
842 sf->get_symbol(),
843 ctxt, out, indent);
844
845 // Now report about inline-ness changes
846 if (ff->is_declared_inline() != sf->is_declared_inline())
847 {
848 out << indent;
849 if (ff->is_declared_inline())
850 out << sf->get_pretty_representation()
851 << " is not declared inline anymore\n";
852 else
853 out << sf->get_pretty_representation()
854 << " is now declared inline\n";
855 }
856
857 // Report about vtable offset changes.
858 if (is_member_function(ff) && is_member_function(sf))
859 {
860 bool ff_is_virtual = get_member_function_is_virtual(ff),
861 sf_is_virtual = get_member_function_is_virtual(sf);
862 if (ff_is_virtual != sf_is_virtual)
863 {
864 out << indent;
865 if (ff_is_virtual)
866 out << ff->get_pretty_representation()
867 << " is no more declared virtual\n";
868 else
869 out << ff->get_pretty_representation()
870 << " is now declared virtual\n";
871 }
872
873 size_t ff_vtable_offset = get_member_function_vtable_offset(ff),
874 sf_vtable_offset = get_member_function_vtable_offset(sf);
875 if (ff_is_virtual && sf_is_virtual
876 && (ff_vtable_offset != sf_vtable_offset))
877 {
878 out << indent
879 << "the vtable offset of " << ff->get_pretty_representation()
880 << " changed from " << ff_vtable_offset
881 << " to " << sf_vtable_offset << "\n";
882 }
883
884 // the classes of the two member functions.
885 class_decl_sptr fc =
886 is_class_type(is_method_type(ff->get_type())->get_class_type());
887 class_decl_sptr sc =
888 is_class_type(is_method_type(sf->get_type())->get_class_type());
889
890 // Detect if the virtual member function changes above
891 // introduced a vtable change or not.
892 bool vtable_added = false, vtable_removed = false;
893 if (!fc->get_is_declaration_only() && !sc->get_is_declaration_only())
894 {
895 vtable_added = !fc->has_vtable() && sc->has_vtable();
896 vtable_removed = fc->has_vtable() && !sc->has_vtable();
897 }
898 bool vtable_changed = ((ff_is_virtual != sf_is_virtual)
899 || (ff_vtable_offset != sf_vtable_offset));
900 bool incompatible_change = (ff_vtable_offset != sf_vtable_offset);
901
902 if (vtable_added)
903 out << indent
904 << " note that a vtable was added to "
905 << fc->get_pretty_representation()
906 << "\n";
907 else if (vtable_removed)
908 out << indent
909 << " note that the vtable was removed from "
910 << fc->get_pretty_representation()
911 << "\n";
912 else if (vtable_changed)
913 {
914 out << indent;
915 if (incompatible_change)
916 out << " note that this is an ABI incompatible "
917 "change to the vtable of ";
918 else
919 out << " note that this induces a change to the vtable of ";
920 out << fc->get_pretty_representation()
921 << "\n";
922 }
923
924 }
925
926 // Report about function type differences.
927 if (diff_to_be_reported(d.type_diff().get()))
928 d.type_diff()->report(out, indent);
929 }
930
931 /// Report the changes carried by a @ref var_diff node.
932 ///
933 /// @param out the output stream to report to.
934 ///
935 /// @param indent the white space string to use for indentation.
936 void
report(const var_diff & d,ostream & out,const string & indent) const937 leaf_reporter::report(const var_diff& d,
938 ostream& out,
939 const string& indent) const
940 {
941 if (!diff_to_be_reported(&d))
942 return;
943
944 decl_base_sptr first = d.first_var(), second = d.second_var();
945 string n = first->get_pretty_representation();
946
947 report_name_size_and_alignment_changes(first, second,
948 d.context(),
949 out, indent);
950
951 maybe_report_diff_for_symbol(d.first_var()->get_symbol(),
952 d.second_var()->get_symbol(),
953 d.context(), out, indent);
954
955 maybe_report_diff_for_member(first, second, d.context(), out, indent);
956
957 if (diff_sptr dif = d.type_diff())
958 {
959 if (diff_to_be_reported(dif.get()))
960 {
961 RETURN_IF_BEING_REPORTED_OR_WAS_REPORTED_EARLIER2(dif, "type");
962 out << indent << "type of variable changed:\n";
963 dif->report(out, indent + " ");
964 }
965 }
966 }
967
968 /// Report the changes carried by a @ref translation_unit_diff node.
969 ///
970 /// @param out the output stream to report to.
971 ///
972 /// @param indent the white space string to use for indentation.
973 void
report(const translation_unit_diff & d,ostream & out,const string & indent) const974 leaf_reporter::report(const translation_unit_diff& d,
975 ostream& out,
976 const string& indent) const
977 {
978 if (!d.to_be_reported())
979 return;
980
981 static_cast<const scope_diff&>(d).report(out, indent);
982 }
983
984 /// Report the changes carried by a @ref corpus_diff node.
985 ///
986 /// @param out the output stream to report to.
987 ///
988 /// @param indent the white space string to use for indentation.
989 void
report(const corpus_diff & d,ostream & out,const string & indent) const990 leaf_reporter::report(const corpus_diff& d,
991 ostream& out,
992 const string& indent) const
993 {
994 if (!d.has_changes())
995 return;
996
997 const corpus_diff::diff_stats &s =
998 const_cast<corpus_diff&>(d).
999 apply_filters_and_suppressions_before_reporting();
1000
1001 const diff_context_sptr& ctxt = d.context();
1002
1003 d.priv_->emit_diff_stats(s, out, indent);
1004 if (ctxt->show_stats_only())
1005 return;
1006 out << "\n";
1007
1008 if (ctxt->show_soname_change()
1009 && !d.priv_->sonames_equal_)
1010 out << indent << "SONAME changed from '"
1011 << d.first_corpus()->get_soname() << "' to '"
1012 << d.second_corpus()->get_soname() << "'\n\n";
1013
1014 if (ctxt->show_architecture_change()
1015 && !d.priv_->architectures_equal_)
1016 out << indent << "architecture changed from '"
1017 << d.first_corpus()->get_architecture_name() << "' to '"
1018 << d.second_corpus()->get_architecture_name() << "'\n\n";
1019
1020 /// Report removed/added/changed functions.
1021 if (ctxt->show_deleted_fns())
1022 {
1023 if (s.net_num_func_removed() == 1)
1024 out << indent << "1 Removed function:\n\n";
1025 else if (s.net_num_func_removed() > 1)
1026 out << indent << s.net_num_func_removed() << " Removed functions:\n\n";
1027
1028 bool emitted = false;
1029 vector<function_decl*>sorted_deleted_fns;
1030 sort_string_function_ptr_map(d.priv_->deleted_fns_, sorted_deleted_fns);
1031 for (vector<function_decl*>::const_iterator i =
1032 sorted_deleted_fns.begin();
1033 i != sorted_deleted_fns.end();
1034 ++i)
1035 {
1036 if (d.priv_->deleted_function_is_suppressed(*i))
1037 continue;
1038
1039 out << indent
1040 << " ";
1041 out << "[D] ";
1042 out << "'" << (*i)->get_pretty_representation() << "'";
1043 if (ctxt->show_linkage_names())
1044 {
1045 out << " {";
1046 show_linkage_name_and_aliases(out, "", *(*i)->get_symbol(),
1047 d.first_corpus()->get_fun_symbol_map());
1048 out << "}";
1049 }
1050 out << "\n";
1051 if (is_member_function(*i) && get_member_function_is_virtual(*i))
1052 {
1053 class_decl_sptr c =
1054 is_class_type(is_method_type((*i)->get_type())->get_class_type());
1055 out << indent
1056 << " "
1057 << "note that this removes an entry from the vtable of "
1058 << c->get_pretty_representation()
1059 << "\n";
1060 }
1061 emitted = true;
1062 }
1063 if (emitted)
1064 out << "\n";
1065 }
1066
1067 if (ctxt->show_added_fns())
1068 {
1069 if (s.net_num_func_added() == 1)
1070 out << indent << "1 Added function:\n\n";
1071 else if (s.net_num_func_added() > 1)
1072 out << indent << s.net_num_func_added()
1073 << " Added functions:\n\n";
1074 bool emitted = false;
1075 vector<function_decl*> sorted_added_fns;
1076 sort_string_function_ptr_map(d.priv_->added_fns_, sorted_added_fns);
1077 for (vector<function_decl*>::const_iterator i = sorted_added_fns.begin();
1078 i != sorted_added_fns.end();
1079 ++i)
1080 {
1081 if (d.priv_->added_function_is_suppressed(*i))
1082 continue;
1083
1084 out
1085 << indent
1086 << " ";
1087 out << "[A] ";
1088 out << "'"
1089 << (*i)->get_pretty_representation()
1090 << "'";
1091 if (ctxt->show_linkage_names())
1092 {
1093 out << " {";
1094 show_linkage_name_and_aliases
1095 (out, "", *(*i)->get_symbol(),
1096 d.second_corpus()->get_fun_symbol_map());
1097 out << "}";
1098 }
1099 out << "\n";
1100 if (is_member_function(*i) && get_member_function_is_virtual(*i))
1101 {
1102 class_decl_sptr c =
1103 is_class_type(is_method_type((*i)->get_type())->get_class_type());
1104 out << indent
1105 << " "
1106 << "note that this adds a new entry to the vtable of "
1107 << c->get_pretty_representation()
1108 << "\n";
1109 }
1110 emitted = true;
1111 }
1112 if (emitted)
1113 out << "\n";
1114 }
1115
1116 if (ctxt->show_changed_fns())
1117 {
1118 // Show changed functions.
1119 size_t num_changed = s.net_num_leaf_func_changes();
1120 if (num_changed == 1)
1121 out << indent << "1 function with some sub-type change:\n\n";
1122 else if (num_changed > 1)
1123 out << indent << num_changed
1124 << " functions with some sub-type change:\n\n";
1125
1126 vector<function_decl_diff_sptr> sorted_changed_fns;
1127 sort_string_function_decl_diff_sptr_map(d.priv_->changed_fns_map_,
1128 sorted_changed_fns);
1129 for (vector<function_decl_diff_sptr>::const_iterator i =
1130 sorted_changed_fns.begin();
1131 i != sorted_changed_fns.end();
1132 ++i)
1133 {
1134 diff_sptr diff = *i;
1135 if (!diff)
1136 continue;
1137
1138 if (diff_to_be_reported(diff.get()))
1139 {
1140 function_decl_sptr fn = (*i)->first_function_decl();
1141 out << indent << " [C] '"
1142 << fn->get_pretty_representation() << "'";
1143 report_loc_info((*i)->second_function_decl(), *ctxt, out);
1144 out << " has some sub-type changes:\n";
1145 if ((fn->get_symbol()->has_aliases()
1146 && !(is_member_function(fn)
1147 && get_member_function_is_ctor(fn))
1148 && !(is_member_function(fn)
1149 && get_member_function_is_dtor(fn)))
1150 || (is_c_language(get_translation_unit(fn)->get_language())
1151 && fn->get_name() != fn->get_linkage_name()))
1152 {
1153 int number_of_aliases =
1154 fn->get_symbol()->get_number_of_aliases();
1155 if (number_of_aliases == 0)
1156 {
1157 out << indent << " "
1158 << "Please note that the exported symbol of "
1159 "this function is "
1160 << fn->get_symbol()->get_id_string()
1161 << "\n";
1162 }
1163 else
1164 {
1165 out << indent << " "
1166 << "Please note that the symbol of this function is "
1167 << fn->get_symbol()->get_id_string()
1168 << "\n and it aliases symbol";
1169 if (number_of_aliases > 1)
1170 out << "s";
1171 out << ": "
1172 << fn->get_symbol()->get_aliases_id_string(false)
1173 << "\n";
1174 }
1175 }
1176 diff->report(out, indent + " ");
1177 // Extra spacing.
1178 out << "\n";
1179 }
1180 }
1181 // Changed functions have extra spacing already. No new line here.
1182 }
1183
1184 // Report removed/added/changed variables.
1185 if (ctxt->show_deleted_vars())
1186 {
1187 if (s.net_num_vars_removed() == 1)
1188 out << indent << "1 Removed variable:\n\n";
1189 else if (s.net_num_vars_removed() > 1)
1190 out << indent << s.net_num_vars_removed()
1191 << " Removed variables:\n\n";
1192 string n;
1193 bool emitted = false;
1194 vector<var_decl*> sorted_deleted_vars;
1195 sort_string_var_ptr_map(d.priv_->deleted_vars_, sorted_deleted_vars);
1196 for (vector<var_decl*>::const_iterator i =
1197 sorted_deleted_vars.begin();
1198 i != sorted_deleted_vars.end();
1199 ++i)
1200 {
1201 if (d.priv_->deleted_variable_is_suppressed(*i))
1202 continue;
1203
1204 n = (*i)->get_pretty_representation();
1205
1206 out << indent
1207 << " ";
1208 out << "[D] ";
1209 out << "'"
1210 << n
1211 << "'";
1212 if (ctxt->show_linkage_names())
1213 {
1214 out << " {";
1215 show_linkage_name_and_aliases(out, "", *(*i)->get_symbol(),
1216 d.first_corpus()->get_var_symbol_map());
1217 out << "}";
1218 }
1219 out << "\n";
1220 emitted = true;
1221 }
1222 if (emitted)
1223 out << "\n";
1224 }
1225
1226 if (ctxt->show_added_vars())
1227 {
1228 if (s.net_num_vars_added() == 1)
1229 out << indent << "1 Added variable:\n\n";
1230 else if (s.net_num_vars_added() > 1)
1231 out << indent << s.net_num_vars_added()
1232 << " Added variables:\n\n";
1233 string n;
1234 bool emitted = false;
1235 vector<var_decl*> sorted_added_vars;
1236 sort_string_var_ptr_map(d.priv_->added_vars_, sorted_added_vars);
1237 for (vector<var_decl*>::const_iterator i =
1238 sorted_added_vars.begin();
1239 i != sorted_added_vars.end();
1240 ++i)
1241 {
1242 if (d.priv_->added_variable_is_suppressed(*i))
1243 continue;
1244
1245 n = (*i)->get_pretty_representation();
1246
1247 out << indent
1248 << " ";
1249 out << "[A] ";
1250 out << "'" << n << "'";
1251 if (ctxt->show_linkage_names())
1252 {
1253 out << " {";
1254 show_linkage_name_and_aliases(out, "", *(*i)->get_symbol(),
1255 d.second_corpus()->get_var_symbol_map());
1256 out << "}";
1257 }
1258 out << "\n";
1259 emitted = true;
1260 }
1261 if (emitted)
1262 out << "\n";
1263 }
1264
1265 if (ctxt->show_changed_vars())
1266 {
1267 size_t num_changed = s.net_num_leaf_var_changes();
1268 if (num_changed == 1)
1269 out << indent << "1 Changed variable:\n\n";
1270 else if (num_changed > 1)
1271 out << indent << num_changed
1272 << " Changed variables:\n\n";
1273 string n1, n2;
1274 for (var_diff_sptrs_type::const_iterator i =
1275 d.priv_->sorted_changed_vars_.begin();
1276 i != d.priv_->sorted_changed_vars_.end();
1277 ++i)
1278 {
1279 diff_sptr diff = *i;
1280
1281 if (!diff)
1282 continue;
1283
1284 if (!diff_to_be_reported(diff.get()))
1285 continue;
1286
1287 n1 = diff->first_subject()->get_pretty_representation();
1288 n2 = diff->second_subject()->get_pretty_representation();
1289
1290 out << indent << " [C] '" << n1 << "' was changed";
1291 if (n1 != n2)
1292 out << " to '" << n2 << "'";
1293 report_loc_info(diff->second_subject(), *ctxt, out);
1294 out << ":\n";
1295 diff->report(out, indent + " ");
1296 // Extra spacing.
1297 out << "\n";
1298 }
1299 // Changed variables have extra spacing already. No new line here.
1300 }
1301
1302 // Report removed function symbols not referenced by any debug info.
1303 if (ctxt->show_symbols_unreferenced_by_debug_info()
1304 && d.priv_->deleted_unrefed_fn_syms_.size())
1305 {
1306 if (s.net_num_removed_func_syms() == 1)
1307 out << indent
1308 << "1 Removed function symbol not referenced by debug info:\n\n";
1309 else if (s.net_num_removed_func_syms() > 0)
1310 out << indent
1311 << s.net_num_removed_func_syms()
1312 << " Removed function symbols not referenced by debug info:\n\n";
1313
1314 bool emitted = false;
1315 vector<elf_symbol_sptr> sorted_deleted_unrefed_fn_syms;
1316 sort_string_elf_symbol_map(d.priv_->deleted_unrefed_fn_syms_,
1317 sorted_deleted_unrefed_fn_syms);
1318 for (vector<elf_symbol_sptr>::const_iterator i =
1319 sorted_deleted_unrefed_fn_syms.begin();
1320 i != sorted_deleted_unrefed_fn_syms.end();
1321 ++i)
1322 {
1323 if (d.priv_->deleted_unrefed_fn_sym_is_suppressed((*i).get()))
1324 continue;
1325
1326 out << indent << " ";
1327 out << "[D] ";
1328
1329 show_linkage_name_and_aliases(out, "", **i,
1330 d.first_corpus()->get_fun_symbol_map());
1331 out << "\n";
1332 emitted = true;
1333 }
1334 if (emitted)
1335 out << "\n";
1336 }
1337
1338 // Report added function symbols not referenced by any debug info.
1339 if (ctxt->show_symbols_unreferenced_by_debug_info()
1340 && ctxt->show_added_symbols_unreferenced_by_debug_info()
1341 && d.priv_->added_unrefed_fn_syms_.size())
1342 {
1343 if (s.net_num_added_func_syms() == 1)
1344 out << indent
1345 << "1 Added function symbol not referenced by debug info:\n\n";
1346 else if (s.net_num_added_func_syms() > 0)
1347 out << indent
1348 << s.net_num_added_func_syms()
1349 << " Added function symbols not referenced by debug info:\n\n";
1350
1351 bool emitted = false;
1352 vector<elf_symbol_sptr> sorted_added_unrefed_fn_syms;
1353 sort_string_elf_symbol_map(d.priv_->added_unrefed_fn_syms_,
1354 sorted_added_unrefed_fn_syms);
1355 for (vector<elf_symbol_sptr>::const_iterator i =
1356 sorted_added_unrefed_fn_syms.begin();
1357 i != sorted_added_unrefed_fn_syms.end();
1358 ++i)
1359 {
1360 if (d.priv_->added_unrefed_fn_sym_is_suppressed((*i).get()))
1361 continue;
1362
1363 out << indent << " ";
1364 out << "[A] ";
1365 show_linkage_name_and_aliases(out, "",
1366 **i,
1367 d.second_corpus()->get_fun_symbol_map());
1368 out << "\n";
1369 emitted = true;
1370 }
1371 if (emitted)
1372 out << "\n";
1373 }
1374
1375 // Report removed variable symbols not referenced by any debug info.
1376 if (ctxt->show_symbols_unreferenced_by_debug_info()
1377 && d.priv_->deleted_unrefed_var_syms_.size())
1378 {
1379 if (s.net_num_removed_var_syms() == 1)
1380 out << indent
1381 << "1 Removed variable symbol not referenced by debug info:\n\n";
1382 else if (s.net_num_removed_var_syms() > 0)
1383 out << indent
1384 << s.net_num_removed_var_syms()
1385 << " Removed variable symbols not referenced by debug info:\n\n";
1386
1387 bool emitted = false;
1388 vector<elf_symbol_sptr> sorted_deleted_unrefed_var_syms;
1389 sort_string_elf_symbol_map(d.priv_->deleted_unrefed_var_syms_,
1390 sorted_deleted_unrefed_var_syms);
1391 for (vector<elf_symbol_sptr>::const_iterator i =
1392 sorted_deleted_unrefed_var_syms.begin();
1393 i != sorted_deleted_unrefed_var_syms.end();
1394 ++i)
1395 {
1396 if (d.priv_->deleted_unrefed_var_sym_is_suppressed((*i).get()))
1397 continue;
1398
1399 out << indent << " ";
1400 out << "[D] ";
1401
1402 show_linkage_name_and_aliases
1403 (out, "", **i,
1404 d.first_corpus()->get_fun_symbol_map());
1405
1406 out << "\n";
1407 emitted = true;
1408 }
1409 if (emitted)
1410 out << "\n";
1411 }
1412
1413 // Report added variable symbols not referenced by any debug info.
1414 if (ctxt->show_symbols_unreferenced_by_debug_info()
1415 && ctxt->show_added_symbols_unreferenced_by_debug_info()
1416 && d.priv_->added_unrefed_var_syms_.size())
1417 {
1418 if (s.net_num_added_var_syms() == 1)
1419 out << indent
1420 << "1 Added variable symbol not referenced by debug info:\n\n";
1421 else if (s.net_num_added_var_syms() > 0)
1422 out << indent
1423 << s.net_num_added_var_syms()
1424 << " Added variable symbols not referenced by debug info:\n\n";
1425
1426 bool emitted = false;
1427 vector<elf_symbol_sptr> sorted_added_unrefed_var_syms;
1428 sort_string_elf_symbol_map(d.priv_->added_unrefed_var_syms_,
1429 sorted_added_unrefed_var_syms);
1430 for (vector<elf_symbol_sptr>::const_iterator i =
1431 sorted_added_unrefed_var_syms.begin();
1432 i != sorted_added_unrefed_var_syms.end();
1433 ++i)
1434 {
1435 if (d.priv_->added_unrefed_var_sym_is_suppressed((*i).get()))
1436 continue;
1437
1438 out << indent << " ";
1439 out << "[A] ";
1440 show_linkage_name_and_aliases(out, "", **i,
1441 d.second_corpus()->get_fun_symbol_map());
1442 out << "\n";
1443 emitted = true;
1444 }
1445 if (emitted)
1446 out << "\n";
1447 }
1448
1449 // Now show the changed types.
1450 const diff_maps& leaf_diffs = d.get_leaf_diffs();
1451 report_type_changes_from_diff_maps(*this, leaf_diffs, out, indent);
1452
1453 // Report added/removed/changed types not reacheable from public
1454 // interfaces.
1455 maybe_report_unreachable_type_changes(d, s, indent, out);
1456
1457 d.priv_->maybe_dump_diff_tree();
1458 }
1459 } // end namespace comparison
1460 } // end namespace abigail
1461