]> sourceware.org Git - libabigail.git/commitdiff
Support source_location_not_in and source_location_not_regexp suppressions
authorDodji Seketeli <dodji@redhat.com>
Wed, 16 Sep 2015 09:37:22 +0000 (11:37 +0200)
committerDodji Seketeli <dodji@redhat.com>
Wed, 16 Sep 2015 18:54:40 +0000 (20:54 +0200)
This patch adds support for properties source_location_not_in and
source_location_not_regexp in the [suppress_type] section of
suppression specifications.  So the suppression specification:

  [suppress_type]
    source_location_not_in = foo1.h, foo2.h bar1.h bar2.h

suppresses ABI change reports about types that are *NOT* defined in
files foo{1,2}.h and bar{1,2}.h.  The intended use of this construct is to
constrain abi change reports to types that are part of the API of a
given shared library.  The API of the library is supposed to be
defined in foo.h and bar.h only.

Similarly, the suppression specification:

  [suppress_type]
    source_location_not_regexp = (foo|bar){1,2}\\.h

suppresses ABI change reports about types that are not defined in the
same set of files foo1.h, foo2.h, bar1.h and bar2.h.

* include/abg-ini.h (enum property_value::value_kind): Add a
LIST_PROPERTY_VALUE kind.
(class {list_property_value, list_property}): Declare new types.
(is_list_property, is_list_property_value): Declare new functions.
* src/abg-ini.cc (struct list_property_value::priv): Define new
type.
(list_property_value::{list_property_value, get_content,
set_content, as_string}): Define new member functions.
(is_list_property_value): Define new function.
(struct list_property::priv): Define new type.
(list_property::{list_property, get_value, set_value,
handle_escape}): Define new member functions.
(is_list_property): Define new function.
(read_context::buf_): New data member.
(read_context::{peek, get, put_back, good, eof, read_string,
read_list_property_value}): New member functions.
(read_context::read_next_char): Use the new read_context::{get,
good, eof} member function, rather than using the input stream
directly.
(read_context::{skip_white_spaces, skip_comments,
skip_white_spaces_or_comments, read_property_name,
read_function_name, read_function_argument,
read_function_call_expr, read_property_value,
read_tuple_property_value, read_section_name, read_section}):
Adjust to use the new member functions of read_context rather than
using the input stream directly.
(read_context::read_string_property_value): Likewise.  Use the new
read_context::read_string() method.
(read_context::{read, write}_property): Support reading list_property.
* include/abg-comparison.h
(type_suppression::{get_source_locations_to_keep,
set_source_locations_to_keep,
set_source_location_to_keep_regex_str,
get_source_location_to_keep_regex_str}): Add new member functions.
* src/abg-comparison.cc
(type_suppression::priv::{source_location_to_keep_,
source_location_to_keep_regex_str_,
source_location_to_keep_regex_}): Add new data members.
(type_suppression::priv::{g,s}et_source_location_to_keep_regex):
Define new member functions.
(type_suppression::{g,s}et_source_locations_to_keep): Define new
member functions.
(type_suppression::{g,s}et_source_location_to_keep_regex_str):
Likewise.
(type_suppression::suppresses_type): Support
"source_location_not_regexp" and "source_location_not_in"
properties of suppression specifications.
(read_type_suppression): Likewise. Also adjust to the fact that
ta tuple property value that is a list of strings is not a list
property value.
* doc/manuals/libabigail-concepts.rst: Add documentation for
source_location_not_in and source_location_not_regexp.
* tests/data/test-diff-suppr/libtest26-loc-suppr-v{0,1}.so: New
binary test inputs.
* tests/data/test-diff-suppr/test26-loc-suppr-{0,1,2}.suppr: New
suppression specification test inputs.
* tests/data/test-diff-suppr/test26-loc-suppr-report-{0,1,2,3}.txt:
New test reference reports.
* tests/data/test-diff-suppr/test26-loc-suppr-v{0,1}.cc: Source
code of the test binary input above.
* tests/data/test-diff-suppr/test26-loc-suppr.h: Likewise.
* tests/data/Makefile.am: Add the new test material to source
distribution.
* tests/test-diff-suppr.cc (in_out_specs): Add the new test inputs above.

Signed-off-by: Dodji Seketeli <dodji@redhat.com>
19 files changed:
doc/manuals/libabigail-concepts.rst
include/abg-comparison.h
include/abg-ini.h
src/abg-comparison.cc
src/abg-ini.cc
tests/data/Makefile.am
tests/data/test-diff-suppr/libtest26-loc-suppr-v0.so [new file with mode: 0755]
tests/data/test-diff-suppr/libtest26-loc-suppr-v1.so [new file with mode: 0755]
tests/data/test-diff-suppr/test26-loc-suppr-0.suppr [new file with mode: 0644]
tests/data/test-diff-suppr/test26-loc-suppr-1.suppr [new file with mode: 0644]
tests/data/test-diff-suppr/test26-loc-suppr-2.suppr [new file with mode: 0644]
tests/data/test-diff-suppr/test26-loc-suppr-report-0.txt [new file with mode: 0644]
tests/data/test-diff-suppr/test26-loc-suppr-report-1.txt [new file with mode: 0644]
tests/data/test-diff-suppr/test26-loc-suppr-report-2.txt [new file with mode: 0644]
tests/data/test-diff-suppr/test26-loc-suppr-report-3.txt [new file with mode: 0644]
tests/data/test-diff-suppr/test26-loc-suppr-v0.cc [new file with mode: 0644]
tests/data/test-diff-suppr/test26-loc-suppr-v1.cc [new file with mode: 0644]
tests/data/test-diff-suppr/test26-loc-suppr.h [new file with mode: 0644]
tests/test-diff-suppr.cc

index 831ae8525665ddfb6ef960c7f6aea0359e507263..f42a62829c81e49bcd2c4ae5aba2af9e4d0bcc0f 100644 (file)
@@ -232,6 +232,44 @@ potential properties of this sections are:
      types.  Example of built-in types are char, int, unsigned int,
      etc.
 
