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