Bug 24690 - Support comparing non-reachable types of a binary
authorDodji Seketeli <dodji@redhat.com>
Wed, 13 Nov 2019 10:04:18 +0000 (11:04 +0100)
committerDodji Seketeli <dodji@redhat.com>
Mon, 6 Jan 2020 13:26:00 +0000 (14:26 +0100)
This patch adds the ability to compare all types of a binary,
including those types that are not reachable from global functions and
variables.

This implies that for types that are not reachable from public
interfaces, we want compare them against each others directly, without
first comparing global functions/variables and walking the graph of
reachable types from there.

The patch adds the --non-reachable-types option to abidiff and
abipkgdiff, instructing them to also compare types that are
non-reachable from global variables and functions.

Using that option, for instance, here is what the summary of
abipkgdiff now looks like, in the test case attached added by this
patch:

================ changes of 'libflatpak.so.0.10204.0'===============
  Functions changes summary: 0 Removed, 0 Changed (16 filtered out), 16 Added functions
  Variables changes summary: 0 Removed, 0 Changed, 0 Added variable
  Unreachable types summary: 3 removed (2 filtered out), 1 changed (15 filtered out), 3 added (1 filtered out) types

You can see that there is a new summary line which starts with the
string: "Unreachable types summary:"

Then in the body of the report, those unreachable types are reported
separately.

In practise, we want to limit the unreachable types to compare
somehow, otherwise we'll end up comparing all the types of the types
of the binary and that can be huge.  So we want to limit the
unreachable type analysis to types that are defined in public headers.

So, for abipkgdiff, one can limit the analysis of non-reachable types
to those defined in public headers by supplying the --devel{1,2}
options that specifies the development packages that contain said
public headers.  For abidiff however, you'll want to use the
--headers-dir{1,2} options for that.

The patch comes with appropriate regression tests.

* include/abg-comparison.h (string_type_base_sptr_map): Define new
typedef.
(diff_context::show_unreachable_types): Declare new member
functions.
(corpus_diff::{deleted_unreachable_types,
deleted_unreachable_types_sorted, added_unreachable_types,
added_unreachable_types_sorted, changed_unreachable_types,
changed_unreachable_types_sorted}): Likewise.
(maybe_report_unreachable_type_changes): Declare this function a
friend of class corpus_diff.
(corpus_diff::diff_stats::{num_added_unreachable_types,
num_added_unreachable_types_filtered_out,
net_num_added_unreachable_types, num_removed_unreachable_types,
num_removed_unreachable_types_filtered_out,
net_num_removed_unreachable_types, num_changed_unreachable_types,
num_changed_unreachable_types_filtered_out,
net_num_changed_unreachable_types}): Likewise.
* src/abg-comparison-priv.h
(diff_context::priv::show_unreachable_types_): Define new data
member.
(diff_context::priv::priv): Initialize the new data member.
(diff_comp::operator()): Use pretty representation of diff
subjects to sort them, rather than just their name.  Also, add
comment to the other member functions of diff_comp.
(corpus_diff::{unreachable_types_edit_script_,
deleted_unreachable_types_, deleted_unreachable_types_sorted_,
suppressed_deleted_unreachable_types_, added_unreachable_types_,
added_unreachable_types_sorted_,
suppressed_added_unreachable_types_, changed_unreachable_types_,
changed_unreachable_types_sorted_}): Define new data members.
(corpus_diff::priv::apply_supprs_to_added_removed_fns_vars_unreachable_types):
Changed the name of
corpus_diff::priv::apply_suppressions_to_added_removed_fns_vars into
this.
(corpus_diff::priv::{added_unreachable_type_is_suppressed,
deleted_unreachable_type_is_suppressed,
changed_unreachable_types_sorted, count_unreachable_types}):
Declare new member functions.
(corpus_diff::diff_stats::priv::{num_added_unreachable_types,
num_added_unreachable_types_filtered_out,
num_removed_unreachable_types,
num_removed_unreachable_types_filtered_out,
num_changed_unreachable_types,
num_changed_unreachable_types_filtered_out}): Define new data
members.
(sort_string_type_base_sptr_map): Declare new function.
* src/abg-comparison.cc (sort_string_type_base_sptr_map)
(diff_context::show_unreachable_types): Define new functions.
(corpus_diff::diff_stats::{num_added_unreachable_types,
num_added_unreachable_types_filtered_out,
net_num_added_unreachable_types,
net_num_removed_unreachable_types,
num_removed_unreachable_types_filtered_out,
num_removed_unreachable_types}): Define new member functions.
(diff_maps::insert_diff_node): Do not update the map "diff ->
impacted interfaces" if the current impacted interface is nil.
This happens if we are looking at a diff node for a change on a
type that is not reachable from any interfaces.
(corpus_diff::priv::ensure_lookup_tables_populated): Handle the
edit script for unreachable types.
(corpus_diff::priv::apply_supprs_to_added_removed_fns_vars_unreachable_types):
Rename
corpus_diff::priv::apply_suppressions_to_added_removed_fns_vars
into this.  Apply suppression specifications to added and removed
unreachable types as well.
(corpus_diff::priv::{added,deleted}_unreachable_type_is_suppressed):
Define new member functions.
(corpus_diff::priv::{count_unreachable_types,
changed_unreachable_types_sorted}): Likewise.
(corpus_diff::priv::apply_filters_and_compute_diff_stats): Update
statistics (including walking changed unreachable types to apply
categorization and redundancy filters to them) related to
unreachable types.
(corpus_diff::priv::emit_diff_stats): Emit diff stats related to
unreachable types.
(corpus_diff::priv::maybe_dump_diff_tree): Dump diff tree nodes
related to unreachable types.
(corpus_diff::{deleted_unreachable_types,
deleted_unreachable_types_sorted, added_unreachable_types,
added_unreachable_types_sorted, changed_unreachable_types,
changed_unreachable_types_sorted): Define new member functions.
(corpus_diff::has_changes): Take deleted/added/changed unreachable
types into account.
(corpus_diff::has_incompatible_changes): Take net removed/changed
unreachable types into account.
(corpus_diff::has_net_subtype_changes): Take net removed and
changed unreachable types into account.
(corpus_diff::has_net_changes): Take net removed/added/changed
unreachable types into account.
(corpus_diff::traverse): When traversing the components of a
corpus_diff node, make sure to traverse the changed unreachable
types of the corpus.
(leaf_diff_node_marker_visitor::visit_begin): Arrange for the fact
that the current topmost interface can be nil if we are looking at
types not reachable from global functions/variables.  Also, make
sure that only leaf nodes that are reachable from a global
function/variable are recorded as leaf nodes.
(compute_diff): In the overload for corpus_sptr, compute the
changes between types not reachable from global functions and
variables, if the user wishes that we do so.  Also, add more
comments.
(apply_suppressions): Update for the name change of the function
apply_suppressions_to_added_removed_fns_vars to
apply_supprs_to_added_removed_fns_vars_unreachable_types.
* include/abg-corpus.h
(corpus::{record_type_as_reachable_from_public_interfaces,
type_is_reachable_from_public_interfaces,
get_types_not_reachable_from_public_interfaces}): Declare new
member functions.
(corpus::recording_types_reachable_from_public_interface_supported):
Declare new virtual member function.
(corpus_group::get_public_types_pretty_representations): Declare
new member functons.
(corpus_group::recording_types_reachable_from_public_interface_supported):
Declare new virtual member function.
* src/abg-corpus-priv.h
(corpus::priv::{types_not_reachable_from_pub_ifaces_,
pub_type_pretty_reprs_}): Define new data members.
(corpus::priv::priv): Initialize the pub_type_pretty_reprs_ data
member because it's a pointer.
(corpus::priv::get_public_types_pretty_representations): Declare
new member function.
(corpus::priv::~priv): Declare a destructor.
* src/abg-corpus.cc
(corpus::priv::get_public_types_pretty_representations): Define
new member function.
(corpus::priv::~priv): Define new destructor to delete the new
pub_type_pretty_reprs_ member pointer.
(corpus::{record_type_as_reachable_from_public_interfaces,
type_is_reachable_from_public_interfaces,
get_types_not_reachable_from_public_interfaces,
recording_types_reachable_from_public_interface_supported}):
Define new member functions
(corpus_group::get_public_types_pretty_representations): Likewise.
* include/abg-diff-utils.h (struct deep_ptr_eq_functor): Document
the equality operator.  Also, add an overload to the equality
operator, for weak_ptr<T>.  The existing equality operator
overload was just for shared_ptr<T>.
* include/abg-fwd.h (is_user_defined_type): Declare function.
* include/abg-ir.h (operator!=(const decl_base_sptr&, const
decl_base_sptr&)): Declare new operator.
(type_maps::get_types_sorted_by_name): Declare
new member function.
(decl_base::{g,s}et_is_artificial): Declare new member function.
(function_decl::parameter::{g,s}et_artificial): Remove these
member functions.
* src/abg-ir.cc (operator!=(const decl_base_sptr&, const
decl_base_sptr&)): Define new operator.
(decl_base::priv::is_artificial_): Define new data
member.
(type_maps::priv::sorted_types_): Define new data member.
(struct type_name_comp): Define new comparison functor to sort
types based on their pretty representations.
(decl_base::priv::priv): Initialize it.
(decl_base::{g,s}et_is_artificial): Define new member functions.
(type_maps::get_types_sorted_by_name): Define new member function.
(is_user_defined_type): Define new function overloads.
(strip_typedef, function_type::{function_type, set_parameters}):
Adjust using decl_base::get_is_artificial rather than
function_decl::parameter::get_artificial.
(function_decl::parameter::priv::artificial_): Remove this data
member.
(function_decl::parameter::priv::priv): Adjust to the removal of
function_decl::parameter::priv::artificial_.  This constructor
does not take an "is_artificial" flag anymore.
(function_decl::parameter::parameter): Adjust to the removal of
the is_artificial flag from the arguments of the constructor of
function_decl::parameter::parameter::priv.
(function_decl::parameter::get_artificial): Remove this member
function.
* src/abg-reporter-priv.h (maybe_report_unreachable_type_changes):
Declare new function.
* src/abg-reporter-priv.cc
(maybe_report_unreachable_type_changes): Define new function.
* src/abg-default-reporter.cc (default_reporter::report): In the
overload for corpus_diff&, report added/removed/changed types that
are not reachable from global functions and variables using the
new function maybe_report_unreachable_type_changes.
* src/abg-leaf-reporter.cc (leaf_reporter::report): In the
overload for corpus_diff, report changes to types unreachable from
global functions or variables, using the new function
maybe_report_unreachable_type_changes.
* src/abg-dwarf-reader.cc (build_ir_node_from_die): When the user
requests that all types be loaded, record relevant types as
reachable from global functions and variables.
(build_enum_type, add_or_update_class_type)
(add_or_update_union_type): Read the 'is-artificial' DWARF
attribute and set the corresponding decl_base property
accordingly.
(finish_member_function_reading, strip_typedef)
(function_type::function_type): Adjust using
decl_base::get_is_artificial, rather than
function_decl::parameter::get_artificial.
* include/abg-reader.h
(consider_types_not_reachable_from_public_interfaces): Declare new
function.
* src/abg-reader.cc
(read_context::m_tracking_non_reachable_types): Add new data
member.
(read_context::read_context): Initialize it.
(read_context::tracking_non_reachable_types): Define accessors for
the new data member above.
(read_is_declaration_only): Re-indent.
(read_is_artificial): Define new helper function.
(build_function_parameter): Use the new read_is_artificial
function here, rather than open-coding it.
(build_enum_type_decl, build_class_decl, build_union_decl):
Support reading the 'is-artificial' property by using the new
read_is_artificial function.
(read_corpus_from_input): If the user wants us to take
non-reachable types into account, then make sure we do so.
(read_tracking_non_reachable_types, read_is_non_reachable_type):
Define new static functions.
(handle_element_node, build_type): Read the "is-non-reachable"
attribute on type element nodes if the user wants us to track
non-reachable types.
(consider_types_not_reachable_from_public_interfaces): Define new
function.
* src/abg-writer.cc (write_is_artificial): Define new static
helper function.
(annotate): Adjust using decl_base::get_is_artificial rather than
function_decl::parameter::get_artificial.
(write_enum_type_decl, write_class_decl_opening_tag)
(write_union_decl_opening_tag): Support writing the
"is-artificial" property, using the new write_is_artificial
function.
(write_function_type): Adjust this to use the new
write_is_artificial rather than open-coding writing the
'is-artificial' attribute.
(write_is_non_reachable)
(write_tracking_non_reachable_types): Define new static functions.
(write_enum_type_decl, write_class_decl_opening_tag)
(write_union_decl_opening_tag): Write the 'is-no-reachable'
attribute when applicable.
(write_corpus, write_corpus_group): Write the
'tracking-non-reachable-types' attribute when applicable.
* tools/abidiff.cc (options::options): Initialize ...
(options::show_all_types): ... new data member.
(display_usage): Add help string from the new
--non-reachable-types option.
(parse_command_line): Parse the new --non-reachable-types option.
(set_diff_context_from_opts): Set the
dwarf_reader::read_context::show_unreachable_types property.
(set_native_xml_reader_options): Define new
static function.
(main): Load all types when analyzing the DWARF or the ABIXML
files, if the user wants us to do so.
* tools/abipkgdiff.cc (options::show_all_types): Define new data
member.
(options::options): Initialize it.
(parse_command_line): Parse the --non-reachable-types option to
set the options::show_all_types data member.
(display_usage): Add a help string for the new
--non-reachable-types option.
(set_diff_context_from_opts): Set the
dwarf_reader::read_context::show_unreachable_types property based
on the options::show_all_type data member.
(compare): Configure the read context to load all types while
analyzing the DWARF info, depending on the options::show_all_type
data member.
* doc/manuals/abidiff.rst: Document the new --non-reachable-types
option added to abidiff above.
* doc/manuals/abipkgdiff.rst: Add documentation for the
--non-reachable-types option.
* tests/data/test-diff-suppr/test47-non-reachable-types-v{0,1}.c:
Source code files of test binary input.
* tests/data/test-diff-suppr/test47-non-reachable-types-suppr-{1,2,3,4,5}.txt:
New test input files.
* tests/data/test-diff-suppr/test47-non-reachable-types-report-{1,2,3,4,5,6,7,8,9,10}.txt:
New test reference output files.
* tests/data/test-diff-suppr/test47-non-reachable-types-v{0,1}.o.alltypes.abixml:
New test input abixml.
* tests/data/Makefile.am: Add the new test material to source
distribution.
* tests/test-diff-suppr.cc (in_out_specs): Add the new tests above
to this test harness.
* tests/data/test-abidiff/test-struct1-report.txt: Adjust.
* tests/data/test-diff-pkg/PR24690/flatpak-debuginfo-1.2.4-3.fc30.x86_64.rpm:
New input binary RPM.
* tests/data/test-diff-pkg/PR24690/flatpak-debuginfo-1.4.0-1.fc30.x86_64.rpm:
Likewise.
* tests/data/test-diff-pkg/PR24690/flatpak-devel-1.2.4-3.fc30.x86_64.rpm:
Likewise.
* tests/data/test-diff-pkg/PR24690/flatpak-devel-1.4.0-1.fc30.x86_64.rpm:
Likewise.
* tests/data/test-diff-pkg/PR24690/flatpak-libs-1.2.4-3.fc30.x86_64.rpm:
Likewise.
* tests/data/test-diff-pkg/PR24690/flatpak-libs-1.4.0-1.fc30.x86_64.rpm:
Likewise.
* tests/data/test-diff-pkg/PR24690/flatpak-libs-debuginfo-1.2.4-3.fc30.x86_64.rpm:
Likewise.
* tests/data/test-diff-pkg/PR24690/flatpak-libs-debuginfo-1.4.0-1.fc30.x86_64.rpm:
Likewise.
* tests/data/test-diff-pkg/PR24690/PR24690-report-0.txt: New test
reference output.
* tests/data/Makefile.am: Add the new test material above to
source distribution.
* tests/test-diff-pkg.cc (in_out_specs): Add the new test material
above to this test harness.

Signed-off-by: Dodji Seketeli <dodji@redhat.com>
56 files changed:
doc/manuals/abidiff.rst
doc/manuals/abipkgdiff.rst
include/abg-comparison.h
include/abg-corpus.h
include/abg-diff-utils.h
include/abg-fwd.h
include/abg-ir.h
include/abg-reader.h
src/abg-comparison-priv.h
src/abg-comparison.cc
src/abg-corpus-priv.h
src/abg-corpus.cc
src/abg-default-reporter.cc
src/abg-dwarf-reader.cc
src/abg-ir.cc
src/abg-leaf-reporter.cc
src/abg-reader.cc
src/abg-reporter-priv.cc
src/abg-reporter-priv.h
src/abg-writer.cc
tests/data/Makefile.am
tests/data/test-abidiff/test-struct1-report.txt
tests/data/test-diff-pkg/PR24690/PR24690-report-0.txt [new file with mode: 0644]
tests/data/test-diff-pkg/PR24690/flatpak-debuginfo-1.2.4-3.fc30.x86_64.rpm [new file with mode: 0644]
tests/data/test-diff-pkg/PR24690/flatpak-debuginfo-1.4.0-1.fc30.x86_64.rpm [new file with mode: 0644]
tests/data/test-diff-pkg/PR24690/flatpak-devel-1.2.4-3.fc30.x86_64.rpm [new file with mode: 0644]
tests/data/test-diff-pkg/PR24690/flatpak-devel-1.4.0-1.fc30.x86_64.rpm [new file with mode: 0644]
tests/data/test-diff-pkg/PR24690/flatpak-libs-1.2.4-3.fc30.x86_64.rpm [new file with mode: 0644]
tests/data/test-diff-pkg/PR24690/flatpak-libs-1.4.0-1.fc30.x86_64.rpm [new file with mode: 0644]
tests/data/test-diff-pkg/PR24690/flatpak-libs-debuginfo-1.2.4-3.fc30.x86_64.rpm [new file with mode: 0644]
tests/data/test-diff-pkg/PR24690/flatpak-libs-debuginfo-1.4.0-1.fc30.x86_64.rpm [new file with mode: 0644]
tests/data/test-diff-suppr/test47-non-reachable-types-report-1.txt [new file with mode: 0644]
tests/data/test-diff-suppr/test47-non-reachable-types-report-10.txt [new file with mode: 0644]
tests/data/test-diff-suppr/test47-non-reachable-types-report-2.txt [new file with mode: 0644]
tests/data/test-diff-suppr/test47-non-reachable-types-report-3.txt [new file with mode: 0644]
tests/data/test-diff-suppr/test47-non-reachable-types-report-4.txt [new file with mode: 0644]
tests/data/test-diff-suppr/test47-non-reachable-types-report-5.txt [new file with mode: 0644]
tests/data/test-diff-suppr/test47-non-reachable-types-report-6.txt [new file with mode: 0644]
tests/data/test-diff-suppr/test47-non-reachable-types-report-7.txt [new file with mode: 0644]
tests/data/test-diff-suppr/test47-non-reachable-types-report-8.txt [new file with mode: 0644]
tests/data/test-diff-suppr/test47-non-reachable-types-report-9.txt [new file with mode: 0644]
tests/data/test-diff-suppr/test47-non-reachable-types-suppr-1.txt [new file with mode: 0644]
tests/data/test-diff-suppr/test47-non-reachable-types-suppr-2.txt [new file with mode: 0644]
tests/data/test-diff-suppr/test47-non-reachable-types-suppr-3.txt [new file with mode: 0644]
tests/data/test-diff-suppr/test47-non-reachable-types-suppr-4.txt [new file with mode: 0644]
tests/data/test-diff-suppr/test47-non-reachable-types-suppr-5.txt [new file with mode: 0644]
tests/data/test-diff-suppr/test47-non-reachable-types-v0.c [new file with mode: 0644]
tests/data/test-diff-suppr/test47-non-reachable-types-v0.o [new file with mode: 0644]
tests/data/test-diff-suppr/test47-non-reachable-types-v0.o.alltypes.abixml [new file with mode: 0644]
tests/data/test-diff-suppr/test47-non-reachable-types-v1.c [new file with mode: 0644]
tests/data/test-diff-suppr/test47-non-reachable-types-v1.o [new file with mode: 0644]
tests/data/test-diff-suppr/test47-non-reachable-types-v1.o.alltypes.abixml [new file with mode: 0644]
tests/test-diff-pkg.cc
tests/test-diff-suppr.cc
tools/abidiff.cc
tools/abipkgdiff.cc

index 0c334b445eb469dcd756b4e755293c3751358c57..052f7d56b8e0dfaaeb1b8acf32962107e357c83f 100644 (file)
@@ -210,6 +210,27 @@ Options
     the global variables that were added (defined) to
     *second-shared-library*.
 
+  * ``--non-reachable-types|-t``
+
+    Analyze and emit change reports for all the types of the binary,
+    including those that are not reachable from global functions and
+    variables.
+
+    This option might incur some serious performance degradation as
+    the number of types analyzed can be huge.  However, if paired with
+    the ``--headers-dir{1,2}`` options, the additional non-reachable
+    types analyzed are restricted to those defined in public headers
+    files, thus hopefully making the performance hit acceptable.
+
+    Also, using this option alongside suppression specifications (by
+    also using the ``--suppressions`` option) might help keep the number of
+    analyzed types (and the potential performance degradation) in
+    control.
+
+    Note that without this option, only types that are reachable from
+    global functions and variables are analyzed, so the tool detects
+    and reports changes on these reachable types only.
+
   * ``--no-added-syms``
 
     In the resulting report about the differences between
index 0ba7284664d0746ffd3364ce039ac16a6c49bcc6..34503a0544ac419df87c4a775c08b836a9e3d191 100644 (file)
@@ -251,6 +251,28 @@ Options
        $
 
 
+  * ``--non-reachable-types|-t``
+
+    Analyze and emit change reports for all the types of the binary,
+    including those that are not reachable from global functions and
+    variables.
+
+    This option might incur some serious performance degradation as
+    the number of types analyzed can be huge.  However, if paired with
+    the ``--devel-pkg{1,2}`` options, the additional non-reachable
+    types analyzed are restricted to those defined in the public
+    headers files carried by the referenced development packages, thus
+    hopefully making the performance hit acceptable.
+
+    Also, using this option alongside suppression specifications (by
+    also using the ``--suppressions`` option) might help keep the number of
+    analyzed types (and the potential performance degradation) in
+    control.
+
+    Note that without this option, only types that are reachable from
+    global functions and variables are analyzed, so the tool detects
+    and reports changes on these reachable types only.
+
   *  ``--redundant``
 
     In the diff reports, do display redundant changes.  A redundant
index 8537a43590344fb9b07105f3f0b49a3752a50a56..a4354912194f1211f48a4f2b620cc3a173f8c64b 100644 (file)
@@ -144,6 +144,10 @@ typedef unordered_map<size_t, size_t> pointer_map;
 /// value is a @ref decl_base_sptr.
 typedef unordered_map<string, decl_base_sptr> string_decl_base_sptr_map;
 
+/// Convenience typedef for a map which key is a string and which
+/// value is a @ref type_base_sptr.
+typedef unordered_map<string, type_base_sptr> string_type_base_sptr_map;
+
 /// Convenience typedef for a map which key is an unsigned integer and
 /// which value is a @ref decl_base_sptr
 typedef unordered_map<unsigned, decl_base_sptr> unsigned_decl_base_sptr_map;
@@ -830,6 +834,10 @@ public:
   void
   show_added_symbols_unreferenced_by_debug_info(bool f);
 
+  void show_unreachable_types(bool f);
+
+  bool show_unreachable_types();
+
   bool
   show_impacted_interfaces() const;
 
@@ -2345,6 +2353,24 @@ public:
   const string_elf_symbol_map&
   added_unrefed_variable_symbols() const;
 
+  const string_type_base_sptr_map&
+  deleted_unreachable_types() const;
+
+  const vector<type_base_sptr>&
+  deleted_unreachable_types_sorted() const;
+
+  const string_type_base_sptr_map&
+  added_unreachable_types() const;
+
+  const vector<type_base_sptr>&
+  added_unreachable_types_sorted() const;
+
+  const string_diff_sptr_map&
+  changed_unreachable_types() const;
+
+  const vector<diff_sptr>&
+  changed_unreachable_types_sorted() const;
+
   const diff_context_sptr
   context() const;
 
@@ -2392,6 +2418,12 @@ public:
   friend void
   apply_suppressions(const corpus_diff* diff_tree);
 
+  friend void
+  maybe_report_unreachable_type_changes(const corpus_diff& d,
+                                       const corpus_diff::diff_stats &s,
+                                       const string& indent,
+                                       ostream& out);
+
   friend class default_reporter;
   friend class leaf_reporter;
 }; // end class corpus_diff
