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