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 // Report base classes re-organisation
1353 maybe_report_base_class_reordering(d, out, indent);
1354 }
1355
1356 d.class_or_union_diff::report(out, indent);
1357
1358 d.currently_reporting(false);
1359
1360 d.reported_once(true);
1361 }
1362
1363 /// Produce a basic report about the changes carried by a @ref
1364 /// union_diff node.
1365 ///
1366 /// @param d the @ref union_diff node to consider.
1367 ///
1368 /// @param out the output stream to report the changes to.
1369 ///
1370 /// @param indent the string to use as an indentation prefix in the
1371 /// report.
1372 void
report(const union_diff & d,ostream & out,const string & indent) const1373 default_reporter::report(const union_diff& d, ostream& out,
1374 const string& indent) const
1375 {
1376 RETURN_IF_BEING_REPORTED_OR_WAS_REPORTED_EARLIER(d.first_subject(),
1377 d.second_subject());
1378
1379 d.currently_reporting(true);
1380
1381 // Now report the changes about the differents parts of the type.
1382 union_decl_sptr first = d.first_union_decl(), second = d.second_union_decl();
1383
1384 report_name_size_and_alignment_changes(first, second, d.context(),
1385 out, indent);
1386
1387 maybe_report_diff_for_member(first, second,d. context(), out, indent);
1388
1389 d.class_or_union_diff::report(out, indent);
1390
1391 if (d.context()->get_allowed_category() & HARMLESS_UNION_CHANGE_CATEGORY
1392 && filtering::union_diff_has_harmless_changes(&d))
1393 {
1394 // The user wants to see harmless changes and the union diff we
1395 // are looking at does carry some harmless changes. Let's show
1396 // the "before" and "after" carried by the diff node.
1397 out << indent << "type changed from:\n"
1398 << get_class_or_union_flat_representation(first, indent + " ",
1399 /*one_line=*/true,
1400 /*internal=*/false,
1401 /*qualified_names=*/false)
1402 << "\n"
1403 << indent << "to:\n"
1404 << get_class_or_union_flat_representation(second, indent + " ",
1405 /*one_line=*/true,
1406 /*internal=*/false,
1407 /*qualified_names=*/false)
1408 << "\n";
1409 }
1410
1411 d.currently_reporting(false);
1412
1413 d.reported_once(true);
1414 }
1415
1416 /// Emit a report about the changes carried by a @ref distinct_diff
1417 /// node.
1418 ///
1419 /// @param d the @ref distinct_diff node to consider.
1420 ///
1421 /// @param out the output stream to send the diff report to.
1422 ///
1423 /// @param indent the indentation string to use in the report.
1424 void
report(const distinct_diff & d,ostream & out,const string & indent) const1425 default_reporter::report(const distinct_diff& d, ostream& out,
1426 const string& indent) const
1427 {
1428 if (!d.to_be_reported())
1429 return;
1430
1431 type_or_decl_base_sptr f = d.first(), s = d.second();
1432
1433 string f_repr = f ? f->get_pretty_representation() : "'void'";
1434 string s_repr = s ? s->get_pretty_representation() : "'void'";
1435
1436 diff_sptr diff = d.compatible_child_diff();
1437
1438 string compatible = diff ? " to compatible type '": " to '";
1439
1440 out << indent << "entity changed from '" << f_repr << "'"
1441 << compatible << s_repr << "'";
1442 report_loc_info(s, *d.context(), out);
1443 out << "\n";
1444
1445 type_base_sptr fs = strip_typedef(is_type(f)),
1446 ss = strip_typedef(is_type(s));
1447
1448 if (diff)
1449 diff->report(out, indent + " ");
1450 else
1451 report_size_and_alignment_changes(f, s, d.context(), out, indent);
1452 }
1453
1454 /// Serialize a report of the changes encapsulated in the current
1455 /// instance of @ref function_decl_diff over to an output stream.
1456 ///
1457 /// @param d the @ref function_decl_diff node to consider.
1458 ///
1459 /// @param out the output stream to serialize the report to.
1460 ///
1461 /// @param indent the string to use an an indentation prefix.
1462 void
report(const function_decl_diff & d,ostream & out,const string & indent) const1463 default_reporter::report(const function_decl_diff& d, ostream& out,
1464 const string& indent) const
1465 {
1466 if (!d.to_be_reported())
1467 return;
1468
1469 maybe_report_diff_for_member(d.first_function_decl(),
1470 d.second_function_decl(),
1471 d.context(), out, indent);
1472
1473 function_decl_sptr ff = d.first_function_decl();
1474 function_decl_sptr sf = d.second_function_decl();
1475
1476 diff_context_sptr ctxt = d.context();
1477 corpus_sptr fc = ctxt->get_first_corpus();
1478 corpus_sptr sc = ctxt->get_second_corpus();
1479
1480
1481 string qn1 = ff->get_qualified_name(), qn2 = sf->get_qualified_name(),
1482 linkage_names1, linkage_names2;
1483 elf_symbol_sptr s1 = ff->get_symbol(), s2 = sf->get_symbol();
1484
1485 if (s1)
1486 linkage_names1 = s1->get_id_string();
1487 if (s2)
1488 linkage_names2 = s2->get_id_string();
1489
1490 // If the symbols for ff and sf have aliases, get all the names of
1491 // the aliases;
1492 if (fc && s1)
1493 linkage_names1 =
1494 s1->get_aliases_id_string(fc->get_fun_symbol_map());
1495 if (sc && s2)
1496 linkage_names2 =
1497 s2->get_aliases_id_string(sc->get_fun_symbol_map());
1498
1499 /// If the set of linkage names of the function have changed, report
1500 /// it.
1501 if (linkage_names1 != linkage_names2)
1502 {
1503 if (linkage_names1.empty())
1504 {
1505 out << indent << ff->get_pretty_representation()
1506 << " didn't have any linkage name, and it now has: '"
1507 << linkage_names2 << "'\n";
1508 }
1509 else if (linkage_names2.empty())
1510 {
1511 out << indent << ff->get_pretty_representation()
1512 << " did have linkage names '" << linkage_names1
1513 << "'\n"
1514 << indent << "but it doesn't have any linkage name anymore\n";
1515 }
1516 else
1517 out << indent << "linkage names of "
1518 << ff->get_pretty_representation()
1519 << "\n" << indent << "changed from '"
1520 << linkage_names1 << "' to '" << linkage_names2 << "'\n";
1521 }
1522
1523 if (qn1 != qn2
1524 && d.type_diff()
1525 && d.type_diff()->to_be_reported())
1526 {
1527 // So the function has sub-type changes that are to be
1528 // reported. Let's see if the function name changed too; if it
1529 // did, then we'd report that change right before reporting the
1530 // sub-type changes.
1531 string frep1 = d.first_function_decl()->get_pretty_representation(),
1532 frep2 = d.second_function_decl()->get_pretty_representation();
1533 out << indent << "'" << frep1 << " {" << linkage_names1<< "}"
1534 << "' now becomes '"
1535 << frep2 << " {" << linkage_names2 << "}" << "'\n";
1536 }
1537
1538 maybe_report_diff_for_symbol(ff->get_symbol(),
1539 sf->get_symbol(),
1540 d.context(), out, indent);
1541
1542 // Now report about inline-ness changes
1543 if (ff->is_declared_inline() != sf->is_declared_inline())
1544 {
1545 out << indent;
1546 if (ff->is_declared_inline())
1547 out << sf->get_pretty_representation()
1548 << " is not declared inline anymore\n";
1549 else
1550 out << sf->get_pretty_representation()
1551 << " is now declared inline\n";
1552 }
1553
1554 // Report about vtable offset changes.
1555 if (is_member_function(ff) && is_member_function(sf))
1556 {
1557 bool ff_is_virtual = get_member_function_is_virtual(ff),
1558 sf_is_virtual = get_member_function_is_virtual(sf);
1559 if (ff_is_virtual != sf_is_virtual)
1560 {
1561 out << indent;
1562 if (ff_is_virtual)
1563 out << ff->get_pretty_representation()
1564 << " is no more declared virtual\n";
1565 else
1566 out << ff->get_pretty_representation()
1567 << " is now declared virtual\n";
1568 }
1569
1570 size_t ff_vtable_offset = get_member_function_vtable_offset(ff),
1571 sf_vtable_offset = get_member_function_vtable_offset(sf);
1572 if (ff_is_virtual && sf_is_virtual
1573 && (ff_vtable_offset != sf_vtable_offset))
1574 {
1575 out << indent
1576 << "the vtable offset of " << ff->get_pretty_representation()
1577 << " changed from " << ff_vtable_offset
1578 << " to " << sf_vtable_offset << "\n";
1579 }
1580
1581 // the parent types (classe or union) of the two member
1582 // functions.
1583 class_or_union_sptr f =
1584 is_class_or_union_type(is_method_type(ff->get_type())->get_class_type());
1585 class_or_union_sptr s =
1586 is_class_or_union_type(is_method_type(sf->get_type())->get_class_type());
1587
1588 class_decl_sptr fc = is_class_type(f);
1589 class_decl_sptr sc = is_class_type(s);
1590
1591 // Detect if the virtual member function changes above
1592 // introduced a vtable change or not.
1593 bool vtable_added = false, vtable_removed = false;
1594 if (!f->get_is_declaration_only() && !s->get_is_declaration_only())
1595 {
1596 if (fc && sc)
1597 {
1598 vtable_added = !fc->has_vtable() && sc->has_vtable();
1599 vtable_removed = fc->has_vtable() && !sc->has_vtable();
1600 }
1601 }
1602 bool vtable_changed = ((ff_is_virtual != sf_is_virtual)
1603 || (ff_vtable_offset != sf_vtable_offset));
1604 bool incompatible_change = (ff_vtable_offset != sf_vtable_offset);
1605
1606 if (vtable_added)
1607 out << indent
1608 << " note that a vtable was added to "
1609 << fc->get_pretty_representation()
1610 << "\n";
1611 else if (vtable_removed)
1612 out << indent
1613 << " note that the vtable was removed from "
1614 << fc->get_pretty_representation()
1615 << "\n";
1616 else if (vtable_changed)
1617 {
1618 out << indent;
1619 if (incompatible_change)
1620 out << " note that this is an ABI incompatible "
1621 "change to the vtable of ";
1622 else
1623 out << " note that this induces a change to the vtable of ";
1624 out << fc->get_pretty_representation()
1625 << "\n";
1626 }
1627
1628 }
1629
1630 // Report about function type differences.
1631 if (d.type_diff() && d.type_diff()->to_be_reported())
1632 d.type_diff()->report(out, indent);
1633 }
1634
1635 /// Report the changes carried by a @ref var_diff node in a serialized
1636 /// form.
1637 ///
1638 /// @param d the @ref var_diff node to consider.
1639 ///
1640 /// @param out the stream to serialize the diff to.
1641 ///
1642 /// @param indent the prefix to use for the indentation of this
1643 /// serialization.
1644 void
report(const var_diff & d,ostream & out,const string & indent) const1645 default_reporter::report(const var_diff& d, ostream& out,
1646 const string& indent) const
1647 {
1648 if (!d.to_be_reported())
1649 return;
1650
1651 decl_base_sptr first = d.first_var(), second = d.second_var();
1652 string n = first->get_pretty_representation();
1653
1654 report_name_size_and_alignment_changes(first, second,
1655 d.context(),
1656 out, indent);
1657
1658 maybe_report_diff_for_symbol(d.first_var()->get_symbol(),
1659 d.second_var()->get_symbol(),
1660 d.context(), out, indent);
1661
1662 maybe_report_diff_for_member(first, second, d.context(), out, indent);
1663
1664 if (diff_sptr dif = d.type_diff())
1665 {
1666 if (dif->to_be_reported())
1667 {
1668 RETURN_IF_BEING_REPORTED_OR_WAS_REPORTED_EARLIER2(dif, "type");
1669 out << indent << "type of variable changed:\n";
1670 dif->report(out, indent + " ");
1671 }
1672 }
1673 }
1674
1675 /// Report the changes carried by a @ref translation_unit_diff node in
1676 /// a serialized form.
1677 ///
1678 /// @param d the @ref translation_unit_diff node to consider.
1679 ///
1680 /// @param out the output stream to serialize the report to.
1681 ///
1682 /// @param indent the prefix to use as indentation for the report.
1683 void
report(const translation_unit_diff & d,ostream & out,const string & indent) const1684 default_reporter::report(const translation_unit_diff& d,
1685 ostream& out,
1686 const string& indent) const
1687 {
1688 static_cast<const scope_diff&>(d).report(out, indent);
1689 }
1690
1691 /// Report the changes carried by a @ref corpus_diff node in a
1692 /// serialized form.
1693 ///
1694 /// @param d the @ref corpus_diff node to consider.
1695 ///
1696 /// @param out the output stream to serialize the report to.
1697 ///
1698 /// @param indent the prefix to use as indentation for the report.
1699 void
report(const corpus_diff & d,ostream & out,const string & indent) const1700 default_reporter::report(const corpus_diff& d, ostream& out,
1701 const string& indent) const
1702 {
1703 const corpus_diff::diff_stats &s =
1704 const_cast<corpus_diff&>(d).
1705 apply_filters_and_suppressions_before_reporting();
1706
1707 const diff_context_sptr& ctxt = d.context();
1708
1709 d.priv_->emit_diff_stats(s, out, indent);
1710 if (ctxt->show_stats_only())
1711 return;
1712 out << "\n";
1713
1714 if (ctxt->show_soname_change()
1715 && !d.priv_->sonames_equal_)
1716 out << indent << "SONAME changed from '"
1717 << d.first_corpus()->get_soname() << "' to '"
1718 << d.second_corpus()->get_soname() << "'\n\n";
1719
1720 if (ctxt->show_architecture_change()
1721 && !d.priv_->architectures_equal_)
1722 out << indent << "architecture changed from '"
1723 << d.first_corpus()->get_architecture_name() << "' to '"
1724 << d.second_corpus()->get_architecture_name() << "'\n\n";
1725
1726 /// Report removed/added/changed functions.
1727 if (ctxt->show_deleted_fns())
1728 {
1729 if (s.net_num_func_removed() == 1)
1730 out << indent << "1 Removed function:\n\n";
1731 else if (s.net_num_func_removed() > 1)
1732 out << indent << s.net_num_func_removed() << " Removed functions:\n\n";
1733
1734 bool emitted = false;
1735 vector<function_decl*>sorted_deleted_fns;
1736 sort_string_function_ptr_map(d.priv_->deleted_fns_, sorted_deleted_fns);
1737 for (vector<function_decl*>::const_iterator i =
1738 sorted_deleted_fns.begin();
1739 i != sorted_deleted_fns.end();
1740 ++i)
1741 {
1742 if (d.priv_->deleted_function_is_suppressed(*i))
1743 continue;
1744
1745 out << indent
1746 << " ";
1747 out << "[D] ";
1748 out << "'" << (*i)->get_pretty_representation() << "'";
1749 if (ctxt->show_linkage_names())
1750 {
1751 out << " {";
1752 show_linkage_name_and_aliases(out, "", *(*i)->get_symbol(),
1753 d.first_corpus()->get_fun_symbol_map());
1754 out << "}";
1755 }
1756 out << "\n";
1757 if (is_member_function(*i) && get_member_function_is_virtual(*i))
1758 {
1759 class_decl_sptr c =
1760 is_class_type(is_method_type((*i)->get_type())->get_class_type());
1761 out << indent
1762 << " "
1763 << "note that this removes an entry from the vtable of "
1764 << c->get_pretty_representation()
1765 << "\n";
1766 }
1767 emitted = true;
1768 }
1769 if (emitted)
1770 out << "\n";
1771 }
1772
1773 if (ctxt->show_added_fns())
1774 {
1775 if (s.net_num_func_added() == 1)
1776 out << indent << "1 Added function:\n\n";
1777 else if (s.net_num_func_added() > 1)
1778 out << indent << s.net_num_func_added()
1779 << " Added functions:\n\n";
1780 bool emitted = false;
1781 vector<function_decl*> sorted_added_fns;
1782 sort_string_function_ptr_map(d.priv_->added_fns_, sorted_added_fns);
1783 for (vector<function_decl*>::const_iterator i = sorted_added_fns.begin();
1784 i != sorted_added_fns.end();
1785 ++i)
1786 {
1787 if (d.priv_->added_function_is_suppressed(*i))
1788 continue;
1789
1790 out
1791 << indent
1792 << " ";
1793 out << "[A] ";
1794 out << "'"
1795 << (*i)->get_pretty_representation()
1796 << "'";
1797 if (ctxt->show_linkage_names())
1798 {
1799 out << " {";
1800 show_linkage_name_and_aliases
1801 (out, "", *(*i)->get_symbol(),
1802 d.second_corpus()->get_fun_symbol_map());
1803 out << "}";
1804 }
1805 out << "\n";
1806 if (is_member_function(*i) && get_member_function_is_virtual(*i))
1807 {
1808 class_decl_sptr c =
1809 is_class_type(is_method_type((*i)->get_type())->get_class_type());
1810 out << indent
1811 << " "
1812 << "note that this adds a new entry to the vtable of "
1813 << c->get_pretty_representation()
1814 << "\n";
1815 }
1816 emitted = true;
1817 }
1818 if (emitted)
1819 out << "\n";
1820 }
1821
1822 if (ctxt->show_changed_fns())
1823 {
1824 size_t num_changed = s.num_func_changed() - s.num_changed_func_filtered_out();
1825 if (num_changed == 1)
1826 out << indent << "1 function with some indirect sub-type change:\n\n";
1827 else if (num_changed > 1)
1828 out << indent << num_changed
1829 << " functions with some indirect sub-type change:\n\n";
1830
1831 vector<function_decl_diff_sptr> sorted_changed_fns;
1832 sort_string_function_decl_diff_sptr_map(d.priv_->changed_fns_map_,
1833 sorted_changed_fns);
1834 for (vector<function_decl_diff_sptr>::const_iterator i =
1835 sorted_changed_fns.begin();
1836 i != sorted_changed_fns.end();
1837 ++i)
1838 {
1839 diff_sptr diff = *i;
1840 if (!diff)
1841 continue;
1842
1843 if (diff->to_be_reported())
1844 {
1845 function_decl_sptr fn = (*i)->first_function_decl();
1846 out << indent << " [C] '"
1847 << fn->get_pretty_representation() << "'";
1848 report_loc_info((*i)->second_function_decl(), *ctxt, out);
1849 out << " has some indirect sub-type changes:\n";
1850 if (// The symbol of the function has aliases and the
1851 // function is not a cdtor (yeah because c++ cdtors
1852 // usually have several aliases).
1853 (fn->get_symbol()->has_aliases()
1854 && !(is_member_function(fn)
1855 && get_member_function_is_ctor(fn))
1856 && !(is_member_function(fn)
1857 && get_member_function_is_dtor(fn)))
1858 || // We are in C and the name of the function is
1859 // different from the symbol name -- without
1860 // taking the possible symbol version into
1861 // account (this usually means the programmers
1862 // was playing tricks with symbol names and
1863 // versions).
1864 (is_c_language(get_translation_unit(fn)->get_language())
1865 && fn->get_name() != fn->get_symbol()->get_name()))
1866 {
1867 // As the name of the symbol of the function doesn't
1868 // seem to be obvious here, make sure to tell the
1869 // user about the name of the (function) symbol she
1870 // is looking at here.
1871 int number_of_aliases =
1872 fn->get_symbol()->get_number_of_aliases();
1873 if (number_of_aliases == 0)
1874 {
1875 out << indent << " "
1876 << "Please note that the exported symbol of "
1877 "this function is "
1878 << fn->get_symbol()->get_id_string()
1879 << "\n";
1880 }
1881 else
1882 {
1883 out << indent << " "
1884 << "Please note that the symbol of this function is "
1885 << fn->get_symbol()->get_id_string()
1886 << "\n and it aliases symbol";
1887 if (number_of_aliases > 1)
1888 out << "s";
1889 out << ": "
1890 << fn->get_symbol()->get_aliases_id_string(false)
1891 << "\n";
1892 }
1893 }
1894 diff->report(out, indent + " ");
1895 // Extra spacing.
1896 out << "\n";
1897 }
1898 }
1899 // Changed functions have extra spacing already. No new line here.
1900 }
1901
1902 // Report removed/added/changed variables.
1903 if (ctxt->show_deleted_vars())
1904 {
1905 if (s.net_num_vars_removed() == 1)
1906 out << indent << "1 Removed variable:\n\n";
1907 else if (s.net_num_vars_removed() > 1)
1908 out << indent << s.net_num_vars_removed()
1909 << " Removed variables:\n\n";
1910 string n;
1911 bool emitted = false;
1912 vector<var_decl*> sorted_deleted_vars;
1913 sort_string_var_ptr_map(d.priv_->deleted_vars_, sorted_deleted_vars);
1914 for (vector<var_decl*>::const_iterator i =
1915 sorted_deleted_vars.begin();
1916 i != sorted_deleted_vars.end();
1917 ++i)
1918 {
1919 if (d.priv_->deleted_variable_is_suppressed(*i))
1920 continue;
1921
1922 n = (*i)->get_pretty_representation();
1923
1924 out << indent
1925 << " ";
1926 out << "[D] ";
1927 out << "'"
1928 << n
1929 << "'";
1930 if (ctxt->show_linkage_names())
1931 {
1932 out << " {";
1933 show_linkage_name_and_aliases(out, "", *(*i)->get_symbol(),
1934 d.first_corpus()->get_var_symbol_map());
1935 out << "}";
1936 }
1937 out << "\n";
1938 emitted = true;
1939 }
1940 if (emitted)
1941 out << "\n";
1942 }
1943
1944 if (ctxt->show_added_vars())
1945 {
1946 if (s.net_num_vars_added() == 1)
1947 out << indent << "1 Added variable:\n\n";
1948 else if (s.net_num_vars_added() > 1)
1949 out << indent << s.net_num_vars_added()
1950 << " Added variables:\n\n";
1951 string n;
1952 bool emitted = false;
1953 vector<var_decl*> sorted_added_vars;
1954 sort_string_var_ptr_map(d.priv_->added_vars_, sorted_added_vars);
1955 for (vector<var_decl*>::const_iterator i =
1956 sorted_added_vars.begin();
1957 i != sorted_added_vars.end();
1958 ++i)
1959 {
1960 if (d.priv_->added_variable_is_suppressed(*i))
1961 continue;
1962
1963 n = (*i)->get_pretty_representation();
1964
1965 out << indent
1966 << " ";
1967 out << "[A] ";
1968 out << "'" << n << "'";
1969 if (ctxt->show_linkage_names())
1970 {
1971 out << " {";
1972 show_linkage_name_and_aliases(out, "", *(*i)->get_symbol(),
1973 d.second_corpus()->get_var_symbol_map());
1974 out << "}";
1975 }
1976 out << "\n";
1977 emitted = true;
1978 }
1979 if (emitted)
1980 out << "\n";
1981 }
1982
1983 if (ctxt->show_changed_vars())
1984 {
1985 size_t num_changed =
1986 s.num_vars_changed() - s.num_changed_vars_filtered_out();
1987 if (num_changed == 1)
1988 out << indent << "1 Changed variable:\n\n";
1989 else if (num_changed > 1)
1990 out << indent << num_changed
1991 << " Changed variables:\n\n";
1992 string n1, n2;
1993
1994 for (var_diff_sptrs_type::const_iterator i =
1995 d.priv_->sorted_changed_vars_.begin();
1996 i != d.priv_->sorted_changed_vars_.end();
1997 ++i)
1998 {
1999 diff_sptr diff = *i;
2000
2001 if (!diff)
2002 continue;
2003
2004 if (!diff->to_be_reported())
2005 continue;
2006
2007 n1 = diff->first_subject()->get_pretty_representation();
2008 n2 = diff->second_subject()->get_pretty_representation();
2009
2010 out << indent << " [C] '" << n1 << "' was changed";
2011 if (n1 != n2)
2012 out << " to '" << n2 << "'";
2013 report_loc_info(diff->second_subject(), *ctxt, out);
2014 out << ":\n";
2015 diff->report(out, indent + " ");
2016 // Extra spacing.
2017 out << "\n";
2018 }
2019 // Changed variables have extra spacing already. No new line here.
2020 }
2021
2022 // Report removed function symbols not referenced by any debug info.
2023 if (ctxt->show_symbols_unreferenced_by_debug_info()
2024 && d.priv_->deleted_unrefed_fn_syms_.size())
2025 {
2026 if (s.net_num_removed_func_syms() == 1)
2027 out << indent
2028 << "1 Removed function symbol not referenced by debug info:\n\n";
2029 else if (s.net_num_removed_func_syms() > 0)
2030 out << indent
2031 << s.net_num_removed_func_syms()
2032 << " Removed function symbols not referenced by debug info:\n\n";
2033
2034 bool emitted = false;
2035 vector<elf_symbol_sptr> sorted_deleted_unrefed_fn_syms;
2036 sort_string_elf_symbol_map(d.priv_->deleted_unrefed_fn_syms_,
2037 sorted_deleted_unrefed_fn_syms);
2038 for (vector<elf_symbol_sptr>::const_iterator i =
2039 sorted_deleted_unrefed_fn_syms.begin();
2040 i != sorted_deleted_unrefed_fn_syms.end();
2041 ++i)
2042 {
2043 if (d.priv_->deleted_unrefed_fn_sym_is_suppressed((*i).get()))
2044 continue;
2045
2046 out << indent << " ";
2047 out << "[D] ";
2048
2049 show_linkage_name_and_aliases(out, "", **i,
2050 d.first_corpus()->get_fun_symbol_map());
2051 out << "\n";
2052 emitted = true;
2053 }
2054 if (emitted)
2055 out << "\n";
2056 }
2057
2058 // Report added function symbols not referenced by any debug info.
2059 if (ctxt->show_symbols_unreferenced_by_debug_info()
2060 && ctxt->show_added_symbols_unreferenced_by_debug_info()
2061 && d.priv_->added_unrefed_fn_syms_.size())
2062 {
2063 if (s.net_num_added_func_syms() == 1)
2064 out << indent
2065 << "1 Added function symbol not referenced by debug info:\n\n";
2066 else if (s.net_num_added_func_syms() > 0)
2067 out << indent
2068 << s.net_num_added_func_syms()
2069 << " Added function symbols not referenced by debug info:\n\n";
2070
2071 bool emitted = false;
2072 vector<elf_symbol_sptr> sorted_added_unrefed_fn_syms;
2073 sort_string_elf_symbol_map(d.priv_->added_unrefed_fn_syms_,
2074 sorted_added_unrefed_fn_syms);
2075 for (vector<elf_symbol_sptr>::const_iterator i =
2076 sorted_added_unrefed_fn_syms.begin();
2077 i != sorted_added_unrefed_fn_syms.end();
2078 ++i)
2079 {
2080 if (d.priv_->added_unrefed_fn_sym_is_suppressed((*i).get()))
2081 continue;
2082
2083 out << indent << " ";
2084 out << "[A] ";
2085 show_linkage_name_and_aliases(out, "",
2086 **i,
2087 d.second_corpus()->get_fun_symbol_map());
2088 out << "\n";
2089 emitted = true;
2090 }
2091 if (emitted)
2092 out << "\n";
2093 }
2094
2095 // Report removed variable symbols not referenced by any debug info.
2096 if (ctxt->show_symbols_unreferenced_by_debug_info()
2097 && d.priv_->deleted_unrefed_var_syms_.size())
2098 {
2099 if (s.net_num_removed_var_syms() == 1)
2100 out << indent
2101 << "1 Removed variable symbol not referenced by debug info:\n\n";
2102 else if (s.net_num_removed_var_syms() > 0)
2103 out << indent
2104 << s.net_num_removed_var_syms()
2105 << " Removed variable symbols not referenced by debug info:\n\n";
2106
2107 bool emitted = false;
2108 vector<elf_symbol_sptr> sorted_deleted_unrefed_var_syms;
2109 sort_string_elf_symbol_map(d.priv_->deleted_unrefed_var_syms_,
2110 sorted_deleted_unrefed_var_syms);
2111 for (vector<elf_symbol_sptr>::const_iterator i =
2112 sorted_deleted_unrefed_var_syms.begin();
2113 i != sorted_deleted_unrefed_var_syms.end();
2114 ++i)
2115 {
2116 if (d.priv_->deleted_unrefed_var_sym_is_suppressed((*i).get()))
2117 continue;
2118
2119 out << indent << " ";
2120 out << "[D] ";
2121
2122 show_linkage_name_and_aliases
2123 (out, "", **i,
2124 d.first_corpus()->get_fun_symbol_map());
2125
2126 out << "\n";
2127 emitted = true;
2128 }
2129 if (emitted)
2130 out << "\n";
2131 }
2132
2133 // Report added variable symbols not referenced by any debug info.
2134 if (ctxt->show_symbols_unreferenced_by_debug_info()
2135 && ctxt->show_added_symbols_unreferenced_by_debug_info()
2136 && d.priv_->added_unrefed_var_syms_.size())
2137 {
2138 if (s.net_num_added_var_syms() == 1)
2139 out << indent
2140 << "1 Added variable symbol not referenced by debug info:\n\n";
2141 else if (s.net_num_added_var_syms() > 0)
2142 out << indent
2143 << s.net_num_added_var_syms()
2144 << " Added variable symbols not referenced by debug info:\n\n";
2145
2146 bool emitted = false;
2147 vector<elf_symbol_sptr> sorted_added_unrefed_var_syms;
2148 sort_string_elf_symbol_map(d.priv_->added_unrefed_var_syms_,
2149 sorted_added_unrefed_var_syms);
2150 for (vector<elf_symbol_sptr>::const_iterator i =
2151 sorted_added_unrefed_var_syms.begin();
2152 i != sorted_added_unrefed_var_syms.end();
2153 ++i)
2154 {
2155 if (d.priv_->added_unrefed_var_sym_is_suppressed((*i).get()))
2156 continue;
2157
2158 out << indent << " ";
2159 out << "[A] ";
2160 show_linkage_name_and_aliases(out, "", **i,
2161 d.second_corpus()->get_fun_symbol_map());
2162 out << "\n";
2163 emitted = true;
2164 }
2165 if (emitted)
2166 out << "\n";
2167 }
2168
2169 // Report added/removed/changed types not reacheable from public
2170 // interfaces.
2171 maybe_report_unreachable_type_changes(d, s, indent, out);
2172
2173 d.priv_->maybe_dump_diff_tree();
2174 }
2175
2176 } // end namespace comparison
2177 }// end namespace libabigail
2178