@@ -2533,6 +2565,28 @@ public:
   size_t num_leaf_var_changes_filtered_out() const;
   void num_leaf_var_changes_filtered_out(size_t);
   size_t net_num_leaf_var_changes() const;
+
+  size_t num_added_unreachable_types() const;
+  void num_added_unreachable_types(size_t);
+
+  size_t num_added_unreachable_types_filtered_out() const;
+  void num_added_unreachable_types_filtered_out(size_t);
+  size_t net_num_added_unreachable_types() const;
+
+  size_t num_removed_unreachable_types() const;
+  void num_removed_unreachable_types(size_t);
+
+  size_t num_removed_unreachable_types_filtered_out() const;
+  void num_removed_unreachable_types_filtered_out(size_t);
+  size_t net_num_removed_unreachable_types() const;
+
+  size_t num_changed_unreachable_types() const;
+  void num_changed_unreachable_types(size_t);
+
+  size_t num_changed_unreachable_types_filtered_out() const;
+  void num_changed_unreachable_types_filtered_out(size_t);
+  size_t net_num_changed_unreachable_types() const;
+
 }; // end class corpus_diff::diff_stats
 
 /// The base class for the node visitors.  These are the types used to
index ac2d78b1aeab377ef7639b24dcae53ad82b85727..c2adfa860b4094dd8a7c23e0d329a528e6e406b7 100644 (file)
@@ -115,6 +115,18 @@ public:
   const type_maps&
   get_type_per_loc_map() const;
 
+  virtual bool
+  recording_types_reachable_from_public_interface_supported();
+
+  void
+  record_type_as_reachable_from_public_interfaces(const type_base&);
+
+  bool
+  type_is_reachable_from_public_interfaces(const type_base&) const;
+
+  const vector<type_base_wptr>&
+  get_types_not_reachable_from_public_interfaces() const;
+
   const corpus_group*
   get_group() const;
 
@@ -406,6 +418,12 @@ public:
   virtual const elf_symbols&
   get_unreferenced_variable_symbols() const;
 
+  unordered_set<interned_string, hash_interned_string>*
+  get_public_types_pretty_representations();
+
+  virtual bool
+  recording_types_reachable_from_public_interface_supported();
+
   bool
   operator==(const corpus_group&) const;
 }; // end class corpus_group
index 219cab690ba45a781a017bacb9591da07e7e9ee5..4006483c22c9e2db0049693742cab9abd77988f3 100644 (file)
@@ -772,12 +772,36 @@ struct deep_ptr_eq_functor
     return *first == *second;
   }
 
+  /// This equality operator compares pointers by comparing the
+  /// pointed-to objects.
+  ///
+  /// @param first the first comparison argument.
+  ///
+  /// @param second the second comparison argument.
+  ///
+  /// @return true if the objects pointed to by the pointers are
+  /// equal, false otherwise.
   template<typename T>
   bool
   operator()(const shared_ptr<T> first,
             const shared_ptr<T> second) const
   {return operator()(first.get(), second.get());}
-};
+
+  /// This equality operator compares pointers by comparing the
+  /// pointed-to objects.
+  ///
+  /// @param first the first comparison argument.
+  ///
+  /// @param second the second comparison argument.
+  ///
+  /// @return true if the objects pointed to by the pointers are
+  /// equal, false otherwise.
+  template<typename T>
+  bool
+  operator()(const weak_ptr<T> first,
+            const weak_ptr<T> second) const
+  {return operator()(shared_ptr<T>(first), shared_ptr<T>(second));}
+}; // end struct deep_ptr_eq_functor
 
 /// Find the end of the furthest reaching d-path on diagonal k, for
 /// two sequences.  In the paper This is referred to as "the basic
index 823f0b96ce6b28bd9a272bda0181f82ddf46aab8..cbba63424b0cdef7f58c0c2d1ccadfe812062f60 100644 (file)
@@ -572,6 +572,12 @@ is_scope_decl(const decl_base_sptr&);
 bool
 is_member_type(const type_base_sptr&);
 
+bool
+is_user_defined_type(const type_base*);
+
+bool
+is_user_defined_type(const type_base_sptr&);
+
 void
 remove_decl_from_scope(decl_base_sptr);
 
@@ -1311,7 +1317,7 @@ typedef shared_ptr<suppression_base> suppression_sptr;
 /// Convenience typedef for a vector of @ref suppression_sptr
 typedef vector<suppression_sptr> suppressions_type;
 
-} // end namespace comparison
+} // end namespace suppr
 
 void
 dump(const decl_base_sptr, std::ostream&);
index 43455714d560c9bae19e22211cbd5378d7784e1a..6aaf61261512334f152dd8de808dd54481cef5cc 100644 (file)
@@ -465,7 +465,8 @@ istring_type_or_decl_base_sptr_map_type;
 ///
 /// For instance, the type_maps contains a map of string to basic
 /// type, a map of string to class type, a map of string to union
-/// types, etc.
+/// types, etc.  The key of a map entry is the pretty representation
+/// of the type, and the value of the map entry is the type.
 class type_maps
 {
   struct priv;
@@ -544,6 +545,9 @@ public:
 
   const istring_type_base_wptrs_map_type&
   function_types() const;
+
+  const vector<type_base_wptr>&
+  get_types_sorted_by_name() const;
 }; // end class type_maps;
 
 /// This is the abstraction of the set of relevant artefacts (types,
@@ -1503,6 +1507,12 @@ public:
   void
   set_is_anonymous(bool);
 
+  bool
+  get_is_artificial() const;
+
+  void
+  set_is_artificial(bool);
+
   bool
   get_has_anonymous_parent() const;
 
@@ -1586,6 +1596,9 @@ public:
 bool
 operator==(const decl_base_sptr&, const decl_base_sptr&);
 
+bool
+operator!=(const decl_base_sptr&, const decl_base_sptr&);
+
 bool
 operator==(const type_base_sptr&, const type_base_sptr&);
 
@@ -2996,12 +3009,6 @@ public:
   void
   set_index(unsigned i);
 
-  bool
-  get_artificial() const;
-
-  void
-  set_artificial(bool f);
-
   bool
   get_variadic_marker() const;
 
index 4ddc8b0b921f5e8a9e26cac737dce2c81ffa8784..29906546a0d7960ef08dfb4544fd3e45f1f32877 100644 (file)
@@ -103,6 +103,9 @@ void
 add_read_context_suppressions(read_context& ctxt,
                              const suppr::suppressions_type& supprs);
 
+void
+consider_types_not_reachable_from_public_interfaces(read_context& ctxt,
+                                                   bool flag);
 }//end xml_reader
 }//end namespace abigail
 
index 325f012f7793c82742724efbe870787631d28ad4..14799468475dc2a20e928ae44f9972c315316dc8 100644 (file)
@@ -217,6 +217,7 @@ struct diff_context::priv
   bool                                 show_redundant_changes_;
   bool                                 show_syms_unreferenced_by_di_;
   bool                                 show_added_syms_unreferenced_by_di_;
+  bool                                 show_unreachable_types_;
   bool                                 show_impacted_interfaces_;
   bool                                 dump_diff_tree_;
 
@@ -245,6 +246,7 @@ struct diff_context::priv
       show_redundant_changes_(true),
       show_syms_unreferenced_by_di_(true),
       show_added_syms_unreferenced_by_di_(true),
+      show_unreachable_types_(false),
       show_impacted_interfaces_(true),
       dump_diff_tree_()
    {}
@@ -792,16 +794,36 @@ struct scope_diff::priv
 /// A comparison functor for instances of @ref diff.
 struct diff_comp
 {
+  /// Lexicographically compare two diff nodes.
+  ///
+  /// Compare the pretty representation of the first subjects of two
+  /// diff nodes.
+  ///
+  /// @return true iff @p l is less than @p r.
   bool
   operator()(const diff& l, diff& r) const
   {
-    return (get_name(l.first_subject()) < get_name(r.first_subject()));
+    return (get_pretty_representation(l.first_subject(), true)
+           <
+           get_pretty_representation(r.first_subject(), true));
   }
 
+  /// Lexicographically compare two diff nodes.
+  ///
+  /// Compare the pretty representation of the first subjects of two
+  /// diff nodes.
+  ///
+  /// @return true iff @p l is less than @p r.
   bool
   operator()(const diff* l, diff* r) const
   {return operator()(*l, *r);}
 
+  /// Lexicographically compare two diff nodes.
+  ///
+  /// Compare the pretty representation of the first subjects of two
+  /// diff nodes.
+  ///
+  /// @return true iff @p l is less than @p r.
   bool
   operator()(const diff_sptr l, diff_sptr r) const
   {return operator()(l.get(), r.get());}
@@ -989,6 +1011,15 @@ struct corpus_diff::priv
   string_elf_symbol_map                suppressed_added_unrefed_var_syms_;
   string_elf_symbol_map                deleted_unrefed_var_syms_;
   string_elf_symbol_map                suppressed_deleted_unrefed_var_syms_;
+  edit_script                          unreachable_types_edit_script_;
+  string_type_base_sptr_map            deleted_unreachable_types_;
+  vector<type_base_sptr>               deleted_unreachable_types_sorted_;
+  string_type_base_sptr_map            suppressed_deleted_unreachable_types_;
+  string_type_base_sptr_map            added_unreachable_types_;
+  vector<type_base_sptr>               added_unreachable_types_sorted_;
+  string_type_base_sptr_map            suppressed_added_unreachable_types_;
+  string_diff_sptr_map                 changed_unreachable_types_;
+  mutable vector<diff_sptr>            changed_unreachable_types_sorted_;
   diff_maps                            leaf_diffs_;
 
   /// Default constructor of corpus_diff::priv.
@@ -1029,7 +1060,7 @@ struct corpus_diff::priv
   ensure_lookup_tables_populated();
 
   void
-  apply_suppressions_to_added_removed_fns_vars();
+  apply_supprs_to_added_removed_fns_vars_unreachable_types();
 
   bool
   deleted_function_is_suppressed(const function_decl* fn) const;
@@ -1043,6 +1074,12 @@ struct corpus_diff::priv
   bool
   added_variable_is_suppressed(const var_decl* var) const;
 
+  bool
+  added_unreachable_type_is_suppressed(const type_base *t)const ;
+
+  bool
+  deleted_unreachable_type_is_suppressed(const type_base *t)const ;
+
   bool
   deleted_unrefed_fn_sym_is_suppressed(const elf_symbol*) const;
 
@@ -1060,6 +1097,16 @@ struct corpus_diff::priv
   void count_leaf_type_changes(size_t &num_type_changes,
                               size_t &num_type_changes_filtered);
 
+  void count_unreachable_types(size_t &num_added_unreachable_types,
+                              size_t &num_removed_unreachable_types,
+                              size_t &num_changed_unreachable_types,
+                              size_t &num_filtered_added_unreachable_types,
+                              size_t &num_filtered_removed_unreachable_types,
+                              size_t &num_filtered_changed_unreachable_types);
+
+  const vector<diff_sptr>&
+  changed_unreachable_types_sorted() const;
+
   void
   apply_filters_and_compute_diff_stats(corpus_diff::diff_stats&);
 
@@ -1236,6 +1283,12 @@ struct corpus_diff::diff_stats::priv
   size_t               num_leaf_func_changes_filtered_out;
   size_t               num_leaf_var_changes;
   size_t               num_leaf_var_changes_filtered_out;
+  size_t               num_added_unreachable_types;
+  size_t               num_added_unreachable_types_filtered_out;
+  size_t               num_removed_unreachable_types;
+  size_t               num_removed_unreachable_types_filtered_out;
+  size_t               num_changed_unreachable_types;
+  size_t               num_changed_unreachable_types_filtered_out;
 
   priv(diff_context_sptr ctxt)
     : ctxt_(ctxt),
@@ -1267,7 +1320,13 @@ struct corpus_diff::diff_stats::priv
       num_leaf_func_changes(),
       num_leaf_func_changes_filtered_out(),
       num_leaf_var_changes(),
-      num_leaf_var_changes_filtered_out()
+      num_leaf_var_changes_filtered_out(),
+      num_added_unreachable_types(),
+      num_added_unreachable_types_filtered_out(),
+      num_removed_unreachable_types(),
+      num_removed_unreachable_types_filtered_out(),
+      num_changed_unreachable_types(),
+      num_changed_unreachable_types_filtered_out()
   {}
 
   diff_context_sptr
@@ -1292,6 +1351,9 @@ sort_string_function_ptr_map(const string_function_ptr_map& map,
                             vector<function_decl*>& sorted);
 
 void
+sort_string_type_base_sptr_map(string_type_base_sptr_map& map,
+                              vector<type_base_sptr>& sorted);
+void
 sort_string_function_decl_diff_sptr_map
 (const string_function_decl_diff_sptr_map& map,
  function_decl_diff_sptrs_type& sorted);
index f23cf576b1181ec7f1f74b84ea2af04adc157bfd..962811a74f0b96f6dd6a81201512e4cef8ab8d55 100644 (file)
@@ -470,7 +470,6 @@ sort_string_parm_map(const string_parm_map& map,
        ++i)
     sorted.push_back(i->second);
 
-  // TODO: finish this.
   parm_comp comp;
   std::sort(sorted.begin(), sorted.end(), comp);
 }
@@ -496,6 +495,29 @@ sort_artifacts_set(const artifact_sptr_set_type& set,
   std::sort(sorted.begin(), sorted.end(), comp);
 }
 
+/// Sort a map of string to type_base_sptr entities.
+///
+/// The entries are sorted based on the lexicographic order of the
+/// pretty representation of the type_sptr_sptr.  The sorted result is
+/// put in a vector of type_base_sptr.
+///
+/// @param map the map to sort.
+///
+/// @param sorted the resulting vector of type_base_sptr
+/// lexicographically sorted using their pretty representation.
+void
+sort_string_type_base_sptr_map(string_type_base_sptr_map& map,
+                              vector<type_base_sptr>& sorted)
+{
+  for (string_type_base_sptr_map::const_iterator i = map.begin();
+       i != map.end();
+       ++i)
+    sorted.push_back(i->second);
+
+  type_or_decl_base_comp comp;
+  std::sort(sorted.begin(), sorted.end(), comp);
+}
+
 /// Return the first underlying type that is not a qualified type.
 /// @param t the qualified type to consider.
 ///
@@ -1780,6 +1802,24 @@ void
 diff_context::show_added_symbols_unreferenced_by_debug_info(bool f)
 {priv_->show_added_syms_unreferenced_by_di_ = f;}
 
+/// Setter for the flag that indicates if changes on types unreachable
+/// from global functions and variables are to be reported.
+///
+/// @param f if true, then changes on types unreachable from global
+/// functions and variables are to be reported.
+void
+diff_context::show_unreachable_types(bool f)
+{priv_->show_unreachable_types_ = f;}
+
+/// Getter for the flag that indicates if changes on types unreachable
+/// from global functions and variables are to be reported.
+///
+/// @return true iff changes on types unreachable from global
+/// functions and variables are to be reported.
+bool
+diff_context::show_unreachable_types()
+{return priv_->show_unreachable_types_;}
+
 /// Getter of the flag that indicates if the leaf reporter should
 /// display a summary of the interfaces impacted by a given leaf
 /// change or not.
@@ -7622,6 +7662,13 @@ diff_maps::get_distinct_diff_map()
 ///
 /// @param dif the new diff node to insert into the @ref diff_maps.
 ///
+/// @param impacted_iface the interface (global function or variable)
+/// currently being analysed that led to analysing the diff node @p
+/// dif.  In other words, this is the interface impacted by the diff
+/// node @p dif.  Note that this can be nil in cases where we are
+/// directly analysing changes to a type that is not reachable from
+/// any global function or variable.
+///
 /// @return true iff the diff node could be added to the current
 /// instance of @ref diff_maps.
 bool
@@ -7660,20 +7707,23 @@ diff_maps::insert_diff_node(const diff *dif,
   else
       ABG_ASSERT_NOT_REACHED;
 
-  // Update the map that associate the interface that is impacted by
-  // this diff, to this diff node.
+  // Update the map that associates this diff node to the set of
+  // interfaces it impacts.
 
-  diff_artifact_set_map_type::iterator i =
-    priv_->impacted_artifacts_map_.find(dif);
-
-  if (i == priv_->impacted_artifacts_map_.end())
+  if (impacted_iface)
     {
-      artifact_sptr_set_type set;
-      set.insert(impacted_iface);
-      priv_->impacted_artifacts_map_[dif] = set;
+      diff_artifact_set_map_type::iterator i =
+       priv_->impacted_artifacts_map_.find(dif);
+
+      if (i == priv_->impacted_artifacts_map_.end())
+       {
+         artifact_sptr_set_type set;
+         set.insert(impacted_iface);
+         priv_->impacted_artifacts_map_[dif] = set;
+       }
+      else
+       i->second.insert(impacted_iface);
     }
-  else
-    i->second.insert(impacted_iface);
 
   return true;
 }
@@ -8384,6 +8434,202 @@ void
 corpus_diff::diff_stats::num_leaf_var_changes(size_t n)
 {priv_->num_leaf_var_changes = n;}
 
