public inbox for libabigail@sourceware.org
 help / color / mirror / Atom feed
* [PATCH 00/13] Support negative suppression specifications
       [not found] <877cvzrnws.fsf@redhat.com>
@ 2023-03-02 18:53 ` Dodji Seketeli
  2023-03-02 18:55   ` [PATCH 01/13] ini: Fix parsing list property values Dodji Seketeli
                     ` (12 more replies)
  0 siblings, 13 replies; 14+ messages in thread
From: Dodji Seketeli @ 2023-03-02 18:53 UTC (permalink / raw)
  To: libabigail; +Cc: dodji

Hello,

The core of this patch is to beef up the suppression specification
language so that we can express something like this:

    [allow_type] #1
      label = first-suppr
      type_kind = struct
      has_data_member_regexp = ^__reserved_padding #2

    [suppress_type]
      label = second-suppr
      type_kind = struct
      has_data_member_inserted_between =
	{
	    offset_of_first_data_member_regexp(^__reserved_padding), #3
	    offset_of_last_data_member_regexp(^__reserved_padding), #4
	}


In #1, the [allow_type] directive suppresses all type changes that do
NOT match this directive.  I call this a "negative type suppression
specification".  In #2, the "has_data_member_regexp" property matches
a type which has a data member which name matches the regular
expression value of this property.

In #3, the offset_of_first_data_member_regexp() function-like expression
evaluates to the offset of the first data member which name matches
the regular expression in argument.

In #4, the offset_of_last_data_member_regexp() function-like
expression evaluates to the offset of the last data member which name
matches the regular expression in argument.

The patch set not only implements the features #1 to #4 but also
includes some performance work to improve the speed of the analysis of
big binaries with lots diff nodes.

Dodji Seketeli (13):
  ini: Fix parsing list property values
  suppr: Support has_data_member and has_data_member_regexp properties
  suppression: Factorize out is_data_member_offset_in_range
  suppression: Support the has_size_change property for suppress_type
  suppression: Support offset_of_{first,last}_data_member_regexp offset selectors
  comparison, suppression: Support [allow_type] directive
  Misc white space fixes
  abidiff: Add extensive logging
  tools-utils: Support kernel stablelist
  comp-filter: Don't re-visit node while applying filters to diff nodes
  comparison: Add a mode to not apply filters on interface sub-graphs
  comparison: When marking leaf nodes don't do unnecessary impact analysis
  comp-filter: Speed up harmless/harmful categorization

 doc/manuals/abidiff.rst                       |  12 +
 doc/manuals/kmidiff.rst                       |  11 +
 doc/manuals/libabigail-concepts.rst           | 158 ++++-
 include/abg-comparison.h                      |  86 ++-
 include/abg-corpus.h                          |   6 +
 include/abg-fwd.h                             |  48 ++
 include/abg-suppression.h                     | 110 +++-
 src/abg-comp-filter.cc                        |  27 +-
 src/abg-comparison-priv.h                     |  23 +-
 src/abg-comparison.cc                         | 581 ++++++++++++++++--
 src/abg-corpus-priv.h                         |   4 +-
 src/abg-corpus.cc                             |  14 +
 src/abg-ctf-reader.cc                         |   1 +
 src/abg-default-reporter.cc                   | 352 ++++++-----
 src/abg-elf-helpers.cc                        |   1 +
 src/abg-ini.cc                                |   1 +
 src/abg-ir.cc                                 | 102 ++-
 src/abg-reader.cc                             |  22 +
 src/abg-suppression-priv.h                    |  43 +-
 src/abg-suppression.cc                        | 479 ++++++++++++---
 src/abg-tools-utils.cc                        |   3 +-
 tests/data/Makefile.am                        |  80 +++
 .../test-allow-type-array-suppr.txt           |   7 +
 .../test-allow-type-array-v0--v1-report-1.txt |  23 +
 .../test-allow-type-array-v0--v1-report-2.txt |   3 +
 .../test-allow-type-array-v0--v2-report-1.txt |  20 +
 .../test-allow-type-array-v0--v2-report-2.txt |  15 +
 .../test-allow-type-array-v0--v3-report-1.txt |  23 +
 .../test-allow-type-array-v0--v3-report-2.txt |   3 +
 .../test-allow-type-array-v0.c                |  18 +
 .../test-allow-type-array-v0.o                | Bin 0 -> 3392 bytes
 .../test-allow-type-array-v1.c                |  20 +
 .../test-allow-type-array-v1.o                | Bin 0 -> 3448 bytes
 .../test-allow-type-array-v2.c                |  20 +
 .../test-allow-type-array-v2.o                | Bin 0 -> 3456 bytes
 .../test-allow-type-array-v3.c                |  20 +
 .../test-allow-type-array-v3.o                | Bin 0 -> 3456 bytes
 .../test-allow-type-region-suppr.txt          |  11 +
 ...test-allow-type-region-v0--v1-report-1.txt |  20 +
 ...test-allow-type-region-v0--v1-report-2.txt |   3 +
 ...test-allow-type-region-v0--v2-report-1.txt |  21 +
 ...test-allow-type-region-v0--v2-report-2.txt |  16 +
 ...test-allow-type-region-v0--v3-report-1.txt |  23 +
 ...test-allow-type-region-v0--v3-report-2.txt |  18 +
 ...test-allow-type-region-v0--v4-report-1.txt |  28 +
 ...test-allow-type-region-v0--v4-report-2.txt |   3 +
 ...test-allow-type-region-v0--v5-report-1.txt |  30 +
 ...test-allow-type-region-v0--v5-report-2.txt |  25 +
 .../test-allow-type-region-v0.c               |  22 +
 .../test-allow-type-region-v0.o               | Bin 0 -> 3576 bytes
 .../test-allow-type-region-v1.c               |  23 +
 .../test-allow-type-region-v1.o               | Bin 0 -> 3632 bytes
 .../test-allow-type-region-v2.c               |  23 +
 .../test-allow-type-region-v2.o               | Bin 0 -> 3632 bytes
 .../test-allow-type-region-v3.c               |  24 +
 .../test-allow-type-region-v3.o               | Bin 0 -> 3688 bytes
 .../test-allow-type-region-v4.c               |  23 +
 .../test-allow-type-region-v4.o               | Bin 0 -> 3640 bytes
 .../test-allow-type-region-v5.c               |  24 +
 .../test-allow-type-region-v5.o               | Bin 0 -> 3696 bytes
 .../test-allow-type-suppr1.txt                |   7 +
 .../test-allow-type-suppr2.txt                |   7 +
 .../test-diff-suppr/has-data-member-1.suppr   |   3 +
 .../test-diff-suppr/has-data-member-2.suppr   |   3 +
 .../test-diff-suppr/has-data-member-3.suppr   |   3 +
 .../test-diff-suppr/has-data-member-4.suppr   |   3 +
 .../test-diff-suppr/has-data-member-5.suppr   |   3 +
 .../test-diff-suppr/has-data-member-6.suppr   |   3 +
 .../test-diff-suppr/has-data-member-7.suppr   |   3 +
 ...ata-member-inserted-between-1-report-1.txt |   3 +
 ...ata-member-inserted-between-1-report-2.txt |  13 +
 ...ata-member-inserted-between-1-report-3.txt |  12 +
 ...ata-member-inserted-between-1-report-4.txt |  11 +
 ...st-has-data-member-inserted-between-1-v0.c |  10 +
 ...st-has-data-member-inserted-between-1-v0.o | Bin 0 -> 3088 bytes
 ...st-has-data-member-inserted-between-1-v1.c |  10 +
 ...st-has-data-member-inserted-between-1-v1.o | Bin 0 -> 3088 bytes
 ...st-has-data-member-inserted-between-1-v2.c |  11 +
 ...st-has-data-member-inserted-between-1-v2.o | Bin 0 -> 3152 bytes
 ...st-has-data-member-inserted-between-1-v3.c |  11 +
 ...st-has-data-member-inserted-between-1-v3.o | Bin 0 -> 3152 bytes
 ...st-has-data-member-inserted-between-1-v4.c |  10 +
 ...st-has-data-member-inserted-between-1-v4.o | Bin 0 -> 3152 bytes
 ...t-has-data-member-inserted-between-1.suppr |   7 +
 .../test-has-data-member-output-1.txt         |  19 +
 .../test-has-data-member-output-2.txt         |  12 +
 .../test-has-data-member-v0.cc                |  22 +
 .../test-diff-suppr/test-has-data-member-v0.o | Bin 0 -> 3608 bytes
 .../test-has-data-member-v1.cc                |  24 +
 .../test-diff-suppr/test-has-data-member-v1.o | Bin 0 -> 3728 bytes
 .../test11-add-data-member-0.1.suppr          |   3 +
 .../test11-add-data-member-0.suppr            |   1 +
 .../test11-add-data-member-1.1.suppr          |   3 +
 .../test11-add-data-member-1.suppr            |   1 +
 .../test11-add-data-member-2.1.suppr          |   3 +
 .../test11-add-data-member-2.suppr            |   1 +
 .../test11-add-data-member-3.1.suppr          |   3 +
 .../test11-add-data-member-3.suppr            |   1 +
 .../test11-add-data-member-4.1.suppr          |   3 +
 .../test11-add-data-member-4.suppr            |   1 +
 .../test11-add-data-member-report-1.1.txt     |  13 +
 .../test12-add-data-member-0.1.suppr          |   3 +
 .../test12-add-data-member-0.suppr            |   1 +
 .../test12-add-data-member-report-1.1.txt     |  13 +
 .../test13-suppr-through-pointer-0.1.suppr    |   4 +
 .../test13-suppr-through-pointer-0.suppr      |   1 +
 ...est13-suppr-through-pointer-report-1.1.txt |  16 +
 .../test35-leaf-report-0.1.txt                |  18 +
 .../data/test-diff-suppr/test35-leaf.1.suppr  |   8 +
 tests/data/test-diff-suppr/test35-leaf.suppr  |   1 +
 tests/test-abidiff-exit.cc                    | 187 ++++++
 tests/test-diff-suppr.cc                      | 202 +++++-
 tools/abidiff.cc                              | 189 +++++-
 tools/kmidiff.cc                              |   8 +
 114 files changed, 3176 insertions(+), 391 deletions(-)
 create mode 100644 tests/data/test-abidiff-exit/test-allow-type-array-suppr.txt
 create mode 100644 tests/data/test-abidiff-exit/test-allow-type-array-v0--v1-report-1.txt
 create mode 100644 tests/data/test-abidiff-exit/test-allow-type-array-v0--v1-report-2.txt
 create mode 100644 tests/data/test-abidiff-exit/test-allow-type-array-v0--v2-report-1.txt
 create mode 100644 tests/data/test-abidiff-exit/test-allow-type-array-v0--v2-report-2.txt
 create mode 100644 tests/data/test-abidiff-exit/test-allow-type-array-v0--v3-report-1.txt
 create mode 100644 tests/data/test-abidiff-exit/test-allow-type-array-v0--v3-report-2.txt
 create mode 100644 tests/data/test-abidiff-exit/test-allow-type-array-v0.c
 create mode 100644 tests/data/test-abidiff-exit/test-allow-type-array-v0.o
 create mode 100644 tests/data/test-abidiff-exit/test-allow-type-array-v1.c
 create mode 100644 tests/data/test-abidiff-exit/test-allow-type-array-v1.o
 create mode 100644 tests/data/test-abidiff-exit/test-allow-type-array-v2.c
 create mode 100644 tests/data/test-abidiff-exit/test-allow-type-array-v2.o
 create mode 100644 tests/data/test-abidiff-exit/test-allow-type-array-v3.c
 create mode 100644 tests/data/test-abidiff-exit/test-allow-type-array-v3.o
 create mode 100644 tests/data/test-abidiff-exit/test-allow-type-region-suppr.txt
 create mode 100644 tests/data/test-abidiff-exit/test-allow-type-region-v0--v1-report-1.txt
 create mode 100644 tests/data/test-abidiff-exit/test-allow-type-region-v0--v1-report-2.txt
 create mode 100644 tests/data/test-abidiff-exit/test-allow-type-region-v0--v2-report-1.txt
 create mode 100644 tests/data/test-abidiff-exit/test-allow-type-region-v0--v2-report-2.txt
 create mode 100644 tests/data/test-abidiff-exit/test-allow-type-region-v0--v3-report-1.txt
 create mode 100644 tests/data/test-abidiff-exit/test-allow-type-region-v0--v3-report-2.txt
 create mode 100644 tests/data/test-abidiff-exit/test-allow-type-region-v0--v4-report-1.txt
 create mode 100644 tests/data/test-abidiff-exit/test-allow-type-region-v0--v4-report-2.txt
 create mode 100644 tests/data/test-abidiff-exit/test-allow-type-region-v0--v5-report-1.txt
 create mode 100644 tests/data/test-abidiff-exit/test-allow-type-region-v0--v5-report-2.txt
 create mode 100644 tests/data/test-abidiff-exit/test-allow-type-region-v0.c
 create mode 100644 tests/data/test-abidiff-exit/test-allow-type-region-v0.o
 create mode 100644 tests/data/test-abidiff-exit/test-allow-type-region-v1.c
 create mode 100644 tests/data/test-abidiff-exit/test-allow-type-region-v1.o
 create mode 100644 tests/data/test-abidiff-exit/test-allow-type-region-v2.c
 create mode 100644 tests/data/test-abidiff-exit/test-allow-type-region-v2.o
 create mode 100644 tests/data/test-abidiff-exit/test-allow-type-region-v3.c
 create mode 100644 tests/data/test-abidiff-exit/test-allow-type-region-v3.o
 create mode 100644 tests/data/test-abidiff-exit/test-allow-type-region-v4.c
 create mode 100644 tests/data/test-abidiff-exit/test-allow-type-region-v4.o
 create mode 100644 tests/data/test-abidiff-exit/test-allow-type-region-v5.c
 create mode 100644 tests/data/test-abidiff-exit/test-allow-type-region-v5.o
 create mode 100644 tests/data/test-abidiff-exit/test-allow-type-suppr1.txt
 create mode 100644 tests/data/test-abidiff-exit/test-allow-type-suppr2.txt
 create mode 100644 tests/data/test-diff-suppr/has-data-member-1.suppr
 create mode 100644 tests/data/test-diff-suppr/has-data-member-2.suppr
 create mode 100644 tests/data/test-diff-suppr/has-data-member-3.suppr
 create mode 100644 tests/data/test-diff-suppr/has-data-member-4.suppr
 create mode 100644 tests/data/test-diff-suppr/has-data-member-5.suppr
 create mode 100644 tests/data/test-diff-suppr/has-data-member-6.suppr
 create mode 100644 tests/data/test-diff-suppr/has-data-member-7.suppr
 create mode 100644 tests/data/test-diff-suppr/test-has-data-member-inserted-between-1-report-1.txt
 create mode 100644 tests/data/test-diff-suppr/test-has-data-member-inserted-between-1-report-2.txt
 create mode 100644 tests/data/test-diff-suppr/test-has-data-member-inserted-between-1-report-3.txt
 create mode 100644 tests/data/test-diff-suppr/test-has-data-member-inserted-between-1-report-4.txt
 create mode 100644 tests/data/test-diff-suppr/test-has-data-member-inserted-between-1-v0.c
 create mode 100644 tests/data/test-diff-suppr/test-has-data-member-inserted-between-1-v0.o
 create mode 100644 tests/data/test-diff-suppr/test-has-data-member-inserted-between-1-v1.c
 create mode 100644 tests/data/test-diff-suppr/test-has-data-member-inserted-between-1-v1.o
 create mode 100644 tests/data/test-diff-suppr/test-has-data-member-inserted-between-1-v2.c
 create mode 100644 tests/data/test-diff-suppr/test-has-data-member-inserted-between-1-v2.o
 create mode 100644 tests/data/test-diff-suppr/test-has-data-member-inserted-between-1-v3.c
 create mode 100644 tests/data/test-diff-suppr/test-has-data-member-inserted-between-1-v3.o
 create mode 100644 tests/data/test-diff-suppr/test-has-data-member-inserted-between-1-v4.c
 create mode 100644 tests/data/test-diff-suppr/test-has-data-member-inserted-between-1-v4.o
 create mode 100644 tests/data/test-diff-suppr/test-has-data-member-inserted-between-1.suppr
 create mode 100644 tests/data/test-diff-suppr/test-has-data-member-output-1.txt
 create mode 100644 tests/data/test-diff-suppr/test-has-data-member-output-2.txt
 create mode 100644 tests/data/test-diff-suppr/test-has-data-member-v0.cc
 create mode 100644 tests/data/test-diff-suppr/test-has-data-member-v0.o
 create mode 100644 tests/data/test-diff-suppr/test-has-data-member-v1.cc
 create mode 100644 tests/data/test-diff-suppr/test-has-data-member-v1.o
 create mode 100644 tests/data/test-diff-suppr/test11-add-data-member-0.1.suppr
 create mode 100644 tests/data/test-diff-suppr/test11-add-data-member-1.1.suppr
 create mode 100644 tests/data/test-diff-suppr/test11-add-data-member-2.1.suppr
 create mode 100644 tests/data/test-diff-suppr/test11-add-data-member-3.1.suppr
 create mode 100644 tests/data/test-diff-suppr/test11-add-data-member-4.1.suppr
 create mode 100644 tests/data/test-diff-suppr/test11-add-data-member-report-1.1.txt
 create mode 100644 tests/data/test-diff-suppr/test12-add-data-member-0.1.suppr
 create mode 100644 tests/data/test-diff-suppr/test12-add-data-member-report-1.1.txt
 create mode 100644 tests/data/test-diff-suppr/test13-suppr-through-pointer-0.1.suppr
 create mode 100644 tests/data/test-diff-suppr/test13-suppr-through-pointer-report-1.1.txt
 create mode 100644 tests/data/test-diff-suppr/test35-leaf-report-0.1.txt
 create mode 100644 tests/data/test-diff-suppr/test35-leaf.1.suppr

-- 
2.39.2



-- 
		Dodji


^ permalink raw reply	[flat|nested] 14+ messages in thread

* [PATCH 01/13] ini: Fix parsing list property values
  2023-03-02 18:53 ` [PATCH 00/13] Support negative suppression specifications Dodji Seketeli
@ 2023-03-02 18:55   ` Dodji Seketeli
  2023-03-02 18:56   ` [PATCH 02/13] suppr: Support has_data_member and has_data_member_regexp properties Dodji Seketeli
                     ` (11 subsequent siblings)
  12 siblings, 0 replies; 14+ messages in thread
From: Dodji Seketeli @ 2023-03-02 18:55 UTC (permalink / raw)
  To: libabigail; +Cc: dodji, ckalina

Hello,

While looking at something else, I came across an issue in
read_context::read_list_property_value.  This function requires
elements of a list property value (separated by a comma) to be on a
single line, for instance.  This is because the code forgets to parse
white spaces after the comma.

Fixed thus.

	* src/abg-ini.cc (read_context::read_list_property_value): Expect
	white spaces after the comma.

Signed-off-by: Dodji Seketeli <dodji@redhat.com>
---
 src/abg-ini.cc | 1 +
 1 file changed, 1 insertion(+)

diff --git a/src/abg-ini.cc b/src/abg-ini.cc
index 227e3c84..db041962 100644
--- a/src/abg-ini.cc
+++ b/src/abg-ini.cc
@@ -1440,6 +1440,7 @@ public:
 	char c = 0;
 	read_next_char(c);
 	ABG_ASSERT(c == ',');
+	skip_white_spaces();
       }
 
     if (!content.empty())
-- 
2.39.2


^ permalink raw reply	[flat|nested] 14+ messages in thread

* [PATCH 02/13] suppr: Support has_data_member and has_data_member_regexp properties
  2023-03-02 18:53 ` [PATCH 00/13] Support negative suppression specifications Dodji Seketeli
  2023-03-02 18:55   ` [PATCH 01/13] ini: Fix parsing list property values Dodji Seketeli
@ 2023-03-02 18:56   ` Dodji Seketeli
  2023-03-02 18:57   ` [PATCH 03/13] suppression: Factorize out is_data_member_offset_in_range Dodji Seketeli
                     ` (10 subsequent siblings)
  12 siblings, 0 replies; 14+ messages in thread
From: Dodji Seketeli @ 2023-03-02 18:56 UTC (permalink / raw)
  To: Dodji Seketeli; +Cc: libabigail, ckalina

Hello,

In the [supress_type] directive, this patch adds support for two new
properties:

* has_data_data_member = {foo, bar, blah}
  Suppresses change reports involving a type which has data members
  with names specified by the value of this property.

* has_data_member_regexp = some-regexp
  Suppresses change reports involving a type which has data members
  with names specified by the regular expression given as a value of
  this property.

	* include/abg-fwd.h (string_set_type): Define new typedef.
	* src/abg-suppression-priv.h
	* include/abg-suppression.h
	(type_suppression::{get,set}_potential_data_member_names[_regex_str]):
	Declare new data member.
	(type_suppression::priv::{potential_data_members_,
	potential_data_members_regex_str_,
	potential_data_members_regex_}): Define new data members.
	(type_suppression::priv::{get,set}_potential_data_member_names_regex):
	Define new member functions.
	* src/abg-suppression.cc
	(type_suppression::{get,set}_potential_data_member_names): Define new
	member functions.
	(type_suppression::{get,set}_potential_data_member_names_regex_str):
	Likewise.
	(type_suppression::suppresses_diff): Implement suppression using
	the new "has_data_member" and "has_data_member_regexp" properties.
	(read_type_suppression): Support parsing the new "has_data_member"
	and "has_data_member_regexp" properties of the type suppression
	directive.
	* tests/data/test-diff-suppr/has-data-member-[1-7].suppr: New
	suppression specifications for test purposes.
	* tests/data/test-diff-suppr/test-has-data-member-output-{1,2}.txt:
	New reference test outputs.
	* tests/data/test-diff-suppr/test-has-data-member-v{0,1}.cc:
	Source code of new input binary tests.
	* tests/data/test-diff-suppr/test-has-data-member-v{0,1}.o: New
	binary test inputs.
	* tests/data/Makefile.am: Add the test inputs below to source
	distribution.
	* tests/test-diff-suppr.cc (in_out_specs): Add the new test inputs
	above to this test harness.

Signed-off-by: Dodji Seketeli <dodji@redhat.com>
---
 doc/manuals/libabigail-concepts.rst           |  48 +++++++
 include/abg-fwd.h                             |   4 +
 include/abg-suppression.h                     |  12 ++
 src/abg-suppression-priv.h                    |  37 +++++
 src/abg-suppression.cc                        | 126 ++++++++++++++++++
 tests/data/Makefile.am                        |  13 ++
 .../test-diff-suppr/has-data-member-1.suppr   |   3 +
 .../test-diff-suppr/has-data-member-2.suppr   |   3 +
 .../test-diff-suppr/has-data-member-3.suppr   |   3 +
 .../test-diff-suppr/has-data-member-4.suppr   |   3 +
 .../test-diff-suppr/has-data-member-5.suppr   |   3 +
 .../test-diff-suppr/has-data-member-6.suppr   |   3 +
 .../test-diff-suppr/has-data-member-7.suppr   |   3 +
 .../test-has-data-member-output-1.txt         |  19 +++
 .../test-has-data-member-output-2.txt         |  12 ++
 .../test-has-data-member-v0.cc                |  22 +++
 .../test-diff-suppr/test-has-data-member-v0.o | Bin 0 -> 3608 bytes
 .../test-has-data-member-v1.cc                |  24 ++++
 .../test-diff-suppr/test-has-data-member-v1.o | Bin 0 -> 3728 bytes
 tests/test-diff-suppr.cc                      |  80 +++++++++++
 20 files changed, 418 insertions(+)
 create mode 100644 tests/data/test-diff-suppr/has-data-member-1.suppr
 create mode 100644 tests/data/test-diff-suppr/has-data-member-2.suppr
 create mode 100644 tests/data/test-diff-suppr/has-data-member-3.suppr
 create mode 100644 tests/data/test-diff-suppr/has-data-member-4.suppr
 create mode 100644 tests/data/test-diff-suppr/has-data-member-5.suppr
 create mode 100644 tests/data/test-diff-suppr/has-data-member-6.suppr
 create mode 100644 tests/data/test-diff-suppr/has-data-member-7.suppr
 create mode 100644 tests/data/test-diff-suppr/test-has-data-member-output-1.txt
 create mode 100644 tests/data/test-diff-suppr/test-has-data-member-output-2.txt
 create mode 100644 tests/data/test-diff-suppr/test-has-data-member-v0.cc
 create mode 100644 tests/data/test-diff-suppr/test-has-data-member-v0.o
 create mode 100644 tests/data/test-diff-suppr/test-has-data-member-v1.cc
 create mode 100644 tests/data/test-diff-suppr/test-has-data-member-v1.o

diff --git a/doc/manuals/libabigail-concepts.rst b/doc/manuals/libabigail-concepts.rst
index c30e87e8..2824b216 100644
--- a/doc/manuals/libabigail-concepts.rst
+++ b/doc/manuals/libabigail-concepts.rst
@@ -423,6 +423,54 @@ The potential properties of this sections are listed below:
  defined in header files whose path match the regular expression
  provided a value of the property.
 
+ .. _suppr_has_data_member_label:
+
+* ``has_data_member``
+
+  Usage:
+
+    ``has_data_member`` ``=`` <``list-of-data-member-names``>
+
+Suppresses change reports involving a type which contains data members
+whose names are provided in the list value of this property.
+
+A usage examples of this property would be: ::
+
+  has_data_member = {private_data_member0, private_data_member1}
+
+
+The property above would match any type which contains at least two
+data members whose names are ``private_data_member0`` and
+``private_data_member1``.
+
+Another usage examples of this property would be: ::
+
+  has_data_member = another_private_data_member
+
+The property above would match any type which contains
+a data member which name is ``another_private_data_member0``.
+
+ .. _suppr_has_data_member_regexp_label:
+
+* ``has_data_member_regexp``
+
+  Usage:
+
+    ``has_data_member_regexp`` ``=`` <``a-regular-expression``>
+
+Suppresses change reports involving a type which contains data members
+whose names match the regular expression provided as the value of this
+property.
+
+A usage examples of this property would be: ::
+
+  has_data_member_regexp = ^private_data_member
+
+The property above would match any type which contains data members
+whose names match the regular expression ``^private_data_member``.  In
+other words, it would match any type which contains data members whose
+names start with the string "private_data_member".
+
  .. _suppr_has_data_member_inserted_at_label:
 
 * ``has_data_member_inserted_at``
diff --git a/include/abg-fwd.h b/include/abg-fwd.h
index 96aeab54..155642f0 100644
--- a/include/abg-fwd.h
+++ b/include/abg-fwd.h
@@ -17,6 +17,7 @@
 #include <string>
 #include <typeinfo>
 #include <unordered_map>
+#include <unordered_set>
 #include <utility> // for std::rel_ops, at least.
 #include <vector>
 #include "abg-interned-str.h"
@@ -54,6 +55,9 @@ using std::weak_ptr;
 using std::unordered_map;
 using std::string;
 using std::vector;
+using std::unordered_set;
+
+typedef unordered_set<string> string_set_type;
 
 // Pull in relational operators.
 using namespace std::rel_ops;
diff --git a/include/abg-suppression.h b/include/abg-suppression.h
index 8e22e122..27f52110 100644
--- a/include/abg-suppression.h
+++ b/include/abg-suppression.h
@@ -243,6 +243,18 @@ public:
   void
   set_reach_kind(reach_kind k);
 
+  const string_set_type&
+  get_potential_data_member_names() const;
+
+  void
+  set_potential_data_member_names(const string_set_type&) const;
+
+  const string&
+  get_potential_data_member_names_regex_str() const;
+
+  void
+  set_potential_data_member_names_regex_str(const string&) const;
+
   void
   set_data_member_insertion_ranges(const insertion_ranges& r);
 
diff --git a/src/abg-suppression-priv.h b/src/abg-suppression-priv.h
index 4b74096d..5fc1ec52 100644
--- a/src/abg-suppression-priv.h
+++ b/src/abg-suppression-priv.h
@@ -570,6 +570,15 @@ class type_suppression::priv
   type_suppression::type_kind		type_kind_;
   bool					consider_reach_kind_;
   type_suppression::reach_kind		reach_kind_;
+  // The data members a class needs to have to match this suppression
+  // specification.  These might be selected by a regular expression.
+  string_set_type			potential_data_members_;
+  // The regular expression string that selects the potential data
+  // members of the class.
+  string				potential_data_members_regex_str_;
+  // The compiled regular expression that selects the potential data
+  // members of the class.
+  mutable regex::regex_t_sptr		potential_data_members_regex_;
   type_suppression::insertion_ranges	insertion_ranges_;
   unordered_set<string>			source_locations_to_keep_;
   string				source_location_to_keep_regex_str_;
@@ -677,6 +686,34 @@ public:
   set_source_location_to_keep_regex(regex::regex_t_sptr r)
   {source_location_to_keep_regex_ = r;}
 
+  /// Getter for the "potential_data_member_names_regex" object.
+  ///
+  /// This regex object matches the names of the data members that are
+  /// needed for this suppression specification to select the type.
+  ///
+  /// @return the "potential_data_member_names_regex" object.
+  const regex::regex_t_sptr
+  get_potential_data_member_names_regex() const
+  {
+    if (!potential_data_members_regex_
+	&& !potential_data_members_regex_str_.empty())
+      {
+	potential_data_members_regex_ =
+	  regex::compile(potential_data_members_regex_str_);
+      }
+    return potential_data_members_regex_;
+  }
+
+  /// Setter for the "potential_data_member_names_regex" object.
+  ///
+  /// This regex object matches the names of the data members that are
+  /// needed for this suppression specification to select the type.
+  ///
+  /// @param r the new "potential_data_member_names_regex" object.
+  void
+  set_potential_data_member_names_regex(regex::regex_t_sptr &r)
+  {potential_data_members_regex_ = r;}
+
   friend class type_suppression;
 }; // class type_suppression::priv
 
diff --git a/src/abg-suppression.cc b/src/abg-suppression.cc
index 059c3909..5fcdef97 100644
--- a/src/abg-suppression.cc
+++ b/src/abg-suppression.cc
@@ -569,6 +569,38 @@ void
 type_suppression::set_reach_kind(reach_kind k)
 {priv_->reach_kind_ = k;}
 
+/// Getter of the "potential_data_member_names" property.
+///
+/// @return the set of potential data member names of this
+/// suppression.
+const unordered_set<string>&
+type_suppression::get_potential_data_member_names() const
+{return priv_->potential_data_members_;}
+
+/// Setter of the "potential_data_member_names" property.
+///
+/// @param s the new set of potential data member names of this
+/// suppression.
+void
+type_suppression::set_potential_data_member_names
+(const string_set_type& s) const
+{priv_->potential_data_members_ = s;}
+
+/// Getter of the "potential_data_member_names_regex" string.
+///
+/// @return the "potential_data_member_names_regex" string.
+const string&
+type_suppression::get_potential_data_member_names_regex_str() const
+{return priv_->potential_data_members_regex_str_;}
+
+/// Setter of the "potential_data_member_names_regex" string.
+///
+/// @param d the new "potential_data_member_names_regex" string.
+void
+type_suppression::set_potential_data_member_names_regex_str
+(const string& d) const
+{priv_->potential_data_members_regex_str_ = d;}
+
 /// Setter for the vector of data member insertion ranges that
 /// specifies where a data member is inserted as far as this
 /// suppression specification is concerned.
@@ -778,6 +810,43 @@ type_suppression::suppresses_diff(const diff* diff) const
   // Now let's consider class diffs in the context of a suppr spec
   // that contains properties like "has_data_member_inserted_*".
 
+  const class_or_union_diff* cou_diff = is_class_or_union_diff(d);
+  if (cou_diff)
+    {
+      class_or_union_sptr f = cou_diff->first_class_or_union();
+      // We are looking at the a class or union diff ...
+      if (!get_potential_data_member_names().empty())
+	{
+	  // ... and the suppr spec has a:
+	  //
+	  //    "has_data_member = {foo, bar}" property
+	  //
+	  for (string var_name : get_potential_data_member_names())
+	    if (!f->find_data_member(var_name))
+	      return false;
+	}
+
+      if (!get_potential_data_member_names_regex_str().empty())
+	{
+	  if (const regex_t_sptr& data_member_name_regex =
+	      priv_->get_potential_data_member_names_regex())
+	    {
+	      bool data_member_matched = false;
+	      for (var_decl_sptr dm : f->get_data_members())
+		{
+		  if (regex::match(data_member_name_regex, dm->get_name()))
+		    {
+		      data_member_matched = true;
+		      break;
+		    }
+		}
+	      if (!data_member_matched)
+		return false;
+	    }
+	}
+    }
+
+  // Evaluate has_data_member_inserted_*" clauses.
   const class_diff* klass_diff = dynamic_cast<const class_diff*>(d);
   if (klass_diff)
     {
@@ -1720,6 +1789,56 @@ read_type_suppression(const ini::config::section& section)
 	read_suppression_reach_kind(reach_kind_prop->get_value()->as_string());
     }
 
+  // Support has_data_member = {}
+  string_set_type potential_data_member_names;
+  if (ini::property_sptr propertee = section.find_property("has_data_member"))
+    {
+      // This is either has_data_member = {foo, blah} or
+      // has_data_member = foo.
+      ini::tuple_property_value_sptr tv;
+      ini::string_property_value_sptr sv;
+      if (ini::tuple_property_sptr prop = is_tuple_property(propertee))
+	// Value is of the form {foo,blah}
+	tv = prop->get_value();
+      else if (ini::simple_property_sptr prop = is_simple_property(propertee))
+	// Value is of the form foo.
+	sv = prop->get_value();
+
+      // Ensure that the property value has the form {"foo", "blah", ...};
+      // Meaning it's a tuple of one element which is a list or a string.
+      if (tv
+	  && tv->get_value_items().size() == 1
+	  && (is_list_property_value(tv->get_value_items().front())
+	      || is_string_property_value(tv->get_value_items().front())))
+	{
+	  ini::list_property_value_sptr val =
+	    is_list_property_value(tv->get_value_items().front());
+	  if (!val)
+	    {
+	      // We have just one potential data member name,as a
+	      // string_property_value.
+	      string name =
+		is_string_property_value(tv->get_value_items().front())
+		->as_string();
+	      potential_data_member_names.insert(name);
+	    }
+	  else
+	    for (const string& name : val->get_content())
+	      potential_data_member_names.insert(name);
+	}
+      else if (sv)
+	{
+	  string name = sv->as_string();
+	  potential_data_member_names.insert(name);
+	}
+    }
+
+  // Support has_data_member_regexp = str
+  string potential_data_member_names_regexp_str;
+  if (ini::simple_property_sptr prop =
+      is_simple_property(section.find_property("has_data_member_regexp")))
+      potential_data_member_names_regexp_str = prop->get_value()->as_string();
+
   // Support has_data_member_inserted_at
   vector<type_suppression::insertion_range_sptr> insert_ranges;
   bool consider_data_member_insertion = false;
@@ -1919,6 +2038,13 @@ read_type_suppression(const ini::config::section& section)
       result->set_reach_kind(reach_kind);
     }
 
+  if (!potential_data_member_names.empty())
+    result->set_potential_data_member_names(potential_data_member_names);
+
+  if (!potential_data_member_names_regexp_str.empty())
+    result->set_potential_data_member_names_regex_str
+      (potential_data_member_names_regexp_str);
+
   if (consider_data_member_insertion)
     result->set_data_member_insertion_ranges(insert_ranges);
 
diff --git a/tests/data/Makefile.am b/tests/data/Makefile.am
index 1a0a3fa4..57abf7a9 100644
--- a/tests/data/Makefile.am
+++ b/tests/data/Makefile.am
@@ -1740,6 +1740,19 @@ test-diff-suppr/PR28073/PR28073.before.o \
 test-diff-suppr/PR28073/PR28073.before.o.abi \
 test-diff-suppr/PR28073/PR28073.c \
 test-diff-suppr/PR28073/bitfield.suppr \
+test-diff-suppr/has-data-member-1.suppr \
+test-diff-suppr/has-data-member-2.suppr \
+test-diff-suppr/has-data-member-3.suppr \
+test-diff-suppr/has-data-member-4.suppr \
+test-diff-suppr/has-data-member-5.suppr \
+test-diff-suppr/has-data-member-6.suppr \
+test-diff-suppr/has-data-member-7.suppr \
+test-diff-suppr/test-has-data-member-output-1.txt \
+test-diff-suppr/test-has-data-member-output-2.txt \
+test-diff-suppr/test-has-data-member-v0.cc \
+test-diff-suppr/test-has-data-member-v0.o \
+test-diff-suppr/test-has-data-member-v1.cc \
+test-diff-suppr/test-has-data-member-v1.o \
 \
 test-diff-dwarf-abixml/test0-pr19026-libvtkIOSQL-6.1.so.1 \
 test-diff-dwarf-abixml/test0-pr19026-libvtkIOSQL-6.1.so.1.abi \
diff --git a/tests/data/test-diff-suppr/has-data-member-1.suppr b/tests/data/test-diff-suppr/has-data-member-1.suppr
new file mode 100644
index 00000000..37c0d1e9
--- /dev/null
+++ b/tests/data/test-diff-suppr/has-data-member-1.suppr
@@ -0,0 +1,3 @@
+[suppress_type]
+  type_kind = struct
+  has_data_member = {private_data_member0, private_data_member1}
diff --git a/tests/data/test-diff-suppr/has-data-member-2.suppr b/tests/data/test-diff-suppr/has-data-member-2.suppr
new file mode 100644
index 00000000..909fd01d
--- /dev/null
+++ b/tests/data/test-diff-suppr/has-data-member-2.suppr
@@ -0,0 +1,3 @@
+[suppress_type]
+  type_kind = struct
+  has_data_member = {private_data_member0}
diff --git a/tests/data/test-diff-suppr/has-data-member-3.suppr b/tests/data/test-diff-suppr/has-data-member-3.suppr
new file mode 100644
index 00000000..43823d79
--- /dev/null
+++ b/tests/data/test-diff-suppr/has-data-member-3.suppr
@@ -0,0 +1,3 @@
+[suppress_type]
+  type_kind = struct
+  has_data_member = private_data_member1
diff --git a/tests/data/test-diff-suppr/has-data-member-4.suppr b/tests/data/test-diff-suppr/has-data-member-4.suppr
new file mode 100644
index 00000000..779a39f0
--- /dev/null
+++ b/tests/data/test-diff-suppr/has-data-member-4.suppr
@@ -0,0 +1,3 @@
+[suppress_type]
+  type_kind = struct
+  has_data_member = {private_data_member3, private_data_member4}
diff --git a/tests/data/test-diff-suppr/has-data-member-5.suppr b/tests/data/test-diff-suppr/has-data-member-5.suppr
new file mode 100644
index 00000000..6ab80758
--- /dev/null
+++ b/tests/data/test-diff-suppr/has-data-member-5.suppr
@@ -0,0 +1,3 @@
+[suppress_type]
+  type_kind = struct
+  has_data_member = private_data_member5
diff --git a/tests/data/test-diff-suppr/has-data-member-6.suppr b/tests/data/test-diff-suppr/has-data-member-6.suppr
new file mode 100644
index 00000000..7bd34159
--- /dev/null
+++ b/tests/data/test-diff-suppr/has-data-member-6.suppr
@@ -0,0 +1,3 @@
+[suppress_type]
+  type_kind = struct
+  has_data_member_regexp = ^private_data_member
diff --git a/tests/data/test-diff-suppr/has-data-member-7.suppr b/tests/data/test-diff-suppr/has-data-member-7.suppr
new file mode 100644
index 00000000..03c9d99b
--- /dev/null
+++ b/tests/data/test-diff-suppr/has-data-member-7.suppr
@@ -0,0 +1,3 @@
+[suppress_type]
+  type_kind = struct
+  has_data_member_regexp = ^private_data_memberWRONG
diff --git a/tests/data/test-diff-suppr/test-has-data-member-output-1.txt b/tests/data/test-diff-suppr/test-has-data-member-output-1.txt
new file mode 100644
index 00000000..2212998a
--- /dev/null
+++ b/tests/data/test-diff-suppr/test-has-data-member-output-1.txt
@@ -0,0 +1,19 @@
+Functions changes summary: 0 Removed, 2 Changed, 0 Added functions
+Variables changes summary: 0 Removed, 0 Changed, 0 Added variable
+
+2 functions with some indirect sub-type change:
+
+  [C] 'function int bar(S1*)' at test-has-data-member-v0.cc:19:1 has some indirect sub-type changes:
+    parameter 1 of type 'S1*' has sub-type changes:
+      in pointed to type 'struct S1' at test-has-data-member-v1.cc:8:1:
+        type size changed from 32 to 64 (in bits)
+        1 data member insertion:
+          'char non_suppressed_added_member', at offset 32 (in bits) at test-has-data-member-v1.cc:11:1
+
+  [C] 'function void foo(S0&)' at test-has-data-member-v0.cc:14:1 has some indirect sub-type changes:
+    parameter 1 of type 'S0&' has sub-type changes:
+      in referenced type 'struct S0' at test-has-data-member-v1.cc:1:1:
+        type size changed from 64 to 96 (in bits)
+        1 data member insertion:
+          'int suppressed_added_member', at offset 64 (in bits) at test-has-data-member-v1.cc:5:1
+
diff --git a/tests/data/test-diff-suppr/test-has-data-member-output-2.txt b/tests/data/test-diff-suppr/test-has-data-member-output-2.txt
new file mode 100644
index 00000000..ab0110c9
--- /dev/null
+++ b/tests/data/test-diff-suppr/test-has-data-member-output-2.txt
@@ -0,0 +1,12 @@
+Functions changes summary: 0 Removed, 1 Changed (1 filtered out), 0 Added functions
+Variables changes summary: 0 Removed, 0 Changed, 0 Added variable
+
+1 function with some indirect sub-type change:
+
+  [C] 'function int bar(S1*)' at test-has-data-member-v0.cc:19:1 has some indirect sub-type changes:
+    parameter 1 of type 'S1*' has sub-type changes:
+      in pointed to type 'struct S1' at test-has-data-member-v1.cc:8:1:
+        type size changed from 32 to 64 (in bits)
+        1 data member insertion:
+          'char non_suppressed_added_member', at offset 32 (in bits) at test-has-data-member-v1.cc:11:1
+
diff --git a/tests/data/test-diff-suppr/test-has-data-member-v0.cc b/tests/data/test-diff-suppr/test-has-data-member-v0.cc
new file mode 100644
index 00000000..554002c3
--- /dev/null
+++ b/tests/data/test-diff-suppr/test-has-data-member-v0.cc
@@ -0,0 +1,22 @@
+struct S0
+{
+  int private_data_member0;
+  char private_data_member1;
+};
+
+struct S1
+{
+  int m0;
+};
+
+
+void
+foo(S0&)
+{
+}
+
+int
+bar(S1* s)
+{
+  return s->m0;
+}
diff --git a/tests/data/test-diff-suppr/test-has-data-member-v0.o b/tests/data/test-diff-suppr/test-has-data-member-v0.o
new file mode 100644
index 0000000000000000000000000000000000000000..dfbebe4c76a0100982e6b62657392d081a5d40e6
GIT binary patch
literal 3608
zcmcIm&2Jk;6o2EjV>_ETPF+Dlkm6Jz`oXS0T1uLdVicuKR0M6JN)f1X*Y?CViS1~;
zF`<YLsZgoPB}j1Nz<~>#khpN*R{j7ajz}DkxFT_@!~x#ho#$-EZiGNQX=i@#_daIk
z&4>NL@~c-PLI8_^D{!bW3ow;D#3dh=;3Q<=*2;t5Rvx_f=-~E4hAaEakM`mA!(Y%X
z1|mV;1~qfkgk>hAi1j=q#7$%(#2Rse0gfg%5Jn*yjWydI#KZ`h#Cidu@gAa)Rvtt$
z2!MFSaY1DKHTWsW7bP8ZAU^*ZQKE}JNkWlN8jwts5shwkIv`R<R&VyBenw<cLjhw6
zn+!ONkuc4LIdR#%G?x$=w2p(33J7yGGmr7cq<A)!F^$=o*-3FCo_-RS@(h^f71O}d
z#1u^7GB7l;U}7p28MnpD(@eEfs~L(fP2HWQffBKhcVr46Ie|%6(K328EF5IUU^PR|
zV`=3K&(o4<G2m~MkEjXnO`k;YI({+qJ&U~%;?LALbyPI`5X}1Zi-_6CM35FO7$G3p
zSB;doSBj0_i-~$9f-&Vc&f})tDB_0hxXqs9$#Tu{oN`OHD$>mlBMWftwOiSxbLR>#
zW(x)Tyj{o^^TlGJP%LCmugO|=#qpHEx^O0IwY=T7T&&BsbemOF9k<$8+?&5>UA$oR
zidm};I0@YUwIUwFY6FjuC%c~2aJm*v#R{fk_40PL3b{t7C3Ce-?X706-t=<Y&5BcL
z)}7{d&TV)Wo>A$xo$Xw$E4QR4iM6)S!*XR;x;>er+}#`{_Mug4Zf;uL-JKoxf6aZ&
zl8!OKwWXzn>}jm`nSW4ff48wYserh<24XKQX2#-)13Vf}BTpZRf4N>srSC_U#z(HA
z19D`0iHs=zm1X<&z#dozTRL@7_~WlZ3q3ymnH(?~>fq}JBY3WK8rkRoM}z_XgEU|>
zAU?na{-MV-<Y5FA2{8dQU~GUR!T@|VhKK<ZLs;@3AK<CpCqnh%2k$w>v(C@warYv0
zg+f<47?K~CQB=d#;u#T7Re2$B$2kQ;Jbf%rD8M{FQ%@<LcDn!2AU+%N=Q$8{Da6yb
zyv}8f&no>*jXzNO`x>uQxUT@uhx31@`G2hR?=^p=_G?A!jWLNm?+52+(<ueGK5r|X
zo_P}He+pD6sGj-Xm3~?Edj729sm9?tmo%Q&3wHONmgiKk_uQawF!JP{2R1!}U{|_b
zuwA+B*fm+%t(Tka%?{dp>{KeQ>>V*XuG6l|qaoYPw&V~Tf+5jmU$1srEs1|1>~_bK
zHa=)p*K?{{DzR*oH(jSC)k6MbN9U4i7e4EhvP2CEpW8qSd>K<ehabiJf1HR=!sn26
z?#VYSbbsl*|J!&v7kW<0&*T3}N=S5HI&bt^mO?%R8>%4lDqs>h^5eUS|4n>Z#8%Hq
zhDAmG;Cmf5y6+7%=^S|rb!hX4p+3@muOmBb{$Hpko#Sc#MCtRdA*bg=3JPlcby7m2
z`_doMVdMX(#=oOtX?%LW^znC)qntcXguZ;F#{Y~7)${n=zl;2^_-S>ZlI-}H`2EMk
zpQ1&f<btl>aysrmMGND;6M~y6KFkr;eZNwiT5jKcL&b0OnE-J8nEnz0xt5qveO@_o
zU8(-`Ys>L`erZ1X*p%WMHU5Wc!t^^wzv6m4?Wuluk%GLsKSfePqOYIkH7x#)8tB_l
IEI~c~Z@Q&JasU7T

literal 0
HcmV?d00001

diff --git a/tests/data/test-diff-suppr/test-has-data-member-v1.cc b/tests/data/test-diff-suppr/test-has-data-member-v1.cc
new file mode 100644
index 00000000..afedcf91
--- /dev/null
+++ b/tests/data/test-diff-suppr/test-has-data-member-v1.cc
@@ -0,0 +1,24 @@
+struct S0
+{
+  int private_data_member0;
+  char private_data_member1;
+  int suppressed_added_member;
+};
+
+struct S1
+{
+  int m0;
+  char non_suppressed_added_member;
+};
+
+
+void
+foo(S0&)
+{
+}
+
+int
+bar(S1* s)
+{
+  return s->m0;
+}
diff --git a/tests/data/test-diff-suppr/test-has-data-member-v1.o b/tests/data/test-diff-suppr/test-has-data-member-v1.o
new file mode 100644
index 0000000000000000000000000000000000000000..b5bfe1205553f779fd2855010b6ae4e631a198e3
GIT binary patch
literal 3728
zcmcIm-D_h-6hD*Ow70$eNH?ptR%j}U`{7NR?7HrD?HVcFZCTN^r65X)H_0@KZIW_x
zTkDD_E(l9ODu_>2eDGm?6MXX3^~E<qAN&V=6-1Dt4|>kMbJFP~EuwfJGr#jYA2W02
z<K~_D=dZ_v02Tq);80^0V6XoW=R=r>(~yLfg}Xm5+<p6CZ}mRIh28msU0A*UJG#X|
z*yL?dGewOq6PglRo`ylOicC1X)kiSGJ$4ykPsqnZ-iDr@c&!mYT=bG#Y&i?I{Q;uh
z`ZS0EE!c^Ip|9xqARY*lgsCYGB@uNX_P;@7x6r4bQ0&7h^xNx*2G*KQ5Q#8SIfJ8s
z*#8ZYLk$HC+G#R~A+UyLro>g}inEx!jLxy7ux!VPxG7sCu_UA7@!_OnO-@XXic@`w
zhj59Hf#Y0rEG$`E!C_nyX0vCU!(@GF@$5KLjp4;4#plOvk5gJZu6f7C0QyHT=^|PN
zj)v($P8=4K<UE*A&hR8Hi53Il28M_leb3q?f*0`@N8fYU3nBg-9-@wlhCc+8Vf|ua
zGBy&WMGHm<=x?i5Lfpy6hwj8hB^JY&!YdbW({AN(b2t6kwin1k*$ccvUDk`zpYBFx
zq0wv<T3ef&zHGH*x!{${*hUd>9Dn15mDJq%^Vz3U*=+hkI-AN(=W^L>E}J^DB+ID<
zFHi>e;@Om254IX|wjvwSua!{s{8Dvx=kg`@(nWVWmvSqB6UQ^Nl*1!ks^YN=WGis1
zUdyHFyU}Fa?QFVKf=snpmzi?2{CX`@sRfyhTG1=kDqd|P<5vS0&$#p(-bSX}lIt>%
z#JcO~;o|zFzb!MAyOp8DA#}^NwKX?f=>MAgge9F|f*W&lGpRFJ@3a4))O0{jQ-dOI
zFM-%eh>5{Idk>G&BgoN@B)p8z4JY1@%?<TlM+fA{_6!+O!Yj`9n~^=TbhdO#d*Ii<
zIxX~wg=e_KWT}f{8m#EqGihXdIyfRM2p_W!n+1IxT;#7k%9@AOex5WEp#_6S38h(p
zuS^%QV5AF6d4@W8s`n|aKKwvFp?KE$xjpGlXxB%30-_=LF&aQsuNKdUc&bV*a>qGE
zLOlIg9#nvNe%2mVJneM*VM2UP^XEAbbw%T8TwdqA!Kak|lEJfHG5Bwl{+_`<rSy*t
zUa9b@0z7}7&-aG^*Gm7<@K<WPwp5=uli2hAaeg*EssPvX4W-j(goODYA{DZ#XZ{zZ
zUsb)CKc#ru6MbLu2G8q-bnDG};1#hC{HU)o3gk`zX?ijtU2L@=?aK`>U6#eIN}<+R
zYoaa0Ua{!Q?IUK-_Zk&>G-RXJkQ{<TFeJJV>ZNAAF7eMwy3q_|8XrHm6?mm}l~`5_
zYra>PY9arzqjOBP)9(hQ%u|EH=RDFPU&ho=;g90OKWs!Od6j|Dldmpx-|2q*+ju$`
zW=_h_<Nra5CZ;c)H+o(3nh(J*RY9sMU=%s><GYOieL|VV*33zU6-DyB44u39-s-}U
z@3=a&)1+u(j&&1l-R3{0Cd%<NfBGtz^DiT3=0pk>)%eS#Xkz*n(AI7I1vSuXDwf8l
z_s$%D6FJJs^F$cRz8e2KCRESkbN?3d-QuM>Q2p$9O#JRK@eNuON+#;sEvMuDBeXDX
zJ0bW)#p@hl)AtL-spYoaKU489x0wiV{h0n70lDUxQ2jmS$aSUq)7P2f`TWv+%&{rO
tfg1l^HDUTL(pTS%r#&_AE>f_g?oW;sP0aPvyt>8zqz3v*izR5r{|)=SU!?#5

literal 0
HcmV?d00001

diff --git a/tests/test-diff-suppr.cc b/tests/test-diff-suppr.cc
index d9efa864..645afe53 100644
--- a/tests/test-diff-suppr.cc
+++ b/tests/test-diff-suppr.cc
@@ -2056,6 +2056,86 @@ InOutSpec in_out_specs[] =
     "data/test-diff-suppr/PR28073/PR28073-output-2.txt",
     "output/test-diff-suppr/PR28073/PR28073-output-2.txt"
   },
+  {
+    "data/test-diff-suppr/test-has-data-member-v0.o",
+    "data/test-diff-suppr/test-has-data-member-v1.o",
+    "",
+    "",
+    "",
+    "--drop-private-types --no-default-suppression",
+    "data/test-diff-suppr/test-has-data-member-output-1.txt",
+    "output/test-diff-suppr/test-has-data-member-output-1.txt"
+  },
+  {
+    "data/test-diff-suppr/test-has-data-member-v0.o",
+    "data/test-diff-suppr/test-has-data-member-v1.o",
+    "",
+    "",
+    "data/test-diff-suppr/has-data-member-1.suppr",
+    "--drop-private-types --no-default-suppression",
+    "data/test-diff-suppr/test-has-data-member-output-2.txt",
+    "output/test-diff-suppr/test-has-data-member-output-2.1.txt"
+  },
+  {
+    "data/test-diff-suppr/test-has-data-member-v0.o",
+    "data/test-diff-suppr/test-has-data-member-v1.o",
+    "",
+    "",
+    "data/test-diff-suppr/has-data-member-2.suppr",
+    "--drop-private-types --no-default-suppression",
+    "data/test-diff-suppr/test-has-data-member-output-2.txt",
+    "output/test-diff-suppr/test-has-data-member-output-2.2.txt"
+  },
+  {
+    "data/test-diff-suppr/test-has-data-member-v0.o",
+    "data/test-diff-suppr/test-has-data-member-v1.o",
+    "",
+    "",
+    "data/test-diff-suppr/has-data-member-3.suppr",
+    "--drop-private-types --no-default-suppression",
+    "data/test-diff-suppr/test-has-data-member-output-2.txt",
+    "output/test-diff-suppr/test-has-data-member-output-2.3.txt"
+  },
+  {
+    "data/test-diff-suppr/test-has-data-member-v0.o",
+    "data/test-diff-suppr/test-has-data-member-v1.o",
+    "",
+    "",
+    "data/test-diff-suppr/has-data-member-4.suppr",
+    "--drop-private-types --no-default-suppression",
+    "data/test-diff-suppr/test-has-data-member-output-1.txt",
+    "output/test-diff-suppr/test-has-data-member-output-1.2.txt"
+  },
+  {
+    "data/test-diff-suppr/test-has-data-member-v0.o",
+    "data/test-diff-suppr/test-has-data-member-v1.o",
+    "",
+    "",
+    "data/test-diff-suppr/has-data-member-5.suppr",
+    "--drop-private-types --no-default-suppression",
+    "data/test-diff-suppr/test-has-data-member-output-1.txt",
+    "output/test-diff-suppr/test-has-data-member-output-1.3.txt"
+  },
+  {
+    "data/test-diff-suppr/test-has-data-member-v0.o",
+    "data/test-diff-suppr/test-has-data-member-v1.o",
+    "",
+    "",
+    "data/test-diff-suppr/has-data-member-6.suppr",
+    "--drop-private-types --no-default-suppression",
+    "data/test-diff-suppr/test-has-data-member-output-2.txt",
+    "output/test-diff-suppr/test-has-data-member-output-2.4.txt"
+  },
+    {
+    "data/test-diff-suppr/test-has-data-member-v0.o",
+    "data/test-diff-suppr/test-has-data-member-v1.o",
+    "",
+    "",
+    "data/test-diff-suppr/has-data-member-7.suppr",
+    "--drop-private-types --no-default-suppression",
+    "data/test-diff-suppr/test-has-data-member-output-1.txt",
+    "output/test-diff-suppr/test-has-data-member-output-1.4.txt"
+  },
   // This should be the last entry
   {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}
 };
-- 
2.39.2


^ permalink raw reply	[flat|nested] 14+ messages in thread

* [PATCH 03/13] suppression: Factorize out is_data_member_offset_in_range
  2023-03-02 18:53 ` [PATCH 00/13] Support negative suppression specifications Dodji Seketeli
  2023-03-02 18:55   ` [PATCH 01/13] ini: Fix parsing list property values Dodji Seketeli
  2023-03-02 18:56   ` [PATCH 02/13] suppr: Support has_data_member and has_data_member_regexp properties Dodji Seketeli
@ 2023-03-02 18:57   ` Dodji Seketeli
  2023-03-02 18:58   ` [PATCH 04/13] suppression: Support the has_size_change property for suppress_type Dodji Seketeli
                     ` (9 subsequent siblings)
  12 siblings, 0 replies; 14+ messages in thread
From: Dodji Seketeli @ 2023-03-02 18:57 UTC (permalink / raw)
  To: Dodji Seketeli; +Cc: libabigail, ckalina

Hello,

In preparation of subsequent changes, this patch factorizes a function
is_data_member_offset_in_range() out of
type_suppression::suppression().  This is useful to determine if a
data member offset is within an "offset range" expressed by the
type_suppression::insertion_range type.

This function is useful to implement the
offset_of_first_data_member_regexp and
offset_of_last_data_member_regexp properties to come in subsequent
patches.

Please note that is_data_member_offset_in_range works on data members
of unions and classes, not just on classes like what the original code
of inside type_suppression::suppresses_diff was doing.

This patch should not have any functional impact on the code.

	* include/abg-fwd.h (get_last_data_member)
	(get_next_data_member_offset): Declare functions.
	* src/abg-ir.cc (get_next_data_member): Add an overload for
	class_or_union and write the overload for class_or_union_sptr in
	term of the former.
	(get_last_data_member): Add overloads form class_or_union& and
	class_or_union*.  Write the overload for class_or_union_sptr in
	terms of the one for class_or_union*.
	(get_next_data_member_offset): Add an overload for
	class_or_union* and write the overload for class_or_union_sptr in
	terms of the former.
	* include/abg-suppression.h
	(type_suppression::insertion_range::eval_boundary): Take a
	class_or_union* for the context, as opposed to a class_decl_sptr.
	This makes this static function work for unions as well.
	(is_data_member_offset_in_range): Declare new function.
	* src/abg-suppression.cc (type_suppression::suppression_diff):
	Factorize ...
	(is_data_member_offset_in_range): ... this function out.
	(type_suppression::insertion_range::eval_boundary): Adjust this to
	make it take a class_or_union* rather than a class_decl_sptr.

Signed-off-by: Dodji Seketeli <dodji@redhat.com>
---
 include/abg-fwd.h         |  11 ++++
 include/abg-suppression.h |  12 +++-
 src/abg-ir.cc             |  57 +++++++++++++++++-
 src/abg-suppression.cc    | 122 ++++++++++++++++++++------------------
 4 files changed, 138 insertions(+), 64 deletions(-)

diff --git a/include/abg-fwd.h b/include/abg-fwd.h
index 155642f0..4051fab5 100644
--- a/include/abg-fwd.h
+++ b/include/abg-fwd.h
@@ -655,6 +655,12 @@ is_data_member(const decl_base *);
 const var_decl_sptr
 get_next_data_member(const class_or_union_sptr&, const var_decl_sptr&);
 
+var_decl_sptr
+get_last_data_member(const class_or_union&);
+
+var_decl_sptr
+get_last_data_member(const class_or_union*);
+
 var_decl_sptr
 get_last_data_member(const class_or_union_sptr&);
 
@@ -740,6 +746,11 @@ get_data_member_offset(const decl_base_sptr);
 uint64_t
 get_absolute_data_member_offset(const var_decl&);
 
+bool
+get_next_data_member_offset(const class_or_union*,
+			    const var_decl_sptr&,
+			    uint64_t&);
+
 bool
 get_next_data_member_offset(const class_or_union_sptr&,
 			    const var_decl_sptr&,
diff --git a/include/abg-suppression.h b/include/abg-suppression.h
index 27f52110..b13bb9fb 100644
--- a/include/abg-suppression.h
+++ b/include/abg-suppression.h
@@ -346,9 +346,9 @@ public:
   create_fn_call_expr_boundary(const string&);
 
   static bool
-  eval_boundary(boundary_sptr	boundary,
-		class_decl_sptr context,
-		uint64_t&	value);
+  eval_boundary(const boundary_sptr	boundary,
+		const class_or_union*	context,
+		uint64_t&		value);
 
   static bool
   boundary_value_is_end(uint64_t value);
@@ -899,6 +899,12 @@ is_type_suppressed(const fe_iface&	fe,
 		   const location&	type_location,
 		   bool&		type_is_private,
 		   bool			require_drop_property = false);
+
+bool
+is_data_member_offset_in_range(const var_decl_sptr&,
+			       const type_suppression::insertion_range_sptr&,
+			       const class_or_union*);
+
 } // end namespace suppr
 
 
diff --git a/src/abg-ir.cc b/src/abg-ir.cc
index 321adbf5..ef19eb41 100644
--- a/src/abg-ir.cc
+++ b/src/abg-ir.cc
@@ -5752,7 +5752,7 @@ get_first_non_anonymous_data_member(const var_decl_sptr anon_dm)
 /// @return the data member that is located right after @p
 /// data_member.
 const var_decl_sptr
-get_next_data_member(const class_or_union_sptr &klass,
+get_next_data_member(const class_or_union *klass,
 		     const var_decl_sptr &data_member)
 {
   if (!klass ||!data_member)
@@ -5773,12 +5773,40 @@ get_next_data_member(const class_or_union_sptr &klass,
   return var_decl_sptr();
 }
 
+/// In the context of a given class or union, this function returns
+/// the data member that is located after a given data member.
+///
+/// @param klass the class or union to consider.
+///
+/// @param the data member to consider.
+///
+/// @return the data member that is located right after @p
+/// data_member.
+const var_decl_sptr
+get_next_data_member(const class_or_union_sptr& klass,
+		     const var_decl_sptr &data_member)
+{return get_next_data_member(klass.get(), data_member);}
+
+/// Get the last data member of a class type.
+///
+/// @param klass the class type to consider.
+var_decl_sptr
+get_last_data_member(const class_or_union& klass)
+{return klass.get_non_static_data_members().back();}
+
+/// Get the last data member of a class type.
+///
+/// @param klass the class type to consider.
+var_decl_sptr
+get_last_data_member(const class_or_union* klass)
+{return get_last_data_member(*klass);}
+
 /// Get the last data member of a class type.
 ///
 /// @param klass the class type to consider.
 var_decl_sptr
 get_last_data_member(const class_or_union_sptr &klass)
-{return klass->get_non_static_data_members().back();}
+{return get_last_data_member(klass.get());}
 
 /// Test if a decl is an anonymous data member.
 ///
@@ -6062,7 +6090,7 @@ get_data_member_offset(const decl_base_sptr d)
 /// @return true iff the data member coming right after @p dm was
 /// found.
 bool
-get_next_data_member_offset(const class_or_union_sptr& klass,
+get_next_data_member_offset(const class_or_union* klass,
 			    const var_decl_sptr& dm,
 			    uint64_t& offset)
 {
@@ -6073,6 +6101,29 @@ get_next_data_member_offset(const class_or_union_sptr& klass,
   return true;
 }
 
+/// Get the offset of the non-static data member that comes after a
+/// given one.
+///
+/// If there is no data member after after the one given to this
+/// function (maybe because the given one is the last data member of
+/// the class type) then the function return false.
+///
+/// @param klass the class to consider.
+///
+/// @param dm the data member before the one we want to retrieve.
+///
+/// @param offset out parameter.  This parameter is set by the
+/// function to the offset of the data member that comes right after
+/// the data member @p dm, iff the function returns true.
+///
+/// @return true iff the data member coming right after @p dm was
+/// found.
+bool
+get_next_data_member_offset(const class_or_union_sptr& klass,
+			    const var_decl_sptr& dm,
+			    uint64_t& offset)
+{return get_next_data_member_offset(klass.get(), dm, offset);}
+
 /// Get the absolute offset of a data member.
 ///
 /// If the data member is part of an anonymous data member then this
diff --git a/src/abg-suppression.cc b/src/abg-suppression.cc
index 5fcdef97..b596e0d5 100644
--- a/src/abg-suppression.cc
+++ b/src/abg-suppression.cc
@@ -865,65 +865,19 @@ type_suppression::suppresses_diff(const diff* diff) const
 	      const class_decl_sptr& first_type_decl =
 		klass_diff->first_class_decl();
 
-	      for (string_decl_base_sptr_map::const_iterator m =
-		     klass_diff->inserted_data_members().begin();
-		   m != klass_diff->inserted_data_members().end();
-		   ++m)
+	      // All inserted data members must be in an allowed
+	      // insertion range.
+	      for (const auto& m : klass_diff->inserted_data_members())
 		{
-		  decl_base_sptr member = m->second;
-		  size_t dm_offset = get_data_member_offset(member);
+		  decl_base_sptr member = m.second;
 		  bool matched = false;
 
-		  for (insertion_ranges::const_iterator i =
-			 get_data_member_insertion_ranges().begin();
-		       i != get_data_member_insertion_ranges().end();
-		       ++i)
-		    {
-		      type_suppression::insertion_range_sptr range = *i;
-		      uint64_t range_begin_val = 0, range_end_val = 0;
-		      if (!type_suppression::insertion_range::eval_boundary
-			  (range->begin(), first_type_decl, range_begin_val))
-			break;
-		      if (!type_suppression::insertion_range::eval_boundary
-			  (range->end(), first_type_decl, range_end_val))
-			break;
-
-		      uint64_t range_begin = range_begin_val;
-		      uint64_t range_end = range_end_val;
-
-		      if (insertion_range::boundary_value_is_end(range_begin)
-			  && insertion_range::boundary_value_is_end(range_end))
-			{
-			  // This idiom represents the predicate
-			  // "has_data_member_inserted_at = end"
-			  if (dm_offset >
-			      get_data_member_offset(get_last_data_member
-						     (first_type_decl)))
-			    {
-			      // So the data member was added after
-			      // last data member of the klass.  That
-			      // matches the suppr spec
-			      // "has_data_member_inserted_at = end".
-			      matched = true;
-			      continue;
-			    }
-			}
-
-			if (range_begin > range_end)
-			  // Wrong suppr spec.  Ignore it.
-			  continue;
-
-		      if (dm_offset < range_begin || dm_offset > range_end)
-			// The offset of the added data member doesn't
-			// match the insertion range specified.  So
-			// the diff object won't be suppressed.
-			continue;
-
-		      // If we reached this point, then all the
-		      // insertion range constraints have been
-		      // satisfied.  So
+		  for (const auto& range : get_data_member_insertion_ranges())
+		    if (is_data_member_offset_in_range(is_var_decl(member),
+						       range,
+						       first_type_decl.get()))
 		      matched = true;
-		    }
+
 		  if (!matched)
 		    return false;
 		}
@@ -1404,9 +1358,9 @@ type_suppression::insertion_range::create_fn_call_expr_boundary(const string& s)
 /// @return true iff the evaluation was successful and @p value
 /// contains the resulting value.
 bool
-type_suppression::insertion_range::eval_boundary(boundary_sptr	 boundary,
-						 class_decl_sptr context,
-						 uint64_t&	 value)
+type_suppression::insertion_range::eval_boundary(const boundary_sptr	boundary,
+						 const class_or_union*	context,
+						 uint64_t&		value)
 {
   if (integer_boundary_sptr b = is_integer_boundary(boundary))
     {
@@ -4962,5 +4916,57 @@ is_type_suppressed(const fe_iface&	fe,
   return false;
 }
 
+/// Test if a data memer offset is in a given insertion range.
+///
+/// @param dm the data member to consider.
+///
+/// @param range the insertion range to consider.
+///
+/// @param the class (or union) type to consider as the context in
+/// which to evaluate the insertion range denoted by @p range.
+///
+/// @return true iff the offset of the data member @p dm is in the
+/// insertion range @p range in the context of the type denoted by @p
+/// context.
+bool
+is_data_member_offset_in_range(const var_decl_sptr& dm,
+			       const type_suppression::insertion_range_sptr& range,
+			       const class_or_union* context)
+{
+  ABG_ASSERT(dm && range && context);
+
+  uint64_t range_begin = 0, range_end = 0;
+  if (!type_suppression::insertion_range::eval_boundary (range->begin(),
+							 context,
+							 range_begin))
+    return false;
+
+  if (!type_suppression::insertion_range::eval_boundary (range->end(),
+							 context,
+							 range_end))
+    return false;
+
+  if (range_begin > range_end)
+    // wrong range, ignore it.
+    return false;
+
+  uint64_t dm_offset = get_data_member_offset(dm);
+  if (type_suppression::insertion_range::boundary_value_is_end(range_begin)
+      && type_suppression::insertion_range::boundary_value_is_end(range_end))
+    {
+      // This idiom represents the predicate
+      // "has_data_member_inserted_at = end"
+      if (dm_offset > get_data_member_offset(get_last_data_member(context)))
+	return true;
+      return false;
+    }
+
+  if (dm_offset < range_begin || dm_offset > range_end)
+    // The offset of the data member is outside the range.
+    return false;
+
+  return true;
+}
+
 }// end namespace suppr
 } // end namespace abigail
-- 
2.39.2


^ permalink raw reply	[flat|nested] 14+ messages in thread

* [PATCH 04/13] suppression: Support the has_size_change property for suppress_type
  2023-03-02 18:53 ` [PATCH 00/13] Support negative suppression specifications Dodji Seketeli
                     ` (2 preceding siblings ...)
  2023-03-02 18:57   ` [PATCH 03/13] suppression: Factorize out is_data_member_offset_in_range Dodji Seketeli
@ 2023-03-02 18:58   ` Dodji Seketeli
  2023-03-02 18:59   ` [PATCH 05/13] suppression: Support offset_of_{first,last}_data_member_regexp offset selectors Dodji Seketeli
                     ` (8 subsequent siblings)
  12 siblings, 0 replies; 14+ messages in thread
From: Dodji Seketeli @ 2023-03-02 18:58 UTC (permalink / raw)
  To: Dodji Seketeli; +Cc: libabigail, ckalina

Hello,

The "has_data_member_inserted_between" and
"has_data_members_inserted_between" properties of the [suppress_type]
directive allows the suppression of type changes when a data member is
inserted in a given range.  It turns out that suppressing type changes
that incur a change in the size of the type might not be what the user
wants by default, because the type size in itself might actually be an
incompatible ABI change that would then fly under the radar because of
this suppression specification.

An arguably better default behavior in this case would be to NOT
suppress the type change if the data member insertion does incur a
change in the size of the type.

But then, there would be cases where the user would really want to
suppress the type change due to data member insertion in a given range
even if it incurs a change in the type size.  This is where this patch
enters into play.

The patch introduces the "has_size_change" property of the
[suppress_type] directive.  In the presence of
"has_data_members_inserted_between" or
"has_data_member_inserted_between" properties, if the
"has_size_change" property is set to "yes", then the type change would
be suppressed if data members are inserted in the given range even if
the insertion incurs a type size change.

Otherwise, with this patch, in the absence of the "has_size_change"
property, the "has_data_member_inserted_between" and
"has_data_members_inserted_between" properties won't trigger the type
change suppression if the data member insertion incurs a type size
change.

	* doc/manuals/libabigail-concepts.rst: Document the new
	has_size_change property.
	* include/abg-suppression.h
	(type_suppression::{g,s}et_has_size_change): Declare new accessors.
	* src/abg-suppression-priv.h
	(type_suppression::priv::has_size_change_): Define new data
	member.
	(type_suppression::priv::priv): Initialize the new data member.
	* src/abg-suppression.cc
	(type_suppression::{g,s}et_has_size_change): Define new accessors.
	(type_suppression::suppresses_diff): Make the
	has_data_member_inserted_* clauses have effect only if the class
	size hasn't changed, unless the class has as the "has_size_change"
	property.  Also, allow members to be deleted in the right
	insertion range if the resulting size stays the same or if the
	has_size_change property is present.  This allows some custom
	behaviours where "padding" data members would be removed while
	some new data members would be added, resulting in a type which
	size would not change.
	(read_type_suppression): Support parsing the "has_size_change"
	property.
	* tests/data/test-diff-suppr/test11-add-data-member-0.1.suppr: New
	test suppression specification.
	* tests/data/test-diff-suppr/test11-add-data-member-1.1.suppr:
	Likewise.
	* tests/data/test-diff-suppr/test11-add-data-member-2.1.suppr:
	Likewise.
	* tests/data/test-diff-suppr/test11-add-data-member-3.1.suppr:
	Likewise.
	* tests/data/test-diff-suppr/test11-add-data-member-4.1.suppr:
	Likewise.
	* tests/data/test-diff-suppr/test11-add-data-member-report-1.1.txt:
	Likewise.
	* tests/data/test-diff-suppr/test12-add-data-member-0.1.suppr:
	Likewise.
	* tests/data/test-diff-suppr/test12-add-data-member-report-1.1.txt:
	New test reference output.
	* tests/data/test-diff-suppr/test13-suppr-through-pointer-0.1.suppr:
	New test suppression specification.
	* tests/data/test-diff-suppr/test13-suppr-through-pointer-report-1.1.txt:
	New test reference output.
	* tests/data/test-diff-suppr/test35-leaf-report-0.1.txt: Likewise.
	* tests/data/test-diff-suppr/test35-leaf.1.suppr: New test
	suppression specification.
	* tests/data/Makefile.am: Add the new testing material to source
	distribution.
	* tests/data/test-diff-suppr/test11-add-data-member-1.suppr: Add
	the has_size_change property to explicitly allow suppressing type
	changes involving data member insertion even when the type size
	changes.
	* tests/data/test-diff-suppr/test11-add-data-member-0.suppr:
	Likewise.
	* tests/data/test-diff-suppr/test11-add-data-member-2.suppr:
	Likewise.
	* tests/data/test-diff-suppr/test11-add-data-member-3.suppr:
	Likewise.
	* tests/data/test-diff-suppr/test11-add-data-member-4.suppr:
	Likewise.
	* tests/data/test-diff-suppr/test12-add-data-member-0.suppr:
	Likewise.
	* tests/data/test-diff-suppr/test13-suppr-through-pointer-0.suppr:
	Likewise.
	* tests/data/test-diff-suppr/test35-leaf.suppr: Likewise.
	* tests/test-diff-suppr.cc (in_out_specs): Add the new test input
	to the test harness.

Signed-off-by: Dodji Seketeli <dodji@redhat.com>
---
 doc/manuals/libabigail-concepts.rst           | 50 +++++++++--
 include/abg-suppression.h                     |  6 ++
 src/abg-suppression-priv.h                    |  4 +-
 src/abg-suppression.cc                        | 51 +++++++++++-
 tests/data/Makefile.am                        | 12 +++
 .../test11-add-data-member-0.1.suppr          |  3 +
 .../test11-add-data-member-0.suppr            |  1 +
 .../test11-add-data-member-1.1.suppr          |  3 +
 .../test11-add-data-member-1.suppr            |  1 +
 .../test11-add-data-member-2.1.suppr          |  3 +
 .../test11-add-data-member-2.suppr            |  1 +
 .../test11-add-data-member-3.1.suppr          |  3 +
 .../test11-add-data-member-3.suppr            |  1 +
 .../test11-add-data-member-4.1.suppr          |  3 +
 .../test11-add-data-member-4.suppr            |  1 +
 .../test11-add-data-member-report-1.1.txt     | 13 +++
 .../test12-add-data-member-0.1.suppr          |  3 +
 .../test12-add-data-member-0.suppr            |  1 +
 .../test12-add-data-member-report-1.1.txt     | 13 +++
 .../test13-suppr-through-pointer-0.1.suppr    |  4 +
 .../test13-suppr-through-pointer-0.suppr      |  1 +
 ...est13-suppr-through-pointer-report-1.1.txt | 16 ++++
 .../test35-leaf-report-0.1.txt                | 18 ++++
 .../data/test-diff-suppr/test35-leaf.1.suppr  |  8 ++
 tests/data/test-diff-suppr/test35-leaf.suppr  |  1 +
 tests/test-diff-suppr.cc                      | 82 ++++++++++++++++++-
 26 files changed, 288 insertions(+), 15 deletions(-)
 create mode 100644 tests/data/test-diff-suppr/test11-add-data-member-0.1.suppr
 create mode 100644 tests/data/test-diff-suppr/test11-add-data-member-1.1.suppr
 create mode 100644 tests/data/test-diff-suppr/test11-add-data-member-2.1.suppr
 create mode 100644 tests/data/test-diff-suppr/test11-add-data-member-3.1.suppr
 create mode 100644 tests/data/test-diff-suppr/test11-add-data-member-4.1.suppr
 create mode 100644 tests/data/test-diff-suppr/test11-add-data-member-report-1.1.txt
 create mode 100644 tests/data/test-diff-suppr/test12-add-data-member-0.1.suppr
 create mode 100644 tests/data/test-diff-suppr/test12-add-data-member-report-1.1.txt
 create mode 100644 tests/data/test-diff-suppr/test13-suppr-through-pointer-0.1.suppr
 create mode 100644 tests/data/test-diff-suppr/test13-suppr-through-pointer-report-1.1.txt
 create mode 100644 tests/data/test-diff-suppr/test35-leaf-report-0.1.txt
 create mode 100644 tests/data/test-diff-suppr/test35-leaf.1.suppr

diff --git a/doc/manuals/libabigail-concepts.rst b/doc/manuals/libabigail-concepts.rst
index 2824b216..dc5a4a5c 100644
--- a/doc/manuals/libabigail-concepts.rst
+++ b/doc/manuals/libabigail-concepts.rst
@@ -481,9 +481,11 @@ names start with the string "private_data_member".
 
  Suppresses change reports involving a type which has at least one
  data member inserted at an offset specified by the property value
- ``offset-in-bit``.  Please note that if a type has a change in which
- at least one of its data members is removed or its size is reduced,
- the type will *NOT* be suppressed by the evaluation of this property.
+ ``offset-in-bit``.  Please note that if the size of the type changed,
+ then the type change will *NOT* be suppressed by the evaluation of
+ this property, unless the
+ :ref:`has_size_change<suppr_has_size_change_property_label>` property
+ is present and set to ``yes``.
 
  The value ``offset-in-bit`` is either:
 
@@ -525,9 +527,16 @@ names start with the string "private_data_member".
  the values ``range-begin`` and ``range-end`` can be of the same form
  as the :ref:`has_data_member_inserted_at
  <suppr_has_data_member_inserted_at_label>` property above.  Please
- also note that if a type has a change in which at least one of its
- data members is removed or its size is reduced, the type will *NOT* be
- suppressed by the evaluation of this property.
+ also note that if the size of the type changed, then the type change
+ will *NOT* be suppressed by the evaluation of this property, unless
+ the :ref:`has_size_change<suppr_has_size_change_property_label>`
+ property is present and set to ``yes``.  Note that data member
+ deletions happening in the range between ``range-begin`` and
+ ``range-end`` won't prevent the type change from being suppressed by
+ the evaluation of this property if the size of the type doesn't
+ change or if the
+ :ref:`has_size_change<suppr_has_size_change_property_label>` property
+ is present and set to ``yes``.
 
  Usage examples of this properties are: ::
 
@@ -564,9 +573,15 @@ names start with the string "private_data_member".
  the ranges are of the same kind as for the
  :ref:`has_data_member_inserted_at
  <suppr_has_data_member_inserted_at_label>` property above.  Please
- note that if a type has a change in which at least one of its data
- members is removed or its size is reduced, the type will *NOT* be
- suppressed by the evaluation of this property.
+ also note that if the size of the type changed, then the type will
+ *NOT* be suppressed by the evaluation of this property, unless the
+ :ref:`has_size_change<suppr_has_size_change_property_label>` property
+ is present and set to ``yes``.  Note that data member deletions
+ happening in the defined ranges won't prevent the type change from
+ being suppressed by the evaluation of this property if the size of
+ the type doesn't change or if the
+ :ref:`has_size_change<suppr_has_size_change_property_label>` property
+ is present and set to ``yes``.
 
  Another usage example of this property is thus: ::
 
@@ -576,6 +591,23 @@ names start with the string "private_data_member".
 	  {72, end}
      }
 
+
+ .. _suppr_has_size_change_property_label:
+* ``has_size_change``
+
+ Usage:
+
+   ``has_size_change`` ``=`` yes | no
+
+
+This property is to be used in conjunction with the properties
+:ref:`has_data_member_inserted_between<suppr_has_data_member_inserted_between_label>`
+and
+:ref:`has_data_members_inserted_between<suppr_has_data_members_inserted_between_label>`.
+Those properties will not match a type change if the size of the type
+changes, unless the ``has_size_changes`` property is set to ``yes``.
+
+
  .. _suppr_accessed_through_property_label:
 
 * ``accessed_through``
diff --git a/include/abg-suppression.h b/include/abg-suppression.h
index b13bb9fb..a51fdc5a 100644
--- a/include/abg-suppression.h
+++ b/include/abg-suppression.h
@@ -243,6 +243,12 @@ public:
   void
   set_reach_kind(reach_kind k);
 
+  bool
+  get_has_size_change() const;
+
+  void
+  set_has_size_change(bool flag);
+
   const string_set_type&
   get_potential_data_member_names() const;
 
diff --git a/src/abg-suppression-priv.h b/src/abg-suppression-priv.h
index 5fc1ec52..dcc5cc53 100644
--- a/src/abg-suppression-priv.h
+++ b/src/abg-suppression-priv.h
@@ -570,6 +570,7 @@ class type_suppression::priv
   type_suppression::type_kind		type_kind_;
   bool					consider_reach_kind_;
   type_suppression::reach_kind		reach_kind_;
+  bool					has_size_change_;
   // The data members a class needs to have to match this suppression
   // specification.  These might be selected by a regular expression.
   string_set_type			potential_data_members_;
@@ -599,7 +600,8 @@ public:
       consider_type_kind_(consider_type_kind),
       type_kind_(type_kind),
       consider_reach_kind_(consider_reach_kind),
-      reach_kind_(reach_kind)
+      reach_kind_(reach_kind),
+      has_size_change_(false)
   {}
 
   /// Get the regular expression object associated to the 'type_name_regex'
diff --git a/src/abg-suppression.cc b/src/abg-suppression.cc
index b596e0d5..54d2d917 100644
--- a/src/abg-suppression.cc
+++ b/src/abg-suppression.cc
@@ -569,6 +569,20 @@ void
 type_suppression::set_reach_kind(reach_kind k)
 {priv_->reach_kind_ = k;}
 
+/// Getter of the "has_size_change" property.
+///
+/// @return the value of the "has_size_change" property.
+bool
+type_suppression::get_has_size_change() const
+{return priv_->has_size_change_;}
+
+/// Setter of the "has_size_change" property.
+///
+/// @param flag the new value of the "has_size_change" property.
+void
+type_suppression::set_has_size_change(bool flag)
+{priv_->has_size_change_ = flag;}
+
 /// Getter of the "potential_data_member_names" property.
 ///
 /// @return the set of potential data member names of this
@@ -855,12 +869,14 @@ type_suppression::suppresses_diff(const diff* diff) const
 	{
 	  // ... and the suppr spec contains a
 	  // "has_data_member_inserted_*" clause ...
-	  if (klass_diff->deleted_data_members().empty()
-	      && (klass_diff->first_class_decl()->get_size_in_bits()
-		  <= klass_diff->second_class_decl()->get_size_in_bits()))
+	  if ((klass_diff->first_class_decl()->get_size_in_bits()
+	       == klass_diff->second_class_decl()->get_size_in_bits())
+	      || get_has_size_change())
 	    {
 	      // That "has_data_member_inserted_*" clause doesn't hold
-	      // if the class has deleted data members or shrunk.
+	      // if the class changed size, unless the user specified
+	      // that suppression applies to types that have size
+	      // change.
 
 	      const class_decl_sptr& first_type_decl =
 		klass_diff->first_class_decl();
@@ -881,6 +897,23 @@ type_suppression::suppresses_diff(const diff* diff) const
 		  if (!matched)
 		    return false;
 		}
+
+	      // Similarly, all deleted data members must be in an
+	      // allowed insertion range.
+	      for (const auto& m : klass_diff->deleted_data_members())
+		{
+		  decl_base_sptr member = m.second;
+		  bool matched = false;
+
+		  for (const auto& range : get_data_member_insertion_ranges())
+		    if (is_data_member_offset_in_range(is_var_decl(member),
+						       range,
+						       first_type_decl.get()))
+		      matched = true;
+
+		  if (!matched)
+		    return false;
+		}
 	    }
 	  else
 	    return false;
@@ -1649,6 +1682,13 @@ read_type_suppression(const ini::config::section& section)
     ? drop_artifact->get_value()->as_string()
     : "";
 
+  ini::simple_property_sptr has_size_change =
+    is_simple_property(section.find_property("has_size_change"));
+
+  string has_size_change_str = has_size_change
+    ? has_size_change->get_value()->as_string()
+    : "";
+
   ini::simple_property_sptr label =
     is_simple_property(section.find_property("label"));
   string label_str = label ? label->get_value()->as_string() : "";
@@ -2030,6 +2070,9 @@ read_type_suppression(const ini::config::section& section)
 	   || !srcloc_not_in.empty())))
     result->set_drops_artifact_from_ir(true);
 
+  if (has_size_change_str == "yes" || has_size_change_str == "true")
+    result->set_has_size_change(true);
+
   if (result->get_type_kind() == type_suppression::ENUM_TYPE_KIND
       && !changed_enumerator_names.empty())
     result->set_changed_enumerator_names(changed_enumerator_names);
diff --git a/tests/data/Makefile.am b/tests/data/Makefile.am
index 57abf7a9..a37a364b 100644
--- a/tests/data/Makefile.am
+++ b/tests/data/Makefile.am
@@ -1294,28 +1294,38 @@ test-diff-suppr/test10-changed-parm-c-v1.c \
 test-diff-suppr/libtest11-add-data-member-v0.so \
 test-diff-suppr/libtest11-add-data-member-v1.so \
 test-diff-suppr/test11-add-data-member-0.suppr \
+test-diff-suppr/test11-add-data-member-0.1.suppr \
 test-diff-suppr/test11-add-data-member-1.suppr \
+test-diff-suppr/test11-add-data-member-1.1.suppr \
 test-diff-suppr/test11-add-data-member-2.suppr \
+test-diff-suppr/test11-add-data-member-2.1.suppr \
 test-diff-suppr/test11-add-data-member-3.suppr \
+test-diff-suppr/test11-add-data-member-3.1.suppr \
 test-diff-suppr/test11-add-data-member-4.suppr \
+test-diff-suppr/test11-add-data-member-4.1.suppr \
 test-diff-suppr/test11-add-data-member-report-0.txt \
 test-diff-suppr/test11-add-data-member-report-1.txt \
+test-diff-suppr/test11-add-data-member-report-1.1.txt \
 test-diff-suppr/test11-add-data-member-v0.cc \
 test-diff-suppr/test11-add-data-member-v1.cc \
 test-diff-suppr/libtest12-add-data-member-v0.so \
 test-diff-suppr/libtest12-add-data-member-v1.so \
 test-diff-suppr/test12-add-data-member-0.suppr \
+test-diff-suppr/test12-add-data-member-0.1.suppr \
 test-diff-suppr/test12-add-data-member-1.suppr \
 test-diff-suppr/test12-add-data-member-report-0.txt \
 test-diff-suppr/test12-add-data-member-report-1.txt \
+test-diff-suppr/test12-add-data-member-report-1.1.txt \
 test-diff-suppr/test12-add-data-member-report-2.txt \
 test-diff-suppr/test12-add-data-member-v0.cc \
 test-diff-suppr/test12-add-data-member-v1.cc \
 test-diff-suppr/libtest13-suppr-through-pointer-v0.so \
 test-diff-suppr/libtest13-suppr-through-pointer-v1.so \
 test-diff-suppr/test13-suppr-through-pointer-0.suppr \
+test-diff-suppr/test13-suppr-through-pointer-0.1.suppr \
 test-diff-suppr/test13-suppr-through-pointer-report-0.txt \
 test-diff-suppr/test13-suppr-through-pointer-report-1.txt \
+test-diff-suppr/test13-suppr-through-pointer-report-1.1.txt \
 test-diff-suppr/test13-suppr-through-pointer-v0.cc \
 test-diff-suppr/test13-suppr-through-pointer-v1.cc \
 test-diff-suppr/test14-suppr-non-redundant-v0.o \
@@ -1618,9 +1628,11 @@ test-diff-suppr/test34-v1.c \
 test-diff-suppr/libtest35-leaf-v0.so \
 test-diff-suppr/libtest35-leaf-v1.so \
 test-diff-suppr/test35-leaf-report-0.txt \
+test-diff-suppr/test35-leaf-report-0.1.txt \
 test-diff-suppr/test35-leaf-v0.cc \
 test-diff-suppr/test35-leaf-v1.cc \
 test-diff-suppr/test35-leaf.suppr \
+test-diff-suppr/test35-leaf.1.suppr \
 test-diff-suppr/libtest36-leaf-v0.so \
 test-diff-suppr/libtest36-leaf-v1.so \
 test-diff-suppr/test36-leaf-report-0.txt \
diff --git a/tests/data/test-diff-suppr/test11-add-data-member-0.1.suppr b/tests/data/test-diff-suppr/test11-add-data-member-0.1.suppr
new file mode 100644
index 00000000..eb5d971d
--- /dev/null
+++ b/tests/data/test-diff-suppr/test11-add-data-member-0.1.suppr
@@ -0,0 +1,3 @@
+[suppress_type]
+	name = S
+	has_data_member_inserted_between = {8, end}
\ No newline at end of file
diff --git a/tests/data/test-diff-suppr/test11-add-data-member-0.suppr b/tests/data/test-diff-suppr/test11-add-data-member-0.suppr
index 9dd993d0..bf997347 100644
--- a/tests/data/test-diff-suppr/test11-add-data-member-0.suppr
+++ b/tests/data/test-diff-suppr/test11-add-data-member-0.suppr
@@ -1,3 +1,4 @@
 [suppress_type]
 	name = S
 	has_data_member_inserted_between = {8, end}
+	has_size_change = true
diff --git a/tests/data/test-diff-suppr/test11-add-data-member-1.1.suppr b/tests/data/test-diff-suppr/test11-add-data-member-1.1.suppr
new file mode 100644
index 00000000..966c3f3a
--- /dev/null
+++ b/tests/data/test-diff-suppr/test11-add-data-member-1.1.suppr
@@ -0,0 +1,3 @@
+[suppress_type]
+	name = S
+	has_data_member_inserted_between = {offset_after(m0), end}
diff --git a/tests/data/test-diff-suppr/test11-add-data-member-1.suppr b/tests/data/test-diff-suppr/test11-add-data-member-1.suppr
index 966c3f3a..8ff1063d 100644
--- a/tests/data/test-diff-suppr/test11-add-data-member-1.suppr
+++ b/tests/data/test-diff-suppr/test11-add-data-member-1.suppr
@@ -1,3 +1,4 @@
 [suppress_type]
 	name = S
 	has_data_member_inserted_between = {offset_after(m0), end}
+	has_size_change = true
diff --git a/tests/data/test-diff-suppr/test11-add-data-member-2.1.suppr b/tests/data/test-diff-suppr/test11-add-data-member-2.1.suppr
new file mode 100644
index 00000000..241d21da
--- /dev/null
+++ b/tests/data/test-diff-suppr/test11-add-data-member-2.1.suppr
@@ -0,0 +1,3 @@
+[suppress_type]
+	name = S
+	has_data_member_inserted_at = 8
\ No newline at end of file
diff --git a/tests/data/test-diff-suppr/test11-add-data-member-2.suppr b/tests/data/test-diff-suppr/test11-add-data-member-2.suppr
index c8ea26ea..017fb88d 100644
--- a/tests/data/test-diff-suppr/test11-add-data-member-2.suppr
+++ b/tests/data/test-diff-suppr/test11-add-data-member-2.suppr
@@ -1,3 +1,4 @@
 [suppress_type]
 	name = S
 	has_data_member_inserted_at = 8
+	has_size_change = true
diff --git a/tests/data/test-diff-suppr/test11-add-data-member-3.1.suppr b/tests/data/test-diff-suppr/test11-add-data-member-3.1.suppr
new file mode 100644
index 00000000..28b0e7cf
--- /dev/null
+++ b/tests/data/test-diff-suppr/test11-add-data-member-3.1.suppr
@@ -0,0 +1,3 @@
+[suppress_type]
+	name = S
+	has_data_member_inserted_at = end
diff --git a/tests/data/test-diff-suppr/test11-add-data-member-3.suppr b/tests/data/test-diff-suppr/test11-add-data-member-3.suppr
index 28b0e7cf..66a5a7c1 100644
--- a/tests/data/test-diff-suppr/test11-add-data-member-3.suppr
+++ b/tests/data/test-diff-suppr/test11-add-data-member-3.suppr
@@ -1,3 +1,4 @@
 [suppress_type]
 	name = S
 	has_data_member_inserted_at = end
+	has_size_change = true
diff --git a/tests/data/test-diff-suppr/test11-add-data-member-4.1.suppr b/tests/data/test-diff-suppr/test11-add-data-member-4.1.suppr
new file mode 100644
index 00000000..d824450d
--- /dev/null
+++ b/tests/data/test-diff-suppr/test11-add-data-member-4.1.suppr
@@ -0,0 +1,3 @@
+[suppress_type]
+	name = S
+	has_data_member_inserted_at = offset_after(m0)
\ No newline at end of file
diff --git a/tests/data/test-diff-suppr/test11-add-data-member-4.suppr b/tests/data/test-diff-suppr/test11-add-data-member-4.suppr
index a99f6f49..82c53312 100644
--- a/tests/data/test-diff-suppr/test11-add-data-member-4.suppr
+++ b/tests/data/test-diff-suppr/test11-add-data-member-4.suppr
@@ -1,3 +1,4 @@
 [suppress_type]
 	name = S
 	has_data_member_inserted_at = offset_after(m0)
+	has_size_change = true
diff --git a/tests/data/test-diff-suppr/test11-add-data-member-report-1.1.txt b/tests/data/test-diff-suppr/test11-add-data-member-report-1.1.txt
new file mode 100644
index 00000000..8e5cd6a4
--- /dev/null
+++ b/tests/data/test-diff-suppr/test11-add-data-member-report-1.1.txt
@@ -0,0 +1,13 @@
+Functions changes summary: 0 Removed, 1 Changed, 0 Added function
+Variables changes summary: 0 Removed, 0 Changed, 0 Added variable
+
+1 function with some indirect sub-type change:
+
+  [C] 'function int foo(S*)' has some indirect sub-type changes:
+    parameter 1 of type 'S*' has sub-type changes:
+      in pointed to type 'struct S':
+        type size changed from 8 to 64 (in bits)
+        2 data member insertions:
+          'char m1', at offset 8 (in bits)
+          'int m2', at offset 32 (in bits)
+
diff --git a/tests/data/test-diff-suppr/test12-add-data-member-0.1.suppr b/tests/data/test-diff-suppr/test12-add-data-member-0.1.suppr
new file mode 100644
index 00000000..795bf09d
--- /dev/null
+++ b/tests/data/test-diff-suppr/test12-add-data-member-0.1.suppr
@@ -0,0 +1,3 @@
+[suppress_type]
+	name = S
+	has_data_members_inserted_between = {{8, 31}, {64, end}}
\ No newline at end of file
diff --git a/tests/data/test-diff-suppr/test12-add-data-member-0.suppr b/tests/data/test-diff-suppr/test12-add-data-member-0.suppr
index b45d5d49..fb558caf 100644
--- a/tests/data/test-diff-suppr/test12-add-data-member-0.suppr
+++ b/tests/data/test-diff-suppr/test12-add-data-member-0.suppr
@@ -1,3 +1,4 @@
 [suppress_type]
 	name = S
 	has_data_members_inserted_between = {{8, 31}, {64, end}}
+	has_size_change = true;
diff --git a/tests/data/test-diff-suppr/test12-add-data-member-report-1.1.txt b/tests/data/test-diff-suppr/test12-add-data-member-report-1.1.txt
new file mode 100644
index 00000000..d7456d7e
--- /dev/null
+++ b/tests/data/test-diff-suppr/test12-add-data-member-report-1.1.txt
@@ -0,0 +1,13 @@
+Functions changes summary: 0 Removed, 1 Changed, 0 Added function
+Variables changes summary: 0 Removed, 0 Changed, 0 Added variable
+
+1 function with some indirect sub-type change:
+
+  [C] 'function int foo(S*)' has some indirect sub-type changes:
+    parameter 1 of type 'S*' has sub-type changes:
+      in pointed to type 'struct S':
+        type size changed from 64 to 96 (in bits)
+        2 data member insertions:
+          'char m_inserted1', at offset 8 (in bits)
+          'char m_inserted2', at offset 64 (in bits)
+
diff --git a/tests/data/test-diff-suppr/test13-suppr-through-pointer-0.1.suppr b/tests/data/test-diff-suppr/test13-suppr-through-pointer-0.1.suppr
new file mode 100644
index 00000000..b8f5ae9b
--- /dev/null
+++ b/tests/data/test-diff-suppr/test13-suppr-through-pointer-0.1.suppr
@@ -0,0 +1,4 @@
+[suppress_type]
+  name = S
+  accessed_through = pointer
+  has_data_member_inserted_at = end
\ No newline at end of file
diff --git a/tests/data/test-diff-suppr/test13-suppr-through-pointer-0.suppr b/tests/data/test-diff-suppr/test13-suppr-through-pointer-0.suppr
index f88c46df..aadaaf77 100644
--- a/tests/data/test-diff-suppr/test13-suppr-through-pointer-0.suppr
+++ b/tests/data/test-diff-suppr/test13-suppr-through-pointer-0.suppr
@@ -2,3 +2,4 @@
   name = S
   accessed_through = pointer
   has_data_member_inserted_at = end
+  has_size_change = true
diff --git a/tests/data/test-diff-suppr/test13-suppr-through-pointer-report-1.1.txt b/tests/data/test-diff-suppr/test13-suppr-through-pointer-report-1.1.txt
new file mode 100644
index 00000000..f408f2dd
--- /dev/null
+++ b/tests/data/test-diff-suppr/test13-suppr-through-pointer-report-1.1.txt
@@ -0,0 +1,16 @@
+Functions changes summary: 0 Removed, 2 Changed (1 filtered out), 0 Added functions
+Variables changes summary: 0 Removed, 0 Changed, 0 Added variable
+
+2 functions with some indirect sub-type change:
+
+  [C] 'function void bar(S*)' has some indirect sub-type changes:
+    parameter 1 of type 'S*' has sub-type changes:
+      in pointed to type 'struct S':
+        type size changed from 32 to 64 (in bits)
+        1 data member insertion:
+          'char m1', at offset 32 (in bits)
+
+  [C] 'function void foo(S)' has some indirect sub-type changes:
+    parameter 1 of type 'struct S' has sub-type changes:
+      details were reported earlier
+
diff --git a/tests/data/test-diff-suppr/test35-leaf-report-0.1.txt b/tests/data/test-diff-suppr/test35-leaf-report-0.1.txt
new file mode 100644
index 00000000..17361c52
--- /dev/null
+++ b/tests/data/test-diff-suppr/test35-leaf-report-0.1.txt
@@ -0,0 +1,18 @@
+Leaf changes summary: 2 artifacts changed
+Changed leaf types summary: 2 leaf types 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
+
+'struct leaf at test35-leaf-v0.cc:5:1' changed:
+  type size changed from 32 to 64 (in bits)
+  1 data member insertion:
+    'char m1', at offset 32 (in bits) at test35-leaf-v1.cc:8:1
+  one impacted interface:
+    function void fn(C&)
+
+'struct leaf_to_filter at test35-leaf-v0.cc:10:1' changed:
+  type size changed from 32 to 64 (in bits)
+  1 data member insertion:
+    'int added', at offset 32 (in bits) at test35-leaf-v1.cc:14:1
+  one impacted interface:
+    function void fn(C&)
diff --git a/tests/data/test-diff-suppr/test35-leaf.1.suppr b/tests/data/test-diff-suppr/test35-leaf.1.suppr
new file mode 100644
index 00000000..b49293ec
--- /dev/null
+++ b/tests/data/test-diff-suppr/test35-leaf.1.suppr
@@ -0,0 +1,8 @@
+[suppress_type]
+
+  # Suppress the type that had a data member inserted after the data
+  # member named 'member0'.  That would be the type named
+  # leaf_to_filter.
+
+  name_regexp = .*
+  has_data_member_inserted_between = {offset_after(member0), end}
diff --git a/tests/data/test-diff-suppr/test35-leaf.suppr b/tests/data/test-diff-suppr/test35-leaf.suppr
index b49293ec..830529bf 100644
--- a/tests/data/test-diff-suppr/test35-leaf.suppr
+++ b/tests/data/test-diff-suppr/test35-leaf.suppr
@@ -6,3 +6,4 @@
 
   name_regexp = .*
   has_data_member_inserted_between = {offset_after(member0), end}
+  has_size_change = true
diff --git a/tests/test-diff-suppr.cc b/tests/test-diff-suppr.cc
index 645afe53..a463b341 100644
--- a/tests/test-diff-suppr.cc
+++ b/tests/test-diff-suppr.cc
@@ -514,7 +514,17 @@ InOutSpec in_out_specs[] =
     "data/test-diff-suppr/test11-add-data-member-0.suppr",
     "--no-default-suppression --no-show-locs --no-redundant",
     "data/test-diff-suppr/test11-add-data-member-report-1.txt",
-    "output/test-diff-suppr/test11-add-data-member-report-1_0.txt"
+    "output/test-diff-suppr/test11-add-data-member-report-0_0.txt"
+  },
+    {
+    "data/test-diff-suppr/libtest11-add-data-member-v0.so",
+    "data/test-diff-suppr/libtest11-add-data-member-v1.so",
+    "",
+    "",
+    "data/test-diff-suppr/test11-add-data-member-0.1.suppr",
+    "--no-default-suppression --no-show-locs --no-redundant",
+    "data/test-diff-suppr/test11-add-data-member-report-1.1.txt",
+    "output/test-diff-suppr/test11-add-data-member-report-0.1_0.txt"
   },
   {
     "data/test-diff-suppr/libtest11-add-data-member-v0.so",
@@ -526,6 +536,16 @@ InOutSpec in_out_specs[] =
     "data/test-diff-suppr/test11-add-data-member-report-1.txt",
     "output/test-diff-suppr/test11-add-data-member-report-1_1.txt"
   },
+  {
+    "data/test-diff-suppr/libtest11-add-data-member-v0.so",
+    "data/test-diff-suppr/libtest11-add-data-member-v1.so",
+    "",
+    "",
+    "data/test-diff-suppr/test11-add-data-member-1.1.suppr",
+    "--no-default-suppression --no-show-locs --no-redundant",
+    "data/test-diff-suppr/test11-add-data-member-report-1.1.txt",
+    "output/test-diff-suppr/test11-add-data-member-report-1.1_1.txt"
+  },
   {
     "data/test-diff-suppr/libtest11-add-data-member-v0.so",
     "data/test-diff-suppr/libtest11-add-data-member-v1.so",
@@ -536,6 +556,16 @@ InOutSpec in_out_specs[] =
     "data/test-diff-suppr/test11-add-data-member-report-1.txt",
     "output/test-diff-suppr/test11-add-data-member-report-1_2.txt"
   },
+  {
+    "data/test-diff-suppr/libtest11-add-data-member-v0.so",
+    "data/test-diff-suppr/libtest11-add-data-member-v1.so",
+    "",
+    "",
+    "data/test-diff-suppr/test11-add-data-member-2.1.suppr",
+    "--no-default-suppression --no-show-locs --no-redundant",
+    "data/test-diff-suppr/test11-add-data-member-report-1.1.txt",
+    "output/test-diff-suppr/test11-add-data-member-report-1.1_2.txt"
+  },
   {
     "data/test-diff-suppr/libtest11-add-data-member-v0.so",
     "data/test-diff-suppr/libtest11-add-data-member-v1.so",
@@ -546,6 +576,16 @@ InOutSpec in_out_specs[] =
     "data/test-diff-suppr/test11-add-data-member-report-1.txt",
     "output/test-diff-suppr/test11-add-data-member-report-1_3.txt"
   },
+  {
+    "data/test-diff-suppr/libtest11-add-data-member-v0.so",
+    "data/test-diff-suppr/libtest11-add-data-member-v1.so",
+    "",
+    "",
+    "data/test-diff-suppr/test11-add-data-member-3.1.suppr",
+    "--no-default-suppression --no-show-locs --no-redundant",
+    "data/test-diff-suppr/test11-add-data-member-report-1.1.txt",
+    "output/test-diff-suppr/test11-add-data-member-report-1.1_3.txt"
+  },
   {
     "data/test-diff-suppr/libtest11-add-data-member-v0.so",
     "data/test-diff-suppr/libtest11-add-data-member-v1.so",
@@ -556,6 +596,16 @@ InOutSpec in_out_specs[] =
     "data/test-diff-suppr/test11-add-data-member-report-1.txt",
     "output/test-diff-suppr/test11-add-data-member-report-1_4.txt"
   },
+  {
+    "data/test-diff-suppr/libtest11-add-data-member-v0.so",
+    "data/test-diff-suppr/libtest11-add-data-member-v1.so",
+    "",
+    "",
+    "data/test-diff-suppr/test11-add-data-member-4.1.suppr",
+    "--no-default-suppression --no-show-locs --no-redundant",
+    "data/test-diff-suppr/test11-add-data-member-report-1.1.txt",
+    "output/test-diff-suppr/test11-add-data-member-report-1.1_4.txt"
+  },
   {
     "data/test-diff-suppr/libtest12-add-data-member-v0.so",
     "data/test-diff-suppr/libtest12-add-data-member-v1.so",
@@ -576,6 +626,16 @@ InOutSpec in_out_specs[] =
     "data/test-diff-suppr/test12-add-data-member-report-1.txt",
     "output/test-diff-suppr/test12-add-data-member-report-1.txt"
   },
+  {
+    "data/test-diff-suppr/libtest12-add-data-member-v0.so",
+    "data/test-diff-suppr/libtest12-add-data-member-v1.so",
+    "",
+    "",
+    "data/test-diff-suppr/test12-add-data-member-0.1.suppr",
+    "--no-default-suppression --no-show-locs --no-redundant",
+    "data/test-diff-suppr/test12-add-data-member-report-1.1.txt",
+    "output/test-diff-suppr/test12-add-data-member-report-1.1.txt"
+  },
   {
     "data/test-diff-suppr/libtest12-add-data-member-v0.so",
     "data/test-diff-suppr/libtest12-add-data-member-v1.so",
@@ -605,6 +665,16 @@ InOutSpec in_out_specs[] =
     "--no-default-suppression --no-show-locs --no-redundant",
     "data/test-diff-suppr/test13-suppr-through-pointer-report-1.txt",
     "output/test-diff-suppr/test13-suppr-through-pointer-report-1.txt"
+  },
+    {
+    "data/test-diff-suppr/libtest13-suppr-through-pointer-v0.so",
+    "data/test-diff-suppr/libtest13-suppr-through-pointer-v1.so",
+    "",
+    "",
+    "data/test-diff-suppr/test13-suppr-through-pointer-0.1.suppr",
+    "--no-default-suppression --no-show-locs --no-redundant",
+    "data/test-diff-suppr/test13-suppr-through-pointer-report-1.1.txt",
+    "output/test-diff-suppr/test13-suppr-through-pointer-report-1.1.txt"
   },
   {
     "data/test-diff-suppr/test14-suppr-non-redundant-v0.o",
@@ -1706,6 +1776,16 @@ InOutSpec in_out_specs[] =
     "data/test-diff-suppr/test35-leaf-report-0.txt",
     "output/test-diff-suppr/test35-leaf-report-0.txt"
   },
+  {
+    "data/test-diff-suppr/libtest35-leaf-v0.so",
+    "data/test-diff-suppr/libtest35-leaf-v1.so",
+    "",
+    "",
+    "data/test-diff-suppr/test35-leaf.1.suppr",
+    "--no-default-suppression --leaf-changes-only --impacted-interfaces",
+    "data/test-diff-suppr/test35-leaf-report-0.1.txt",
+    "output/test-diff-suppr/test35-leaf-report-0.1.txt"
+  },
   {
     "data/test-diff-suppr/libtest36-leaf-v0.so",
     "data/test-diff-suppr/libtest36-leaf-v1.so",
-- 
2.39.2


^ permalink raw reply	[flat|nested] 14+ messages in thread

* [PATCH 05/13] suppression: Support offset_of_{first,last}_data_member_regexp offset selectors
  2023-03-02 18:53 ` [PATCH 00/13] Support negative suppression specifications Dodji Seketeli
                     ` (3 preceding siblings ...)
  2023-03-02 18:58   ` [PATCH 04/13] suppression: Support the has_size_change property for suppress_type Dodji Seketeli
@ 2023-03-02 18:59   ` Dodji Seketeli
  2023-03-02 18:59   ` [PATCH 06/13] comparison, suppression: Support [allow_type] directive Dodji Seketeli
                     ` (7 subsequent siblings)
  12 siblings, 0 replies; 14+ messages in thread
From: Dodji Seketeli @ 2023-03-02 18:59 UTC (permalink / raw)
  To: Dodji Seketeli; +Cc: libabigail, ckalina

Hello,

This patch adds support for two data member offset selector
expressions for properties of the [suppress_type] directive:

offset_of_first_data_member_regexp() and offset_of_last_data_member_regexp().

These function-call expressions take a regular expression argument and
evaluate to the offset of the first (resp. last) data member matching
the regular expression argument.

An example of their use would be be:

    [suppress_type]
      type_kind = struct
      has_data_member_inserted_between =
	{
	  offset_of_first_data_member_regexp(^__special_padding_space),
	  offset_of_last_data_member_regexp(^__special_padding_space)
	}

This would be useful to suppress change reports involving a struct
which has "padding" data members added on-purpose like:

    struct S
    {
      int member0;
      char member1;
      unsigned __special_padding_space1;
      unsigned __special_padding_space2;
      unsigned __special_padding_space3;
    };

	* doc/manuals/libabigail-concepts.rst: Document the new
	properties.
	* include/abg-fwd.h: Forward declare comparison::{diff_context,
	diff_context_sptr, diff_context_wptr, diff, diff_wptr} and
	regex::regex_t_sptr.
	(find_first_data_member_matching_regexp)
	(find_last_data_member_matching_regexp): Declare new functions.
	* include/abg-suppression.h: Inject std::{string, shared_ptr,
	vector} and comparison::{diff, diff_context_sptr} into the suppr
	namespace.  Remove the "abg-comparison.h" header.
	* src/abg-elf-helpers.cc: Include sstream.
	* src/abg-ir.cc (find_first_data_member_matching_regexp)
	(find_last_data_member_matching_regexp): Define new functions.
	* src/abg-suppression.cc
	(type_suppression::insertion_range::eval_boundary): Support
	evaluating "offset_of_first_data_member_regexp" and
	"offset_of_first_data_member_regexp".
	* src/abg-ctf-reader.cc: Include sstream.
	* tests/data/test-diff-suppr/test-has-data-member-inserted-between-1-report-[1-4].txt:
	New test reference outputs.
	* tests/data/test-diff-suppr/test-has-data-member-inserted-between-1-v[0-4].c:
	Source code of new test input.
	* tests/data/test-diff-suppr/test-has-data-member-inserted-between-1-v[0-4].o:
	New binary test input.
	* tests/data/test-diff-suppr/test-has-data-member-inserted-between-1.suppr:
	New suppression specification.
	* tests/data/Makefile.am: Add the new test input files to source
	distribution.
	* tests/test-diff-suppr.cc (in_out_specs): Add the new test input
	to this test harness.

Signed-off-by: Dodji Seketeli <dodji@redhat.com>
---
 doc/manuals/libabigail-concepts.rst           |  60 +++++++++++-----
 include/abg-fwd.h                             |  33 +++++++++
 include/abg-suppression.h                     |   9 ++-
 src/abg-ctf-reader.cc                         |   1 +
 src/abg-elf-helpers.cc                        |   1 +
 src/abg-ir.cc                                 |  45 ++++++++++++
 src/abg-suppression.cc                        |  64 +++++++++++++-----
 tests/data/Makefile.am                        |  15 ++++
 ...ata-member-inserted-between-1-report-1.txt |   3 +
 ...ata-member-inserted-between-1-report-2.txt |  13 ++++
 ...ata-member-inserted-between-1-report-3.txt |  12 ++++
 ...ata-member-inserted-between-1-report-4.txt |  11 +++
 ...st-has-data-member-inserted-between-1-v0.c |  10 +++
 ...st-has-data-member-inserted-between-1-v0.o | Bin 0 -> 3088 bytes
 ...st-has-data-member-inserted-between-1-v1.c |  10 +++
 ...st-has-data-member-inserted-between-1-v1.o | Bin 0 -> 3088 bytes
 ...st-has-data-member-inserted-between-1-v2.c |  11 +++
 ...st-has-data-member-inserted-between-1-v2.o | Bin 0 -> 3152 bytes
 ...st-has-data-member-inserted-between-1-v3.c |  11 +++
 ...st-has-data-member-inserted-between-1-v3.o | Bin 0 -> 3152 bytes
 ...st-has-data-member-inserted-between-1-v4.c |  10 +++
 ...st-has-data-member-inserted-between-1-v4.o | Bin 0 -> 3152 bytes
 ...t-has-data-member-inserted-between-1.suppr |   7 ++
 tests/test-diff-suppr.cc                      |  42 +++++++++++-
 24 files changed, 328 insertions(+), 40 deletions(-)
 create mode 100644 tests/data/test-diff-suppr/test-has-data-member-inserted-between-1-report-1.txt
 create mode 100644 tests/data/test-diff-suppr/test-has-data-member-inserted-between-1-report-2.txt
 create mode 100644 tests/data/test-diff-suppr/test-has-data-member-inserted-between-1-report-3.txt
 create mode 100644 tests/data/test-diff-suppr/test-has-data-member-inserted-between-1-report-4.txt
 create mode 100644 tests/data/test-diff-suppr/test-has-data-member-inserted-between-1-v0.c
 create mode 100644 tests/data/test-diff-suppr/test-has-data-member-inserted-between-1-v0.o
 create mode 100644 tests/data/test-diff-suppr/test-has-data-member-inserted-between-1-v1.c
 create mode 100644 tests/data/test-diff-suppr/test-has-data-member-inserted-between-1-v1.o
 create mode 100644 tests/data/test-diff-suppr/test-has-data-member-inserted-between-1-v2.c
 create mode 100644 tests/data/test-diff-suppr/test-has-data-member-inserted-between-1-v2.o
 create mode 100644 tests/data/test-diff-suppr/test-has-data-member-inserted-between-1-v3.c
 create mode 100644 tests/data/test-diff-suppr/test-has-data-member-inserted-between-1-v3.o
 create mode 100644 tests/data/test-diff-suppr/test-has-data-member-inserted-between-1-v4.c
 create mode 100644 tests/data/test-diff-suppr/test-has-data-member-inserted-between-1-v4.o
 create mode 100644 tests/data/test-diff-suppr/test-has-data-member-inserted-between-1.suppr

diff --git a/doc/manuals/libabigail-concepts.rst b/doc/manuals/libabigail-concepts.rst
index dc5a4a5c..326a346e 100644
--- a/doc/manuals/libabigail-concepts.rst
+++ b/doc/manuals/libabigail-concepts.rst
@@ -493,24 +493,48 @@ names start with the string "private_data_member".
 	   offset of the insertion point of the data member, starting
 	   from the beginning of the relevant structure or class.
 
-	 - the keyword ``end`` which is a named constant which value
-	   equals the offset of the end of the of the structure or
-	   class.
-
-	 - the function call expression
-	   ``offset_of(data-member-name)`` where `data-member-name` is
-	   the name of a given data member of the relevant structure
-	   or class.  The value of this function call expression is an
-	   integer that represents the offset of the data member
-	   denoted by ``data-member-name``.
-
-	 - the function call expression
-	   ``offset_after(data-member-name)`` where `data-member-name`
-	   is the name of a given data member of the relevant
-	   structure or class.  The value of this function call
-	   expression is an integer that represents the offset of the
-	   point that comes right after the region occupied by the
-	   data member denoted by ``data-member-name``.
+.. _suppr_data_member_offset_selector_expressions_label:
+
+	 - data member offset selector expressions, such as:
+
+	     - the keyword ``end`` which is a named constant which value
+	       equals the offset of the end of the of the structure or
+	       class.
+
+	     - the function call expression
+	       ``offset_of(data-member-name)`` where `data-member-name` is
+	       the name of a given data member of the relevant structure
+	       or class.  The value of this function call expression is an
+	       integer that represents the offset of the data member
+	       denoted by ``data-member-name``.
+
+	     - the function call expression
+	       ``offset_after(data-member-name)`` where `data-member-name`
+	       is the name of a given data member of the relevant
+	       structure or class.  The value of this function call
+	       expression is an integer that represents the offset of the
+	       point that comes right after the region occupied by the
+	       data member denoted by ``data-member-name``.
+
+	     - the function call expression
+	       ``offset_of_first_data_member_regexp(data-member-name-regexp)``
+	       where `data-member-name-regexp` is a regular expression
+	       matching a data member.  The value of this function
+	       call expression is an integer that represents the
+	       offset of the first data member which name matches the
+	       regular expression argument.  If no data member of a
+	       given class type matches the regular expression, then
+	       the class type won't match the current directive.
+
+	     - the function call expression
+	       ``offset_of_last_data_member_regexp(data-member-name-regexp)``
+	       where `data-member-name-regexp` is a regular expression
+	       matching a data member.  The value of this function
+	       call expression is an integer that represents the
+	       offset of the last data member which name matches the
+	       regular expression argument.  If no data member of a
+	       given class type matches the regular expression, then
+	       the class type won't match the current directive.
 
   .. _suppr_has_data_member_inserted_between_label:
 
diff --git a/include/abg-fwd.h b/include/abg-fwd.h
index 4051fab5..fc3f4374 100644
--- a/include/abg-fwd.h
+++ b/include/abg-fwd.h
@@ -11,6 +11,7 @@
 #include <stdint.h>
 #include <cstddef>
 #include <cstdlib>
+#include <regex.h>
 #include <list>
 #include <memory>
 #include <ostream>
@@ -62,6 +63,31 @@ typedef unordered_set<string> string_set_type;
 // Pull in relational operators.
 using namespace std::rel_ops;
 
+namespace comparison
+{
+class diff_context;
+
+/// Convenience typedef for a shared pointer of @ref diff_context.
+typedef shared_ptr<diff_context> diff_context_sptr;
+
+/// Convenience typedef for a weak pointer of @ref diff_context.
+typedef weak_ptr<diff_context> diff_context_wptr;
+
+class diff;
+
+/// Convenience typedef for a shared_ptr for the @ref diff class
+typedef shared_ptr<diff> diff_sptr;
+
+/// Convenience typedef for a weak_ptr for the @ref diff class
+typedef weak_ptr<diff> diff_wptr;
+}
+
+namespace regex
+{
+/// A convenience typedef for a shared pointer of regex_t.
+typedef std::shared_ptr<regex_t> regex_t_sptr;
+}// end namespace regex
+
 namespace ir
 {
 
@@ -1472,6 +1498,13 @@ build_internal_underlying_enum_type_name(const string &base_name,
 					 bool is_anonymous,
 					 uint64_t size);
 
+var_decl_sptr
+find_first_data_member_matching_regexp(const class_or_union& t,
+				       const regex::regex_t_sptr& r);
+
+var_decl_sptr
+find_last_data_member_matching_regexp(const class_or_union& t,
+				      const regex::regex_t_sptr& regex);
 } // end namespace ir
 
 using namespace abigail::ir;
diff --git a/include/abg-suppression.h b/include/abg-suppression.h
index a51fdc5a..bfc37a40 100644
--- a/include/abg-suppression.h
+++ b/include/abg-suppression.h
@@ -11,7 +11,7 @@
 #include <unordered_set>
 
 #include "abg-ini.h"
-#include "abg-comparison.h"
+#include "abg-ir.h"
 
 namespace abigail
 {
@@ -29,9 +29,12 @@ class fe_iface;
 /// that are defined in this namespace.
 namespace suppr
 {
-
-using namespace abigail::comparison;
 using std::unordered_set;
+using std::string;
+using std::shared_ptr;
+using std::vector;
+using comparison::diff;
+using comparison::diff_context_sptr;
 
 /// Base type of the suppression specifications types.
 ///
diff --git a/src/abg-ctf-reader.cc b/src/abg-ctf-reader.cc
index 7db8dbc6..9b34ee2e 100644
--- a/src/abg-ctf-reader.cc
+++ b/src/abg-ctf-reader.cc
@@ -14,6 +14,7 @@
 #include "config.h"
 
 #include <fcntl.h> /* For open(3) */
+#include <sstream>
 #include <iostream>
 #include <memory>
 #include <map>
diff --git a/src/abg-elf-helpers.cc b/src/abg-elf-helpers.cc
index d61114bf..8ffeefb3 100644
--- a/src/abg-elf-helpers.cc
+++ b/src/abg-elf-helpers.cc
@@ -12,6 +12,7 @@
 #include <unistd.h>
 #include <limits.h>
 #include <elfutils/libdwfl.h>
+#include <sstream>
 #include "abg-elf-helpers.h"
 #include "abg-tools-utils.h"
 
diff --git a/src/abg-ir.cc b/src/abg-ir.cc
index ef19eb41..015aa2b3 100644
--- a/src/abg-ir.cc
+++ b/src/abg-ir.cc
@@ -28,6 +28,7 @@ ABG_BEGIN_EXPORT_DECLARATIONS
 #include "abg-interned-str.h"
 #include "abg-ir.h"
 #include "abg-corpus.h"
+#include "abg-regex.h"
 
 ABG_END_EXPORT_DECLARATIONS
 // </headers defining libabigail's API>
@@ -26609,6 +26610,50 @@ build_internal_underlying_enum_type_name(const string &base_name,
   return o.str();
 }
 
+/// Find the first data member of a class or union which name matches
+/// a regular expression.
+///
+/// @param t the class or union to consider.
+///
+/// @param r the regular expression to consider.
+///
+/// @return the data member matched by @p r or nil if none was found.
+var_decl_sptr
+find_first_data_member_matching_regexp(const class_or_union& t,
+				       const regex::regex_t_sptr& r)
+{
+  for (auto data_member : t.get_data_members())
+    {
+      if (regex::match(r, data_member->get_name()))
+	return data_member;
+    }
+
+  return var_decl_sptr();
+}
+
+/// Find the last data member of a class or union which name matches
+/// a regular expression.
+///
+/// @param t the class or union to consider.
+///
+/// @param r the regular expression to consider.
+///
+/// @return the data member matched by @p r or nil if none was found.
+var_decl_sptr
+find_last_data_member_matching_regexp(const class_or_union& t,
+				      const regex::regex_t_sptr& regex)
+{
+  auto d = t.get_data_members().rbegin();
+  auto e = t.get_data_members().rend();
+  for (; d != e; ++d)
+    {
+      if (regex::match(regex, (*d)->get_name()))
+	return *d;
+    }
+
+  return var_decl_sptr();
+}
+
 bool
 ir_traversable_base::traverse(ir_node_visitor&)
 {return true;}
diff --git a/src/abg-suppression.cc b/src/abg-suppression.cc
index 54d2d917..a79deb35 100644
--- a/src/abg-suppression.cc
+++ b/src/abg-suppression.cc
@@ -24,6 +24,7 @@ ABG_BEGIN_EXPORT_DECLARATIONS
 #include "abg-suppression.h"
 #include "abg-tools-utils.h"
 #include "abg-fe-iface.h"
+#include "abg-comparison.h"
 
 ABG_END_EXPORT_DECLARATIONS
 // </headers defining libabigail's API>
@@ -36,6 +37,9 @@ namespace abigail
 namespace suppr
 {
 
+// Inject the abigail::comparison namespace in here.
+using namespace comparison;
+
 using std::dynamic_pointer_cast;
 using regex::regex_t_sptr;
 
@@ -1404,32 +1408,56 @@ type_suppression::insertion_range::eval_boundary(const boundary_sptr	boundary,
     {
       ini::function_call_expr_sptr fn_call = b->as_function_call_expr();
       if ((fn_call->get_name() == "offset_of"
-	   || fn_call->get_name() == "offset_after")
+	   || fn_call->get_name() == "offset_after"
+	   || fn_call->get_name() == "offset_of_first_data_member_regexp"
+	   || fn_call->get_name() == "offset_of_last_data_member_regexp")
 	  && fn_call->get_arguments().size() == 1)
 	{
-	  string member_name = fn_call->get_arguments()[0];
-	  for (class_decl::data_members::const_iterator it =
-		 context->get_data_members().begin();
-	       it != context->get_data_members().end();
-	       ++it)
+	  if (fn_call->get_name() == "offset_of"
+	      || fn_call->get_name() == "offset_after")
 	    {
-	      if (!get_data_member_is_laid_out(**it))
-		continue;
-	      if ((*it)->get_name() == member_name)
+	      string member_name = fn_call->get_arguments()[0];
+	      for (class_decl::data_members::const_iterator it =
+		     context->get_data_members().begin();
+		   it != context->get_data_members().end();
+		   ++it)
 		{
-		  if (fn_call->get_name() == "offset_of")
-		    value = get_data_member_offset(*it);
-		  else if (fn_call->get_name() == "offset_after")
+		  if (!get_data_member_is_laid_out(**it))
+		    continue;
+		  if ((*it)->get_name() == member_name)
 		    {
-		      if (!get_next_data_member_offset(context, *it, value))
+		      if (fn_call->get_name() == "offset_of")
+			value = get_data_member_offset(*it);
+		      else if (fn_call->get_name() == "offset_after")
 			{
-			  value = get_data_member_offset(*it) +
-			    (*it)->get_type()->get_size_in_bits();
+			  if (!get_next_data_member_offset(context, *it, value))
+			    {
+			      value = get_data_member_offset(*it) +
+				(*it)->get_type()->get_size_in_bits();
+			    }
 			}
+		      else
+			// We should not reach this point.
+			abort();
+		      return true;
 		    }
-		  else
-		    // We should not reach this point.
-		    abort();
+		}
+	    }
+	  else if (fn_call->get_name() == "offset_of_first_data_member_regexp"
+		   || fn_call->get_name() == "offset_of_last_data_member_regexp")
+	    {
+	      string name_regexp = fn_call->get_arguments()[0];
+	      auto r = regex::compile(name_regexp);
+	      var_decl_sptr dm;
+
+	      if (fn_call->get_name() == "offset_of_first_data_member_regexp")
+		dm = find_first_data_member_matching_regexp(*context, r);
+	      else if (fn_call->get_name() == "offset_of_last_data_member_regexp")
+		dm = find_last_data_member_matching_regexp(*context, r);
+
+	      if (dm)
+		{
+		  value = get_data_member_offset(dm);
 		  return true;
 		}
 	    }
diff --git a/tests/data/Makefile.am b/tests/data/Makefile.am
index a37a364b..a343759e 100644
--- a/tests/data/Makefile.am
+++ b/tests/data/Makefile.am
@@ -1765,6 +1765,21 @@ test-diff-suppr/test-has-data-member-v0.cc \
 test-diff-suppr/test-has-data-member-v0.o \
 test-diff-suppr/test-has-data-member-v1.cc \
 test-diff-suppr/test-has-data-member-v1.o \
+test-diff-suppr/test-has-data-member-inserted-between-1-report-1.txt \
+test-diff-suppr/test-has-data-member-inserted-between-1-report-2.txt \
+test-diff-suppr/test-has-data-member-inserted-between-1-report-3.txt \
+test-diff-suppr/test-has-data-member-inserted-between-1-report-4.txt \
+test-diff-suppr/test-has-data-member-inserted-between-1-v0.c \
+test-diff-suppr/test-has-data-member-inserted-between-1-v0.o \
+test-diff-suppr/test-has-data-member-inserted-between-1-v1.c \
+test-diff-suppr/test-has-data-member-inserted-between-1-v1.o \
+test-diff-suppr/test-has-data-member-inserted-between-1-v2.c \
+test-diff-suppr/test-has-data-member-inserted-between-1-v2.o \
+test-diff-suppr/test-has-data-member-inserted-between-1-v3.c \
+test-diff-suppr/test-has-data-member-inserted-between-1-v3.o \
+test-diff-suppr/test-has-data-member-inserted-between-1-v4.c \
+test-diff-suppr/test-has-data-member-inserted-between-1-v4.o \
+test-diff-suppr/test-has-data-member-inserted-between-1.suppr \
 \
 test-diff-dwarf-abixml/test0-pr19026-libvtkIOSQL-6.1.so.1 \
 test-diff-dwarf-abixml/test0-pr19026-libvtkIOSQL-6.1.so.1.abi \
diff --git a/tests/data/test-diff-suppr/test-has-data-member-inserted-between-1-report-1.txt b/tests/data/test-diff-suppr/test-has-data-member-inserted-between-1-report-1.txt
new file mode 100644
index 00000000..fa4b7506
--- /dev/null
+++ b/tests/data/test-diff-suppr/test-has-data-member-inserted-between-1-report-1.txt
@@ -0,0 +1,3 @@
+Functions changes summary: 0 Removed, 0 Changed, 0 Added function
+Variables changes summary: 0 Removed, 0 Changed (1 filtered out), 0 Added variable
+
diff --git a/tests/data/test-diff-suppr/test-has-data-member-inserted-between-1-report-2.txt b/tests/data/test-diff-suppr/test-has-data-member-inserted-between-1-report-2.txt
new file mode 100644
index 00000000..605fb3c2
--- /dev/null
+++ b/tests/data/test-diff-suppr/test-has-data-member-inserted-between-1-report-2.txt
@@ -0,0 +1,13 @@
+Functions changes summary: 0 Removed, 0 Changed, 0 Added function
+Variables changes summary: 0 Removed, 1 Changed, 0 Added variable
+
+1 Changed variable:
+
+  [C] 'S s' was changed at test-has-data-member-inserted-between-1-v2.c:11:1:
+    type of variable changed:
+      type size hasn't changed
+      1 data member insertion:
+        'char data_member_incorrectly_inserted', at offset 32 (in bits) at test-has-data-member-inserted-between-1-v2.c:4:1
+      1 data member change:
+        'char member1' offset changed from 32 to 40 (in bits) (by +8 bits)
+
diff --git a/tests/data/test-diff-suppr/test-has-data-member-inserted-between-1-report-3.txt b/tests/data/test-diff-suppr/test-has-data-member-inserted-between-1-report-3.txt
new file mode 100644
index 00000000..e87aa121
--- /dev/null
+++ b/tests/data/test-diff-suppr/test-has-data-member-inserted-between-1-report-3.txt
@@ -0,0 +1,12 @@
+Functions changes summary: 0 Removed, 0 Changed, 0 Added function
+Variables changes summary: 0 Removed, 1 Changed, 0 Added variable
+
+1 Changed variable:
+
+  [C] 'S s' was changed at test-has-data-member-inserted-between-1-v3.c:11:1:
+    size of symbol changed from 20 to 24
+    type of variable changed:
+      type size changed from 160 to 192 (in bits)
+      1 data member insertion:
+        'char data_member_incorrectly_inserted', at offset 160 (in bits) at test-has-data-member-inserted-between-1-v3.c:8:1
+
diff --git a/tests/data/test-diff-suppr/test-has-data-member-inserted-between-1-report-4.txt b/tests/data/test-diff-suppr/test-has-data-member-inserted-between-1-report-4.txt
new file mode 100644
index 00000000..dccae160
--- /dev/null
+++ b/tests/data/test-diff-suppr/test-has-data-member-inserted-between-1-report-4.txt
@@ -0,0 +1,11 @@
+Functions changes summary: 0 Removed, 0 Changed, 0 Added function
+Variables changes summary: 0 Removed, 1 Changed, 0 Added variable
+
+1 Changed variable:
+
+  [C] 'S s' was changed at test-has-data-member-inserted-between-1-v4.c:10:1:
+    type of variable changed:
+      type size hasn't changed
+      1 data member insertion:
+        'char data_member_incorrectly_inserted', at offset 40 (in bits) at test-has-data-member-inserted-between-1-v4.c:5:1
+
diff --git a/tests/data/test-diff-suppr/test-has-data-member-inserted-between-1-v0.c b/tests/data/test-diff-suppr/test-has-data-member-inserted-between-1-v0.c
new file mode 100644
index 00000000..ea309110
--- /dev/null
+++ b/tests/data/test-diff-suppr/test-has-data-member-inserted-between-1-v0.c
@@ -0,0 +1,10 @@
+struct S
+{
+  int member0;
+  char member1;
+  unsigned __special_padding_space1;
+  unsigned __special_padding_space2;
+  unsigned __special_padding_space3;
+};
+
+struct S s;
diff --git a/tests/data/test-diff-suppr/test-has-data-member-inserted-between-1-v0.o b/tests/data/test-diff-suppr/test-has-data-member-inserted-between-1-v0.o
new file mode 100644
index 0000000000000000000000000000000000000000..f091cba2e0dbef0ed5a03faace02638c0dd0cacf
GIT binary patch
literal 3088
zcmcgt&2Jl35TEthiJMK*q%NNYvPB@;s&D<#D1@LCiwM$+s)|5d5Xh{(FZL39ZSB6K
zMI4a0fCPsgI3P|O_zyVp7jWyJ!Id)yn0Y(T*}m9_2#JyQ&CG8;-{*e$&b@a_jsu1q
zxDBV8K>;2up2}@4w&4=2!*?iGz^M}C((ob;hRPqnWyh%=qOOy=izMc9xjI6ztntdS
zLb<Bt1uZXYc~Q%2P$^g9L_x_}pzu;zH?xp|v$XUQ0vB{;GJ&%K?&{7}=S}Yo?{00|
zL+@9>^KN_YDmJ+8yt-QR+>I+6YtLZqn&VbIZ^t8Nr-l<@SDeklCb-qya<}%@bERi7
z_Y3%ys0irkYN<H1P^qBp26D%7{#;!lMiapgg$+HYlCx2|n5WUf$4M6V6B+q5!;u_y
zW!ivFC!5G#91c5^FpA=&k9ydX&3T5_Ji{&M4Z;-m?|<m;HDC9e&EQth^jnQqtFhT?
z`q$pa!goT&2GP3ii;+4`<ZfRkGL3tvhG}oG`*3?pY;B5@mM{9;WfPRllo*7Wh(Z;L
zymt{NnM{?8L|3W@QYNA)P8vZE>Vxq})}wLsSzPbOsy>XnVK?rF@vxo_6ox68CgHGv
z3&iVDCXb|&<Rp$TK%^4$pU65D&+1fJiz12-4@Gu7nWX<`sdFvCxtw8tZ*RxHhC6rt
zUu>H`O8+p>(JR9rf6a1nXWn?a&xK=J2z2wCUO;lu#R{{?1$}MLm|Q44Dg~<x7Yf>`
z=mK8n8RWw943zrEJ3oV5SUH0Rh^!apb=i2FYtORj%dmv;#aWw*gwyOK=fA|9aQX~A
z&4R%N>(3YP;wyBcfKz@`Z_C1eVf}3je~R_{7EbFz^ij@qkCcC(^@)Xl%K8HfXBEC?
zfzG4pksz~HG~WsVOfNGx>+l2XH!a-NZ!194!W)c3kbOQ<VHdedb3QPVk`EOG^pZf(
z%`ym5IShkHc8~j=I5`}nP0L}oo63_X%wZZPefea_FixZi!4gI!xTE#ncr=pu*ayj2
z$pA07$W+)n;?DmYyMo~~WwV|pTiXa0H_Kz-t0)k5`bW(z?VOz%?fBQwgU;o=`02gk
zOlwv~+CMudxwL6OdX%&2X#3K+pjW6p^~i+-K2+Nra1mkhGk1vo??k)!**OV+Oas!#
zIh&5Q?*UqDjGS*W=#x?)+xI=R&FcRf-cZvg^-r&--G79bof9Fr$Dnx+2qUt6JBZDy
z{}b20qoV+*KD`5W{Rv`}vw>fczT!f^7D1FveZ#+C%#PnJ<$<3VN6uqZ!@r#q|05sB
zI>#Ai;tc<u<Bi?$-|Pd;1`}ueNat1!r1@ixKj45hMCtvr*N@h8Rw<v)ue~mI{q;HV
Ozw`e6jP@eg@qYtbxIbh7

literal 0
HcmV?d00001

diff --git a/tests/data/test-diff-suppr/test-has-data-member-inserted-between-1-v1.c b/tests/data/test-diff-suppr/test-has-data-member-inserted-between-1-v1.c
new file mode 100644
index 00000000..dfb23703
--- /dev/null
+++ b/tests/data/test-diff-suppr/test-has-data-member-inserted-between-1-v1.c
@@ -0,0 +1,10 @@
+struct S
+{
+  int member0;
+  char member1;
+  int correctly_inserted_data_member;
+  unsigned __special_padding_space2;
+  unsigned __special_padding_space3;
+};
+
+struct S s;
diff --git a/tests/data/test-diff-suppr/test-has-data-member-inserted-between-1-v1.o b/tests/data/test-diff-suppr/test-has-data-member-inserted-between-1-v1.o
new file mode 100644
index 0000000000000000000000000000000000000000..657d4713252ea0a7c8f227446bd556ebbdeb490b
GIT binary patch
literal 3088
zcmd5-OK%)S5U$zTwKpEyv9k%U0G^;Ak|;g9v$2dZa#&#zCnAb0qFf@-Y98&~iFapL
zGi@gk2P7^a!663@9CP4LaO4+|pTU(g2dL`lUQgO%iEyH%>8kqb)zz>0=CixEYL*3>
zEVv2BnnnSh%^%A>DfZwTtiq2dH^6EVWYh2t4Vvor;k;!v_feNg?RgRlx!xS3SeJN1
z;!P>fEegaJr93a?6=>8ONh+Xb%~5!*E+;dWgSD{mE&^v{Wio-a1oraQCF^77Bj-+Q
z(?Rdc;5auOdl@IVYQ4YQa_qH>Yb&o~?UrRX9cRlSXRC#cC|B%eZXN7qVY$=#<jvX}
znEP$~YE%SdyHY9-T~r!qyN2AdtiP6*h*2Z>p|B=<s#$Bbvqc&ie3<6RAmy=39gg`p
z<XH#${d~%!BpCIlK^!OP0QDf^-O~&=Aev+ukHqL{KS^_*2_E<3Km`3FBSga>gWY=%
z+#UZz*Y~{}p6_-$-EL>S>$_LJ!1A{Pp$x2h)n#LGnDXrbPkEL^s0LXy+<vmT!8X>}
zQP*VyHCG?n!^xPp<4OD|X%7<79wlKACW9auwX>mM*cQ*yU{t~x;_W!+2V8J+Vh0$&
zGLG{;;%&imk+*5`Qe<(mzt8f+=`;f}v0;!iva+HzEKu~zLM|S2o-&^u`CjzDo!2Wh
z0lPapTkaLyxvT$T+vI_&9O&rP;m^OTjCSRXr~6zwrlml;xalRN5N)hbMK;K5d%|Qx
z=}{?JZ8%fXjzt^rI#-Ylixnu%AMbnx*|2m1^$=Mt^>to*ywYyvvzK83<I7#^iiA^l
z2P*$L#R;cR<F6UGQsJ!<UVeqHm2k?h>unhLuS);az;~5?&%iGz{h@)=dJ#<vCg)Cd
z<@=8Ov4JZUzEc97M?FV^^jgsxGzd`ip5l6aepdQ*1GnYd3g8=fN8#Y*Psbt%k&CS0
zLoEsZM1V(63V30jgO~A9;Ke*V9N=TRKS7(6gD}ka(M#qa3(^69Ib@WiT!&x@EfU<9
zdNdi2IX?DYIuYE%tIcu|M0)2{1OJg-#5ihMucuDa(}KbE@@V)H3WOd1Q4342U?)a1
z{sZ)&b9q|)>|Uu%D@I1zKQkw}^k_hOR<PM<`qH_eSEx7hD1<$As5Vu=S%k??-y!<H
z6K&&X<|O<D4M<-WY&M#{duTB+a=xxWmy{BjzMrG5YX0A=4b}6c`P1uZ&Ob)X%!v@(
zRiJ(k2qQ9m`-oN5f3E7^mQesypYFR^e~K98?BG|XZ&jh+${@<RzUE&m%#7cu6@g!A
zN0mpbn*VT0{Bw06+bT{o9jEzEDqh<)|J^*$%An)4AL(kU8j}2lir-TKD~QtjXRaTu
bX;mp#onLcZ%=)XR#Q&l8?-#U}$&CL8X>~%d

literal 0
HcmV?d00001

diff --git a/tests/data/test-diff-suppr/test-has-data-member-inserted-between-1-v2.c b/tests/data/test-diff-suppr/test-has-data-member-inserted-between-1-v2.c
new file mode 100644
index 00000000..ee7efcb4
--- /dev/null
+++ b/tests/data/test-diff-suppr/test-has-data-member-inserted-between-1-v2.c
@@ -0,0 +1,11 @@
+struct S
+{
+  int member0;
+  char data_member_incorrectly_inserted;
+  char member1;
+  unsigned __special_padding_space1;
+  unsigned __special_padding_space2;
+  unsigned __special_padding_space3;
+};
+
+struct S s;
diff --git a/tests/data/test-diff-suppr/test-has-data-member-inserted-between-1-v2.o b/tests/data/test-diff-suppr/test-has-data-member-inserted-between-1-v2.o
new file mode 100644
index 0000000000000000000000000000000000000000..4e02e07835e2bef3243fb9fef2f0b414ca85e958
GIT binary patch
literal 3152
zcmd5;&2Jl35TEthiQ7%mq^|fX5Et>0R_xmAMj-^HSVWLit*Q!ygsLL5A20Sc_S)Lr
zghU_#LgIqN1r8iI^1r~514j=03tSKqhcff_jkA4OOOz8M?VFk3e7u>rZ^!=Z?c48E
z3<H=9xB;gcqX56mpUPb+cHttd!1pNEz^D^s(()WFOx5?`l3~;n)MZk0p2S?P)=yBZ
zO1viJnv~}jK1aMR<#{PDNVzWMWvEqaQJg`=m_s+HRApgvDHzuI=Mgw3s~{5?i(oEo
zT`}IUUbk*GHZAmi5iIM5WiDZ{E5=Jp4a;1;yt@1(*3mG`x@B!y<ZLvsDdmct&8>r3
z&n-6_Z$4dl3Ufb;PlYxCbSR+?Vl}i~Lv9$x-%E?cs1bZnSd~3hjMd78JdMn87^hJ`
z7GaY*?Df)#2qJITn|NUu#eLMhKwO8B82KV;&vI~Q89ESpnb*t1dr=&WlSBmB@C4;l
zB$)^y7<dWn-g&>d<G$8(UFW*vHaqQ3r@h{Bn``f4WqV$x40h*g(;j7qvDog5SR_$^
zs+R<V?c>c2dt==`>NM@Xs@8?pU_277a2!5}TKy<%4I|(4qrMjnTgf1^v15_M-mrk%
zh_}L29EePilYM{zb|SFWBhku4nx!r3NQ!nC?eE*^;bf8kne2g=+GMr!esP1mN81<K
z$3n!miv>Et|F*8jY65n5cD9;p_~x$ui`ORaTKPiPvI>7ZEVG)WlYzeb!WAwAn)yU1
zAh~Gb4rOG5{G`vAOehSWg4Kj`1?^Nc0YCK;GGU<vrTTFml#mIFXHW-`l|o;a*yFKY
zS2~><=P|z6HSb6`b@!FZe^GJ5={ZpP6B@2mc&30CC)Bk9PWfL}`i6#or1ZBm{8y#l
z(QrB!qI)@$HB$avrSEGv_xY)Yf1&g*HC(CitrF;-at?y{4C!oY1SonzaXvTty&`%w
zXR=PRnQ|TjxEkJ8I5_FaDD!;eS(5VsOPM&%z@b?Kj-RIBBx2|}q3{p;I9d0{Xp^$%
z`-wPu#Ox(r+!v3A45L_Z2yVe5!9A%5<Izaq4}=qsGvVMjZ>O0T@QqLn{6}^f<EUjm
zPfpWiLF0Tp3|~Qku+v}R+|te2nN^Q}1wH7#&WfLYZz|KWmXY3{o|9a<v>^SEv+1h)
z(!HSR)SY_d!m+wkcU8azgvpP;E&5MDw2hCRlkneYLAsx_>8ktQMT?G+^QHouq!dW^
zy@j^2`hQk0l<TDWX{ze=hluGp5rPjC$a8`)BHg!#SlRx+tNr(66aei{^FiN#f*9p&
z<5Q$>)JA-cY~%fze}aax_y?6d;zM>+30P(R^^EwR)P-!RIA$Ej{5uuTcIJQT7g`xO
wj{Qhir7|S>Un-vOAKiPJfBN~+nU-y}r|z$QuKNBfGve3OPw<auFOnYr4>AZ)uK)l5

literal 0
HcmV?d00001

diff --git a/tests/data/test-diff-suppr/test-has-data-member-inserted-between-1-v3.c b/tests/data/test-diff-suppr/test-has-data-member-inserted-between-1-v3.c
new file mode 100644
index 00000000..314803cf
--- /dev/null
+++ b/tests/data/test-diff-suppr/test-has-data-member-inserted-between-1-v3.c
@@ -0,0 +1,11 @@
+struct S
+{
+  int member0;
+  char member1;
+  unsigned __special_padding_space1;
+  unsigned __special_padding_space2;
+  unsigned __special_padding_space3;
+  char data_member_incorrectly_inserted;
+};
+
+struct S s;
diff --git a/tests/data/test-diff-suppr/test-has-data-member-inserted-between-1-v3.o b/tests/data/test-diff-suppr/test-has-data-member-inserted-between-1-v3.o
new file mode 100644
index 0000000000000000000000000000000000000000..1d6d30fafd91ede6b6e2a31cbb576d6a460574e0
GIT binary patch
literal 3152
zcmcgt&2QsW5Ff`$n_aivmhiE#K(h!$cg1r)T9s1NC8C0M1zN3kg@h0&IQDDeZQ?|>
zTUvnx2#E_47dUX>$o~RI4jeh~FK|If9AM`8P3nDhBq}6E@|&68e7@)B@uzRR{btFs
zfXRYeaHcT|@bmnc+)-i&9)T734&@41Rf23<o}q=Qd>^h_Ry9UlCAH^C%;a+Q6veW_
zD+`~YTvhU%k{6UbujFMVSC#*xP$^f!D20+WhapfZtH$OMu$;@!B5+A{K_;*k!Cu<F
zZoTTf;@qxnIq3a7IL<A{UczQqtQVGQj=g$qb@>VGqh{Gv$Juts*{b2Dv@7mxZUgLU
zX1QH^?a9(r%>6VzB`N~wP(W?ODrmcj+_J2{mKKT8M({ylRqd%{t(LB2X;hA*C<*(K
z4C=JQksS48+=Om7nMgnMhTVx51Yy)i-ScH@mZ3e%unvKjdfhB<H;nvo97{hPo}!${
zIF$kT122ZXJMYwYTQApJEqC2*)!WT>ySdSB)z|J}XZv2N4WfOcE=K85BzO8Ul5yyx
z>c#$G=VWVBY;K6-c3t%KHd>I%BozZM5!8gpnh;!A^kn*xl#ytO<8{}E#$Y^>jbI!+
z2pjz{Z4ASn*9-ezIBdj&6vI*(N8T`p3&a~iA`fLM$w?eyfQTh->{vFac+#NCN)$nO
za3GSS$t3<iOI_#)F60b*ySv-<HGFe7{>5ukcddA#YgvZh|1PrHg_D84``i`I1=`s}
z$RU|%V}&BJL4DHaOg7|(PtIz?rJQyq+JK*W0okxnfKvZB4+_YJ#dD~O$VzTsGz%16
z=yfI2sc{*D^SkDXgwyW6RLKEnZAOIC!}Pco7#CW9Du?GM)Xf}D`MKVvf&ZfQ*A4tJ
zt=}<lIv1k%GNyW@{CiqIFz^qx{)vHquJtbrT&wVn7U-UG?*#D~(m7NJ(Da<<d_MHQ
zB6=xfs!y`nY90f$47{mvaFf$f>h+MPamEKMrSc>Nmxc?ty(9rQmP5}CWbdetll5SX
zHYIz#UM!CvGJCNX_2t7M!!VK@f+biaxT|!3JQ_*-LAcR4l`eksB1t{}P<Q^{*fk8N
zE%SMDwhjvh=i_1cItqlH{e?42Cu8SUGyX;Np!+&2e)_%XOv^?_dVgk4a_P{5^nJ#r
ztLaPkf~Hev>X8X2`cmE10ap+vKmNA3uN}n9oRr~LT9Dq)*mO00@1n)T$azbHby9L<
z`o4v>qW*u<FO>VF{%NY3{RfDdIT3<)HOO;<Fe1~pi&#<p-*o+b6$L=`X+D_sCx}tb
zCO&!kS{LGTWE<CK{xKSg;vbZ<h!5CNCt#KNS2N;&)EBa$<Ct+A^KW%L+nN7iUTAIL
wIQAo5x5|*>f9iO?e{}C@{+Z`TXIfNhU*BKzT+R9`Gve3uFZc(v=gEx!2j78Ee*gdg

literal 0
HcmV?d00001

diff --git a/tests/data/test-diff-suppr/test-has-data-member-inserted-between-1-v4.c b/tests/data/test-diff-suppr/test-has-data-member-inserted-between-1-v4.c
new file mode 100644
index 00000000..13262920
--- /dev/null
+++ b/tests/data/test-diff-suppr/test-has-data-member-inserted-between-1-v4.c
@@ -0,0 +1,10 @@
+struct S
+{
+  int member0;
+  char member1;
+  char data_member_incorrectly_inserted;
+  unsigned __special_padding_space1;
+  unsigned __special_padding_space2;
+  unsigned __special_padding_space3;
+};
+struct S s;
diff --git a/tests/data/test-diff-suppr/test-has-data-member-inserted-between-1-v4.o b/tests/data/test-diff-suppr/test-has-data-member-inserted-between-1-v4.o
new file mode 100644
index 0000000000000000000000000000000000000000..2ed4bccf15f0499b3886733f7071b2dfddd55bb8
GIT binary patch
literal 3152
zcmd5;&2QsW5Ff`$o2}b!yIJwIK)tI4x-0C&*{YPHE)f;9E6{2MLP7`>9Q!r#HgO``
zEv-NTgv14j3miCb<bQ!92aX*07q}oK4$I8*o7DU2NGvBt@|&68e7u?G=Se<${jE1D
zh5<|l+=eraQGj3O&*Y92J8%hB;d_*8VAKgRX?cbgrs_Lz*)Zw}>N2T0Phu`t>!&DI
zC0>(qP0DkNpCewE^1PH6rCgWt3e>8#D9)f_%%K}ps<N=T6pV$1XA!s{s~{5?OJFW<
zUo~E_UbgNuwk-6%4wiM>GMBN~RpYtkhGni@SzCD=>u4Be-LkeVayA;+lyb$+<~G2r
z=axH-SD&mrfw`Z?r$U<mI+Rcwu^QTLA~y`<@8u<8)CfK(tjV4###-fKo<`<4iqohc
zi_oSHyWMmmg2*3sCw>@4aUXR*5Vv3?Mm>?VW;uAX3~dPg%<tym-6#&mNg{%5c#3i=
zl1zjU4EzN4?!Ig9dN0|Y=iYKXyWMKHTN`c9UVj@a+xIhNaN0L)XOtbqVy7=+kwgKi
zei96JPPR6k%?;<cZ99Ectq0A)cqE$PIJ_S<`%%^$Mm@h5_5EnrOa_^Q9g8IPhXve0
zycwqAP-KFfoI?z75`ncIi)JR$ENxOpQgp)T;J`_bCX)on<P7}OA*+-3iyP!UIz5qn
zEJW;h&hdsD{BP@eq$Xf*cX!)f$2WK5U%WPX*UA^VmR0!UL7CMooecEd7p`z2(99=7
z0m(%ZcPJwh<R^X3WI|#16s#s(C}?M*3HYg(kO_+=DAkYipoC0VI)}Q5tQPvZ%pQ;Q
zy3*;?SityV*SsU))ZJGq|0Tr<r{_@Vk7>A4;i&>%oKQClIOTs{>6;q<tI}W7@Q;*!
zSHtOCh~Cectda8XDg8jhxzA5E{0pUjso_e6Z<RpzlyeZoXGmvLBS6tL#rfRm_loGn
zoXI-LX3BXC;AwbE;ozpHqs;Fi&yt)ESjxmn1}@DSaC>PAZX$-h8;aghA1CX<7;RGa
zd%Z*)KV<e3Kkkc%Lxxc-I0Uy~k>IY>gYjr2@CU+;$C+^Pn|IR85BNr?2L2<vf^pO`
zpC_m3u%K~19)_=?K-k%@aBk`3?A)ryzknWeUuVTnzc-a>MaxL<PtQp%9a@ln$k}w&
zed%7%bm~k!a^XZ>s(UKnBEsay-xmERAlku4&q?@iv><(uv+1h)-b0Iyk@J=UZBh!P
z`@V^`vig5kFO=(~`e~}_^@oV*IT3>Q6v%UeFe2Tzi&)wIzpMTCWfTDIPxC?Fe}Wk0
zY~fR+Z`4M7j%?%onSX+YviSRzJmN!kR0&vR{`HLbpVWnHsyJpG$NW1L&vxd2>K9rW
xIF9{DSEVu}`ClrY?;qWJnt%HF(V3QQwXg24ey;ldt25%))lcw`XfKi;{||IgP__U7

literal 0
HcmV?d00001

diff --git a/tests/data/test-diff-suppr/test-has-data-member-inserted-between-1.suppr b/tests/data/test-diff-suppr/test-has-data-member-inserted-between-1.suppr
new file mode 100644
index 00000000..094d1d67
--- /dev/null
+++ b/tests/data/test-diff-suppr/test-has-data-member-inserted-between-1.suppr
@@ -0,0 +1,7 @@
+[suppress_type]
+  type_kind = struct
+  has_data_member_inserted_between =
+    {
+      offset_of_first_data_member_regexp(^__special_padding_space),
+      offset_of_last_data_member_regexp(^__special_padding_space)
+    }
diff --git a/tests/test-diff-suppr.cc b/tests/test-diff-suppr.cc
index a463b341..11be9b9a 100644
--- a/tests/test-diff-suppr.cc
+++ b/tests/test-diff-suppr.cc
@@ -2206,7 +2206,7 @@ InOutSpec in_out_specs[] =
     "data/test-diff-suppr/test-has-data-member-output-2.txt",
     "output/test-diff-suppr/test-has-data-member-output-2.4.txt"
   },
-    {
+  {
     "data/test-diff-suppr/test-has-data-member-v0.o",
     "data/test-diff-suppr/test-has-data-member-v1.o",
     "",
@@ -2216,6 +2216,46 @@ InOutSpec in_out_specs[] =
     "data/test-diff-suppr/test-has-data-member-output-1.txt",
     "output/test-diff-suppr/test-has-data-member-output-1.4.txt"
   },
+  {
+    "data/test-diff-suppr/test-has-data-member-inserted-between-1-v0.o",
+    "data/test-diff-suppr/test-has-data-member-inserted-between-1-v1.o",
+    "",
+    "",
+    "data/test-diff-suppr/test-has-data-member-inserted-between-1.suppr",
+    "--drop-private-types --no-default-suppression",
+    "data/test-diff-suppr/test-has-data-member-inserted-between-1-report-1.txt",
+    "output/test-diff-suppr/test-has-data-member-inserted-between-1-report-1.txt"
+  },
+  {
+    "data/test-diff-suppr/test-has-data-member-inserted-between-1-v0.o",
+    "data/test-diff-suppr/test-has-data-member-inserted-between-1-v2.o",
+    "",
+    "",
+    "data/test-diff-suppr/test-has-data-member-inserted-between-1.suppr",
+    "--drop-private-types --no-default-suppression",
+    "data/test-diff-suppr/test-has-data-member-inserted-between-1-report-2.txt",
+    "output/test-diff-suppr/test-has-data-member-inserted-between-1-report-2.txt"
+  },
+  {
+    "data/test-diff-suppr/test-has-data-member-inserted-between-1-v0.o",
+    "data/test-diff-suppr/test-has-data-member-inserted-between-1-v3.o",
+    "",
+    "",
+    "data/test-diff-suppr/test-has-data-member-inserted-between-1.suppr",
+    "--drop-private-types --no-default-suppression",
+    "data/test-diff-suppr/test-has-data-member-inserted-between-1-report-3.txt",
+    "output/test-diff-suppr/test-has-data-member-inserted-between-1-report-3.txt"
+  },
+  {
+    "data/test-diff-suppr/test-has-data-member-inserted-between-1-v0.o",
+    "data/test-diff-suppr/test-has-data-member-inserted-between-1-v4.o",
+    "",
+    "",
+    "data/test-diff-suppr/test-has-data-member-inserted-between-1.suppr",
+    "--drop-private-types --no-default-suppression",
+    "data/test-diff-suppr/test-has-data-member-inserted-between-1-report-4.txt",
+    "output/test-diff-suppr/test-has-data-member-inserted-between-1-report-4.txt"
+  },
   // This should be the last entry
   {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}
 };
-- 
2.39.2


-- 
		Dodji


^ permalink raw reply	[flat|nested] 14+ messages in thread

* [PATCH 06/13] comparison, suppression: Support [allow_type] directive
  2023-03-02 18:53 ` [PATCH 00/13] Support negative suppression specifications Dodji Seketeli
                     ` (4 preceding siblings ...)
  2023-03-02 18:59   ` [PATCH 05/13] suppression: Support offset_of_{first,last}_data_member_regexp offset selectors Dodji Seketeli
@ 2023-03-02 18:59   ` Dodji Seketeli
  2023-03-02 19:00   ` [PATCH 07/13] Misc white space fixes Dodji Seketeli
                     ` (6 subsequent siblings)
  12 siblings, 0 replies; 14+ messages in thread
From: Dodji Seketeli @ 2023-03-02 18:59 UTC (permalink / raw)
  To: Dodji Seketeli; +Cc: libabigail, ckalina

Hello,

This patch adds support for a new 'allow_type' suppression directive.
It suppresses all the changes that are NOT matched by the directive.
In other words, this directive determines the set of type changes that
are NOT suppressed.  Any other change is suppressed.  This thus called
a "negated suppression directive".

The way these negated suppression directives interact with the direct
suppression directives that already exist is the following.

The suppression evaluation pass visits every single diff node
(carrying a type change) of the diff graph.  Negated suppressions are
evaluated first, in order of occurrence.

There are thus, two alternatives:

    1/ At least one negated suppression matches the current diff node.
or
    2/ No negated suppression matches the current diff node.

In case of 1/ then direct suppression specifications are
considered. There are two alternatives:

    1.1/ At least one direct suppression matches the current diff node.
         The diff node is suppressed: categorized as being in the
         SUPPRESSED_CATEGORY category)
or
    1.2/ No direct suppression matches the current diff node.
         The diff node is not suppressed: categorized as being in the
         HAS_ALLOWED_CHANGE_CATEGORY category.

In case of 2/ then direct suppression specifications are
considered. There are two alternatives:

    2.1 At least one direct suppression matches the current diff node.
	The diff node is categorized as being in the
	SUPPRESSED_CATEGORY category, just like in 1.1.

    2.2 No direct suppression matches the current diff node.
	The diff node is not suppressed and not categorized.

As a result of the category propagation pass, a node which has a
parent node categorized as HAS_ALLOWED_CHANGE_CATEGORY is itself
categorized as HAS_PARENT_WITH_ALLOWED_CHANGE_CATEGORY.  A node which
has a descendant categorized as HAS_ALLOWED_CHANGE_CATEGORY will
itself be categorized as HAS_DESCENDANT_WITH_ALLOWED_CHANGE_CATEGORY.

Nodes that are categorized as HAS_ALLOWED_CHANGE_CATEGORY,
HAS_DESCENDANT_WITH_ALLOWED_CHANGE_CATEGORY and
HAS_PARENT_WITH_ALLOWED_CHANGE_CATEGORY are not suppressed by the
reporting passes.  This is needed for the reporting passes to emit the
impact sub-tree up to the diff node which carry the change that was
actually categorized as HAS_ALLOWED_CHANGE_CATEGORY.

	* include/abg-comparison.h: Include abg-suppression.h
	(diff, diff_context, diff_sptr, diff_context_sptr): Remove these
	forward decls from here.
	(enum diff_category::{HAS_ALLOWED_CHANGE_CATEGORY,
	HAS_DESCENDANT_WITH_ALLOWED_CHANGE_CATEGORY,
	HAS_PARENT_WITH_ALLOWED_CHANGE_CATEGORY}): Add new enumerators.
	(enum diff_category::EVERYTHING_CATEGORY): Update enumerator.
	(diff_context::{negated_suppressions, direct_suppressions}): Declare
	new member functions.
	(diff_context::suppressions): Add overload.
	(diff::{is_filtered_out_without_looking_at_allowed_changes,
	is_allowed_by_specific_negated_suppression,
	has_descendant_allowed_by_specific_negated_suppression,
	has_parent_allowed_by_specific_negated_suppression}): Declare new
	member functions.
	* include/abg-suppression.h (class negated_suppression_base, class
	negated_type_suppression): Declare new classes.
	(negated_suppression_sptr, negated_suppression_type): Define new
	typedefs.
	(is_negated_suppression): Declare new functions.
	* src/abg-suppression.cc
	(negated_suppression_base::{negated_suppression_base,
	~negated_suppression_base}): Define member functions.
	(negated_type_suppression::{negated_type_suppression,
	suppresses_diff, ~negated_type_suppression}): Likewise.
	(is_negated_suppression): Define functions.
	(read_type_suppression): Allow parsing the "allow_type" directive
	and instantiate a negated_type_suppression.
	* src/abg-comparison-priv.h
	(diff_context::priv::{negated_suppression_type_,
	direct_suppressions}): Define new data members.
	(diff::priv::is_filtered_out): A node categorized as
	HAS_DESCENDANT_ALLOWED_BY_SPECIFIC_NEGATED_SUPPRESSION,
	HAS_PARENT_ALLOWED_BY_SPECIFIC_NEGATED_SUPPRESSION and
	HAS_ALLOWED_CHANGE_CATEGORY is not filtered out.
	* src/abg-comparison.cc (diff_context::suppressions): Add a
	non-const overload.
	(diff_context::{negated,direct}_suppressions): Define new member
	function.
	(diff_context::add_suppression): Invalidate the cache data members
	diff_context::priv::{negated,direct}_suppressions_.
	(diff::is_filtered_out): A node categorized as
	HAS_DESCENDANT_ALLOWED_BY_SPECIFIC_NEGATED_SUPPRESSION,
	HAS_PARENT_ALLOWED_BY_SPECIFIC_NEGATED_SUPPRESSION and
	HAS_ALLOWED_CHANGE_CATEGORY is not filtered out.
	(diff::is_filtered_out_without_looking_at_allowed_changes): Define
	new member function.
	(diff::is_suppressed): If there is at least one negated
	suppression that match the diff node, then it's not suppressed,
	unless it's matched by a direct suppression.
	(diff::{is_allowed_by_specific_negated_suppression,
	has_descendant_allowed_by_specific_negated_suppression,
	has_parent_allowed_by_specific_negated_suppression}): Define new
	member functions.
	(operator<<(ostream& o, diff_category c)): Serialize
	HAS_{DESCENDANT_WITH,PARENT_WITH}_ALLOWED_CHANGE_CATEGORY
	enumerators.
	(category_propagation_visitor::visit_end): Do not propagate
	HAS_ALLOWED_CHANGE_CATEGORY,
	HAS_DESCENDANT_WITH_ALLOWED_CHANGE_CATEGORY and
	HAS_PARENT_WITH_ALLOWED_CHANGE_CATEGORY categories.
	(suppression_categorization_visitor::visit_begin): Categorize a
	node that is not suppressed by a direct suppression and is
	suppressed by a negated one as
	HAS_ALLOWED_CHANGE_CATEGORY. Propagate it to descendant nodes as
	HAS_PARENT_WITH_ALLOWED_CHANGE_CATEGORY ...
	(suppression_categorization_visitor::visit_end): ... and to parent
	node as HAS_DESCENDANT_WITH_ALLOWED_CHANGE_CATEGORY.
	* src/abg-default-reporter.cc (default::reporter): In the overload
	for typedef_diff, qualified_type_diff, reference_diff,
	fn_parm_diff, function_type_diff, array_diff, base_diff,
	function_decl_diff, report local changes only
	on node that are not filtered out wrt allowed changed.
	* tests/data/test-abidiff-exit/test-allow-type-array-suppr.txt:
	New test input.
	* tests/data/test-abidiff-exit/test-allow-type-array-v0--v1-report-{1,2}.txt:
	Likewise.
	* tests/data/test-abidiff-exit/test-allow-type-array-v0--v2-report-{1,2}.txt:
	Likewise.
	* tests/data/test-abidiff-exit/test-allow-type-array-v0--v3-report-{1,2}.txt:
	Likewise.
	* tests/data/test-abidiff-exit/test-allow-type-array-v{0,1,2,3}.c:
	Source code of new binary test inputs.
	* tests/data/test-abidiff-exit/test-allow-type-array-v{0,1,2,3}.o:
	New binary test inputs.
	* tests/data/test-abidiff-exit/test-allow-type-region-suppr.txt:
	New test input.
	* tests/data/test-abidiff-exit/test-allow-type-region-v0--v1-report-{1,2}.txt:
	* tests/data/test-abidiff-exit/test-allow-type-region-v0--v2-report-{1,2}.txt:
	Likewise.
	* tests/data/test-abidiff-exit/test-allow-type-region-v0--v3-report-{1,2}.txt:
	Likewise.
	* tests/data/test-abidiff-exit/test-allow-type-region-v0--v4-report-{1,2}.txt:
	Likewise.
	* tests/data/test-abidiff-exit/test-allow-type-region-v0--v5-report-{1,2}.txt:
	Likewise.
	* tests/data/test-abidiff-exit/test-allow-type-region-v{0,1,2,3,4,5}.c:
	Source code of new binary test input.
	* tests/data/test-abidiff-exit/test-allow-type-region-v{0,1,2,3,4,5}.o:
	New binary test inputs.
	* tests/data/test-abidiff-exit/test-allow-type-suppr{1,2}.txt: New
	test inputs.
	* tests/data/Makefile.am: Add the new testing files above to
	source distribution.

Signed-off-by: Dodji Seketeli <dodji@redhat.com>
---
 include/abg-comparison.h                      |  62 +++-
 include/abg-suppression.h                     |  69 +++-
 src/abg-comparison-priv.h                     |  17 +
 src/abg-comparison.cc                         | 289 +++++++++++++--
 src/abg-default-reporter.cc                   | 348 +++++++++---------
 src/abg-suppression.cc                        | 116 +++++-
 tests/data/Makefile.am                        |  40 ++
 .../test-allow-type-array-suppr.txt           |   7 +
 .../test-allow-type-array-v0--v1-report-1.txt |  23 ++
 .../test-allow-type-array-v0--v1-report-2.txt |   3 +
 .../test-allow-type-array-v0--v2-report-1.txt |  20 +
 .../test-allow-type-array-v0--v2-report-2.txt |  15 +
 .../test-allow-type-array-v0--v3-report-1.txt |  23 ++
 .../test-allow-type-array-v0--v3-report-2.txt |   3 +
 .../test-allow-type-array-v0.c                |  18 +
 .../test-allow-type-array-v0.o                | Bin 0 -> 3392 bytes
 .../test-allow-type-array-v1.c                |  20 +
 .../test-allow-type-array-v1.o                | Bin 0 -> 3448 bytes
 .../test-allow-type-array-v2.c                |  20 +
 .../test-allow-type-array-v2.o                | Bin 0 -> 3456 bytes
 .../test-allow-type-array-v3.c                |  20 +
 .../test-allow-type-array-v3.o                | Bin 0 -> 3456 bytes
 .../test-allow-type-region-suppr.txt          |  11 +
 ...test-allow-type-region-v0--v1-report-1.txt |  20 +
 ...test-allow-type-region-v0--v1-report-2.txt |   3 +
 ...test-allow-type-region-v0--v2-report-1.txt |  21 ++
 ...test-allow-type-region-v0--v2-report-2.txt |  16 +
 ...test-allow-type-region-v0--v3-report-1.txt |  23 ++
 ...test-allow-type-region-v0--v3-report-2.txt |  18 +
 ...test-allow-type-region-v0--v4-report-1.txt |  28 ++
 ...test-allow-type-region-v0--v4-report-2.txt |   3 +
 ...test-allow-type-region-v0--v5-report-1.txt |  30 ++
 ...test-allow-type-region-v0--v5-report-2.txt |  25 ++
 .../test-allow-type-region-v0.c               |  22 ++
 .../test-allow-type-region-v0.o               | Bin 0 -> 3576 bytes
 .../test-allow-type-region-v1.c               |  23 ++
 .../test-allow-type-region-v1.o               | Bin 0 -> 3632 bytes
 .../test-allow-type-region-v2.c               |  23 ++
 .../test-allow-type-region-v2.o               | Bin 0 -> 3632 bytes
 .../test-allow-type-region-v3.c               |  24 ++
 .../test-allow-type-region-v3.o               | Bin 0 -> 3688 bytes
 .../test-allow-type-region-v4.c               |  23 ++
 .../test-allow-type-region-v4.o               | Bin 0 -> 3640 bytes
 .../test-allow-type-region-v5.c               |  24 ++
 .../test-allow-type-region-v5.o               | Bin 0 -> 3696 bytes
 .../test-allow-type-suppr1.txt                |   7 +
 .../test-allow-type-suppr2.txt                |   7 +
 tests/test-abidiff-exit.cc                    | 187 ++++++++++
 48 files changed, 1442 insertions(+), 209 deletions(-)
 create mode 100644 tests/data/test-abidiff-exit/test-allow-type-array-suppr.txt
 create mode 100644 tests/data/test-abidiff-exit/test-allow-type-array-v0--v1-report-1.txt
 create mode 100644 tests/data/test-abidiff-exit/test-allow-type-array-v0--v1-report-2.txt
 create mode 100644 tests/data/test-abidiff-exit/test-allow-type-array-v0--v2-report-1.txt
 create mode 100644 tests/data/test-abidiff-exit/test-allow-type-array-v0--v2-report-2.txt
 create mode 100644 tests/data/test-abidiff-exit/test-allow-type-array-v0--v3-report-1.txt
 create mode 100644 tests/data/test-abidiff-exit/test-allow-type-array-v0--v3-report-2.txt
 create mode 100644 tests/data/test-abidiff-exit/test-allow-type-array-v0.c
 create mode 100644 tests/data/test-abidiff-exit/test-allow-type-array-v0.o
 create mode 100644 tests/data/test-abidiff-exit/test-allow-type-array-v1.c
 create mode 100644 tests/data/test-abidiff-exit/test-allow-type-array-v1.o
 create mode 100644 tests/data/test-abidiff-exit/test-allow-type-array-v2.c
 create mode 100644 tests/data/test-abidiff-exit/test-allow-type-array-v2.o
 create mode 100644 tests/data/test-abidiff-exit/test-allow-type-array-v3.c
 create mode 100644 tests/data/test-abidiff-exit/test-allow-type-array-v3.o
 create mode 100644 tests/data/test-abidiff-exit/test-allow-type-region-suppr.txt
 create mode 100644 tests/data/test-abidiff-exit/test-allow-type-region-v0--v1-report-1.txt
 create mode 100644 tests/data/test-abidiff-exit/test-allow-type-region-v0--v1-report-2.txt
 create mode 100644 tests/data/test-abidiff-exit/test-allow-type-region-v0--v2-report-1.txt
 create mode 100644 tests/data/test-abidiff-exit/test-allow-type-region-v0--v2-report-2.txt
 create mode 100644 tests/data/test-abidiff-exit/test-allow-type-region-v0--v3-report-1.txt
 create mode 100644 tests/data/test-abidiff-exit/test-allow-type-region-v0--v3-report-2.txt
 create mode 100644 tests/data/test-abidiff-exit/test-allow-type-region-v0--v4-report-1.txt
 create mode 100644 tests/data/test-abidiff-exit/test-allow-type-region-v0--v4-report-2.txt
 create mode 100644 tests/data/test-abidiff-exit/test-allow-type-region-v0--v5-report-1.txt
 create mode 100644 tests/data/test-abidiff-exit/test-allow-type-region-v0--v5-report-2.txt
 create mode 100644 tests/data/test-abidiff-exit/test-allow-type-region-v0.c
 create mode 100644 tests/data/test-abidiff-exit/test-allow-type-region-v0.o
 create mode 100644 tests/data/test-abidiff-exit/test-allow-type-region-v1.c
 create mode 100644 tests/data/test-abidiff-exit/test-allow-type-region-v1.o
 create mode 100644 tests/data/test-abidiff-exit/test-allow-type-region-v2.c
 create mode 100644 tests/data/test-abidiff-exit/test-allow-type-region-v2.o
 create mode 100644 tests/data/test-abidiff-exit/test-allow-type-region-v3.c
 create mode 100644 tests/data/test-abidiff-exit/test-allow-type-region-v3.o
 create mode 100644 tests/data/test-abidiff-exit/test-allow-type-region-v4.c
 create mode 100644 tests/data/test-abidiff-exit/test-allow-type-region-v4.o
 create mode 100644 tests/data/test-abidiff-exit/test-allow-type-region-v5.c
 create mode 100644 tests/data/test-abidiff-exit/test-allow-type-region-v5.o
 create mode 100644 tests/data/test-abidiff-exit/test-allow-type-suppr1.txt
 create mode 100644 tests/data/test-abidiff-exit/test-allow-type-suppr2.txt

diff --git a/include/abg-comparison.h b/include/abg-comparison.h
index 440080a5..839478bf 100644
--- a/include/abg-comparison.h
+++ b/include/abg-comparison.h
@@ -17,6 +17,7 @@
 #include "abg-corpus.h"
 #include "abg-diff-utils.h"
 #include "abg-reporter.h"
+#include "abg-suppression.h"
 
 namespace abigail
 {
@@ -46,14 +47,6 @@ using diff_utils::insertion;
 using diff_utils::deletion;
 using diff_utils::edit_script;
 
-class diff;
-
-/// Convenience typedef for a shared_ptr for the @ref diff class
-typedef shared_ptr<diff> diff_sptr;
-
-/// Convenience typedef for a weak_ptr for the @ref diff class
-typedef weak_ptr<diff> diff_wptr;
-
 /// Hasher for @ref diff_sptr.
 struct diff_sptr_hasher
 {
@@ -261,14 +254,6 @@ typedef unordered_map<string, elf_symbol_sptr> string_elf_symbol_map;
 /// value is a @ref var_diff_sptr.
 typedef unordered_map<string, var_diff_sptr> string_var_diff_ptr_map;
 
-class diff_context;
-
-/// Convenience typedef for a shared pointer of @ref diff_context.
-typedef shared_ptr<diff_context> diff_context_sptr;
-
-/// Convenience typedef for a weak pointer of @ref diff_context.
-typedef weak_ptr<diff_context> diff_context_wptr;
-
 class diff_node_visitor;
 
 class diff_traversable_base;
@@ -438,6 +423,25 @@ enum diff_category
   /// variable didn't change.
   BENIGN_INFINITE_ARRAY_CHANGE_CATEGORY = 1 << 21,
 
+  /// A diff node in this category carries a change that must be
+  /// reported, even if the diff node is also in the
+  /// SUPPRESSED_CATEGORY or PRIVATE_TYPE_CATEGORY categories.
+  /// Typically, this node matches a suppression specification like
+  /// the [allow_type] directive.
+  HAS_ALLOWED_CHANGE_CATEGORY = 1 << 22,
+
+  /// A diff node in this category has a descendant node that is in
+  /// the HAS_ALLOWED_CHANGE_CATEGORY category.  Nodes in this
+  /// category must be reported, even if they are also in the
+  /// SUPPRESSED_CATEGORY or PRIVATE_TYPE_CATEGORY categories.
+  HAS_DESCENDANT_WITH_ALLOWED_CHANGE_CATEGORY = 1 << 23,
+
+  /// A diff node in this category has a parent node that is in the
+  /// HAS_ALLOWED_CHANGE_CATEGORY category.  Nodes in this category
+  /// must be reported, even if they are also in the
+  /// SUPPRESSED_CATEGORY or PRIVATE_TYPE_CATEGORY categories.
+  HAS_PARENT_WITH_ALLOWED_CHANGE_CATEGORY = 1 << 24,
+
   /// A special enumerator that is the logical 'or' all the
   /// enumerators above.
   ///
@@ -466,6 +470,9 @@ enum diff_category
   | VAR_TYPE_CV_CHANGE_CATEGORY
   | VOID_PTR_TO_PTR_CHANGE_CATEGORY
   | BENIGN_INFINITE_ARRAY_CHANGE_CATEGORY
+  | HAS_ALLOWED_CHANGE_CATEGORY
+  | HAS_DESCENDANT_WITH_ALLOWED_CHANGE_CATEGORY
+  | HAS_PARENT_WITH_ALLOWED_CHANGE_CATEGORY
 }; // enum diff_category
 
 diff_category
@@ -730,9 +737,18 @@ public:
   void
   maybe_apply_filters(corpus_diff_sptr diff);
 
-  suppr::suppressions_type&
+  const suppr::suppressions_type&
   suppressions() const;
 
+  suppr::suppressions_type&
+  suppressions();
+
+  const suppr::suppressions_type&
+  negated_suppressions() const;
+
+  const suppr::suppressions_type&
+  direct_suppressions() const;
+
   void
   add_suppression(const suppr::suppression_sptr suppr);
 
@@ -1037,6 +1053,9 @@ public:
   bool
   is_filtered_out_wrt_non_inherited_categories() const;
 
+  bool
+  is_filtered_out_without_looking_at_allowed_changes() const;
+
   bool
   is_suppressed() const;
 
@@ -1049,6 +1068,15 @@ public:
   bool
   has_local_changes_to_be_reported() const;
 
+  bool
+  is_allowed_by_specific_negated_suppression() const;
+
+  bool
+  has_descendant_allowed_by_specific_negated_suppression() const;
+
+  bool
+  has_parent_allowed_by_specific_negated_suppression() const;
+
   virtual const string&
   get_pretty_representation() const;
 
diff --git a/include/abg-suppression.h b/include/abg-suppression.h
index bfc37a40..05b0e8b4 100644
--- a/include/abg-suppression.h
+++ b/include/abg-suppression.h
@@ -36,11 +36,17 @@ using std::vector;
 using comparison::diff;
 using comparison::diff_context_sptr;
 
-/// Base type of the suppression specifications types.
+/// Base type of a direct suppression specifications types.
 ///
 /// This abstracts a suppression specification.  It's a way to specify
 /// how to drop reports about a particular diff node on the floor, if
 /// it matches the supppression specification.
+///
+/// Note that a direct suppression specification suppresses (for
+/// reporting purposes) the diff node that it matches.  A negated
+/// suppression specification, however, suppresses a diff node that it
+/// DOES NOT match.  A Negated suppression specification is abstracted
+/// by the class @ref negated_suppression_base.
 class suppression_base
 {
 public:
@@ -143,6 +149,36 @@ typedef shared_ptr<type_suppression> type_suppression_sptr;
 /// Convenience typedef for vector of @ref type_suppression_sptr.
 typedef vector<type_suppression_sptr> type_suppressions_type;
 
+/// The base class of suppression specifications that are defined by
+/// the negation of matching clauses.
+///
+/// A direct suppression specification suppresses (for reporting
+/// purposes) the diff node that it matches.  A negated suppression
+/// specification suppresses a diff node that it DOES NOT match.
+class negated_suppression_base
+{
+public:
+  negated_suppression_base();
+
+  virtual ~negated_suppression_base();
+}; // end class negated_suppression_base.
+
+/// A convenience typedef for a shared pointer to @ref
+/// negated_suppression_base.
+typedef shared_ptr<negated_suppression_base> negated_suppression_sptr;
+
+/// Convenience typedef for a vector of @ref negated_suppression_sptr
+typedef vector<negated_suppression_sptr> negated_suppressions_type;
+
+bool
+is_negated_suppression(const suppression_base&);
+
+const negated_suppression_base*
+is_negated_suppression(const suppression_base*);
+
+negated_suppression_sptr
+is_negated_suppression(const suppression_sptr&);
+
 /// Abstraction of a type suppression specification.
 ///
 /// Specifies under which condition reports about a type diff node
@@ -416,6 +452,37 @@ public:
   ~fn_call_expr_boundary();
 }; //end class type_suppression::insertion_range::fn_call_expr_boundary
 
+/// Abstraction of a negated type suppression specification.
+///
+/// A negated type suppression suppresses a type if the negation of
+/// the equivalent propositions for a @ref type_suppression are valid.
+class negated_type_suppression : virtual public type_suppression,
+				 virtual public negated_suppression_base
+{
+
+public:
+
+  negated_type_suppression(const string& label,
+			   const string& type_name_regexp,
+			   const string& type_name);
+
+  virtual bool
+  suppresses_diff(const diff* diff) const;
+
+  bool
+  suppresses_type(const type_base_sptr& type,
+		  const diff_context_sptr& ctxt) const;
+
+  bool
+  suppresses_type(const type_base_sptr& type) const;
+
+  bool
+  suppresses_type(const type_base_sptr& type,
+		  const scope_decl* type_scope) const;
+
+  virtual ~negated_type_suppression();
+};// end class negated_type_suppression
+
 class function_suppression;
 
 /// Convenience typedef for a shared pointer to function_suppression.
diff --git a/src/abg-comparison-priv.h b/src/abg-comparison-priv.h
index 46438e94..fb843665 100644
--- a/src/abg-comparison-priv.h
+++ b/src/abg-comparison-priv.h
@@ -174,7 +174,17 @@ struct diff_context::priv
   unordered_diff_sptr_set		live_diffs_;
   vector<diff_sptr>			canonical_diffs;
   vector<filtering::filter_base_sptr>	filters_;
+  // All the suppressions specifications are stored in this data
+  // member.
   suppressions_type			suppressions_;
+  // The negated suppressions specifications that are in
+  // suppressions_ are stored here.  Each time suppressions_ is
+  // modified, this data member should be cleared.
+  suppressions_type			negated_suppressions_;
+  // The non-negated suppressions specifications that are in
+  // suppressions_ are stored here.  Each time suppressions_ is
+  // modified, this data member should be cleared.
+  suppressions_type			direct_suppressions_;
   pointer_map				visited_diff_nodes_;
   corpus_diff_sptr			corpus_diff_;
   ostream*				default_output_stream_;
@@ -304,6 +314,13 @@ public:
     if (ctxt->get_allowed_category() == EVERYTHING_CATEGORY)
       return false;
 
+    // If this node is on the path of a node that *must* be reported,
+    // then do not filter it.
+    if (category & (HAS_DESCENDANT_WITH_ALLOWED_CHANGE_CATEGORY
+		    | HAS_PARENT_WITH_ALLOWED_CHANGE_CATEGORY
+		    | HAS_ALLOWED_CHANGE_CATEGORY))
+      return false;
+
   /// We don't want to display nodes suppressed by a user-provided
   /// suppression specification or by a "private type" suppression
   /// specification.
diff --git a/src/abg-comparison.cc b/src/abg-comparison.cc
index 969c32d8..609c5bf2 100644
--- a/src/abg-comparison.cc
+++ b/src/abg-comparison.cc
@@ -1399,10 +1399,72 @@ diff_context::maybe_apply_filters(corpus_diff_sptr diff)
 /// reports should be dropped on the floor.
 ///
 /// @return the set of suppressions.
-suppressions_type&
+const suppressions_type&
 diff_context::suppressions() const
 {return priv_->suppressions_;}
 
+/// Getter for the vector of suppressions that specify which diff node
+/// reports should be dropped on the floor.
+///
+/// @return the set of suppressions.
+suppr::suppressions_type&
+diff_context::suppressions()
+{
+  // Invalidate negated and direct suppressions caches that are built
+  // from priv_->suppressions_;
+  priv_->negated_suppressions_.clear();
+  priv_->direct_suppressions_.clear();
+  return priv_->suppressions_;
+}
+
+/// Getter of the negated suppression specifications that are
+/// comprised in the general vector of suppression specifications
+/// returned by diff_context::suppressions().
+///
+/// Note that the first invocation of this function scans the vector
+/// returned by diff_context::suppressions() and caches the negated
+/// suppressions from there.
+///
+/// Subsequent invocations of this function just return the cached
+/// negated suppressions.
+///
+/// @return the negated suppression specifications stored in this diff
+/// context.
+const suppr::suppressions_type&
+diff_context::negated_suppressions() const
+{
+  if (priv_->negated_suppressions_.empty())
+    for (auto s : suppressions())
+      if (is_negated_suppression(s))
+	priv_->negated_suppressions_.push_back(s);
+
+  return priv_->negated_suppressions_;
+}
+
+/// Getter of the direct suppression specification (those that are
+/// not negated) comprised in the general vector of suppression
+/// specifications returned by diff_context::suppression().
+///
+/// Note that the first invocation of this function scans the vector
+/// returned by diff_context::suppressions() and caches the direct
+/// suppressions from there.
+///
+/// Subsequent invocations of this function just return the cached
+/// direct suppressions.
+///
+/// @return the direct suppression specifications.
+const suppr::suppressions_type&
+diff_context::direct_suppressions() const
+{
+   if (priv_->direct_suppressions_.empty())
+    {
+      for (auto s : suppressions())
+	if (!is_negated_suppression(s))
+	  priv_->direct_suppressions_.push_back(s);
+    }
+   return priv_->direct_suppressions_;
+}
+
 /// Add a new suppression specification that specifies which diff node
 /// reports should be dropped on the floor.
 ///
@@ -1410,7 +1472,13 @@ diff_context::suppressions() const
 /// existing set of suppressions specifications of the diff context.
 void
 diff_context::add_suppression(const suppression_sptr suppr)
-{priv_->suppressions_.push_back(suppr);}
+{
+  priv_->suppressions_.push_back(suppr);
+  // Invalidate negated and direct suppressions caches that are built
+  // from priv_->suppressions_;
+  priv_->negated_suppressions_.clear();
+  priv_->direct_suppressions_.clear();
+}
 
 /// Add new suppression specifications that specify which diff node
 /// reports should be dropped on the floor.
@@ -2313,6 +2381,16 @@ diff::set_local_category(diff_category c)
 /// Test if this diff tree node is to be filtered out for reporting
 /// purposes.
 ///
+/// There is a difference between a diff node being filtered out and
+/// being suppressed.  Being suppressed means that there is a
+/// suppression specification that suppresses the diff node
+/// specifically.  Being filtered out mean the node is either
+/// suppressed, or it's filtered out because the suppression of a set
+/// of (children) nodes caused this node to be filtered out as well.
+/// For instance, if a function diff has all its children diff nodes
+/// suppressed and if the function diff node carries no local change,
+/// then the function diff node itself is going to be filtered out.
+///
 /// The function tests if the categories of the diff tree node are
 /// "forbidden" by the context or not.
 ///
@@ -2321,13 +2399,16 @@ bool
 diff::is_filtered_out() const
 {
   if (diff * canonical = get_canonical_diff())
-    if (canonical->get_category() & SUPPRESSED_CATEGORY
-	|| canonical->get_category() & PRIVATE_TYPE_CATEGORY)
+    if ((canonical->get_category() & SUPPRESSED_CATEGORY
+	 || canonical->get_category() & PRIVATE_TYPE_CATEGORY)
+	&& !canonical->is_allowed_by_specific_negated_suppression()
+	&& !canonical->has_descendant_allowed_by_specific_negated_suppression()
+	&& !canonical->has_parent_allowed_by_specific_negated_suppression())
       // The canonical type was suppressed either by a user-provided
       // suppression specification or by a "private-type" suppression
-      // specification..  This means all the class of equivalence of
-      // that canonical type was suppressed.  So this node should be
-      // suppressed too.
+      // specification..  This means all the classes of equivalence of
+      // that canonical type were suppressed.  So this node should be
+      // filtered out.
       return true;
   return priv_->is_filtered_out(get_category());
 }
@@ -2345,6 +2426,27 @@ bool
 diff::is_filtered_out_wrt_non_inherited_categories() const
 {return priv_->is_filtered_out(get_local_category());}
 
+/// Test if this diff tree node is to be filtered out for reporting
+/// purposes, but without considering the categories that can /force/
+/// the node to be unfiltered.
+///
+/// The function tests if the categories of the diff tree node are
+/// "forbidden" by the context or not.
+///
+/// @return true iff the current diff node should should NOT be
+/// reported, with respect to the categories that might filter it out
+/// only.
+bool
+diff::is_filtered_out_without_looking_at_allowed_changes() const
+{
+  diff_category c = get_category();
+  c &= ~(HAS_DESCENDANT_WITH_ALLOWED_CHANGE_CATEGORY
+	 | HAS_PARENT_WITH_ALLOWED_CHANGE_CATEGORY
+	 | HAS_ALLOWED_CHANGE_CATEGORY);
+
+    return priv_->is_filtered_out(c);
+}
+
 /// Test if the current diff node has been suppressed by a
 /// user-provided suppression specification.
 ///
@@ -2364,6 +2466,13 @@ diff::is_suppressed() const
 /// Note that private type suppressions are auto-generated from the
 /// path to where public headers are, as given by the user.
 ///
+/// Here is the current algorithm:
+///
+///         First, suppress this diff node if it's not matched by any
+///         negated suppression specifications.  If it's not
+///         suppressed, then suppress it if it's matched by direct
+///         suppression specifications.
+///
 /// @param is_private_type out parameter if the current diff node was
 /// suppressed because it's a private type then this parameter is set
 /// to true.
@@ -2373,19 +2482,32 @@ diff::is_suppressed() const
 bool
 diff::is_suppressed(bool &is_private_type) const
 {
-  const suppressions_type& suppressions = context()->suppressions();
-  for (suppressions_type::const_iterator i = suppressions.begin();
-       i != suppressions.end();
-       ++i)
-    {
-      if ((*i)->suppresses_diff(this))
-	{
-	  if (is_private_type_suppr_spec(*i))
-	    is_private_type = true;
-	  return true;
-	}
-    }
-  return false;
+  // If there is at least one negated suppression, then suppress the
+  // current diff node by default ...
+  bool do_suppress = !context()->negated_suppressions().empty();
+
+  // ... unless there is at least one negated suppression that
+  // specifically asks to keep this diff node around (un-suppressed).
+  for (auto n : context()->negated_suppressions())
+    if (!n->suppresses_diff(this))
+      {
+	do_suppress = false;
+	break;
+      }
+
+  // Then walk the set of non-negated, AKA direct, suppressions.  If at
+  // least one suppression suppresses the current diff node then the
+  // diff node must be suppressed.
+  for (auto d : context()->direct_suppressions())
+    if (d->suppresses_diff(this))
+      {
+	do_suppress = true;
+	if (is_private_type_suppr_spec(d))
+	  is_private_type = true;
+	break;
+      }
+
+  return do_suppress;
 }
 
 /// Test if this diff tree node should be reported.
@@ -2412,6 +2534,51 @@ diff::has_local_changes_to_be_reported() const
   return false;
 }
 
+/// Test if this diff node is allowed (prevented from being
+/// suppressed) by at least one negated suppression specification.
+///
+/// @return true if this diff node is meant to be allowed by at least
+/// one negated suppression specification.
+bool
+diff::is_allowed_by_specific_negated_suppression() const
+{
+  const suppressions_type& suppressions = context()->suppressions();
+  for (suppressions_type::const_iterator i = suppressions.begin();
+       i != suppressions.end();
+       ++i)
+    {
+      if (is_negated_suppression(*i)
+	  && !(*i)->suppresses_diff(this))
+	return true;
+    }
+  return false;
+}
+
+/// Test if the current diff node has a descendant node which is
+/// specifically allowed by a negated suppression specification.
+///
+/// @return true iff the current diff node has a descendant node
+/// which is specifically allowed by a negated suppression
+/// specification.
+bool
+diff::has_descendant_allowed_by_specific_negated_suppression() const
+{
+  bool result = (get_category() & HAS_DESCENDANT_WITH_ALLOWED_CHANGE_CATEGORY);
+  return result;
+}
+
+/// Test if the current diff node has a parent node which is
+/// specifically allowed by a negated suppression specification.
+///
+/// @return true iff the current diff node has a parent node which is
+/// specifically allowed by a negated suppression specification.
+bool
+diff::has_parent_allowed_by_specific_negated_suppression() const
+{
+  bool result = (get_category() & HAS_PARENT_WITH_ALLOWED_CHANGE_CATEGORY);
+  return result;
+}
+
 /// Get a pretty representation of the current @ref diff node.
 ///
 /// This is suitable for e.g. emitting debugging traces for the diff
@@ -3075,6 +3242,30 @@ operator<<(ostream& o, diff_category c)
       emitted_a_category |= true;
     }
 
+  if (c & HAS_ALLOWED_CHANGE_CATEGORY)
+    {
+      if (emitted_a_category)
+	o << "|";
+      o << "HAS_ALLOWED_CHANGE_CATEGORY";
+      emitted_a_category |= true;
+    }
+
+  if (c & HAS_DESCENDANT_WITH_ALLOWED_CHANGE_CATEGORY)
+    {
+      if (emitted_a_category)
+	o << "|";
+      o << "HAS_DESCENDANT_WITH_ALLOWED_CHANGE_CATEGORY";
+      emitted_a_category |= true;
+    }
+
+    if (c & HAS_PARENT_WITH_ALLOWED_CHANGE_CATEGORY)
+    {
+      if (emitted_a_category)
+	o << "|";
+      o << "HAS_PARENT_WITH_ALLOWED_CHANGE_CATEGORY";
+      emitted_a_category |= true;
+    }
+
   return o;
 }
 
@@ -11385,7 +11576,10 @@ struct category_propagation_visitor : public diff_node_visitor
 	// are propagated in a specific pass elsewhere.
 	c &= ~(REDUNDANT_CATEGORY
 	       | SUPPRESSED_CATEGORY
-	       | PRIVATE_TYPE_CATEGORY);
+	       | PRIVATE_TYPE_CATEGORY
+	       | HAS_ALLOWED_CHANGE_CATEGORY
+	       | HAS_DESCENDANT_WITH_ALLOWED_CHANGE_CATEGORY
+	       | HAS_PARENT_WITH_ALLOWED_CHANGE_CATEGORY);
 	// Also, if a (class) type has got a harmful name change, do not
 	// propagate harmless name changes coming from its sub-types
 	// (i.e, data members) to the class itself.
@@ -11481,6 +11675,40 @@ struct suppression_categorization_visitor : public diff_node_visitor
 	if (canonical_diff != d)
 	  canonical_diff->add_to_category(c);
       }
+    else if (d->is_allowed_by_specific_negated_suppression())
+      {
+	// This diff node is specifically allowed by a
+	// negated_suppression, then mark it as being in the
+	// HAS_ALLOWED_CHANGE_CATEGORY.
+	diff_category c = HAS_ALLOWED_CHANGE_CATEGORY;
+	d->add_to_local_category(c);
+	diff *canonical_diff = d->get_canonical_diff();
+	canonical_diff->add_to_category(c);
+
+	// Note that some complementary code later down below does
+	// categorize the descendants and parents nodes of this node
+	// as HAS_PARENT_WITH_ALLOWED_CHANGE_CATEGORY and
+	// HAS_DESCENDANT_WITH_ALLOWED_CHANGE_CATEGORY, repectively.
+      }
+
+    // If a parent node has been allowed by a negated suppression
+    // specification, then categorize the current node as
+    // HAS_PARENT_WITH_ALLOWED_CHANGE_CATEGORY.
+    if (d->parent_node())
+      {
+	diff_category c = d->parent_node()->get_local_category();
+	if (c & (HAS_ALLOWED_CHANGE_CATEGORY
+		 | HAS_PARENT_WITH_ALLOWED_CHANGE_CATEGORY))
+	  d->add_to_category(HAS_PARENT_WITH_ALLOWED_CHANGE_CATEGORY);
+	else
+	  {
+	    c = d->parent_node()->get_category();
+	    if (c & (HAS_ALLOWED_CHANGE_CATEGORY
+		     | HAS_PARENT_WITH_ALLOWED_CHANGE_CATEGORY))
+	      d->add_to_category(HAS_PARENT_WITH_ALLOWED_CHANGE_CATEGORY);
+	  }
+      }
+
   }
 
   /// After visiting the children nodes of a given diff node,
@@ -11505,6 +11733,7 @@ struct suppression_categorization_visitor : public diff_node_visitor
     bool has_suppressed_child = false;
     bool has_non_private_child = false;
     bool has_private_child = false;
+    bool has_descendant_with_allowed_change = false;
 
     if (// A node to which we can propagate the "SUPPRESSED_CATEGORY"
 	// (or the PRIVATE_TYPE_CATEGORY for the same matter)
@@ -11670,6 +11899,24 @@ struct suppression_categorization_visitor : public diff_node_visitor
 		  }
 	  }
       }
+
+    // If any descendant node was selected by a negated suppression
+    // specification then categorize the current one as
+    // HAS_DESCENDANT_WITH_ALLOWED_CHANGE_CATEGORY.
+    for (auto child_node : d->children_nodes())
+      {
+	diff *canonical_diff = child_node->get_canonical_diff();
+	diff_category c = canonical_diff->get_category();
+	if (c & (HAS_ALLOWED_CHANGE_CATEGORY
+		 | HAS_DESCENDANT_WITH_ALLOWED_CHANGE_CATEGORY))
+	  has_descendant_with_allowed_change = true;
+      }
+    if (has_descendant_with_allowed_change)
+      {
+	diff_category c = HAS_DESCENDANT_WITH_ALLOWED_CHANGE_CATEGORY;
+	d->add_to_category(c);
+	d->get_canonical_diff()->add_to_category(c);
+      }
   }
 }; //end struct suppression_categorization_visitor
 
diff --git a/src/abg-default-reporter.cc b/src/abg-default-reporter.cc
index 0c7d3e1d..243ddbfb 100644
--- a/src/abg-default-reporter.cc
+++ b/src/abg-default-reporter.cc
@@ -272,7 +272,8 @@ default_reporter::report(const typedef_diff& d,
 
   typedef_decl_sptr f = d.first_typedef_decl(), s = d.second_typedef_decl();
 
-  report_non_type_typedef_changes(d, out, indent);
+  if (!d.is_filtered_out_without_looking_at_allowed_changes())
+    report_non_type_typedef_changes(d, out, indent);
 
   diff_sptr dif = d.underlying_type_diff();
   if (dif && dif->has_changes())
@@ -364,11 +365,12 @@ default_reporter::report(const qualified_type_diff& d, ostream& out,
   RETURN_IF_BEING_REPORTED_OR_WAS_REPORTED_EARLIER(d.first_qualified_type(),
 						   d.second_qualified_type());
 
-  if (report_local_qualified_type_changes(d, out, indent))
-    // The local change was emitted and it's a name change.  If the
-    // type name changed, the it means the type changed altogether.
-    // It makes a little sense to detail the changes in extenso here.
-    return;
+  if (!d.is_filtered_out_without_looking_at_allowed_changes())
+    if (report_local_qualified_type_changes(d, out, indent))
+      // The local change was emitted and it's a name change.  If the
+      // type name changed, the it means the type changed altogether.
+      // It makes a little sense to detail the changes in extenso here.
+      return;
 
   diff_sptr dif = d.leaf_underlying_type_diff();
   ABG_ASSERT(dif);
@@ -474,8 +476,9 @@ default_reporter::report(const reference_diff& d, ostream& out,
   enum change_kind k = ir::NO_CHANGE_KIND;
   equals(*d.first_reference(), *d.second_reference(), &k);
 
-  if ((k & ALL_LOCAL_CHANGES_MASK) && !(k & SUBTYPE_CHANGE_KIND))
-    report_local_reference_type_changes(d, out, indent);
+  if (!d.is_filtered_out_without_looking_at_allowed_changes())
+    if ((k & ALL_LOCAL_CHANGES_MASK) && !(k & SUBTYPE_CHANGE_KIND))
+      report_local_reference_type_changes(d, out, indent);
 
   if (k & SUBTYPE_CHANGE_KIND)
     if (diff_sptr dif = d.underlying_type_diff())
@@ -503,6 +506,9 @@ void
 default_reporter::report(const fn_parm_diff& d, ostream& out,
 			 const string& indent) const
 {
+  if (!d.to_be_reported())
+    return;
+
   function_decl::parameter_sptr f = d.first_parameter(),
     s = d.second_parameter();
 
@@ -514,26 +520,23 @@ default_reporter::report(const fn_parm_diff& d, ostream& out,
     type_has_sub_type_changes(d.first_parameter()->get_type(),
 			      d.second_parameter()->get_type());
 
-  if (d.to_be_reported())
-    {
-      diff_sptr type_diff = d.type_diff();
-      ABG_ASSERT(type_diff->has_changes());
+  diff_sptr type_diff = d.type_diff();
+  ABG_ASSERT(type_diff->has_changes());
 
-      out << indent;
-      if (f->get_is_artificial())
-	out << "implicit ";
-      out << "parameter " << f->get_index();
-      report_loc_info(f, *d.context(), out);
-      out << " of type '"
-	  << f->get_type_pretty_representation();
-
-      if (has_sub_type_change)
-	out << "' has sub-type changes:\n";
-      else
-	out << "' changed:\n";
+  out << indent;
+  if (f->get_is_artificial())
+    out << "implicit ";
+  out << "parameter " << f->get_index();
+  report_loc_info(f, *d.context(), out);
+  out << " of type '"
+      << f->get_type_pretty_representation();
 
-      type_diff->report(out, indent + "  ");
-    }
+  if (has_sub_type_change)
+    out << "' has sub-type changes:\n";
+  else
+    out << "' changed:\n";
+
+  type_diff->report(out, indent + "  ");
 }
 
 /// For a @ref function_type_diff node, report the local changes
@@ -645,8 +648,8 @@ default_reporter::report(const function_type_diff& d, ostream& out,
 	dif->report(out, indent);
     }
 
-  report_local_function_type_changes(d, out, indent);
-
+  if (!d.is_filtered_out_without_looking_at_allowed_changes())
+    report_local_function_type_changes(d, out, indent);
 }
 
 /// Report a @ref array_diff in a serialized form.
@@ -678,10 +681,11 @@ default_reporter::report(const array_diff& d, ostream& out,
       dif->report(out, indent + "  ");
     }
 
-  report_name_size_and_alignment_changes(d.first_array(),
-					 d.second_array(),
-					 d.context(),
-					 out, indent);
+  if (!d.is_filtered_out_without_looking_at_allowed_changes())
+    report_name_size_and_alignment_changes(d.first_array(),
+					   d.second_array(),
+					   d.context(),
+					   out, indent);
 }
 
 /// Generates a report for an intance of @ref base_diff.
@@ -702,30 +706,32 @@ default_reporter::report(const base_diff& d, ostream& out,
   string repr = f->get_base_class()->get_pretty_representation();
   bool emitted = false;
 
-  if (f->get_is_static() != s->get_is_static())
+  if (!d.is_filtered_out_without_looking_at_allowed_changes())
     {
-      if (f->get_is_static())
-	out << indent << "is no more static";
-      else
-	out << indent << "now becomes static";
-      emitted = true;
-    }
+      if (f->get_is_static() != s->get_is_static())
+	{
+	  if (f->get_is_static())
+	    out << indent << "is no more static";
+	  else
+	    out << indent << "now becomes static";
+	  emitted = true;
+	}
 
-  if ((d.context()->get_allowed_category() & ACCESS_CHANGE_CATEGORY)
-      && (f->get_access_specifier() != s->get_access_specifier()))
-    {
-      if (emitted)
-	out << ", ";
+      if ((d.context()->get_allowed_category() & ACCESS_CHANGE_CATEGORY)
+	  && (f->get_access_specifier() != s->get_access_specifier()))
+	{
+	  if (emitted)
+	    out << ", ";
 
-      out << "has access changed from '"
-	  << f->get_access_specifier()
-	  << "' to '"
-	  << s->get_access_specifier()
-	  << "'";
+	  out << "has access changed from '"
+	      << f->get_access_specifier()
+	      << "' to '"
+	      << s->get_access_specifier()
+	      << "'";
 
-      emitted = true;
+	  emitted = true;
+	}
     }
-
   if (class_diff_sptr dif = d.get_underlying_class_diff())
     {
       if (dif->to_be_reported())
@@ -1493,135 +1499,138 @@ default_reporter::report(const function_decl_diff& d, ostream& out,
     linkage_names2 =
       s2->get_aliases_id_string(sc->get_fun_symbol_map());
 
-  /// If the set of linkage names of the function have changed, report
-  /// it.
-  if (linkage_names1 != linkage_names2)
+  if (!d.is_filtered_out_without_looking_at_allowed_changes())
     {
-      if (linkage_names1.empty())
+      /// If the set of linkage names of the function have changed, report
+      /// it.
+      if (linkage_names1 != linkage_names2)
 	{
-	  out << indent << ff->get_pretty_representation()
-	      << " didn't have any linkage name, and it now has: '"
-	      << linkage_names2 << "'\n";
+	  if (linkage_names1.empty())
+	    {
+	      out << indent << ff->get_pretty_representation()
+		  << " didn't have any linkage name, and it now has: '"
+		  << linkage_names2 << "'\n";
+	    }
+	  else if (linkage_names2.empty())
+	    {
+	      out << indent << ff->get_pretty_representation()
+		  << " did have linkage names '" << linkage_names1
+		  << "'\n"
+		  << indent << "but it doesn't have any linkage name anymore\n";
+	    }
+	  else
+	    out << indent << "linkage names of "
+		<< ff->get_pretty_representation()
+		<< "\n" << indent << "changed from '"
+		<< linkage_names1 << "' to '" << linkage_names2 << "'\n";
 	}
-      else if (linkage_names2.empty())
+
+      if (qn1 != qn2
+	  && d.type_diff()
+	  && d.type_diff()->to_be_reported())
 	{
-	  out << indent << ff->get_pretty_representation()
-	      << " did have linkage names '" << linkage_names1
-	      << "'\n"
-	      << indent << "but it doesn't have any linkage name anymore\n";
+	  // So the function has sub-type changes that are to be
+	  // reported.  Let's see if the function name changed too; if it
+	  // did, then we'd report that change right before reporting the
+	  // sub-type changes.
+	  string frep1 = d.first_function_decl()->get_pretty_representation(),
+	    frep2 = d.second_function_decl()->get_pretty_representation();
+	  out << indent << "'" << frep1 << " {" << linkage_names1<< "}"
+	      << "' now becomes '"
+	      << frep2 << " {" << linkage_names2 << "}" << "'\n";
 	}
-      else
-	out << indent << "linkage names of "
-	    << ff->get_pretty_representation()
-	    << "\n" << indent << "changed from '"
-	    << linkage_names1 << "' to '" << linkage_names2 << "'\n";
-    }
 
-  if (qn1 != qn2
-      && d.type_diff()
-      && d.type_diff()->to_be_reported())
-    {
-      // So the function has sub-type changes that are to be
-      // reported.  Let's see if the function name changed too; if it
-      // did, then we'd report that change right before reporting the
-      // sub-type changes.
-      string frep1 = d.first_function_decl()->get_pretty_representation(),
-	frep2 = d.second_function_decl()->get_pretty_representation();
-      out << indent << "'" << frep1 << " {" << linkage_names1<< "}"
-	  << "' now becomes '"
-	  << frep2 << " {" << linkage_names2 << "}" << "'\n";
-    }
-
-  maybe_report_diff_for_symbol(ff->get_symbol(),
-			       sf->get_symbol(),
-			       d.context(), out, indent);
-
-  // Now report about inline-ness changes
-  if (ff->is_declared_inline() != sf->is_declared_inline())
-    {
-      out << indent;
-      if (ff->is_declared_inline())
-	out << sf->get_pretty_representation()
-	    << " is not declared inline anymore\n";
-      else
-	out << sf->get_pretty_representation()
-	    << " is now declared inline\n";
-    }
+      maybe_report_diff_for_symbol(ff->get_symbol(),
+				   sf->get_symbol(),
+				   d.context(), out, indent);
 
-  // Report about vtable offset changes.
-  if (is_member_function(ff) && is_member_function(sf))
-    {
-      bool ff_is_virtual = get_member_function_is_virtual(ff),
-	sf_is_virtual = get_member_function_is_virtual(sf);
-      if (ff_is_virtual != sf_is_virtual)
+      // Now report about inline-ness changes
+      if (ff->is_declared_inline() != sf->is_declared_inline())
 	{
 	  out << indent;
-	  if (ff_is_virtual)
-	    out << ff->get_pretty_representation()
-		<< " is no more declared virtual\n";
+	  if (ff->is_declared_inline())
+	    out << sf->get_pretty_representation()
+		<< " is not declared inline anymore\n";
 	  else
-	    out << ff->get_pretty_representation()
-		<< " is now declared virtual\n";
+	    out << sf->get_pretty_representation()
+		<< " is now declared inline\n";
 	}
 
-      size_t ff_vtable_offset = get_member_function_vtable_offset(ff),
-	sf_vtable_offset = get_member_function_vtable_offset(sf);
-      if (ff_is_virtual && sf_is_virtual
-	  && (ff_vtable_offset != sf_vtable_offset))
+      // Report about vtable offset changes.
+      if (is_member_function(ff) && is_member_function(sf))
 	{
-	  out << indent
-	      << "the vtable offset of "  << ff->get_pretty_representation()
-	      << " changed from " << ff_vtable_offset
-	      << " to " << sf_vtable_offset << "\n";
-	}
+	  bool ff_is_virtual = get_member_function_is_virtual(ff),
+	    sf_is_virtual = get_member_function_is_virtual(sf);
+	  if (ff_is_virtual != sf_is_virtual)
+	    {
+	      out << indent;
+	      if (ff_is_virtual)
+		out << ff->get_pretty_representation()
+		    << " is no more declared virtual\n";
+	      else
+		out << ff->get_pretty_representation()
+		    << " is now declared virtual\n";
+	    }
 
-      // the parent types (classe or union) of the two member
-      // functions.
-      class_or_union_sptr f =
-	is_class_or_union_type(is_method_type(ff->get_type())->get_class_type());
-      class_or_union_sptr s =
-	is_class_or_union_type(is_method_type(sf->get_type())->get_class_type());
+	  size_t ff_vtable_offset = get_member_function_vtable_offset(ff),
+	    sf_vtable_offset = get_member_function_vtable_offset(sf);
+	  if (ff_is_virtual && sf_is_virtual
+	      && (ff_vtable_offset != sf_vtable_offset))
+	    {
+	      out << indent
+		  << "the vtable offset of "  << ff->get_pretty_representation()
+		  << " changed from " << ff_vtable_offset
+		  << " to " << sf_vtable_offset << "\n";
+	    }
 
-      class_decl_sptr fc = is_class_type(f);
-      class_decl_sptr sc = is_class_type(s);
+	  // the parent types (classe or union) of the two member
+	  // functions.
+	  class_or_union_sptr f =
+	    is_class_or_union_type(is_method_type(ff->get_type())->get_class_type());
+	  class_or_union_sptr s =
+	    is_class_or_union_type(is_method_type(sf->get_type())->get_class_type());
 
-      // Detect if the virtual member function changes above
-      // introduced a vtable change or not.
-      bool vtable_added = false, vtable_removed = false;
-      if (!f->get_is_declaration_only() && !s->get_is_declaration_only())
-	{
-	  if (fc && sc)
+	  class_decl_sptr fc = is_class_type(f);
+	  class_decl_sptr sc = is_class_type(s);
+
+	  // Detect if the virtual member function changes above
+	  // introduced a vtable change or not.
+	  bool vtable_added = false, vtable_removed = false;
+	  if (!f->get_is_declaration_only() && !s->get_is_declaration_only())
 	    {
-	      vtable_added = !fc->has_vtable() && sc->has_vtable();
-	      vtable_removed = fc->has_vtable() && !sc->has_vtable();
+	      if (fc && sc)
+		{
+		  vtable_added = !fc->has_vtable() && sc->has_vtable();
+		  vtable_removed = fc->has_vtable() && !sc->has_vtable();
+		}
+	    }
+	  bool vtable_changed = ((ff_is_virtual != sf_is_virtual)
+				 || (ff_vtable_offset != sf_vtable_offset));
+	  bool incompatible_change = (ff_vtable_offset != sf_vtable_offset);
+
+	  if (vtable_added)
+	    out << indent
+		<< "  note that a vtable was added to "
+		<< fc->get_pretty_representation()
+		<< "\n";
+	  else if (vtable_removed)
+	    out << indent
+		<< "  note that the vtable was removed from "
+		<< fc->get_pretty_representation()
+		<< "\n";
+	  else if (vtable_changed)
+	    {
+	      out << indent;
+	      if (incompatible_change)
+		out << "  note that this is an ABI incompatible "
+		  "change to the vtable of ";
+	      else
+		out << "  note that this induces a change to the vtable of ";
+	      out << fc->get_pretty_representation()
+		  << "\n";
 	    }
-	}
-      bool vtable_changed = ((ff_is_virtual != sf_is_virtual)
-			     || (ff_vtable_offset != sf_vtable_offset));
-      bool incompatible_change = (ff_vtable_offset != sf_vtable_offset);
 
-      if (vtable_added)
-	out << indent
-	    << "  note that a vtable was added to "
-	    << fc->get_pretty_representation()
-	    << "\n";
-      else if (vtable_removed)
-	out << indent
-	    << "  note that the vtable was removed from "
-	    << fc->get_pretty_representation()
-	    << "\n";
-      else if (vtable_changed)
-	{
-	  out << indent;
-	  if (incompatible_change)
-	    out << "  note that this is an ABI incompatible "
-	      "change to the vtable of ";
-	  else
-	    out << "  note that this induces a change to the vtable of ";
-	  out << fc->get_pretty_representation()
-	      << "\n";
 	}
-
     }
 
   // Report about function type differences.
@@ -1648,17 +1657,20 @@ default_reporter::report(const var_diff& d, ostream& out,
   decl_base_sptr first = d.first_var(), second = d.second_var();
   string n = first->get_pretty_representation();
 
-  report_name_size_and_alignment_changes(first, second,
-					 d.context(),
-					 out, indent);
+  if (!d.is_filtered_out_without_looking_at_allowed_changes())
+    {
+      report_name_size_and_alignment_changes(first, second,
+					     d.context(),
+					     out, indent);
 
-  maybe_report_diff_for_symbol(d.first_var()->get_symbol(),
-			       d.second_var()->get_symbol(),
-			       d.context(), out, indent);
+      maybe_report_diff_for_symbol(d.first_var()->get_symbol(),
+				   d.second_var()->get_symbol(),
+				   d.context(), out, indent);
 
-  maybe_report_diff_for_member(first, second, d.context(), out, indent);
+      maybe_report_diff_for_member(first, second, d.context(), out, indent);
 
-  maybe_report_diff_for_variable(first, second, d.context(), out, indent);
+      maybe_report_diff_for_variable(first, second, d.context(), out, indent);
+    }
 
   if (diff_sptr dif = d.type_diff())
     {
diff --git a/src/abg-suppression.cc b/src/abg-suppression.cc
index a79deb35..aaf175ca 100644
--- a/src/abg-suppression.cc
+++ b/src/abg-suppression.cc
@@ -274,6 +274,67 @@ suppression_base::has_soname_related_property() const
 	    && get_soname_not_regex_str().empty()));
 }
 
+/// Constructor of the @ref negated_suppression_base.
+negated_suppression_base::negated_suppression_base()
+{
+}
+
+/// Destructor of the @ref negated_suppression_base.
+negated_suppression_base::~negated_suppression_base()
+{
+}
+
+/// Test if a suppression specification is a negated suppression.
+///
+/// @param s the suppression to consider.
+///
+/// @return true iff @p s is an instance of @ref
+/// negated_suppression_base.
+bool
+is_negated_suppression(const suppression_base& s)
+{
+  bool result = true;
+  try
+    {
+      dynamic_cast<const negated_suppression_base&>(s);
+    }
+  catch (...)
+    {
+      result = false;
+    }
+  return result;
+}
+
+/// Test if a suppression specification is a negated suppression.
+///
+/// @param s the suppression to consider.
+///
+/// @return true a pointer to the @ref negated_suppression_base which
+/// @p s, or nil if it's not a negated suppression.
+/// negated_suppression_base.
+const negated_suppression_base*
+is_negated_suppression(const suppression_base* s)
+{
+  const negated_suppression_base* result = nullptr;
+  result = dynamic_cast<const negated_suppression_base*>(s);
+  return result;
+}
+
+/// Test if a suppression specification is a negated suppression.
+///
+/// @param s the suppression to consider.
+///
+/// @return true a pointer to the @ref negated_suppression_base which
+/// @p s, or nil if it's not a negated suppression.
+/// negated_suppression_base.
+negated_suppression_sptr
+is_negated_suppression(const suppression_sptr& s)
+{
+  negated_suppression_sptr result;
+  result = dynamic_pointer_cast<negated_suppression_base>(s);
+  return result;
+}
+
 /// Check if the SONAMEs of the two binaries being compared match the
 /// content of the properties "soname_regexp" and "soname_not_regexp"
 /// of the current suppression specification.
@@ -1619,6 +1680,52 @@ is_type_suppression(suppression_sptr suppr)
 
 // </type_suppression stuff>
 
+// <negated_type_suppression stuff>
+
+/// Constructor for @ref negated_type_suppression.
+///
+/// @param label the label of the suppression.  This is just a free
+/// form comment explaining what the suppression is about.
+///
+/// @param type_name_regexp the regular expression describing the
+/// types about which diff reports should be suppressed.  If it's an
+/// empty string, the parameter is ignored.
+///
+/// @param type_name the name of the type about which diff reports
+/// should be suppressed.  If it's an empty string, the parameter is
+/// ignored.
+///
+/// Note that parameter @p type_name_regexp and @p type_name_regexp
+/// should not necessarily be populated.  It usually is either one or
+/// the other that the user wants.
+negated_type_suppression::negated_type_suppression(const string& label,
+						   const string& type_name_regexp,
+						   const string& type_name)
+  : type_suppression(label, type_name_regexp, type_name),
+    negated_suppression_base()
+{
+}
+
+/// Evaluate this suppression specification on a given diff node and
+/// say if the diff node should be suppressed or not.
+///
+/// @param diff the diff node to evaluate this suppression
+/// specification against.
+///
+/// @return true if @p diff should be suppressed.
+bool
+negated_type_suppression::suppresses_diff(const diff* diff) const
+{
+  return !type_suppression::suppresses_diff(diff);
+}
+
+/// Destructor of the @ref negated_type_suppression type.
+negated_type_suppression::~negated_type_suppression()
+{
+}
+
+// </negated_type_suppression stuff>
+
 /// Parse the value of the "type_kind" property in the "suppress_type"
 /// section.
 ///
@@ -1681,7 +1788,8 @@ read_type_suppression(const ini::config::section& section)
 {
   type_suppression_sptr result;
 
-  if (section.get_name() != "suppress_type")
+  if (section.get_name() != "suppress_type"
+      && section.get_name() != "allow_type")
     return result;
 
   static const char *const sufficient_props[] = {
@@ -2046,7 +2154,11 @@ read_type_suppression(const ini::config::section& section)
 	changed_enumerator_names.push_back(p->get_value()->as_string());
     }
 
-  result.reset(new type_suppression(label_str, name_regex_str, name_str));
+  if (section.get_name() == "suppress_type")
+    result.reset(new type_suppression(label_str, name_regex_str, name_str));
+  else if (section.get_name() == "allow_type")
+    result.reset(new negated_type_suppression(label_str, name_regex_str,
+					      name_str));
 
   if (consider_type_kind)
     {
diff --git a/tests/data/Makefile.am b/tests/data/Makefile.am
index a343759e..aafa41de 100644
--- a/tests/data/Makefile.am
+++ b/tests/data/Makefile.am
@@ -258,6 +258,46 @@ test-abidiff-exit/PR30048-test-2-v0.cc \
 test-abidiff-exit/PR30048-test-2-v0.o \
 test-abidiff-exit/PR30048-test-2-v1.cc \
 test-abidiff-exit/PR30048-test-2-v1.o \
+test-abidiff-exit/test-allow-type-array-suppr.txt \
+test-abidiff-exit/test-allow-type-array-v0--v1-report-1.txt \
+test-abidiff-exit/test-allow-type-array-v0--v1-report-2.txt \
+test-abidiff-exit/test-allow-type-array-v0--v2-report-1.txt \
+test-abidiff-exit/test-allow-type-array-v0--v2-report-2.txt \
+test-abidiff-exit/test-allow-type-array-v0--v3-report-1.txt \
+test-abidiff-exit/test-allow-type-array-v0--v3-report-2.txt \
+test-abidiff-exit/test-allow-type-array-v0.c \
+test-abidiff-exit/test-allow-type-array-v0.o \
+test-abidiff-exit/test-allow-type-array-v1.c \
+test-abidiff-exit/test-allow-type-array-v1.o \
+test-abidiff-exit/test-allow-type-array-v2.c \
+test-abidiff-exit/test-allow-type-array-v2.o \
+test-abidiff-exit/test-allow-type-array-v3.c \
+test-abidiff-exit/test-allow-type-array-v3.o \
+test-abidiff-exit/test-allow-type-region-suppr.txt \
+test-abidiff-exit/test-allow-type-region-v0--v1-report-1.txt \
+test-abidiff-exit/test-allow-type-region-v0--v1-report-2.txt \
+test-abidiff-exit/test-allow-type-region-v0--v2-report-1.txt \
+test-abidiff-exit/test-allow-type-region-v0--v2-report-2.txt \
+test-abidiff-exit/test-allow-type-region-v0--v3-report-1.txt \
+test-abidiff-exit/test-allow-type-region-v0--v3-report-2.txt \
+test-abidiff-exit/test-allow-type-region-v0--v4-report-1.txt \
+test-abidiff-exit/test-allow-type-region-v0--v4-report-2.txt \
+test-abidiff-exit/test-allow-type-region-v0--v5-report-1.txt \
+test-abidiff-exit/test-allow-type-region-v0--v5-report-2.txt \
+test-abidiff-exit/test-allow-type-region-v0.c \
+test-abidiff-exit/test-allow-type-region-v0.o \
+test-abidiff-exit/test-allow-type-region-v1.c \
+test-abidiff-exit/test-allow-type-region-v1.o \
+test-abidiff-exit/test-allow-type-region-v2.c \
+test-abidiff-exit/test-allow-type-region-v2.o \
+test-abidiff-exit/test-allow-type-region-v3.c \
+test-abidiff-exit/test-allow-type-region-v3.o \
+test-abidiff-exit/test-allow-type-region-v4.c \
+test-abidiff-exit/test-allow-type-region-v4.o \
+test-abidiff-exit/test-allow-type-region-v5.c \
+test-abidiff-exit/test-allow-type-region-v5.o \
+test-abidiff-exit/test-allow-type-suppr2.txt \
+test-abidiff-exit/test-allow-type-suppr1.txt \
 \
 test-diff-dwarf/test0-v0.cc		\
 test-diff-dwarf/test0-v0.o			\
diff --git a/tests/data/test-abidiff-exit/test-allow-type-array-suppr.txt b/tests/data/test-abidiff-exit/test-allow-type-array-suppr.txt
new file mode 100644
index 00000000..043cfb39
--- /dev/null
+++ b/tests/data/test-abidiff-exit/test-allow-type-array-suppr.txt
@@ -0,0 +1,7 @@
+[allow_type]
+ type_kind = struct
+ has_data_member_regexp = rh_kabi_reserved
+
+[suppress_type]
+ type_kind = struct
+ has_data_member_inserted_between = {offset_of(rh_kabi_reserved1), end}
diff --git a/tests/data/test-abidiff-exit/test-allow-type-array-v0--v1-report-1.txt b/tests/data/test-abidiff-exit/test-allow-type-array-v0--v1-report-1.txt
new file mode 100644
index 00000000..36987f27
--- /dev/null
+++ b/tests/data/test-abidiff-exit/test-allow-type-array-v0--v1-report-1.txt
@@ -0,0 +1,23 @@
+Functions changes summary: 0 Removed, 1 Changed, 0 Added function
+Variables changes summary: 0 Removed, 0 Changed, 0 Added variable
+
+1 function with some indirect sub-type change:
+
+  [C] 'function int foo(C0*, C1*)' at test-allow-type-array-v0.c:15:1 has some indirect sub-type changes:
+    parameter 1 of type 'C0*' has sub-type changes:
+      in pointed to type 'struct C0' at test-allow-type-array-v1.c:1:1:
+        type size hasn't changed
+        1 data member insertion:
+          'int inserted', at offset 64 (in bits) at test-allow-type-array-v1.c:5:1
+        1 data member change:
+          type of 'char rh_kabi_reserved1[50]' changed:
+            type name changed from 'char[50]' to 'char[46]'
+            array type size changed from 400 to 368
+            array type subrange 1 changed length from 50 to 46
+          and offset changed from 64 to 96 (in bits) (by +32 bits)
+    parameter 2 of type 'C1*' has sub-type changes:
+      in pointed to type 'struct C1' at test-allow-type-array-v1.c:9:1:
+        type size changed from 64 to 96 (in bits)
+        1 data member insertion:
+          'int m2', at offset 64 (in bits) at test-allow-type-array-v1.c:13:1
+
diff --git a/tests/data/test-abidiff-exit/test-allow-type-array-v0--v1-report-2.txt b/tests/data/test-abidiff-exit/test-allow-type-array-v0--v1-report-2.txt
new file mode 100644
index 00000000..9666a8fd
--- /dev/null
+++ b/tests/data/test-abidiff-exit/test-allow-type-array-v0--v1-report-2.txt
@@ -0,0 +1,3 @@
+Functions changes summary: 0 Removed, 0 Changed (1 filtered out), 0 Added function
+Variables changes summary: 0 Removed, 0 Changed, 0 Added variable
+
diff --git a/tests/data/test-abidiff-exit/test-allow-type-array-v0--v2-report-1.txt b/tests/data/test-abidiff-exit/test-allow-type-array-v0--v2-report-1.txt
new file mode 100644
index 00000000..5d2fb16c
--- /dev/null
+++ b/tests/data/test-abidiff-exit/test-allow-type-array-v0--v2-report-1.txt
@@ -0,0 +1,20 @@
+Functions changes summary: 0 Removed, 1 Changed, 0 Added function
+Variables changes summary: 0 Removed, 0 Changed, 0 Added variable
+
+1 function with some indirect sub-type change:
+
+  [C] 'function int foo(C0*, C1*)' at test-allow-type-array-v0.c:15:1 has some indirect sub-type changes:
+    parameter 1 of type 'C0*' has sub-type changes:
+      in pointed to type 'struct C0' at test-allow-type-array-v2.c:1:1:
+        type size changed from 480 to 512 (in bits)
+        1 data member insertion:
+          'int wrongly_inserted', at offset 32 (in bits) at test-allow-type-array-v2.c:4:1
+        2 data member changes:
+          'int m1' offset changed from 32 to 64 (in bits) (by +32 bits)
+          'char rh_kabi_reserved1[50]' offset changed from 64 to 96 (in bits) (by +32 bits)
+    parameter 2 of type 'C1*' has sub-type changes:
+      in pointed to type 'struct C1' at test-allow-type-array-v2.c:9:1:
+        type size changed from 64 to 96 (in bits)
+        1 data member insertion:
+          'int m2', at offset 64 (in bits) at test-allow-type-array-v2.c:13:1
+
diff --git a/tests/data/test-abidiff-exit/test-allow-type-array-v0--v2-report-2.txt b/tests/data/test-abidiff-exit/test-allow-type-array-v0--v2-report-2.txt
new file mode 100644
index 00000000..cade60df
--- /dev/null
+++ b/tests/data/test-abidiff-exit/test-allow-type-array-v0--v2-report-2.txt
@@ -0,0 +1,15 @@
+Functions changes summary: 0 Removed, 1 Changed, 0 Added function
+Variables changes summary: 0 Removed, 0 Changed, 0 Added variable
+
+1 function with some indirect sub-type change:
+
+  [C] 'function int foo(C0*, C1*)' at test-allow-type-array-v0.c:15:1 has some indirect sub-type changes:
+    parameter 1 of type 'C0*' has sub-type changes:
+      in pointed to type 'struct C0' at test-allow-type-array-v2.c:1:1:
+        type size changed from 480 to 512 (in bits)
+        1 data member insertion:
+          'int wrongly_inserted', at offset 32 (in bits) at test-allow-type-array-v2.c:4:1
+        2 data member changes:
+          'int m1' offset changed from 32 to 64 (in bits) (by +32 bits)
+          'char rh_kabi_reserved1[50]' offset changed from 64 to 96 (in bits) (by +32 bits)
+
diff --git a/tests/data/test-abidiff-exit/test-allow-type-array-v0--v3-report-1.txt b/tests/data/test-abidiff-exit/test-allow-type-array-v0--v3-report-1.txt
new file mode 100644
index 00000000..b0e1d85c
--- /dev/null
+++ b/tests/data/test-abidiff-exit/test-allow-type-array-v0--v3-report-1.txt
@@ -0,0 +1,23 @@
+Functions changes summary: 0 Removed, 1 Changed, 0 Added function
+Variables changes summary: 0 Removed, 0 Changed, 0 Added variable
+
+1 function with some indirect sub-type change:
+
+  [C] 'function int foo(C0*, C1*)' at test-allow-type-array-v0.c:15:1 has some indirect sub-type changes:
+    parameter 1 of type 'C0*' has sub-type changes:
+      in pointed to type 'struct C0' at test-allow-type-array-v3.c:1:1:
+        type size hasn't changed
+        1 data member insertion:
+          'int correctly_inserted', at offset 64 (in bits) at test-allow-type-array-v3.c:5:1
+        1 data member change:
+          type of 'char rh_kabi_reserved1[50]' changed:
+            type name changed from 'char[50]' to 'char[46]'
+            array type size changed from 400 to 368
+            array type subrange 1 changed length from 50 to 46
+          and offset changed from 64 to 96 (in bits) (by +32 bits)
+    parameter 2 of type 'C1*' has sub-type changes:
+      in pointed to type 'struct C1' at test-allow-type-array-v3.c:9:1:
+        type size changed from 64 to 96 (in bits)
+        1 data member insertion:
+          'int m2', at offset 64 (in bits) at test-allow-type-array-v3.c:13:1
+
diff --git a/tests/data/test-abidiff-exit/test-allow-type-array-v0--v3-report-2.txt b/tests/data/test-abidiff-exit/test-allow-type-array-v0--v3-report-2.txt
new file mode 100644
index 00000000..9666a8fd
--- /dev/null
+++ b/tests/data/test-abidiff-exit/test-allow-type-array-v0--v3-report-2.txt
@@ -0,0 +1,3 @@
+Functions changes summary: 0 Removed, 0 Changed (1 filtered out), 0 Added function
+Variables changes summary: 0 Removed, 0 Changed, 0 Added variable
+
diff --git a/tests/data/test-abidiff-exit/test-allow-type-array-v0.c b/tests/data/test-abidiff-exit/test-allow-type-array-v0.c
new file mode 100644
index 00000000..174de3a0
--- /dev/null
+++ b/tests/data/test-abidiff-exit/test-allow-type-array-v0.c
@@ -0,0 +1,18 @@
+struct C0
+{
+  int m0;
+  int m1;
+  char rh_kabi_reserved1[50];
+};
+
+struct C1
+{
+  int m0;
+  char m1;
+};
+
+int
+foo(struct C0 *c0, struct C1 *c1)
+{
+  return c0->m0 + c1->m0;
+}
diff --git a/tests/data/test-abidiff-exit/test-allow-type-array-v0.o b/tests/data/test-abidiff-exit/test-allow-type-array-v0.o
new file mode 100644
index 0000000000000000000000000000000000000000..83dc3783c1bdbaec61f46a705554269a55bba07d
GIT binary patch
literal 3392
zcmc&$&2Jl35TEDuCYx*$J24*(0cDLqZ6kK=4OIy#DM6t$5vZV|s+WSZwx8`K{)pC_
zPgEf;h$t73IC4T<kPsJ+9Qg-&<Aj6|NSsjMRH+A;S<g$hn{9>Qz(~6@^PBn2ynSEx
z2Up&BEh7Xd5pWq!JcR-*8!<aFm*Ej8z>U>|Usn%q-(5Y}I$Axva`!Ng@1sKyci#E_
z7tGT^M9d-LJQWdRuP0-6W~B@-iCc(>P8qaeve0xP8=2YR$k&Y;Y6Fa{?sr2N*+;LB
z&nUfI(2OWe6H&j+iVKGE1xBdk=t)V_AYK;CMz04V7j;D-5;Z3A`H$#lYh}ojk@>Wh
zt+_D!b7ZfB?x3N6h%XT1*mtogE}1WyYlQ_9lM6yKOml!04N(Blo&nRmY-$r&Q|J6*
zjtH4ESSSZ((OAR0p-J)VM8VW%XJ#kG)NuZBfMKjZv1p!@H@Ftg?1&I+1*$(<gtbBm
zdz+4Uc^bzP<)58iD^Qu`GyBt&1<&D=p+Vyu#zlQ~93%T}e00p6LoI~3H!)6%b_hOb
z%to6fBW5$XL0OC-L%_(ftL20~_MQG{L!WHuqLj%X@9MQ1){6Ur<+{%Ej%&@A=jUB_
z-nGtMmvw8^3prq4IB(gVaH}hqnzAbczlN?C)LKis3m5H+7wqkM%Wguu*KJx`-M-)K
zBAxa{(5h^BRlgF*z6`cy-Gy4q3!v2Mb!4gDtH0-$ntoVn`&G<pdVaeUv_c!1GU$5k
zQoS!XWGD%>sgBKcN}=qB{Zicvy~wmNR`(kXTkiTHP|)_;?cR<Z?rloj3j%M?-Yz?}
z|F7{w#spVaRu-*uxD@CA!8%0i3V7xRBKEI?*v*TXv0>vA+yk_K)E<iTD--#TGArXl
zH!*=ZX^OT_7XJ7vEvBX3M0k{V2PZiiM$$yX4r!u6bW5BHX%Jl^Dfq}Ki~tSr*rkXD
zV<{-*<0X(H8st(?${#<4(;QF5=10x_!!{vZc4{p~kstqh)q(K%S59$f!s&UJ{l^kG
zyYK`Xw64cD0NQhmQw=o+g5Heb)OVfz?F4>}{o4sVVE-G|$-e3vsjtpCr1ESh^Pgs%
z^6A-QpYBZ(h5s;c@h4{>3jdS+ORSUiEyihX;`0Mef3FjIRn%cH&@E+!ayJBrE*)^H
z{XRH>Y<o^!R=1iJzuV|xEV8|7HIUo)g}uP*Hs$@4w%?VCf-RIua3%6<y-r8sCF*p0
zp>*(mu=}A`+u*{oRcQoXNAidKM>T_dnj^JNs@yUg$NQRn%8g4A9m0|)|5iLHW+(Y)
zF-H6G-|}hwlQpTG>i-)#F-hjqzM|V{IhI4_5pUKb#wSrzo;o}DpCOVZd?#yC;8g~{
zj<QZ($=tUvl*B0eh{5s+HL{YquOpf^{-1b36`#gWw`y|yb;Oc25dw=3$4zo#lFY4O
zEUkZ@ukdx|Qh&N%lKnRkqnc`*XhyQb{dW|=TJ=}@eZ<rFzcYVaB|d<E_yGPr-jF5c
zDW%FO{TcIBT<JNQloU}s75WMd!g#m}hBEUPj*)?(=1;+2qLDWLn>_z&pVIu(ubbj4
xJgqm?ZgKw>514+3=vOnzr!`HUU4-BcpC5~ym?XcS#+AnZn8*KZj7w&c|2JnMH)a3;

literal 0
HcmV?d00001

diff --git a/tests/data/test-abidiff-exit/test-allow-type-array-v1.c b/tests/data/test-abidiff-exit/test-allow-type-array-v1.c
new file mode 100644
index 00000000..648f835a
--- /dev/null
+++ b/tests/data/test-abidiff-exit/test-allow-type-array-v1.c
@@ -0,0 +1,20 @@
+struct C0
+{
+  int m0;
+  int m1;
+  int inserted;
+  char rh_kabi_reserved1[46];
+};
+
+struct C1
+{
+  int m0;
+  char m1;
+  int m2;
+};
+
+int
+foo(struct C0 *c0, struct C1 *c1)
+{
+  return c0->m0 + c1->m0;
+}
diff --git a/tests/data/test-abidiff-exit/test-allow-type-array-v1.o b/tests/data/test-abidiff-exit/test-allow-type-array-v1.o
new file mode 100644
index 0000000000000000000000000000000000000000..9be83bf41befede1b2da405aa7036bca3a37edcf
GIT binary patch
literal 3448
zcmcImOK&4Z5U%!kl1U~HJLZuPP#gr>hZs8(R=Z)BEJ4w-37}=QXtfd#C1bm753$Fq
z@gyXRcpX3+1tE|)a^wI9PF#?<Ai)pd#xLL=;1YoZRE?{hNoR-<h>~Wi>g#%ScTL%E
zUwQtSj1Ztmz-2hp6bi6y9m-3wxCHk=0dB7B{<O0D=E2JD=Kjjwm4m$;zW4V){P5~`
zzoDH8BK904pNC?{#OuL?oLQcOCq)Ah(VhcikR%jc$i`yUAl-~*E0zbW0Fe=}vZfzI
zF!Ua3Z{agcF9)(#oHHv1P=(C03nN2cqeRi|!{{Rx`DI0W9!7h?!LD~YAo6i7<V&O~
z6`%f!npvHL90{3^n`YI8nS)qfhk+6b10UcE#3&fn#aVI5e!^ZYEZAsV5Qb&jJ#5wz
z1pot$#*P?e9|7CGY#U>k#pDRHc?yx4#L(kl&swYKIhYrZjumWUW_l(sCI)l&16b^e
z2R<akYJn0A&%$b<jLGeicxnm@vZ<a^Q>z8?TAI8wMON?_J{hVS68DUn+IGB@c)f_S
ziMD5v3n6|V8zn}KgAWQbaSvt0OlG_nHug0I4E0qbFU*l|%zNu*zHW+gCWC%guiY#z
zyBCVCTY9|Y7U$>Y=UsQ+EuOw1YsD2W;sxj2nWEE<HUqiXkbw;SDym*sZ7yyvoOjNj
zbGGJ-P6Jw<pi$fmx_%=-cj^&8=*lpXH3*w)8(zg<3nc<uvgSgy>4gx<Zsd5aR_Apm
z+PN(qFATjMXUi>Bq1^1WWx3X=z2=u2epGJx6%5eu{8l+^Mh-@oVc@mOwXWQdktEci
z6b`2-Q|xZJ=0#pCI=EKz>vczN`_ca#{e*3uumo3^moFAi<DH!O2Nx1wFyKt+k+^dM
z#CA?hj|^HLVKY!8sb-4#(_^{!Gs~j`x6pun(irto7JmLSZOurXRQOSGE9*W6uB4HH
z8Pdprcyt_FG9aETDfrMai~s}h`%57MhgTXIfFFMf88DuLQvA_lIGyoC;(XNEpVZ<s
z_S9VrqknSuDg)ukT~2Uj!s$_T9}5bfX8i%?bg%tUfbv<!$zIh!&<hEi@~*S~vWEMt
zzpLTbSih^`Z?OJ7bG;t5cdEx^8)7+@b^C`ISNq*zodzqB!hh_k;4)YEFRWi;uE#Gj
zPG^=pHz;*?+L2d59)&&LR8l0jBPh|tfl{U0g;FS6Ua2N4n~gO;sCRHJmc2?Plv_uZ
zz0eC9@~BJ852SLz6iOs`E!L}@c3a|<Ed`xOmT+!3-N>tMaA4V7tA}1&@<#q6qkf_@
zQun07EwRw=ch<=^NwcUBrcZ=dl12$x_dkVe)TjT}pYC6eN$FJnKS)WCZcBYdQ)?+<
zLt>5(#A1g$Vq~YjDf|x+(;~k0n6&T`gI~s0hcDgsIxguLSr2k^&K<hOl5YDPqG|O{
z@@Q55R6jv_{WZk&m<YiI&VP%P1nIVGxR#cGh4ZhoFXgAXqvyYk7{yd|q8QVMoc|rx
z6;t^Yr}>lSKgJC;sth@RQWf9(8~<tA6jHgz`fuf@_$Roc<h~IK&$53KBU!iof-&B1
z-~2lJFZ7X~pw3UhpP@jeOA28AIiJ7kQ#yb8)l>cor+W0<6yh%DZ*sx(`$WH{x<B2i
e{_P?J`#kK6q$Eh+Kh>4y|1H=5Rl=7<-TyDlemc<r

literal 0
HcmV?d00001

diff --git a/tests/data/test-abidiff-exit/test-allow-type-array-v2.c b/tests/data/test-abidiff-exit/test-allow-type-array-v2.c
new file mode 100644
index 00000000..2024aa8e
--- /dev/null
+++ b/tests/data/test-abidiff-exit/test-allow-type-array-v2.c
@@ -0,0 +1,20 @@
+struct C0
+{
+  int m0;
+  int wrongly_inserted;
+  int m1;
+  char rh_kabi_reserved1[50];
+};
+
+struct C1
+{
+  int m0;
+  char m1;
+  int m2;
+};
+
+int
+foo(struct C0 *c0, struct C1 *c1)
+{
+  return c0->m0 + c1->m0;
+}
diff --git a/tests/data/test-abidiff-exit/test-allow-type-array-v2.o b/tests/data/test-abidiff-exit/test-allow-type-array-v2.o
new file mode 100644
index 0000000000000000000000000000000000000000..88f3d0bfb043d22f936663784792614fb2aeb844
GIT binary patch
literal 3456
zcmc&$&2Jl35TEDu#+z)M*fErZfU@R8{Sdp(nyL-0QjAP#B2ZNYRYiQcYx~(=Vz0}3
zlO{!c34s~~p(1hYkqZYTBrZrC;lPm_e*yObmk1<aX8oSC*=#EW2S)OHGru>#nRy=`
z``z;|JeOh&q%b%K`<6rj-qZH=N~l)gJ}AKDm7SkgcHZ7w+1a?WvU`4SH;3b$U0^@H
z_TBI3rveL^8R9$~5+lPy5j|C1gbHgT!g`CKWJm*rhIFW>S<ePFRJ9Nu(>z?91TC$)
zUI61CqV*1bCTOrBt%W($Yz$3ERc)9Y{{}URt`DPA#7T{K7`zPGVQ|n}{XVdKSPOAU
zWTou(Z)mBFMaYqo`n01qY*^R}^{X&up)&R{4#2XYXlIJ-tp1d~T3FK2xxf@n*N0e9
zV+GtFI!zsLN<Rj=eoj}WFpDZ86!R1!HH)FAK`&~n$js!~<5LA)S(snQvzbip0RT-<
z@xaF!TP;w6i6X2PN|;=?*wb@ZkWTd+om(vst1^3Ij=bOr{G_OAh}<(;a@*ljWOxyE
z6@5?O%ozJ)DoYbR9Q>fN5Wb-lTS!e0!-iay!T88j@=TrlR=vNa=36Q&rBcYdaPhKH
zwND$iZ9Qq(MtQMZw(YWQ99!c}W5o%?f_d_|VfKOzk1w~m$9=bfrsFp{%UesQ%u^@L
z&9Y&(q1*S`#)da=+aA*Cja>H;>TcIuZ@~S4H^J}J)}6Xr^Em>WylF$D<M>eO^n1M2
z>^I+VOKmqOb=^98wH>!x@;iZvp}6lk-BNSF*LlDRH7SlM;*<hD2nMC56F8x2;#$*f
zwM@R{20)9Z)9v=Jo5A)qZaTj2Y-7z<<Nr&1kDA~@wR*-lhIe!PA6!g$(SW^SDD1`>
zu&o@MpUh~t@KMl%l8-8!pP9;il&WUOuAqb9Bu(_5((ub)Noz`C$D-yjdTa-1%CM~*
zz{pTALy{;Ewva<h3WOad0Utkv5ugBm4--VeCYB@$U=v6X1*Q{Fil04%(;3f1&PSg8
zy&fU8KY14u$dB$`W*|Je%NdcGaQeJ1>?1K;nDC%b=w73@bRyy}Yar;^2u^vg3j389
z{-&_skKq@EeKUrCAnfnO9Ir?2o%W;(ADVJPkNZC&aCr{f!lnjGBJrPwCjL$hMdH5-
z`>dGb@eP5~nMKbHtig6KaOyY*{&4O{E#O-LSk#Qbst*QW`Mm2`O<vz<V<&9&aV^xH
zdfn%n2bLY*@!I?#rR#cJQZR*739f~9qu=Xs?6#KI54eSW!yE)oV_gL1om$IxdR%Pe
zKTh+=r!$iGB*RsNGJ5aACf}$O(I71RM-f?gHKIq=IR7Xv(mVaPe7gU5Ov)$o|4Bwf
z;=c5*sB2XsKBUfy1JMK_j~MyM?-c%*2xS?^cuZP&S%6=LUi(Ab_Yy9}G4jrc$GNyq
z4Yj!M^N1$ZZ-~|^`BXna@%o#H#bY7_r$zoNWJDzHTf?=a{Fg-jtAb1Usqe(|Uqg&y
z$~sXE<r9(rrUb++^UL`y#FO|(#DmI8$Gh-%@4|nSHic3ePNPwVlk;b|BK46IDr<rt
z#Ym6)ejzZi-I4oCg1<CEhKf8t34e|XxmF|~=C8#0%XdoWPrrJSFLA0To|{7468Sr#
oVETQcU(-0B?lk`0MF{ppvp2|yNPPcPR}%jRQUBKwE~#<;-*v`5{r~^~

literal 0
HcmV?d00001

diff --git a/tests/data/test-abidiff-exit/test-allow-type-array-v3.c b/tests/data/test-abidiff-exit/test-allow-type-array-v3.c
new file mode 100644
index 00000000..e8111f69
--- /dev/null
+++ b/tests/data/test-abidiff-exit/test-allow-type-array-v3.c
@@ -0,0 +1,20 @@
+struct C0
+{
+  int m0;
+  int m1;
+  int correctly_inserted;
+  char rh_kabi_reserved1[46];
+};
+
+struct C1
+{
+  int m0;
+  char m1;
+  int m2;
+};
+
+int
+foo(struct C0 *c0, struct C1 *c1)
+{
+  return c0->m0 + c1->m0;
+}
diff --git a/tests/data/test-abidiff-exit/test-allow-type-array-v3.o b/tests/data/test-abidiff-exit/test-allow-type-array-v3.o
new file mode 100644
index 0000000000000000000000000000000000000000..5fb6bf50c1869701bc5779720ae8eb61bf80a262
GIT binary patch
literal 3456
zcmc&$&2Jk;6o2FO#+z)M*l~e`fMO%6eu!PiZq<gC6eFuN5vWv!s)#RVZI3^QKdSX6
zBt?83K!hMvB#u3D;edq11&JdZICA4J;9lSofdsrao^dvlZH3^#NIUc1Z{F{{w=-{_
z{m#W_pN=sGYB0C}`<g-l-nI7Sk`PO91Tt`KW#{LWowxQ@b~f&;>|Wg4P2qfJ7ub)l
ze)l`tnZN`yL7ayLF*H3Cl4IpXc!D(%VckVA5+tEmfVdFj2I;1dEg_FtJwztKikt0T
z0ORkY_BK8fG&vBrM9erFLlt7>0!)s7gA#eS_q|VA#EY|T5t2jaU^n`GU}=#HacQVb
z+3nv@Gi!^GA|du^%d8b(VNb|wFy^8#_AyStl3-Zpa_qeQxV@TLve7uh49m8MSk7V@
z00WJt4j9EB1>3%08&ep?lpb<vauJ(F*VAC<tW{(t((LF|#x@q_7t(Adk$M2Yl2knK
zamH3N6ks9;tC>6ow_Wz+942H_KF8)(GsG&*-kc*VcnqHyWeuTwMoqo8*a}V0qimw>
zNn9CYe@rE5p~Arjg#}SVF}4t!9=a7=lfn2%HPXzS{MNj$VWt}<%g18KyL9<lwp=)y
zEfm~SZXsJ-EEWreVj+9{Dz9f(yg+U^r%z;^Zm`kg%T3<ne!GUM=hs@xTT5r0GpC)+
zV%BLwr{8O4H+qA1vxju55x-Sg_p0rR&j;M!<n;p7`o7O=L1(+t?jaWNI@DU85BXNV
z%k%Yq{q=Uf*$(oZb`^<DuieS}t-wKN-0yjve0{*zdB6#E$h9L~=L0?n2Kl-dctUh=
zuikDn9KO{KfHobk)9JtA1lu>b<N3a~?QEWMYyV&1d*lR{%H?y}<9ItK{=wCVt3D!T
ztc9Cbfo-MO{A9wqg+)OHrHV?dPfevhh?SFL*U><7q86&BIQ;Tg)SeO9vGC)PC9QJ|
z+=&_nMu-{)h!%2a%K*_~BJlA;7y$<0cNj4YY+_Nv0Biyg!+_}sl>8?T;dI6`q4QB^
zf3Fgx_NVS*0{P*$R}lygzvYaKOgKHSN&S8emnu9c1-jQ!OF;Rg#L0)sfuQF?IK}m)
z{<4OzN&P(yzby3+HJqA1jqitxIH_<x>fI?HQ`*pylCqxvBN8WjdbXub4VH$&e;TS#
zkgLLfmHK(P>i)A5ryRq1f;-sm23{4{z#pzHB?Wvd0GC=3xYfY`T%UJ5x6Z2@P3(G&
zKJE$Gt5$u!d0^Y~y<U?aq;%Rnt|%Bni3C@KUh8+e9J{UC>j&J$uHg&<ueL57^H!zd
zdtEMH<UcO+$fq+>_oUpFq%f*?sgrGZ<WM0@?+CAijY6`{KZbi$r~j5u_pke;a4P<v
zq=bfUOLawEs}!;!aaJCPB?)Q7$WDDz_+LVdWt?@NwDFPzzZ6#clWuzjw{(oG6S6oL
z_o+hCZJ$9jD*vo(t%^_i6Qt)~M@;vL5S*3quagoQx@`sbqT*kX@z*4m;#1$z<KIAx
ze5yQA6yp;a|04y+RmE59n}|p8XJtVpmEm3ZyLaIqrI*5yA1<R=xl`+BxTEBe5ehFz
zei$QJxBWt5@^we%FG~K>2pJ0M{1p5-3S?SRfLy<l=dbFN&Yynu6kp+#j~<&`+>-HI
pGGY3CqF+;;Pj{++y9mLaZ1!1FLPLLl$}5WhgUtWy5SK)q|2NDSKh*#L

literal 0
HcmV?d00001

diff --git a/tests/data/test-abidiff-exit/test-allow-type-region-suppr.txt b/tests/data/test-abidiff-exit/test-allow-type-region-suppr.txt
new file mode 100644
index 00000000..0706c4d6
--- /dev/null
+++ b/tests/data/test-abidiff-exit/test-allow-type-region-suppr.txt
@@ -0,0 +1,11 @@
+[allow_type]
+ type_kind = struct
+ has_data_member_regexp = rh_kabi_reserved
+
+[suppress_type]
+ type_kind = struct
+ has_data_member_inserted_between =
+  {
+   offset_of_first_data_member_regexp(rh_kabi_reserved),
+   offset_of_last_data_member_regexp(rh_kabi_reserved)
+  }
diff --git a/tests/data/test-abidiff-exit/test-allow-type-region-v0--v1-report-1.txt b/tests/data/test-abidiff-exit/test-allow-type-region-v0--v1-report-1.txt
new file mode 100644
index 00000000..7249e6c8
--- /dev/null
+++ b/tests/data/test-abidiff-exit/test-allow-type-region-v0--v1-report-1.txt
@@ -0,0 +1,20 @@
+Functions changes summary: 0 Removed, 1 Changed, 0 Added function
+Variables changes summary: 0 Removed, 0 Changed, 0 Added variable
+
+1 function with some indirect sub-type change:
+
+  [C] 'function int foo(C0*, C1*)' at test-allow-type-region-v0.c:19:1 has some indirect sub-type changes:
+    parameter 1 of type 'C0*' has sub-type changes:
+      in pointed to type 'struct C0' at test-allow-type-region-v1.c:1:1:
+        type size hasn't changed
+        1 data member change:
+          type of 'unsigned int rh_kabi_reserved1' changed:
+            type name changed from 'unsigned int' to 'int'
+            type size hasn't changed
+          and name of 'C0::rh_kabi_reserved1' changed to 'C0::correctly_inserted' at test-allow-type-region-v1.c:5:1
+    parameter 2 of type 'C1*' has sub-type changes:
+      in pointed to type 'struct C1' at test-allow-type-region-v1.c:12:1:
+        type size hasn't changed
+        1 data member insertion:
+          'char wrongly_inserted', at offset 40 (in bits) at test-allow-type-region-v1.c:16:1
+
diff --git a/tests/data/test-abidiff-exit/test-allow-type-region-v0--v1-report-2.txt b/tests/data/test-abidiff-exit/test-allow-type-region-v0--v1-report-2.txt
new file mode 100644
index 00000000..9666a8fd
--- /dev/null
+++ b/tests/data/test-abidiff-exit/test-allow-type-region-v0--v1-report-2.txt
@@ -0,0 +1,3 @@
+Functions changes summary: 0 Removed, 0 Changed (1 filtered out), 0 Added function
+Variables changes summary: 0 Removed, 0 Changed, 0 Added variable
+
diff --git a/tests/data/test-abidiff-exit/test-allow-type-region-v0--v2-report-1.txt b/tests/data/test-abidiff-exit/test-allow-type-region-v0--v2-report-1.txt
new file mode 100644
index 00000000..0db5dda4
--- /dev/null
+++ b/tests/data/test-abidiff-exit/test-allow-type-region-v0--v2-report-1.txt
@@ -0,0 +1,21 @@
+Functions changes summary: 0 Removed, 1 Changed, 0 Added function
+Variables changes summary: 0 Removed, 0 Changed, 0 Added variable
+
+1 function with some indirect sub-type change:
+
+  [C] 'function int foo(C0*, C1*)' at test-allow-type-region-v0.c:19:1 has some indirect sub-type changes:
+    parameter 1 of type 'C0*' has sub-type changes:
+      in pointed to type 'struct C0' at test-allow-type-region-v2.c:1:1:
+        type size hasn't changed
+        1 data member deletion:
+          'unsigned int rh_kabi_reserved1', at offset 64 (in bits) at test-allow-type-region-v0.c:5:1
+        1 data member insertion:
+          'int incorrectly_inserted', at offset 32 (in bits) at test-allow-type-region-v2.c:4:1
+        1 data member change:
+          'int m1' offset changed from 32 to 64 (in bits) (by +32 bits)
+    parameter 2 of type 'C1*' has sub-type changes:
+      in pointed to type 'struct C1' at test-allow-type-region-v2.c:12:1:
+        type size hasn't changed
+        1 data member insertion:
+          'char wrongly_inserted', at offset 40 (in bits) at test-allow-type-region-v2.c:16:1
+
diff --git a/tests/data/test-abidiff-exit/test-allow-type-region-v0--v2-report-2.txt b/tests/data/test-abidiff-exit/test-allow-type-region-v0--v2-report-2.txt
new file mode 100644
index 00000000..e807fcec
--- /dev/null
+++ b/tests/data/test-abidiff-exit/test-allow-type-region-v0--v2-report-2.txt
@@ -0,0 +1,16 @@
+Functions changes summary: 0 Removed, 1 Changed, 0 Added function
+Variables changes summary: 0 Removed, 0 Changed, 0 Added variable
+
+1 function with some indirect sub-type change:
+
+  [C] 'function int foo(C0*, C1*)' at test-allow-type-region-v0.c:19:1 has some indirect sub-type changes:
+    parameter 1 of type 'C0*' has sub-type changes:
+      in pointed to type 'struct C0' at test-allow-type-region-v2.c:1:1:
+        type size hasn't changed
+        1 data member deletion:
+          'unsigned int rh_kabi_reserved1', at offset 64 (in bits) at test-allow-type-region-v0.c:5:1
+        1 data member insertion:
+          'int incorrectly_inserted', at offset 32 (in bits) at test-allow-type-region-v2.c:4:1
+        1 data member change:
+          'int m1' offset changed from 32 to 64 (in bits) (by +32 bits)
+
diff --git a/tests/data/test-abidiff-exit/test-allow-type-region-v0--v3-report-1.txt b/tests/data/test-abidiff-exit/test-allow-type-region-v0--v3-report-1.txt
new file mode 100644
index 00000000..c5dbee67
--- /dev/null
+++ b/tests/data/test-abidiff-exit/test-allow-type-region-v0--v3-report-1.txt
@@ -0,0 +1,23 @@
+Functions changes summary: 0 Removed, 1 Changed, 0 Added function
+Variables changes summary: 0 Removed, 0 Changed, 0 Added variable
+
+1 function with some indirect sub-type change:
+
+  [C] 'function int foo(C0*, C1*)' at test-allow-type-region-v0.c:19:1 has some indirect sub-type changes:
+    parameter 1 of type 'C0*' has sub-type changes:
+      in pointed to type 'struct C0' at test-allow-type-region-v3.c:1:1:
+        type size changed from 224 to 256 (in bits)
+        1 data member insertion:
+          'int incorrectly_inserted', at offset 64 (in bits) at test-allow-type-region-v3.c:5:1
+        5 data member changes:
+          'unsigned int rh_kabi_reserved1' offset changed from 64 to 96 (in bits) (by +32 bits)
+          'unsigned int rh_kabi_reserved2' offset changed from 96 to 128 (in bits) (by +32 bits)
+          'unsigned int rh_kabi_reserved3' offset changed from 128 to 160 (in bits) (by +32 bits)
+          'unsigned int rh_kabi_reserved4' offset changed from 160 to 192 (in bits) (by +32 bits)
+          'unsigned int rh_kabi_reserved5' offset changed from 192 to 224 (in bits) (by +32 bits)
+    parameter 2 of type 'C1*' has sub-type changes:
+      in pointed to type 'struct C1' at test-allow-type-region-v3.c:13:1:
+        type size hasn't changed
+        1 data member insertion:
+          'char wrongly_inserted', at offset 40 (in bits) at test-allow-type-region-v3.c:17:1
+
diff --git a/tests/data/test-abidiff-exit/test-allow-type-region-v0--v3-report-2.txt b/tests/data/test-abidiff-exit/test-allow-type-region-v0--v3-report-2.txt
new file mode 100644
index 00000000..e4f836e9
--- /dev/null
+++ b/tests/data/test-abidiff-exit/test-allow-type-region-v0--v3-report-2.txt
@@ -0,0 +1,18 @@
+Functions changes summary: 0 Removed, 1 Changed, 0 Added function
+Variables changes summary: 0 Removed, 0 Changed, 0 Added variable
+
+1 function with some indirect sub-type change:
+
+  [C] 'function int foo(C0*, C1*)' at test-allow-type-region-v0.c:19:1 has some indirect sub-type changes:
+    parameter 1 of type 'C0*' has sub-type changes:
+      in pointed to type 'struct C0' at test-allow-type-region-v3.c:1:1:
+        type size changed from 224 to 256 (in bits)
+        1 data member insertion:
+          'int incorrectly_inserted', at offset 64 (in bits) at test-allow-type-region-v3.c:5:1
+        5 data member changes:
+          'unsigned int rh_kabi_reserved1' offset changed from 64 to 96 (in bits) (by +32 bits)
+          'unsigned int rh_kabi_reserved2' offset changed from 96 to 128 (in bits) (by +32 bits)
+          'unsigned int rh_kabi_reserved3' offset changed from 128 to 160 (in bits) (by +32 bits)
+          'unsigned int rh_kabi_reserved4' offset changed from 160 to 192 (in bits) (by +32 bits)
+          'unsigned int rh_kabi_reserved5' offset changed from 192 to 224 (in bits) (by +32 bits)
+
diff --git a/tests/data/test-abidiff-exit/test-allow-type-region-v0--v4-report-1.txt b/tests/data/test-abidiff-exit/test-allow-type-region-v0--v4-report-1.txt
new file mode 100644
index 00000000..8a435001
--- /dev/null
+++ b/tests/data/test-abidiff-exit/test-allow-type-region-v0--v4-report-1.txt
@@ -0,0 +1,28 @@
+Functions changes summary: 0 Removed, 1 Changed, 0 Added function
+Variables changes summary: 0 Removed, 0 Changed, 0 Added variable
+
+1 function with some indirect sub-type change:
+
+  [C] 'function int foo(C0*, C1*)' at test-allow-type-region-v0.c:19:1 has some indirect sub-type changes:
+    parameter 1 of type 'C0*' has sub-type changes:
+      in pointed to type 'struct C0' at test-allow-type-region-v4.c:1:1:
+        type size hasn't changed
+        3 data member changes:
+          type of 'unsigned int rh_kabi_reserved1' changed:
+            type name changed from 'unsigned int' to 'int'
+            type size hasn't changed
+          and name of 'C0::rh_kabi_reserved1' changed to 'C0::correctly_inserted1' at test-allow-type-region-v4.c:5:1
+          type of 'unsigned int rh_kabi_reserved3' changed:
+            type name changed from 'unsigned int' to 'int'
+            type size hasn't changed
+          and name of 'C0::rh_kabi_reserved3' changed to 'C0::correctly_inserted2' at test-allow-type-region-v4.c:7:1
+          type of 'unsigned int rh_kabi_reserved4' changed:
+            type name changed from 'unsigned int' to 'int'
+            type size hasn't changed
+          and name of 'C0::rh_kabi_reserved4' changed to 'C0::correctly_inserted3' at test-allow-type-region-v4.c:8:1
+    parameter 2 of type 'C1*' has sub-type changes:
+      in pointed to type 'struct C1' at test-allow-type-region-v4.c:12:1:
+        type size hasn't changed
+        1 data member insertion:
+          'char wrongly_inserted', at offset 40 (in bits) at test-allow-type-region-v4.c:16:1
+
diff --git a/tests/data/test-abidiff-exit/test-allow-type-region-v0--v4-report-2.txt b/tests/data/test-abidiff-exit/test-allow-type-region-v0--v4-report-2.txt
new file mode 100644
index 00000000..9666a8fd
--- /dev/null
+++ b/tests/data/test-abidiff-exit/test-allow-type-region-v0--v4-report-2.txt
@@ -0,0 +1,3 @@
+Functions changes summary: 0 Removed, 0 Changed (1 filtered out), 0 Added function
+Variables changes summary: 0 Removed, 0 Changed, 0 Added variable
+
diff --git a/tests/data/test-abidiff-exit/test-allow-type-region-v0--v5-report-1.txt b/tests/data/test-abidiff-exit/test-allow-type-region-v0--v5-report-1.txt
new file mode 100644
index 00000000..ffc32324
--- /dev/null
+++ b/tests/data/test-abidiff-exit/test-allow-type-region-v0--v5-report-1.txt
@@ -0,0 +1,30 @@
+Functions changes summary: 0 Removed, 1 Changed, 0 Added function
+Variables changes summary: 0 Removed, 0 Changed, 0 Added variable
+
+1 function with some indirect sub-type change:
+
+  [C] 'function int foo(C0*, C1*)' at test-allow-type-region-v0.c:19:1 has some indirect sub-type changes:
+    parameter 1 of type 'C0*' has sub-type changes:
+      in pointed to type 'struct C0' at test-allow-type-region-v5.c:1:1:
+        type size changed from 224 to 256 (in bits)
+        1 data member insertion:
+          'int incorrectly_inserted', at offset 224 (in bits) at test-allow-type-region-v5.c:10:1
+        3 data member changes:
+          type of 'unsigned int rh_kabi_reserved1' changed:
+            type name changed from 'unsigned int' to 'int'
+            type size hasn't changed
+          and name of 'C0::rh_kabi_reserved1' changed to 'C0::correctly_inserted1' at test-allow-type-region-v5.c:5:1
+          type of 'unsigned int rh_kabi_reserved3' changed:
+            type name changed from 'unsigned int' to 'int'
+            type size hasn't changed
+          and name of 'C0::rh_kabi_reserved3' changed to 'C0::correctly_inserted2' at test-allow-type-region-v5.c:7:1
+          type of 'unsigned int rh_kabi_reserved4' changed:
+            type name changed from 'unsigned int' to 'int'
+            type size hasn't changed
+          and name of 'C0::rh_kabi_reserved4' changed to 'C0::correctly_inserted3' at test-allow-type-region-v5.c:8:1
+    parameter 2 of type 'C1*' has sub-type changes:
+      in pointed to type 'struct C1' at test-allow-type-region-v5.c:13:1:
+        type size hasn't changed
+        1 data member insertion:
+          'char wrongly_inserted', at offset 40 (in bits) at test-allow-type-region-v5.c:17:1
+
diff --git a/tests/data/test-abidiff-exit/test-allow-type-region-v0--v5-report-2.txt b/tests/data/test-abidiff-exit/test-allow-type-region-v0--v5-report-2.txt
new file mode 100644
index 00000000..c467ab22
--- /dev/null
+++ b/tests/data/test-abidiff-exit/test-allow-type-region-v0--v5-report-2.txt
@@ -0,0 +1,25 @@
+Functions changes summary: 0 Removed, 1 Changed, 0 Added function
+Variables changes summary: 0 Removed, 0 Changed, 0 Added variable
+
+1 function with some indirect sub-type change:
+
+  [C] 'function int foo(C0*, C1*)' at test-allow-type-region-v0.c:19:1 has some indirect sub-type changes:
+    parameter 1 of type 'C0*' has sub-type changes:
+      in pointed to type 'struct C0' at test-allow-type-region-v5.c:1:1:
+        type size changed from 224 to 256 (in bits)
+        1 data member insertion:
+          'int incorrectly_inserted', at offset 224 (in bits) at test-allow-type-region-v5.c:10:1
+        3 data member changes:
+          type of 'unsigned int rh_kabi_reserved1' changed:
+            type name changed from 'unsigned int' to 'int'
+            type size hasn't changed
+          and name of 'C0::rh_kabi_reserved1' changed to 'C0::correctly_inserted1' at test-allow-type-region-v5.c:5:1
+          type of 'unsigned int rh_kabi_reserved3' changed:
+            type name changed from 'unsigned int' to 'int'
+            type size hasn't changed
+          and name of 'C0::rh_kabi_reserved3' changed to 'C0::correctly_inserted2' at test-allow-type-region-v5.c:7:1
+          type of 'unsigned int rh_kabi_reserved4' changed:
+            type name changed from 'unsigned int' to 'int'
+            type size hasn't changed
+          and name of 'C0::rh_kabi_reserved4' changed to 'C0::correctly_inserted3' at test-allow-type-region-v5.c:8:1
+
diff --git a/tests/data/test-abidiff-exit/test-allow-type-region-v0.c b/tests/data/test-abidiff-exit/test-allow-type-region-v0.c
new file mode 100644
index 00000000..d053a313
--- /dev/null
+++ b/tests/data/test-abidiff-exit/test-allow-type-region-v0.c
@@ -0,0 +1,22 @@
+struct C0
+{
+  int m0;
+  int m1;
+  unsigned rh_kabi_reserved1;
+  unsigned rh_kabi_reserved2;
+  unsigned rh_kabi_reserved3;
+  unsigned rh_kabi_reserved4;
+  unsigned rh_kabi_reserved5;
+};
+
+struct C1
+{
+  int m0;
+  char m1;
+};
+
+int
+foo(struct C0 *c0, struct C1 *c1)
+{
+  return c0->m0 + c1->m0;
+}
diff --git a/tests/data/test-abidiff-exit/test-allow-type-region-v0.o b/tests/data/test-abidiff-exit/test-allow-type-region-v0.o
new file mode 100644
index 0000000000000000000000000000000000000000..4a036677aa27e0c2cc8b16106ed3421cf6820086
GIT binary patch
literal 3576
zcmc&$&2Jl35TEDu#tzPh6BkHGQEUWiTCsMVhLSc)F^WnP6+u;0RpO)R+J3f|_#;|x
zLK=h$mr7BD)JhyU^adBs2&pPZE<JMOAK+djBqBh-%=5ft^So537e?~VZ+>s)&AgAD
z^{oqUz7Z1wv<Ns4hn8jm_LGOWq+kh-K?bg_?Ebv6`^o;w?&hPFy$kz$X?#A~1M%Yr
zKm3J$HV74&pqS@XVCZo?gstUSI4*7@6Kl<al^}$$2yq4Dsw(?)B$Emb5FAo4K`^ag
zlHf4~2MJC<GH!eA0OD~~g0)<P<j{9GLRm%*vy3K*j6+-upoYO(rvoCR>LHs%S1a!Q
zhL&BKg*1WK-G*H$!t|dC-++NUf`Kpa0b&@ek#jk*m^zyqTFoq^&>N{_k4p6zD<iCA
zD#dO|k-;)vgjDK$${N8mHv8u?XE7i)fpd>SDwkYEzr>h$X(W@frl+RI#CRh8EN%{m
z;;s+o#cPw8EQRS)Cni@j<Wrj1nWPl(GX7#zB!rF@Exk%L6neaa*hb$|sD%)Jj0}^a
zoyH%6X|?GwF&!K2O{-#U0fPr7uGAj--hR4fkJW5Zh{Z5&v)%RTZCTCIAzggyYIeE!
zYPML+pUxMvbF*`E#o}BsJ97mcR@{IGocWVkrxk3r<x*X?rSDbHbp1+WX?x*}b7tPz
zn#($M@EdFEZrNM&Wmo!JvRdq8=K7e^eaw6x^9ocNt`CJqrzH#3PW3~tQ1^mD(<@`P
zy6ZIyej{*jJ<@Nx%|f*+*JU7yb*LVP>nQ}X8*~d*H*ghnaIEUpYL49Y0-!<1Z8kd}
zIl;{hi9vO*({{FI^OgTG^W&<5i_6RBvNL#wC;!6%s@s9DDr>QG1;ln*ObsQHckm|B
zEz~zr>DNcnw`0r01J}@jW1<vY!Z`f$chr~_+1UUreh<tT3rC{F!VFPjfodInLl&sk
z6TuJmaYR^veIi0E7>Z!YANxdvSTGvFlK*fYPpx@8v_E?9AA1j}N3!TMc?#|DF6d0e
z(`vOn&T+)k?>gJh7(AQs90NMvgMNbe6!SEPu7RkxLp<eukL`C2zQwjQ_*-mm82nwf
zKQ{QU*uG=%Y{Gqmr;iQE!=6$*A6}Q<Lt2;3KBUr&&3$-*dGe><4%^h_Ni_dc&x9gt
z&Hu{wB5QO0Eb~-jxK7A-Z?*!rj5_dpx}i}Zw*$yie}jCv+l9O@n{K`;%bWEzuU+fl
zn8I$k?8~hq!>;eP>+)zw(`!o|f+;j4dQI7tPOByHE0S+_0-47?<a7hKvd$CB##+sH
zTl%1RJx{8pFp&00pOc=a#NgolVViuzl0$<yv%|a}b_!uL{sfNEeSgw;I)8IcN~iP3
zj6uUx{~o9dmMADBE4(3>IbaOO$WOnU`1eK05<bm2Y2Z4O_mtP+!}Pt3LncSwzcV>|
z7^;xzdkNX7`oH8>t>dYFqRjfM$eD8@1*iDsxJE`uOy4yei^~6y^WWfD%1`~v%)fyg
z&8h1|sN^%w|3DM0b$+d>e?`SF@`dXCn?9UG$7#LyFY%XXQ7DC;I(X_ht?BQv#s^La
zb~rxF5jK6lVeUvTLhe(JU!b6n=>5~w*9ge9qzTqD?5OW4?LU1*b-d=O9y2#hai8-y
oxM2Fu(bw6Gr!zI*U8Eq(?@x}5keKVIx}xGsoaoz7EI~8=A2fhgBme*a

literal 0
HcmV?d00001

diff --git a/tests/data/test-abidiff-exit/test-allow-type-region-v1.c b/tests/data/test-abidiff-exit/test-allow-type-region-v1.c
new file mode 100644
index 00000000..09c1ec8d
--- /dev/null
+++ b/tests/data/test-abidiff-exit/test-allow-type-region-v1.c
@@ -0,0 +1,23 @@
+struct C0
+{
+  int m0;
+  int m1;
+  int correctly_inserted;
+  unsigned rh_kabi_reserved2;
+  unsigned rh_kabi_reserved3;
+  unsigned rh_kabi_reserved4;
+  unsigned rh_kabi_reserved5;
+};
+
+struct C1
+{
+  int m0;
+  char m1;
+  char wrongly_inserted;
+};
+
+int
+foo(struct C0 *c0, struct C1 *c1)
+{
+  return c0->m0 + c1->m0;
+}
diff --git a/tests/data/test-abidiff-exit/test-allow-type-region-v1.o b/tests/data/test-abidiff-exit/test-allow-type-region-v1.o
new file mode 100644
index 0000000000000000000000000000000000000000..668224e36f697e124c0928a75922eac538893d5a
GIT binary patch
literal 3632
zcmc&$&2Jl35TEsC6Q@obCpM6fB0C7w53zPO4JD*XF|tY%fdCa%JwQp;_Ordj_Ezgn
zngl^_NKq~=f&-!lgt%~q6C9C1NE~`Be*pI)aj3)rW}fFco0nCE;J`@x<~P5YdGo&Z
z?cTcd+A9ge03`-oghNZQ0P~}VxGZ5Aa*&0qtNXvL?tk!bb${!@>cORl2N`T19Ds4}
z`VSM>kp)JQ!bhliikhgLj2LER9!?qu$Rs-RV2%(%=)jPKLvmD>#7T*V$3Di0F^NYc
z&Pbe;I4AL_#M6)*vb=5xLql>LX2pTz*pFN)eOPKDNo2oNMq&%CVdP-6;rn3Z<N(Pg
zQNuUx(ul0uJY)zY?zF6$1Nm`@H(<DkvBRHHhA|FidU?*cka{t-mR(9=Ffz#_jF~me
zWGdC?=8{GhNAwJ&QWsNZ8mm|wKbNB}64TiA1f=GYYZx~&X*`?Grp)|oe$tp4$vgp&
z<W$^+(W3Fv3>HgK^;0uzS&Au7@6AvNcn+TgjS=LANkomB<bYuC7HSJ)&!RRA<InUs
zDOz=W5abC$!pJ8k`rVQVmI0#!)65yx*!R|B4QsMt8Kp!5^Mcm;rd##a1JM(~wx~Z3
zTiu@5?23AUcJT7+R|^&Ag@WT0&lR1*!u-O5<19FZ(^oKH)eYHUFP<sbop7rwR+^$K
z0<VUq8`N4WJ4@&7^NaTOLcwmr&A{(A+q>&t7mJ0W4tn*4W6Zf@%p%nMAP}`M+EK0L
z22g7G9Z{<L_4m9|(+f*&uZmM^x?Z~!v_c!_BZ98mF4cQtQ-p$8n<j7b<V&IGg}qYU
z4P6Os^wqsa!xlSU2;{WgcH6&chr2ffCN(|3Yi~Qn+W%PjQKN#(mCADAH16S<f3Sh_
zyyRqy2WsyM7&{qbc5Ebh8&4LUN_8@&emR}_C{Y<7zJ>vu6Q}4<4#Dq##re$Gn-9R`
z_d?Gx(Gw>oR)`Z5<V|wSW%h5!7(R08M!Y8Ay%8fOjK#1te!Mqg#Ds|$mg2{c@pN@h
zMb<~H{Uc8yU70LuPfnm6T?JK%c$#gV<EK~?PtUt-Kd$j?!jlYWe+Tyq;<L<CA8HIl
zy%FK5?ze2et??hS-PHKoZ1*(&OSW%mJe%;T#;bXLr|})Of7bY)*#4EZK3}!AG+&Ej
zNM#u7@lP{P@$~GmO<w~N#ozCn;ILNwA8cP>t@mGGp7tcVFHr35c0#v`It=={rBEn#
zLMYO$2gPcy2gN|N-C|u-x0-lUH+=L->{hFR*goQR1Gn20M^oBfSEv*$p&-%g(ysZP
zj=*nAvFnGTh_{p73*Fi#cPv`#jlk^)p2&YxvzSk7r1nX5S7tDHuh^#8sLY{3oPP7X
z6Ag-BJ^vK?=sozi`LzFfpHxoO&uUK1lz$m?JC-RaA}wB!7AH)ikK)uji@!NiR<PCk
zB!|!B=Q8TB(PQ64m(Ef22|k?jhp7_jv9BT<H~xF_#}faOIiJQ)ls^7Ca(bVXvds0b
zkr5F+b{&0j^;Mw_&ZYWvuj%z~AV+<waUzs*hwJ~M2-d2;((fZ5mw%lPRR0(JuoIQ1
zH2u}b<#%XOD5bs{v?@>OPtc?AAPB)-&X010_1LeO<LM5<zu^3(0n-O+{S^Hb0tziF
zg7pRms&h)~PhVG+uXq}dUYokO&-K;$r|$xNwe@`3Q~ljV3d;Qc%#jfhef~7AxcnV1
L^i3p}pq~FXvNv33

literal 0
HcmV?d00001

diff --git a/tests/data/test-abidiff-exit/test-allow-type-region-v2.c b/tests/data/test-abidiff-exit/test-allow-type-region-v2.c
new file mode 100644
index 00000000..e95b3115
--- /dev/null
+++ b/tests/data/test-abidiff-exit/test-allow-type-region-v2.c
@@ -0,0 +1,23 @@
+struct C0
+{
+  int m0;
+  int incorrectly_inserted;
+  int m1;
+  unsigned rh_kabi_reserved2;
+  unsigned rh_kabi_reserved3;
+  unsigned rh_kabi_reserved4;
+  unsigned rh_kabi_reserved5;
+};
+
+struct C1
+{
+  int m0;
+  char m1;
+  char wrongly_inserted;
+};
+
+int
+foo(struct C0 *c0, struct C1 *c1)
+{
+  return c0->m0 + c1->m0;
+}
diff --git a/tests/data/test-abidiff-exit/test-allow-type-region-v2.o b/tests/data/test-abidiff-exit/test-allow-type-region-v2.o
new file mode 100644
index 0000000000000000000000000000000000000000..924ae596926394cd7f0f020d9486c5eecfca4294
GIT binary patch
literal 3632
zcmcIm&2Jl35TEsW6Q_0(Ck~L1qBscD53zQfh7wYx7@5*UAcTsl9soIO``KP%?^f$g
zngl^_NKr17j{^cB4shVy0}>pOkhq{8%OAkKNE|A0fSKoc&gNwmArK>Z=QqDMU+-g|
z_x6=nUrrbXC^6tN99oJ6SQt6PWeLkL1v$9Bw*TAO{(BGC_O~9a9b9>Mkj3YN12FEr
z{o^<$vcO1@`!GG8phr|rM+~#F0H=)uWD?y4Foy~CTS-csOms_NNtluy4rM+<kBr2_
z5@#h&NjxR-h{V&7N?NWLLNY1)Fe@cUWq#sPWBpQ;emq5FQif(Fw$K_v4n~^355|;i
zkZcmwzVQWhWYrfSOCWKlZPiOKH!AT43>DCK=rf8iM!_6gnKv$_pHHvnmec5rOzH?@
z<_t5HPWQR_l##=Zo`Q7xa@rijDi-_CPf?1*G^QSh^n7X^{e~xur^j+>b8dES!k8S+
zJ_eBDP~3%)g7Lx(7E4q0Gc)Ts@+nX6%}@z=27d|a5#)wRL=QE|0m0x6)E4@lM{O9!
zpJSt>XzB2WV2&UpjJd>kKP{PH889+1%_+mm{9rxOv?iLCQA{K-E@)Rbotj$>L{9|U
zqVX(jc|Et~iAJ7w@ak*V^Oe$b`BJHHp-{>%E-WsVN{glZ*=y*q=7c<8FP+QV-Ehkj
zt1aP)z^$X{1oign&hkb3;*!0+n73PSEAYKmXSeElSS%C`aJ{-81fm{BNxh{-(3z!U
z%mt{ood8173vH*<@o(AT?oD9_qUHJ?Rxi|{*!H`k*zg<gxW$$m7CUYYC*E@0PBCbQ
zHqKTAp3^BddSX+Af>@iD29wC8DN^!YvEhV{gf@;f+-B1jJ8t+tXFq9PCyl|?N@XQ~
z7I*R7KiEP%FH`c=;DOq^2F6a-n9U5QKE#tnr&66vsb3t+evqh)4&6Wpj)_xrD3kE}
zUvXn*?9B&Y@_V7jm^czACRT_O6XZ>DY{=~2jxl`X(v6IofcHj>n2?EKseimTV#I{;
z7?%7;kMVSMPe#^9t^J87oUTk3wI`?0j;?~LL_E!Qj{PTD6Hh-Sk1|la>f>?dX@3Xz
z3*z$;f7Js~FGYB&`##%mYW&A+w>17cwtE`?4coUho=x~v<JCOB*Z3~mKWn_Qe`Q{u
zuUcDLAB%lRWf|-KPcpC8bB}HMHy}~`{k{n$){6gw?Mtln{CVbSPonz*h2CyAbZV%>
zps(8sg<>a!0^Ms+sP%eK2t>yzG(>Hyg*SE6$1#bWS}hRUM~0oi@mk_&NXPYr3c(Tz
z5?z&c-S2h<{<jo7KNJPLo9tfb)HgY?Xjhwo(-l0C|EOj$p4Le1lS)@+pw6@6$u}zV
zXb`90Ja0vvB3O?<gJbj_{M&fie?2FaQ}uJ&pk~Ui47wf56cmv*FG!mMCUA`W)H{ox
zIZ{^fspq5tpUKaq*Wp9=eI18%j=WFt;aoTjl}Pt}1=+a%@5v8K{7&Y0>YpgR{|0h;
zP6}D!`Zvgki0)g(vAFuG&<4j+eY)55`ZtlIoT^WRQtoj5UlhSw)mQpm<m2M2e4zTD
z;D<?6oYM4F9~bY_q)>`|HF&Bxr9Z(Dg$GUuzU25QM_Bj$mN}m8!2K(ZUmh@hpw>^(
zUn3ybvLaY-u%kMswEpz(s^S$-{phtR#eJ@?&OiMx(7(1GPkXAryGTKq-=BFhBBIZq
R`ihI+;X-#Ku>|$_zX1}OT?haG

literal 0
HcmV?d00001

diff --git a/tests/data/test-abidiff-exit/test-allow-type-region-v3.c b/tests/data/test-abidiff-exit/test-allow-type-region-v3.c
new file mode 100644
index 00000000..a7a638cc
--- /dev/null
+++ b/tests/data/test-abidiff-exit/test-allow-type-region-v3.c
@@ -0,0 +1,24 @@
+struct C0
+{
+  int m0;
+  int m1;
+  int incorrectly_inserted;
+  unsigned rh_kabi_reserved1;
+  unsigned rh_kabi_reserved2;
+  unsigned rh_kabi_reserved3;
+  unsigned rh_kabi_reserved4;
+  unsigned rh_kabi_reserved5;
+};
+
+struct C1
+{
+  int m0;
+  char m1;
+  char wrongly_inserted;
+};
+
+int
+foo(struct C0 *c0, struct C1 *c1)
+{
+  return c0->m0 + c1->m0;
+}
diff --git a/tests/data/test-abidiff-exit/test-allow-type-region-v3.o b/tests/data/test-abidiff-exit/test-allow-type-region-v3.o
new file mode 100644
index 0000000000000000000000000000000000000000..b30403eba8fe5a73be8dda242406da5fac9b2ed8
GIT binary patch
literal 3688
zcmc&$O=u)l5U!q{PC7gJnPieRF)NvfvOm}}nIx`pm#mRtcM}wqm1Ph9Bt0{4X4=g3
zEIplUl4V^_;u3@v1O<OU(1SO@g9kkbg5XUALGa>b?<;r+c+jdh^(LK{CWsz<(67Gw
zs_NDI>*~IJ@!3lW!vNI`xB&Z_Vgc%X`*=yhB^ZSaTwT6%Z~4xxz2!R__m_7s?(L?r
zzrPE{kFR_;i33?+*yP?z-9ywx+lh!`7H1%9yoXGpIRj=dA%q1;N|=<hvLv=8?&<#+
zJ^Cf?l{hW2E%B(teG-pLd`#jKU?(ll4<VV9b25tsum^tPv_t!8(>9Tvw8IixsP!NR
zebrVAj4?S`(y6JX7#~t*t2_f~0*QC(R=EIE6B4gMPY!*1KBEX@0L;O2)5iJKlc|-=
zLJFOcu@5k2#xU(vs>4m&Mh0i}2&7UMQsy9LvDklljD|>z<IqEpnzmQauXosZbTE@L
zrzWR{jgj8;0{}LM;)(R-jHf0rS&FisoLI?_&(iqL1f_t-@JY}dK^~q&)Tu>w2?j5s
zw9xkqO2aVz92_7)yN(ZnDT0tNrV>M)VMztcfWEG3ju}?}*Vb6o8m?MKK9Rt<puW29
zmb}$Kv_-HfDv!g4-}Y+0sATB`FF$uRTP!@0EfjKdxk7e!W_Gqvm@Q;aT|tLsH)Mk|
ze>&?l!wp|7)`TwtuZ*f2l<SLI3um3P^UmgM)~Ugbpyk&Z+pC_B$wE;9J$nH>zuXD}
zQ4WuCn>`dbe~6iba@`FeUvD)<zS63^=H+W%m~VI`TyD+v8u_3eI=Ds=_--R#X^V9c
z3Su4F5QjG;ABuL^&R5*fmC!+3#j92wvE_w8M#pV5S~r|<`?|oOn%DB3&AD9pf6RQ;
ztl)C7crJSi&-L^_I8=FuMrDteZ08CXTWMpmzt_Htw~cP9x~)<^J(zwoQ5@*Gh7KGP
zC+MOk;rGAdY-Vg_1Tgts(PK=s#5EH$#5EISbva}*JJmCWj}+yI)dZ|1G0lYj7?$Rb
zwIrsQFcia*|G*)hO8ZD;f7IR|c?+qGa#Lq=4E5+Ps7%DuYNyzLgeCFxyvq7<jb{}e
zWI*TJtvQI#Fi&HsIS}=Hgr~fBS$|36UuV6h@v_1r*VgzSSii0DU$MTc@vOop8n4#>
zy~a0L|5@XIW&IvYeIL}G(>_@2Ln6&s_kWmq@~3Bqb^3BpQ~WO-6$&gB{|D>mS?c3w
znWwXgo)_fW+s)7|p$vnLtSc0Xtq^il4Ix)*w;>mZhMTL1(nbxddbNc%iQQ5u5Ss_g
zZs7VgaWJIe`9g(Y3I&N?m3q0=Yzq7i<@{DCa#*#TcIcMZd0<grtp;vW@IwBhn8bM6
zBXv${xFrVN_l<S(jkal2h|??5%TcEY*5gm2joy!c8&Bu2k4foN{>~o)F`cIS(!HXp
zxzsUt`?q*Q-r#^?w2+^Am+_ZKwngmqG0AX~$q%I0ey97sfF_+I@8f(qXZAxS(tV#n
zHg5hlPgKRz{OP|`pMM28eN3d_66e20N~Gz&t7wbMzrcysIF|BLz0>nwM~=o+^F%1y
zN1XpR)|F8CmAr|DxcFOqp*p|rDtdpdDxT_MT>RU#C~WzT==Lg3$#>DBaMuaJHyj`3
z2<yI|GsnyAx_`^@3tgrI)cz^@Qv~F?qzIPpv!lAFwEy(gRq=|a`RKW6h&|3<=Lyqy
mfxhN?Je{fj?ji+i{QgXn5^4JSX<l*hUvQ!?Be4Ya_`d<0OK8jh

literal 0
HcmV?d00001

diff --git a/tests/data/test-abidiff-exit/test-allow-type-region-v4.c b/tests/data/test-abidiff-exit/test-allow-type-region-v4.c
new file mode 100644
index 00000000..bcb26e95
--- /dev/null
+++ b/tests/data/test-abidiff-exit/test-allow-type-region-v4.c
@@ -0,0 +1,23 @@
+struct C0
+{
+  int m0;
+  int m1;
+  int correctly_inserted1;
+  unsigned rh_kabi_reserved2;
+  int correctly_inserted2;
+  int correctly_inserted3;
+  unsigned rh_kabi_reserved5;
+};
+
+struct C1
+{
+  int m0;
+  char m1;
+  char wrongly_inserted;
+};
+
+int
+foo(struct C0 *c0, struct C1 *c1)
+{
+  return c0->m0 + c1->m0;
+}
diff --git a/tests/data/test-abidiff-exit/test-allow-type-region-v4.o b/tests/data/test-abidiff-exit/test-allow-type-region-v4.o
new file mode 100644
index 0000000000000000000000000000000000000000..8c77f1ab621c96d80c1e49893ffe116c04ebc514
GIT binary patch
literal 3640
zcmc&$O>7%g5T57tCYw4xPHZ3{MRpLVKVt3J4JD*%35rS+fdCa%J@AvR?Pq(5f1>p!
zO@g4DkfK~#1P25cq#oeR1;G&s35f&T%8d*6B5|n10cM`(Ih)s2h2X$Q`{tW(X5PHN
zeY-a<zVt#u2+$(n0vuYJ1y~$8#LEgULl!b{Wp)3z)%|zxt?qB#T|KyX?;wrs-2)Im
zz4_e)c4UKaD14Zj$Eb<Q@rYrq%)@bUfJ~x257satge6ESm{g;(6?POJ8vPI>Mim}b
zd|F{g;jF?VDrOp-r0sV?NG8=dtd$Ztqu+C>aTTvhO*lmMOQjXI(HcSyM(W)zh^!hQ
z*(7@S;xihNU7d$CfyAw*T`fUwOyO%VRKVDwPbfo-fi=D~C(fmwO|4}XQy7elbA+)n
z!g5lnJ~!ux436jtNTn{MtZ}SjbNpPEx=2i8*AtMMbJj3!cv3t$o=I7`+1#X<8csg~
z;BYGL!bm|pH-p7eRQ<%vT83hlr}t*41U!XLg2o7H!xW-MPjWyocn!6Uv8PcBA^sd6
zBSou@4}u&)NQhiwqTel<U<()-m{wNUqu<&O)$Pf;EsBW*<^|354X@&_2eK!FZCQI7
zwmLn((UG-0?ck-CujE%s&*V#`!kI!TU!E_QOQmutfATU0ta>3k+=Wwlw;gVE<oSl|
z$iT0n=>^s1`JKhH?%4%*yPS6$a6RaD8m--Rzk|g>Sp&0r8LHhNkkv2}REpu^;?6*|
z=><@1cH6R8>(<`(iw!?4w)_gtui^QvV$cj-oRkbYUaMH^$qgAwVqIDRmsg+|%3jzj
z*1XVD&_!R(uh(6<<A*>_*K4)9*WGY;Q({uX?{?hng+ldzto)!+!KIazrTj_U$W#Ad
z7uBJ`Cd)wVT?Vm}7PF(n&P_aFbUO73Rr>ky^aqKRv7xINpp#=X9nK{D{#T3(tk@e6
zkRP1fBWV_T;>5xVabkhGQSNhD{aZ4Ik6gYHuLXF2#E1o>F)WQA?~fR<U?PU4__6zV
zy27U->!a8Hfd`SUP8Pi<$Iy<hg04h7%{IsJQ>=-n=PkA$Hh4DSQ3kZX(cL;7iPvKw
z>Xisjb#Jo$y1~E4cEjLrv)wcJui3s~@NB}z2CwJ&jls9s{=wjXWcwG^=6v<q(wf;E
zLn_VKjDMVYz0P}V(^r8+^LP3tlvr#254O*-Hv7*rPkR#G7bx_0+o4xM9R_{f)F_lY
zAr$E1gF>a(gF+x%UZEx{TMfLi>s|CI>{Tj(+&<#=0<Y7MM^jpUN9q(Tp&`-h%C2_X
zZHeEQLZ=(b0$xyVFZ8M#+_7w~*8{IDc_RN&&0;>Sk=`fW-7<s0d&M@zMr95S;>?@p
zjc8B=oB1cuNAJPE&8Pi0`=oNZe#USbruy5U+i{taBC^Q~(&U6m^iiCCXYqGO$$4ze
zKFQH#@>3Oc*qE`eqRZqc`WPS1`NLF&%-9!^jT`?R^@9nZ^J)A<nd7e^XZDE{EOGs-
zWJJV_T}NMBeTNHO<6Npw_nKLM6FKTrj}xJiTU`HVO|aJWwSEWrxcmwqsQ$0`VJA9I
zYx=v7%MWN$D8;@Sv^r1gkI<v>APB+doFC-~o3URq$I~5zf5G{S1Evr3`f2($0t#K$
z1nVXT>T^o#PhVG^uX!4eS)01}mFw&CPu~UlYMc4Ar{=qh6s+?5Ge<^5%=y!};_~0;
MLSIF437YwT1884f`v3p{

literal 0
HcmV?d00001

diff --git a/tests/data/test-abidiff-exit/test-allow-type-region-v5.c b/tests/data/test-abidiff-exit/test-allow-type-region-v5.c
new file mode 100644
index 00000000..a3a33698
--- /dev/null
+++ b/tests/data/test-abidiff-exit/test-allow-type-region-v5.c
@@ -0,0 +1,24 @@
+struct C0
+{
+  int m0;
+  int m1;
+  int correctly_inserted1;
+  unsigned rh_kabi_reserved2;
+  int correctly_inserted2;
+  int correctly_inserted3;
+  unsigned rh_kabi_reserved5;
+  int incorrectly_inserted;
+};
+
+struct C1
+{
+  int m0;
+  char m1;
+  char wrongly_inserted;
+};
+
+int
+foo(struct C0 *c0, struct C1 *c1)
+{
+  return c0->m0 + c1->m0;
+}
diff --git a/tests/data/test-abidiff-exit/test-allow-type-region-v5.o b/tests/data/test-abidiff-exit/test-allow-type-region-v5.o
new file mode 100644
index 0000000000000000000000000000000000000000..a1da2d31dfa842f08f93a6e54b71b3ee9dbb50cf
GIT binary patch
literal 3696
zcmc&$O>7%g5T57F#!j6dCuvGUisB$pf5h5x`a@{dVidJa1Oil4^}tWMwx8`K_BvW`
z(liwm2??pnB?u%A{0OA>z?n-WZXh8J91s!`R}SS|Bo380z|7lu&gNxRAviG7zWL^R
zGjHDC&hE|gFI-3o0a^r{gB?q=0ILH#xU66q_QN<_Uc7a0@zz@p7H_THU)(zXU@M31
z{VfnbzW(hL4rGJKkb9b%$Eb<Rp_pMU%tBs#giNYE3s#yC!V>f;*r#S?E1XfdfABN(
z7*sf|_?*HSh4(8wpnMK0yif6mAk$}ieh7ViYEITd2{J=JaoQ2(qtfOwL`G>x6}Hjp
zM-B$+oeqczHCeJr^isqpl-aJ%LXJS{{ia<l!StlUSE0XvzWsM7LJWa5e0oNl$v%@^
z8lTUiGcuW7j5RK-Og0;FGZ`_CGkOBD*>hQI7_->yKQlo?qz>ZHBaofRETLa|R6IF6
zp0%c@rboqCI`;@bhC}g01`6WYNlccd?1v|p#>uCAaC4GUz*G37XpW!`Pa$gbB6|db
zS5VvNdknP@;?LnBQnc&%AebfyDKVWIiH0Q;Yyks3)0z<W;Mev<-5#ymqL@lyT+m!z
zaVy?(AiFYHm$j3y=6Ah@FKc-^!HX|m&M%an&X-Ds<AqXwZgy_2RGKU0k6c2BMK|OQ
z=fu&x(+<~sdA1>a8F*DR-Jse$yD@*tId#HWpUXQ9xE6H$M(g^r=VP)^*1*g@2h~mx
z$Z8mKDkX49e#gP{_eMjt=>|}2cG|L7>(t)#iVZI;w!8{1y5V}QV$cj7T&WCvw^gil
z<%$d?u?}sE!`o5}WjE{=Yi{T&=wM&XtJfX5;e|k*j@xQ=t~ufLRf$0juj4!GCkxg8
zG4sP_1s4|<PUnx{=^p(DN2_iUU@eM7Y+eGfkrPvc>C86XI=Zp?)++tnaPGa-!chMe
zbl{jIMHjXYe*Y_}&q}P202aS9W{ibBNn&A!B(XqMnLS-rR7Df`SaFW^T7WerK`a<d
zU}^qXQxe32kp!0fhxYJP;>Tk9qxb%y_mIjdi$0TmXvcR!XCj_fJI(%Mtcj=R4Ym&$
zJe%++13KSc?LmBuc^X5{fvA^aJmuYH`&EN~o9%|d-)6gO@ORn1Y4B{qmcjqP_7?`P
z*Z-Ztx7q&L;D2TN9&2+S^q$i`*z7|p$Jq3LoO$x6XOnIEf{<wbm&k+?Yt8?`_8HdZ
z_<82(tm5Ydh3@rs=vGjNL8O}+g>oZ=0@Xt(RJvU#1hVB8YO=D{!0KM_V4uQnr4q>X
zUEOZr`VF}|q~-ZihhPc~iC$KAwbO1({2mqjPACglx14V1R#$jn*<7v%Zd>v~{-c`0
zc-kX<PI|a9gWmhbHu=V71`XoOiu79CDTdAX!`Mgf$G?rI^Ebz&bUJ_Zr$9_3rZ3$q
zs+#4fyVq{>hP=Z8qu4`!`d!9fBPD0CHOHim8%%zzymlJX_hsxdIr2WhmveR}R3X#%
zd1RC3pW}(@c$z=`pPKWpA!m+>6kOo^SICHo>AQ@5N%_m1=qkrjeyVq7{#E2?Og&G8
zN<QWMziEQC&ad?i<dfpx<qH-4!tV^C<Fux_m=ym3EefR=sa~t&wEhr#H10Ve_=e--
z9AVS<OXhgFJ@@Z9e!j;<K<}TXZzCYrvL;x6&W`$?(*Dy|SI29f=40koLvXQf@`UNT
lKwon+p3c;KcPYNf@6QYwF)`Oq^Gb@p%Za{<#S%2*{|4n9YbO8z

literal 0
HcmV?d00001

diff --git a/tests/data/test-abidiff-exit/test-allow-type-suppr1.txt b/tests/data/test-abidiff-exit/test-allow-type-suppr1.txt
new file mode 100644
index 00000000..18725f37
--- /dev/null
+++ b/tests/data/test-abidiff-exit/test-allow-type-suppr1.txt
@@ -0,0 +1,7 @@
+[allow_type]
+ kind = struct
+ has_data_member_inserted_between = {offsetof(rh_kabi_reserved1), end}
+
+[allow_type]
+ kind = union
+ has_data_member_inserted_between = {offsetof(rh_kabi_reserved1), end}
diff --git a/tests/data/test-abidiff-exit/test-allow-type-suppr2.txt b/tests/data/test-abidiff-exit/test-allow-type-suppr2.txt
new file mode 100644
index 00000000..043cfb39
--- /dev/null
+++ b/tests/data/test-abidiff-exit/test-allow-type-suppr2.txt
@@ -0,0 +1,7 @@
+[allow_type]
+ type_kind = struct
+ has_data_member_regexp = rh_kabi_reserved
+
+[suppress_type]
+ type_kind = struct
+ has_data_member_inserted_between = {offset_of(rh_kabi_reserved1), end}
diff --git a/tests/test-abidiff-exit.cc b/tests/test-abidiff-exit.cc
index 2424675f..f7ded109 100644
--- a/tests/test-abidiff-exit.cc
+++ b/tests/test-abidiff-exit.cc
@@ -493,6 +493,193 @@ InOutSpec in_out_specs[] =
     "data/test-abidiff-exit/PR30048-test-2-report-1.txt",
     "output/test-abidiff-exit/PR30048-test-2-report-1.txt"
   },
+  {
+    "data/test-abidiff-exit/test-allow-type-array-v0.o",
+    "data/test-abidiff-exit/test-allow-type-array-v1.o",
+    "",
+    "",
+    "",
+    "--no-default-suppression",
+    abigail::tools_utils::ABIDIFF_ABI_CHANGE,
+    "data/test-abidiff-exit/test-allow-type-array-v0--v1-report-1.txt",
+    "output/test-abidiff-exit/test-allow-type-array-v0--v1-report-1.txt"
+  },
+  {
+    "data/test-abidiff-exit/test-allow-type-array-v0.o",
+    "data/test-abidiff-exit/test-allow-type-array-v1.o",
+    "data/test-abidiff-exit/test-allow-type-array-suppr.txt",
+    "",
+    "",
+    "--no-default-suppression",
+    abigail::tools_utils::ABIDIFF_OK,
+    "data/test-abidiff-exit/test-allow-type-array-v0--v1-report-2.txt",
+    "output/test-abidiff-exit/test-allow-type-array-v0--v1-report-2.txt"
+  },
+  {
+    "data/test-abidiff-exit/test-allow-type-array-v0.o",
+    "data/test-abidiff-exit/test-allow-type-array-v2.o",
+    "",
+    "",
+    "",
+    "--no-default-suppression",
+    abigail::tools_utils::ABIDIFF_ABI_CHANGE,
+    "data/test-abidiff-exit/test-allow-type-array-v0--v2-report-1.txt",
+    "output/test-abidiff-exit/test-allow-type-array-v0--v2-report-1.txt"
+  },
+  {
+    "data/test-abidiff-exit/test-allow-type-array-v0.o",
+    "data/test-abidiff-exit/test-allow-type-array-v2.o",
+    "data/test-abidiff-exit/test-allow-type-array-suppr.txt",
+    "",
+    "",
+    "--no-default-suppression",
+    abigail::tools_utils::ABIDIFF_ABI_CHANGE,
+    "data/test-abidiff-exit/test-allow-type-array-v0--v2-report-2.txt",
+    "output/test-abidiff-exit/test-allow-type-array-v0--v2-report-2.txt"
+  },
+  {
+    "data/test-abidiff-exit/test-allow-type-array-v0.o",
+    "data/test-abidiff-exit/test-allow-type-array-v3.o",
+    "",
+    "",
+    "",
+    "--no-default-suppression",
+    abigail::tools_utils::ABIDIFF_ABI_CHANGE,
+    "data/test-abidiff-exit/test-allow-type-array-v0--v3-report-1.txt",
+    "output/test-abidiff-exit/test-allow-type-array-v0--v3-report-1.txt"
+  },
+  {
+    "data/test-abidiff-exit/test-allow-type-array-v0.o",
+    "data/test-abidiff-exit/test-allow-type-array-v3.o",
+    "data/test-abidiff-exit/test-allow-type-array-suppr.txt",
+    "",
+    "",
+    "--no-default-suppression",
+    abigail::tools_utils::ABIDIFF_OK,
+    "data/test-abidiff-exit/test-allow-type-array-v0--v3-report-2.txt",
+    "output/test-abidiff-exit/test-allow-type-array-v0--v3-report-2.txt"
+  },
+  {
+    "data/test-abidiff-exit/test-allow-type-region-v0.o",
+    "data/test-abidiff-exit/test-allow-type-region-v1.o",
+    "",
+    "",
+    "",
+    "--no-default-suppression",
+    abigail::tools_utils::ABIDIFF_ABI_CHANGE,
+    "data/test-abidiff-exit/test-allow-type-region-v0--v1-report-1.txt",
+    "output/test-abidiff-exit/test-allow-type-region-v0--v1-report-1.txt"
+  },
+  {
+    "data/test-abidiff-exit/test-allow-type-region-v0.o",
+    "data/test-abidiff-exit/test-allow-type-region-v1.o",
+    "data/test-abidiff-exit/test-allow-type-region-suppr.txt",
+    "",
+    "",
+    "--no-default-suppression",
+    abigail::tools_utils::ABIDIFF_OK,
+    "data/test-abidiff-exit/test-allow-type-region-v0--v1-report-2.txt",
+    "output/test-abidiff-exit/test-allow-type-region-v0--v1-report-2.txt"
+  },
+  {
+    "data/test-abidiff-exit/test-allow-type-region-v0.o",
+    "data/test-abidiff-exit/test-allow-type-region-v2.o",
+    "",
+    "",
+    "",
+    "--no-default-suppression",
+    abigail::tools_utils::ABIDIFF_ABI_CHANGE,
+    "data/test-abidiff-exit/test-allow-type-region-v0--v2-report-1.txt",
+    "output/test-abidiff-exit/test-allow-type-region-v0--v2-report-1.txt"
+  },
+  {
+    "data/test-abidiff-exit/test-allow-type-region-v0.o",
+    "data/test-abidiff-exit/test-allow-type-region-v2.o",
+    "",
+    "",
+    "",
+    "--no-default-suppression",
+    abigail::tools_utils::ABIDIFF_ABI_CHANGE,
+    "data/test-abidiff-exit/test-allow-type-region-v0--v2-report-1.txt",
+    "output/test-abidiff-exit/test-allow-type-region-v0--v2-report-1.txt"
+  },
+  {
+    "data/test-abidiff-exit/test-allow-type-region-v0.o",
+    "data/test-abidiff-exit/test-allow-type-region-v2.o",
+    "data/test-abidiff-exit/test-allow-type-region-suppr.txt",
+    "",
+    "",
+    "--no-default-suppression",
+    abigail::tools_utils::ABIDIFF_ABI_CHANGE,
+    "data/test-abidiff-exit/test-allow-type-region-v0--v2-report-2.txt",
+    "output/test-abidiff-exit/test-allow-type-region-v0--v2-report-2.txt"
+  },
+  {
+    "data/test-abidiff-exit/test-allow-type-region-v0.o",
+    "data/test-abidiff-exit/test-allow-type-region-v3.o",
+    "",
+    "",
+    "",
+    "--no-default-suppression",
+    abigail::tools_utils::ABIDIFF_ABI_CHANGE,
+    "data/test-abidiff-exit/test-allow-type-region-v0--v3-report-1.txt",
+    "output/test-abidiff-exit/test-allow-type-region-v0--v3-report-1.txt"
+  },
+  {
+    "data/test-abidiff-exit/test-allow-type-region-v0.o",
+    "data/test-abidiff-exit/test-allow-type-region-v3.o",
+    "data/test-abidiff-exit/test-allow-type-region-suppr.txt",
+    "",
+    "",
+    "--no-default-suppression",
+    abigail::tools_utils::ABIDIFF_ABI_CHANGE,
+    "data/test-abidiff-exit/test-allow-type-region-v0--v3-report-2.txt",
+    "output/test-abidiff-exit/test-allow-type-region-v0--v3-report-2.txt"
+  },
+  {
+    "data/test-abidiff-exit/test-allow-type-region-v0.o",
+    "data/test-abidiff-exit/test-allow-type-region-v4.o",
+    "",
+    "",
+    "",
+    "--no-default-suppression",
+    abigail::tools_utils::ABIDIFF_ABI_CHANGE,
+    "data/test-abidiff-exit/test-allow-type-region-v0--v4-report-1.txt",
+    "output/test-abidiff-exit/test-allow-type-region-v0--v4-report-1.txt"
+  },
+  {
+    "data/test-abidiff-exit/test-allow-type-region-v0.o",
+    "data/test-abidiff-exit/test-allow-type-region-v4.o",
+    "data/test-abidiff-exit/test-allow-type-region-suppr.txt",
+    "",
+    "",
+    "--no-default-suppression",
+    abigail::tools_utils::ABIDIFF_OK,
+    "data/test-abidiff-exit/test-allow-type-region-v0--v4-report-2.txt",
+    "output/test-abidiff-exit/test-allow-type-region-v0--v4-report-2.txt"
+  },
+  {
+    "data/test-abidiff-exit/test-allow-type-region-v0.o",
+    "data/test-abidiff-exit/test-allow-type-region-v5.o",
+    "",
+    "",
+    "",
+    "--no-default-suppression",
+    abigail::tools_utils::ABIDIFF_ABI_CHANGE,
+    "data/test-abidiff-exit/test-allow-type-region-v0--v5-report-1.txt",
+    "output/test-abidiff-exit/test-allow-type-region-v0--v5-report-1.txt"
+  },
+  {
+    "data/test-abidiff-exit/test-allow-type-region-v0.o",
+    "data/test-abidiff-exit/test-allow-type-region-v5.o",
+    "data/test-abidiff-exit/test-allow-type-region-suppr.txt",
+    "",
+    "",
+    "--no-default-suppression",
+    abigail::tools_utils::ABIDIFF_ABI_CHANGE,
+    "data/test-abidiff-exit/test-allow-type-region-v0--v5-report-2.txt",
+    "output/test-abidiff-exit/test-allow-type-region-v0--v5-report-2.txt"
+  },
 #ifdef WITH_BTF
   {
     "data/test-abidiff-exit/btf/test0-v0.o",
-- 
2.39.2



-- 
		Dodji


^ permalink raw reply	[flat|nested] 14+ messages in thread

* [PATCH 07/13] Misc white space fixes
  2023-03-02 18:53 ` [PATCH 00/13] Support negative suppression specifications Dodji Seketeli
                     ` (5 preceding siblings ...)
  2023-03-02 18:59   ` [PATCH 06/13] comparison, suppression: Support [allow_type] directive Dodji Seketeli
@ 2023-03-02 19:00   ` Dodji Seketeli
  2023-03-02 19:01   ` [PATCH 08/13] abidiff: Add extensive logging Dodji Seketeli
                     ` (5 subsequent siblings)
  12 siblings, 0 replies; 14+ messages in thread
From: Dodji Seketeli @ 2023-03-02 19:00 UTC (permalink / raw)
  To: Dodji Seketeli; +Cc: libabigail, ckalina

Hello,

	* include/abg-suppression.h (class
	type_suppression::insertion_range::end): Fix indentation.
	* src/abg-default-reporter.cc (default_reporter::report): Fix
	indentation in the overload for corpus_diff.
	* src/abg-suppression-priv.h
	(type_suppression::priv::source_locations_to_keep_): Fix alignment.
	* src/abg-suppression.cc (read_type_suppression): Fix alignment of
	comment.

Signed-off-by: Dodji Seketeli <dodji@redhat.com>
---
 include/abg-suppression.h   | 2 +-
 src/abg-default-reporter.cc | 4 ++--
 src/abg-suppression-priv.h  | 2 +-
 src/abg-suppression.cc      | 6 +++---
 4 files changed, 7 insertions(+), 7 deletions(-)

diff --git a/include/abg-suppression.h b/include/abg-suppression.h
index 05b0e8b4..9449d06d 100644
--- a/include/abg-suppression.h
+++ b/include/abg-suppression.h
@@ -379,7 +379,7 @@ public:
   begin() const;
 
  boundary_sptr
-  end() const;
+ end() const;
 
   static insertion_range::integer_boundary_sptr
   create_integer_boundary(int value);
diff --git a/src/abg-default-reporter.cc b/src/abg-default-reporter.cc
index 243ddbfb..38b89e62 100644
--- a/src/abg-default-reporter.cc
+++ b/src/abg-default-reporter.cc
@@ -1873,7 +1873,7 @@ default_reporter::report(const corpus_diff& d, ostream& out,
 		     // was playing tricks with symbol names and
 		     // versions).
 		  (is_c_language(get_translation_unit(fn)->get_language())
-		      && fn->get_name() != fn->get_symbol()->get_name()))
+		   && fn->get_name() != fn->get_symbol()->get_name()))
 		{
 		  // As the name of the symbol of the function doesn't
 		  // seem to be obvious here, make sure to tell the
@@ -1949,7 +1949,7 @@ default_reporter::report(const corpus_diff& d, ostream& out,
 	  emitted = true;
 	}
       if (emitted)
-	  out << "\n";
+	out << "\n";
     }
 
   if (ctxt->show_added_vars())
diff --git a/src/abg-suppression-priv.h b/src/abg-suppression-priv.h
index dcc5cc53..cf66a9f1 100644
--- a/src/abg-suppression-priv.h
+++ b/src/abg-suppression-priv.h
@@ -581,7 +581,7 @@ class type_suppression::priv
   // members of the class.
   mutable regex::regex_t_sptr		potential_data_members_regex_;
   type_suppression::insertion_ranges	insertion_ranges_;
-  unordered_set<string>			source_locations_to_keep_;
+  unordered_set<string>		source_locations_to_keep_;
   string				source_location_to_keep_regex_str_;
   mutable regex::regex_t_sptr		source_location_to_keep_regex_;
   mutable vector<string>		changed_enumerator_names_;
diff --git a/src/abg-suppression.cc b/src/abg-suppression.cc
index aaf175ca..4ed42e82 100644
--- a/src/abg-suppression.cc
+++ b/src/abg-suppression.cc
@@ -2007,9 +2007,9 @@ read_type_suppression(const ini::config::section& section)
       // and not (for instance):
       //  has_data_member_inserted_between = {{0 , end}, {1, foo}}
       //
-      //  This means that the tuple_property_value contains just one
-      //  value, which is a list_property that itself contains 2
-      //  values.
+      // This means that the tuple_property_value contains just one
+      // value, which is a list_property that itself contains 2
+      // values.
       type_suppression::insertion_range::boundary_sptr begin, end;
       ini::tuple_property_value_sptr v = prop->get_value();
       if (v
-- 
2.39.2



-- 
		Dodji


^ permalink raw reply	[flat|nested] 14+ messages in thread

* [PATCH 08/13] abidiff: Add extensive logging
  2023-03-02 18:53 ` [PATCH 00/13] Support negative suppression specifications Dodji Seketeli
                     ` (6 preceding siblings ...)
  2023-03-02 19:00   ` [PATCH 07/13] Misc white space fixes Dodji Seketeli
@ 2023-03-02 19:01   ` Dodji Seketeli
  2023-03-02 19:01   ` [PATCH 09/13] tools-utils: Support kernel stablelist Dodji Seketeli
                     ` (4 subsequent siblings)
  12 siblings, 0 replies; 14+ messages in thread
From: Dodji Seketeli @ 2023-03-02 19:01 UTC (permalink / raw)
  To: Dodji Seketeli; +Cc: libabigail, ckalina

Hello,

While looking at something else I felt the need for having
"abidiff --verbose" emit more timing information.  I have thus added
a lot more logging around.

	* include/abg-comparison.h ({diff, corpus_diff,
	diff_context}::do_log): Declare member functions.
	* include/abg-corpus.h (corpus::do_log): Likewise.
	* src/abg-comparison-priv.h (diff_context::priv::do_log_): Add new
	data member.
	(diff_context::priv::priv): Initialize the new data member.
	* src/abg-comparison.cc ({diff, corpus_diff,
	diff_context}::do_log): Define member functions.
	(diff_context::maybe_apply_filters): Add timing logs to applying
	filters and propagating categories.
	(corpus_diff::priv::apply_filters_and_compute_diff_stats): Add
	timing logs to applying and propagating filters to changed
	functions, variables, unreachable & leaf type changes,
	suppressions application.
	* src/abg-corpus-priv.h (corpus::priv::do_log): Add new data
	member.
	(corpus::priv::priv): Initialize it.
	* src/abg-corpus.cc (corpus::do_log): Define member functions.
	* src/abg-reader.cc (reader::do_log): Likewise.
	(reader::read_corpus): Add timing log around the invocation of
	perform_late_type_canonicalizing.
	* tools/abidiff.cc (set_diff_context_from_opts): Set logging.
	(main): Add timing logging for diff computing, changes analysis &
	report generation.

Signed-off-by: Dodji Seketeli <dodji@redhat.com>
---
 include/abg-comparison.h  |  18 ++++
 include/abg-corpus.h      |   6 ++
 src/abg-comparison-priv.h |   4 +-
 src/abg-comparison.cc     | 211 ++++++++++++++++++++++++++++++++++++++
 src/abg-corpus-priv.h     |   4 +-
 src/abg-corpus.cc         |  14 +++
 src/abg-reader.cc         |  22 ++++
 tools/abidiff.cc          | 181 ++++++++++++++++++++++++++++++--
 8 files changed, 452 insertions(+), 8 deletions(-)

diff --git a/include/abg-comparison.h b/include/abg-comparison.h
index 839478bf..506f2bb7 100644
--- a/include/abg-comparison.h
+++ b/include/abg-comparison.h
@@ -649,6 +649,12 @@ public:
 
   ~diff_context();
 
+  bool
+  do_log() const;
+
+  void
+  do_log(bool);
+
   void
   set_corpus_diff(const corpus_diff_sptr&);
 
@@ -966,6 +972,12 @@ protected:
        type_or_decl_base_sptr	second_subject,
        diff_context_sptr	ctxt);
 
+  bool
+  do_log() const;
+
+  void
+  do_log(bool);
+
   void
   begin_traversing();
 
@@ -2337,6 +2349,12 @@ public:
   /// A convenience typedef for a shared pointer to @ref diff_stats
   typedef shared_ptr<diff_stats> diff_stats_sptr;
 
+  bool
+  do_log() const;
+
+  void
+  do_log(bool);
+
   corpus_sptr
   first_corpus() const;
 
diff --git a/include/abg-corpus.h b/include/abg-corpus.h
index fabda0f9..090bad14 100644
--- a/include/abg-corpus.h
+++ b/include/abg-corpus.h
@@ -69,6 +69,12 @@ public:
   const environment&
   get_environment() const;
 
+  bool
+  do_log() const;
+
+  void
+  do_log(bool);
+
   void
   add(const translation_unit_sptr&);
 
diff --git a/src/abg-comparison-priv.h b/src/abg-comparison-priv.h
index fb843665..48a01188 100644
--- a/src/abg-comparison-priv.h
+++ b/src/abg-comparison-priv.h
@@ -212,6 +212,7 @@ struct diff_context::priv
   bool					show_unreachable_types_;
   bool					show_impacted_interfaces_;
   bool					dump_diff_tree_;
+  bool					do_log_;
 
   priv()
     : allowed_category_(EVERYTHING_CATEGORY),
@@ -240,7 +241,8 @@ struct diff_context::priv
       show_added_syms_unreferenced_by_di_(true),
       show_unreachable_types_(false),
       show_impacted_interfaces_(true),
-      dump_diff_tree_()
+      dump_diff_tree_(),
+      do_log_()
    {}
 };// end struct diff_context::priv
 
diff --git a/src/abg-comparison.cc b/src/abg-comparison.cc
index 609c5bf2..dc451868 100644
--- a/src/abg-comparison.cc
+++ b/src/abg-comparison.cc
@@ -17,6 +17,7 @@
 
 #include "abg-comparison-priv.h"
 #include "abg-reporter-priv.h"
+#include "abg-tools-utils.h"
 
 namespace abigail
 {
@@ -916,6 +917,20 @@ diff_context::diff_context()
 
 diff_context::~diff_context() = default;
 
+/// Test if logging was requested.
+///
+/// @return true iff logging was requested.
+bool
+diff_context::do_log() const
+{return priv_->do_log_;}
+
+/// Set logging as requested.
+///
+/// @param f the flag
+void
+diff_context::do_log(bool f)
+{priv_->do_log_ = f;}
+
 /// Set the corpus diff relevant to this context.
 ///
 /// @param d the corpus_diff we are interested in.
@@ -1365,8 +1380,33 @@ diff_context::maybe_apply_filters(diff_sptr diff)
        i != diff_filters().end();
        ++i)
     {
+      tools_utils::timer t;
+      if (do_log())
+	{
+	  std::cerr << "applying a filter to diff '"
+		    << diff->get_pretty_representation()
+		    << "'...\n";
+	  t.start();
+	}
+
       filtering::apply_filter(*i, diff);
+
+      if (do_log())
+	{
+	  t.stop();
+	  std::cerr << "filter applied!:" << t << "\n";
+
+	  std::cerr << "propagating categories for the same diff node ... \n";
+	  t.start();
+	}
+
       propagate_categories(diff);
+
+      if (do_log())
+	{
+	  t.stop();
+	  std::cerr << "category propagated!: " << t << "\n";
+	}
     }
 
  }
@@ -1926,6 +1966,20 @@ diff::diff(type_or_decl_base_sptr	first_subject,
 		   /*currently_reporting=*/false))
 {}
 
+/// Test if logging was requested
+///
+/// @return true iff logging was requested.
+bool
+diff::do_log() const
+{return context()->do_log();}
+
+/// Request logging (or not)
+///
+/// @param f true iff logging is to be requested.
+void
+diff::do_log(bool f)
+{context()->do_log(f);}
+
 /// Flag a given diff node as being traversed.
 ///
 /// For certain diff nodes like @ref class_diff, it's important to
@@ -9934,6 +9988,15 @@ corpus_diff::priv::apply_filters_and_compute_diff_stats(diff_stats& stat)
 
   diff_context_sptr ctxt = get_context();
 
+  tools_utils::timer t;
+  if (get_context()->do_log())
+    {
+      std::cerr << "in apply_filters_and_compute_diff_stats:"
+		<< "applying filters to "
+		<< changed_fns_.size()
+		<< " changed fns ...\n";
+      t.start();
+    }
   // Walk the changed function diff nodes to apply the categorization
   // filters.
   diff_sptr diff;
@@ -9946,6 +10009,19 @@ corpus_diff::priv::apply_filters_and_compute_diff_stats(diff_stats& stat)
       ctxt->maybe_apply_filters(diff);
     }
 
+  if (get_context()->do_log())
+    {
+      t.stop();
+      std::cerr << "in apply_filters_and_compute_diff_stats:"
+		<< "filters to changed fn applied!:" << t << "\n";
+
+      std::cerr << "in apply_filters_and_compute_diff_stats:"
+		<< "applying filters to "
+		<< sorted_changed_vars_.size()
+		<< " changed vars ...\n";
+      t.start();
+    }
+
   // Walk the changed variable diff nodes to apply the categorization
   // filters.
   for (var_diff_sptrs_type::const_iterator i = sorted_changed_vars_.begin();
@@ -9956,6 +10032,17 @@ corpus_diff::priv::apply_filters_and_compute_diff_stats(diff_stats& stat)
       ctxt->maybe_apply_filters(diff);
     }
 
+  if (get_context()->do_log())
+    {
+      t.stop();
+      std::cerr << "in apply_filters_and_compute_diff_stats:"
+		<< "filters to changed vars applied!:" << t << "\n";
+
+      std::cerr << "in apply_filters_and_compute_diff_stats:"
+		<< "applying filters to unreachable types ...\n";
+      t.start();
+    }
+
   // walk the changed unreachable types to apply categorization
   // filters
   for (diff_sptrs_type::const_iterator i =
@@ -9967,8 +10054,30 @@ corpus_diff::priv::apply_filters_and_compute_diff_stats(diff_stats& stat)
       ctxt->maybe_apply_filters(diff);
     }
 
+  if (get_context()->do_log())
+    {
+      t.stop();
+      std::cerr << "in apply_filters_and_compute_diff_stats:"
+		<< "filters to unreachable types applied!:" << t << "\n";
+
+      std::cerr << "in apply_filters_and_compute_diff_stats:"
+		<< "categorizing redundant changed sub nodes ...\n";
+      t.start();
+    }
+
   categorize_redundant_changed_sub_nodes();
 
+  if (get_context()->do_log())
+    {
+      t.stop();
+      std::cerr << "in apply_filters_and_compute_diff_stats:"
+		<< "redundant changed sub nodes categorized!:" << t << "\n";
+
+      std::cerr << "in apply_filters_and_compute_diff_stats:"
+		<< "count changed fns ...\n";
+      t.start();
+    }
+
   // Walk the changed function diff nodes to count the number of
   // filtered-out functions and the number of functions with virtual
   // offset changes.
@@ -9998,6 +10107,17 @@ corpus_diff::priv::apply_filters_and_compute_diff_stats(diff_stats& stat)
 	  (stat.num_leaf_func_changes() + 1);
     }
 
+  if (get_context()->do_log())
+    {
+      t.stop();
+      std::cerr << "in apply_filters_and_compute_diff_stats:"
+		<< "changed fn counted!:" << t << "\n";
+
+      std::cerr << "in apply_filters_and_compute_diff_stats:"
+		<< "count changed vars ...\n";
+      t.start();
+    }
+
   // Walk the changed variables diff nodes to count the number of
   // filtered-out variables.
   for (var_diff_sptrs_type ::const_iterator i = sorted_changed_vars_.begin();
@@ -10018,6 +10138,17 @@ corpus_diff::priv::apply_filters_and_compute_diff_stats(diff_stats& stat)
 	  (stat.num_leaf_var_changes() + 1);
     }
 
+  if (get_context()->do_log())
+    {
+      t.stop();
+      std::cerr << "in apply_filters_and_compute_diff_stats:"
+		<< "changed vars counted!:" << t << "\n";
+
+      std::cerr << "in apply_filters_and_compute_diff_stats:"
+		<< "count leaf changed types ...\n";
+      t.start();
+    }
+
   stat.num_func_syms_added(added_unrefed_fn_syms_.size());
   stat.num_added_func_syms_filtered_out(suppressed_added_unrefed_fn_syms_.size());
   stat.num_func_syms_removed(deleted_unrefed_fn_syms_.size());
@@ -10036,6 +10167,17 @@ corpus_diff::priv::apply_filters_and_compute_diff_stats(diff_stats& stat)
     stat.num_leaf_type_changes_filtered_out(num_type_filtered);
   }
 
+  if (get_context()->do_log())
+    {
+      t.stop();
+      std::cerr << "in apply_filters_and_compute_diff_stats:"
+		<< "changed leaf types counted!:" << t << "\n";
+
+      std::cerr << "in apply_filters_and_compute_diff_stats:"
+		<< "count leaf changed artefacts ...\n";
+      t.start();
+    }
+
   // Walk the general leaf artefacts diff nodes to count them
   {
     size_t num_changes = 0, num_filtered = 0;
@@ -10045,6 +10187,17 @@ corpus_diff::priv::apply_filters_and_compute_diff_stats(diff_stats& stat)
     stat.num_leaf_changes_filtered_out(num_filtered);
   }
 
+  if (get_context()->do_log())
+    {
+      t.stop();
+      std::cerr << "in apply_filters_and_compute_diff_stats:"
+		<< "changed leaf artefacts counted!:" << t << "\n";
+
+      std::cerr << "in apply_filters_and_compute_diff_stats:"
+		<< "count unreachable types ...\n";
+      t.start();
+    }
+
   // Walk the unreachable types to count them
   {
     size_t num_added_unreachable_types = 0,
@@ -10061,6 +10214,13 @@ corpus_diff::priv::apply_filters_and_compute_diff_stats(diff_stats& stat)
 			    num_deleted_unreachable_types_filtered,
 			    num_changed_unreachable_types_filtered);
 
+    if (get_context()->do_log())
+      {
+	t.stop();
+	std::cerr << "in apply_filters_and_compute_diff_stats:"
+		  << "unreachable types counted!:" << t << "\n";
+      }
+
     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);
@@ -10495,6 +10655,20 @@ corpus_diff::finish_diff_type()
   priv_->finished_ = true;
 }
 
+/// Test if logging was requested.
+///
+/// @return true iff logging was requested.
+bool
+corpus_diff::do_log() const
+{return context()->do_log();}
+
+/// Request logging, or not.
+///
+/// @param f true iff logging is requested.
+void
+corpus_diff::do_log(bool f)
+{context()->do_log(f);}
+
 /// @return the first corpus of the diff.
 corpus_sptr
 corpus_diff::first_corpus() const
@@ -10895,10 +11069,47 @@ corpus_diff::apply_filters_and_suppressions_before_reporting()
   if (priv_->diff_stats_)
     return *priv_->diff_stats_;
 
+  tools_utils::timer t;
+  if (do_log())
+    {
+      std::cerr << "Applying suppressions ...\n";
+      t.start();
+    }
+
   apply_suppressions(this);
+
+  if (do_log())
+    {
+      t.stop();
+      std::cerr << "suppressions applied!:" << t << "\n";
+    }
+
   priv_->diff_stats_.reset(new diff_stats(context()));
+
+  if (do_log())
+    {
+      std::cerr << "Marking leaf nodes ...\n";
+      t.start();
+    }
+
   mark_leaf_diff_nodes();
+
+  if (do_log())
+    {
+      t.stop();
+      std::cerr << "leaf nodes marked!:" << t << "\n";
+      std::cerr << "Applying filters and computing diff stats ...\n";
+      t.start();
+    }
+
   priv_->apply_filters_and_compute_diff_stats(*priv_->diff_stats_);
+
+  if (do_log())
+    {
+      t.stop();
+      std::cerr << "Filters applied and diff stats computed!: " << t << "\n";
+    }
+
   return *priv_->diff_stats_;
 }
 
diff --git a/src/abg-corpus-priv.h b/src/abg-corpus-priv.h
index 300ec687..422b7870 100644
--- a/src/abg-corpus-priv.h
+++ b/src/abg-corpus-priv.h
@@ -701,6 +701,7 @@ struct corpus::priv
   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_;
+  bool 						do_log;
 
 private:
   priv();
@@ -723,7 +724,8 @@ public:
       group(),
       origin_(ARTIFICIAL_ORIGIN),
       path(p),
-      pub_type_pretty_reprs_()
+      pub_type_pretty_reprs_(),
+      do_log()
   {}
 
   type_maps&
diff --git a/src/abg-corpus.cc b/src/abg-corpus.cc
index 4f20a888..cf6685d6 100644
--- a/src/abg-corpus.cc
+++ b/src/abg-corpus.cc
@@ -633,6 +633,20 @@ const environment&
 corpus::get_environment() const
 {return priv_->env;}
 
+/// Test if logging was requested.
+///
+/// @return true iff logging was requested.
+bool
+corpus::do_log() const
+{return priv_->do_log;}
+
+/// Request logging, or not.
+///
+/// @param f true iff logging is requested.
+void
+corpus::do_log(bool f)
+{priv_->do_log = f;}
+
 /// Add a translation unit to the current ABI Corpus.
 ///
 /// Note that two translation units with the same path (as returned by
diff --git a/src/abg-reader.cc b/src/abg-reader.cc
index 5a4fb3c0..b93cce3a 100644
--- a/src/abg-reader.cc
+++ b/src/abg-reader.cc
@@ -165,6 +165,13 @@ public:
   {
   }
 
+  /// Test if logging was requested.
+  ///
+  /// @return true iff logging was requested.
+  bool
+  do_log() const
+  {return options().do_log;}
+
   /// Getter for the flag that tells us if we are tracking types that
   /// are not reachable from global functions and variables.
   ///
@@ -1233,8 +1240,23 @@ public:
       }
 
 
+    tools_utils::timer t;
+    if (do_log())
+      {
+	std::cerr << "perform late type canonicalization ...\n";
+	t.start();
+      }
+
     perform_late_type_canonicalizing();
 
+    if (do_log())
+      {
+	t.stop();
+	std::cerr << "late type canonicalization DONE@"
+		  << corpus()->get_path()
+		  << ":" << t << "\n";
+      }
+
     get_environment().canonicalization_is_done(true);
 
     if (call_reader_next)
diff --git a/tools/abidiff.cc b/tools/abidiff.cc
index bf0bf9fc..a0a670cb 100644
--- a/tools/abidiff.cc
+++ b/tools/abidiff.cc
@@ -830,6 +830,8 @@ set_diff_context_from_opts(diff_context_sptr ctxt,
     }
 
   ctxt->dump_diff_tree(opts.dump_diff_tree);
+
+  ctxt->do_log(opts.do_log);
 }
 
 /// Set a bunch of tunable buttons on the ELF-based reader from the
@@ -934,7 +936,9 @@ set_native_xml_reader_options(abigail::fe_iface& rdr,
 			      const options& opts)
 {
   abixml::consider_types_not_reachable_from_public_interfaces(rdr,
-									      opts.show_all_types);
+							      opts.show_all_types);
+  rdr.options().do_log = opts.do_log;
+
 }
 
 /// Set the regex patterns describing the functions to drop from the
@@ -1409,9 +1413,38 @@ main(int argc, char* argv[])
 
       if (t1)
 	{
+	  tools_utils::timer t;
+	  if (opts.do_log)
+	    {
+	      t.start();
+	      std::cerr << "Compute diff ...\n";
+	    }
+
 	  translation_unit_diff_sptr diff = compute_diff(t1, t2, ctxt);
+
+	  if (opts.do_log)
+	    {
+	      t.stop();
+	      std::cerr << "diff computed!:" << t << "\n";
+	    }
+
 	  if (diff->has_changes())
-	    diff->report(cout);
+	    {
+	      tools_utils::timer t;
+	      if (opts.do_log)
+		{
+		  t.start();
+		  std::cerr << "Computing the report ...\n";
+		}
+
+	      diff->report(cout);
+
+	      if (opts.do_log)
+		{
+		  t.stop();
+		  std::cerr << "Report computed!:" << t << "\n";
+		}
+	    }
 	}
       else if (c1)
 	{
@@ -1436,16 +1469,81 @@ main(int argc, char* argv[])
 	  set_corpus_keep_drop_regex_patterns(opts, c1);
 	  set_corpus_keep_drop_regex_patterns(opts, c2);
 
+	  tools_utils::timer t;
+	  if (opts.do_log)
+	    {
+	      t.start();
+	      std::cerr << "Compute diff ...\n";
+	    }
+
 	  corpus_diff_sptr diff = compute_diff(c1, c2, ctxt);
 
+	  if (opts.do_log)
+	    {
+	      t.stop();
+	      std::cerr << "diff computed!:" << t << "\n";
+	    }
+
+	  if (opts.do_log)
+	    {
+	      t.start();
+	      std::cerr << "Computing net changes ...\n";
+	    }
+
 	  if (diff->has_net_changes())
-	    status = abigail::tools_utils::ABIDIFF_ABI_CHANGE;
+	    {
+	      if (opts.do_log)
+		{
+		  t.stop();
+		  std::cerr << "net changes computed!: "<< t << "\n";
+		}
+	      status = abigail::tools_utils::ABIDIFF_ABI_CHANGE;
+	    }
+
+	  if (opts.do_log)
+	    {
+	      t.start();
+	      std::cerr << "Computing incompatible changes ...\n";
+	    }
 
 	  if (diff->has_incompatible_changes())
-	    status |= abigail::tools_utils::ABIDIFF_ABI_INCOMPATIBLE_CHANGE;
+	    {
+	      if (opts.do_log)
+		{
+		  t.stop();
+		  std::cerr << "incompatible changes computed!: "<< t << "\n";
+		}
+	      status |= abigail::tools_utils::ABIDIFF_ABI_INCOMPATIBLE_CHANGE;
+	    }
+
+	  if (opts.do_log)
+	    {
+	      t.start();
+	      std::cerr << "Computing changes ...\n";
+	    }
 
 	  if (diff->has_changes())
-	    diff->report(cout);
+	    {
+	      if (opts.do_log)
+		{
+		  t.stop();
+		  std::cerr << "changes computed!: "<< t << "\n";
+		}
+
+	      if (opts.do_log)
+		{
+		  t.start();
+		  std::cerr << "Computing report ...\n";
+		}
+
+	      diff->report(cout);
+
+	      if (opts.do_log)
+		{
+		  t.stop();
+		  std::cerr << "Report computed!:" << t << "\n";
+		}
+	    }
 	}
       else if (g1)
 	{
@@ -1468,16 +1566,87 @@ main(int argc, char* argv[])
 	    }
 
 	  adjust_diff_context_for_kmidiff(*ctxt);
+	  tools_utils::timer t;
+	  if (opts.do_log)
+	    {
+	      t.start();
+	      std::cerr << "Compute diff ...\n";
+	    }
+
 	  corpus_diff_sptr diff = compute_diff(g1, g2, ctxt);
 
+	  if (opts.do_log)
+	    {
+	      t.stop();
+	      diff->do_log(true);
+	      std::cerr << "diff computed!:" << t << "\n";
+	    }
+
+	  if (opts.do_log)
+	    {
+	      std::cerr << "Computing net changes ...\n";
+	      t.start();
+	    }
+
 	  if (diff->has_net_changes())
 	    status = abigail::tools_utils::ABIDIFF_ABI_CHANGE;
+	  if (opts.do_log)
+	    {
+	      t.stop();
+	      std::cerr << "net changes computed!: "<< t << "\n";
+	    }
+
+	  if (opts.do_log)
+	    {
+	      t.start();
+	      std::cerr << "Computing incompatible changes ...\n";
+	    }
 
 	  if (diff->has_incompatible_changes())
 	    status |= abigail::tools_utils::ABIDIFF_ABI_INCOMPATIBLE_CHANGE;
 
+	  if (opts.do_log)
+	    {
+	      t.stop();
+	      std::cerr << "incompatible changes computed!: "<< t << "\n";
+	    }
+
+	  if (opts.do_log)
+	    {
+	      t.start();
+	      std::cerr << "Computing changes ...\n";
+	    }
+
 	  if (diff->has_changes())
-	    diff->report(cout);
+	    {
+	      if (opts.do_log)
+		{
+		  t.stop();
+		  std::cerr << "changes computed!: "<< t << "\n";
+		}
+
+	      if (opts.do_log)
+		{
+		  t.start();
+		  std::cerr << "Computing report ...\n";
+		}
+
+	      diff->report(cout);
+
+	      if (opts.do_log)
+		{
+		  t.stop();
+		  std::cerr << "Report computed!:" << t << "\n";
+		}
+	    }
+	  else
+	    {
+	      if (opts.do_log)
+		{
+		  t.stop();
+		  std::cerr << "changes computed!: "<< t << "\n";
+		}
+	    }
 
 	}
       else
-- 
2.39.2



-- 
		Dodji


^ permalink raw reply	[flat|nested] 14+ messages in thread

* [PATCH 09/13] tools-utils: Support kernel stablelist
  2023-03-02 18:53 ` [PATCH 00/13] Support negative suppression specifications Dodji Seketeli
                     ` (7 preceding siblings ...)
  2023-03-02 19:01   ` [PATCH 08/13] abidiff: Add extensive logging Dodji Seketeli
@ 2023-03-02 19:01   ` Dodji Seketeli
  2023-03-02 19:02   ` [PATCH 10/13] comp-filter: Don't re-visit node while applying filters to diff nodes Dodji Seketeli
                     ` (3 subsequent siblings)
  12 siblings, 0 replies; 14+ messages in thread
From: Dodji Seketeli @ 2023-03-02 19:01 UTC (permalink / raw)
  To: Dodji Seketeli; +Cc: libabigail, ckalina

Hello,

Up until now, a kernel whitelist was expected to be a ini file with a
section having a name ending with the word "whitelist".  Nowadays,
they are called "stablelist", so the name of the section ends up with
"stablelist".  This patch makes
gen_suppr_spec_from_kernel_abi_whitelists support that.

	* src/abg-tools-utils.cc
	(gen_suppr_spec_from_kernel_abi_whitelists): Support section name
	that ends with the word 'stablelist'.

Signed-off-by: Dodji Seketeli <dodji@redhat.com>
---
 src/abg-tools-utils.cc | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/src/abg-tools-utils.cc b/src/abg-tools-utils.cc
index 85719cdc..94dd8d05 100644
--- a/src/abg-tools-utils.cc
+++ b/src/abg-tools-utils.cc
@@ -2151,7 +2151,8 @@ gen_suppr_spec_from_kernel_abi_whitelists
 	   ++section_iter)
 	{
 	  std::string section_name = (*section_iter)->get_name();
-	  if (!string_ends_with(section_name, "whitelist"))
+	  if (!string_ends_with(section_name, "whitelist")
+	      && !string_ends_with(section_name, "stablelist"))
 	    continue;
 	  for (ini::config::properties_type::const_iterator
 		   prop_iter = (*section_iter)->get_properties().begin(),
-- 
2.39.2



-- 
		Dodji


^ permalink raw reply	[flat|nested] 14+ messages in thread

* [PATCH 10/13] comp-filter: Don't re-visit node while applying filters to diff nodes
  2023-03-02 18:53 ` [PATCH 00/13] Support negative suppression specifications Dodji Seketeli
                     ` (8 preceding siblings ...)
  2023-03-02 19:01   ` [PATCH 09/13] tools-utils: Support kernel stablelist Dodji Seketeli
@ 2023-03-02 19:02   ` Dodji Seketeli
  2023-03-02 19:03   ` [PATCH 11/13] comparison: Add a mode to not apply filters on interface sub-graphs Dodji Seketeli
                     ` (2 subsequent siblings)
  12 siblings, 0 replies; 14+ messages in thread
From: Dodji Seketeli @ 2023-03-02 19:02 UTC (permalink / raw)
  To: Dodji Seketeli; +Cc: libabigail, ckalina

Hello,

When applying a filter to a corpus_diff node, visit each diff node
only once.  This can have some serious performance impact when there
are a lot of diff nodes to visit.

	* src/abg-comp-filter.cc (apply_filter): In the overload for
	corpus_diff, visit each diff node only once.

Signed-off-by: Dodji Seketeli <dodji@redhat.com>
---
 src/abg-comp-filter.cc | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/abg-comp-filter.cc b/src/abg-comp-filter.cc
index 83c406cf..5300d07c 100644
--- a/src/abg-comp-filter.cc
+++ b/src/abg-comp-filter.cc
@@ -41,7 +41,7 @@ void
 apply_filter(filter_base& filter, corpus_diff_sptr d)
 {
   bool s = d->context()->visiting_a_node_twice_is_forbidden();
-  d->context()->forbid_visiting_a_node_twice(false);
+  d->context()->forbid_visiting_a_node_twice(true);
   d->traverse(filter);
   d->context()->forbid_visiting_a_node_twice(s);
 }
-- 
2.39.2

-- 
		Dodji


^ permalink raw reply	[flat|nested] 14+ messages in thread

* [PATCH 11/13] comparison: Add a mode to not apply filters on interface sub-graphs
  2023-03-02 18:53 ` [PATCH 00/13] Support negative suppression specifications Dodji Seketeli
                     ` (9 preceding siblings ...)
  2023-03-02 19:02   ` [PATCH 10/13] comp-filter: Don't re-visit node while applying filters to diff nodes Dodji Seketeli
@ 2023-03-02 19:03   ` Dodji Seketeli
  2023-03-02 19:04   ` [PATCH 12/13] comparison: When marking leaf nodes don't do unnecessary impact analysis Dodji Seketeli
  2023-03-02 19:05   ` [PATCH 13/13] comp-filter: Speed up harmless/harmful categorization Dodji Seketeli
  12 siblings, 0 replies; 14+ messages in thread
From: Dodji Seketeli @ 2023-03-02 19:03 UTC (permalink / raw)
  To: Dodji Seketeli; +Cc: libabigail, ckalina

Hello,

This patch allows to avoid applying filters on interface diff node
sub-graphs because those filters are useful for interface impact
analysis only, which is not needed in the leaf-node report, for
instance.  When using the leaf-node report, this capability speeds up
corpus_diff::apply_filters_and_suppressions_before_reporting, hence
the functions like corpus_diff::{has_incompatible_changes,
has_net_subtype_changes} are sped up too.

That patch thus adds a --no-change-categorization option to abidiff to
avoid doing that change categorization (A.K.A applying filters).

	* doc/manuals/abidiff.rst: Document the new
	--no-change-categorization option.
	* doc/manuals/kmidiff.rst: Likewise.
	* include/abg-comparison.h
	(diff_context::perform_change_categorization): Declare new
	accessor member functions.
	* src/abg-comparison-priv.h
	(diff_context::priv::perform_change_categorization_): Add new data
	member.
	(diff_context::priv::priv): Initialize the new data member.
	* src/abg-comparison.cc
	(diff_context::perform_change_categorization): Define new accessor
	member functions.
	(corpus_diff::priv::apply_filters_and_compute_diff_stats):
	Don't apply filters on the diff node sub-graphs of interfaces when
	the user requested "no change categorization".
	* tools/abidiff.cc (options::perform_change_categorization): New
	data member.
	(options::options): Initialize the new data member.
	(display_usage): Add a help string for the new
	--no-change-categorization.
	(parse_command_line): Parse the new --no-change-categorization
	option.
	(set_diff_context_from_opts): Set the option on the diff context
	here.
	* tools/kmidiff.cc(options::perform_change_categorization): New
	data member.
	(options::options): Initialize the new data member.
	(display_usage): Add a help string for the new
	--no-change-categorization.
	(parse_command_line): Parse the new --no-change-categorization
	option.
	(set_diff_context_from_opts): Set the option on the diff context
	here.

Signed-off-by: Dodji Seketeli <dodji@redhat.com>
---
 doc/manuals/abidiff.rst   |  12 +++
 doc/manuals/kmidiff.rst   |  11 +++
 include/abg-comparison.h  |   6 ++
 src/abg-comparison-priv.h |   2 +
 src/abg-comparison.cc     | 168 +++++++++++++++++++++-----------------
 tools/abidiff.cc          |   8 ++
 tools/kmidiff.cc          |   8 ++
 7 files changed, 140 insertions(+), 75 deletions(-)

diff --git a/doc/manuals/abidiff.rst b/doc/manuals/abidiff.rst
index 2debc20d..d5dc9699 100644
--- a/doc/manuals/abidiff.rst
+++ b/doc/manuals/abidiff.rst
@@ -601,6 +601,18 @@ Options
 
     This option disables those optimizations.
 
+  * ``--no-change-categorization | -x``
+
+    This option disables the categorization of changes into harmless
+    and harmful changes.  Note that this categorization is a
+    pre-requisite for the filtering of changes so this option disables
+    that filtering.  The goal of this option is to speed-up the
+    execution of the program for cases where the graph of changes is
+    huge and where the user is just interested in looking at, for
+    instance, leaf node changes without caring about their possible
+    impact on interfaces.  In that case, this option would be used
+    along with the ``--leaf-changes-only`` one.
+
   * ``--ctf``
 
     When comparing binaries, extract ABI information from `CTF`_ debug
diff --git a/doc/manuals/kmidiff.rst b/doc/manuals/kmidiff.rst
index 40358b92..0e0b33f1 100644
--- a/doc/manuals/kmidiff.rst
+++ b/doc/manuals/kmidiff.rst
@@ -178,6 +178,17 @@ Options
     the :ref:`default suppression specification files
     <abidiff_default_supprs_label>` are loaded .
 
+  * ``--no-change-categorization | -x``
+
+    This option disables the categorization of changes into harmless
+    and harmful changes.  Note that this categorization is a
+    pre-requisite for the filtering of changes so this option disables
+    that filtering.  The goal of this option is to speed-up the
+    execution of the program for cases where the graph of changes is
+    huge and where the user is just interested in looking at, for
+    instance, leaf node changes without caring about their possible
+    impact on interfaces.
+
   * ``--ctf``
 
     Extract ABI information from `CTF`_ debug information, if present,
diff --git a/include/abg-comparison.h b/include/abg-comparison.h
index 506f2bb7..2addb7ac 100644
--- a/include/abg-comparison.h
+++ b/include/abg-comparison.h
@@ -761,6 +761,12 @@ public:
   void
   add_suppressions(const suppr::suppressions_type& supprs);
 
+  bool
+  perform_change_categorization() const;
+
+  void
+  perform_change_categorization(bool);
+
   void
   show_leaf_changes_only(bool f);
 
diff --git a/src/abg-comparison-priv.h b/src/abg-comparison-priv.h
index 48a01188..29d2d2ac 100644
--- a/src/abg-comparison-priv.h
+++ b/src/abg-comparison-priv.h
@@ -189,6 +189,7 @@ struct diff_context::priv
   corpus_diff_sptr			corpus_diff_;
   ostream*				default_output_stream_;
   ostream*				error_output_stream_;
+  bool					perform_change_categorization_;
   bool					leaf_changes_only_;
   bool					forbid_visiting_a_node_twice_;
   bool					reset_visited_diffs_for_each_interface_;
@@ -219,6 +220,7 @@ struct diff_context::priv
       reporter_(),
       default_output_stream_(),
       error_output_stream_(),
+      perform_change_categorization_(true),
       leaf_changes_only_(),
       forbid_visiting_a_node_twice_(true),
       reset_visited_diffs_for_each_interface_(),
diff --git a/src/abg-comparison.cc b/src/abg-comparison.cc
index dc451868..886e48fd 100644
--- a/src/abg-comparison.cc
+++ b/src/abg-comparison.cc
@@ -1532,6 +1532,21 @@ diff_context::add_suppressions(const suppressions_type& supprs)
 			      supprs.begin(), supprs.end());
 }
 
+/// Test if it's requested to perform diff node categorization.
+///
+/// @return true iff it's requested to perform diff node
+/// categorization.
+bool
+diff_context::perform_change_categorization() const
+{return priv_->perform_change_categorization_;}
+
+/// Request change categorization or not.
+///
+/// @param f true iff change categorization is requested.
+void
+diff_context::perform_change_categorization(bool f)
+{priv_->perform_change_categorization_ = f;}
+
 /// Set the flag that indicates if the diff using this context should
 /// show only leaf changes or not.
 ///
@@ -9989,93 +10004,96 @@ corpus_diff::priv::apply_filters_and_compute_diff_stats(diff_stats& stat)
   diff_context_sptr ctxt = get_context();
 
   tools_utils::timer t;
-  if (get_context()->do_log())
-    {
-      std::cerr << "in apply_filters_and_compute_diff_stats:"
-		<< "applying filters to "
-		<< changed_fns_.size()
-		<< " changed fns ...\n";
-      t.start();
-    }
-  // Walk the changed function diff nodes to apply the categorization
-  // filters.
-  diff_sptr diff;
-  for (function_decl_diff_sptrs_type::const_iterator i =
-	 changed_fns_.begin();
-       i != changed_fns_.end();
-       ++i)
+  if (ctxt->perform_change_categorization())
     {
-      diff_sptr diff = *i;
-      ctxt->maybe_apply_filters(diff);
-    }
+      if (get_context()->do_log())
+	{
+	  std::cerr << "in apply_filters_and_compute_diff_stats:"
+		    << "applying filters to "
+		    << changed_fns_.size()
+		    << " changed fns ...\n";
+	  t.start();
+	}
+      // Walk the changed function diff nodes to apply the categorization
+      // filters.
+      diff_sptr diff;
+      for (function_decl_diff_sptrs_type::const_iterator i =
+	     changed_fns_.begin();
+	   i != changed_fns_.end();
+	   ++i)
+	{
+	  diff_sptr diff = *i;
+	  ctxt->maybe_apply_filters(diff);
+	}
 
-  if (get_context()->do_log())
-    {
-      t.stop();
-      std::cerr << "in apply_filters_and_compute_diff_stats:"
-		<< "filters to changed fn applied!:" << t << "\n";
+      if (get_context()->do_log())
+	{
+	  t.stop();
+	  std::cerr << "in apply_filters_and_compute_diff_stats:"
+		    << "filters to changed fn applied!:" << t << "\n";
 
-      std::cerr << "in apply_filters_and_compute_diff_stats:"
-		<< "applying filters to "
-		<< sorted_changed_vars_.size()
-		<< " changed vars ...\n";
-      t.start();
-    }
+	  std::cerr << "in apply_filters_and_compute_diff_stats:"
+		    << "applying filters to "
+		    << sorted_changed_vars_.size()
+		    << " changed vars ...\n";
+	  t.start();
+	}
 
-  // Walk the changed variable diff nodes to apply the categorization
-  // filters.
-  for (var_diff_sptrs_type::const_iterator i = sorted_changed_vars_.begin();
-       i != sorted_changed_vars_.end();
-       ++i)
-    {
-      diff_sptr diff = *i;
-      ctxt->maybe_apply_filters(diff);
-    }
+      // Walk the changed variable diff nodes to apply the categorization
+      // filters.
+      for (var_diff_sptrs_type::const_iterator i = sorted_changed_vars_.begin();
+	   i != sorted_changed_vars_.end();
+	   ++i)
+	{
+	  diff_sptr diff = *i;
+	  ctxt->maybe_apply_filters(diff);
+	}
 
-  if (get_context()->do_log())
-    {
-      t.stop();
-      std::cerr << "in apply_filters_and_compute_diff_stats:"
-		<< "filters to changed vars applied!:" << t << "\n";
+      if (get_context()->do_log())
+	{
+	  t.stop();
+	  std::cerr << "in apply_filters_and_compute_diff_stats:"
+		    << "filters to changed vars applied!:" << t << "\n";
 
-      std::cerr << "in apply_filters_and_compute_diff_stats:"
-		<< "applying filters to unreachable types ...\n";
-      t.start();
-    }
+	  std::cerr << "in apply_filters_and_compute_diff_stats:"
+		    << "applying filters to unreachable types ...\n";
+	  t.start();
+	}
 
-  // 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);
-    }
+      // 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);
+	}
 
-  if (get_context()->do_log())
-    {
-      t.stop();
-      std::cerr << "in apply_filters_and_compute_diff_stats:"
-		<< "filters to unreachable types applied!:" << t << "\n";
+      if (get_context()->do_log())
+	{
+	  t.stop();
+	  std::cerr << "in apply_filters_and_compute_diff_stats:"
+		    << "filters to unreachable types applied!:" << t << "\n";
 
-      std::cerr << "in apply_filters_and_compute_diff_stats:"
-		<< "categorizing redundant changed sub nodes ...\n";
-      t.start();
-    }
+	  std::cerr << "in apply_filters_and_compute_diff_stats:"
+		    << "categorizing redundant changed sub nodes ...\n";
+	  t.start();
+	}
 
-  categorize_redundant_changed_sub_nodes();
+      categorize_redundant_changed_sub_nodes();
 
-  if (get_context()->do_log())
-    {
-      t.stop();
-      std::cerr << "in apply_filters_and_compute_diff_stats:"
-		<< "redundant changed sub nodes categorized!:" << t << "\n";
+      if (get_context()->do_log())
+	{
+	  t.stop();
+	  std::cerr << "in apply_filters_and_compute_diff_stats:"
+		    << "redundant changed sub nodes categorized!:" << t << "\n";
 
-      std::cerr << "in apply_filters_and_compute_diff_stats:"
-		<< "count changed fns ...\n";
-      t.start();
+	  std::cerr << "in apply_filters_and_compute_diff_stats:"
+		    << "count changed fns ...\n";
+	  t.start();
+	}
     }
 
   // Walk the changed function diff nodes to count the number of
diff --git a/tools/abidiff.cc b/tools/abidiff.cc
index a0a670cb..3613a4a3 100644
--- a/tools/abidiff.cc
+++ b/tools/abidiff.cc
@@ -114,6 +114,7 @@ struct options
   bool			show_impacted_interfaces;
   bool			assume_odr_for_cplusplus;
   bool			leverage_dwarf_factorization;
+  bool			perform_change_categorization;
   bool			dump_diff_tree;
   bool			show_stats;
   bool			do_log;
@@ -170,6 +171,7 @@ struct options
       show_impacted_interfaces(),
       assume_odr_for_cplusplus(true),
       leverage_dwarf_factorization(true),
+      perform_change_categorization(true),
       dump_diff_tree(),
       show_stats(),
       do_log()
@@ -276,6 +278,8 @@ display_usage(const string& prog_name, ostream& out)
     << " --impacted-interfaces  display interfaces impacted by leaf changes\n"
     << " --no-leverage-dwarf-factorization  do not use DWZ optimisations to "
     "speed-up the analysis of the binary\n"
+    << " --no-change-categorization | -x don't perform categorization "
+    "of changes, for speed purposes\n"
     << " --no-assume-odr-for-cplusplus  do not assume the ODR to speed-up the "
     "analysis of the binary\n"
     << " --dump-diff-tree  emit a debug dump of the internal diff tree to "
@@ -641,6 +645,9 @@ parse_command_line(int argc, char* argv[], options& opts)
 	opts.show_impacted_interfaces = true;
       else if (!strcmp(argv[i], "--no-leverage-dwarf-factorization"))
 	opts.leverage_dwarf_factorization = false;
+      else if (!strcmp(argv[i], "--no-change-categorization")
+	       || !strcmp(argv[i], "-x"))
+	opts.perform_change_categorization = false;
       else if (!strcmp(argv[i], "--no-assume-odr-for-cplusplus"))
 	opts.leverage_dwarf_factorization = false;
       else if (!strcmp(argv[i], "--dump-diff-tree"))
@@ -752,6 +759,7 @@ set_diff_context_from_opts(diff_context_sptr ctxt,
 {
   ctxt->default_output_stream(&cout);
   ctxt->error_output_stream(&cerr);
+  ctxt->perform_change_categorization(opts.perform_change_categorization);
   ctxt->show_leaf_changes_only(opts.leaf_changes_only);
   ctxt->show_hex_values(opts.show_hexadecimal_values);
   ctxt->show_offsets_sizes_in_bits(opts.show_offsets_sizes_in_bits);
diff --git a/tools/kmidiff.cc b/tools/kmidiff.cc
index f00895a3..f07e4c59 100644
--- a/tools/kmidiff.cc
+++ b/tools/kmidiff.cc
@@ -56,6 +56,7 @@ struct options
   bool			display_version;
   bool			verbose;
   bool			missing_operand;
+  bool			perform_change_categorization;
   bool			leaf_changes_only;
   bool			show_hexadecimal_values;
   bool			show_offsets_sizes_in_bits;
@@ -84,6 +85,7 @@ struct options
       display_version(),
       verbose(),
       missing_operand(),
+      perform_change_categorization(true),
       leaf_changes_only(true),
       show_hexadecimal_values(true),
       show_offsets_sizes_in_bits(false),
@@ -128,6 +130,8 @@ display_usage(const string& prog_name, ostream& out)
 #ifdef WITH_BTF
     << " --btf use BTF instead of DWARF in ELF files\n"
 #endif
+    << " --no-change-categorization | -x don't perform categorization "
+    "of changes, for speed purposes\n"
     << " --impacted-interfaces|-i  show interfaces impacted by ABI changes\n"
     << " --full-impact|-f  show the full impact of changes on top-most "
 	 "interfaces\n"
@@ -274,6 +278,9 @@ parse_command_line(int argc, char* argv[], options& opts)
       else if (!strcmp(argv[i], "--btf"))
 	opts.use_btf = true;
 #endif
+      else if (!strcmp(argv[i], "--no-change-categorization")
+	       || !strcmp(argv[i], "-x"))
+	opts.perform_change_categorization = false;
       else if (!strcmp(argv[i], "--impacted-interfaces")
 	       || !strcmp(argv[i], "-i"))
 	opts.show_impacted_interfaces = true;
@@ -344,6 +351,7 @@ set_diff_context(diff_context_sptr ctxt, const options& opts)
   ctxt->show_linkage_names(false);
   ctxt->show_symbols_unreferenced_by_debug_info
     (true);
+  ctxt->perform_change_categorization(opts.perform_change_categorization);
   ctxt->show_leaf_changes_only(opts.leaf_changes_only);
   ctxt->show_impacted_interfaces(opts.show_impacted_interfaces);
   ctxt->show_hex_values(opts.show_hexadecimal_values);
-- 
2.39.2



-- 
		Dodji


^ permalink raw reply	[flat|nested] 14+ messages in thread

* [PATCH 12/13] comparison: When marking leaf nodes don't do unnecessary impact analysis
  2023-03-02 18:53 ` [PATCH 00/13] Support negative suppression specifications Dodji Seketeli
                     ` (10 preceding siblings ...)
  2023-03-02 19:03   ` [PATCH 11/13] comparison: Add a mode to not apply filters on interface sub-graphs Dodji Seketeli
@ 2023-03-02 19:04   ` Dodji Seketeli
  2023-03-02 19:05   ` [PATCH 13/13] comp-filter: Speed up harmless/harmful categorization Dodji Seketeli
  12 siblings, 0 replies; 14+ messages in thread
From: Dodji Seketeli @ 2023-03-02 19:04 UTC (permalink / raw)
  To: Dodji Seketeli; +Cc: libabigail, ckalina

Hello,

When marking leaf nodes, if interface impact analysis is not required,
then avoid visiting the same diff node twice when walking the diff
graph.  Allowing to visit the same diff node twice would be useful so
that the sub-graph of each interface diff node would be walked in full
to determine the impact of each leaf diff node on each interface.  But
if such impact analysis is not required, then we can just visit each
diff graph node once and speed up things greatly in leaf node
reporting mode.

	* src/abg-comparison.cc (corpus_diff::mark_leaf_diff_nodes): If
	impact analysis is not required, visit each node just once.

Signed-off-by: Dodji Seketeli <dodji@redhat.com>
---
 src/abg-comparison.cc | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/src/abg-comparison.cc b/src/abg-comparison.cc
index 886e48fd..1533d9d1 100644
--- a/src/abg-comparison.cc
+++ b/src/abg-comparison.cc
@@ -11230,7 +11230,8 @@ corpus_diff::mark_leaf_diff_nodes()
   context()->forget_visited_diffs();
   bool s = context()->visiting_a_node_twice_is_forbidden();
   context()->forbid_visiting_a_node_twice(true);
-  context()->forbid_visiting_a_node_twice_per_interface(true);
+  if (context()->show_impacted_interfaces())
+    context()->forbid_visiting_a_node_twice_per_interface(true);
   traverse(v);
   context()->forbid_visiting_a_node_twice(s);
   context()->forbid_visiting_a_node_twice_per_interface(false);
-- 
2.39.2

-- 
		Dodji


^ permalink raw reply	[flat|nested] 14+ messages in thread

* [PATCH 13/13] comp-filter: Speed up harmless/harmful categorization
  2023-03-02 18:53 ` [PATCH 00/13] Support negative suppression specifications Dodji Seketeli
                     ` (11 preceding siblings ...)
  2023-03-02 19:04   ` [PATCH 12/13] comparison: When marking leaf nodes don't do unnecessary impact analysis Dodji Seketeli
@ 2023-03-02 19:05   ` Dodji Seketeli
  12 siblings, 0 replies; 14+ messages in thread
From: Dodji Seketeli @ 2023-03-02 19:05 UTC (permalink / raw)
  To: Dodji Seketeli; +Cc: libabigail, ckalina

Hello,

Categorizing a diff graph with a thousands of function diff nodes takes a
lot of time.  At that point, categorizing each node has
harmful/harmless is showing up in the profile, particularly because
has_var_type_cv_qual_change and to a lesser extend
class_diff_has_harmless_odr_violation_change perform some structural
comparison still, oops.  This patch avoids doing that.  On my machine,
the categorizing of each node goes from around 130ms to 92 ms.

	* src/abg-comp-filter.cc
	(class_diff_has_harmless_odr_violation_change)
	(has_var_type_cv_qual_change): Avoid doing structural comparison
	here.

Signed-off-by: Dodji Seketeli <dodji@redhat.com>
---
 src/abg-comp-filter.cc | 25 ++++++-------------------
 1 file changed, 6 insertions(+), 19 deletions(-)

diff --git a/src/abg-comp-filter.cc b/src/abg-comp-filter.cc
index 5300d07c..f1d67de5 100644
--- a/src/abg-comp-filter.cc
+++ b/src/abg-comp-filter.cc
@@ -775,14 +775,10 @@ class_diff_has_harmless_odr_violation_change(const diff* dif)
   class_decl_sptr first = d->first_class_decl();
   class_decl_sptr second = d->second_class_decl();
 
-  if (equals(*first, *second, 0))
-    {
-      class_decl_sptr fc = is_class_type(first->get_canonical_type());
-      class_decl_sptr sc = is_class_type(second->get_canonical_type());
-
-      if (!equals(*fc, *sc, 0))
-	return true;
-    }
+  if (first->get_qualified_name() == second->get_qualified_name()
+      && first != second
+      && first->get_corpus() == second->get_corpus())
+    return true;
 
   return false;
 }
@@ -1643,18 +1639,9 @@ has_var_type_cv_qual_change(const diff* dif)
   if (!var_dif)
     return false;
 
-  {
-    // Make sure the variable diff does carry a type change at least
-    change_kind ch_kind = NO_CHANGE_KIND;
-    if (equals(*var_dif->first_var(), *var_dif->second_var(), &ch_kind))
-      return false;
-
-    if (!(ch_kind & LOCAL_TYPE_CHANGE_KIND || ch_kind & SUBTYPE_CHANGE_KIND))
-      return false;
-  }
-
   diff *type_dif = var_dif->type_diff().get();
-  ABG_ASSERT(type_dif);
+  if (!type_dif)
+    return false;
 
   return type_diff_has_cv_qual_change_only(type_dif);
 }
-- 
2.39.2

-- 
		Dodji


^ permalink raw reply	[flat|nested] 14+ messages in thread

end of thread, other threads:[~2023-03-02 19:05 UTC | newest]

Thread overview: 14+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
     [not found] <877cvzrnws.fsf@redhat.com>
2023-03-02 18:53 ` [PATCH 00/13] Support negative suppression specifications Dodji Seketeli
2023-03-02 18:55   ` [PATCH 01/13] ini: Fix parsing list property values Dodji Seketeli
2023-03-02 18:56   ` [PATCH 02/13] suppr: Support has_data_member and has_data_member_regexp properties Dodji Seketeli
2023-03-02 18:57   ` [PATCH 03/13] suppression: Factorize out is_data_member_offset_in_range Dodji Seketeli
2023-03-02 18:58   ` [PATCH 04/13] suppression: Support the has_size_change property for suppress_type Dodji Seketeli
2023-03-02 18:59   ` [PATCH 05/13] suppression: Support offset_of_{first,last}_data_member_regexp offset selectors Dodji Seketeli
2023-03-02 18:59   ` [PATCH 06/13] comparison, suppression: Support [allow_type] directive Dodji Seketeli
2023-03-02 19:00   ` [PATCH 07/13] Misc white space fixes Dodji Seketeli
2023-03-02 19:01   ` [PATCH 08/13] abidiff: Add extensive logging Dodji Seketeli
2023-03-02 19:01   ` [PATCH 09/13] tools-utils: Support kernel stablelist Dodji Seketeli
2023-03-02 19:02   ` [PATCH 10/13] comp-filter: Don't re-visit node while applying filters to diff nodes Dodji Seketeli
2023-03-02 19:03   ` [PATCH 11/13] comparison: Add a mode to not apply filters on interface sub-graphs Dodji Seketeli
2023-03-02 19:04   ` [PATCH 12/13] comparison: When marking leaf nodes don't do unnecessary impact analysis Dodji Seketeli
2023-03-02 19:05   ` [PATCH 13/13] comp-filter: Speed up harmless/harmful categorization Dodji Seketeli

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).