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