+ .. _suppr_source_location_not_in_label:
+
+* ``source_location_not_in``
+
+ Usage:
+
+  ``source_location_not_in`` ``=`` <``list-of-file-paths``>
+
+ Suppresses change reports involving a type which is defined in a file
+ which path is *NOT* listed in the value ``list-of-file-paths``.  Note
+ that the value is a comma-separated list of file paths e.g, this
+ property ::
+   source_location_not_in = libabigail/abg-ir.h, libabigail/abg-dwarf-reader.h 
+
+ suppresses change reports about all the types that are *NOT* defined
+ in header files whose path end up with the strings
+ libabigail/abg-ir.h or libabigail/abg-dwarf-reader.h.
+
+ .. _suppr_source_location_not_regexp_label:
+
+* ``source_location_not_regexp``
+
+ Usage:
+
+  ``source_location_not_regexp`` ``=`` <:ref:`regular-expression <suppr_regexp_label>`>
+
+ Suppresses change reports involving a type which is defined in a file
+ which path does *NOT* match the :ref:`regular expression
+ <suppr_regexp_label>` provided as value of the property. E.g, this
+ property ::
+
+   source_location_not_regexp = libabigail/abg-.*\\.h
+
+ suppresses change reports involving all the types that are *NOT*
+ defined in header files whose path match the regular expression
+ provided a value of the property.
+
  .. _suppr_has_data_member_inserted_at_label:
 
 * ``has_data_member_inserted_at``
index bbd5405bbe3e68fdd42b500dd759aeffda9dfa91..5e335202ca964fc1e8b82b9220ba2cd106365fc5 100644 (file)
@@ -568,6 +568,18 @@ public:
   insertion_ranges&
   get_data_member_insertion_ranges();
 
+  const vector<string>&
+  get_source_locations_to_keep() const;
+
+  void
+  set_source_locations_to_keep(const vector<string>&);
+
+  const string&
+  get_source_location_to_keep_regex_str() const;
+
+  void
+  set_source_location_to_keep_regex_str(const string&);
+
   virtual bool
   suppresses_diff(const diff* diff) const;
 
index dff600c71d25e13bc16015a5293550f5bd6f7aec..6e0ed6c6f7830988ab15f74764f954db227c665e 100644 (file)
@@ -86,7 +86,8 @@ public:
   {
     ABSTRACT_PROPERTY_VALUE = 0,
     STRING_PROPERTY_VALUE = 1,
-    TUPLE_PROPERTY_VALUE = 2,
+    LIST_PROPERTY_VALUE = 2,
+    TUPLE_PROPERTY_VALUE = 3,
   };
 
 private:
@@ -143,6 +144,48 @@ is_string_property_value(const property_value*);
 string_property_value_sptr
 is_string_property_value(const property_value_sptr);
 
+class list_property_value;
+
+/// A convenience typedef for a shared_ptr to @ref
+/// list_property_value.
+typedef shared_ptr<list_property_value> list_property_value_sptr;
+
+/// Abstracts the value of a property representing a list of strings.
+///
+/// It's the right hand side of the construct which syntax looks like:
+///
+///   name = val1, val2, val3
+///
+/// where val1, val2 and val3 are strings.
+///
+/// So this class abstracts the set [val1, val2, val3].
+class list_property_value : public property_value
+{
+  struct priv;
+  typedef shared_ptr<priv> priv_sptr;
+
+  priv_sptr priv_;
+
+public:
+  list_property_value();
+  list_property_value(const vector<string>& values);
+
+  const vector<string>&
+  get_content() const;
+
+  void
+  set_content(const vector<string>&);
+
+  virtual const string&
+  as_string() const;
+}; // end class list_property_value
+
+list_property_value*
+is_list_property_value(const property_value*);
+
+list_property_value_sptr
+is_list_property_value(const property_value_sptr&);
+
 class tuple_property_value;
 
 /// Convenience typedef for a shared_ptr to a @ref
@@ -216,6 +259,47 @@ is_simple_property(const property* p);
 simple_property_sptr
 is_simple_property(const property_sptr p);
 
+class list_property;
+
+/// A convenience typedef for a shared_ptr to a @ref list_property.
+typedef shared_ptr<list_property> list_property_sptr;
+
+/// A class representing a list property.
+///
+/// It abstracts a construct which syntax looks like:
+///
+///    name = val1, val2, val3
+///
+/// The value of a list property is a @ref list_property_value, i.e, a
+/// list of strings.
+class list_property : public property
+{
+  struct priv;
+  typedef shared_ptr<priv> priv_sptr;
+
+  priv_sptr priv_;
+
+public:
+  list_property();
+
+  list_property(const string& name,
+               const list_property_value_sptr& value);
+
+  const list_property_value_sptr&
+  get_value() const;
+
+  void
+  set_value(const list_property_value_sptr& value);
+
+  virtual ~list_property();
+}; // end class list_property
+
+list_property*
+is_list_property(const property* p);
+
+list_property_sptr
+is_list_property(const property_sptr p);
+
 class tuple_property;
 /// Convenience typedef for a shared_ptr of @ref tuple_property.
 typedef shared_ptr<tuple_property> tuple_property_sptr;