+/// Getter of the number of added types that are unreachable from the
+/// public interface of the ABI corpus.
+///
+/// Public interface means the set of defined and publicly exported
+/// functions and variables of the ABI corpus.
+///
+/// @return the number of added types that are unreachable from the
+/// public interface of the ABI corpus.
+size_t
+corpus_diff::diff_stats::num_added_unreachable_types() const
+{return priv_->num_added_unreachable_types;}
+
+/// Setter of the number of added types that are unreachable from the
+/// public interface (global functions or variables) of the ABI
+/// corpus.
+///
+/// Public interface means the set of defined and publicly exported
+/// functions and variables of the ABI corpus.
+///
+/// @param n the new number of added types that are unreachable from
+/// the public interface of the ABI corpus.
+void
+corpus_diff::diff_stats::num_added_unreachable_types(size_t n)
+{priv_->num_added_unreachable_types = n;}
+
+/// Getter of the number of added types that are unreachable from
+/// public interfaces and that are filtered out by suppression
+/// specifications.
+///
+/// @return the number of added types that are unreachable from public
+/// interfaces and that are filtered out by suppression
+/// specifications.
+size_t
+corpus_diff::diff_stats::num_added_unreachable_types_filtered_out() const
+{return priv_->num_added_unreachable_types_filtered_out;}
+
+/// Setter of the number of added types that are unreachable from
+/// public interfaces and that are filtered out by suppression
+/// specifications.
+///
+/// @param n the new number of added types that are unreachable from
+/// public interfaces and that are filtered out by suppression
+/// specifications.
+void
+corpus_diff::diff_stats::num_added_unreachable_types_filtered_out(size_t n)
+{priv_->num_added_unreachable_types_filtered_out = n;}
+
+/// Getter of the number of added types that are unreachable from
+/// public interfaces and that are *NOT* filtered out by suppression
+/// specifications.
+///
+/// @return the number of added types that are unreachable from public
+/// interfaces and that are *NOT* filtered out by suppression
+/// specifications.
+size_t
+corpus_diff::diff_stats::net_num_added_unreachable_types() const
+{
+  ABG_ASSERT(num_added_unreachable_types()
+            >=
+            num_added_unreachable_types_filtered_out());
+
+  return (num_added_unreachable_types()
+         -
+         num_added_unreachable_types_filtered_out());
+}
+
+/// Getter of the number of removed types that are unreachable from
+/// the public interface of the ABI corpus.
+///
+/// Public interface means the set of defined and publicly exported
+/// functions and variables of the ABI corpus.
+///
+/// @return the number of removed types that are unreachable from
+/// the public interface of the ABI corpus.
+size_t
+corpus_diff::diff_stats::num_removed_unreachable_types() const
+{return priv_->num_removed_unreachable_types;}
+
+/// Setter of the number of removed types that are unreachable from
+/// the public interface of the ABI corpus.
+///
+/// Public interface means the set of defined and publicly exported
+/// functions and variables of the ABI corpus.
+///
+///@param n the new number of removed types that are unreachable from
+/// the public interface of the ABI corpus.
+void
+corpus_diff::diff_stats::num_removed_unreachable_types(size_t n)
+{priv_->num_removed_unreachable_types = n;}
+
+/// Getter of the number of removed types that are not reachable from
+/// public interfaces and that have been filtered out by suppression
+/// specifications.
+///
+/// @return the number of removed types that are not reachable from
+/// public interfaces and that have been filtered out by suppression
+/// specifications.
+size_t
+corpus_diff::diff_stats::num_removed_unreachable_types_filtered_out() const
+{return priv_->num_removed_unreachable_types_filtered_out;}
+
+/// Setter of the number of removed types that are not reachable from
+/// public interfaces and that have been filtered out by suppression
+/// specifications.
+///
+/// @param n the new number of removed types that are not reachable
+/// from public interfaces and that have been filtered out by
+/// suppression specifications.
+void
+corpus_diff::diff_stats::num_removed_unreachable_types_filtered_out(size_t n)
+{priv_->num_removed_unreachable_types_filtered_out = n;}
+
+/// Getter of the number of removed types that are not reachable from
+/// public interfaces and that have *NOT* been filtered out by
+/// suppression specifications.
+///
+/// @return the number of removed types that are not reachable from
+/// public interfaces and that have *NOT* been filtered out by
+/// suppression specifications.
+size_t
+corpus_diff::diff_stats::net_num_removed_unreachable_types() const
+{
+  ABG_ASSERT(num_removed_unreachable_types()
+            >=
+            num_removed_unreachable_types_filtered_out());
+
+  return (num_removed_unreachable_types()
+         -
+         num_removed_unreachable_types_filtered_out());
+}
+
+/// Getter of the number of changed types that are unreachable from
+/// the public interface of the ABI corpus.
+///
+/// Public interface means the set of defined and publicly exported
+/// functions and variables of the ABI corpus.
+///
+/// @return the number of changed types that are unreachable from the
+/// public interface of the ABI corpus.
+size_t
+corpus_diff::diff_stats::num_changed_unreachable_types() const
+{return priv_->num_changed_unreachable_types;}
+
+/// Setter of the number of changed types that are unreachable from
+/// the public interface of the ABI corpus.
+///
+/// Public interface means the set of defined and publicly exported
+/// functions and variables of the ABI corpus.
+///
+///@param n the new number of changed types that are unreachable from
+/// the public interface of the ABI corpus.
+void
+corpus_diff::diff_stats::num_changed_unreachable_types(size_t n)
+{priv_->num_changed_unreachable_types = n;}
+
+/// Getter of the number of changed types that are unreachable from
+/// public interfaces and that have been filtered out by suppression
+/// specifications.
+///
+/// @return the number of changed types that are unreachable from
+/// public interfaces and that have been filtered out by suppression
+/// specifications.
+size_t
+corpus_diff::diff_stats::num_changed_unreachable_types_filtered_out() const
+{return priv_->num_changed_unreachable_types_filtered_out;}
+
+/// Setter of the number of changed types that are unreachable from
+/// public interfaces and that have been filtered out by suppression
+/// specifications.
+///
+/// @param n the new number of changed types that are unreachable from
+/// public interfaces and that have been filtered out by suppression
+/// specifications.
+void
+corpus_diff::diff_stats::num_changed_unreachable_types_filtered_out(size_t n)
+{priv_->num_changed_unreachable_types_filtered_out = n;}
+
+/// Getter of the number of changed types that are unreachable from
+/// public interfaces and that have *NOT* been filtered out by
+/// suppression specifications.
+///
+/// @return the number of changed types that are unreachable from
+/// public interfaces and that have *NOT* been filtered out by
+/// suppression specifications.
+size_t
+corpus_diff::diff_stats::net_num_changed_unreachable_types() const
+{
+  ABG_ASSERT(num_changed_unreachable_types()
+            >=
+            num_changed_unreachable_types_filtered_out());
+
+  return (num_changed_unreachable_types()
+         -
+         num_changed_unreachable_types_filtered_out());
+}
+
 /// Getter for the number of leaf variable changes diff nodes that
 /// have been filtered out.
 ///
@@ -8412,7 +8658,9 @@ corpus_diff::diff_stats::num_leaf_var_changes_filtered_out(size_t n)
 size_t
 corpus_diff::diff_stats::net_num_leaf_var_changes() const
 {return num_leaf_var_changes() - num_leaf_var_changes_filtered_out();}
-// <corpus stuff>
+
+
+// <corpus_diff stuff>
 
 /// Getter of the context associated with this corpus.
 ///
@@ -8784,6 +9032,91 @@ corpus_diff::priv::ensure_lookup_tables_populated()
          }
       }
   }
+
+  // Handle the unreachable_types_edit_script_
+  {
+    edit_script& e = unreachable_types_edit_script_;
+
+    // Populate the map of deleted unreachable types from the
+    // deletions of the edit script.
+    for (vector<deletion>::const_iterator it = e.deletions().begin();
+        it != e.deletions().end();
+        ++it)
+      {
+       unsigned i = it->index();
+       type_base_sptr t
+         (first_->get_types_not_reachable_from_public_interfaces()[i]);
+
+       if (!is_user_defined_type(t))
+         continue;
+
+       string repr = abigail::ir::get_pretty_representation(t, true);
+       deleted_unreachable_types_[repr] = t;
+      }
+
+    // Populate the map of added and change unreachable types from the
+    // insertions of the edit script.
+    for (vector<insertion>::const_iterator it = e.insertions().begin();
+        it != e.insertions().end();
+        ++it)
+      {
+       for (vector<unsigned>::const_iterator iit =
+              it->inserted_indexes().begin();
+            iit != it->inserted_indexes().end();
+            ++iit)
+         {
+           unsigned i = *iit;
+           type_base_sptr t
+             (second_->get_types_not_reachable_from_public_interfaces()[i]);
+
+           if (!is_user_defined_type(t))
+             continue;
+
+           string repr = abigail::ir::get_pretty_representation(t, true);
+
+           // Let's see if the inserted type we are looking at was
+           // reported as deleted as well.
+           //
+           // If it's been deleted and a different version of it has
+           // now been added, it means it's been *changed*.  In that
+           // case we'll compute the diff of that change and store it
+           // in the map of changed unreachable types.
+           //
+           // Otherwise, it means the type's been added so we'll add
+           // it to the set of added unreachable types.
+
+           string_type_base_sptr_map::const_iterator j =
+             deleted_unreachable_types_.find(repr);
+           if (j != deleted_unreachable_types_.end())
+             {
+               // So there was another type of the same pretty
+               // representation which was reported as deleted.
+               // Let's see if they are different or not ...
+               decl_base_sptr old_type = is_decl(j->second);
+               decl_base_sptr new_type = is_decl(t);
+               if (old_type != new_type)
+                 {
+                   // The previously added type is different from this
+                   // one that is added.  That means the initial type
+                   // was changed.  Let's compute its diff and store it
+                   // as a changed type.
+                   diff_sptr d = compute_diff(old_type, new_type, ctxt);
+                   ABG_ASSERT(d->has_changes());
+                   changed_unreachable_types_[repr]= d;
+                 }
+
+               // In any case, the type was both deleted and added,
+               // so we cannot have it marked as being deleted.  So
+               // let's remove it from the deleted types.
+               deleted_unreachable_types_.erase(j);
+             }
+           else
+             // The type wasn't previously reported as deleted, so
+             // it's really added.
+             added_unreachable_types_[repr] = t;
+         }
+      }
+  }
 }
 
 /// Test if a change reports about a given @ref function_decl that is
@@ -8840,10 +9173,11 @@ variable_is_suppressed(const var_decl* var,
   return var_suppr->suppresses_variable(var, k, ctxt);
 }
 
-/// Apply the suppression specifications for this corpus diff to the
-/// set of added and removed functions and variables.
+/// Apply suppression specifications for this corpus diff to the set
+/// of added/removed functions/variables, as well as to types not
+/// reachable from global functions/variables.
 void
-corpus_diff::priv::apply_suppressions_to_added_removed_fns_vars()
+corpus_diff::priv::apply_supprs_to_added_removed_fns_vars_unreachable_types()
 {
   diff_context_sptr ctxt = get_context();
 
@@ -8926,6 +9260,24 @@ corpus_diff::priv::apply_suppressions_to_added_removed_fns_vars()
                if (type_suppr->suppresses_type(c, ctxt))
                  suppressed_deleted_fns_[e->first] = e->second;
              }
+
+         // Apply this type suppression to deleted types
+         // non-reachable from a public interface.
+         for (string_type_base_sptr_map::const_iterator e =
+                deleted_unreachable_types_.begin();
+              e != deleted_unreachable_types_.end();
+              ++e)
+           if (type_suppr->suppresses_type(e->second, ctxt))
+             suppressed_deleted_unreachable_types_[e->first] = e->second;
+
+         // Apply this type suppression to added types
+         // non-reachable from a public interface.
+         for (string_type_base_sptr_map::const_iterator e =
+                added_unreachable_types_.begin();
+              e != added_unreachable_types_.end();
+              ++e)
+           if (type_suppr->suppresses_type(e->second, ctxt))
+             suppressed_added_unreachable_types_[e->first] = e->second;
        }
       // Added/Deleted variables
       else if (variable_suppression_sptr var_suppr =
@@ -8991,6 +9343,50 @@ corpus_diff::priv::deleted_function_is_suppressed(const function_decl* fn) const
   return (i != suppressed_deleted_fns_.end());
 }
 
+/// Test if an added type that is unreachable from public interface
+/// has been suppressed by a suppression specification.
+///
+/// @param t the added unreachable type to be considered.
+///
+/// @return true iff @p t has been suppressed by a suppression
+/// specification.
+bool
+corpus_diff::priv::added_unreachable_type_is_suppressed(const type_base *t)const
+{
+  if (!t)
+    return false;
+
+  string repr = abigail::ir::get_pretty_representation(t, /*internal=*/true);
+  string_type_base_sptr_map::const_iterator i =
+    suppressed_added_unreachable_types_.find(repr);
+  if (i == suppressed_added_unreachable_types_.end())
+    return false;
+
+  return true;
+}
+
+/// Test if a deleted type that is unreachable from public interface
+/// has been suppressed by a suppression specification.
+///
+/// @param t the deleted unreachable type to be considered.
+///
+/// @return true iff @p t has been suppressed by a suppression
+/// specification.
+bool
+corpus_diff::priv::deleted_unreachable_type_is_suppressed(const type_base *t) const
+{
+  if (!t)
+    return false;
+
+  string repr = abigail::ir::get_pretty_representation(t, /*internal=*/true);
+  string_type_base_sptr_map::const_iterator i =
+    suppressed_deleted_unreachable_types_.find(repr);
+  if (i == suppressed_deleted_unreachable_types_.end())
+    return false;
+
+  return true;
+}
+
 /// Test if the change reports for a give given added function has
 /// been deleted.
 ///
@@ -9195,6 +9591,71 @@ corpus_diff::priv::count_leaf_type_changes(size_t &num_changes,
                            num_changes, num_filtered);
 }
 
+/// Count the number of types not reachable from the interface (i.e,
+/// not reachable from global functions or variables).
+///
+/// @param num_added this is set to the number of added types not
+/// reachable from the interface.
+///
+/// @param num_deleted this is set to the number of deleted types not
+/// reachable from the interface.
+///
+/// @param num_changed this is set to the number of changed types not
+/// reachable from the interface.
+///
+/// @param num_filtered_added this is set to the number of added types
+/// not reachable from the interface and that have been filtered out
+/// by suppression specifications.
+///
+/// @param num_filtered_deleted this is set to the number of deleted
+/// types not reachable from the interface and that have been filtered
+/// out by suppression specifications.
+///
+/// @param num_filtered_changed this is set to the number of changed
+/// types not reachable from the interface and that have been filtered
+/// out by suppression specifications.
+void
+corpus_diff::priv::count_unreachable_types(size_t &num_added,
+                                          size_t &num_deleted,
+                                          size_t &num_changed,
+                                          size_t &num_filtered_added,
+                                          size_t &num_filtered_deleted,
+                                          size_t &num_filtered_changed)
+{
+  num_added = added_unreachable_types_.size();
+  num_deleted = deleted_unreachable_types_.size();
+  num_changed = changed_unreachable_types_.size();
+  num_filtered_added = suppressed_added_unreachable_types_.size();
+  num_filtered_deleted = suppressed_deleted_unreachable_types_.size();
+
+  for (vector<diff_sptr>::const_iterator i =
+        changed_unreachable_types_sorted().begin();
+       i != changed_unreachable_types_sorted().end();
+       ++i)
+    if (!(*i)->to_be_reported())
+      ++num_filtered_changed;
+}
+
+/// Get the sorted vector of diff nodes representing changed
+/// unreachable types.
+///
+/// Upon the first invocation of this method, if the vector is empty,
+/// this function gets the diff nodes representing changed
+/// unreachable, sort them, and return the sorted vector.
+///
+/// @return the sorted vector of diff nodes representing changed
+/// unreachable types.
+const vector<diff_sptr>&
+corpus_diff::priv::changed_unreachable_types_sorted() const
+{
+if (changed_unreachable_types_sorted_.empty())
+  if (!changed_unreachable_types_.empty())
+    sort_string_diff_sptr_map(changed_unreachable_types_,
+                             changed_unreachable_types_sorted_);
+
+ return changed_unreachable_types_sorted_;
+}
+
 /// Compute the diff stats.
 ///
 /// To know the number of functions that got filtered out, this
@@ -9211,7 +9672,6 @@ corpus_diff::priv::count_leaf_type_changes(size_t &num_changes,
 /// got filtered out from the report
 void
 corpus_diff::priv::apply_filters_and_compute_diff_stats(diff_stats& stat)
-
 {
   stat.num_func_removed(deleted_fns_.size());
   stat.num_removed_func_filtered_out(suppressed_deleted_fns_.size());
@@ -9249,6 +9709,17 @@ corpus_diff::priv::apply_filters_and_compute_diff_stats(diff_stats& stat)
       ctxt->maybe_apply_filters(diff);
     }
 
+  // walk the changed unreachable types to apply categorization
+  // filters
+  for (diff_sptrs_type::const_iterator i =
+         changed_unreachable_types_sorted().begin();
+       i != changed_unreachable_types_sorted().end();
+       ++i)
+    {
+      diff_sptr diff = *i;
+      ctxt->maybe_apply_filters(diff);
+    }
+
   categorize_redundant_changed_sub_nodes();
 
   // Walk the changed function diff nodes to count the number of
@@ -9326,6 +9797,33 @@ corpus_diff::priv::apply_filters_and_compute_diff_stats(diff_stats& stat)
     stat.num_leaf_changes(num_changes);
     stat.num_leaf_changes_filtered_out(num_filtered);
   }
+
+  // Walk the unreachable types to count them
+  {
+    size_t num_added_unreachable_types = 0,
+      num_changed_unreachable_types = 0,
+      num_deleted_unreachable_types = 0,
+      num_added_unreachable_types_filtered = 0,
+      num_changed_unreachable_types_filtered = 0,
+      num_deleted_unreachable_types_filtered = 0;
+
+    count_unreachable_types(num_added_unreachable_types,
+                           num_deleted_unreachable_types,
+                           num_changed_unreachable_types,
+                           num_added_unreachable_types_filtered,
+                           num_deleted_unreachable_types_filtered,
+                           num_changed_unreachable_types_filtered);
+
+    stat.num_added_unreachable_types(num_added_unreachable_types);
+    stat.num_removed_unreachable_types(num_deleted_unreachable_types);
+    stat.num_changed_unreachable_types(num_changed_unreachable_types);
+    stat.num_added_unreachable_types_filtered_out
+      (num_added_unreachable_types_filtered);
+    stat.num_removed_unreachable_types_filtered_out
+      (num_deleted_unreachable_types_filtered);
+    stat.num_changed_unreachable_types_filtered_out
+      (num_changed_unreachable_types_filtered);
+  }
 }
 
 /// Emit the summary of the functions & variables that got
@@ -9487,6 +9985,43 @@ corpus_diff::priv::emit_diff_stats(const diff_stats&     s,
       out << "\n";
     }
 
+  // Show statistics about types not reachable from global
+  // functions/variables.
+  if (ctxt->show_unreachable_types())
+    {
+      size_t total_nb_variable_changes =
+       s.num_removed_unreachable_types()
+       + s.num_changed_unreachable_types()
+       + s.num_added_unreachable_types();
+
+      // Show summary of unreachable types
+      out << indent << "Unreachable types summary: "
+         << s.net_num_removed_unreachable_types()
+         << " removed";
+      if (s.num_removed_unreachable_types_filtered_out())
+       out << " (" << s.num_removed_unreachable_types_filtered_out()
+           << " filtered out)";
+      out << ", ";
+
+      out << s.net_num_changed_unreachable_types()
+         << " changed";
+      if (s.num_changed_unreachable_types_filtered_out())
+       out << " (" << s.num_changed_unreachable_types_filtered_out()
+           << " filtered out)";
+      out << ", ";
+
+      out << s.net_num_added_unreachable_types()
+         << " added";
+      if (s.num_added_unreachable_types_filtered_out())
+       out << " (" << s.num_added_unreachable_types_filtered_out()
+           << " filtered out)";
+      if (total_nb_variable_changes <= 1)
+       out << " type";
+      else
+       out << " types";
+      out << "\n";
+    }
+
   if (ctxt->show_symbols_unreferenced_by_debug_info()
       && (s.num_func_syms_removed()
          || s.num_func_syms_added()
@@ -9577,6 +10112,15 @@ corpus_diff::priv::categorize_redundant_changed_sub_nodes()
       diff_sptr diff = *i;
       categorize_redundancy(diff);
     }
+
+  for (diff_sptrs_type::const_iterator i =
+        changed_unreachable_types_sorted().begin();
+       i!= changed_unreachable_types_sorted().end();
+       ++i)
+    {
+      diff_sptr diff = *i;
+      categorize_redundancy(diff);
+    }
 }
 
 /// Walk the changed functions and variables diff nodes and clear the
@@ -9641,6 +10185,20 @@ corpus_diff::priv::maybe_dump_diff_tree()
          print_diff_tree(d, *ctxt->error_output_stream());
        }
     }
+
+  if (!changed_unreachable_types_sorted().empty())
+    {
+      *ctxt->error_output_stream() << "\nchanged unreachable "
+       "types diff tree: \n\n";
+      for (vector<diff_sptr>::const_iterator i =
+            changed_unreachable_types_sorted().begin();
+          i != changed_unreachable_types_sorted().end();
+          ++i)
+       {
+         diff_sptr d = *i;
+         print_diff_tree(d, *ctxt->error_output_stream());
+       }
+    }
 }
 
 /// Populate the vector of children node of the @ref corpus_diff type.
@@ -9868,6 +10426,77 @@ const string_elf_symbol_map&
 corpus_diff::added_unrefed_variable_symbols() const
 {return priv_->added_unrefed_var_syms_;}
 
