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