• 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 given instance of @ref corpus_diff carries changes whose
24 /// reports are not suppressed by any suppression specification.  In
25 /// effect, these are deemed incompatible ABI changes.
26 ///
27 /// @param d the @ref corpus_diff to consider
28 ///
29 /// @return true iff @p d carries subtype changes that are deemed
30 /// incompatible ABI changes.
31 bool
diff_has_net_changes(const corpus_diff * d) const32 default_reporter::diff_has_net_changes(const corpus_diff *d) const
33 {
34   if (!d)
35     return false;
36 
37   const corpus_diff::diff_stats& stats = const_cast<corpus_diff*>(d)->
38     apply_filters_and_suppressions_before_reporting();
39 
40   // Logic here should match emit_diff_stats.
41   return (d->architecture_changed()
42 	  || d->soname_changed()
43 	  || stats.net_num_func_removed()
44 	  || stats.net_num_func_changed()
45 	  || stats.net_num_func_added()
46 	  || stats.net_num_vars_removed()
47 	  || stats.net_num_vars_changed()
48 	  || stats.net_num_vars_added()
49 	  || stats.net_num_removed_unreachable_types()
50 	  || stats.net_num_changed_unreachable_types()
51 	  || stats.net_num_added_unreachable_types()
52 	  || stats.net_num_removed_func_syms()
53 	  || stats.net_num_added_func_syms()
54 	  || stats.net_num_removed_var_syms()
55 	  || stats.net_num_added_var_syms());
56 }
57 
58 /// Ouputs a report of the differences between of the two type_decl
59 /// involved in the @ref type_decl_diff.
60 ///
61 /// @param d the @ref type_decl_diff to consider.
62 ///
63 /// @param out the output stream to emit the report to.
64 ///
65 /// @param indent the string to use for indentatino indent.
66 void
report(const type_decl_diff & d,ostream & out,const string & indent) const67 default_reporter::report(const type_decl_diff& d,
68 			 ostream& out,
69 			 const string& indent) const
70 {
71   if (!d.to_be_reported())
72     return;
73 
74   type_decl_sptr f = d.first_type_decl(), s = d.second_type_decl();
75 
76   string name = f->get_pretty_representation();
77 
78   report_name_size_and_alignment_changes(f, s, d.context(),
79 					 out, indent);
80 
81   if (f->get_visibility() != s->get_visibility())
82     {
83       out << indent
84 	  << "visibility changed from '"
85 	  << f->get_visibility() << "' to '" << s->get_visibility()
86 	  << "\n";
87     }
88 
89   if (f->get_linkage_name() != s->get_linkage_name())
90     {
91       out << indent
92 	  << "mangled name changed from '"
93 	  << f->get_linkage_name() << "' to "
94 	  << s->get_linkage_name()
95 	  << "\n";
96     }
97 }
98 
99 /// Report the differences between the two enums.
100 ///
101 /// @param d the enum diff to consider.
102 ///
103 /// @param out the output stream to send the report to.
104 ///
105 /// @param indent the string to use for indentation.
106 void
report(const enum_diff & d,ostream & out,const string & indent) const107 default_reporter::report(const enum_diff& d, ostream& out,
108 			 const string& indent) const
109 {
110   if (!d.to_be_reported())
111     return;
112 
113   RETURN_IF_BEING_REPORTED_OR_WAS_REPORTED_EARLIER3(d.first_subject(),
114 						   d.second_subject(),
115 						   "enum type");
116 
117   string name = d.first_enum()->get_pretty_representation();
118 
119   enum_type_decl_sptr first = d.first_enum(), second = d.second_enum();
120 
121   const diff_context_sptr& ctxt = d.context();
122 
123   // Report enum decl-only <-> definition changes.
124   if (ctxt->get_allowed_category() & TYPE_DECL_ONLY_DEF_CHANGE_CATEGORY)
125     if (filtering::has_enum_decl_only_def_change(first, second))
126       {
127 	string was =
128 	  first->get_is_declaration_only()
129 	  ? " was a declaration-only enum type"
130 	  : " was a defined enum type";
131 
132 	string is_now =
133 	  second->get_is_declaration_only()
134 	  ? " and is now a declaration-only enum type"
135 	  : " and is now a defined enum type";
136 
137 	out << indent << "enum type " << name << was << is_now << "\n";
138 	return;
139       }
140 
141   report_name_size_and_alignment_changes(first, second, ctxt,
142 					 out, indent);
143   maybe_report_diff_for_member(first, second, ctxt, out, indent);
144 
145   //underlying type
146   d.underlying_type_diff()->report(out, indent);
147 
148   //report deletions/insertions/change of enumerators
149   unsigned numdels = d.deleted_enumerators().size();
150   unsigned numins = d.inserted_enumerators().size();
151   unsigned numchanges = d.changed_enumerators().size();
152 
153   if (numdels)
154     {
155       report_mem_header(out, numdels, 0, del_kind, "enumerator", indent);
156       enum_type_decl::enumerators sorted_deleted_enumerators;
157       sort_enumerators(d.deleted_enumerators(), sorted_deleted_enumerators);
158       for (enum_type_decl::enumerators::const_iterator i =
159 	     sorted_deleted_enumerators.begin();
160 	   i != sorted_deleted_enumerators.end();
161 	   ++i)
162 	{
163 	  out << indent
164 	      << "  '"
165 	      << i->get_qualified_name()
166 	      << "' value '"
167 	      << i->get_value()
168 	      << "'";
169 	  out << "\n";
170 	}
171     }
172   if (numins)
173     {
174       report_mem_header(out, numins, 0, ins_kind, "enumerator", indent);
175       enum_type_decl::enumerators sorted_inserted_enumerators;
176       sort_enumerators(d.inserted_enumerators(), sorted_inserted_enumerators);
177       for (enum_type_decl::enumerators::const_iterator i =
178 	     sorted_inserted_enumerators.begin();
179 	   i != sorted_inserted_enumerators.end();
180 	   ++i)
181 	{
182 	  out << indent
183 	      << "  '"
184 	      << i->get_qualified_name()
185 	      << "' value '"
186 	      << i->get_value()
187 	      << "'";
188 	  out << "\n";
189 	}
190     }
191   if (numchanges)
192     {
193       report_mem_header(out, numchanges, 0, change_kind, "enumerator", indent);
194       changed_enumerators_type sorted_changed_enumerators;
195       sort_changed_enumerators(d.changed_enumerators(),
196 			       sorted_changed_enumerators);
197       for (changed_enumerators_type::const_iterator i =
198 	     sorted_changed_enumerators.begin();
199 	   i != sorted_changed_enumerators.end();
200 	   ++i)
201 	{
202 	  out << indent
203 	      << "  '"
204 	      << i->first.get_qualified_name()
205 	      << "' from value '"
206 	      << i->first.get_value() << "' to '"
207 	      << i->second.get_value() << "'";
208 	  report_loc_info(second, *ctxt, out);
209 	  out << "\n";
210 	}
211     }
212 
213   if (ctxt->show_leaf_changes_only())
214     maybe_report_interfaces_impacted_by_diff(&d, out, indent);
215 
216   d.reported_once(true);
217 }
218 
219 /// For a @ref typedef_diff node, report the local changes to the
220 /// typedef rather the changes to its underlying type.
221 ///
222 /// Note that changes to the underlying type are also considered
223 /// local.
224 ///
225 /// @param d the @ref typedef_diff node to consider.
226 ///
227 /// @param out the output stream to report to.
228 ///
229 /// @param indent the white space string to use for indentation.
230 void
report_non_type_typedef_changes(const typedef_diff & d,ostream & out,const string & indent) const231 default_reporter::report_non_type_typedef_changes(const typedef_diff &d,
232 						  ostream& out,
233 						  const string& indent) const
234 {
235   if (!d.to_be_reported())
236     return;
237 
238   typedef_decl_sptr f = d.first_typedef_decl(), s = d.second_typedef_decl();
239 
240   maybe_report_diff_for_member(f, s, d.context(), out, indent);
241 
242   if ((filtering::has_harmless_name_change(f, s)
243        && ((d.context()->get_allowed_category()
244 	    & HARMLESS_DECL_NAME_CHANGE_CATEGORY)
245 	   || d.context()->show_leaf_changes_only()))
246       || f->get_qualified_name() != s->get_qualified_name())
247     {
248       out << indent << "typedef name changed from "
249 	  << f->get_qualified_name()
250 	  << " to "
251 	  << s->get_qualified_name();
252       report_loc_info(s, *d.context(), out);
253       out << "\n";
254     }
255 }
256 
257 /// Reports the difference between the two subjects of the diff in a
258 /// serialized form.
259 ///
260 /// @param d @ref typedef_diff node to consider.
261 ///
262 /// @param out the output stream to emit the report to.
263 ///
264 /// @param indent the indentation string to use.
265 void
report(const typedef_diff & d,ostream & out,const string & indent) const266 default_reporter::report(const typedef_diff& d,
267 			 ostream& out,
268 			 const string& indent) const
269 {
270   if (!d.to_be_reported())
271     return;
272 
273   typedef_decl_sptr f = d.first_typedef_decl(), s = d.second_typedef_decl();
274 
275   report_non_type_typedef_changes(d, out, indent);
276 
277   diff_sptr dif = d.underlying_type_diff();
278   if (dif && dif->has_changes())
279     {
280       if (dif->to_be_reported())
281 	{
282 	  RETURN_IF_BEING_REPORTED_OR_WAS_REPORTED_EARLIER2(dif,
283 							    "underlying type");
284 	  out << indent
285 	      << "underlying type '"
286 	      << dif->first_subject()->get_pretty_representation() << "'";
287 	  report_loc_info(dif->first_subject(), *d.context(), out);
288 	  out << " changed:\n";
289 	  dif->report(out, indent + "  ");
290 	}
291       else
292 	{
293 	  // The typedef change is to be reported, so we'll report its
294 	  // underlying type change too (even if its redundant),
295 	  // unless it's suppressed.  It makes sense in this
296 	  // particular case to emit the underlying type change
297 	  // because of the informative value underneath.  We don't
298 	  // want to just know about the local changes of the typedef,
299 	  // but also about the changes on the underlying type.
300 	  diff_category c = dif->get_category();
301 	  if (!(c & (SUPPRESSED_CATEGORY | PRIVATE_TYPE_CATEGORY)))
302 	    {
303 	      out << indent
304 		  << "underlying type '"
305 		  << dif->first_subject()->get_pretty_representation() << "'";
306 	      report_loc_info(dif->first_subject(), *d.context(), out);
307 	      out << " changed:\n";
308 	      if (c & REDUNDANT_CATEGORY)
309 		dif->set_category(c & ~REDUNDANT_CATEGORY);
310 	      dif->report(out, indent + "  ");
311 	      if (c & REDUNDANT_CATEGORY)
312 		dif->set_category(c | REDUNDANT_CATEGORY);
313 	    }
314 	}
315     }
316 
317   d.reported_once(true);
318 }
319 
320 /// For a @ref qualified_type_diff node, report the changes that are
321 /// local.
322 ///
323 /// @param d the @ref qualified_type_diff node to consider.
324 ///
325 /// @param out the output stream to emit the report to.
326 ///
327 /// @param indent the white string to use for indentation.
328 ///
329 /// @return true iff a local change has been emitted.  In this case,
330 /// the local change is a name change.
331 bool
report_local_qualified_type_changes(const qualified_type_diff & d,ostream & out,const string & indent) const332 default_reporter::report_local_qualified_type_changes(const qualified_type_diff& d,
333 						      ostream& out,
334 						      const string& indent) const
335 {
336   if (!d.to_be_reported())
337     return false;
338 
339   string fname = d.first_qualified_type()->get_pretty_representation(),
340     sname = d.second_qualified_type()->get_pretty_representation();
341 
342   if (fname != sname)
343     {
344       out << indent << "'" << fname << "' changed to '" << sname << "'\n";
345       return true;
346     }
347   return false;
348 }
349 
350 /// Report a @ref qualified_type_diff in a serialized form.
351 ///
352 /// @param d the @ref qualified_type_diff node to consider.
353 ///
354 /// @param out the output stream to serialize to.
355 ///
356 /// @param indent the string to use to indent the lines of the report.
357 void
report(const qualified_type_diff & d,ostream & out,const string & indent) const358 default_reporter::report(const qualified_type_diff& d, ostream& out,
359 			 const string& indent) const
360 {
361   if (!d.to_be_reported())
362     return;
363 
364   RETURN_IF_BEING_REPORTED_OR_WAS_REPORTED_EARLIER(d.first_qualified_type(),
365 						   d.second_qualified_type());
366 
367   if (report_local_qualified_type_changes(d, out, indent))
368     // The local change was emitted and it's a name change.  If the
369     // type name changed, the it means the type changed altogether.
370     // It makes a little sense to detail the changes in extenso here.
371     return;
372 
373   diff_sptr dif = d.leaf_underlying_type_diff();
374   ABG_ASSERT(dif);
375   ABG_ASSERT(dif->to_be_reported());
376   RETURN_IF_BEING_REPORTED_OR_WAS_REPORTED_EARLIER2(dif,
377 						    "unqualified "
378 						    "underlying type");
379 
380   string fltname = dif->first_subject()->get_pretty_representation();
381   out << indent << "in unqualified underlying type '" << fltname << "'";
382   report_loc_info(dif->second_subject(), *d.context(), out);
383   out << ":\n";
384   dif->report(out, indent + "  ");
385 }
386 
387 /// Report the @ref pointer_diff in a serialized form.
388 ///
389 /// @param d the @ref pointer_diff node to consider.
390 ///
391 /// @param out the stream to serialize the diff to.
392 ///
393 /// @param indent the prefix to use for the indentation of this
394 /// serialization.
395 void
report(const pointer_diff & d,ostream & out,const string & indent) const396 default_reporter::report(const pointer_diff& d, ostream& out,
397 			 const string& indent) const
398 {
399   if (!d.to_be_reported())
400     return;
401 
402   if (diff_sptr dif = d.underlying_type_diff())
403     {
404       RETURN_IF_BEING_REPORTED_OR_WAS_REPORTED_EARLIER2(dif, "pointed to type");
405       string repr = dif->first_subject()
406 	? dif->first_subject()->get_pretty_representation()
407 	: string("void");
408 
409       out << indent
410 	  << "in pointed to type '" << repr << "'";
411       report_loc_info(dif->second_subject(), *d.context(), out);
412       out << ":\n";
413       dif->report(out, indent + "  ");
414     }
415 }
416 
417 /// For a @reference_diff node, report the local changes carried by
418 /// the diff node.
419 ///
420 /// @param d the @reference_diff node to consider.
421 ///
422 /// @param out the output stream to report to.
423 ///
424 /// @param indent the white space indentation to use in the report.
425 void
report_local_reference_type_changes(const reference_diff & d,ostream & out,const string & indent) const426 default_reporter::report_local_reference_type_changes(const reference_diff& d,
427 						      ostream& out,
428 						      const string& indent) const
429 {
430   if (!d.to_be_reported())
431     return;
432 
433   reference_type_def_sptr f = d.first_reference(), s = d.second_reference();
434   ABG_ASSERT(f && s);
435 
436   string f_repr = f->get_pretty_representation(),
437     s_repr = s->get_pretty_representation();
438 
439   if (f->is_lvalue() != s->is_lvalue())
440     {
441       out << indent;
442       if (f->is_lvalue())
443 	out << "lvalue reference type '" << f_repr
444 	    << " became an rvalue reference type: '"
445 	    << s_repr
446 	    << "'\n";
447       else
448 	out << "rvalue reference type '" << f_repr
449 	    << " became an lvalue reference type: '"
450 	    << s_repr
451 	    << "'\n";
452     }
453   else if (!types_have_similar_structure(f->get_pointed_to_type().get(),
454 					 s->get_pointed_to_type().get()))
455     out << indent
456 	<< "reference type changed from: '"
457 	<< f_repr << "' to: '" << s_repr << "'\n";
458 }
459 
460 /// Report a @ref reference_diff in a serialized form.
461 ///
462 /// @param d the @ref reference_diff node to consider.
463 ///
464 /// @param out the output stream to serialize the dif to.
465 ///
466 /// @param indent the string to use for indenting the report.
467 void
report(const reference_diff & d,ostream & out,const string & indent) const468 default_reporter::report(const reference_diff& d, ostream& out,
469 			 const string& indent) const
470 {
471   if (!d.to_be_reported())
472     return;
473 
474   enum change_kind k = ir::NO_CHANGE_KIND;
475   equals(*d.first_reference(), *d.second_reference(), &k);
476 
477   if ((k & ALL_LOCAL_CHANGES_MASK) && !(k & SUBTYPE_CHANGE_KIND))
478     report_local_reference_type_changes(d, out, indent);
479 
480   if (k & SUBTYPE_CHANGE_KIND)
481     if (diff_sptr dif = d.underlying_type_diff())
482       {
483 	RETURN_IF_BEING_REPORTED_OR_WAS_REPORTED_EARLIER2(dif,
484 							  "referenced type");
485 
486 	out << indent
487 	    << "in referenced type '"
488 	    << dif->first_subject()->get_pretty_representation() << "'";
489 	report_loc_info(dif->second_subject(), *d.context(), out);
490 	out << ":\n";
491 	dif->report(out, indent + "  ");
492       }
493 }
494 
495 /// Emit a textual report about the a @ref fn_parm_diff instance.
496 ///
497 /// @param d the @ref fn_parm_diff to consider.
498 ///
499 /// @param out the output stream to emit the textual report to.
500 ///
501 /// @param indent the indentation string to use in the report.
502 void
report(const fn_parm_diff & d,ostream & out,const string & indent) const503 default_reporter::report(const fn_parm_diff& d, ostream& out,
504 			 const string& indent) const
505 {
506   function_decl::parameter_sptr f = d.first_parameter(),
507     s = d.second_parameter();
508 
509   // either the parameter has a sub-type change (if its type name
510   // hasn't changed) or it has a "grey" change (that is, a change that
511   // changes his type name w/o changing the signature of the
512   // function).
513   bool has_sub_type_change =
514     type_has_sub_type_changes(d.first_parameter()->get_type(),
515 			      d.second_parameter()->get_type());
516 
517   if (d.to_be_reported())
518     {
519       diff_sptr type_diff = d.type_diff();
520       ABG_ASSERT(type_diff->has_changes());
521 
522       out << indent;
523       if (f->get_is_artificial())
524 	out << "implicit ";
525       out << "parameter " << f->get_index();
526       report_loc_info(f, *d.context(), out);
527       out << " of type '"
528 	  << f->get_type_pretty_representation();
529 
530       if (has_sub_type_change)
531 	out << "' has sub-type changes:\n";
532       else
533 	out << "' changed:\n";
534 
535       type_diff->report(out, indent + "  ");
536     }
537 }
538 
539 /// For a @ref function_type_diff node, report the local changes
540 /// carried by the diff node.
541 ///
542 /// @param d the @ref function_type_diff node to consider.
543 ///
544 /// @param out the output stream to report to.
545 ///
546 /// @param indent the white space indentation string to use.
547 void
report_local_function_type_changes(const function_type_diff & d,ostream & out,const string & indent) const548 default_reporter::report_local_function_type_changes(const function_type_diff& d,
549 						     ostream& out,
550 						     const string& indent) const
551 
552 {
553   if (!d.to_be_reported())
554     return;
555 
556   function_type_sptr fft = d.first_function_type();
557   function_type_sptr sft = d.second_function_type();
558 
559   diff_context_sptr ctxt = d.context();
560 
561   // Report about the size of the function address
562   if (fft->get_size_in_bits() != sft->get_size_in_bits())
563     {
564       out << indent << "address size of function changed from "
565 	  << fft->get_size_in_bits()
566 	  << " bits to "
567 	  << sft->get_size_in_bits()
568 	  << " bits\n";
569     }
570 
571   // Report about the alignment of the function address
572   if (fft->get_alignment_in_bits()
573       != sft->get_alignment_in_bits())
574     {
575       out << indent << "address alignment of function changed from "
576 	  << fft->get_alignment_in_bits()
577 	  << " bits to "
578 	  << sft->get_alignment_in_bits()
579 	  << " bits\n";
580     }
581 
582   // Hmmh, the above was quick.  Now report about function parameters;
583   // this shouldn't be as straightforward.
584 
585   // Report about the parameters that got removed.
586   for (vector<function_decl::parameter_sptr>::const_iterator i =
587 	 d.priv_->sorted_deleted_parms_.begin();
588        i != d.priv_->sorted_deleted_parms_.end();
589        ++i)
590     {
591       out << indent << "parameter " << (*i)->get_index()
592 	  << " of type '" << (*i)->get_type_pretty_representation()
593 	  << "' was removed\n";
594     }
595 
596   // Report about the parameters that got added
597   for (vector<function_decl::parameter_sptr>::const_iterator i =
598 	 d.priv_->sorted_added_parms_.begin();
599        i != d.priv_->sorted_added_parms_.end();
600        ++i)
601     {
602       out << indent << "parameter " << (*i)->get_index()
603 	  << " of type '" << (*i)->get_type_pretty_representation()
604 	  << "' was added\n";
605     }
606 }
607 
608 /// Build and emit a textual report about a @ref function_type_diff.
609 ///
610 /// @param d the @ref function_type_diff to consider.
611 ///
612 /// @param out the output stream.
613 ///
614 /// @param indent the indentation string to use.
615 void
report(const function_type_diff & d,ostream & out,const string & indent) const616 default_reporter::report(const function_type_diff& d, ostream& out,
617 			 const string& indent) const
618 {
619   if (!d.to_be_reported())
620     return;
621 
622   function_type_sptr fft = d.first_function_type();
623   function_type_sptr sft = d.second_function_type();
624 
625   diff_context_sptr ctxt = d.context();
626   corpus_sptr fc = ctxt->get_first_corpus();
627   corpus_sptr sc = ctxt->get_second_corpus();
628 
629   // Report about return type differences.
630   if (d.priv_->return_type_diff_
631       && d.priv_->return_type_diff_->to_be_reported())
632     {
633       out << indent << "return type changed:\n";
634       d.priv_->return_type_diff_->report(out, indent + "  ");
635     }
636 
637   // Report about the parameter types that have changed sub-types.
638   for (vector<fn_parm_diff_sptr>::const_iterator i =
639 	 d.priv_->sorted_subtype_changed_parms_.begin();
640        i != d.priv_->sorted_subtype_changed_parms_.end();
641        ++i)
642     {
643       diff_sptr dif = *i;
644       if (dif && dif->to_be_reported())
645 	dif->report(out, indent);
646     }
647 
648   report_local_function_type_changes(d, out, indent);
649 
650 }
651 
652 /// Report a @ref array_diff in a serialized form.
653 ///
654 /// @param d the @ref array_diff to consider.
655 ///
656 /// @param out the output stream to serialize the dif to.
657 ///
658 /// @param indent the string to use for indenting the report.
659 void
report(const array_diff & d,ostream & out,const string & indent) const660 default_reporter::report(const array_diff& d, ostream& out,
661 			 const string& indent) const
662 {
663   if (!d.to_be_reported())
664     return;
665 
666   string name = d.first_array()->get_pretty_representation();
667   RETURN_IF_BEING_REPORTED_OR_WAS_REPORTED_EARLIER3(d.first_array(),
668 						    d.second_array(),
669 						    "array type");
670 
671   diff_sptr dif = d.element_type_diff();
672   if (dif->to_be_reported())
673     {
674       string fn = ir::get_pretty_representation(is_type(dif->first_subject()));
675       // report array element type changes
676       out << indent << "array element type '"
677 	  << fn << "' changed:\n";
678       dif->report(out, indent + "  ");
679     }
680 
681   report_name_size_and_alignment_changes(d.first_array(),
682 					 d.second_array(),
683 					 d.context(),
684 					 out, indent);
685 }
686 
687 /// Generates a report for an intance of @ref base_diff.
688 ///
689 /// @param d the @ref base_diff to consider.
690 ///
691 /// @param out the output stream to send the report to.
692 ///
693 /// @param indent the string to use for indentation.
694 void
report(const base_diff & d,ostream & out,const string & indent) const695 default_reporter::report(const base_diff& d, ostream& out,
696 			 const string& indent) const
697 {
698   if (!d.to_be_reported())
699     return;
700 
701   class_decl::base_spec_sptr f = d.first_base(), s = d.second_base();
702   string repr = f->get_base_class()->get_pretty_representation();
703   bool emitted = false;
704 
705   if (f->get_is_static() != s->get_is_static())
706     {
707       if (f->get_is_static())
708 	out << indent << "is no more static";
709       else
710 	out << indent << "now becomes static";
711       emitted = true;
712     }
713 
714   if ((d.context()->get_allowed_category() & ACCESS_CHANGE_CATEGORY)
715       && (f->get_access_specifier() != s->get_access_specifier()))
716     {
717       if (emitted)
718 	out << ", ";
719 
720       out << "has access changed from '"
721 	  << f->get_access_specifier()
722 	  << "' to '"
723 	  << s->get_access_specifier()
724 	  << "'";
725 
726       emitted = true;
727     }
728 
729   if (class_diff_sptr dif = d.get_underlying_class_diff())
730     {
731       if (dif->to_be_reported())
732 	{
733 	  if (emitted)
734 	    out << "\n";
735 	  dif->report(out, indent);
736 	}
737     }
738 }
739 
740 /// Report the changes carried by a @ref scope_diff.
741 ///
742 /// @param d the @ref scope_diff to consider.
743 ///
744 /// @param out the out stream to report the changes to.
745 ///
746 /// @param indent the string to use for indentation.
747 void
report(const scope_diff & d,ostream & out,const string & indent) const748 default_reporter::report(const scope_diff& d, ostream& out,
749 			 const string& indent) const
750 {
751   if (!d.to_be_reported())
752     return;
753 
754   // Report changed types.
755   unsigned num_changed_types = d.changed_types().size();
756   if (num_changed_types == 0)
757     ;
758   else if (num_changed_types == 1)
759     out << indent << "1 changed type:\n";
760   else
761     out << indent << num_changed_types << " changed types:\n";
762 
763   for (diff_sptrs_type::const_iterator dif = d.changed_types().begin();
764        dif != d.changed_types().end();
765        ++dif)
766     {
767       if (!*dif)
768 	continue;
769 
770       out << indent << "  '"
771 	  << (*dif)->first_subject()->get_pretty_representation()
772 	  << "' changed:\n";
773       (*dif)->report(out, indent + "    ");
774     }
775 
776   // Report changed decls
777   unsigned num_changed_decls = d.changed_decls().size();
778   if (num_changed_decls == 0)
779     ;
780   else if (num_changed_decls == 1)
781     out << indent << "1 changed declaration:\n";
782   else
783     out << indent << num_changed_decls << " changed declarations:\n";
784 
785   for (diff_sptrs_type::const_iterator dif= d.changed_decls().begin();
786        dif != d.changed_decls().end ();
787        ++dif)
788     {
789       if (!*dif)
790 	continue;
791 
792       out << indent << "  '"
793 	  << (*dif)->first_subject()->get_pretty_representation()
794 	  << "' was changed to '"
795 	  << (*dif)->second_subject()->get_pretty_representation() << "'";
796       report_loc_info((*dif)->second_subject(), *d.context(), out);
797       out << ":\n";
798 
799       (*dif)->report(out, indent + "    ");
800     }
801 
802   // Report removed types/decls
803   for (string_decl_base_sptr_map::const_iterator i =
804 	 d.priv_->deleted_types_.begin();
805        i != d.priv_->deleted_types_.end();
806        ++i)
807     out << indent
808 	<< "  '"
809 	<< i->second->get_pretty_representation()
810 	<< "' was removed\n";
811 
812   if (d.priv_->deleted_types_.size())
813     out << "\n";
814 
815   for (string_decl_base_sptr_map::const_iterator i =
816 	 d.priv_->deleted_decls_.begin();
817        i != d.priv_->deleted_decls_.end();
818        ++i)
819     out << indent
820 	<< "  '"
821 	<< i->second->get_pretty_representation()
822 	<< "' was removed\n";
823 
824   if (d.priv_->deleted_decls_.size())
825     out << "\n";
826 
827   // Report added types/decls
828   bool emitted = false;
829   for (string_decl_base_sptr_map::const_iterator i =
830 	 d.priv_->inserted_types_.begin();
831        i != d.priv_->inserted_types_.end();
832        ++i)
833     {
834       // Do not report about type_decl as these are usually built-in
835       // types.
836       if (dynamic_pointer_cast<type_decl>(i->second))
837 	continue;
838       out << indent
839 	  << "  '"
840 	  << i->second->get_pretty_representation()
841 	  << "' was added\n";
842       emitted = true;
843     }
844 
845   if (emitted)
846     out << "\n";
847 
848   emitted = false;
849   for (string_decl_base_sptr_map::const_iterator i =
850 	 d.priv_->inserted_decls_.begin();
851        i != d.priv_->inserted_decls_.end();
852        ++i)
853     {
854       // Do not report about type_decl as these are usually built-in
855       // types.
856       if (dynamic_pointer_cast<type_decl>(i->second))
857 	continue;
858       out << indent
859 	  << "  '"
860 	  << i->second->get_pretty_representation()
861 	  << "' was added\n";
862       emitted = true;
863     }
864 
865   if (emitted)
866     out << "\n";
867 }
868 
869 /// Report the changes carried by a @ref class_or_union_diff node in a
870 /// textual format.
871 ///
872 /// @param d the @ref class_or_union_diff node to consider.
873 ///
874 /// @param out the output stream to write the textual report to.
875 ///
876 /// @param indent the number of white space to use as indentation.
877 void
report(const class_or_union_diff & d,ostream & out,const string & indent) const878 default_reporter::report(const class_or_union_diff& d,
879 			 ostream& out,
880 			 const string& indent) const
881 {
882   if (!d.to_be_reported())
883     return;
884 
885   class_or_union_sptr first = d.first_class_or_union(),
886     second = d.second_class_or_union();
887 
888   const diff_context_sptr& ctxt = d.context();
889 
890   // Report class decl-only <-> definition change.
891   if (ctxt->get_allowed_category() & TYPE_DECL_ONLY_DEF_CHANGE_CATEGORY)
892     if (filtering::has_class_decl_only_def_change(first, second))
893       {
894 	string was =
895 	  first->get_is_declaration_only()
896 	  ? " was a declaration-only type"
897 	  : " was a defined type";
898 
899 	string is_now =
900 	  second->get_is_declaration_only()
901 	  ? " and is now a declaration-only type"
902 	  : " and is now a defined type";
903 
904 	out << indent << "type " << first->get_pretty_representation()
905 	    << was << is_now << "\n";
906 	return;
907       }
908 
909   // member functions
910   if (d.member_fns_changes())
911     {
912       // report deletions
913       int numdels = d.get_priv()->deleted_member_functions_.size();
914       size_t num_filtered =
915 	d.get_priv()->count_filtered_deleted_mem_fns(ctxt);
916       if (numdels)
917 	report_mem_header(out, numdels, num_filtered, del_kind,
918 			  "member function", indent);
919       for (class_or_union::member_functions::const_iterator i =
920 	     d.get_priv()->sorted_deleted_member_functions_.begin();
921 	   i != d.get_priv()->sorted_deleted_member_functions_.end();
922 	   ++i)
923 	{
924 	  if (!(ctxt->get_allowed_category()
925 		& NON_VIRT_MEM_FUN_CHANGE_CATEGORY)
926 	      && !get_member_function_is_virtual(*i))
927 	    continue;
928 
929 	  method_decl_sptr mem_fun = *i;
930 	  out << indent << "  ";
931 	  represent(*ctxt, mem_fun, out);
932 	}
933 
934       // report insertions;
935       int numins = d.get_priv()->inserted_member_functions_.size();
936       num_filtered = d.get_priv()->count_filtered_inserted_mem_fns(ctxt);
937       if (numins)
938 	report_mem_header(out, numins, num_filtered, ins_kind,
939 			  "member function", indent);
940       for (class_or_union::member_functions::const_iterator i =
941 	     d.get_priv()->sorted_inserted_member_functions_.begin();
942 	   i != d.get_priv()->sorted_inserted_member_functions_.end();
943 	   ++i)
944 	{
945 	  if (!(ctxt->get_allowed_category()
946 		& NON_VIRT_MEM_FUN_CHANGE_CATEGORY)
947 	      && !get_member_function_is_virtual(*i))
948 	    continue;
949 
950 	  method_decl_sptr mem_fun = *i;
951 	  out << indent << "  ";
952 	  represent(*ctxt, mem_fun, out);
953 	}
954 
955       // report member function with sub-types changes
956       int numchanges = d.get_priv()->sorted_changed_member_functions_.size();
957       num_filtered = d.get_priv()->count_filtered_changed_mem_fns(ctxt);
958       if (numchanges)
959 	report_mem_header(out, numchanges, num_filtered, change_kind,
960 			  "member function", indent);
961       for (function_decl_diff_sptrs_type::const_iterator i =
962 	     d.get_priv()->sorted_changed_member_functions_.begin();
963 	   i != d.get_priv()->sorted_changed_member_functions_.end();
964 	   ++i)
965 	{
966 	  if (!(ctxt->get_allowed_category()
967 		& NON_VIRT_MEM_FUN_CHANGE_CATEGORY)
968 	      && !(get_member_function_is_virtual
969 		   ((*i)->first_function_decl()))
970 	      && !(get_member_function_is_virtual
971 		   ((*i)->second_function_decl())))
972 	    continue;
973 
974 	  diff_sptr diff = *i;
975 	  if (!diff || !diff->to_be_reported())
976 	    continue;
977 
978 	  string repr =
979 	    (*i)->first_function_decl()->get_pretty_representation();
980 	  out << indent << "  '" << repr << "' has some sub-type changes:\n";
981 	  diff->report(out, indent + "    ");
982 	}
983     }
984 
985   // data members
986   if (d.data_members_changes())
987     {
988       // report deletions
989       int numdels = d.class_or_union_diff::get_priv()->
990 	get_deleted_non_static_data_members_number();
991       if (numdels)
992 	{
993 	  report_mem_header(out, numdels, 0, del_kind,
994 			    "data member", indent);
995 	  vector<decl_base_sptr> sorted_dms;
996 	  sort_data_members
997 	    (d.class_or_union_diff::get_priv()->deleted_data_members_,
998 	     sorted_dms);
999 	  for (vector<decl_base_sptr>::const_iterator i = sorted_dms.begin();
1000 	       i != sorted_dms.end();
1001 	       ++i)
1002 	    {
1003 	      var_decl_sptr data_mem =
1004 		dynamic_pointer_cast<var_decl>(*i);
1005 	      ABG_ASSERT(data_mem);
1006 	      if (get_member_is_static(data_mem))
1007 		continue;
1008 	      represent_data_member(data_mem, ctxt, out, indent + "  ");
1009 	    }
1010 	}
1011 
1012       //report insertions
1013       int numins =
1014 	d.class_or_union_diff::get_priv()->inserted_data_members_.size();
1015       if (numins)
1016 	{
1017 	  report_mem_header(out, numins, 0, ins_kind,
1018 			    "data member", indent);
1019 	  vector<decl_base_sptr> sorted_dms;
1020 	  sort_data_members
1021 	    (d.class_or_union_diff::get_priv()->inserted_data_members_,
1022 	     sorted_dms);
1023 	  for (vector<decl_base_sptr>::const_iterator i = sorted_dms.begin();
1024 	       i != sorted_dms.end();
1025 	       ++i)
1026 	    {
1027 	      var_decl_sptr data_mem =
1028 		dynamic_pointer_cast<var_decl>(*i);
1029 	      ABG_ASSERT(data_mem);
1030 	      represent_data_member(data_mem, ctxt, out, indent + "  ");
1031 	    }
1032 	}
1033 
1034       // report change
1035       size_t num_changes =
1036 	(d.sorted_subtype_changed_data_members().size()
1037 	 + d.sorted_changed_data_members().size());
1038 
1039       size_t num_changes_filtered =
1040 	(d.count_filtered_subtype_changed_data_members()
1041 	 + d.count_filtered_changed_data_members());
1042 
1043       if (num_changes)
1044 	{
1045 	  report_mem_header(out, num_changes, num_changes_filtered,
1046 			    change_kind, "data member", indent);
1047 
1048 	  for (var_diff_sptrs_type::const_iterator it =
1049 		 d.sorted_changed_data_members().begin();
1050 	       it != d.sorted_changed_data_members().end();
1051 	       ++it)
1052 	    if ((*it)->to_be_reported())
1053 	      represent(*it, ctxt, out, indent + "  ");
1054 
1055 	  for (var_diff_sptrs_type::const_iterator it =
1056 		 d.sorted_subtype_changed_data_members().begin();
1057 	       it != d.sorted_subtype_changed_data_members().end();
1058 	       ++it)
1059 	    if ((*it)->to_be_reported())
1060 	      represent(*it, ctxt, out, indent + "  ");
1061 	}
1062 
1063       // Report about data members replaced by an anonymous union data
1064       // member.
1065       maybe_report_data_members_replaced_by_anon_dm(d, out, indent);
1066     }
1067 
1068   // member types
1069   if (const edit_script& e = d.member_types_changes())
1070     {
1071       int numchanges =
1072 	d.class_or_union_diff::get_priv()->sorted_changed_member_types_.size();
1073       int numdels =
1074 	d.class_or_union_diff::get_priv()->deleted_member_types_.size();
1075 
1076       // report deletions
1077       if (numdels)
1078 	{
1079 	  report_mem_header(out, numdels, 0, del_kind,
1080 			    "member type", indent);
1081 
1082 	  for (string_decl_base_sptr_map::const_iterator i =
1083 		 d.class_or_union_diff::get_priv()->deleted_member_types_.begin();
1084 	       i != d.class_or_union_diff::get_priv()->deleted_member_types_.end();
1085 	       ++i)
1086 	    {
1087 	      decl_base_sptr mem_type = i->second;
1088 	      out << indent << "  '"
1089 		  << mem_type->get_pretty_representation()
1090 		  << "'\n";
1091 	    }
1092 	  out << "\n";
1093 	}
1094       // report changes
1095       if (numchanges)
1096 	{
1097 	  report_mem_header(out, numchanges, 0, change_kind,
1098 			    "member type", indent);
1099 
1100 	  for (diff_sptrs_type::const_iterator it =
1101 		 d.class_or_union_diff::get_priv()->sorted_changed_member_types_.begin();
1102 	       it != d.class_or_union_diff::get_priv()->sorted_changed_member_types_.end();
1103 	       ++it)
1104 	    {
1105 	      if (!(*it)->to_be_reported())
1106 		continue;
1107 
1108 	      type_or_decl_base_sptr o = (*it)->first_subject();
1109 	      type_or_decl_base_sptr n = (*it)->second_subject();
1110 	      out << indent << "  '"
1111 		  << o->get_pretty_representation()
1112 		  << "' changed ";
1113 	      report_loc_info(n, *ctxt, out);
1114 	      out << ":\n";
1115 	      (*it)->report(out, indent + "    ");
1116 	    }
1117 	  out << "\n";
1118 	}
1119 
1120       // report insertions
1121       int numins = e.num_insertions();
1122       ABG_ASSERT(numchanges <= numins);
1123       numins -= numchanges;
1124 
1125       if (numins)
1126 	{
1127 	  report_mem_header(out, numins, 0, ins_kind,
1128 			    "member type", indent);
1129 
1130 	  for (vector<insertion>::const_iterator i = e.insertions().begin();
1131 	       i != e.insertions().end();
1132 	       ++i)
1133 	    {
1134 	      type_base_sptr mem_type;
1135 	      for (vector<unsigned>::const_iterator j =
1136 		     i->inserted_indexes().begin();
1137 		   j != i->inserted_indexes().end();
1138 		   ++j)
1139 		{
1140 		  mem_type = second->get_member_types()[*j];
1141 		  if (!d.class_or_union_diff::get_priv()->
1142 		      member_type_has_changed(get_type_declaration(mem_type)))
1143 		    {
1144 		      out << indent << "  '"
1145 			  << get_type_declaration(mem_type)->
1146 			get_pretty_representation()
1147 			  << "'\n";
1148 		    }
1149 		}
1150 	    }
1151 	  out << "\n";
1152 	}
1153     }
1154 
1155   // member function templates
1156   if (const edit_script& e = d.member_fn_tmpls_changes())
1157     {
1158       // report deletions
1159       int numdels = e.num_deletions();
1160       if (numdels)
1161 	report_mem_header(out, numdels, 0, del_kind,
1162 			  "member function template", indent);
1163       for (vector<deletion>::const_iterator i = e.deletions().begin();
1164 	   i != e.deletions().end();
1165 	   ++i)
1166 	{
1167 	  member_function_template_sptr mem_fn_tmpl =
1168 	    first->get_member_function_templates()[i->index()];
1169 	  out << indent << "  '"
1170 	      << mem_fn_tmpl->as_function_tdecl()->get_pretty_representation()
1171 	      << "'\n";
1172 	}
1173 
1174       // report insertions
1175       int numins = e.num_insertions();
1176       if (numins)
1177 	report_mem_header(out, numins, 0, ins_kind,
1178 			  "member function template", indent);
1179       for (vector<insertion>::const_iterator i = e.insertions().begin();
1180 	   i != e.insertions().end();
1181 	   ++i)
1182 	{
1183 	  member_function_template_sptr mem_fn_tmpl;
1184 	  for (vector<unsigned>::const_iterator j =
1185 		 i->inserted_indexes().begin();
1186 	       j != i->inserted_indexes().end();
1187 	       ++j)
1188 	    {
1189 	      mem_fn_tmpl = second->get_member_function_templates()[*j];
1190 	      out << indent << "  '"
1191 		  << mem_fn_tmpl->as_function_tdecl()->
1192 		get_pretty_representation()
1193 		  << "'\n";
1194 	    }
1195 	}
1196     }
1197 
1198   // member class templates.
1199   if (const edit_script& e = d.member_class_tmpls_changes())
1200     {
1201       // report deletions
1202       int numdels = e.num_deletions();
1203       if (numdels)
1204 	report_mem_header(out, numdels, 0, del_kind,
1205 			  "member class template", indent);
1206       for (vector<deletion>::const_iterator i = e.deletions().begin();
1207 	   i != e.deletions().end();
1208 	   ++i)
1209 	{
1210 	  member_class_template_sptr mem_cls_tmpl =
1211 	    first->get_member_class_templates()[i->index()];
1212 	  out << indent << "  '"
1213 	      << mem_cls_tmpl->as_class_tdecl()->get_pretty_representation()
1214 	      << "'\n";
1215 	}
1216 
1217       // report insertions
1218       int numins = e.num_insertions();
1219       if (numins)
1220 	report_mem_header(out, numins, 0, ins_kind,
1221 			  "member class template", indent);
1222       for (vector<insertion>::const_iterator i = e.insertions().begin();
1223 	   i != e.insertions().end();
1224 	   ++i)
1225 	{
1226 	  member_class_template_sptr mem_cls_tmpl;
1227 	  for (vector<unsigned>::const_iterator j =
1228 		 i->inserted_indexes().begin();
1229 	       j != i->inserted_indexes().end();
1230 	       ++j)
1231 	    {
1232 	      mem_cls_tmpl = second->get_member_class_templates()[*j];
1233 	      out << indent << "  '"
1234 		  << mem_cls_tmpl->as_class_tdecl()
1235 		->get_pretty_representation()
1236 		  << "'\n";
1237 	    }
1238 	}
1239     }
1240 }
1241 
1242 /// Produce a basic report about the changes carried by a @ref
1243 /// class_diff node.
1244 ///
1245 /// @param d the @ref class_diff node to consider.
1246 ///
1247 /// @param out the output stream to report the changes to.
1248 ///
1249 /// @param indent the string to use as an indentation prefix in the
1250 /// report.
1251 void
report(const class_diff & d,ostream & out,const string & indent) const1252 default_reporter::report(const class_diff& d, ostream& out,
1253 			 const string& indent) const
1254 {
1255   if (!d.to_be_reported())
1256     return;
1257 
1258   string name = d.first_subject()->get_pretty_representation();
1259 
1260   RETURN_IF_BEING_REPORTED_OR_WAS_REPORTED_EARLIER(d.first_subject(),
1261 						   d.second_subject());
1262 
1263   d.currently_reporting(true);
1264 
1265   // Now report the changes about the differents parts of the type.
1266   class_decl_sptr first = d.first_class_decl(),
1267     second = d.second_class_decl();
1268 
1269   report_name_size_and_alignment_changes(first, second, d.context(),
1270 					 out, indent);
1271 
1272   const diff_context_sptr& ctxt = d.context();
1273   maybe_report_diff_for_member(first, second, ctxt, out, indent);
1274 
1275   // bases classes
1276   if (d.base_changes())
1277     {
1278       // Report deletions.
1279       int numdels = d.get_priv()->deleted_bases_.size();
1280       size_t numchanges = d.get_priv()->sorted_changed_bases_.size();
1281 
1282       if (numdels)
1283 	{
1284 	  report_mem_header(out, numdels, 0, del_kind,
1285 			    "base class", indent);
1286 
1287 	  for (class_decl::base_specs::const_iterator i
1288 		 = d.get_priv()->sorted_deleted_bases_.begin();
1289 	       i != d.get_priv()->sorted_deleted_bases_.end();
1290 	       ++i)
1291 	    {
1292 	      if (i != d.get_priv()->sorted_deleted_bases_.begin())
1293 		out << "\n";
1294 
1295 	      class_decl::base_spec_sptr base = *i;
1296 
1297 	      if (d.get_priv()->base_has_changed(base))
1298 		continue;
1299 	      out << indent << "  "
1300 		  << base->get_base_class()->get_pretty_representation();
1301 	      report_loc_info(base->get_base_class(), *d.context(), out);
1302 	    }
1303 	  out << "\n";
1304 	}
1305 
1306       // Report changes.
1307       size_t num_filtered = d.get_priv()->count_filtered_bases();
1308       if (numchanges)
1309 	{
1310 	  report_mem_header(out, numchanges, num_filtered, change_kind,
1311 			    "base class", indent);
1312 	  for (base_diff_sptrs_type::const_iterator it =
1313 		 d.get_priv()->sorted_changed_bases_.begin();
1314 	       it != d.get_priv()->sorted_changed_bases_.end();
1315 	       ++it)
1316 	    {
1317 	      base_diff_sptr diff = *it;
1318 	      if (!diff || !diff->to_be_reported())
1319 		continue;
1320 
1321 	      class_decl::base_spec_sptr o = diff->first_base();
1322 	      out << indent << "  '"
1323 		  << o->get_base_class()->get_pretty_representation() << "'";
1324 	      report_loc_info(o->get_base_class(), *d.context(), out);
1325 	      out << " changed:\n";
1326 	      diff->report(out, indent + "    ");
1327 	    }
1328 	}
1329 
1330       //Report insertions.
1331       int numins = d.get_priv()->inserted_bases_.size();
1332       if (numins)
1333 	{
1334 	  report_mem_header(out, numins, 0, ins_kind,
1335 			    "base class", indent);
1336 
1337 	  for (class_decl::base_specs::const_iterator i =
1338 		 d.get_priv()->sorted_inserted_bases_.begin();
1339 	       i != d.get_priv()->sorted_inserted_bases_.end();
1340 	       ++i)
1341 	    {
1342 	      class_decl_sptr b = (*i)->get_base_class();
1343 	      out << indent << "  " << b->get_pretty_representation();
1344 	      report_loc_info(b, *ctxt, out);
1345 	      out << "\n";
1346 	    }
1347 	}
1348 
1349       // Report base classes re-organisation
1350       maybe_report_base_class_reordering(d, out, indent);
1351     }
1352 
1353   d.class_or_union_diff::report(out, indent);
1354 
1355   d.currently_reporting(false);
1356 
1357   d.reported_once(true);
1358 }
1359 
1360 /// Produce a basic report about the changes carried by a @ref
1361 /// union_diff node.
1362 ///
1363 /// @param d the @ref union_diff node to consider.
1364 ///
1365 /// @param out the output stream to report the changes to.
1366 ///
1367 /// @param indent the string to use as an indentation prefix in the
1368 /// report.
1369 void
report(const union_diff & d,ostream & out,const string & indent) const1370 default_reporter::report(const union_diff& d, ostream& out,
1371 			 const string& indent) const
1372 {
1373   RETURN_IF_BEING_REPORTED_OR_WAS_REPORTED_EARLIER(d.first_subject(),
1374 						   d.second_subject());
1375 
1376   d.currently_reporting(true);
1377 
1378   // Now report the changes about the differents parts of the type.
1379   union_decl_sptr first = d.first_union_decl(), second = d.second_union_decl();
1380 
1381   report_name_size_and_alignment_changes(first, second, d.context(),
1382 					 out, indent);
1383 
1384   maybe_report_diff_for_member(first, second,d. context(), out, indent);
1385 
1386   d.class_or_union_diff::report(out, indent);
1387 
1388   if (d.context()->get_allowed_category() & HARMLESS_UNION_CHANGE_CATEGORY
1389       && filtering::union_diff_has_harmless_changes(&d))
1390     {
1391       // The user wants to see harmless changes and the union diff we
1392       // are looking at does carry some harmless changes.  Let's show
1393       // the "before" and "after" carried by the diff node.
1394       out << indent << "type changed from:\n"
1395 	  << get_class_or_union_flat_representation(first, indent + "  ",
1396 						    /*one_line=*/true,
1397 						    /*internal=*/false,
1398 						    /*qualified_names=*/false)
1399 	  << "\n"
1400 	  << indent << "to:\n"
1401 	  << get_class_or_union_flat_representation(second, indent + "  ",
1402 						    /*one_line=*/true,
1403 						    /*internal=*/false,
1404 						    /*qualified_names=*/false)
1405 	  << "\n";
1406     }
1407 
1408   d.currently_reporting(false);
1409 
1410   d.reported_once(true);
1411 }
1412 
1413 /// Emit a report about the changes carried by a @ref distinct_diff
1414 /// node.
1415 ///
1416 /// @param d the @ref distinct_diff node to consider.
1417 ///
1418 /// @param out the output stream to send the diff report to.
1419 ///
1420 /// @param indent the indentation string to use in the report.
1421 void
report(const distinct_diff & d,ostream & out,const string & indent) const1422 default_reporter::report(const distinct_diff& d, ostream& out,
1423 			 const string& indent) const
1424 {
1425   if (!d.to_be_reported())
1426     return;
1427 
1428   type_or_decl_base_sptr f = d.first(), s = d.second();
1429 
1430   string f_repr = f ? f->get_pretty_representation() : "'void'";
1431   string s_repr = s ? s->get_pretty_representation() : "'void'";
1432 
1433   diff_sptr diff = d.compatible_child_diff();
1434 
1435   string compatible = diff ? " to compatible type '": " to '";
1436 
1437   out << indent << "entity changed from '" << f_repr << "'"
1438       << compatible << s_repr << "'";
1439   report_loc_info(s, *d.context(), out);
1440   out << "\n";
1441 
1442   type_base_sptr fs = strip_typedef(is_type(f)),
1443     ss = strip_typedef(is_type(s));
1444 
1445   if (diff)
1446     diff->report(out, indent + "  ");
1447   else
1448     report_size_and_alignment_changes(f, s, d.context(), out, indent);
1449 }
1450 
1451 /// Serialize a report of the changes encapsulated in the current
1452 /// instance of @ref function_decl_diff over to an output stream.
1453 ///
1454 /// @param d the @ref function_decl_diff node to consider.
1455 ///
1456 /// @param out the output stream to serialize the report to.
1457 ///
1458 /// @param indent the string to use an an indentation prefix.
1459 void
report(const function_decl_diff & d,ostream & out,const string & indent) const1460 default_reporter::report(const function_decl_diff& d, ostream& out,
1461 			 const string& indent) const
1462 {
1463   if (!d.to_be_reported())
1464     return;
1465 
1466   maybe_report_diff_for_member(d.first_function_decl(),
1467 			       d.second_function_decl(),
1468 			       d.context(), out, indent);
1469 
1470   function_decl_sptr ff = d.first_function_decl();
1471   function_decl_sptr sf = d.second_function_decl();
1472 
1473   diff_context_sptr ctxt = d.context();
1474   corpus_sptr fc = ctxt->get_first_corpus();
1475   corpus_sptr sc = ctxt->get_second_corpus();
1476 
1477 
1478   string qn1 = ff->get_qualified_name(), qn2 = sf->get_qualified_name(),
1479     linkage_names1, linkage_names2;
1480   elf_symbol_sptr s1 = ff->get_symbol(), s2 = sf->get_symbol();
1481 
1482   if (s1)
1483     linkage_names1 = s1->get_id_string();
1484   if (s2)
1485     linkage_names2 = s2->get_id_string();
1486 
1487   // If the symbols for ff and sf have aliases, get all the names of
1488   // the aliases;
1489   if (fc && s1)
1490     linkage_names1 =
1491       s1->get_aliases_id_string(fc->get_fun_symbol_map());
1492   if (sc && s2)
1493     linkage_names2 =
1494       s2->get_aliases_id_string(sc->get_fun_symbol_map());
1495 
1496   /// If the set of linkage names of the function have changed, report
1497   /// it.
1498   if (linkage_names1 != linkage_names2)
1499     {
1500       if (linkage_names1.empty())
1501 	{
1502 	  out << indent << ff->get_pretty_representation()
1503 	      << " didn't have any linkage name, and it now has: '"
1504 	      << linkage_names2 << "'\n";
1505 	}
1506       else if (linkage_names2.empty())
1507 	{
1508 	  out << indent << ff->get_pretty_representation()
1509 	      << " did have linkage names '" << linkage_names1
1510 	      << "'\n"
1511 	      << indent << "but it doesn't have any linkage name anymore\n";
1512 	}
1513       else
1514 	out << indent << "linkage names of "
1515 	    << ff->get_pretty_representation()
1516 	    << "\n" << indent << "changed from '"
1517 	    << linkage_names1 << "' to '" << linkage_names2 << "'\n";
1518     }
1519 
1520   if (qn1 != qn2
1521       && d.type_diff()
1522       && d.type_diff()->to_be_reported())
1523     {
1524       // So the function has sub-type changes that are to be
1525       // reported.  Let's see if the function name changed too; if it
1526       // did, then we'd report that change right before reporting the
1527       // sub-type changes.
1528       string frep1 = d.first_function_decl()->get_pretty_representation(),
1529 	frep2 = d.second_function_decl()->get_pretty_representation();
1530       out << indent << "'" << frep1 << " {" << linkage_names1<< "}"
1531 	  << "' now becomes '"
1532 	  << frep2 << " {" << linkage_names2 << "}" << "'\n";
1533     }
1534 
1535   maybe_report_diff_for_symbol(ff->get_symbol(),
1536 			       sf->get_symbol(),
1537 			       d.context(), out, indent);
1538 
1539   // Now report about inline-ness changes
1540   if (ff->is_declared_inline() != sf->is_declared_inline())
1541     {
1542       out << indent;
1543       if (ff->is_declared_inline())
1544 	out << sf->get_pretty_representation()
1545 	    << " is not declared inline anymore\n";
1546       else
1547 	out << sf->get_pretty_representation()
1548 	    << " is now declared inline\n";
1549     }
1550 
1551   // Report about vtable offset changes.
1552   if (is_member_function(ff) && is_member_function(sf))
1553     {
1554       bool ff_is_virtual = get_member_function_is_virtual(ff),
1555 	sf_is_virtual = get_member_function_is_virtual(sf);
1556       if (ff_is_virtual != sf_is_virtual)
1557 	{
1558 	  out << indent;
1559 	  if (ff_is_virtual)
1560 	    out << ff->get_pretty_representation()
1561 		<< " is no more declared virtual\n";
1562 	  else
1563 	    out << ff->get_pretty_representation()
1564 		<< " is now declared virtual\n";
1565 	}
1566 
1567       size_t ff_vtable_offset = get_member_function_vtable_offset(ff),
1568 	sf_vtable_offset = get_member_function_vtable_offset(sf);
1569       if (ff_is_virtual && sf_is_virtual
1570 	  && (ff_vtable_offset != sf_vtable_offset))
1571 	{
1572 	  out << indent
1573 	      << "the vtable offset of "  << ff->get_pretty_representation()
1574 	      << " changed from " << ff_vtable_offset
1575 	      << " to " << sf_vtable_offset << "\n";
1576 	}
1577 
1578       // the parent types (classe or union) of the two member
1579       // functions.
1580       class_or_union_sptr f =
1581 	is_class_or_union_type(is_method_type(ff->get_type())->get_class_type());
1582       class_or_union_sptr s =
1583 	is_class_or_union_type(is_method_type(sf->get_type())->get_class_type());
1584 
1585       class_decl_sptr fc = is_class_type(f);
1586       class_decl_sptr sc = is_class_type(s);
1587 
1588       // Detect if the virtual member function changes above
1589       // introduced a vtable change or not.
1590       bool vtable_added = false, vtable_removed = false;
1591       if (!f->get_is_declaration_only() && !s->get_is_declaration_only())
1592 	{
1593 	  if (fc && sc)
1594 	    {
1595 	      vtable_added = !fc->has_vtable() && sc->has_vtable();
1596 	      vtable_removed = fc->has_vtable() && !sc->has_vtable();
1597 	    }
1598 	}
1599       bool vtable_changed = ((ff_is_virtual != sf_is_virtual)
1600 			     || (ff_vtable_offset != sf_vtable_offset));
1601       bool incompatible_change = (ff_vtable_offset != sf_vtable_offset);
1602 
1603       if (vtable_added)
1604 	out << indent
1605 	    << "  note that a vtable was added to "
1606 	    << fc->get_pretty_representation()
1607 	    << "\n";
1608       else if (vtable_removed)
1609 	out << indent
1610 	    << "  note that the vtable was removed from "
1611 	    << fc->get_pretty_representation()
1612 	    << "\n";
1613       else if (vtable_changed)
1614 	{
1615 	  out << indent;
1616 	  if (incompatible_change)
1617 	    out << "  note that this is an ABI incompatible "
1618 	      "change to the vtable of ";
1619 	  else
1620 	    out << "  note that this induces a change to the vtable of ";
1621 	  out << fc->get_pretty_representation()
1622 	      << "\n";
1623 	}
1624 
1625     }
1626 
1627   // Report about function type differences.
1628   if (d.type_diff() && d.type_diff()->to_be_reported())
1629     d.type_diff()->report(out, indent);
1630 }
1631 
1632 /// Report the changes carried by a @ref var_diff node in a serialized
1633 /// form.
1634 ///
1635 /// @param d the @ref var_diff node to consider.
1636 ///
1637 /// @param out the stream to serialize the diff to.
1638 ///
1639 /// @param indent the prefix to use for the indentation of this
1640 /// serialization.
1641 void
report(const var_diff & d,ostream & out,const string & indent) const1642 default_reporter::report(const var_diff& d, ostream& out,
1643 			 const string& indent) const
1644 {
1645   if (!d.to_be_reported())
1646     return;
1647 
1648   decl_base_sptr first = d.first_var(), second = d.second_var();
1649   string n = first->get_pretty_representation();
1650 
1651   report_name_size_and_alignment_changes(first, second,
1652 					 d.context(),
1653 					 out, indent);
1654 
1655   maybe_report_diff_for_symbol(d.first_var()->get_symbol(),
1656 			       d.second_var()->get_symbol(),
1657 			       d.context(), out, indent);
1658 
1659   maybe_report_diff_for_member(first, second, d.context(), out, indent);
1660 
1661   if (diff_sptr dif = d.type_diff())
1662     {
1663       if (dif->to_be_reported())
1664 	{
1665 	  RETURN_IF_BEING_REPORTED_OR_WAS_REPORTED_EARLIER2(dif, "type");
1666 	  out << indent << "type of variable changed:\n";
1667 	  dif->report(out, indent + "  ");
1668 	}
1669     }
1670 }
1671 
1672 /// Report the changes carried by a @ref translation_unit_diff node in
1673 /// a serialized form.
1674 ///
1675 /// @param d the @ref translation_unit_diff node to consider.
1676 ///
1677 /// @param out the output stream to serialize the report to.
1678 ///
1679 /// @param indent the prefix to use as indentation for the report.
1680 void
report(const translation_unit_diff & d,ostream & out,const string & indent) const1681 default_reporter::report(const translation_unit_diff& d,
1682 			 ostream& out,
1683 			 const string& indent) const
1684 {
1685   static_cast<const scope_diff&>(d).report(out, indent);
1686 }
1687 
1688 /// Report the changes carried by a @ref corpus_diff node in a
1689 /// serialized form.
1690 ///
1691 /// @param d the @ref corpus_diff node to consider.
1692 ///
1693 /// @param out the output stream to serialize the report to.
1694 ///
1695 /// @param indent the prefix to use as indentation for the report.
1696 void
report(const corpus_diff & d,ostream & out,const string & indent) const1697 default_reporter::report(const corpus_diff& d, ostream& out,
1698 			 const string& indent) const
1699 {
1700   const corpus_diff::diff_stats &s =
1701     const_cast<corpus_diff&>(d).
1702     apply_filters_and_suppressions_before_reporting();
1703 
1704   const diff_context_sptr& ctxt = d.context();
1705 
1706   d.priv_->emit_diff_stats(s, out, indent);
1707   if (ctxt->show_stats_only())
1708     return;
1709   out << "\n";
1710 
1711   if (ctxt->show_soname_change()
1712       && !d.priv_->sonames_equal_)
1713     out << indent << "SONAME changed from '"
1714 	<< d.first_corpus()->get_soname() << "' to '"
1715 	<< d.second_corpus()->get_soname() << "'\n\n";
1716 
1717   if (ctxt->show_architecture_change()
1718       && !d.priv_->architectures_equal_)
1719     out << indent << "architecture changed from '"
1720 	<< d.first_corpus()->get_architecture_name() << "' to '"
1721 	<< d.second_corpus()->get_architecture_name() << "'\n\n";
1722 
1723   /// Report removed/added/changed functions.
1724   if (ctxt->show_deleted_fns())
1725     {
1726       if (s.net_num_func_removed() == 1)
1727 	out << indent << "1 Removed function:\n\n";
1728       else if (s.net_num_func_removed() > 1)
1729 	out << indent << s.net_num_func_removed() << " Removed functions:\n\n";
1730 
1731       bool emitted = false;
1732       vector<function_decl*>sorted_deleted_fns;
1733       sort_string_function_ptr_map(d.priv_->deleted_fns_, sorted_deleted_fns);
1734       for (vector<function_decl*>::const_iterator i =
1735 	     sorted_deleted_fns.begin();
1736 	   i != sorted_deleted_fns.end();
1737 	   ++i)
1738 	{
1739 	  if (d.priv_->deleted_function_is_suppressed(*i))
1740 	    continue;
1741 
1742 	  out << indent
1743 	      << "  ";
1744 	  out << "[D] ";
1745 	  out << "'" << (*i)->get_pretty_representation() << "'";
1746 	  if (ctxt->show_linkage_names())
1747 	    {
1748 	      out << "    {";
1749 	      show_linkage_name_and_aliases(out, "", *(*i)->get_symbol(),
1750 					    d.first_corpus()->get_fun_symbol_map());
1751 	      out << "}";
1752 	    }
1753 	  out << "\n";
1754 	  if (is_member_function(*i) && get_member_function_is_virtual(*i))
1755 	    {
1756 	      class_decl_sptr c =
1757 		is_class_type(is_method_type((*i)->get_type())->get_class_type());
1758 	      out << indent
1759 		  << "    "
1760 		  << "note that this removes an entry from the vtable of "
1761 		  << c->get_pretty_representation()
1762 		  << "\n";
1763 	    }
1764 	  emitted = true;
1765 	}
1766       if (emitted)
1767 	out << "\n";
1768     }
1769 
1770   if (ctxt->show_added_fns())
1771     {
1772       if (s.net_num_func_added() == 1)
1773 	out << indent << "1 Added function:\n\n";
1774       else if (s.net_num_func_added() > 1)
1775 	out << indent << s.net_num_func_added()
1776 	    << " Added functions:\n\n";
1777       bool emitted = false;
1778       vector<function_decl*> sorted_added_fns;
1779       sort_string_function_ptr_map(d.priv_->added_fns_, sorted_added_fns);
1780       for (vector<function_decl*>::const_iterator i = sorted_added_fns.begin();
1781 	   i != sorted_added_fns.end();
1782 	   ++i)
1783 	{
1784 	  if (d.priv_->added_function_is_suppressed(*i))
1785 	    continue;
1786 
1787 	  out
1788 	    << indent
1789 	    << "  ";
1790 	  out << "[A] ";
1791 	  out << "'"
1792 	      << (*i)->get_pretty_representation()
1793 	      << "'";
1794 	  if (ctxt->show_linkage_names())
1795 	    {
1796 	      out << "    {";
1797 	      show_linkage_name_and_aliases
1798 		(out, "", *(*i)->get_symbol(),
1799 		 d.second_corpus()->get_fun_symbol_map());
1800 	      out << "}";
1801 	    }
1802 	  out << "\n";
1803 	  if (is_member_function(*i) && get_member_function_is_virtual(*i))
1804 	    {
1805 	      class_decl_sptr c =
1806 		is_class_type(is_method_type((*i)->get_type())->get_class_type());
1807 	      out << indent
1808 		  << "    "
1809 		  << "note that this adds a new entry to the vtable of "
1810 		  << c->get_pretty_representation()
1811 		  << "\n";
1812 	    }
1813 	  emitted = true;
1814 	}
1815       if (emitted)
1816 	out << "\n";
1817     }
1818 
1819   if (ctxt->show_changed_fns())
1820     {
1821       size_t num_changed = s.num_func_changed() - s.num_changed_func_filtered_out();
1822       if (num_changed == 1)
1823 	out << indent << "1 function with some indirect sub-type change:\n\n";
1824       else if (num_changed > 1)
1825 	out << indent << num_changed
1826 	    << " functions with some indirect sub-type change:\n\n";
1827 
1828       vector<function_decl_diff_sptr> sorted_changed_fns;
1829       sort_string_function_decl_diff_sptr_map(d.priv_->changed_fns_map_,
1830 					      sorted_changed_fns);
1831       for (vector<function_decl_diff_sptr>::const_iterator i =
1832 	     sorted_changed_fns.begin();
1833 	   i != sorted_changed_fns.end();
1834 	   ++i)
1835 	{
1836 	  diff_sptr diff = *i;
1837 	  if (!diff)
1838 	    continue;
1839 
1840 	  if (diff->to_be_reported())
1841 	    {
1842 	      function_decl_sptr fn = (*i)->first_function_decl();
1843 	      out << indent << "  [C] '"
1844 		  << fn->get_pretty_representation() << "'";
1845 	      report_loc_info((*i)->second_function_decl(), *ctxt, out);
1846 	      out << " has some indirect sub-type changes:\n";
1847 	      if (// The symbol of the function has aliases and the
1848 		  // function is not a cdtor (yeah because c++ cdtors
1849 		  // usually have several aliases).
1850 		  (fn->get_symbol()->has_aliases()
1851 		   && !(is_member_function(fn)
1852 			&& get_member_function_is_ctor(fn))
1853 		   && !(is_member_function(fn)
1854 			&& get_member_function_is_dtor(fn)))
1855 		  || // We are in C and the name of the function is
1856 		     // different from the symbol name -- without
1857 		     // taking the possible symbol version into
1858 		     // account (this usually means the programmers
1859 		     // was playing tricks with symbol names and
1860 		     // versions).
1861 		  (is_c_language(get_translation_unit(fn)->get_language())
1862 		      && fn->get_name() != fn->get_symbol()->get_name()))
1863 		{
1864 		  // As the name of the symbol of the function doesn't
1865 		  // seem to be obvious here, make sure to tell the
1866 		  // user about the name of the (function) symbol she
1867 		  // is looking at here.
1868 		  int number_of_aliases =
1869 		    fn->get_symbol()->get_number_of_aliases();
1870 		  if (number_of_aliases == 0)
1871 		    {
1872 		      out << indent << "    "
1873 			  << "Please note that the exported symbol of "
1874 			"this function is "
1875 			  << fn->get_symbol()->get_id_string()
1876 			  << "\n";
1877 		    }
1878 		  else
1879 		    {
1880 		      out << indent << "    "
1881 			  << "Please note that the symbol of this function is "
1882 			  << fn->get_symbol()->get_id_string()
1883 			  << "\n     and it aliases symbol";
1884 		      if (number_of_aliases > 1)
1885 			out << "s";
1886 		      out << ": "
1887 			  << fn->get_symbol()->get_aliases_id_string(false)
1888 			  << "\n";
1889 		    }
1890 		}
1891 	      diff->report(out, indent + "    ");
1892 	      // Extra spacing.
1893 	      out << "\n";
1894 	    }
1895 	}
1896       // Changed functions have extra spacing already. No new line here.
1897     }
1898 
1899   // Report removed/added/changed variables.
1900   if (ctxt->show_deleted_vars())
1901     {
1902       if (s.net_num_vars_removed() == 1)
1903 	out << indent << "1 Removed variable:\n\n";
1904       else if (s.net_num_vars_removed() > 1)
1905 	out << indent << s.net_num_vars_removed()
1906 	    << " Removed variables:\n\n";
1907       string n;
1908       bool emitted = false;
1909       vector<var_decl*> sorted_deleted_vars;
1910       sort_string_var_ptr_map(d.priv_->deleted_vars_, sorted_deleted_vars);
1911       for (vector<var_decl*>::const_iterator i =
1912 	     sorted_deleted_vars.begin();
1913 	   i != sorted_deleted_vars.end();
1914 	   ++i)
1915 	{
1916 	  if (d.priv_->deleted_variable_is_suppressed(*i))
1917 	    continue;
1918 
1919 	  n = (*i)->get_pretty_representation();
1920 
1921 	  out << indent
1922 	      << "  ";
1923 	  out << "[D] ";
1924 	  out << "'"
1925 	      << n
1926 	      << "'";
1927 	  if (ctxt->show_linkage_names())
1928 	    {
1929 	      out << "    {";
1930 	      show_linkage_name_and_aliases(out, "", *(*i)->get_symbol(),
1931 					    d.first_corpus()->get_var_symbol_map());
1932 	      out << "}";
1933 	    }
1934 	  out << "\n";
1935 	  emitted = true;
1936 	}
1937       if (emitted)
1938 	  out << "\n";
1939     }
1940 
1941   if (ctxt->show_added_vars())
1942     {
1943       if (s.net_num_vars_added() == 1)
1944 	out << indent << "1 Added variable:\n\n";
1945       else if (s.net_num_vars_added() > 1)
1946 	out << indent << s.net_num_vars_added()
1947 	    << " Added variables:\n\n";
1948       string n;
1949       bool emitted = false;
1950       vector<var_decl*> sorted_added_vars;
1951       sort_string_var_ptr_map(d.priv_->added_vars_, sorted_added_vars);
1952       for (vector<var_decl*>::const_iterator i =
1953 	     sorted_added_vars.begin();
1954 	   i != sorted_added_vars.end();
1955 	   ++i)
1956 	{
1957 	  if (d.priv_->added_variable_is_suppressed(*i))
1958 	    continue;
1959 
1960 	  n = (*i)->get_pretty_representation();
1961 
1962 	  out << indent
1963 	      << "  ";
1964 	  out << "[A] ";
1965 	  out << "'" << n << "'";
1966 	  if (ctxt->show_linkage_names())
1967 	    {
1968 	      out << "    {";
1969 	      show_linkage_name_and_aliases(out, "", *(*i)->get_symbol(),
1970 					    d.second_corpus()->get_var_symbol_map());
1971 	      out << "}";
1972 	    }
1973 	  out << "\n";
1974 	  emitted = true;
1975 	}
1976       if (emitted)
1977 	out << "\n";
1978     }
1979 
1980   if (ctxt->show_changed_vars())
1981     {
1982       size_t num_changed =
1983 	s.num_vars_changed() - s.num_changed_vars_filtered_out();
1984       if (num_changed == 1)
1985 	out << indent << "1 Changed variable:\n\n";
1986       else if (num_changed > 1)
1987 	out << indent << num_changed
1988 	    << " Changed variables:\n\n";
1989       string n1, n2;
1990 
1991       for (var_diff_sptrs_type::const_iterator i =
1992 	     d.priv_->sorted_changed_vars_.begin();
1993 	   i != d.priv_->sorted_changed_vars_.end();
1994 	   ++i)
1995 	{
1996 	  diff_sptr diff = *i;
1997 
1998 	  if (!diff)
1999 	    continue;
2000 
2001 	  if (!diff->to_be_reported())
2002 	    continue;
2003 
2004 	  n1 = diff->first_subject()->get_pretty_representation();
2005 	  n2 = diff->second_subject()->get_pretty_representation();
2006 
2007 	  out << indent << "  [C] '" << n1 << "' was changed";
2008 	  if (n1 != n2)
2009 	    out << " to '" << n2 << "'";
2010 	  report_loc_info(diff->second_subject(), *ctxt, out);
2011 	  out << ":\n";
2012 	  diff->report(out, indent + "    ");
2013 	  // Extra spacing.
2014 	  out << "\n";
2015 	}
2016       // Changed variables have extra spacing already. No new line here.
2017     }
2018 
2019   // Report removed function symbols not referenced by any debug info.
2020   if (ctxt->show_symbols_unreferenced_by_debug_info()
2021       && d.priv_->deleted_unrefed_fn_syms_.size())
2022     {
2023       if (s.net_num_removed_func_syms() == 1)
2024 	out << indent
2025 	    << "1 Removed function symbol not referenced by debug info:\n\n";
2026       else if (s.net_num_removed_func_syms() > 0)
2027 	out << indent
2028 	    << s.net_num_removed_func_syms()
2029 	    << " Removed function symbols not referenced by debug info:\n\n";
2030 
2031       bool emitted = false;
2032       vector<elf_symbol_sptr> sorted_deleted_unrefed_fn_syms;
2033       sort_string_elf_symbol_map(d.priv_->deleted_unrefed_fn_syms_,
2034 				 sorted_deleted_unrefed_fn_syms);
2035       for (vector<elf_symbol_sptr>::const_iterator i =
2036 	     sorted_deleted_unrefed_fn_syms.begin();
2037 	   i != sorted_deleted_unrefed_fn_syms.end();
2038 	   ++i)
2039 	{
2040 	  if (d.priv_->deleted_unrefed_fn_sym_is_suppressed((*i).get()))
2041 	    continue;
2042 
2043 	  out << indent << "  ";
2044 	  out << "[D] ";
2045 
2046 	  show_linkage_name_and_aliases(out, "", **i,
2047 					d.first_corpus()->get_fun_symbol_map());
2048 	  out << "\n";
2049 	  emitted = true;
2050 	}
2051       if (emitted)
2052 	out << "\n";
2053     }
2054 
2055   // Report added function symbols not referenced by any debug info.
2056   if (ctxt->show_symbols_unreferenced_by_debug_info()
2057       && ctxt->show_added_symbols_unreferenced_by_debug_info()
2058       && d.priv_->added_unrefed_fn_syms_.size())
2059     {
2060       if (s.net_num_added_func_syms() == 1)
2061 	out << indent
2062 	    << "1 Added function symbol not referenced by debug info:\n\n";
2063       else if (s.net_num_added_func_syms() > 0)
2064 	out << indent
2065 	    << s.net_num_added_func_syms()
2066 	    << " Added function symbols not referenced by debug info:\n\n";
2067 
2068       bool emitted = false;
2069       vector<elf_symbol_sptr> sorted_added_unrefed_fn_syms;
2070       sort_string_elf_symbol_map(d.priv_->added_unrefed_fn_syms_,
2071 				 sorted_added_unrefed_fn_syms);
2072       for (vector<elf_symbol_sptr>::const_iterator i =
2073 	     sorted_added_unrefed_fn_syms.begin();
2074 	   i != sorted_added_unrefed_fn_syms.end();
2075 	   ++i)
2076 	{
2077 	  if (d.priv_->added_unrefed_fn_sym_is_suppressed((*i).get()))
2078 	    continue;
2079 
2080 	  out << indent << "  ";
2081 	  out << "[A] ";
2082 	  show_linkage_name_and_aliases(out, "",
2083 					**i,
2084 					d.second_corpus()->get_fun_symbol_map());
2085 	  out << "\n";
2086 	  emitted = true;
2087 	}
2088       if (emitted)
2089 	out << "\n";
2090     }
2091 
2092   // Report removed variable symbols not referenced by any debug info.
2093   if (ctxt->show_symbols_unreferenced_by_debug_info()
2094       && d.priv_->deleted_unrefed_var_syms_.size())
2095     {
2096       if (s.net_num_removed_var_syms() == 1)
2097 	out << indent
2098 	    << "1 Removed variable symbol not referenced by debug info:\n\n";
2099       else if (s.net_num_removed_var_syms() > 0)
2100 	out << indent
2101 	    << s.net_num_removed_var_syms()
2102 	    << " Removed variable symbols not referenced by debug info:\n\n";
2103 
2104       bool emitted = false;
2105       vector<elf_symbol_sptr> sorted_deleted_unrefed_var_syms;
2106       sort_string_elf_symbol_map(d.priv_->deleted_unrefed_var_syms_,
2107 				 sorted_deleted_unrefed_var_syms);
2108       for (vector<elf_symbol_sptr>::const_iterator i =
2109 	     sorted_deleted_unrefed_var_syms.begin();
2110 	   i != sorted_deleted_unrefed_var_syms.end();
2111 	   ++i)
2112 	{
2113 	  if (d.priv_->deleted_unrefed_var_sym_is_suppressed((*i).get()))
2114 	    continue;
2115 
2116 	  out << indent << "  ";
2117 	  out << "[D] ";
2118 
2119 	  show_linkage_name_and_aliases
2120 	    (out, "", **i,
2121 	     d.first_corpus()->get_fun_symbol_map());
2122 
2123 	  out << "\n";
2124 	  emitted = true;
2125 	}
2126       if (emitted)
2127 	out << "\n";
2128     }
2129 
2130   // Report added variable symbols not referenced by any debug info.
2131   if (ctxt->show_symbols_unreferenced_by_debug_info()
2132       && ctxt->show_added_symbols_unreferenced_by_debug_info()
2133       && d.priv_->added_unrefed_var_syms_.size())
2134     {
2135       if (s.net_num_added_var_syms() == 1)
2136 	out << indent
2137 	    << "1 Added variable symbol not referenced by debug info:\n\n";
2138       else if (s.net_num_added_var_syms() > 0)
2139 	out << indent
2140 	    << s.net_num_added_var_syms()
2141 	    << " Added variable symbols not referenced by debug info:\n\n";
2142 
2143       bool emitted = false;
2144       vector<elf_symbol_sptr> sorted_added_unrefed_var_syms;
2145       sort_string_elf_symbol_map(d.priv_->added_unrefed_var_syms_,
2146 				 sorted_added_unrefed_var_syms);
2147       for (vector<elf_symbol_sptr>::const_iterator i =
2148 	     sorted_added_unrefed_var_syms.begin();
2149 	   i != sorted_added_unrefed_var_syms.end();
2150 	   ++i)
2151 	{
2152 	  if (d.priv_->added_unrefed_var_sym_is_suppressed((*i).get()))
2153 	    continue;
2154 
2155 	  out << indent << "  ";
2156 	  out << "[A] ";
2157 	  show_linkage_name_and_aliases(out, "", **i,
2158 					d.second_corpus()->get_fun_symbol_map());
2159 	  out << "\n";
2160 	  emitted = true;
2161 	}
2162       if (emitted)
2163 	out << "\n";
2164     }
2165 
2166   // Report added/removed/changed types not reacheable from public
2167   // interfaces.
2168   maybe_report_unreachable_type_changes(d, s, indent, out);
2169 
2170   d.priv_->maybe_dump_diff_tree();
2171 }
2172 
2173 } // end namespace comparison
2174 }// end namespace libabigail
2175