+/// Getter for a map of deleted types that are not reachable from
+/// global functions/variables.
+///
+/// @return a map that associates pretty representation of deleted
+/// unreachable types and said types.
+const string_type_base_sptr_map&
+corpus_diff::deleted_unreachable_types() const
+{return priv_->deleted_unreachable_types_;}
+
+/// Getter of a sorted vector of deleted types that are not reachable
+/// from global functions/variables.
+///
+/// @return a sorted vector of deleted types that are not reachable
+/// from global functions/variables.  The types are lexicographically
+/// sorted by considering their pretty representation.
+const vector<type_base_sptr>&
+corpus_diff::deleted_unreachable_types_sorted() const
+{
+  if (priv_->deleted_unreachable_types_sorted_.empty())
+    if (!priv_->deleted_unreachable_types_.empty())
+      sort_string_type_base_sptr_map(priv_->deleted_unreachable_types_,
+                                    priv_->deleted_unreachable_types_sorted_);
+
+  return priv_->deleted_unreachable_types_sorted_;
+}
+
+/// Getter for a map of added types that are not reachable from global
+/// functions/variables.
+///
+/// @return a map that associates pretty representation of added
+/// unreachable types and said types.
+const string_type_base_sptr_map&
+corpus_diff::added_unreachable_types() const
+{return priv_->added_unreachable_types_;}
+
+/// Getter of a sorted vector of added types that are not reachable
+/// from global functions/variables.
+///
+/// @return a sorted vector of added types that are not reachable from
+/// global functions/variables.  The types are lexicographically
+/// sorted by considering their pretty representation.
+const vector<type_base_sptr>&
+corpus_diff::added_unreachable_types_sorted() const
+{
+  if (priv_->added_unreachable_types_sorted_.empty())
+    if (!priv_->added_unreachable_types_.empty())
+      sort_string_type_base_sptr_map(priv_->added_unreachable_types_,
+                                    priv_->added_unreachable_types_sorted_);
+
+  return priv_->added_unreachable_types_sorted_;
+}
+
+/// Getter for a map of changed types that are not reachable from
+/// global functions/variables.
+///
+/// @return a map that associates pretty representation of changed
+/// unreachable types and said types.
+const string_diff_sptr_map&
+corpus_diff::changed_unreachable_types() const
+{return priv_->changed_unreachable_types_;}
+
+/// Getter of a sorted vector of changed types that are not reachable
+/// from global functions/variables.
+///
+/// @return a sorted vector of changed types that are not reachable
+/// from global functions/variables.  The diffs are lexicographically
+/// sorted by considering their pretty representation.
+const vector<diff_sptr>&
+corpus_diff::changed_unreachable_types_sorted() const
+{return priv_->changed_unreachable_types_sorted();}
+
 /// Getter of the diff context of this diff
 ///
 /// @return the diff context for this diff.
@@ -9892,7 +10521,8 @@ corpus_diff::get_pretty_representation() const
     }
   return priv_->pretty_representation_;
 }
-/// Return true iff the current diff node carries a change.
+/// Return true iff the current @ref corpus_diff node carries a
+/// change.
 ///
 /// @return true iff the current diff node carries a change.
 bool
@@ -9909,7 +10539,10 @@ corpus_diff::has_changes() const
          || priv_->added_unrefed_fn_syms_.size()
          || priv_->deleted_unrefed_fn_syms_.size()
          || priv_->added_unrefed_var_syms_.size()
-         || priv_->deleted_unrefed_var_syms_.size());
+         || priv_->deleted_unrefed_var_syms_.size()
+         || priv_->deleted_unreachable_types_.size()
+         || priv_->added_unreachable_types_.size()
+         || priv_->changed_unreachable_types_.size());
 }
 
 /// Test if the current instance of @ref corpus_diff carries changes
@@ -9949,7 +10582,9 @@ corpus_diff::has_incompatible_changes() const
              && stats.net_num_func_changed() != 0)
          || stats.net_num_vars_removed() != 0
          || stats.net_num_removed_func_syms() != 0
-         || stats.net_num_removed_var_syms() != 0);
+         || stats.net_num_removed_var_syms() != 0
+         || stats.net_num_removed_unreachable_types() != 0
+         || stats.net_num_changed_unreachable_types() != 0);
 }
 
 /// Test if the current instance of @ref corpus_diff carries subtype
@@ -9966,7 +10601,9 @@ corpus_diff::has_net_subtype_changes() const
       apply_filters_and_suppressions_before_reporting();
 
   return (stats.net_num_func_changed() != 0
-         || stats.net_num_vars_changed() != 0);
+         || stats.net_num_vars_changed() != 0
+         || stats.net_num_removed_unreachable_types() != 0
+         || stats.net_num_changed_unreachable_types() != 0);
 }
 
 /// Test if the current instance of @ref corpus_diff carries changes
@@ -9992,7 +10629,10 @@ corpus_diff::has_net_changes() const
            || stats.net_num_vars_added()
            || stats.net_num_added_var_syms()
            || stats.net_num_vars_removed()
-           || stats.net_num_removed_var_syms());
+           || stats.net_num_removed_var_syms()
+           || stats.net_num_added_unreachable_types()
+           || stats.net_num_removed_unreachable_types()
+           || stats.net_num_changed_unreachable_types());
 }
 
 /// Apply the different filters that are registered to be applied to
@@ -10037,10 +10677,14 @@ struct leaf_diff_node_marker_visitor : public diff_node_visitor
   /// This is called when the visitor visits a diff node.
   ///
   /// It basically tests if the diff node being visited is a leaf diff
-  /// node - that is, if contains local changes.  If it does, then the
+  /// node - that is, it contains local changes.  If it does, then the
   /// node is added to the set of maps that hold leaf diffs in the
   /// current corpus_diff.
   ///
+  /// Note that only leaf nodes that are reachable from public
+  /// interfaces (global functions or variables) are collected by this
+  /// visitor.
+  ///
   /// @param d the diff node being visited.
   virtual void
   visit_begin(diff *d)
@@ -10089,12 +10733,15 @@ struct leaf_diff_node_marker_visitor : public diff_node_visitor
        diff_context_sptr ctxt = d->context();
        const corpus_diff *corpus_diff_node = ctxt->get_corpus_diff().get();
        ABG_ASSERT(corpus_diff_node);
-       type_or_decl_base_sptr iface =
-         get_current_topmost_iface_diff()->first_subject();
-       // So this is diff node carries a leaf change.  Let's add it
-       // to the set of of leaf diffs of corpus_diff_node.
-       const_cast<corpus_diff*>(corpus_diff_node)->
-         get_leaf_diffs().insert_diff_node(d, iface);
+
+       if (diff *iface_diff = get_current_topmost_iface_diff())
+         {
+           type_or_decl_base_sptr iface = iface_diff->first_subject();
+           // So this diff node carries a leaf change.  Let's add it
+           // to the set of of leaf diffs of corpus_diff_node.
+           const_cast<corpus_diff*>(corpus_diff_node)->
+             get_leaf_diffs().insert_diff_node(d, iface);
+         }
       }
   }
 }; // end struct leaf_diff_node_marker_visitor
@@ -10218,6 +10865,27 @@ corpus_diff::traverse(diff_node_visitor& v)
 
   v.set_current_topmost_iface_diff(0);
 
+  // Traverse the changed unreachable type diffs.  These diffs are on
+  // types that are not reachable from global functions or variables.
+  for (vector<diff_sptr>::const_iterator i =
+        changed_unreachable_types_sorted().begin();
+       i != changed_unreachable_types_sorted().end();
+       ++i)
+    {
+      if (diff_sptr d = *i)
+       {
+         const diff_context_sptr &ctxt = context();
+         if (ctxt->visiting_a_node_twice_is_forbidden_per_interface())
+           ctxt->forget_visited_diffs();
+
+         if (!d->traverse(v))
+           {
+             v.visit_end(this);
+             return false;
+           }
+       }
+    }
+
   v.visit_end(this);
   return true;
 }
@@ -10243,6 +10911,7 @@ compute_diff(const corpus_sptr  f,
   typedef corpus::variables::const_iterator vars_it_type;
   typedef elf_symbols::const_iterator symbols_it_type;
   typedef diff_utils::deep_ptr_eq_functor eq_type;
+  typedef vector<type_base_wptr>::const_iterator type_base_wptr_it_type;
 
   ABG_ASSERT(f && s);
 
@@ -10262,17 +10931,21 @@ compute_diff(const corpus_sptr        f,
   r->priv_->architectures_equal_ =
     f->get_architecture_name() == s->get_architecture_name();
 
+  // Compute the diff of publicly defined and exported functions
   diff_utils::compute_diff<fns_it_type, eq_type>(f->get_functions().begin(),
                                                 f->get_functions().end(),
                                                 s->get_functions().begin(),
                                                 s->get_functions().end(),
                                                 r->priv_->fns_edit_script_);
 
+  // Compute the diff of publicly defined and exported variables.
   diff_utils::compute_diff<vars_it_type, eq_type>
     (f->get_variables().begin(), f->get_variables().end(),
      s->get_variables().begin(), s->get_variables().end(),
      r->priv_->vars_edit_script_);
 
+  // Compute the diff of function elf symbols not referenced by debug
+  // info.
   diff_utils::compute_diff<symbols_it_type, eq_type>
     (f->get_unreferenced_function_symbols().begin(),
      f->get_unreferenced_function_symbols().end(),
@@ -10280,6 +10953,8 @@ compute_diff(const corpus_sptr  f,
      s->get_unreferenced_function_symbols().end(),
      r->priv_->unrefed_fn_syms_edit_script_);
 
+  // Compute the diff of variable elf symbols not referenced by debug
+  // info.
     diff_utils::compute_diff<symbols_it_type, eq_type>
     (f->get_unreferenced_variable_symbols().begin(),
      f->get_unreferenced_variable_symbols().end(),
@@ -10287,6 +10962,16 @@ compute_diff(const corpus_sptr f,
      s->get_unreferenced_variable_symbols().end(),
      r->priv_->unrefed_var_syms_edit_script_);
 
+    if (ctxt->show_unreachable_types())
+      // Compute the diff of types not reachable from public functions
+      // or global variables that are exported.
+      diff_utils::compute_diff<type_base_wptr_it_type, eq_type>
+       (f->get_types_not_reachable_from_public_interfaces().begin(),
+        f->get_types_not_reachable_from_public_interfaces().end(),
+        s->get_types_not_reachable_from_public_interfaces().begin(),
+        s->get_types_not_reachable_from_public_interfaces().end(),
+        r->priv_->unreachable_types_edit_script_);
+
   r->priv_->ensure_lookup_tables_populated();
 
   return r;
@@ -10953,8 +11638,8 @@ void
 apply_suppressions(diff_sptr diff_tree)
 {apply_suppressions(diff_tree.get());}
 
-/// Walk a diff tree and appply the suppressions carried by the
-/// context.  If the suppression applies to a given node than
+/// Walk a @ref corpus_diff tree and appply the suppressions carried
+/// by the context.  If the suppression applies to a given node then
 /// categorize the node into the SUPPRESSED_CATEGORY category and
 /// propagate that categorization.
 ///
@@ -10974,9 +11659,11 @@ apply_suppressions(const corpus_diff* diff_tree)
       const_cast<corpus_diff*>(diff_tree)->traverse(v);
       diff_tree->context()->forbid_visiting_a_node_twice(s);
 
-      // ... then also visit the set added and removed functions,
-      // variables, and symbols
-      diff_tree->priv_->apply_suppressions_to_added_removed_fns_vars();
+      // ... then also visit the set of added and removed functions,
+      // variables, symbols, and types not reachable from global
+      // functions and variables.
+      diff_tree->priv_->
+       apply_supprs_to_added_removed_fns_vars_unreachable_types();
     }
 }
 
index 4422532e873d3bbbc60ea118cfea8d735737aec7..bb7dcde656c5371847bfc4294025d2243ffea216 100644 (file)
@@ -718,6 +718,8 @@ struct corpus::priv
   // the type maps of each translation unit.
   type_maps                                    types_;
   type_maps                                    type_per_loc_map_;
+  mutable vector<type_base_wptr>               types_not_reachable_from_pub_ifaces_;
+  unordered_set<interned_string, hash_interned_string> *pub_type_pretty_reprs_;
 
 private:
   priv();
@@ -728,7 +730,8 @@ public:
     : env(e),
       group(),
       origin_(ARTIFICIAL_ORIGIN),
-      path(p)
+      path(p),
+      pub_type_pretty_reprs_()
   {}
 
   void
@@ -739,6 +742,11 @@ public:
 
   const type_maps&
   get_types() const;
+
+  unordered_set<interned_string, hash_interned_string>*
+  get_public_types_pretty_representations();
+
+  ~priv();
 }; // end struct corpus::priv
 
 void
index 486e335e11d9d064239fb58f38b2c2d2247dfe8a..4088ff995366db43d08db63a3ecd91f533329fc5 100644 (file)
@@ -450,6 +450,29 @@ const type_maps&
 corpus::priv::get_types() const
 {return types_;}
 
+/// Getter of the set of pretty representation of types that are
+/// reachable from public interfaces (global functions and variables).
+///
+/// @return the set of pretty representation of types that are
+/// reachable from public interfaces (global functions and variables).
+unordered_set<interned_string, hash_interned_string>*
+corpus::priv::get_public_types_pretty_representations()
+{
+  if (group)
+    return group->get_public_types_pretty_representations();
+
+  if (pub_type_pretty_reprs_ == 0)
+    pub_type_pretty_reprs_ =
+       new unordered_set<interned_string, hash_interned_string>;
+  return pub_type_pretty_reprs_;
+}
+
+/// Destructor of the @ref corpus::priv type.
+corpus::priv::~priv()
+{
+  delete pub_type_pretty_reprs_;
+}
+
 /// Record a canonical type which has been computed for the current
 /// corpus.
 ///
@@ -610,6 +633,81 @@ const type_maps&
 corpus::get_type_per_loc_map() const
 {return priv_->type_per_loc_map_;}
 
+/// Test if the recording of reachable types (and thus, indirectly,
+/// the recording of non-reachable types) is activated for the
+/// current @ref corpus.
+///
+/// @return true iff the recording of reachable types is activated for
+/// the current @ref corpus.
+bool
+corpus::recording_types_reachable_from_public_interface_supported()
+{
+  return (priv_->get_public_types_pretty_representations()
+         && !priv_->get_public_types_pretty_representations()->empty());
+}
+
+/// Record a type as being reachable from public interfaces (global
+/// functions and variables).
+///
+/// @param t the type to record as reachable.
+void
+corpus::record_type_as_reachable_from_public_interfaces(const type_base& t)
+{
+  string repr = get_pretty_representation(&t, /*internal=*/true);
+  interned_string s = t.get_environment()->intern(repr);
+  priv_->get_public_types_pretty_representations()->insert(s);
+}
+
+/// Test if a type is reachable from public interfaces (global
+/// functions and variables).
+///
+/// For a type to be considered reachable from public interfaces, it
+/// must have been previously marked as such by calling
+/// corpus::record_type_as_reachable_from_public_interfaces.
+///
+/// @param t the type to test for.
+///
+/// @return true iff @p t is reachable from public interfaces.
+bool
+corpus::type_is_reachable_from_public_interfaces(const type_base& t) const
+{
+  string repr = get_pretty_representation(&t, /*internal=*/true);
+  interned_string s = t.get_environment()->intern(repr);
+
+  return (priv_->get_public_types_pretty_representations()->find(s)
+         !=  priv_->get_public_types_pretty_representations()->end());
+}
+
+/// Getter ofa a sorted vector of the types that are *NOT* reachable
+/// from public interfaces.
+///
+/// Note that for this to be non-empty, the libabigail reader that
+/// analyzed the input (be it a binary or an abixml file) must have be
+/// configured to load types that are not reachable from public
+/// interfaces.
+///
+/// @return a reference to a vector of sorted types NON reachable from
+/// public interfaces.
+const vector<type_base_wptr>&
+corpus::get_types_not_reachable_from_public_interfaces() const
+{
+  if (priv_->types_not_reachable_from_pub_ifaces_.empty())
+    {
+      const type_maps& types = get_types();
+      for (vector<type_base_wptr>::const_iterator it =
+            types.get_types_sorted_by_name().begin();
+          it != types.get_types_sorted_by_name().end();
+          ++it)
+       {
+         type_base_sptr t(*it);
+         if (!type_is_reachable_from_public_interfaces(*t))
+           priv_->types_not_reachable_from_pub_ifaces_.push_back(t);
+       }
+    }
+
+  return priv_->types_not_reachable_from_pub_ifaces_;
+}
+
 /// Get the maps that associate a location string to a certain kind of
 /// type.
 ///
@@ -1524,6 +1622,7 @@ struct corpus_group::priv
   unordered_map<string, elf_symbol_sptr> unrefed_var_symbol_map;
   elf_symbols                  unrefed_var_symbols;
   bool                         unrefed_var_symbols_built;
+  unordered_set<interned_string, hash_interned_string> pub_type_pretty_reprs_;
 
   priv()
     : unrefed_fun_symbols_built(),
@@ -1913,6 +2012,22 @@ corpus_group::get_unreferenced_variable_symbols() const
   return priv_->unrefed_var_symbols;
 }
 
+/// Getter of a pointer to the set of types reachable from public
+/// interfaces of a given corpus group.
+unordered_set<interned_string, hash_interned_string>*
+corpus_group::get_public_types_pretty_representations()
+{return &priv_->pub_type_pretty_reprs_;}
+
+/// Test if the recording of reachable types (and thus, indirectly,
+/// the recording of non-reachable types) is activated for the
+/// current @ref corpus_group.
+///
+/// @return true iff the recording of reachable types is activated for
+/// the current @ref corpus_group.
+bool
+corpus_group::recording_types_reachable_from_public_interface_supported()
+{return !get_public_types_pretty_representations()->empty();}
+
 // </corpus_group stuff>
 
 }// end namespace ir
index 5e582861ee25844d0d0a35e33783680919db1301..5234b51da10c7896056fe15ae43c53dc504c84bf 100644 (file)
@@ -2245,6 +2245,11 @@ default_reporter::report(const corpus_diff& d, ostream& out,
        out << '\n';
     }
 
+  // Report added/removed/changed types not reacheable from public
+  // interfaces.
+
+  maybe_report_unreachable_type_changes(d, s, indent, out);
+
   d.priv_->maybe_dump_diff_tree();
 }
 
index 6f8f5beb3b30c73d876e7f37d9fc055608b6adc6..69c328593ec6e846a7cf13adc19ad31013f5ebe2 100644 (file)
@@ -14547,6 +14547,8 @@ build_enum_type(read_context&   ctxt,
       while (dwarf_siblingof(&child, &child) == 0);
     }
 
+  bool is_artificial = die_is_artificial(die);
+
   // DWARF up to version 4 (at least) doesn't seem to carry the
   // underlying type, so let's create an artificial one here, which
   // sole purpose is to be passed to the constructor of the
@@ -14563,6 +14565,7 @@ build_enum_type(read_context&   ctxt,
   ABG_ASSERT(t);
   result.reset(new enum_type_decl(name, loc, t, enms, linkage_name));
   result->set_is_anonymous(enum_is_anonymous);
+  result->set_is_artificial(is_artificial);
   ctxt.associate_die_to_type(die, result, where_offset);
   return result;
 }
