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