index 88a258e441b43168402509c126ab64115b76b2b8..fb54497da00b31155e501c7172c8c11e4aaf6992 100644 (file)
@@ -663,14 +663,17 @@ read_suppressions(const string& file_path,
 /// The private data for @ref type_suppression.
 class type_suppression::priv
 {
-  string                                type_name_regex_str_;
-  mutable sptr_utils::regex_t_sptr      type_name_regex_;
-  string                                type_name_;
-  bool                                  consider_type_kind_;
-  type_suppression::type_kind           type_kind_;
-  bool                                  consider_reach_kind_;
-  type_suppression::reach_kind          reach_kind_;
-  type_suppression::insertion_ranges    insertion_ranges_;
+  string                               type_name_regex_str_;
+  mutable sptr_utils::regex_t_sptr     type_name_regex_;
+  string                               type_name_;
+  bool                                 consider_type_kind_;
+  type_suppression::type_kind          type_kind_;
+  bool                                 consider_reach_kind_;
+  type_suppression::reach_kind         reach_kind_;
+  type_suppression::insertion_ranges   insertion_ranges_;
+  vector<string>                       source_locations_to_keep_;
+  string                               source_location_to_keep_regex_str_;
+  mutable sptr_utils::regex_t_sptr     source_location_to_keep_regex_;
 
   priv();
 
@@ -721,6 +724,33 @@ public:
   set_type_name_regex(sptr_utils::regex_t_sptr r)
   {type_name_regex_ = r;}
 
+  /// Getter for the source_location_to_keep_regex object.
+  ///
+  /// This function builds the regex if it's not yet built.
+  const sptr_utils::regex_t_sptr
+  get_source_location_to_keep_regex() const
+  {
+    if (!source_location_to_keep_regex_)
+      {
+       if (!source_location_to_keep_regex_str_.empty())
+         {
+           sptr_utils::regex_t_sptr r(new regex_t);
+           if (regcomp(r.get(),
+                       source_location_to_keep_regex_str_.c_str(),
+                       REG_EXTENDED) == 0)
+             source_location_to_keep_regex_ = r;
+         }
+      }
+    return source_location_to_keep_regex_;
+  }
+
+  /// Setter for the source_location_to_keep_regex object.
+  ///
+  /// @param r the new regex object.
+  void
+  set_source_location_to_keep_regex(sptr_utils::regex_t_sptr r)
+  {source_location_to_keep_regex_ = r;}
+
   friend class type_suppression;
 }; // class type_suppression::priv
 
@@ -896,6 +926,39 @@ type_suppression::insertion_ranges&
 type_suppression::get_data_member_insertion_ranges()
 {return priv_->insertion_ranges_;}
 
+/// Getter for the array of source location paths of types that should
+/// *NOT* be suppressed.
+///
+/// @return the array of source locations of types that should *NOT*
+/// be supressed.
+const vector<string>&
+type_suppression::get_source_locations_to_keep() const
+{return priv_->source_locations_to_keep_;}
+
+/// Setter for the array of source location paths of types that should
+/// *NOT* be suppressed.
+///
+/// @param l the new array.
+void
+type_suppression::set_source_locations_to_keep(const vector<string>& l)
+{priv_->source_locations_to_keep_ = l;}
+
+/// Getter of the regular expression string that designates the source
+/// location paths of types that should not be suppressed.
+///
+/// @return the regular expression string.
+const string&
+type_suppression::get_source_location_to_keep_regex_str() const
+{return priv_->source_location_to_keep_regex_str_;}
+
+/// Setter of the regular expression string that designates the source
+/// location paths of types that should not be suppressed.
+///
+/// @param r the new regular expression.
+void
+type_suppression::set_source_location_to_keep_regex_str(const string& r)
+{priv_->source_location_to_keep_regex_str_ = r;}
+
 /// Evaluate this suppression specification on a given diff node and
 /// say if the diff node should be suppressed or not.
 ///
@@ -1163,6 +1226,69 @@ type_suppression::suppresses_type(const type_base_sptr type,
        return false;
     }
 
+  // Check if there is a source location related match.
+  if (decl_base_sptr d = get_type_declaration(type))
+    {
+      location loc = d->get_location();
+      if (loc)
+       {
+         translation_unit* tu = get_translation_unit(d);
+         if (tu)
+           {
+             string loc_path, loc_path_base;
+             unsigned loc_line = 0, loc_column = 0;
+             tu->get_loc_mgr().expand_location(loc, loc_path,
+                                               loc_line, loc_column);
+
+             if (sptr_utils::regex_t_sptr regexp =
+                 priv_->get_source_location_to_keep_regex())
+               if (regexec(regexp.get(), loc_path.c_str(), 0, NULL, 0) == 0)
+                 return false;
+
+             tools_utils::base_name(loc_path, loc_path_base);
+             for (vector<string>::const_iterator s =
+                    get_source_locations_to_keep().begin();
+                  s != get_source_locations_to_keep().end();
+                  ++s)
+               {
+                 if (tools_utils::string_ends_with(*s, loc_path)
+                     || tools_utils::string_ends_with(*s, loc_path_base))
+                   return false;
+               }
+           }
+         else
+           {
+             if (!get_source_locations_to_keep().empty()
+                 || priv_->get_source_location_to_keep_regex())
+               // The user provided a "source_location_not_regexp" or
+               // a "source_location_not_in" property that was not
+               // triggered.  This means the current type suppression
+               // doesn't suppress the type given.
+               return false;
+           }
+       }
+      else
+       {
+         if (!get_source_locations_to_keep().empty()
+             || priv_->get_source_location_to_keep_regex())
+           // The user provided a "source_location_not_regexp" or
+           // a "source_location_not_in" property that was not
+           // triggered.  This means the current type suppression
+           // doesn't suppress the type given.
+           return false;
+       }
+    }
+  else
+    {
+      if (!get_source_locations_to_keep().empty()
+         || priv_->get_source_location_to_keep_regex())
+       // The user provided a "source_location_not_regexp" or
+       // a "source_location_not_in" property that was not
+       // triggered.  This means the current type suppression
+       // doesn't suppress the type given.
+       return false;
+    }
+
   return true;
 }
 
@@ -1553,6 +1679,28 @@ read_type_suppression(const ini::config::section& section)
     ? name_prop->get_value()->as_string()
     : "";
 
+  ini::property_sptr srcloc_not_in_prop =
+    section.find_property("source_location_not_in");
+  vector<string> srcloc_not_in;
+  if (srcloc_not_in_prop)
+    {
+      if (ini::simple_property_sptr p = is_simple_property(srcloc_not_in_prop))
+       srcloc_not_in.push_back(p->get_value()->as_string());
+      else
+       {
+         ini::list_property_sptr list_property =
+           is_list_property(srcloc_not_in_prop);
+         if (list_property)
+           srcloc_not_in = list_property->get_value()->get_content();
+       }
+    }
+
+  ini::simple_property_sptr srcloc_not_regexp_prop =
+    is_simple_property(section.find_property("source_location_not_regexp"));
+  string srcloc_not_regexp_str;
+  if (srcloc_not_regexp_prop)
+    srcloc_not_regexp_str = srcloc_not_regexp_prop->get_value()->as_string();
+
   bool consider_type_kind = false;
   type_suppression::type_kind type_kind = type_suppression::UNKNOWN_TYPE_KIND;
   if (ini::simple_property_sptr type_kind_prop =
@@ -1610,12 +1758,21 @@ read_type_suppression(const ini::config::section& section)
       //  has_data_member_inserted_between = {0 , end};
       // 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.
       type_suppression::insertion_range::boundary_sptr begin, end;
-      if (prop->get_value()->get_value_items().size() == 2
-         && is_string_property_value(prop->get_value()->get_value_items()[0])
-         && is_string_property_value(prop->get_value()->get_value_items()[1]))
+      ini::tuple_property_value_sptr v = prop->get_value();
+      if (v
+         && v->get_value_items().size() == 1
+         && is_list_property_value(v->get_value_items()[0])
+         && is_list_property_value(v->get_value_items()[0])->get_content().size() == 2)
        {
-         string str = prop->get_value()->get_value_items()[0]->as_string();
+         ini::list_property_value_sptr val =
+           is_list_property_value(v->get_value_items()[0]);
+         assert(val);
+         string str = val->get_content()[0];
          if (str == "end")
            begin =
              type_suppression::insertion_range::create_integer_boundary(-1);
@@ -1628,7 +1785,7 @@ read_type_suppression(const ini::config::section& section)
          else
            return nil;
 
-         str = prop->get_value()->get_value_items()[1]->as_string();
+         str = val->get_content()[1];
          if (str == "end")
            end =
              type_suppression::insertion_range::create_integer_boundary(-1);
@@ -1654,6 +1811,13 @@ read_type_suppression(const ini::config::section& section)
     }
 
   // Support has_data_members_inserted_between
+  // The syntax looks like:
+  //
+  //    has_data_members_inserted_between = {{8, 24}, {32, 64}, {128, end}}
+  //
+  // So we expect a tuple property, with potentially several pairs (as
+  // part of the value); each pair designating a range.  Note that
+  // each pair (range) is a list property value.
   if (ini::tuple_property_sptr prop =
       is_tuple_property(section.find_property
                        ("has_data_members_inserted_between")))
@@ -1667,13 +1831,22 @@ read_type_suppression(const ini::config::section& section)
          ini::tuple_property_value_sptr tuple_value =
            is_tuple_property_value(*i);
          if (!tuple_value
-             || tuple_value->get_value_items().size() != 2
-             || !is_string_property_value(tuple_value->get_value_items()[0])
-             || !is_string_property_value(tuple_value->get_value_items()[1]))
-           is_well_formed = false;
+             || tuple_value->get_value_items().size() != 1
+             || !is_list_property_value(tuple_value->get_value_items()[0]))
+           {
+             is_well_formed = false;
+             break;
+           }
+         ini::list_property_value_sptr list_value =
+           is_list_property_value(tuple_value->get_value_items()[0]);
+         if (list_value->get_content().size() != 2)
+           {
+             is_well_formed = false;
+             break;
+           }
 
          type_suppression::insertion_range::boundary_sptr begin, end;