@@ -14619,7 +14622,7 @@ finish_member_function_reading(Dwarf_Die*                 die,
       first_parm = f->get_parameters()[0];
 
     bool is_artificial =
-      first_parm && first_parm->get_artificial();;
+      first_parm && first_parm->get_is_artificial();;
     pointer_type_def_sptr this_ptr_type;
     type_base_sptr other_klass;
 
@@ -15045,6 +15048,7 @@ add_or_update_class_type(read_context&   ctxt,
 
   uint64_t size = 0;
   die_size_in_bits(die, size);
+  bool is_artificial = die_is_artificial(die);
 
   Dwarf_Die child;
   bool has_child = (dwarf_child(die, &child) == 0);
@@ -15074,6 +15078,8 @@ add_or_update_class_type(read_context&   ctxt,
   if (size)
     result->set_size_in_bits(size);
 
+  result->set_is_artificial(is_artificial);
+
   ctxt.associate_die_to_type(die, result, where_offset);
 
   ctxt.maybe_schedule_declaration_only_class_for_resolution(result);
@@ -15383,6 +15389,7 @@ add_or_update_union_type(read_context&  ctxt,
 
   uint64_t size = 0;
   die_size_in_bits(die, size);
+  bool is_artificial = die_is_artificial(die);
 
   if (union_type)
     {
@@ -15406,6 +15413,8 @@ add_or_update_union_type(read_context&  ctxt,
       result->set_is_declaration_only(false);
     }
 
+  result->set_is_artificial(is_artificial);
+
   ctxt.associate_die_to_type(die, result, where_offset);
 
   // TODO: maybe schedule declaration-only union for result like we do
@@ -17619,7 +17628,15 @@ build_ir_node_from_die(read_context&   ctxt,
 
   if ((result = ctxt.lookup_decl_from_die_offset(dwarf_dieoffset(die),
                                                 source_of_die)))
-    return result;
+    {
+      if (ctxt.load_all_types())
+       if (called_from_public_decl)
+         if (type_base_sptr t = is_type(result))
+           if (corpus *abi_corpus = scope->get_corpus())
+             abi_corpus->record_type_as_reachable_from_public_interfaces(*t);
+
+      return result;
+    }
 
   switch (tag)
     {
@@ -18091,6 +18108,13 @@ build_ir_node_from_die(read_context&   ctxt,
     ctxt.associate_die_to_decl(die, is_decl(result), where_offset,
                               /*associate_by_repr=*/false);
 
+  if (result)
+    if (ctxt.load_all_types())
+      if (called_from_public_decl)
+       if (type_base_sptr t = is_type(result))
+         if (corpus *abi_corpus = scope->get_corpus())
+           abi_corpus->record_type_as_reachable_from_public_interfaces(*t);
+
   return result;
 }
 
index a6ae7ae68d8831c887ad57420d484e44b55df065..9ac24c38365045c5195cc4bbde76237eb48583aa 100644 (file)
@@ -420,6 +420,7 @@ struct type_maps::priv
   mutable istring_type_base_wptrs_map_type     array_types_;
   mutable istring_type_base_wptrs_map_type     subrange_types_;
   mutable istring_type_base_wptrs_map_type     function_types_;
+  mutable vector<type_base_wptr>               sorted_types_;
 }; // end struct type_maps::priv
 
 type_maps::type_maps()
@@ -596,6 +597,99 @@ istring_type_base_wptrs_map_type&
 type_maps::function_types()
 {return priv_->function_types_;}
 
+/// A comparison functor to compare/sort types based on their pretty
+/// representations.
+struct type_name_comp
+{
+  /// Comparison operator for two instances of @ref type_base.
+  ///
+  /// This compares the two types by lexicographically comparing their
+  /// pretty representation.
+  ///
+  /// @param l the left-most type to compare.
+  ///
+  /// @param r the right-most type to compare.
+  ///
+  /// @return true iff @p l < @p r.
+  bool
+  operator()(type_base *l, type_base *r) const
+  {
+    if (l == 0 && r == 0)
+      return false;
+
+    string l_repr = get_pretty_representation(l);
+    string r_repr = get_pretty_representation(r);
+    return l_repr < r_repr;
+  }
+
+  /// Comparison operator for two instances of @ref type_base.
+  ///
+  /// This compares the two types by lexicographically comparing their
+  /// pretty representation.
+  ///
+  /// @param l the left-most type to compare.
+  ///
+  /// @param r the right-most type to compare.
+  ///
+  /// @return true iff @p l < @p r.
+  bool
+  operator()(const type_base_sptr &l, const type_base_sptr &r) const
+  {return operator()(l.get(), r.get());}
+
+  /// Comparison operator for two instances of @ref type_base.
+  ///
+  /// This compares the two types by lexicographically comparing their
+  /// pretty representation.
+  ///
+  /// @param l the left-most type to compare.
+  ///
+  /// @param r the right-most type to compare.
+  ///
+  /// @return true iff @p l < @p r.
+  bool
+  operator()(const type_base_wptr &l, const type_base_wptr &r) const
+  {return operator()(type_base_sptr(l), type_base_sptr(r));}
+}; // end struct type_name_comp
+
+/// Getter of all types types sorted by their pretty representation.
+///
+/// @return a sorted vector of all types sorted by their pretty
+/// representation.
+const vector<type_base_wptr>&
+type_maps::get_types_sorted_by_name() const
+{
+  if (priv_->sorted_types_.empty())
+    {
+      istring_type_base_wptrs_map_type::const_iterator i;
+      vector<type_base_wptr>::const_iterator j;
+
+      for (i = basic_types().begin(); i != basic_types().end(); ++i)
+       for (j = i->second.begin(); j != i->second.end(); ++j)
+         priv_->sorted_types_.push_back(*j);
+
+      for (i = class_types().begin(); i != class_types().end(); ++i)
+       for (j = i->second.begin(); j != i->second.end(); ++j)
+         priv_->sorted_types_.push_back(*j);
+
+      for (i = union_types().begin(); i != union_types().end(); ++i)
+       for (j = i->second.begin(); j != i->second.end(); ++j)
+         priv_->sorted_types_.push_back(*j);
+
+      for (i = enum_types().begin(); i != enum_types().end(); ++i)
+       for (j = i->second.begin(); j != i->second.end(); ++j)
+         priv_->sorted_types_.push_back(*j);
+
+      for (i = typedef_types().begin(); i != typedef_types().end(); ++i)
+       for (j = i->second.begin(); j != i->second.end(); ++j)
+         priv_->sorted_types_.push_back(*j);
+
+      type_name_comp comp;
+      sort(priv_->sorted_types_.begin(), priv_->sorted_types_.end(), comp);
+    }
+
+  return priv_->sorted_types_;
+}
+
 // </type_maps stuff>
 
 // <translation_unit stuff>
@@ -3191,6 +3285,7 @@ struct decl_base::priv
 {
   bool                 in_pub_sym_tab_;
   bool                 is_anonymous_;
+  bool                 is_artificial_;
   bool                 has_anonymous_parent_;
   location             location_;
   context_rel          *context_;
@@ -3218,6 +3313,7 @@ struct decl_base::priv
   priv()
     : in_pub_sym_tab_(false),
       is_anonymous_(true),
+      is_artificial_(false),
       has_anonymous_parent_(false),
       context_(),
       visibility_(VISIBILITY_DEFAULT)
@@ -3491,6 +3587,29 @@ void
 decl_base::set_is_anonymous(bool f)
 {priv_->is_anonymous_ = f;}
 
+/// Getter of the flag that says if the declaration is artificial.
+///
+/// Being artificial means the parameter was not explicitely
+/// mentionned in the source code, but was rather artificially created
+/// by the compiler.
+///
+/// @return true iff the declaration is artificial.
+bool
+decl_base::get_is_artificial() const
+{return priv_->is_artificial_;}
+
+/// Setter of the flag that says if the declaration is artificial.
+///
+/// Being artificial means the parameter was not explicitely
+/// mentionned in the source code, but was rather artificially created
+/// by the compiler.
+///
+/// @param f the new value of the flag that says if the declaration is
+/// artificial.
+void
+decl_base::set_is_artificial(bool f)
+{priv_->is_artificial_ = f;}
+
 /// Get the "has_anonymous_parent" flag of the current declaration.
 ///
 /// Having an anoymous parent means having a anonymous parent scope
@@ -3926,7 +4045,8 @@ operator<<(std::ostream& o, decl_base::binding b)
 }
 
 /// Turn equality of shared_ptr of decl_base into a deep equality;
-/// that is, make it compare the pointed to objects too.
+/// that is, make it compare the pointed to objects, not just the
+/// pointers.
 ///
 /// @param l the shared_ptr of decl_base on left-hand-side of the
 /// equality.
@@ -3947,6 +4067,20 @@ operator==(const decl_base_sptr& l, const decl_base_sptr& r)
   return *l == *r;
 }
 
+/// Inequality operator of shared_ptr of @ref decl_base.
+///
+/// This is a deep equality operator, that is, it compares the
+/// pointed-to objects, rather than just the pointers.
+///
+/// @param l the left-hand-side operand.
+///
+/// @param r the right-hand-side operand.
+///
+/// @return true iff @p l is different from @p r.
+bool
+operator!=(const decl_base_sptr& l, const decl_base_sptr& r)
+{return !operator==(l, r);}
+
 /// Turn equality of shared_ptr of type_base into a deep equality;
 /// that is, make it compare the pointed to objects too.
 ///
@@ -4061,6 +4195,43 @@ is_member_type(const type_base_sptr& t)
   return is_member_decl(d);
 }
 
+/// Test if a type is user-defined.
+///
+/// A type is considered user-defined if it's a
+/// struct/class/union/enum that is *NOT* artificial.
+///
+/// @param t the type to consider.
+///
+/// @return true iff the type @p t is user-defined.
+bool
+is_user_defined_type(const type_base* t)
+{
+  if (t == 0)
+    return false;
+
+  t = peel_qualified_or_typedef_type(t);
+  decl_base *d = is_decl(t);
+
+  if ((is_class_or_union_type(t) || is_enum_type(t))
+      && d && !d->get_is_artificial())
+    return true;
+
+  return false;
+}
+
+/// Test if a type is user-defined.
+///
+/// A type is considered user-defined if it's a
+/// struct/class/union/enum.
+///
+///
+/// @param t the type to consider.
+///
+/// @return true iff the type @p t is user-defined.
+bool
+is_user_defined_type(const type_base_sptr& t)
+{return is_user_defined_type(t.get());}
+
 /// Gets the access specifier for a class member.
 ///
 /// @param d the declaration of the class member to consider.  Note
@@ -4982,7 +5153,7 @@ strip_typedef(const type_base_sptr type)
                                          p->get_name(),
                                          p->get_location(),
                                          p->get_variadic_marker(),
-                                         p->get_artificial()));
+                                         p->get_is_artificial()));
          parm.push_back(stripped);
        }
       type_base_sptr p = strip_typedef(ty->get_return_type());
@@ -5009,7 +5180,7 @@ strip_typedef(const type_base_sptr type)
                                          p->get_name(),
                                          p->get_location(),
                                          p->get_variadic_marker(),
-                                         p->get_artificial()));
+                                         p->get_is_artificial()));
          parm.push_back(stripped);
        }
       type_base_sptr p = strip_typedef(ty->get_return_type());
@@ -11043,7 +11214,7 @@ synthesize_function_type_from_translation_unit(const function_type& fn_type,
                                              (*i)->get_name(),
                                              (*i)->get_location(),
                                              (*i)->get_variadic_marker(),
-                                             (*i)->get_artificial()));
+                                             (*i)->get_is_artificial()));
       parms.push_back(parm);
     }
 
@@ -15859,7 +16030,7 @@ function_type::function_type(type_base_sptr     return_type,
        i < priv_->parms_.size();
        ++i, ++j)
     {
-      if (i == 0 && priv_->parms_[i]->get_artificial())
+      if (i == 0 && priv_->parms_[i]->get_is_artificial())
        // If the first parameter is artificial, then it certainly
        // means that this is a member function, and the first
        // parameter is the implicit this pointer.  In that case, set
@@ -15980,7 +16151,7 @@ function_type::set_parameters(const parameters &p)
        i < priv_->parms_.size();
        ++i, ++j)
     {
-      if (i == 0 && priv_->parms_[i]->get_artificial())
+      if (i == 0 && priv_->parms_[i]->get_is_artificial())
        // If the first parameter is artificial, then it certainly
        // means that this is a member function, and the first
        // parameter is the implicit this pointer.  In that case, set
@@ -17212,22 +17383,18 @@ struct function_decl::parameter::priv
   type_base_wptr       type_;
   unsigned             index_;
   bool                 variadic_marker_;
-  bool                 artificial_;
 
   priv()
     : index_(),
-      variadic_marker_(),
-      artificial_()
+      variadic_marker_()
   {}
 
   priv(type_base_sptr type,
        unsigned index,
-       bool variadic_marker,
-       bool artificial)
+       bool variadic_marker)
     : type_(type),
       index_(index),
-      variadic_marker_(variadic_marker),
-      artificial_(artificial)
+      variadic_marker_(variadic_marker)
   {}
 };// end struct function_decl::parameter::priv
 
@@ -17239,7 +17406,7 @@ function_decl::parameter::parameter(const type_base_sptr        type,
   : type_or_decl_base(type->get_environment(),
                      FUNCTION_PARAMETER_DECL | ABSTRACT_DECL_BASE),
     decl_base(type->get_environment(), name, loc),
-    priv_(new priv(type, index, is_variadic, /*is_artificial=*/false))
+    priv_(new priv(type, index, is_variadic))
 {
   runtime_type_instance(this);
 }
@@ -17253,9 +17420,10 @@ function_decl::parameter::parameter(const type_base_sptr       type,
   : type_or_decl_base(type->get_environment(),
                      FUNCTION_PARAMETER_DECL | ABSTRACT_DECL_BASE),
     decl_base(type->get_environment(), name, loc),
-    priv_(new priv(type, index, is_variadic, is_artificial))
+    priv_(new priv(type, index, is_variadic))
 {
   runtime_type_instance(this);
+  set_is_artificial(is_artificial);
 }
 
 function_decl::parameter::parameter(const type_base_sptr       type,
@@ -17266,9 +17434,10 @@ function_decl::parameter::parameter(const type_base_sptr       type,
   : type_or_decl_base(type->get_environment(),
                      FUNCTION_PARAMETER_DECL | ABSTRACT_DECL_BASE),
     decl_base(type->get_environment(), name, loc),
-    priv_(new priv(type, 0, is_variadic, is_artificial))
+    priv_(new priv(type, 0, is_variadic))
 {
   runtime_type_instance(this);
+  set_is_artificial(is_artificial);
 }
 
 function_decl::parameter::parameter(const type_base_sptr       type,
@@ -17277,7 +17446,7 @@ function_decl::parameter::parameter(const type_base_sptr        type,
   : type_or_decl_base(type->get_environment(),
                      FUNCTION_PARAMETER_DECL | ABSTRACT_DECL_BASE),
     decl_base(type->get_environment(), "", location()),
-    priv_(new priv(type, index, variad, /*is_artificial=*/false))
+    priv_(new priv(type, index, variad))
 {
   runtime_type_instance(this);
 }
@@ -17350,32 +17519,11 @@ void
 function_decl::parameter::set_index(unsigned i)
 {priv_->index_ = i;}
 
-/// Test if the parameter is artificial.
-///
-/// Being artificial means the parameter was not explicitely
-/// mentionned in the source code, but was rather artificially
-/// created by the compiler.
-///
-/// @return true if the parameter is artificial, false otherwise.
-bool
-function_decl::parameter::get_artificial() const
-{return priv_->artificial_;}
 
 bool
 function_decl::parameter::get_variadic_marker() const
 {return priv_->variadic_marker_;}
 
-/// Getter for the artificial-ness of the parameter.
-///
-/// Being artificial means the parameter was not explicitely
-/// mentionned in the source code, but was rather artificially
-/// created by the compiler.
-///
-/// @param f set to true if the parameter is artificial.
-void
-function_decl::parameter::set_artificial(bool f)
-{priv_->artificial_ = f;}
-
 /// Compares two instances of @ref function_decl::parameter.
 ///
 /// If the two intances are different, set a bitfield to give some
index 06fdee9ac63dde4cf43c676a063982ee33b91c3b..85ecf0674e2de02206147f1b99c1d88495129225 100644 (file)
@@ -1231,6 +1231,10 @@ leaf_reporter::report(const corpus_diff& d,
   // Now show the changed types.
   const diff_maps& leaf_diffs = d.get_leaf_diffs();
   report_type_changes_from_diff_maps(*this, leaf_diffs, out, indent);
+
+  // Report added/removed/changed types not reacheable from public
+  // interfaces.
+  maybe_report_unreachable_type_changes(d, s, indent, out);
 }
 } // end namespace comparison
 } // end namespace abigail
index ce1f3ea7467c935e6eb3c64fcfd89d1de6bfd208..61d5de96955b309c8f188dff8b5906cf3d22a17a 100644 (file)
@@ -76,6 +76,9 @@ using zip_utils::open_file_in_archive;
 #endif //WITH_ZIP_ARCHIVE
 
 static bool    read_is_declaration_only(xmlNodePtr, bool&);
+static bool    read_is_artificial(xmlNodePtr, bool&);
+static bool    read_tracking_non_reachable_types(xmlNodePtr, bool&);
+static bool    read_is_non_reachable_type(xmlNodePtr, bool&);
 
 class read_context;
 
@@ -125,6 +128,7 @@ private:
   corpus_group_sptr                                    m_corpus_group;
   corpus::exported_decls_builder*                      m_exported_decls_builder;
   suppr::suppressions_type                             m_supprs;
+  bool                                                 m_tracking_non_reachable_types;
 
   read_context();
 
@@ -134,9 +138,28 @@ public:
     : m_env(env),
       m_reader(reader),
       m_corp_node(),
-      m_exported_decls_builder()
+      m_exported_decls_builder(),
+      m_tracking_non_reachable_types()
   {}
 
+  /// Getter for the flag that tells us if we are tracking types that
+  /// are not reachable from global functions and variables.
+  ///
+  /// @return true iff we are tracking types that are not reachable
+  /// from global functions and variables.
+  bool
+  tracking_non_reachable_types() const
+  {return m_tracking_non_reachable_types;}
+
+  /// Setter for the flag that tells us if we are tracking types that
+  /// are not reachable from global functions and variables.
+  ///
+  /// @param f the new value of the flag.
+  /// from global functions and variables.
+  void
+  tracking_non_reachable_types(bool f)
+  {m_tracking_non_reachable_types = f;}
+
   /// Getter of the path to the ABI file.
   ///
   /// @return the path to the native xml abi file.
@@ -1768,6 +1791,19 @@ add_read_context_suppressions(read_context& ctxt,
       ctxt.get_suppressions().push_back(*i);
 }
 
+/// Configure the @ref read_context so that types not reachable from
+/// public interface are taken into account when the abixml file is
+/// read.
+///
+/// @param ctxt the @read_context to consider.
+///
+/// @param flag if yes, then types not reachable from public interface
+/// are taken into account when the abixml file is read.
+void
+consider_types_not_reachable_from_public_interfaces(read_context& ctxt,
+                                                   bool flag)
+{ctxt.tracking_non_reachable_types(flag);}
+
 /// Parse the input XML document containing an ABI corpus, represented
 /// by an 'abi-corpus' element node, associated to the current
 /// context.
@@ -1907,6 +1943,17 @@ read_corpus_from_input(read_context& ctxt)
     }
   while (is_ok);
 
+  if (ctxt.tracking_non_reachable_types())
+    {
+      bool is_tracking_non_reachable_types = false;
+      read_tracking_non_reachable_types(node, is_tracking_non_reachable_types);
+
+      ABG_ASSERT
+       (corp.recording_types_reachable_from_public_interface_supported()
+        == is_tracking_non_reachable_types);
+    }
+
+
   ctxt.perform_late_type_canonicalizing();
 
   ctxt.get_environment()->canonicalization_is_done(true);
@@ -2116,6 +2163,23 @@ handle_element_node(read_context& ctxt, xmlNodePtr node,
                                    add_to_current_scope))
    || (decl = handle_class_tdecl(ctxt, node,
                                 add_to_current_scope)));
+
+  // If the user wants us to track non-reachable types, then read the
+  // 'is-non-reachable-type' attribute on type elements and record
+  // reachable types accordingly.
+  if (ctxt.tracking_non_reachable_types())
+    {
+      if (type_base_sptr t = is_type(decl))
+       {
+         corpus_sptr abi = ctxt.get_corpus();
+         ABG_ASSERT(abi);
+         bool is_non_reachable_type = false;
+         read_is_non_reachable_type(node, is_non_reachable_type);
+         if (!is_non_reachable_type)
+           abi->record_type_as_reachable_from_public_interfaces(*t);
+       }
+    }
+
     return decl;
 }
 
@@ -2384,22 +2448,98 @@ read_cdtor_const(xmlNodePtr     node,
 /// @param node the xml node to consider.
 ///
 /// @param is_decl_only is set to true iff the "is-declaration-only" attribute
-/// is present and set to "yes"
+/// is present and set to "yes".
 ///
 /// @return true iff the is_decl_only attribute was set.
 static bool
 read_is_declaration_only(xmlNodePtr node, bool& is_decl_only)
 {
-    if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "is-declaration-only"))
-      {
-       string str = CHAR_STR(s);
-       if (str == "yes")
-         is_decl_only = true;
-       else
-         is_decl_only = false;
-       return true;
-      }
-    return false;
+  if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "is-declaration-only"))
+    {
+      string str = CHAR_STR(s);
+      if (str == "yes")
+       is_decl_only = true;
+      else
+       is_decl_only = false;
+      return true;
+    }
+  return false;
+}
+
+/// Read the "is-artificial" attribute of the current XML node.
+///
+/// @param node the XML node to consider.
+///
+/// @param is_artificial this output parameter is set to true iff the
+/// "is-artificial" parameter is present and set to 'yes'.
+///
+/// @return true iff the "is-artificial" parameter was present on the
+/// XML node.
+static bool
+read_is_artificial(xmlNodePtr node, bool& is_artificial)
+{
+  if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "is-artificial"))
+    {
+      string is_artificial_str = CHAR_STR(s) ? CHAR_STR(s) : "";
+      is_artificial = (is_artificial_str == "yes") ? true : false;
+      return true;
+    }
+  return false;
+}
+
+/// Read the 'tracking-non-reachable-types' attribute on the current
+/// XML element.
+///
+/// @param node the current XML element.
+///
+/// @param tracking_non_reachable_types output parameter.  This is set
+/// to true iff the 'tracking-non-reachable-types' attribute is
+/// present on the current XML node and set to 'yes'.  In that case,
+/// the function returns true.
+///
+/// @return true iff the 'tracking-non-reachable-types' attribute is
+/// present on the current XML node and set to 'yes'.
+static bool
+read_tracking_non_reachable_types(xmlNodePtr node,
+                                 bool& tracking_non_reachable_types)
+{
+  if (xml_char_sptr s =
+      XML_NODE_GET_ATTRIBUTE(node, "tracking-non-reachable-types"))
+    {
+      string tracking_non_reachable_types_str = CHAR_STR(s) ? CHAR_STR(s) : "";
+      tracking_non_reachable_types =
+       (tracking_non_reachable_types_str == "yes")
+       ? true
+       : false;
+      return true;
+    }
+  return false;
+}
+
+/// Read the 'is-non-reachable' attribute on the current XML element.
+///
+/// @param node the current XML element.
+///
+/// @param is_non_reachable_type output parameter. This is set to true
+/// iff the 'is-non-reachable' attribute is present on the current XML
+/// element with a value se to 'yes'.
+///
+/// @return true iff the 'is-non-reachable' attribute is present on
+/// the current XML element with a value se to 'yes'.
+static bool
+read_is_non_reachable_type(xmlNodePtr node, bool& is_non_reachable_type)
+{
+  if (xml_char_sptr s =
+      XML_NODE_GET_ATTRIBUTE(node, "is-non-reachable"))
+    {
+      string is_non_reachable_type_str = CHAR_STR(s) ? CHAR_STR(s) : "";
+      is_non_reachable_type =
+       (is_non_reachable_type_str == "yes")
+       ? true
+       : false;
+      return true;
+    }
+  return false;
 }
 
 /// Read the "is-virtual" attribute of the current xml node.
