1 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
2 // -*- Mode: C++ -*-
3 //
4 // Copyright (C) 2017-2020 Red Hat, Inc.
5 //
6 // Author: Dodji Seketeli
7 
8 
9 /// @file
10 ///
11 /// This is the implementation of the
12 /// abigail::comparison::default_reporter type.
13 
14 #include "abg-comparison-priv.h"
15 #include "abg-reporter.h"
16 #include "abg-reporter-priv.h"
17 
18 namespace abigail
19 {
20 namespace comparison
21 {
22 
23 /// Test if a 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       diff_category saved_category = type_diff->get_category();
522       // Parameter type changes are never redundants.
523       type_diff->set_category(saved_category & ~REDUNDANT_CATEGORY);
524       out << indent;
525       if (f->get_is_artificial())
526 	out << "implicit ";
527       out << "parameter " << f->get_index();
528       report_loc_info(f, *d.context(), out);
529       out << " of type '"
530 	  << f->get_type_pretty_representation();
531 
532       if (has_sub_type_change)
533 	out << "' has sub-type changes:\n";
534       else
535 	out << "' changed:\n";
536 
537       type_diff->report(out, indent + "  ");
538       type_diff->set_category(saved_category);
539     }
540 }
541 
542 /// For a @ref function_type_diff node, report the local changes
543 /// carried by the diff node.
544 ///
545 /// @param d the @ref function_type_diff node to consider.
546 ///
547 /// @param out the output stream to report to.
548 ///
549 /// @param indent the white space indentation string to use.
550 void
report_local_function_type_changes(const function_type_diff & d,ostream & out,const string & indent) const551 default_reporter::report_local_function_type_changes(const function_type_diff& d,
552 						     ostream& out,
553 						     const string& indent) const
554 
555 {
556   if (!d.to_be_reported())
557     return;
558 
559   function_type_sptr fft = d.first_function_type();
560   function_type_sptr sft = d.second_function_type();
561 
562   diff_context_sptr ctxt = d.context();
563 
564   // Report about the size of the function address
565   if (fft->get_size_in_bits() != sft->get_size_in_bits())
566     {
567       out << indent << "address size of function changed from "
568 	  << fft->get_size_in_bits()
569 	  << " bits to "
570 	  << sft->get_size_in_bits()
571 	  << " bits\n";
572     }
573 
574   // Report about the alignment of the function address
575   if (fft->get_alignment_in_bits()
576       != sft->get_alignment_in_bits())
577     {
578       out << indent << "address alignment of function changed from "
579 	  << fft->get_alignment_in_bits()
580 	  << " bits to "
581 	  << sft->get_alignment_in_bits()
582 	  << " bits\n";
583     }
584 
585   // Hmmh, the above was quick.  Now report about function parameters;
586   // this shouldn't be as straightforward.
587 
588   // Report about the parameters that got removed.
589   for (vector<function_decl::parameter_sptr>::const_iterator i =
590 	 d.priv_->sorted_deleted_parms_.begin();
591        i != d.priv_->sorted_deleted_parms_.end();
592        ++i)
593     {
594       out << indent << "parameter " << (*i)->get_index()
595 	  << " of type '" << (*i)->get_type_pretty_representation()
596 	  << "' was removed\n";
597     }
598 
599   // Report about the parameters that got added
600   for (vector<function_decl::parameter_sptr>::const_iterator i =
601 	 d.priv_->sorted_added_parms_.begin();
602        i != d.priv_->sorted_added_parms_.end();
603        ++i)
604     {
605       out << indent << "parameter " << (*i)->get_index()
606 	  << " of type '" << (*i)->get_type_pretty_representation()
607 	  << "' was added\n";
608     }
609 }
610 
611 /// Build and emit a textual report about a @ref function_type_diff.
612 ///
613 /// @param d the @ref function_type_diff to consider.
614 ///
615 /// @param out the output stream.
616 ///
617 /// @param indent the indentation string to use.
618 void
report(const function_type_diff & d,ostream & out,const string & indent) const619 default_reporter::report(const function_type_diff& d, ostream& out,
620 			 const string& indent) const
621 {
622   if (!d.to_be_reported())
623     return;
624 
625   function_type_sptr fft = d.first_function_type();
626   function_type_sptr sft = d.second_function_type();
627 
628   diff_context_sptr ctxt = d.context();
629   corpus_sptr fc = ctxt->get_first_corpus();
630   corpus_sptr sc = ctxt->get_second_corpus();
631 
632   // Report about return type differences.
633   if (d.priv_->return_type_diff_
634       && d.priv_->return_type_diff_->to_be_reported())
635     {
636       out << indent << "return type changed:\n";
637       d.priv_->return_type_diff_->report(out, indent + "  ");
638     }
639 
640   // Report about the parameter types that have changed sub-types.
641   for (vector<fn_parm_diff_sptr>::const_iterator i =
642 	 d.priv_->sorted_subtype_changed_parms_.begin();
643        i != d.priv_->sorted_subtype_changed_parms_.end();
644        ++i)
645     {
646       diff_sptr dif = *i;
647       if (dif && dif->to_be_reported())
648 	dif->report(out, indent);
649     }
650 
651   report_local_function_type_changes(d, out, indent);
652 
653 }
654 
655 /// Report a @ref array_diff in a serialized form.
656 ///
657 /// @param d the @ref array_diff to consider.
658 ///
659 /// @param out the output stream to serialize the dif to.
660 ///
661 /// @param indent the string to use for indenting the report.
662 void
report(const array_diff & d,ostream & out,const string & indent) const663 default_reporter::report(const array_diff& d, ostream& out,
664 			 const string& indent) const
665 {
666   if (!d.to_be_reported())
667     return;
668 
669   string name = d.first_array()->get_pretty_representation();
670   RETURN_IF_BEING_REPORTED_OR_WAS_REPORTED_EARLIER3(d.first_array(),
671 						    d.second_array(),
672 						    "array type");
673 
674   diff_sptr dif = d.element_type_diff();
675   if (dif->to_be_reported())
676     {
677       string fn = ir::get_pretty_representation(is_type(dif->first_subject()));
678       // report array element type changes
679       out << indent << "array element type '"
680 	  << fn << "' changed:\n";
681       dif->report(out, indent + "  ");
682     }
683 
684   report_name_size_and_alignment_changes(d.first_array(),
685 					 d.second_array(),
686 					 d.context(),
687 					 out, indent);
688 }
689 
690 /// Generates a report for an intance of @ref base_diff.
691 ///
692 /// @param d the @ref base_diff to consider.
693 ///
694 /// @param out the output stream to send the report to.
695 ///
696 /// @param indent the string to use for indentation.
697 void
report(const base_diff & d,ostream & out,const string & indent) const698 default_reporter::report(const base_diff& d, ostream& out,
699 			 const string& indent) const
700 {
701   if (!d.to_be_reported())
702     return;
703 
704   class_decl::base_spec_sptr f = d.first_base(), s = d.second_base();
705   string repr = f->get_base_class()->get_pretty_representation();
706   bool emitted = false;
707 
708   if (f->get_is_static() != s->get_is_static())
709     {
710       if (f->get_is_static())
711 	out << indent << "is no more static";
712       else
713 	out << indent << "now becomes static";
714       emitted = true;
715     }
716 
717   if ((d.context()->get_allowed_category() & ACCESS_CHANGE_CATEGORY)
718       && (f->get_access_specifier() != s->get_access_specifier()))
719     {
720       if (emitted)
721 	out << ", ";
722 
723       out << "has access changed from '"
724 	  << f->get_access_specifier()
725 	  << "' to '"
726 	  << s->get_access_specifier()
727 	  << "'";
728 
729       emitted = true;
730     }
731 
732   if (class_diff_sptr dif = d.get_underlying_class_diff())
733     {
734       if (dif->to_be_reported())
735 	{
736 	  if (emitted)
737 	    out << "\n";
738 	  dif->report(out, indent);
739 	}
740     }
741 }
742 
743 /// Report the changes carried by a @ref scope_diff.
744 ///
745 /// @param d the @ref scope_diff to consider.
746 ///
747 /// @param out the out stream to report the changes to.
748 ///
749 /// @param indent the string to use for indentation.
750 void
report(const scope_diff & d,ostream & out,const string & indent) const751 default_reporter::report(const scope_diff& d, ostream& out,
752 			 const string& indent) const
753 {
754   if (!d.to_be_reported())
755     return;
756 
757   // Report changed types.
758   unsigned num_changed_types = d.changed_types().size();
759   if (num_changed_types == 0)
760     ;
761   else if (num_changed_types == 1)
762     out << indent << "1 changed type:\n";
763   else
764     out << indent << num_changed_types << " changed types:\n";
765 
766   for (diff_sptrs_type::const_iterator dif = d.changed_types().begin();
767        dif != d.changed_types().end();
768        ++dif)
769     {
770       if (!*dif)
771 	continue;
772 
773       out << indent << "  '"
774 	  << (*dif)->first_subject()->get_pretty_representation()
775 	  << "' changed:\n";
776       (*dif)->report(out, indent + "    ");
777     }
778 
779   // Report changed decls
780   unsigned num_changed_decls = d.changed_decls().size();
781   if (num_changed_decls == 0)
782     ;
783   else if (num_changed_decls == 1)
784     out << indent << "1 changed declaration:\n";
785   else
786     out << indent << num_changed_decls << " changed declarations:\n";
787 
788   for (diff_sptrs_type::const_iterator dif= d.changed_decls().begin();
789        dif != d.changed_decls().end ();
790        ++dif)
791     {
792       if (!*dif)
793 	continue;
794 
795       out << indent << "  '"
796 	  << (*dif)->first_subject()->get_pretty_representation()
797 	  << "' was changed to '"
798 	  << (*dif)->second_subject()->get_pretty_representation() << "'";
799       report_loc_info((*dif)->second_subject(), *d.context(), out);
800       out << ":\n";
801 
802       (*dif)->report(out, indent + "    ");
803     }
804 
805   // Report removed types/decls
806   for (string_decl_base_sptr_map::const_iterator i =
807 	 d.priv_->deleted_types_.begin();
808        i != d.priv_->deleted_types_.end();
809        ++i)
810     out << indent
811 	<< "  '"
812 	<< i->second->get_pretty_representation()
813 	<< "' was removed\n";
814 
815   if (d.priv_->deleted_types_.size())
816     out << "\n";
817 
818   for (string_decl_base_sptr_map::const_iterator i =
819 	 d.priv_->deleted_decls_.begin();
820        i != d.priv_->deleted_decls_.end();
821        ++i)
822     out << indent
823 	<< "  '"
824 	<< i->second->get_pretty_representation()
825 	<< "' was removed\n";
826 
827   if (d.priv_->deleted_decls_.size())
828     out << "\n";
829 
830   // Report added types/decls
831   bool emitted = false;
832   for (string_decl_base_sptr_map::const_iterator i =
833 	 d.priv_->inserted_types_.begin();
834        i != d.priv_->inserted_types_.end();
835        ++i)
836     {
837       // Do not report about type_decl as these are usually built-in
838       // types.
839       if (dynamic_pointer_cast<type_decl>(i->second))
840 	continue;
841       out << indent
842 	  << "  '"
843 	  << i->second->get_pretty_representation()
844 	  << "' was added\n";
845       emitted = true;
846     }
847 
848   if (emitted)
849     out << "\n";
850 
851   emitted = false;
852   for (string_decl_base_sptr_map::const_iterator i =
853 	 d.priv_->inserted_decls_.begin();
854        i != d.priv_->inserted_decls_.end();
855        ++i)
856     {
857       // Do not report about type_decl as these are usually built-in
858       // types.
859       if (dynamic_pointer_cast<type_decl>(i->second))
860 	continue;
861       out << indent
862 	  << "  '"
863 	  << i->second->get_pretty_representation()
864 	  << "' was added\n";
865       emitted = true;
866     }
867 
868   if (emitted)
869     out << "\n";
870 }
871 
872 /// Report the changes carried by a @ref class_or_union_diff node in a
873 /// textual format.
874 ///
875 /// @param d the @ref class_or_union_diff node to consider.
876 ///
877 /// @param out the output stream to write the textual report to.
878 ///
879 /// @param indent the number of white space to use as indentation.
880 void
report(const class_or_union_diff & d,ostream & out,const string & indent) const881 default_reporter::report(const class_or_union_diff& d,
882 			 ostream& out,
883 			 const string& indent) const
884 {
885   if (!d.to_be_reported())
886     return;
887 
888   class_or_union_sptr first = d.first_class_or_union(),
889     second = d.second_class_or_union();
890 
891   const diff_context_sptr& ctxt = d.context();
892 
893   // Report class decl-only <-> definition change.
894   if (ctxt->get_allowed_category() & TYPE_DECL_ONLY_DEF_CHANGE_CATEGORY)
895     if (filtering::has_class_decl_only_def_change(first, second))
896       {
897 	string was =
898 	  first->get_is_declaration_only()
899 	  ? " was a declaration-only type"
900 	  : " was a defined type";
901 
902 	string is_now =
903 	  second->get_is_declaration_only()
904 	  ? " and is now a declaration-only type"
905 	  : " and is now a defined type";
906 
907 	out << indent << "type " << first->get_pretty_representation()
908 	    << was << is_now << "\n";
909 	return;
910       }
911 
912   // member functions
913   if (d.member_fns_changes())
914     {
915       // report deletions
916       int numdels = d.get_priv()->deleted_member_functions_.size();
917       size_t num_filtered =
918 	d.get_priv()->count_filtered_deleted_mem_fns(ctxt);
919       if (numdels)
920 	report_mem_header(out, numdels, num_filtered, del_kind,
921 			  "member function", indent);
922       for (class_or_union::member_functions::const_iterator i =
923 	     d.get_priv()->sorted_deleted_member_functions_.begin();
924 	   i != d.get_priv()->sorted_deleted_member_functions_.end();
925 	   ++i)
926 	{
927 	  if (!(ctxt->get_allowed_category()
928 		& NON_VIRT_MEM_FUN_CHANGE_CATEGORY)
929 	      && !get_member_function_is_virtual(*i))
930 	    continue;
931 
932 	  method_decl_sptr mem_fun = *i;
933 	  out << indent << "  ";
934 	  represent(*ctxt, mem_fun, out);
935 	}
936 
937       // report insertions;
938       int numins = d.get_priv()->inserted_member_functions_.size();
939       num_filtered = d.get_priv()->count_filtered_inserted_mem_fns(ctxt);
940       if (numins)
941 	report_mem_header(out, numins, num_filtered, ins_kind,
942 			  "member function", indent);
943       for (class_or_union::member_functions::const_iterator i =
944 	     d.get_priv()->sorted_inserted_member_functions_.begin();
945 	   i != d.get_priv()->sorted_inserted_member_functions_.end();
946 	   ++i)
947 	{
948 	  if (!(ctxt->get_allowed_category()
949 		& NON_VIRT_MEM_FUN_CHANGE_CATEGORY)
950 	      && !get_member_function_is_virtual(*i))
951 	    continue;
952 
953 	  method_decl_sptr mem_fun = *i;
954 	  out << indent << "  ";
955 	  represent(*ctxt, mem_fun, out);
956 	}
957 
958       // report member function with sub-types changes
959       int numchanges = d.get_priv()->sorted_changed_member_functions_.size();
960       num_filtered = d.get_priv()->count_filtered_changed_mem_fns(ctxt);
961       if (numchanges)
962 	report_mem_header(out, numchanges, num_filtered, change_kind,
963 			  "member function", indent);
964       for (function_decl_diff_sptrs_type::const_iterator i =
965 	     d.get_priv()->sorted_changed_member_functions_.begin();
966 	   i != d.get_priv()->sorted_changed_member_functions_.end();
967 	   ++i)
968 	{
969 	  if (!(ctxt->get_allowed_category()
970 		& NON_VIRT_MEM_FUN_CHANGE_CATEGORY)
971 	      && !(get_member_function_is_virtual
972 		   ((*i)->first_function_decl()))
973 	      && !(get_member_function_is_virtual
974 		   ((*i)->second_function_decl())))
975 	    continue;
976 
977 	  diff_sptr diff = *i;
978 	  if (!diff || !diff->to_be_reported())
979 	    continue;
980 
981 	  string repr =
982 	    (*i)->first_function_decl()->get_pretty_representation();
983 	  out << indent << "  '" << repr << "' has some sub-type changes:\n";
984 	  diff->report(out, indent + "    ");
985 	}
986     }
987 
988   // data members
989   if (d.data_members_changes())
990     {
991       // report deletions
992       int numdels = d.class_or_union_diff::get_priv()->
993 	get_deleted_non_static_data_members_number();
994       if (numdels)
995 	{
996 	  report_mem_header(out, numdels, 0, del_kind,
997 			    "data member", indent);
998 	  vector<decl_base_sptr> sorted_dms;
999 	  sort_data_members
1000 	    (d.class_or_union_diff::get_priv()->deleted_data_members_,
1001 	     sorted_dms);
1002 	  for (vector<decl_base_sptr>::const_iterator i = sorted_dms.begin();
1003 	       i != sorted_dms.end();
1004 	       ++i)
1005 	    {
1006 	      var_decl_sptr data_mem =
1007 		dynamic_pointer_cast<var_decl>(*i);
1008 	      ABG_ASSERT(data_mem);
1009 	      if (get_member_is_static(data_mem))
1010 		continue;
1011 	      represent_data_member(data_mem, ctxt, out, indent + "  ");
1012 	    }
1013 	}
1014 
1015       //report insertions
1016       int numins =
1017 	d.class_or_union_diff::get_priv()->inserted_data_members_.size();
1018       if (numins)
1019 	{
1020 	  report_mem_header(out, numins, 0, ins_kind,
1021 			    "data member", indent);
1022 	  vector<decl_base_sptr> sorted_dms;
1023 	  sort_data_members
1024 	    (d.class_or_union_diff::get_priv()->inserted_data_members_,
1025 	     sorted_dms);
1026 	  for (vector<decl_base_sptr>::const_iterator i = sorted_dms.begin();
1027 	       i != sorted_dms.end();
1028 	       ++i)
1029 	    {
1030 	      var_decl_sptr data_mem =
1031 		dynamic_pointer_cast<var_decl>(*i);
1032 	      ABG_ASSERT(data_mem);
1033 	      represent_data_member(data_mem, ctxt, out, indent + "  ");
1034 	    }
1035 	}
1036 
1037       // report change
1038       size_t num_changes =
1039 	(d.sorted_subtype_changed_data_members().size()
1040 	 + d.sorted_changed_data_members().size());
1041 
1042       size_t num_changes_filtered =
1043 	(d.count_filtered_subtype_changed_data_members()
1044 	 + d.count_filtered_changed_data_members());
1045 
1046       if (num_changes)
1047 	{
1048 	  report_mem_header(out, num_changes, num_changes_filtered,
1049 			    change_kind, "data member", indent);
1050 
1051 	  for (var_diff_sptrs_type::const_iterator it =
1052 		 d.sorted_changed_data_members().begin();
1053 	       it != d.sorted_changed_data_members().end();
1054 	       ++it)
1055 	    if ((*it)->to_be_reported())
1056 	      represent(*it, ctxt, out, indent + "  ");
1057 
1058 	  for (var_diff_sptrs_type::const_iterator it =
1059 		 d.sorted_subtype_changed_data_members().begin();
1060 	       it != d.sorted_subtype_changed_data_members().end();
1061 	       ++it)
1062 	    if ((*it)->to_be_reported())
1063 	      represent(*it, ctxt, out, indent + "  ");
1064 	}
1065 
1066       // Report about data members replaced by an anonymous union data
1067       // member.
1068       maybe_report_data_members_replaced_by_anon_dm(d, out, indent);
1069     }
1070 
1071   // member types
1072   if (const edit_script& e = d.member_types_changes())
1073     {
1074       int numchanges =
1075 	d.class_or_union_diff::get_priv()->sorted_changed_member_types_.size();
1076       int numdels =
1077 	d.class_or_union_diff::get_priv()->deleted_member_types_.size();
1078 
1079       // report deletions
1080       if (numdels)
1081 	{
1082 	  report_mem_header(out, numdels, 0, del_kind,
1083 			    "member type", indent);
1084 
1085 	  for (string_decl_base_sptr_map::const_iterator i =
1086 		 d.class_or_union_diff::get_priv()->deleted_member_types_.begin();
1087 	       i != d.class_or_union_diff::get_priv()->deleted_member_types_.end();
1088 	       ++i)
1089 	    {
1090 	      decl_base_sptr mem_type = i->second;
1091 	      out << indent << "  '"
1092 		  << mem_type->get_pretty_representation()
1093 		  << "'\n";
1094 	    }
1095 	  out << "\n";
1096 	}
1097       // report changes
1098       if (numchanges)
1099 	{
1100 	  report_mem_header(out, numchanges, 0, change_kind,
1101 			    "member type", indent);
1102 
1103 	  for (diff_sptrs_type::const_iterator it =
1104 		 d.class_or_union_diff::get_priv()->sorted_changed_member_types_.begin();
1105 	       it != d.class_or_union_diff::get_priv()->sorted_changed_member_types_.end();
1106 	       ++it)
1107 	    {
1108 	      if (!(*it)->to_be_reported())
1109 		continue;
1110 
1111 	      type_or_decl_base_sptr o = (*it)->first_subject();
1112 	      type_or_decl_base_sptr n = (*it)->second_subject();
1113 	      out << indent << "  '"
1114 		  << o->get_pretty_representation()
1115 		  << "' changed ";
1116 	      report_loc_info(n, *ctxt, out);
1117 	      out << ":\n";
1118 	      (*it)->report(out, indent + "    ");
1119 	    }
1120 	  out << "\n";
1121 	}
1122 
1123       // report insertions
1124       int numins = e.num_insertions();
1125       ABG_ASSERT(numchanges <= numins);
1126       numins -= numchanges;
1127 
1128       if (numins)
1129 	{
1130 	  report_mem_header(out, numins, 0, ins_kind,
1131 			    "member type", indent);
1132 
1133 	  for (vector<insertion>::const_iterator i = e.insertions().begin();
1134 	       i != e.insertions().end();
1135 	       ++i)
1136 	    {
1137 	      type_base_sptr mem_type;
1138 	      for (vector<unsigned>::const_iterator j =
1139 		     i->inserted_indexes().begin();
1140 		   j != i->inserted_indexes().end();
1141 		   ++j)
1142 		{
1143 		  mem_type = second->get_member_types()[*j];
1144 		  if (!d.class_or_union_diff::get_priv()->
1145 		      member_type_has_changed(get_type_declaration(mem_type)))
1146 		    {
1147 		      out << indent << "  '"
1148 			  << get_type_declaration(mem_type)->
1149 			get_pretty_representation()
1150 			  << "'\n";
1151 		    }
1152 		}
1153 	    }
1154 	  out << "\n";
1155 	}
1156     }
1157 
1158   // member function templates
1159   if (const edit_script& e = d.member_fn_tmpls_changes())
1160     {
1161       // report deletions
1162       int numdels = e.num_deletions();
1163       if (numdels)
1164 	report_mem_header(out, numdels, 0, del_kind,
1165 			  "member function template", indent);
1166       for (vector<deletion>::const_iterator i = e.deletions().begin();
1167 	   i != e.deletions().end();
1168 	   ++i)
1169 	{
1170 	  member_function_template_sptr mem_fn_tmpl =
1171 	    first->get_member_function_templates()[i->index()];
1172 	  out << indent << "  '"
1173 	      << mem_fn_tmpl->as_function_tdecl()->get_pretty_representation()
1174 	      << "'\n";
1175 	}
1176 
1177       // report insertions
1178       int numins = e.num_insertions();
1179       if (numins)
1180 	report_mem_header(out, numins, 0, ins_kind,
1181 			  "member function template", indent);
1182       for (vector<insertion>::const_iterator i = e.insertions().begin();
1183 	   i != e.insertions().end();
1184 	   ++i)
1185 	{
1186 	  member_function_template_sptr mem_fn_tmpl;
1187 	  for (vector<unsigned>::const_iterator j =
1188 		 i->inserted_indexes().begin();
1189 	       j != i->inserted_indexes().end();
1190 	       ++j)
1191 	    {
1192 	      mem_fn_tmpl = second->get_member_function_templates()[*j];
1193 	      out << indent << "  '"
1194 		  << mem_fn_tmpl->as_function_tdecl()->
1195 		get_pretty_representation()
1196 		  << "'\n";
1197 	    }
1198 	}
1199     }
1200 
1201   // member class templates.
1202   if (const edit_script& e = d.member_class_tmpls_changes())
1203     {
1204       // report deletions
1205       int numdels = e.num_deletions();
1206       if (numdels)
1207 	report_mem_header(out, numdels, 0, del_kind,
1208 			  "member class template", indent);
1209       for (vector<deletion>::const_iterator i = e.deletions().begin();
1210 	   i != e.deletions().end();
1211 	   ++i)
1212 	{
1213 	  member_class_template_sptr mem_cls_tmpl =
1214 	    first->get_member_class_templates()[i->index()];
1215 	  out << indent << "  '"
1216 	      << mem_cls_tmpl->as_class_tdecl()->get_pretty_representation()
1217 	      << "'\n";
1218 	}
1219 
1220       // report insertions
1221       int numins = e.num_insertions();
1222       if (numins)
1223 	report_mem_header(out, numins, 0, ins_kind,
1224 			  "member class template", indent);
1225       for (vector<insertion>::const_iterator i = e.insertions().begin();
1226 	   i != e.insertions().end();
1227 	   ++i)
1228 	{
1229 	  member_class_template_sptr mem_cls_tmpl;
1230 	  for (vector<unsigned>::const_iterator j =
1231 		 i->inserted_indexes().begin();
1232 	       j != i->inserted_indexes().end();
1233 	       ++j)
1234 	    {
1235 	      mem_cls_tmpl = second->get_member_class_templates()[*j];
1236 	      out << indent << "  '"
1237 		  << mem_cls_tmpl->as_class_tdecl()
1238 		->get_pretty_representation()
1239 		  << "'\n";
1240 	    }
1241 	}
1242     }
1243 }
1244 
1245 /// Produce a basic report about the changes carried by a @ref
1246 /// class_diff node.
1247 ///
1248 /// @param d the @ref class_diff node to consider.
1249 ///
1250 /// @param out the output stream to report the changes to.
1251 ///
1252 /// @param indent the string to use as an indentation prefix in the
1253 /// report.
1254 void
report(const class_diff & d,ostream & out,const string & indent) const1255 default_reporter::report(const class_diff& d, ostream& out,
1256 			 const string& indent) const
1257 {
1258   if (!d.to_be_reported())
1259     return;
1260 
1261   string name = d.first_subject()->get_pretty_representation();
1262 
1263   RETURN_IF_BEING_REPORTED_OR_WAS_REPORTED_EARLIER(d.first_subject(),
1264 						   d.second_subject());
1265 
1266   d.currently_reporting(true);
1267 
1268   // Now report the changes about the differents parts of the type.
1269   class_decl_sptr first = d.first_class_decl(),
1270     second = d.second_class_decl();
1271 
1272   report_name_size_and_alignment_changes(first, second, d.context(),
1273 					 out, indent);
1274 
1275   const diff_context_sptr& ctxt = d.context();
1276   maybe_report_diff_for_member(first, second, ctxt, out, indent);
1277 
1278   // bases classes
1279   if (d.base_changes())
1280     {
1281       // Report deletions.
1282       int numdels = d.get_priv()->deleted_bases_.size();
1283       size_t numchanges = d.get_priv()->sorted_changed_bases_.size();
1284 
1285       if (numdels)
1286 	{
1287 	  report_mem_header(out, numdels, 0, del_kind,
1288 			    "base class", indent);
1289 
1290 	  for (class_decl::base_specs::const_iterator i
1291 		 = d.get_priv()->sorted_deleted_bases_.begin();
1292 	       i != d.get_priv()->sorted_deleted_bases_.end();
1293 	       ++i)
1294 	    {
1295 	      if (i != d.get_priv()->sorted_deleted_bases_.begin())
1296 		out << "\n";
1297 
1298 	      class_decl::base_spec_sptr base = *i;
1299 
1300 	      if (d.get_priv()->base_has_changed(base))
1301 		continue;
1302 	      out << indent << "  "
1303 		  << base->get_base_class()->get_pretty_representation();
1304 	      report_loc_info(base->get_base_class(), *d.context(), out);
1305 	    }
1306 	  out << "\n";
1307 	}
1308 
1309       // Report changes.
1310       size_t num_filtered = d.get_priv()->count_filtered_bases();
1311       if (numchanges)
1312 	{
1313 	  report_mem_header(out, numchanges, num_filtered, change_kind,
1314 			    "base class", indent);
1315 	  for (base_diff_sptrs_type::const_iterator it =
1316 		 d.get_priv()->sorted_changed_bases_.begin();
1317 	       it != d.get_priv()->sorted_changed_bases_.end();
1318 	       ++it)
1319 	    {
1320 	      base_diff_sptr diff = *it;
1321 	      if (!diff || !diff->to_be_reported())
1322 		continue;
1323 
1324 	      class_decl::base_spec_sptr o = diff->first_base();
1325 	      out << indent << "  '"
1326 		  << o->get_base_class()->get_pretty_representation() << "'";
1327 	      report_loc_info(o->get_base_class(), *d.context(), out);
1328 	      out << " changed:\n";
1329 	      diff->report(out, indent + "    ");
1330 	    }
1331 	}
1332 
1333       //Report insertions.
1334       int numins = d.get_priv()->inserted_bases_.size();
1335       if (numins)
1336 	{
1337 	  report_mem_header(out, numins, 0, ins_kind,
1338 			    "base class", indent);
1339 
1340 	  for (class_decl::base_specs::const_iterator i =
1341 		 d.get_priv()->sorted_inserted_bases_.begin();
1342 	       i != d.get_priv()->sorted_inserted_bases_.end();
1343 	       ++i)
1344 	    {
1345 	      class_decl_sptr b = (*i)->get_base_class();
1346 	      out << indent << "  " << b->get_pretty_representation();
1347 	      report_loc_info(b, *ctxt, out);
1348 	      out << "\n";
1349 	    }
1350 	}
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