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