@@ -2849,13 +2989,7 @@ build_function_parameter(read_context& ctxt, const xmlNodePtr node)
     }
 
   bool is_artificial = false;
-  string is_artificial_str;
-  if (xml_char_sptr s =
-      xml::build_sptr(xmlGetProp(node, BAD_CAST("is-artificial"))))
-    {
-      is_artificial_str = CHAR_STR(s) ? CHAR_STR(s) : "";
-      is_artificial = (is_artificial_str == "yes") ? true : false;
-    }
+  read_is_artificial(node, is_artificial);
 
   string type_id;
   if (xml_char_sptr a = xml::build_sptr(xmlGetProp(node, BAD_CAST("type-id"))))
@@ -3963,6 +4097,9 @@ build_enum_type_decl(read_context&        ctxt,
   bool is_anonymous = false;
   read_is_anonymous(node, is_anonymous);
 
+  bool is_artificial = false;
+  read_is_artificial(node, is_artificial);
+
   string id;
   if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "id"))
     id = CHAR_STR(s);
@@ -4016,6 +4153,7 @@ build_enum_type_decl(read_context&        ctxt,
                                           underlying_type,
                                           enums, linkage_name));
   t->set_is_anonymous(is_anonymous);
+  t->set_is_artificial(is_artificial);
   if (ctxt.push_and_key_type_decl(t, id, add_to_current_scope))
     {
       ctxt.map_xml_node_to_decl(node, t);
@@ -4182,6 +4320,9 @@ build_class_decl(read_context&            ctxt,
   decl_base::visibility vis = decl_base::VISIBILITY_NONE;
   read_visibility(node, vis);
 
+  bool is_artificial = false;
+  read_is_artificial(node, is_artificial);
+
   string id;
   if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "id"))
     id = CHAR_STR(s);
@@ -4271,6 +4412,8 @@ build_class_decl(read_context&            ctxt,
       decl->set_is_anonymous(is_anonymous);
     }
 
+  decl->set_is_artificial(is_artificial);
+
   string def_id;
   bool is_def_of_decl = false;
   if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "def-of-decl-id"))
@@ -4595,6 +4738,9 @@ build_union_decl(read_context& ctxt,
   decl_base::visibility vis = decl_base::VISIBILITY_NONE;
   read_visibility(node, vis);
 
+  bool is_artificial = false;
+  read_is_artificial(node, is_artificial);
+
   string id;
   if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "id"))
     id = CHAR_STR(s);
@@ -4673,6 +4819,8 @@ build_union_decl(read_context& ctxt,
       decl->set_is_anonymous(is_anonymous);
     }
 
+  decl->set_is_artificial(is_artificial);
+
   string def_id;
   bool is_def_of_decl = false;
   if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "def-of-decl-id"))
@@ -5325,6 +5473,16 @@ build_type(read_context& ctxt,
    || (t = build_union_decl_if_not_suppressed(ctxt, node,
                                              add_to_current_scope)));
 
+  if (ctxt.tracking_non_reachable_types() && t)
+    {
+      corpus_sptr abi = ctxt.get_corpus();
+      ABG_ASSERT(abi);
+      bool is_non_reachable_type = false;
+      read_is_non_reachable_type(node, is_non_reachable_type);
+      if (!is_non_reachable_type)
+       abi->record_type_as_reachable_from_public_interfaces(*t);
+    }
+
   return t;
 }
 
index 6bdf177ea4cd7289df8a6c8fce1301981a5b2074..f9e08c9115bfc058a9b2fe1ecfa6f4eee40e7a10 100644 (file)
@@ -1219,8 +1219,148 @@ show_linkage_name_and_aliases(ostream& out,
     out << ", aliases " << aliases;
 }
 
+/// Report changes about types that are not reachable from global
+/// functions and variables, in a given @param corpus_diff.
+///
+/// @param d the corpus_diff to consider.
+///
+/// @param s the statistics of the changes, after filters and
+/// suppressions are reported.  This is typically what is returned by
+/// corpus_diff::apply_filters_and_suppressions_before_reporting().
+///
+/// @param indent the indendation string (usually a string of white
+/// spaces) to use for indentation during the reporting.
+///
+/// @param out the output stream to emit the report to.
+void
+maybe_report_unreachable_type_changes(const corpus_diff& d,
+                                     const corpus_diff::diff_stats &s,
+                                     const string& indent,
+                                     ostream& out)
+{
+  const unsigned large_num = 100;
+  const diff_context_sptr& ctxt = d.context();
+
+  if (!(ctxt->show_unreachable_types()
+       && (!d.priv_->deleted_unreachable_types_.empty()
+           || !d.priv_->added_unreachable_types_.empty()
+           || !d.priv_->changed_unreachable_types_.empty())))
+    // The user either doesn't want us to show changes about
+    // unreachable types or there are not such changes.
+    return;
+
+  // Handle removed unreachable types.
+  if (s.net_num_removed_unreachable_types() == 1)
+    out << indent
+       << "1 removed type unreachable from any public interface:\n\n";
+  else if (s.net_num_removed_unreachable_types() > 1)
+    out << indent
+       << s.net_num_removed_unreachable_types()
+       << " removed types unreachable from any public interface:\n\n";
+
+  vector<type_base_sptr> sorted_removed_unreachable_types;
+  sort_string_type_base_sptr_map(d.priv_->deleted_unreachable_types_,
+                                sorted_removed_unreachable_types);
+  bool emitted = false;
+  for (vector<type_base_sptr>::const_iterator i =
+        sorted_removed_unreachable_types.begin();
+       i != sorted_removed_unreachable_types.end();
+       ++i)
+    {
+      if (d.priv_->deleted_unreachable_type_is_suppressed((*i).get()))
+       continue;
+
+      out << indent << "  ";
+      if (s.num_removed_unreachable_types() > large_num)
+       out << "[D] ";
+      out << get_pretty_representation(*i);
+      report_loc_info(*i, *ctxt, out);
+      out << "\n";
+      emitted = true;
+    }
+  if (emitted)
+    {
+      out << "\n";
+      emitted = false;
+    }
+
+  // Handle changed unreachable types!
+  if (s.net_num_changed_unreachable_types() == 1)
+    out << indent
+       << "1 changed type unreachable from any public interface:\n\n";
+  else if (s.net_num_changed_unreachable_types() > 1)
+    out << indent
+       << s.net_num_changed_unreachable_types()
+       << " changed types unreachable from any public interface:\n\n";
+
+  diff_sptrs_type sorted_diff_sptrs;
+  sort_string_diff_sptr_map(d.priv_->changed_unreachable_types_,
+                           sorted_diff_sptrs);
+  emitted =  true;
+  for (diff_sptrs_type::const_iterator i = sorted_diff_sptrs.begin();
+       i != sorted_diff_sptrs.end();
+       ++i)
+    {
+      diff_sptr diff = *i;
+      if (!diff || !diff->to_be_reported())
+       continue;
+
+      string repr = diff->first_subject()->get_pretty_representation();
+
+      out << "  ";
+
+      if (sorted_diff_sptrs.size() > large_num)
+       out << "[C] ";
+
+      out << "'" << repr << "' changed:\n";
+      diff->report(out, indent + "    ");
+      out << "\n";
+      emitted = true;
+    }
+  if (emitted)
+    {
+      out << "\n";
+      emitted = false;
+    }
+
+  // Handle added unreachable types.
+  if (s.net_num_added_unreachable_types() == 1)
+    out << indent
+       << "1 added type unreachable from any public interface:\n\n";
+  else if (s.net_num_added_unreachable_types() > 1)
+    out << indent
+       << s.net_num_added_unreachable_types()
+       << " added types unreachable from any public interface:\n\n";
+
+  vector<type_base_sptr> sorted_added_unreachable_types;
+  sort_string_type_base_sptr_map(d.priv_->added_unreachable_types_,
+                                sorted_added_unreachable_types);
+  emitted = false;
+  for (vector<type_base_sptr>::const_iterator i =
+        sorted_added_unreachable_types.begin();
+       i != sorted_added_unreachable_types.end();
+       ++i)
+    {
+      if (d.priv_->added_unreachable_type_is_suppressed((*i).get()))
+       continue;
+
+      out << indent << "  ";
+      if (s.num_added_unreachable_types() > large_num)
+       out << "[A] ";
+      out << "'" << get_pretty_representation(*i) << "'";
+      report_loc_info(*i, *ctxt, out);
+      out << "\n";
+      emitted = true;
+    }
+  if (emitted)
+    {
+      out << "\n";
+      emitted = false;
+    }
+}
+
 /// If a given diff node impacts some public interfaces, then report
-/// about those impacted interfaces on standard output.
+/// about those impacted interfaces on a given output stream.
 ///
 /// @param d the diff node to get the impacted interfaces for.
 ///
index d1b5200841f8f854aca946c1f3b77487155a455a..530b748800e3b70425bf7d971cd8176457a2c55a 100644 (file)
@@ -235,6 +235,12 @@ show_linkage_name_and_aliases(ostream& out,
                              const elf_symbol& symbol,
                              const string_elf_symbols_map_type& sym_map);
 
