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