-         string str = tuple_value->get_value_items()[0]->as_string();
+         string str = list_value->get_content()[0];
          if (str == "end")
            begin =
              type_suppression::insertion_range::create_integer_boundary(-1);
@@ -1687,7 +1860,7 @@ read_type_suppression(const ini::config::section& section)
          else
            return nil;
 
-         str = tuple_value->get_value_items()[1]->as_string();
+         str = list_value->get_content()[1];
          if (str == "end")
            end =
              type_suppression::insertion_range::create_integer_boundary(-1);
@@ -1711,7 +1884,9 @@ read_type_suppression(const ini::config::section& section)
 
   if ((!name_regex_prop || name_regex_prop->get_value()->as_string().empty())
       && (!name_prop || name_prop->get_value()->as_string().empty())
-      && !consider_type_kind)
+      && !consider_type_kind
+      && srcloc_not_regexp_str.empty()
+      && srcloc_not_in.empty())
     return nil;
 
   type_suppression_sptr suppr(new type_suppression(label_str,
@@ -1738,6 +1913,12 @@ read_type_suppression(const ini::config::section& section)
   if (!soname_regex_str.empty())
     suppr->set_soname_regex_str(soname_regex_str);
 
+  if (!srcloc_not_in.empty())
+    suppr->set_source_locations_to_keep(srcloc_not_in);
+
+  if (!srcloc_not_regexp_str.empty())
+    suppr->set_source_location_to_keep_regex_str(srcloc_not_regexp_str);
+
   return suppr;
 }
 
index 43a87fa85dbf4f9d3b5d711b0c66abe458067023..b09342dc6fd125bf2d0d77e75091a24a2ad05bf3 100644 (file)
@@ -26,6 +26,7 @@
 /// the libabigail library.
 
 #include <cassert>
+#include <cstdlib>
 #include <utility>
 #include <memory>
 #include <fstream>
@@ -352,7 +353,101 @@ is_string_property_value(const property_value_sptr v)
 string_property_value::~string_property_value()
 {}
 
-// </string_property stuff>
+// </string_property_value stuff>
+
+// <list_property_value stuff>
+struct list_property_value::priv
+{
+  vector<string> values_;
+  string representation_;
+
+  priv()
+  {}
+
+  priv(const vector<string>& vals)
+    : values_(vals)
+  {}
+}; // end struct list_property_value::priv
+
+/// Default constructor of the @ref list_property_value type.
+list_property_value::list_property_value()
+  : property_value(property_value::LIST_PROPERTY_VALUE),
+   priv_(new priv)
+{}
+
+/// Copy constructor of the @ref list_property_value type.
+///
+/// @param values the instance of @ref list_property_value to copy from.
+list_property_value::list_property_value(const vector<string>& values)
+  : property_value(property_value::LIST_PROPERTY_VALUE),
+    priv_(new priv(values))
+{
+}
+
+/// Getter of the content of the @ref list_property_value.
+///
+/// The content of the @ref list_property_value is a vector of
+/// strings.
+///
+/// @return the vector of strings contained in the @ref
+/// list_property_value.
+const vector<string>&
+list_property_value::get_content() const
+{return priv_->values_;}
+
+/// Setter of the content of the @ref list_property_value.
+///
+/// @param values the new content, which is a vector of strings.
+void
+list_property_value::set_content(const vector<string>& values)
+{
+  priv_->values_ = values;
+  priv_->representation_.clear();
+}
+
+/// Return a string representation of the @list_property_value.
+///
+/// @return the string representation.
+const string&
+list_property_value::as_string() const
+{
+  if (priv_->representation_.empty())
+    {
+      for (vector<string>::const_iterator i = priv_->values_.begin();
+          i != priv_->values_.end();
+          ++i)
+       {
+         if (i != priv_->values_.begin())
+           priv_->representation_ += ",";
+         priv_->representation_ += *i;
+       }
+    }
+  return priv_->representation_;
+}
+
+/// Test if an instance of @property_value is a @ref list_property_value.
+///
+/// @param v the property_value to consider.
+///
+/// @return the @ref property_value converted into a @ref
+/// list_property_value if the @p v is a @ref list_property_value, nil
+/// otherwise.
+list_property_value*
+is_list_property_value(const property_value* v)
+{return dynamic_cast<list_property_value*>(const_cast<property_value*>(v));}
+
+/// Test if an instance of @property_value is a @ref list_property_value.
+///
+/// @param v the property_value to consider.
+///
+/// @return the @ref property_value converted into a @ref
+/// list_property_value if the @p v is a @ref list_property_value, nil
+/// otherwise.
+list_property_value_sptr
+is_list_property_value(const property_value_sptr&v)
+{return dynamic_pointer_cast<list_property_value>(v);}
+
+// </list_property_value stuff>
 
 // <tuple_property_value>
 
@@ -511,6 +606,74 @@ is_simple_property(const property_sptr p)
 
 // </simple_property stuff>
 
+// <list_property stuff>
+struct list_property::priv
+{
+  list_property_value_sptr value_;
+
+  priv()
+  {}
+
+  priv(const list_property_value_sptr value)
+    : value_(value)
+  {}
+}; //end struct list_property
+
+/// Default constructor for @ref list_property.
+list_property::list_property()
+  : priv_(new priv)
+{}
+
+/// Constructor for @ref list_property.
+///
+/// @param name the name of the property.
+///
+/// @param value the value of the property.
+list_property::list_property(const string& name,
+                            const list_property_value_sptr& value)
+  : property(name),
+    priv_(new priv(value))
+{}
+
+/// Getter for the value of the @ref list_property_value
+const list_property_value_sptr&
+list_property::get_value() const
+{return priv_->value_;}
+
+/// Setter for the value of the @ref list_property.
+///
+/// @param value the new value.
+void
+list_property::set_value(const list_property_value_sptr& value)
+{priv_->value_ = value;}
+
+/// Destructor of the @ref list_property type.
+list_property::~list_property()
+{}
+
+/// Test if an instance of a @ref property is actually an instance of
+/// @ref list_property.
+///
+/// @param p the @ref property to test.
+///
+/// @return the @p p converted into a @ref list_property if it's of
+/// type @ref list_property, or nil otherwise.
+list_property*
+is_list_property(const property* p)
+{return dynamic_cast<list_property*>(const_cast<property*>(p));}
+
+/// Test if an instance of a @ref property is actually an instance of
+/// @ref list_property.
+///
+/// @param p the @ref property to test.
+///
+/// @return the @p p converted into a @ref list_property if it's of
+/// type @ref list_property, or nil otherwise.
+list_property_sptr
+is_list_property(const property_sptr p)
+{return dynamic_pointer_cast<list_property>(p);}
+// </list_property stuff>
+
 // <tuple_property stuff>
 struct tuple_property::priv
 {
@@ -684,12 +847,15 @@ class read_context
   unsigned cur_line_;
   /// The current column on the current line.
   unsigned cur_column_;
+  vector<char> buf_;
 
   // Forbid this;
   read_context();
 
 public:
 
+  /// The constructor of @ref read_context.
+  ///
   /// @param in the input stream to parse from.
   read_context(istream& in)
     : in_(in),
@@ -697,35 +863,120 @@ public:
       cur_column_(0)
   {}
 
-  /// Read the next character from the input stream.
+  /// @return the character that is going to be read by the next
+  /// invocation of read_next_char().
   ///
-  /// This method updates the current line/column number after looking
-  /// at the actual char that got read.  It also handle escaped
-  /// characters.
+  /// Note that this function doesn't alter the input stream.
   ///
-  /// @param c output parameter.  This is set by this function to the
-  /// character that was read.  It's set iff the function returned
-  /// true.
+  /// Also note that this function handles escaping using the '\'
+  /// (backslash) character.
   ///
-  /// @return true if the reading went well and if the input stream is
-  /// in a non-erratic state.
+  /// @return peeked character.
+  char
+  peek()
+  {
+    if (!buf_.empty())
+      return buf_.back();
+
+    char c = in_.peek();
+    if (handle_escape(c, /*peek=*/true))
+      put_back(c);
+    return c;
+  }
+
+  /// Get the next character of the input stream.
+  ///
+  /// This function knows how to handles escaped characters from the
+  /// input stream.
+  ///
+  /// @param do_handle_escape if yes, this function handles escaped
+  /// characters from the input stream.
+  ///
+  /// @return the next character of the input stream.
+  char
+  get(bool do_handle_escape = true)
+  {
+    char result = 0;
+    if (!buf_.empty())
+      {
+       result = buf_.back();
+       buf_.pop_back();
+      }
+    else
+      {
+       result = in_.get();
+       if (do_handle_escape)
+         handle_escape(result);
+      }
+    return result;
+  }
+
+  /// Put a character that was read from the input stream, back into
+  /// that input stream, so that a subsequent call to
+  /// read_context::get() returns that same character.
+  ///
+  /// @param c the character to put back into the stream.
+  void
+  put_back(char c)
+  {buf_.push_back(c);}
+
+  /// Test if the status of the input stream is good.
+  ///
+  /// @return true iff the status of the input stream is good.
   bool
-  read_next_char(char& c)
+  good() const
+  {
+    if (!buf_.empty())
+      return true;
+    return in_.good();
+  }
+
+  /// Tests if the input stream has reached end of file.
+  ///
+  /// @return true iff the input stream has reached end of file.
+  bool
+  eof() const
   {
-    char b = in_.get();
-    if (!in_.good())
+    if (!buf_.empty())
       return false;
+    return in_.eof();
+  }
 
-    bool escaping = false;
-    if (b == '\\')
-      escaping = true;
+  /// Handles the escaping of a character.
+  ///
+  /// This function must be called whenever the low level character
+  /// reading function encountered a backslash character ('\').  In
+  /// that case, this function reads the subsequent characters from
+  /// the input stream, sees if it needs to espace those and then
+  /// handles the escaping if need be.  Otherwise, it does nothing.
+  ///
+  /// This is a subroutine of the read_context::get() and
+  /// read_context::peek() functions.
+  ///
+  /// @param peek if true, it means this function was called after the
+  /// caller issued a read_context::peek() call, rather than a
+  /// read_context::get() call.
+  ///
+  /// @return true if an escaping took place.
+  bool
+  handle_escape(char& c, bool peek = false)
+  {
+    bool escaped = false;
+    char b = c;
 
-    // Handle escape
-    if (escaping)
+    if (b == '\\')
       {
-       b = in_.get();
-       if (!in_.good())
-         return false;
+       escaped = true;
+       b = get(/*escape=*/false);
+       if (!good())
+         return escaped;
+       if (peek)
+         {
+           assert(b == c);
+           b = get(/*escape=*/false);
+           if (!good())
+             return escaped;
+         }
 
        switch (b)
          {
@@ -744,9 +995,10 @@ public:
            // character and this end-of-line character on the floor
            // just like if they never existed.
            ++cur_column_;
-           b = in_.get();
-           if (!in_.good())
-             return false;
+           b = get(/*escape=*/false);
+           if (!good())
+             return escaped;
+           c = b;
            break;
          case '\\':
          case ';':
@@ -757,11 +1009,34 @@ public:
            c = b;
            break;
          }
-       ++cur_column_;
       }
     else
       c = b;
 
+    return escaped;
+  }
+
+  /// Read the next character from the input stream.
+  ///
+  /// This method updates the current line/column number after looking
+  /// at the actual char that got read.  Note that escaped characters
+  /// are handled transparently at this point.
+  ///
+  /// @param c output parameter.  This is set by this function to the
+  /// character that was read.  It's set iff the function returned
+  /// true.
+  ///
+  /// @return true if the reading went well and if the input stream is
+  /// in a non-erratic state.
+  bool
+  read_next_char(char& c)
+  {
+    char b = get();
+    if (!good())
+      return false;
+
+    c = b;
+
     if (cur_line_ == 0)
       cur_line_ = 1;
 
@@ -794,7 +1069,7 @@ public:
       if (c == '\n')
        break;
 
-    return (c == '\n' || in_.eof());
+    return (c == '\n' || eof());
   }
 
   /// If the current character is a white space, skip it and all the
@@ -804,12 +1079,12 @@ public:
   bool
   skip_white_spaces()
   {
-    for (char c = in_.peek(); in_.good(); c = in_.peek())
+    for (char c = peek(); good(); c = peek())
       if (char_is_white_space(c))
        assert(read_next_char(c));
       else
        break;
-    return in_.good() || in_.eof();
+    return good() || eof();
   }
 
   /// If the current character is the beginning of a comment, skip
@@ -820,12 +1095,12 @@ public:
   bool
   skip_comments()
   {
-    for (char c = in_.peek(); in_.good(); c = in_.peek())
+    for (char c = peek(); good(); c = peek())
       if (char_is_comment_start(c))
        skip_line();
       else
        break;
-    return in_.good() || in_.eof();
+    return good() || eof();
   }
 
   /// If the current character is either the beginning of a comment or
@@ -837,9 +1112,9 @@ public:
   skip_white_spaces_or_comments()
   {
     int b = 0;
-    while (in_.good())
+    while (good())
       {
-       b = in_.peek();
+       b = peek();
        if (char_is_white_space(b))
          skip_white_spaces();
        else if (char_is_comment_start(b))
@@ -847,7 +1122,7 @@ public:
        else
          break;
       }
-    return in_.good() || in_.eof();
+    return good() || eof();
   }
 
   /// Read a property name.
@@ -861,14 +1136,14 @@ public:
   bool
   read_property_name(string& name)
   {
-    char c = in_.peek();
-    if (!in_.good() || !char_is_property_name_char(c))
+    char c = peek();
+    if (!good() || !char_is_property_name_char(c))
       return false;
 
     assert(read_next_char(c));
     name += c;
 
-    for (c = in_.peek(); in_.good(); c = in_.peek())
+    for (c = peek(); good(); c = peek())
       {
        if (!char_is_property_name_char(c))
          break;
@@ -890,14 +1165,14 @@ public:
   bool
   read_function_name(string& name)
   {
-    char c = in_.peek();
-    if (!in_.good() || !char_is_function_name_char(c))
+    char c = peek();
+    if (!good() || !char_is_function_name_char(c))
       return false;
 
     assert(read_next_char(c));
     name += c;
 
-    for (c = in_.peek(); in_.good(); c = in_.peek())
+    for (c = peek(); good(); c = peek())
       {
        if (!char_is_function_name_char(c))
          break;
@@ -918,14 +1193,14 @@ public:
   bool
   read_function_argument(string& argument)
   {
-    char c = in_.peek();
-    if (!in_.good() || !char_is_function_argument_char(c))
+    char c = peek();
+    if (!good() || !char_is_function_argument_char(c))
       return false;
 
     assert(read_next_char(c));
     argument += c;
 
-    for (c = in_.peek(); in_.good(); c = in_.peek())
+    for (c = peek(); good(); c = peek())
       {
        if (!char_is_function_argument_char(c))
          break;
@@ -950,11 +1225,11 @@ public:
   bool
   read_function_call_expr(function_call_expr_sptr& expr)
   {
-    if (!in_.good())
+    if (!good())
       return false;
 
     skip_white_spaces_or_comments();
-    if (!in_.good())
+    if (!good())
       return false;
 
     string name;
@@ -963,8 +1238,8 @@ public:
 
     skip_white_spaces_or_comments();
 
-    int b = in_.peek();
-    if (!in_.good() || b != '(')
+    int b = peek();
+    if (!good() || b != '(')
       return false;
 
     char c = 0;
@@ -973,14 +1248,14 @@ public:
     assert(c == '(');
 
     skip_white_spaces_or_comments();
-    if (!in_.good())
+    if (!good())
       return false;
 
     // Read function call arguments.
     vector<string> arguments;
     for (;;)
       {
-       if (in_.peek() == ')')
+       if (peek() == ')')
          break;
 
        string arg;
@@ -988,15 +1263,15 @@ public:
          return true;
 
        skip_white_spaces_or_comments();
-       if (!in_.good())
+       if (!good())
          return false;
 
-       if (in_.peek() == ',')
+       if (peek() == ',')
          {
            c = 0;
            assert(read_next_char(c) && c == ',');
            skip_white_spaces_or_comments();
-           if (!in_.good())
+           if (!good())
              return false;
          }
 
@@ -1018,8 +1293,8 @@ public:
   {
     property_value_sptr nil, result;
 
-    int b = in_.peek();
-    if (!in_.good())
+    int b = peek();
+    if (!good())
       return nil;
 
     if (b == '{')
@@ -1028,7 +1303,45 @@ public:
          return t;
        return nil;
       }
-    return read_string_property_value();
+
+    list_property_value_sptr list = read_list_property_value();
+    if (list->get_content().size() == 1)
+      result.reset(new string_property_value(list->get_content()[0]));
+    else
+      result = list;
+
+    return result;
+  }
+
+  /// Reads a string from the input stream.
+  ///
+  /// A string is just a contiguous set of characters that test
+  /// positive when passed to
+  /// read_context::char_is_property_name_char().
+  ///
+  /// @return the string read.
+  string
+  read_string()
+  {
+    int b = peek();
+    if (!good())
+      return "";
+
+    if (char_is_delimiter(b, /*include_white_space=*/false))
+      // Empty property value.  This is accepted.
+      return "";
+
+    string v;
+    for (b = peek(); good();b = peek())
+      {
+       if (!char_is_property_value_char(b))
+         break;
+       char c = 0;
+       assert(read_next_char(c));
+       v += c;
+      }
+    string result = remove_trailing_white_spaces(v);
+    return result;
   }
 
   /// Read a string property value.
@@ -1038,31 +1351,47 @@ public:
   read_string_property_value()
   {
     string_property_value_sptr nil, result;
-    int b = in_.peek();
-    if (!in_.good())
+    if (!good())
       return nil;
 
-    if (char_is_delimiter(b, /*include_white_space=*/false))
-      {
-       // Empty property value.  This is accepted.
-       result.reset(new string_property_value);
-       return result;
-      }
+    string value = read_string();
+    result.reset(new string_property_value(value));
+    return result;
+  }
 
-    string v;
-    char c = 0;
-    assert(read_next_char(c));
-    v += c;
+  /// Read a @ref list_property_value.
+  ///
+  /// @return the instance of @ref list_property_value read, or nil if
+  /// none was read.
+  list_property_value_sptr
+  read_list_property_value()
+  {
+    list_property_value_sptr nil, result;
+    string str;
+    vector<string> content;
 
-    for (b = in_.peek(); in_.good();b = in_.peek())
+    for (;;)
       {
-       if (!char_is_property_value_char(b))
+       str = read_string();
+       if (str.empty())
          break;
-       assert(read_next_char(c));
-       v += c;
+       content.push_back(str);
+
+       skip_white_spaces();
+
+       int b = peek();
+       if (!good() || b != ',')
+         break;
+       skip_white_spaces();
+
+       char c = 0;
+       read_next_char(c);
+       assert(c == ',');
       }
-    string value = remove_trailing_white_spaces(v);
-    result.reset(new string_property_value(value));
+
+    if (!content.empty())
+      result.reset(new list_property_value(content));
+
     return result;
   }
 
@@ -1076,8 +1405,8 @@ public:
   read_tuple_property_value()
   {
     tuple_property_value_sptr nil, result;
-    int b = in_.peek();
-    if (!in_.good())
+    int b = peek();
+    if (!good())
       return nil;
 
     if (b != '{')
@@ -1088,22 +1417,23 @@ public:
 
     property_value_sptr value;
     vector<property_value_sptr> values;
-    while (in_.good() && in_.peek() != '}')
+    while (good() && peek() != '}')
       {
        skip_white_spaces();
        if ((value = read_property_value()))
          values.push_back(value);
        skip_white_spaces();
-       if (in_.good() && in_.peek() == ',')
+       if (good() && peek() == ',')
          {
            c = 0;
            read_next_char(c);
          }
       }
 
-    b = in_.peek();
+    b = peek();
     if (b != '}')
       return nil;
+
     c = 0;
     read_next_char(c);
 
@@ -1122,15 +1452,15 @@ public:
   bool
   read_section_name(string& name)
   {
-    int b = in_.peek();
-    if (!in_.good() || !char_is_section_name_char(b))
+    int b = peek();
+    if (!good() || !char_is_section_name_char(b))
       return false;
 
     char c = 0;
     assert(read_next_char(c) || char_is_section_name_char(b));
     name += c;
 
-    for (b = in_.peek(); in_.good(); b = in_.peek())
+    for (b = peek(); good(); b = peek())
       {
        if (!char_is_section_name_char(b))
          break;
@@ -1155,7 +1485,7 @@ public:
       return nil;
 
     skip_white_spaces();
-    if (!in_.good())
+    if (!good())
       return nil;
 
     char c = 0;
@@ -1163,7 +1493,7 @@ public:
       return nil;
 
     skip_white_spaces();
-    if (!in_.good())
+    if (!good())
       return nil;
 
     property_value_sptr value = read_property_value();
@@ -1173,12 +1503,13 @@ public:
     property_sptr result;
     if (tuple_property_value_sptr tv = is_tuple_property_value(value))
       result.reset(new tuple_property(name, tv));
+    else if (list_property_value_sptr lv = is_list_property_value(value))
+      result.reset(new list_property(name, lv));
+    else if (string_property_value_sptr sv = is_string_property_value(value))
+      result.reset(new simple_property(name, sv));
     else
-      {
-       string_property_value_sptr sv = is_string_property_value(value);
-       assert(sv);
-       result.reset(new simple_property(name, sv));
-      }
+      // This new kind of property is not yet supported!
+      std::abort();
 
     return result;
   }
@@ -1192,8 +1523,8 @@ public:
   {
     config::section_sptr nil;
 
-    int b = in_.peek();
-    if (!in_.good())
+    int b = peek();
+    if (!good())
       return nil;
 
     char c = 0;
@@ -1423,12 +1754,13 @@ write_property_value(const property_sptr& prop)
   string result;
   if (simple_property_sptr simple_prop = is_simple_property(prop))
     result = simple_prop->get_value()->as_string();
-  else
-    {
-      tuple_property_sptr tuple_prop = is_tuple_property(prop);
-      assert(tuple_prop);
+  else if (list_property_sptr list_prop = is_list_property(prop))
+    result = list_prop->get_value()->as_string();
+  else if (tuple_property_sptr tuple_prop = is_tuple_property(prop))
       result = tuple_prop->get_value()->as_string();
-    }
+  else
+    // This new kind of property is not yet supported!
+    abort();
     return result;
 }
 
index 708538f65a20664bc4a431e36bcd53f002e98f3e..145ef3b21b91985c77f798d0d76ae0e32741f3b8 100644 (file)
@@ -757,6 +757,18 @@ test-diff-suppr/test25-typedef-report-0.txt \
 test-diff-suppr/test25-typedef-report-1.txt\
 test-diff-suppr/test25-typedef-v0.c \
 test-diff-suppr/test25-typedef-v1.c \
+test-diff-suppr/libtest26-loc-suppr-v0.so \
+test-diff-suppr/libtest26-loc-suppr-v1.so \
+test-diff-suppr/test26-loc-suppr-0.suppr \
+test-diff-suppr/test26-loc-suppr-1.suppr \
+test-diff-suppr/test26-loc-suppr-2.suppr \
+test-diff-suppr/test26-loc-suppr-report-0.txt \
+test-diff-suppr/test26-loc-suppr-report-1.txt \
+test-diff-suppr/test26-loc-suppr-report-2.txt \
+test-diff-suppr/test26-loc-suppr-report-3.txt \
+test-diff-suppr/test26-loc-suppr-v0.cc \
+test-diff-suppr/test26-loc-suppr-v1.cc \
+test-diff-suppr/test26-loc-suppr.h \
 \
 test-lookup-syms/test0.cc              \
 test-lookup-syms/test0.o               \
diff --git a/tests/data/test-diff-suppr/libtest26-loc-suppr-v0.so b/tests/data/test-diff-suppr/libtest26-loc-suppr-v0.so
new file mode 100755 (executable)
index 0000000..c3d8320
Binary files /dev/null and b/tests/data/test-diff-suppr/libtest26-loc-suppr-v0.so differ
diff --git a/tests/data/test-diff-suppr/libtest26-loc-suppr-v1.so b/tests/data/test-diff-suppr/libtest26-loc-suppr-v1.so
new file mode 100755 (executable)
index 0000000..bb6ab5a
Binary files /dev/null and b/tests/data/test-diff-suppr/libtest26-loc-suppr-v1.so differ
diff --git a/tests/data/test-diff-suppr/test26-loc-suppr-0.suppr b/tests/data/test-diff-suppr/test26-loc-suppr-0.suppr
new file mode 100644 (file)
index 0000000..263e980
--- /dev/null
@@ -0,0 +1,2 @@
+[suppress_type]
+  source_location_not_in = test26-loc-suppr.h
diff --git a/tests/data/test-diff-suppr/test26-loc-suppr-1.suppr b/tests/data/test-diff-suppr/test26-loc-suppr-1.suppr
new file mode 100644 (file)
index 0000000..b0c0675
--- /dev/null
@@ -0,0 +1,2 @@
+[suppress_type]
+  source_location_not_regexp = test26-loc.*\\.h
diff --git a/tests/data/test-diff-suppr/test26-loc-suppr-2.suppr b/tests/data/test-diff-suppr/test26-loc-suppr-2.suppr
new file mode 100644 (file)
index 0000000..282702c
--- /dev/null
@@ -0,0 +1,2 @@
+[suppress_type]
+  source_location_not_regexp = test26-loc.*\\.cc
diff --git a/tests/data/test-diff-suppr/test26-loc-suppr-report-0.txt b/tests/data/test-diff-suppr/test26-loc-suppr-report-0.txt
new file mode 100644 (file)
index 0000000..c2ba8c2
--- /dev/null
@@ -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 void func0(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 bits
+        1 data member insertion:
+          'char S::added_member', at offset 32 (in bits)
+
+
diff --git a/tests/data/test-diff-suppr/test26-loc-suppr-report-1.txt b/tests/data/test-diff-suppr/test26-loc-suppr-report-1.txt
new file mode 100644 (file)
index 0000000..9666a8f
--- /dev/null
@@ -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-diff-suppr/test26-loc-suppr-report-2.txt b/tests/data/test-diff-suppr/test26-loc-suppr-report-2.txt
new file mode 100644 (file)
index 0000000..9666a8f
--- /dev/null
@@ -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-diff-suppr/test26-loc-suppr-report-3.txt b/tests/data/test-diff-suppr/test26-loc-suppr-report-3.txt
new file mode 100644 (file)
index 0000000..c2ba8c2
--- /dev/null
@@ -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 void func0(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 bits
+        1 data member insertion:
+          'char S::added_member', at offset 32 (in bits)
+
+
diff --git a/tests/data/test-diff-suppr/test26-loc-suppr-v0.cc b/tests/data/test-diff-suppr/test26-loc-suppr-v0.cc
new file mode 100644 (file)
index 0000000..fab6688
--- /dev/null
@@ -0,0 +1,15 @@
+// Compile this with:
+//   g++ -g -Wall -shared -o libtest26-loc-suppr-v0.so test26-loc-suppr-v0.cc
+//
+
+#include "test26-loc-suppr.h"
+
+struct S
+{
+  int m0;
+};
+
+void
+func0(S*)
+{
+}
diff --git a/tests/data/test-diff-suppr/test26-loc-suppr-v1.cc b/tests/data/test-diff-suppr/test26-loc-suppr-v1.cc
new file mode 100644 (file)
index 0000000..e740030
--- /dev/null
@@ -0,0 +1,16 @@
+// Compile this with:
+//   g++ -g -Wall -shared -o libtest26-loc-suppr-v1.so test26-loc-suppr-v1.cc
+//
+
+#include "test26-loc-suppr.h"
+
+struct S
+{
+  int m0;
+  char added_member;
+};
+
+void
+func0(S*)
+{
+}
diff --git a/tests/data/test-diff-suppr/test26-loc-suppr.h b/tests/data/test-diff-suppr/test26-loc-suppr.h
new file mode 100644 (file)
index 0000000..0e44eb4
--- /dev/null
@@ -0,0 +1,3 @@
+struct S;
+
+void func0(S*);
index 2973840a1fc20373e40cd1f5f7b88b1dbed328f3..cb8d07dc573039c72db28ae0ad2d91fb728b9bef 100644 (file)
@@ -927,6 +927,38 @@ InOutSpec in_out_specs[] =
     "data/test-diff-suppr/test25-typedef-report-1.txt",
     "output/test-diff-suppr/test25-typedef-report-1.txt"
   },
+  {
+    "data/test-diff-suppr/libtest26-loc-suppr-v0.so",
+    "data/test-diff-suppr/libtest26-loc-suppr-v1.so",
+    "",
+    "--no-redundant",
+    "data/test-diff-suppr/test26-loc-suppr-report-0.txt",
+    "output/test-diff-suppr/test26-loc-suppr-report-0.txt"
+  },
+  {
+    "data/test-diff-suppr/libtest26-loc-suppr-v0.so",
+    "data/test-diff-suppr/libtest26-loc-suppr-v1.so",
+    "data/test-diff-suppr/test26-loc-suppr-0.suppr",
+    "--no-redundant",
+    "data/test-diff-suppr/test26-loc-suppr-report-1.txt",
+    "output/test-diff-suppr/test26-loc-suppr-report-1.txt"
+  },
+  {
+    "data/test-diff-suppr/libtest26-loc-suppr-v0.so",
+    "data/test-diff-suppr/libtest26-loc-suppr-v1.so",
+    "data/test-diff-suppr/test26-loc-suppr-1.suppr",
+    "--no-redundant",
+    "data/test-diff-suppr/test26-loc-suppr-report-2.txt",
+    "output/test-diff-suppr/test26-loc-suppr-report-2.txt"
+  },
+  {
+    "data/test-diff-suppr/libtest26-loc-suppr-v0.so",
+    "data/test-diff-suppr/libtest26-loc-suppr-v1.so",
+    "data/test-diff-suppr/test26-loc-suppr-2.suppr",
+    "--no-redundant",
+    "data/test-diff-suppr/test26-loc-suppr-report-3.txt",
+    "output/test-diff-suppr/test26-loc-suppr-report-3.txt"
+  },
   // This should be the last entry
   {NULL, NULL, NULL, NULL, NULL, NULL}
 };
This page took 0.079693 seconds and 5 git commands to generate.