+void
+maybe_report_unreachable_type_changes(const corpus_diff& d,
+                                     const corpus_diff::diff_stats &s,
+                                     const string& indent,
+                                     ostream& out);
+
 void
 maybe_report_interfaces_impacted_by_diff(const diff            *d,
                                         ostream                &out,
index bc2246babd1f4d0081eca64210bad605348c6497..78844cf4135ac5c0cfa44cf0d05f5135e6ad22ec 100644 (file)
@@ -772,6 +772,9 @@ static void write_location(const location&, write_context&);
 static void write_location(const decl_base_sptr&, write_context&);
 static bool write_visibility(const decl_base_sptr&, ostream&);
 static bool write_binding(const decl_base_sptr&, ostream&);
+static bool write_is_artificial(const decl_base_sptr&, ostream&);
+static bool write_is_non_reachable(const type_base_sptr&, ostream&);
+static bool write_tracking_non_reachable_types(const corpus_sptr&, ostream&);
 static void write_array_size_and_alignment(const array_type_def_sptr,
                                           ostream&);
 static void write_size_and_alignment(const type_base_sptr, ostream&);
@@ -1127,7 +1130,7 @@ annotate(const function_decl::parameter_sptr&     parm,
     o << "variadic parameter";
   else
     {
-      if (parm->get_artificial())
+      if (parm->get_is_artificial())
        {
          if (parm->get_index() == 0)
            o << "implicit ";
@@ -1287,6 +1290,75 @@ write_binding(const shared_ptr<decl_base>& decl, ostream& o)
   return true;
 }
 
+/// Write the "is-artificial" attribute of the @ref decl.
+///
+/// @param decl the declaration to consider.
+///
+/// @param o the output stream to emit the "is-artificial" attribute
+/// to.
+///
+/// @return true iff the "is-artificial" attribute was emitted.
+static bool
+write_is_artificial(const decl_base_sptr& decl, ostream& o)
+{
+  if (!decl)
+    return false;
+
+  if (decl->get_is_artificial())
+    o << " is-artificial='yes'";
+
+  return true;
+}
+
+/// Write the 'is-non-reachable' attribute if a given type we are
+/// looking at is not reachable from global functions and variables
+/// and if the user asked us to track that information.
+///
+/// @param t the type to consider.
+///
+/// @param o the output stream to write the 'is-non-reachable'
+/// attribute to.
+static bool
+write_is_non_reachable(const type_base_sptr& t, ostream& o)
+{
+  if (!t)
+    return false;
+
+  corpus* c = t->get_corpus();
+  if (!c)
+    return false;
+
+  if (!c->recording_types_reachable_from_public_interface_supported()
+      || c->type_is_reachable_from_public_interfaces(*t))
+    return false;
+
+  o << " is-non-reachable='yes'";
+
+  return true;
+}
+
+/// Write the 'tracking-non-reachable-types' attribute if for a given
+/// corpus, the user wants us to track non-reachable types.
+///
+/// @param corpus the ABI corpus to consider.
+///
+/// @param o the output parameter to write the
+/// 'tracking-non-reachable-types' attribute to.
+static bool
+write_tracking_non_reachable_types(const corpus_sptr& corpus,
+                                  ostream& o)
+{
+  corpus_group* group = corpus->get_group();
+  if (!group)
+    if (corpus->recording_types_reachable_from_public_interface_supported())
+      {
+       o << " tracking-non-reachable-types='yes'";
+       return true;
+      }
+
+  return false;
+}
+
 /// Serialize the size and alignment attributes of a given type.
 ///
 /// @param decl the type to consider.
@@ -2704,6 +2776,8 @@ write_enum_type_decl(const enum_type_decl_sptr& decl,
   o << "<enum-decl name='" << xml::escape_xml_string(decl->get_name()) << "'";
 
   write_is_anonymous(decl, o);
+  write_is_artificial(decl, o);
+  write_is_non_reachable(is_type(decl), o);
 
   if (!decl->get_linkage_name().empty())
     o << " linkage-name='" << decl->get_linkage_name() << "'";
@@ -3075,8 +3149,7 @@ write_function_decl(const function_decl_sptr& decl, write_context& ctxt,
          if (!(*pi)->get_name().empty())
            o << " name='" << (*pi)->get_name() << "'";
        }
-      if ((*pi)->get_artificial())
-         o << " is-artificial='yes'";
+      write_is_artificial(*pi, o);
       write_location((*pi)->get_location(), ctxt);
       o << "/>\n";
     }
@@ -3171,8 +3244,7 @@ write_function_type(const function_type_sptr& fn_type,
              o << " name='" << name << "'";
            }
        }
-      if ((*pi)->get_artificial())
-       o << " is-artificial='yes'";
+      write_is_artificial(*pi, o);
       o << "/>\n";
     }
 
@@ -3230,6 +3302,10 @@ write_class_decl_opening_tag(const class_decl_sptr&      decl,
 
   write_is_anonymous(decl, o);
 
+  write_is_artificial(decl, o);
+
+  write_is_non_reachable(is_type(decl), o);
+
   write_naming_typedef(decl, ctxt);
 
   write_visibility(decl, o);
@@ -3304,6 +3380,10 @@ write_union_decl_opening_tag(const union_decl_sptr&      decl,
 
   write_visibility(decl, o);
 
+  write_is_artificial(decl, o);
+
+  write_is_non_reachable(is_type(decl), o);
+
   write_location(decl, ctxt);
 
   write_class_or_union_is_declaration_only(decl, o);
@@ -4329,6 +4409,8 @@ write_corpus(write_context&       ctxt,
   if (!corpus->get_soname().empty())
     out << " soname='" << corpus->get_soname()<< "'";
 
+  write_tracking_non_reachable_types(corpus, out);
+
   if (corpus->is_empty())
     {
       out << "/>\n";
@@ -4424,6 +4506,8 @@ std::ostream& out = ctxt.get_ostream();
   if (!group->get_architecture_name().empty() && ctxt.get_write_architecture())
     out << " architecture='" << group->get_architecture_name()<< "'";
 
+  write_tracking_non_reachable_types(group, out);
+
   if (group->is_empty())
     {
       out << "/>\n";
index 30e17a0f36e57b692ff10cfcd40f2bd79f3e1b0a..16026545fff4e0482ea5d0da62a13a9f91eb8e61 100644 (file)
@@ -1240,6 +1240,27 @@ test-diff-suppr/test45-abi.xml \
 test-diff-suppr/test46-PR25128-base.xml \
 test-diff-suppr/test46-PR25128-new.xml \
 test-diff-suppr/test46-PR25128-report-1.txt \
+test-diff-suppr/test47-non-reachable-types-report-1.txt \
+test-diff-suppr/test47-non-reachable-types-report-2.txt \
+test-diff-suppr/test47-non-reachable-types-report-3.txt \
+test-diff-suppr/test47-non-reachable-types-report-4.txt \
+test-diff-suppr/test47-non-reachable-types-report-5.txt \
+test-diff-suppr/test47-non-reachable-types-report-6.txt \
+test-diff-suppr/test47-non-reachable-types-report-7.txt \
+test-diff-suppr/test47-non-reachable-types-report-8.txt \
+test-diff-suppr/test47-non-reachable-types-report-9.txt \
+test-diff-suppr/test47-non-reachable-types-suppr-1.txt \
+test-diff-suppr/test47-non-reachable-types-suppr-2.txt \
+test-diff-suppr/test47-non-reachable-types-suppr-3.txt \
+test-diff-suppr/test47-non-reachable-types-suppr-4.txt \
+test-diff-suppr/test47-non-reachable-types-suppr-5.txt \
+test-diff-suppr/test47-non-reachable-types-v0.c \
+test-diff-suppr/test47-non-reachable-types-v0.o \
+test-diff-suppr/test47-non-reachable-types-v1.c \
+test-diff-suppr/test47-non-reachable-types-v1.o \
+test-diff-suppr/test47-non-reachable-types-v0.o.alltypes.abixml \
+test-diff-suppr/test47-non-reachable-types-v1.o.alltypes.abixml \
+test-diff-suppr/test47-non-reachable-types-report-10.txt \
 \
 test-diff-dwarf-abixml/test0-pr19026-libvtkIOSQL-6.1.so.1 \
 test-diff-dwarf-abixml/test0-pr19026-libvtkIOSQL-6.1.so.1.abi \
@@ -1529,6 +1550,15 @@ test-diff-pkg/PR24410-old/poppler-qt5-0.73.0-4.fc30.x86_64.rpm \
 test-diff-pkg/PR24410-old/poppler-qt5-debuginfo-0.73.0-4.fc30.x86_64.rpm \
 test-diff-pkg/PR24410-old/poppler-qt5-devel-0.73.0-4.fc30.x86_64.rpm \
 test-diff-pkg/PR24410-report-0.txt \
+test-diff-pkg/PR24690/PR24690-report-0.txt \
+test-diff-pkg/PR24690/flatpak-debuginfo-1.2.4-3.fc30.x86_64.rpm \
+test-diff-pkg/PR24690/flatpak-debuginfo-1.4.0-1.fc30.x86_64.rpm \
+test-diff-pkg/PR24690/flatpak-devel-1.2.4-3.fc30.x86_64.rpm \
+test-diff-pkg/PR24690/flatpak-devel-1.4.0-1.fc30.x86_64.rpm \
+test-diff-pkg/PR24690/flatpak-libs-1.2.4-3.fc30.x86_64.rpm \
+test-diff-pkg/PR24690/flatpak-libs-1.4.0-1.fc30.x86_64.rpm \
+test-diff-pkg/PR24690/flatpak-libs-debuginfo-1.2.4-3.fc30.x86_64.rpm \
+test-diff-pkg/PR24690/flatpak-libs-debuginfo-1.4.0-1.fc30.x86_64.rpm \
 \
 test-fedabipkgdiff/dbus-glib-0.104-3.fc23.x86_64.rpm \
 test-fedabipkgdiff/dbus-glib-debuginfo-0.104-3.fc23.x86_64.rpm \
index 49f33c165f3e5a2c45af0a1d21f8380c7455913e..7f372cd7869d1cc02260032b5a9137d3e522c0cc 100644 (file)
@@ -1,27 +1,26 @@
 5 changed types:
-  'const s0' changed:
-    in unqualified underlying type 'class s0':
-      type size changed from 192 to 256 (in bits)
-      1 member function insertion:
-        'method virtual int s0::foo(int, char) const', virtual at voffset 2/2
+  'class s0' changed:
+    type size changed from 192 to 256 (in bits)
+    1 member function insertion:
+      'method virtual int s0::foo(int, char) const', virtual at voffset 2/2
 
-      1 data member deletion:
-        'char s0::m1', at offset 96 (in bits)
+    1 data member deletion:
+      'char s0::m1', at offset 96 (in bits)
 
-      1 data member insertion:
-        'double s0::m01', at offset 128 (in bits)
-      2 data member changes:
-       type of 'int s0::m0' changed:
-         type name changed from 'int' to 'char'
-         type size changed from 32 to 8 (in bits)
-         type alignement changed from 32 to 8
+    1 data member insertion:
+      'double s0::m01', at offset 128 (in bits)
+    2 data member changes:
+     type of 'int s0::m0' changed:
+       type name changed from 'int' to 'char'
+       type size changed from 32 to 8 (in bits)
+       type alignement changed from 32 to 8
 
-       'unsigned int s0::m2' offset changed from 128 to 192 (in bits) (by +64 bits)
+     'unsigned int s0::m2' offset changed from 128 to 192 (in bits) (by +64 bits)
+  'const s0' changed:
+    unqualified underlying type 'class s0' changed, as reported earlier
   'const s0*' changed:
     in pointed to type 'const s0':
       unqualified underlying type 'class s0' changed, as reported earlier
-  'class s0' changed:
-    details were reported earlier
   's0&' changed:
     referenced type 'class s0' changed, as reported earlier
   's0*' changed:
diff --git a/tests/data/test-diff-pkg/PR24690/PR24690-report-0.txt b/tests/data/test-diff-pkg/PR24690/PR24690-report-0.txt
new file mode 100644 (file)
index 0000000..d106bf9
--- /dev/null
@@ -0,0 +1,51 @@
+================ changes of 'libflatpak.so.0.10204.0'===============
+  Functions changes summary: 0 Removed, 0 Changed (16 filtered out), 16 Added functions
+  Variables changes summary: 0 Removed, 0 Changed, 0 Added variable
+  Unreachable types summary: 3 removed (2 filtered out), 1 changed (15 filtered out), 3 added (1 filtered out) types
+
+  16 Added functions:
+
+    'function gboolean flatpak_installation_add_remote(FlatpakInstallation*, FlatpakRemote*, gboolean, GCancellable*, GError**)'    {flatpak_installation_add_remote}
+    'function FlatpakRemoteRef* flatpak_installation_fetch_remote_ref_sync_full(FlatpakInstallation*, const char*, FlatpakRefKind, const char*, const char*, const char*, FlatpakQueryFlags, GCancellable*, GError**)'    {flatpak_installation_fetch_remote_ref_sync_full}
+    'function GPtrArray* flatpak_installation_list_remote_refs_sync_full(FlatpakInstallation*, const char*, FlatpakQueryFlags, GCancellable*, GError**)'    {flatpak_installation_list_remote_refs_sync_full}
+    'function GType flatpak_query_flags_get_type()'    {flatpak_query_flags_get_type}
+    'function char* flatpak_remote_get_comment(FlatpakRemote*)'    {flatpak_remote_get_comment}
+    'function char* flatpak_remote_get_description(FlatpakRemote*)'    {flatpak_remote_get_description}
+    'function char* flatpak_remote_get_filter(FlatpakRemote*)'    {flatpak_remote_get_filter}
+    'function char* flatpak_remote_get_homepage(FlatpakRemote*)'    {flatpak_remote_get_homepage}
+    'function char* flatpak_remote_get_icon(FlatpakRemote*)'    {flatpak_remote_get_icon}
+    'function FlatpakRemote* flatpak_remote_new_from_file(const char*, GBytes*, GError**)'    {flatpak_remote_new_from_file}
+    'function void flatpak_remote_set_comment(FlatpakRemote*, const char*)'    {flatpak_remote_set_comment}
+    'function void flatpak_remote_set_description(FlatpakRemote*, const char*)'    {flatpak_remote_set_description}
+    'function void flatpak_remote_set_filter(FlatpakRemote*, const char*)'    {flatpak_remote_set_filter}
+    'function void flatpak_remote_set_homepage(FlatpakRemote*, const char*)'    {flatpak_remote_set_homepage}
+    'function void flatpak_remote_set_icon(FlatpakRemote*, const char*)'    {flatpak_remote_set_icon}
+    'function gboolean flatpak_transaction_add_rebase(FlatpakTransaction*, const char*, const char*, const char**, const char**, GError**)'    {flatpak_transaction_add_rebase}
+
+  3 removed types unreachable from any public interface:
+
+    struct _GUnixFDList at gunixfdlist.h:58:1
+    typedef GUnixFDList at giotypes.h:549:1
+    typedef GUnixFDListPrivate at gunixfdlist.h:41:1
+
+  1 changed type unreachable from any public interface:
+
+  'struct _FlatpakTransactionClass' changed:
+      type size changed from 2176 to 2240 (in bits)
+      1 data member insertion:
+        'typedef gboolean (FlatpakTransaction*, const char*, const char*, const char*, const char*, const char**)* _FlatpakTransactionClass::end_of_lifed_with_rebase', at offset 1408 (in bits) at flatpak-transaction.h:117:1
+      4 data member changes (3 filtered):
+       'typedef gboolean (FlatpakTransaction*)* _FlatpakTransactionClass::ready' offset changed from 1408 to 1472 (in bits) (by +64 bits)
+       'typedef gboolean (FlatpakTransaction*, typedef FlatpakTransactionRemoteReason, const char*, const char*, const char*)* _FlatpakTransactionClass::add_new_remote' offset changed from 1472 to 1536 (in bits) (by +64 bits)
+       'typedef gboolean (FlatpakTransaction*, GCancellable*, GError**)* _FlatpakTransactionClass::run' offset changed from 1536 to 1600 (in bits) (by +64 bits)
+       'gpointer _FlatpakTransactionClass::padding[9]' offset changed from 1600 to 1664 (in bits) (by +64 bits)
+
+
+  3 added types unreachable from any public interface:
+
+    'struct gpgme_data_cbs' at gpgme-64.h:1139:1
+    'typedef GSubprocess' at giotypes.h:642:1
+    'typedef OstreeRepoResolveRevExtFlags' at ostree-repo.h:473:1
+
+================ end of changes of 'libflatpak.so.0.10204.0'===============
+
diff --git a/tests/data/test-diff-pkg/PR24690/flatpak-debuginfo-1.2.4-3.fc30.x86_64.rpm b/tests/data/test-diff-pkg/PR24690/flatpak-debuginfo-1.2.4-3.fc30.x86_64.rpm
new file mode 100644 (file)
index 0000000..bb85cd9
Binary files /dev/null and b/tests/data/test-diff-pkg/PR24690/flatpak-debuginfo-1.2.4-3.fc30.x86_64.rpm differ
diff --git a/tests/data/test-diff-pkg/PR24690/flatpak-debuginfo-1.4.0-1.fc30.x86_64.rpm b/tests/data/test-diff-pkg/PR24690/flatpak-debuginfo-1.4.0-1.fc30.x86_64.rpm
new file mode 100644 (file)
index 0000000..6ece8ac
Binary files /dev/null and b/tests/data/test-diff-pkg/PR24690/flatpak-debuginfo-1.4.0-1.fc30.x86_64.rpm differ
diff --git a/tests/data/test-diff-pkg/PR24690/flatpak-devel-1.2.4-3.fc30.x86_64.rpm b/tests/data/test-diff-pkg/PR24690/flatpak-devel-1.2.4-3.fc30.x86_64.rpm
new file mode 100644 (file)
index 0000000..f34b434
Binary files /dev/null and b/tests/data/test-diff-pkg/PR24690/flatpak-devel-1.2.4-3.fc30.x86_64.rpm differ
diff --git a/tests/data/test-diff-pkg/PR24690/flatpak-devel-1.4.0-1.fc30.x86_64.rpm b/tests/data/test-diff-pkg/PR24690/flatpak-devel-1.4.0-1.fc30.x86_64.rpm
new file mode 100644 (file)
index 0000000..75c8d41
Binary files /dev/null and b/tests/data/test-diff-pkg/PR24690/flatpak-devel-1.4.0-1.fc30.x86_64.rpm differ
diff --git a/tests/data/test-diff-pkg/PR24690/flatpak-libs-1.2.4-3.fc30.x86_64.rpm b/tests/data/test-diff-pkg/PR24690/flatpak-libs-1.2.4-3.fc30.x86_64.rpm
new file mode 100644 (file)
index 0000000..844dcc1
Binary files /dev/null and b/tests/data/test-diff-pkg/PR24690/flatpak-libs-1.2.4-3.fc30.x86_64.rpm differ
diff --git a/tests/data/test-diff-pkg/PR24690/flatpak-libs-1.4.0-1.fc30.x86_64.rpm b/tests/data/test-diff-pkg/PR24690/flatpak-libs-1.4.0-1.fc30.x86_64.rpm
new file mode 100644 (file)
index 0000000..877bf7b
Binary files /dev/null and b/tests/data/test-diff-pkg/PR24690/flatpak-libs-1.4.0-1.fc30.x86_64.rpm differ
diff --git a/tests/data/test-diff-pkg/PR24690/flatpak-libs-debuginfo-1.2.4-3.fc30.x86_64.rpm b/tests/data/test-diff-pkg/PR24690/flatpak-libs-debuginfo-1.2.4-3.fc30.x86_64.rpm
new file mode 100644 (file)
index 0000000..42d4214
Binary files /dev/null and b/tests/data/test-diff-pkg/PR24690/flatpak-libs-debuginfo-1.2.4-3.fc30.x86_64.rpm differ
diff --git a/tests/data/test-diff-pkg/PR24690/flatpak-libs-debuginfo-1.4.0-1.fc30.x86_64.rpm b/tests/data/test-diff-pkg/PR24690/flatpak-libs-debuginfo-1.4.0-1.fc30.x86_64.rpm
new file mode 100644 (file)
index 0000000..3e97613
Binary files /dev/null and b/tests/data/test-diff-pkg/PR24690/flatpak-libs-debuginfo-1.4.0-1.fc30.x86_64.rpm differ
diff --git a/tests/data/test-diff-suppr/test47-non-reachable-types-report-1.txt b/tests/data/test-diff-suppr/test47-non-reachable-types-report-1.txt
new file mode 100644 (file)
index 0000000..5efd5df
--- /dev/null
@@ -0,0 +1,20 @@
+Functions changes summary: 0 Removed, 0 Changed, 0 Added function
+Variables changes summary: 0 Removed, 0 Changed, 0 Added variable
+Unreachable types summary: 1 removed, 1 changed, 1 added types
+
+1 removed type unreachable from any public interface:
+
+  struct unreachable_struct1 at test-v0.c:6:1
+
+1 changed type unreachable from any public interface:
+
+  'struct unreachable_struct2' changed:
+    type size changed from 32 to 64 (in bits)
+    1 data member insertion:
+      'char unreachable_struct2::m2', at offset 32 (in bits) at test-v1.c:9:1
+
+
+1 added type unreachable from any public interface:
+
+  'struct unreachable_struct3' at test-v1.c:12:1
+
diff --git a/tests/data/test-diff-suppr/test47-non-reachable-types-report-10.txt b/tests/data/test-diff-suppr/test47-non-reachable-types-report-10.txt
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/tests/data/test-diff-suppr/test47-non-reachable-types-report-2.txt b/tests/data/test-diff-suppr/test47-non-reachable-types-report-2.txt
new file mode 100644 (file)
index 0000000..21005c2
--- /dev/null
@@ -0,0 +1,16 @@
+Functions changes summary: 0 Removed, 0 Changed, 0 Added function
+Variables changes summary: 0 Removed, 0 Changed, 0 Added variable
+Unreachable types summary: 0 removed (1 filtered out), 1 changed, 1 added types
+
+1 changed type unreachable from any public interface:
+
+  'struct unreachable_struct2' changed:
+    type size changed from 32 to 64 (in bits)
+    1 data member insertion:
+      'char unreachable_struct2::m2', at offset 32 (in bits) at test-v1.c:9:1
+
+
+1 added type unreachable from any public interface:
+
+  'struct unreachable_struct3' at test-v1.c:12:1
+
diff --git a/tests/data/test-diff-suppr/test47-non-reachable-types-report-3.txt b/tests/data/test-diff-suppr/test47-non-reachable-types-report-3.txt
new file mode 100644 (file)
index 0000000..d0736bf
--- /dev/null
@@ -0,0 +1,13 @@
+Functions changes summary: 0 Removed, 0 Changed, 0 Added function
+Variables changes summary: 0 Removed, 0 Changed, 0 Added variable
+Unreachable types summary: 1 removed, 0 changed (1 filtered out), 1 added types
+
+1 removed type unreachable from any public interface:
+
+  struct unreachable_struct1 at test-v0.c:6:1
+
+
+1 added type unreachable from any public interface:
+
+  'struct unreachable_struct3' at test-v1.c:12:1
+
diff --git a/tests/data/test-diff-suppr/test47-non-reachable-types-report-4.txt b/tests/data/test-diff-suppr/test47-non-reachable-types-report-4.txt
new file mode 100644 (file)
index 0000000..1004cdf
--- /dev/null
@@ -0,0 +1,16 @@
+Functions changes summary: 0 Removed, 0 Changed, 0 Added function
+Variables changes summary: 0 Removed, 0 Changed, 0 Added variable
+Unreachable types summary: 1 removed, 1 changed, 0 added (1 filtered out) types
+
+1 removed type unreachable from any public interface:
+
+  struct unreachable_struct1 at test-v0.c:6:1
+
+1 changed type unreachable from any public interface:
+
+  'struct unreachable_struct2' changed:
+    type size changed from 32 to 64 (in bits)
+    1 data member insertion:
+      'char unreachable_struct2::m2', at offset 32 (in bits) at test-v1.c:9:1
+
+
diff --git a/tests/data/test-diff-suppr/test47-non-reachable-types-report-5.txt b/tests/data/test-diff-suppr/test47-non-reachable-types-report-5.txt
new file mode 100644 (file)
index 0000000..11d0d20
--- /dev/null
@@ -0,0 +1,5 @@
+Functions changes summary: 0 Removed, 0 Changed, 0 Added function
+Variables changes summary: 0 Removed, 0 Changed, 0 Added variable
+Unreachable types summary: 0 removed (1 filtered out), 0 changed (1 filtered out), 0 added (1 filtered out) types
+
+
diff --git a/tests/data/test-diff-suppr/test47-non-reachable-types-report-6.txt b/tests/data/test-diff-suppr/test47-non-reachable-types-report-6.txt
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/tests/data/test-diff-suppr/test47-non-reachable-types-report-7.txt b/tests/data/test-diff-suppr/test47-non-reachable-types-report-7.txt
new file mode 100644 (file)
index 0000000..51af7fe
--- /dev/null
@@ -0,0 +1,22 @@
+Leaf changes summary: 0 artifact changed
+Changed leaf types summary: 0 leaf type changed
+Removed/Changed/Added functions summary: 0 Removed, 0 Changed, 0 Added function
+Removed/Changed/Added variables summary: 0 Removed, 0 Changed, 0 Added variable
+Unreachable types summary: 1 removed, 1 changed, 1 added types
+
+1 removed type unreachable from any public interface:
+
+  struct unreachable_struct1 at test-v0.c:6:1
+
+1 changed type unreachable from any public interface:
+
+  'struct unreachable_struct2' changed:
+    type size changed from 32 to 64 (in bits)
+    1 data member insertion:
+      'char unreachable_struct2::m2', at offset 32 (in bits) at test-v1.c:9:1
+
+
+1 added type unreachable from any public interface:
+
+  'struct unreachable_struct3' at test-v1.c:12:1
+
diff --git a/tests/data/test-diff-suppr/test47-non-reachable-types-report-8.txt b/tests/data/test-diff-suppr/test47-non-reachable-types-report-8.txt
new file mode 100644 (file)
index 0000000..46e854b
--- /dev/null
@@ -0,0 +1,7 @@
+Leaf changes summary: 0 artifact changed
+Changed leaf types summary: 0 leaf type changed
+Removed/Changed/Added functions summary: 0 Removed, 0 Changed, 0 Added function
+Removed/Changed/Added variables summary: 0 Removed, 0 Changed, 0 Added variable
+Unreachable types summary: 0 removed (1 filtered out), 0 changed (1 filtered out), 0 added (1 filtered out) types
+
+
diff --git a/tests/data/test-diff-suppr/test47-non-reachable-types-report-9.txt b/tests/data/test-diff-suppr/test47-non-reachable-types-report-9.txt
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/tests/data/test-diff-suppr/test47-non-reachable-types-suppr-1.txt b/tests/data/test-diff-suppr/test47-non-reachable-types-suppr-1.txt
new file mode 100644 (file)
index 0000000..7c38db8
--- /dev/null
@@ -0,0 +1,3 @@
+[suppress_type]
+  name = unreachable_struct1
+
diff --git a/tests/data/test-diff-suppr/test47-non-reachable-types-suppr-2.txt b/tests/data/test-diff-suppr/test47-non-reachable-types-suppr-2.txt
new file mode 100644 (file)
index 0000000..7a1630b
--- /dev/null
@@ -0,0 +1,3 @@
+[suppress_type]
+  name = unreachable_struct2
+
diff --git a/tests/data/test-diff-suppr/test47-non-reachable-types-suppr-3.txt b/tests/data/test-diff-suppr/test47-non-reachable-types-suppr-3.txt
new file mode 100644 (file)
index 0000000..83dcad7
--- /dev/null
@@ -0,0 +1,3 @@
+[suppress_type]
+  name = unreachable_struct3
+
diff --git a/tests/data/test-diff-suppr/test47-non-reachable-types-suppr-4.txt b/tests/data/test-diff-suppr/test47-non-reachable-types-suppr-4.txt
new file mode 100644 (file)
index 0000000..4e32841
--- /dev/null
@@ -0,0 +1,2 @@
+[suppress_type]
+  name_regexp = unreachable_struct.
diff --git a/tests/data/test-diff-suppr/test47-non-reachable-types-suppr-5.txt b/tests/data/test-diff-suppr/test47-non-reachable-types-suppr-5.txt
new file mode 100644 (file)
index 0000000..db75c0c
--- /dev/null
@@ -0,0 +1,4 @@
+[suppress_type]
+  name_regexp = unreachable_struct.
+  drop = true
+
diff --git a/tests/data/test-diff-suppr/test47-non-reachable-types-v0.c b/tests/data/test-diff-suppr/test47-non-reachable-types-v0.c
new file mode 100644 (file)
index 0000000..f04d831
--- /dev/null
@@ -0,0 +1,28 @@
+struct reachable_struct1
+{
+  int m0;
+};
+
+struct unreachable_struct1
+{
+  int m1;
+};
+
+struct unreachable_struct2
+{
+  int m1;
+};
+
+void foo(struct reachable_struct1* __attribute__((unused)) s)
+{
+}
+
+static void private_func()
+{
+  struct unreachable_struct1 s1;
+  s1.m1 = 0;
+
+  struct unreachable_struct2 s0;
+  s0.m1 = 0;
+
+}
diff --git a/tests/data/test-diff-suppr/test47-non-reachable-types-v0.o b/tests/data/test-diff-suppr/test47-non-reachable-types-v0.o
new file mode 100644 (file)
index 0000000..2b4786f
Binary files /dev/null and b/tests/data/test-diff-suppr/test47-non-reachable-types-v0.o differ
diff --git a/tests/data/test-diff-suppr/test47-non-reachable-types-v0.o.alltypes.abixml b/tests/data/test-diff-suppr/test47-non-reachable-types-v0.o.alltypes.abixml
new file mode 100644 (file)
index 0000000..eecad63
--- /dev/null
@@ -0,0 +1,29 @@
+<abi-corpus path='test-v0.o' architecture='elf-amd-x86_64' tracking-non-reachable-types='yes'>
+  <elf-function-symbols>
+    <elf-symbol name='foo' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
+  </elf-function-symbols>
+  <abi-instr version='1.0' address-size='64' path='test-v0.c' comp-dir-path='/home/dodji/git/libabigail/compare-all-types/prtests/PR24690' language='LANG_C89'>
+    <type-decl name='int' size-in-bits='32' id='type-id-1'/>
+    <type-decl name='void' id='type-id-2'/>
+    <class-decl name='reachable_struct1' size-in-bits='32' is-struct='yes' visibility='default' filepath='/home/dodji/git/libabigail/compare-all-types/prtests/PR24690/test-v0.c' line='1' column='1' id='type-id-3'>
+      <data-member access='public' layout-offset-in-bits='0'>
+        <var-decl name='m0' type-id='type-id-1' visibility='default' filepath='/home/dodji/git/libabigail/compare-all-types/prtests/PR24690/test-v0.c' line='3' column='1'/>
+      </data-member>
+    </class-decl>
+    <class-decl name='unreachable_struct1' size-in-bits='32' is-struct='yes' is-non-reachable='yes' visibility='default' filepath='/home/dodji/git/libabigail/compare-all-types/prtests/PR24690/test-v0.c' line='6' column='1' id='type-id-4'>
+      <data-member access='public' layout-offset-in-bits='0'>
+        <var-decl name='m1' type-id='type-id-1' visibility='default' filepath='/home/dodji/git/libabigail/compare-all-types/prtests/PR24690/test-v0.c' line='8' column='1'/>
+      </data-member>
+    </class-decl>
+    <class-decl name='unreachable_struct2' size-in-bits='32' is-struct='yes' is-non-reachable='yes' visibility='default' filepath='/home/dodji/git/libabigail/compare-all-types/prtests/PR24690/test-v0.c' line='11' column='1' id='type-id-5'>
+      <data-member access='public' layout-offset-in-bits='0'>
+        <var-decl name='m1' type-id='type-id-1' visibility='default' filepath='/home/dodji/git/libabigail/compare-all-types/prtests/PR24690/test-v0.c' line='13' column='1'/>
+      </data-member>
+    </class-decl>
+    <pointer-type-def type-id='type-id-3' size-in-bits='64' id='type-id-6'/>
+    <function-decl name='foo' mangled-name='foo' filepath='/home/dodji/git/libabigail/compare-all-types/prtests/PR24690/test-v0.c' line='16' column='1' visibility='default' binding='global' size-in-bits='64' elf-symbol-id='foo'>
+      <parameter type-id='type-id-6' name='s' filepath='/home/dodji/git/libabigail/compare-all-types/prtests/PR24690/test-v0.c' line='16' column='1'/>
+      <return type-id='type-id-2'/>
+    </function-decl>
+  </abi-instr>
+</abi-corpus>
diff --git a/tests/data/test-diff-suppr/test47-non-reachable-types-v1.c b/tests/data/test-diff-suppr/test47-non-reachable-types-v1.c
new file mode 100644 (file)
index 0000000..43fbc79
--- /dev/null
@@ -0,0 +1,27 @@
+struct reachable_struct1
+{
+  int m0;
+};
+
+struct unreachable_struct2
+{
+  int m1;
+  char m2;
+};
+
+struct unreachable_struct3
+{
+  int m1;
+};
+
+void foo(struct reachable_struct1* __attribute__((unused)) s)
+{
+}
+
+static void private_func()
+{
+  struct unreachable_struct2 s0;
+  s0.m1 = 0;
+  struct unreachable_struct3 s1;
+  s1.m1 = 0;
+}
diff --git a/tests/data/test-diff-suppr/test47-non-reachable-types-v1.o b/tests/data/test-diff-suppr/test47-non-reachable-types-v1.o
new file mode 100644 (file)
index 0000000..fe38711
Binary files /dev/null and b/tests/data/test-diff-suppr/test47-non-reachable-types-v1.o differ
diff --git a/tests/data/test-diff-suppr/test47-non-reachable-types-v1.o.alltypes.abixml b/tests/data/test-diff-suppr/test47-non-reachable-types-v1.o.alltypes.abixml
new file mode 100644 (file)
index 0000000..ddfd4b5
--- /dev/null
@@ -0,0 +1,33 @@
+<abi-corpus path='test-v1.o' architecture='elf-amd-x86_64' tracking-non-reachable-types='yes'>
+  <elf-function-symbols>
+    <elf-symbol name='foo' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
+  </elf-function-symbols>
+  <abi-instr version='1.0' address-size='64' path='test-v1.c' comp-dir-path='/home/dodji/git/libabigail/compare-all-types/prtests/PR24690' language='LANG_C89'>
+    <type-decl name='char' size-in-bits='8' id='type-id-1'/>
+    <type-decl name='int' size-in-bits='32' id='type-id-2'/>
+    <type-decl name='void' id='type-id-3'/>
+    <class-decl name='reachable_struct1' size-in-bits='32' is-struct='yes' visibility='default' filepath='/home/dodji/git/libabigail/compare-all-types/prtests/PR24690/test-v1.c' line='1' column='1' id='type-id-4'>
+      <data-member access='public' layout-offset-in-bits='0'>
+        <var-decl name='m0' type-id='type-id-2' visibility='default' filepath='/home/dodji/git/libabigail/compare-all-types/prtests/PR24690/test-v1.c' line='3' column='1'/>
+      </data-member>
+    </class-decl>
+    <class-decl name='unreachable_struct2' size-in-bits='64' is-struct='yes' is-non-reachable='yes' visibility='default' filepath='/home/dodji/git/libabigail/compare-all-types/prtests/PR24690/test-v1.c' line='6' column='1' id='type-id-5'>
+      <data-member access='public' layout-offset-in-bits='0'>
+        <var-decl name='m1' type-id='type-id-2' visibility='default' filepath='/home/dodji/git/libabigail/compare-all-types/prtests/PR24690/test-v1.c' line='8' column='1'/>
+      </data-member>
+      <data-member access='public' layout-offset-in-bits='32'>
+        <var-decl name='m2' type-id='type-id-1' visibility='default' filepath='/home/dodji/git/libabigail/compare-all-types/prtests/PR24690/test-v1.c' line='9' column='1'/>
+      </data-member>
+    </class-decl>
+    <class-decl name='unreachable_struct3' size-in-bits='32' is-struct='yes' is-non-reachable='yes' visibility='default' filepath='/home/dodji/git/libabigail/compare-all-types/prtests/PR24690/test-v1.c' line='12' column='1' id='type-id-6'>
+      <data-member access='public' layout-offset-in-bits='0'>
+        <var-decl name='m1' type-id='type-id-2' visibility='default' filepath='/home/dodji/git/libabigail/compare-all-types/prtests/PR24690/test-v1.c' line='14' column='1'/>
+      </data-member>
+    </class-decl>
+    <pointer-type-def type-id='type-id-4' size-in-bits='64' id='type-id-7'/>
+    <function-decl name='foo' mangled-name='foo' filepath='/home/dodji/git/libabigail/compare-all-types/prtests/PR24690/test-v1.c' line='17' column='1' visibility='default' binding='global' size-in-bits='64' elf-symbol-id='foo'>
+      <parameter type-id='type-id-7' name='s' filepath='/home/dodji/git/libabigail/compare-all-types/prtests/PR24690/test-v1.c' line='17' column='1'/>
+      <return type-id='type-id-3'/>
+    </function-decl>
+  </abi-instr>
+</abi-corpus>
index 95338beb83aed3f6650ff0fc0db39c9a4506019b..d8fa24f5a099c1b7db18162b8a2ed9d31dd43f06 100644 (file)
@@ -605,6 +605,20 @@ static InOutSpec in_out_specs[] =
     "data/test-diff-pkg/PR24410-report-0.txt",
     "output/test-diff-pkg/PR24410-report-0.txt"
   },
+  {
+    "data/test-diff-pkg/PR24690/flatpak-libs-1.2.4-3.fc30.x86_64.rpm",
+    "data/test-diff-pkg/PR24690/flatpak-libs-1.4.0-1.fc30.x86_64.rpm",
+    "--non-reachable-types",
+    "",
+    "data/test-diff-pkg/PR24690/flatpak-debuginfo-1.2.4-3.fc30.x86_64.rpm, "
+    "data/test-diff-pkg/PR24690/flatpak-libs-debuginfo-1.2.4-3.fc30.x86_64.rpm",
+    "data/test-diff-pkg/PR24690/flatpak-debuginfo-1.4.0-1.fc30.x86_64.rpm, "
+    "data/test-diff-pkg/PR24690/flatpak-libs-debuginfo-1.4.0-1.fc30.x86_64.rpm",
+    "data/test-diff-pkg/PR24690/flatpak-devel-1.2.4-3.fc30.x86_64.rpm",
+    "data/test-diff-pkg/PR24690/flatpak-devel-1.4.0-1.fc30.x86_64.rpm",
+    "data/test-diff-pkg/PR24690/PR24690-report-0.txt",
+    "output/test-diff-pkg/PR24690/PR24690-report-0.txt"
+  },
 #endif //WITH_RPM
 
 #ifdef WITH_DEB
index a8e509691e201819c05048ece2200a93b4122c99..5d9528fe0bd9656308f03859415fecc021b719b2 100644 (file)
@@ -1838,6 +1838,116 @@ InOutSpec in_out_specs[] =
     "data/test-diff-suppr/test46-PR25128-report-1.txt",
     "output/test-diff-suppr/test46-PR25128-report-1.txt"
   },
+  {
+    "data/test-diff-suppr/test47-non-reachable-types-v0.o",
+    "data/test-diff-suppr/test47-non-reachable-types-v1.o",
+    "",
+    "",
+    "",
+    "--no-default-suppression --non-reachable-types",
+    "data/test-diff-suppr/test47-non-reachable-types-report-1.txt",
+    "output/test-diff-suppr/test47-non-reachable-types-report-1.txt"
+  },
+  {
+    "data/test-diff-suppr/test47-non-reachable-types-v0.o",
+    "data/test-diff-suppr/test47-non-reachable-types-v1.o",
+    "",
+    "",
+    "data/test-diff-suppr/test47-non-reachable-types-suppr-1.txt",
+    "--no-default-suppression --non-reachable-types",
+    "data/test-diff-suppr/test47-non-reachable-types-report-2.txt",
+    "output/test-diff-suppr/test47-non-reachable-types-report-2.txt"
+  },
+  {
+    "data/test-diff-suppr/test47-non-reachable-types-v0.o",
+    "data/test-diff-suppr/test47-non-reachable-types-v1.o",
+    "",
+    "",
+    "data/test-diff-suppr/test47-non-reachable-types-suppr-2.txt",
+    "--no-default-suppression --non-reachable-types",
+    "data/test-diff-suppr/test47-non-reachable-types-report-3.txt",
+    "output/test-diff-suppr/test47-non-reachable-types-report-3.txt"
+  },
+  {
+    "data/test-diff-suppr/test47-non-reachable-types-v0.o",
+    "data/test-diff-suppr/test47-non-reachable-types-v1.o",
+    "",
+    "",
+    "data/test-diff-suppr/test47-non-reachable-types-suppr-3.txt",
+    "--no-default-suppression --non-reachable-types",
+    "data/test-diff-suppr/test47-non-reachable-types-report-4.txt",
+    "output/test-diff-suppr/test47-non-reachable-types-report-4.txt"
+  },
+  {
+    "data/test-diff-suppr/test47-non-reachable-types-v0.o",
+    "data/test-diff-suppr/test47-non-reachable-types-v1.o",
+    "",
+    "",
+    "data/test-diff-suppr/test47-non-reachable-types-suppr-4.txt",
+    "--no-default-suppression --non-reachable-types",
+    "data/test-diff-suppr/test47-non-reachable-types-report-5.txt",
+    "output/test-diff-suppr/test47-non-reachable-types-report-5.txt"
+  },
+  {
+    "data/test-diff-suppr/test47-non-reachable-types-v0.o",
+    "data/test-diff-suppr/test47-non-reachable-types-v1.o",
+    "",
+    "",
+    "data/test-diff-suppr/test47-non-reachable-types-suppr-5.txt",
+    "--no-default-suppression --non-reachable-types",
+    "data/test-diff-suppr/test47-non-reachable-types-report-6.txt",
+    "output/test-diff-suppr/test47-non-reachable-types-report-6.txt"
+  },
+  {
+    "data/test-diff-suppr/test47-non-reachable-types-v0.o",
+    "data/test-diff-suppr/test47-non-reachable-types-v1.o",
+    "",
+    "",
+    "",
+    "--no-default-suppression --non-reachable-types --leaf-changes-only",
+    "data/test-diff-suppr/test47-non-reachable-types-report-7.txt",
+    "output/test-diff-suppr/test47-non-reachable-types-report-7.txt"
+  },
+  {
+    "data/test-diff-suppr/test47-non-reachable-types-v0.o",
+    "data/test-diff-suppr/test47-non-reachable-types-v1.o",
+    "",
+    "",
+    "data/test-diff-suppr/test47-non-reachable-types-suppr-4.txt",
+    "--no-default-suppression --non-reachable-types --leaf-changes-only",
+    "data/test-diff-suppr/test47-non-reachable-types-report-8.txt",
+    "output/test-diff-suppr/test47-non-reachable-types-report-8.txt"
+  },
+  {
+    "data/test-diff-suppr/test47-non-reachable-types-v0.o",
+    "data/test-diff-suppr/test47-non-reachable-types-v1.o",
+    "",
+    "",
+    "data/test-diff-suppr/test47-non-reachable-types-suppr-5.txt",
+    "--no-default-suppression --non-reachable-types --leaf-changes-only",
+    "data/test-diff-suppr/test47-non-reachable-types-report-9.txt",
+    "output/test-diff-suppr/test47-non-reachable-types-report-9.txt"
+  },
+  {
+    "data/test-diff-suppr/test47-non-reachable-types-v0.o.alltypes.abixml",
+    "data/test-diff-suppr/test47-non-reachable-types-v1.o.alltypes.abixml",
+    "",
+    "",
+    "",
+    "--no-default-suppression --non-reachable-types",
+    "data/test-diff-suppr/test47-non-reachable-types-report-1.txt",
+    "output/test-diff-suppr/test47-non-reachable-types-report-1.txt"
+  },
+  {
+    "data/test-diff-suppr/test47-non-reachable-types-v0.o.alltypes.abixml",
+    "data/test-diff-suppr/test47-non-reachable-types-v1.o.alltypes.abixml",
+    "",
+    "",
+    "",
+    "--no-default-suppression",
+    "data/test-diff-suppr/test47-non-reachable-types-report-10.txt",
+    "output/test-diff-suppr/test47-non-reachable-types-report-10.txt"
+  },
   // This should be the last entry
   {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}
 };
index a8b7828980946d5a7198bec92112f7e77e32261b..1edb1b401cc4cdc39d70d2a221e87b68eac35242 100644 (file)
@@ -101,6 +101,7 @@ struct options
   bool                 show_changed_vars;
   bool                 show_added_vars;
   bool                 show_all_vars;
+  bool                 show_all_types;
   bool                 show_linkage_names;
   bool                 show_locs;
   bool                 show_harmful_changes;
@@ -141,6 +142,7 @@ struct options
       show_changed_vars(),
       show_added_vars(),
       show_all_vars(true),
+      show_all_types(false),
       show_linkage_names(true),
       show_locs(true),
       show_harmful_changes(true),
@@ -203,6 +205,8 @@ display_usage(const string& prog_name, ostream& out)
     << " --deleted-vars  display deleted global public variables\n"
     << " --changed-vars  display changed global public variables\n"
     << " --added-vars  display added global public variables\n"
+    << " --non-reachable-types|-t  consider types non reachable"
+    " from public interfaces\n"
     << " --no-added-syms  do not display added functions or variables\n"
     << " --no-linkage-name  do not display linkage names of "
     "added/removed/changed\n"
@@ -399,6 +403,9 @@ parse_command_line(int argc, char* argv[], options& opts)
          opts.show_all_fns = false;
          opts.show_all_vars = false;
        }
+      else if (!strcmp(argv[i], "--non-reachable-types")
+              || !strcmp(argv[i], "-t"))
+         opts.show_all_types = true;
       else if (!strcmp(argv[i], "--no-added-syms"))
        {
          opts.show_added_syms = false;
@@ -647,6 +654,7 @@ set_diff_context_from_opts(diff_context_sptr ctxt,
     (opts.show_symbols_not_referenced_by_debug_info);
   ctxt->show_added_symbols_unreferenced_by_debug_info
     (opts.show_symbols_not_referenced_by_debug_info && opts.show_added_syms);
+  ctxt->show_unreachable_types(opts.show_all_types);
   ctxt->show_impacted_interfaces(opts.show_impacted_interfaces);
 
   if (!opts.show_harmless_changes)
@@ -769,6 +777,20 @@ set_suppressions(ReadContextType& read_ctxt, const options& opts)
   add_read_context_suppressions(read_ctxt, supprs);
 }
 
+/// Configure the abigail::xml_reacher::read_context based on the
+/// relevant command-line options.
+///
+/// @param ctxt the read context to configure.
+///
+/// @param opts the command-line options to configure @p ctxt from.
+static void
+set_native_xml_reader_options(abigail::xml_reader::read_context& ctxt,
+                             const options& opts)
+{
+  consider_types_not_reachable_from_public_interfaces(ctxt,
+                                                     opts.show_all_types);
+}
+
 /// Set the regex patterns describing the functions to drop from the
 /// symbol table of a given corpus.
 ///
@@ -1059,13 +1081,14 @@ main(int argc, char* argv[])
          t1 = abigail::xml_reader::read_translation_unit_from_file(opts.file1,
                                                                    env.get());
          break;
-       case abigail::tools_utils::FILE_TYPE_ELF:
+       case abigail::tools_utils::FILE_TYPE_ELF: // fall through
        case abigail::tools_utils::FILE_TYPE_AR:
          {
            abigail::dwarf_reader::read_context_sptr ctxt =
              abigail::dwarf_reader::create_read_context
-             (opts.file1, opts.prepared_di_root_paths1, env.get(),
-              /*readalltypes*/false, opts.linux_kernel_mode);
+             (opts.file1, opts.prepared_di_root_paths1,
+              env.get(), /*readalltypes=*/opts.show_all_types,
+              opts.linux_kernel_mode);
            assert(ctxt);
 
            abigail::dwarf_reader::set_show_stats(*ctxt, opts.show_stats);
@@ -1087,6 +1110,7 @@ main(int argc, char* argv[])
                                                                  env.get());
            assert(ctxt);
            set_suppressions(*ctxt, opts);
+           set_native_xml_reader_options(*ctxt, opts);
            c1 = abigail::xml_reader::read_corpus_from_input(*ctxt);
            if (!c1)
              return handle_error(c1_status, /*ctxt=*/0,
@@ -1100,6 +1124,7 @@ main(int argc, char* argv[])
                                                                  env.get());
            assert(ctxt);
            set_suppressions(*ctxt, opts);
+           set_native_xml_reader_options(*ctxt, opts);
            g1 = abigail::xml_reader::read_corpus_group_from_input(*ctxt);
            if (!g1)
              return handle_error(c1_status, /*ctxt=*/0,
@@ -1132,13 +1157,14 @@ main(int argc, char* argv[])
          t2 = abigail::xml_reader::read_translation_unit_from_file(opts.file2,
                                                                    env.get());
          break;
-       case abigail::tools_utils::FILE_TYPE_ELF:
+       case abigail::tools_utils::FILE_TYPE_ELF: // Fall through
        case abigail::tools_utils::FILE_TYPE_AR:
          {
            abigail::dwarf_reader::read_context_sptr ctxt =
              abigail::dwarf_reader::create_read_context
-             (opts.file2, opts.prepared_di_root_paths2, env.get(),
-              /*readalltypes=*/false, opts.linux_kernel_mode);
+             (opts.file2, opts.prepared_di_root_paths2,
+              env.get(), /*readalltypes=*/opts.show_all_types,
+              opts.linux_kernel_mode);
            assert(ctxt);
            abigail::dwarf_reader::set_show_stats(*ctxt, opts.show_stats);
            abigail::dwarf_reader::set_do_log(*ctxt, opts.do_log);
@@ -1160,6 +1186,7 @@ main(int argc, char* argv[])
                                                                  env.get());
            assert(ctxt);
            set_suppressions(*ctxt, opts);
+           set_native_xml_reader_options(*ctxt, opts);
            c2 = abigail::xml_reader::read_corpus_from_input(*ctxt);
            if (!c2)
              return handle_error(c2_status, /*ctxt=*/0, argv[0], opts);
@@ -1173,6 +1200,7 @@ main(int argc, char* argv[])
                                                                  env.get());
            assert(ctxt);
            set_suppressions(*ctxt, opts);
+           set_native_xml_reader_options(*ctxt, opts);
            g2 = abigail::xml_reader::read_corpus_group_from_input(*ctxt);
            if (!g2)
              return handle_error(c2_status, /*ctxt=*/0, argv[0], opts);
index f3065fdf05ac9593988feff60aa71ce6ef90820f..ce60f67625935ba7ebf0dc4f4c48493ce7788e8d 100644 (file)
@@ -192,6 +192,7 @@ public:
   bool         compare_dso_only;
   bool         compare_private_dsos;
   bool         leaf_changes_only;
+  bool         show_all_types;
   bool         show_hexadecimal_values;
   bool         show_offsets_sizes_in_bits;
   bool         show_impacted_interfaces;
@@ -226,6 +227,7 @@ public:
       compare_dso_only(),
       compare_private_dsos(),
       leaf_changes_only(),
+      show_all_types(),
       show_hexadecimal_values(),
       show_offsets_sizes_in_bits(true),
       show_impacted_interfaces(),
@@ -853,6 +855,8 @@ display_usage(const string& prog_name, ostream& out)
     "interfaces impacted by ABI changes\n"
     << " --full-impact|-f  when comparing kernel packages, show the "
     "full impact analysis report rather than the default leaf changes reports\n"
+    << " --non-reachable-types|-t  consider types non reachable"
+    " from public interfaces\n"
     << " --no-linkage-name             do not display linkage names of "
     "added/removed/changed\n"
     << " --redundant                    display redundant changes\n"
@@ -1173,6 +1177,7 @@ set_diff_context_from_opts(diff_context_sptr ctxt,
   ctxt->show_redundant_changes(opts.show_redundant_changes);
   ctxt->show_leaf_changes_only(opts.leaf_changes_only);
   ctxt->show_impacted_interfaces(opts.show_impacted_interfaces);
+  ctxt->show_unreachable_types(opts.show_all_types);
   ctxt->show_hex_values(opts.show_hexadecimal_values);
   ctxt->show_offsets_sizes_in_bits(opts.show_offsets_sizes_in_bits);
   ctxt->show_relative_offset_changes(opts.show_relative_offset_changes);
@@ -1294,8 +1299,9 @@ compare(const elf_file& elf1,
 
   corpus_sptr corpus1;
   {
-    read_context_sptr c = create_read_context(elf1.path, di_dirs1, env.get(),
-                                             /*load_all_types=*/false);
+    read_context_sptr c =
+      create_read_context(elf1.path, di_dirs1, env.get(),
+                         /*load_all_types=*/opts.show_all_types);
     add_read_context_suppressions(*c, priv_types_supprs1);
     if (!opts.kabi_suppressions.empty())
       add_read_context_suppressions(*c, opts.kabi_suppressions);
@@ -1379,8 +1385,9 @@ compare(const elf_file& elf1,
 
   corpus_sptr corpus2;
   {
-    read_context_sptr c = create_read_context(elf2.path, di_dirs2, env.get(),
-                                             /*load_all_types=*/false);
+    read_context_sptr c =
+      create_read_context(elf2.path, di_dirs2, env.get(),
+                         /*load_all_types=*/opts.show_all_types);
     add_read_context_suppressions(*c, priv_types_supprs2);
 
     if (!opts.kabi_suppressions.empty())
@@ -2809,6 +2816,9 @@ parse_command_line(int argc, char* argv[], options& opts)
       else if (!strcmp(argv[i], "--impacted-interfaces")
               ||!strcmp(argv[i], "-i"))
        opts.show_impacted_interfaces = true;
+      else if (!strcmp(argv[i], "--non-reachable-types")
+              ||!strcmp(argv[i], "-t"))
+       opts.show_all_types = true;
       else if (!strcmp(argv[i], "--full-impact")
               ||!strcmp(argv[i], "-f"))
        opts.show_full_impact_report = true;