Leading and trailing white spaces are ignored around property
name and values.
+.. _suppr_regexp_label:
+
+ * Regular expressions
+
+ The value of some properties might be a regular expression. In
+ that case, they must comply with the syntax of `extended POSIX
+ regular expressions
+ <http://www.gnu.org/software/findutils/manual/html_node/find_html/posix_002dextended-regular-expression-syntax.html#posix_002dextended-regular-expression-syntax>`_.
+ Note that Libabigail uses the regular expression engine of the
+ `GNU C Library`_.
+
+ * Escaping a character in a regular expression
+
+ When trying to match a string that contains a ``*`` character,
+ like int the pointer type ``int*``, one must be careful to
+ notice that the character ``*`` is a special character in the
+ extended POSIX regular expression syntax. And that character
+ must be escaped for the regular expression engine. Thus the
+ regular expression that would match the string ``int*`` in a
+ suppression file should be ::
+
+ int\\*
+
+ Wait; but then why the two ``\`` characters? Well, because the
+ ``\`` character is a special character in the `Ini File Syntax`_
+ used for specifying suppressions. So it must be escaped as
+ well, so that the Ini File parser leaves a ``\`` character
+ intact in the data stream that is handed to the regular
+ expression engine. Hence the ``\\`` targeted at the Ini File
+ parser.
+
+ So, in short, to escape a character in a regular expression,
+ always prefix the character with the ``\\`` sequence.
+
* Sections
Properties are then grouped into arbitrarily named sections that
Suppresses report messages about a type change. The potential
properties of this sections are:
- * ``name_regexp`` ``=`` <regular-expression>
+ * ``name_regexp`` ``=`` <:ref:`regular-expression <suppr_regexp_label>`>
Suppresses change reports involving types whose name
matches the regular expression specified as value of this
native) types. Example of built-in types are char,
int, unsigned int, etc.
+.. _suppr_label_property_label:
* ``label`` ``=`` <some-value>
informative string that might be used by abidiff to refer
to a type suppression in error messages.
+ * ``[suppress_function]``
+
+ Suppresses report messages about a change the sub-types of a
+ function. The potential properties of this sections are:
+
+ * ``label`` ``=`` <some-value>
+
+ This property is the same as the :ref:`label property
+ <suppr_label_property_label>` defined above.
+
+ * ``name`` ``=`` <some-value>
+
+ Suppresses change reports involving functions whose name
+ equals the value of this property.
+
+ * ``name_regexp`` ``=`` <:ref:`regular-expression <suppr_regexp_label>`>
+
+ Suppresses change reports involving functions whose name
+ matches the regular expression specified as value of this
+ property.
+
+ * ``parameter`` ``=`` <function-parameter-specification>
+
+ Suppresses change reports involving functions whose
+ parameters match the parameter specification indicated as
+ value of this property.
+
+ The format of the function parameter specification is:
+
+ ``'``<parameter-index> `` `` <type-name-or-regular-expression>
+
+ That is, an apostrophe followed by a number that is the
+ index of the parameter, followed by one of several spaces,
+ followed by either the name of the type of the parameter,
+ or a regular expression describing a family of parameter
+ type names.
+
+ If the parameter type name is designated by a regular
+ expression, then said regular expression must be enclosed
+ between two slashes; like ``/some-regular-expression/``.
+
+ The index of the first parameter of the function is zero.
+ Note that for member functions (methods of classes), the
+ this is the first parameter that comes after the implicit
+ "this" pointer parameter.
+
+ Examples of function parameter specifications are: ::
+
+ '0 int
+
+ Which means, the parameter at index 0, whose type name is
+ ``int``. ::
+
+ '4 unsigned char*
+
+ Which means, the parameter at index 4, whose type name is
+ ``unsigned char*``. ::
+
+ '2 /^foo.*&/
+
+ Which means, the parameter at index 2, whose type name
+ starts with the string "foo" and ends with an '&'. In
+ other words, this is the third parameter and it's a
+ reference on a type that starts with the string "foo".
+
+ * ``return_type_name`` ``=`` <some-value>
+
+ Suppresses change reports involving functions whose return
+ type name equals the value of this property.
+
+ * ``return_type_regexp`` ``=`` <:ref:`regular-expression <suppr_regexp_label>`>
+
+ Suppresses change reports involving functions whose return
+ type name matches the regular expression specified as
+ value of this property.
+
+ * ``symbol_name`` ``=`` <some-value>
+
+ Suppresses change reports involving functions whose symbol
+ name equals the value of this property.
+
+ * ``symbol_name_regexp`` ``=`` <:ref:`regular-expression <suppr_regexp_label>`>
+
+ Suppresses change reports involving functions whose symbol
+ name matches the regular expression specified as value of
+ this property.
+
+ * ``symbol_version`` ``=`` <some-value>
+
+ Suppresses change reports involving functions whose symbol
+ version equals the value of this property.
+
+ * ``symbol_version_regexp`` ``=`` <:ref:`regular-expression <suppr_regexp_label>`>
+
+ Suppresses change reports involving functions whose symbol
+ version matches the regular expression specified as value
+ of this property.
+
* Comments
``;`` or ``#`` ASCII character at the beginning of a line
* Code examples
- Suppose we have a library named ``libtest1-v0.so`` which contains this
- very useful code: ::
+ 1. Suppressing change reports about types.
+
+ Suppose we have a library named ``libtest1-v0.so`` which
+ contains this very useful code: ::
$ cat -n test1-v0.cc
1 // A forward declaration for a type considered to be opaque to
As you can see, ``abidiff`` does not report the change anymore; it
tells us that it was filtered out instead.
+ 2. Suppressing change reports about functions.
+
+ Suppose we have a first version a library named
+ ``libtest2-v0.so`` whose source code is: ::
+
+ $ cat -n test2-v0.cc
+
+ 1 struct S1
+ 2 {
+ 3 int m0;
+ 4
+ 5 S1()
+ 6 : m0()
+ 7 {}
+ 8 };
+ 9
+ 10 struct S2
+ 11 {
+ 12 int m0;
+ 13
+ 14 S2()
+ 15 : m0()
+ 16 {}
+ 17 };
+ 18
+ 19 struct S3
+ 20 {
+ 21 int m0;
+ 22
+ 23 S3()
+ 24 : m0()
+ 25 {}
+ 26 };
+ 27
+ 28 int
+ 29 func(S1&)
+ 30 {
+ 31 // suppose the code does something with the argument.
+ 32 return 0;
+ 33
+ 34 }
+ 35
+ 36 char
+ 37 func(S2*)
+ 38 {
+ 39 // suppose the code does something with the argument.
+ 40 return 0;
+ 41 }
+ 42
+ 43 unsigned
+ 44 func(S3)
+ 45 {
+ 46 // suppose the code does something with the argument.
+ 47 return 0;
+ 48 }
+ $
+
+ And then we come up with a second version ``libtest2-v1.so`` of
+ that library; the source code is modified by making the
+ structures ``S1``, ``S2``, ``S3`` inherit another struct: ::
+
+ $ cat -n test2-v1.cc
+ 1 struct base_type
+ 2 {
+ 3 int m_inserted;
+ 4 };
+ 5
+ 6 struct S1 : public base_type // <--- S1 now has base_type as its base
+ 7 // type.
+ 8 {
+ 9 int m0;
+ 10
+ 11 S1()
+ 12 : m0()
+ 13 {}
+ 14 };
+ 15
+ 16 struct S2 : public base_type // <--- S2 now has base_type as its base
+ 17 // type.
+ 18 {
+ 19 int m0;
+ 20
+ 21 S2()
+ 22 : m0()
+ 23 {}
+ 24 };
+ 25
+ 26 struct S3 : public base_type // <--- S3 now has base_type as its base
+ 27 // type.
+ 28 {
+ 29 int m0;
+ 30
+ 31 S3()
+ 32 : m0()
+ 33 {}
+ 34 };
+ 35
+ 36 int
+ 37 func(S1&)
+ 38 {
+ 39 // suppose the code does something with the argument.
+ 40 return 0;
+ 41
+ 42 }
+ 43
+ 44 char
+ 45 func(S2*)
+ 46 {
+ 47 // suppose the code does something with the argument.
+ 48 return 0;
+ 49 }
+ 50
+ 51 unsigned
+ 52 func(S3)
+ 53 {
+ 54 // suppose the code does something with the argument.
+ 55 return 0;
+ 56 }
+ $
+
+ Now let's build the two libraries: ::
+
+ g++ -Wall -g -shared -o libtest2-v0.so test2-v0.cc
+ g++ -Wall -g -shared -o libtest2-v0.so test2-v0.cc
+
+ Let's look at the output of ``abidiff``: ::
+
+ $ abidiff libtest2-v0.so libtest2-v1.so
+ Functions changes summary: 0 Removed, 3 Changed, 0 Added functions
+ Variables changes summary: 0 Removed, 0 Changed, 0 Added variable
+
+ 3 functions with some indirect sub-type change:
+
+ [C]'function unsigned int func(S3)' has some indirect sub-type changes:
+ parameter 0 of type 'struct S3' has sub-type changes:
+ size changed from 32 to 64 bits
+ 1 base class insertion:
+ struct base_type
+ 1 data member change:
+ 'int S3::m0' offset changed from 0 to 32
+
+ [C]'function char func(S2*)' has some indirect sub-type changes:
+ parameter 0 of type 'S2*' has sub-type changes:
+ in pointed to type 'struct S2':
+ size changed from 32 to 64 bits
+ 1 base class insertion:
+ struct base_type
+ 1 data member change:
+ 'int S2::m0' offset changed from 0 to 32
+
+ [C]'function int func(S1&)' has some indirect sub-type changes:
+ parameter 0 of type 'S1&' has sub-type changes:
+ in referenced type 'struct S1':
+ size changed from 32 to 64 bits
+ 1 base class insertion:
+ struct base_type
+ 1 data member change:
+ 'int S1::m0' offset changed from 0 to 32
+ $
+
+ Let's tell ``abidiff`` to avoid showing us the differences on the
+ overloads of ``func`` that takes either a pointer or a reference.
+ For that, we author this simple suppression specification: ::
+
+ $ cat -n libtest2.suppr
+ 1 [suppress_function]
+ 2 name = func
+ 3 parameter = '0 S1&
+ 4
+ 5 [suppress_function]
+ 6 name = func
+ 7 parameter = '0 S2*
+ $
+
+ And then let's invoke ``abidiff`` with the suppression
+ specification: ::
+
+ $ ../build/tools/abidiff --suppressions libtest2.suppr libtest2-v0.so libtest2-v1.so
+ Functions changes summary: 0 Removed, 1 Changed (2 filtered out), 0 Added function
+ Variables changes summary: 0 Removed, 0 Changed, 0 Added variable
+
+ 1 function with some indirect sub-type change:
+
+ [C]'function unsigned int func(S3)' has some indirect sub-type changes:
+ parameter 0 of type 'struct S3' has sub-type changes:
+ size changed from 32 to 64 bits
+ 1 base class insertion:
+ struct base_type
+ 1 data member change:
+ 'int S3::m0' offset changed from 0 to 32
+
+
+ The suppression specification could be reduced using
+ :ref:`regular expressions <suppr_regexp_label>`: ::
+
+ $ cat -n libtest2-1.suppr
+ 1 [suppress_function]
+ 2 name = func
+ 3 parameter = '0 /^S.(&|\\*)/
+ $
+
+ $ ../build/tools/abidiff --suppressions libtest2-1.suppr libtest2-v0.so libtest2-v1.so
+ Functions changes summary: 0 Removed, 1 Changed (2 filtered out), 0 Added function
+ Variables changes summary: 0 Removed, 0 Changed, 0 Added variable
+
+ 1 function with some indirect sub-type change:
+
+ [C]'function unsigned int func(S3)' has some indirect sub-type changes:
+ parameter 0 of type 'struct S3' has sub-type changes:
+ size changed from 32 to 64 bits
+ 1 base class insertion:
+ struct base_type
+ 1 data member change:
+ 'int S3::m0' offset changed from 0 to 32
+
+ $
.. _ELF: http://en.wikipedia.org/wiki/Executable_and_Linkable_Format
.. _Ini File Syntax: http://en.wikipedia.org/wiki/INI_file
+
+.. _GNU C Library: http://www.gnu.org/software/libc
/// This contains the implementation of the comparison engine of
/// libabigail.
+#include <ctype.h>
#include <algorithm>
#include <sstream>
#include "abg-hash.h"
{return static_cast<visiting_kind>(static_cast<unsigned>(l)
| static_cast<unsigned>(r));}
+visiting_kind
+operator&(visiting_kind l, visiting_kind r)
+{
+ return static_cast<visiting_kind>(static_cast<unsigned>(l)
+ & static_cast<unsigned>(r));
+}
+
+visiting_kind
+operator~(visiting_kind l)
+{return static_cast<visiting_kind>(~static_cast<unsigned>(l));}
+
/// This is a subroutine of a *::report() function.
///
/// If the diff about two subjects S1 and S2 was reported earlier or
suppression_base::~suppression_base()
{}
+
+static type_suppression_sptr
+read_type_suppression(const ini::config::section& section);
+
+static function_suppression_sptr
+read_function_suppression(const ini::config::section& section);
+
+/// Read a vector of suppression specifications from the sections of
+/// an ini::config.
+///
+/// Note that each time a new kind of suppression specification is
+/// added, this function needs to be updated.
+///
+/// @param config the config to read from.
+///
+/// @param suppressions out parameter. The vector of suppressions to
+/// append the newly read suppressions to.
+static void
+read_suppressions(const ini::config& config,
+ suppressions_type& suppressions)
+{
+ suppression_sptr s;
+ for (ini::config::sections_type::const_iterator i =
+ config.get_sections().begin();
+ i != config.get_sections().end();
+ ++i)
+ if ((s = read_type_suppression(**i))
+ || (s = read_function_suppression(**i)))
+ suppressions.push_back(s);
+
+}
+
+/// Read suppressions specifications from an input stream.
+///
+/// @param input the input stream to read from.
+///
+/// @param suppressions the vector of suppressions to append the newly
+/// read suppressions to.
+void
+read_suppressions(std::istream& input,
+ suppressions_type& suppressions)
+{
+ if (ini::config_sptr config = ini::read_config(input))
+ read_suppressions(*config, suppressions);
+}
+
+/// Read suppressions specifications from an input file on disk.
+///
+/// @param input the path to the input file to read from.
+///
+/// @param suppressions the vector of suppressions to append the newly
+/// read suppressions to.
+void
+read_suppressions(const string& file_path,
+ suppressions_type& suppressions)
+{
+ if (ini::config_sptr config = ini::read_config(file_path))
+ read_suppressions(*config, suppressions);
+}
// </suppression_base stuff>
// <type_suppression stuff>
suppressions.push_back(s);
}
-/// Read a vector of suppression specifications from the sections of
-/// an ini::config.
-///
-/// @param config the config to read from.
-///
-/// @param suppressions out parameter. The vector of suppressions to
-/// append the newly read suppressions to.
-static void
-read_suppressions(const ini::config& config,
- suppressions_type& suppressions)
-{
- for (ini::config::sections_type::const_iterator i =
- config.get_sections().begin();
- i != config.get_sections().end();
- ++i)
- if (suppression_sptr s = read_type_suppression(**i))
- suppressions.push_back(s);
-}
-
/// Read type suppressions specifications from an input stream.
///
/// @param input the input stream to read from.
read_type_suppressions(*config, suppressions);
}
-/// Read suppressions specifications from an input stream.
+// <function_suppression stuff>
+class function_suppression::parameter_spec::priv
+{
+ friend class function_suppression::parameter_spec;
+ friend class function_suppression;
+
+ size_t index_;
+ string type_name_;
+ string type_name_regex_str_;
+ mutable sptr_utils::regex_t_sptr type_name_regex_;
+
+ priv()
+ : index_()
+ {}
+
+ priv(size_t i, const string& tn)
+ : index_(i), type_name_(tn)
+ {}
+
+ priv(size_t i, const string& tn, const string& tn_regex)
+ : index_(i), type_name_(tn), type_name_regex_str_(tn_regex)
+ {}
+
+ const sptr_utils::regex_t_sptr
+ get_type_name_regex() const
+ {
+ if (!type_name_regex_ && !type_name_regex_str_.empty())
+ {
+ sptr_utils::regex_t_sptr r(new regex_t);
+ if (regcomp(r.get(),
+ type_name_regex_str_.c_str(),
+ REG_EXTENDED) == 0)
+ type_name_regex_ = r;
+ }
+ return type_name_regex_;
+ }
+}; // end class function_suppression::parameter_spec::priv
+
+/// Constructor for the @ref the function_suppression::parameter_spec
+/// type.
///
-/// @param input the input stream to read from.
+/// @param i the index of the parameter designated by this specification.
///
-/// @param suppressions the vector of suppressions to append the newly
-/// read suppressions to.
+/// @param tn the type name of the parameter designated by this specification.
+///
+/// @param tn_regex a regular expression that defines a set of type
+/// names for the parameter designated by this specification. Note
+/// that at evaluation time, this regular expression is taken in
+/// account only if the parameter @p tn is empty.
+function_suppression::parameter_spec::parameter_spec(size_t i,
+ const string& tn,
+ const string& tn_regex)
+ : priv_(new priv(i, tn, tn_regex))
+{}
+
+/// Getter for the index of the parameter designated by this
+/// specification.
+///
+/// @return the index of the parameter designated by this
+/// specification.
+size_t
+function_suppression::parameter_spec::get_index() const
+{return priv_->index_;}
+
+/// Setter for the index of the parameter designated by this
+/// specification.
+///
+/// @param i the new index to set.
void
-read_suppressions(std::istream& input,
- suppressions_type& suppressions)
+function_suppression::parameter_spec::set_index(size_t i)
+{priv_->index_ = i;}
+
+/// Getter for the type name of the parameter designated by this specification.
+///
+/// @return the type name of the parameter.
+const string&
+function_suppression::parameter_spec::get_parameter_type_name() const
+{return priv_->type_name_;}
+
+/// Setter for the type name of the parameter designated by this
+/// specification.
+///
+/// @param tn new parameter type name to set.
+void
+function_suppression::parameter_spec::set_parameter_type_name(const string& tn)
+{priv_->type_name_ = tn;}
+
+/// Getter for the regular expression that defines a set of type names
+/// for the parameter designated by this specification.
+///
+/// Note that at evaluation time, this regular expression is taken in
+/// account only if the name of the parameter as returned by
+/// function_suppression::parameter_spec::get_parameter_type_name() is
+/// empty.
+///
+/// @return the regular expression or the parameter type name.
+const string&
+function_suppression::parameter_spec::get_parameter_type_name_regex_str() const
+{return priv_->type_name_regex_str_;}
+
+/// Setter for the regular expression that defines a set of type names
+/// for the parameter designated by this specification.
+///
+/// Note that at evaluation time, this regular expression is taken in
+/// account only if the name of the parameter as returned by
+/// function_suppression::parameter_spec::get_parameter_type_name() is
+/// empty.
+///
+/// @param type_name_regex_str the new type name regular expression to
+/// set.
+void
+function_suppression::parameter_spec::set_parameter_type_name_regex_str
+(const string& type_name_regex_str)
+{priv_->type_name_regex_str_ = type_name_regex_str;}
+
+/// The type of the private data of the @ref function_suppression
+/// type.
+class function_suppression::priv
+{
+ friend function_suppression;
+
+ string name_;
+ string name_regex_str_;
+ mutable sptr_utils::regex_t_sptr name_regex_;
+ string return_type_name_;
+ string return_type_regex_str_;
+ mutable sptr_utils::regex_t_sptr return_type_regex_;
+ parameter_specs_type parm_specs_;
+ string symbol_name_;
+ string symbol_name_regex_str_;
+ mutable sptr_utils::regex_t_sptr symbol_name_regex_;
+ string symbol_version_;
+ string symbol_version_regex_str_;
+ mutable sptr_utils::regex_t_sptr symbol_version_regex_;
+
+ priv(const string& name,
+ const string& name_regex_str,
+ const string& return_type_name,
+ const string& return_type_regex_str,
+ const parameter_specs_type& parm_specs,
+ const string& symbol_name,
+ const string& symbol_name_regex_str,
+ const string& symbol_version,
+ const string& symbol_version_regex_str)
+ : name_(name),
+ name_regex_str_(name_regex_str),
+ return_type_name_(return_type_name),
+ return_type_regex_str_(return_type_regex_str),
+ parm_specs_(parm_specs),
+ symbol_name_(symbol_name),
+ symbol_name_regex_str_(symbol_name_regex_str),
+ symbol_version_(symbol_version),
+ symbol_version_regex_str_(symbol_version_regex_str)
+ {}
+
+
+ /// Getter for a pointer to a regular expression object built from
+ /// the regular expression string
+ /// function_suppression::priv::name_regex_str_.
+ ///
+ /// If that string is empty, then an empty regular expression object
+ /// pointer is returned.
+ ///
+ /// @return a pointer to the regular expression object of
+ /// function_suppression::priv::name_regex_str_..
+ const sptr_utils::regex_t_sptr
+ get_name_regex() const
+ {
+ if (!name_regex_ && !name_regex_str_.empty())
+ {
+ sptr_utils::regex_t_sptr r(new regex_t);
+ if (regcomp(r.get(),
+ name_regex_str_.c_str(),
+ REG_EXTENDED) == 0)
+ name_regex_ = r;
+ }
+ return name_regex_;
+ }
+
+ /// Getter for a pointer to a regular expression object built from
+ /// the regular expression string
+ /// function_suppression::priv::return_type_regex_str_.
+ ///
+ /// If that string is empty, then an empty regular expression object
+ /// pointer is returned.
+ ///
+ /// @return a pointer to the regular expression object of
+ /// function_suppression::priv::return_type_regex_str_.
+ const sptr_utils::regex_t_sptr
+ get_return_type_regex() const
+ {
+ if (!return_type_regex_ && !return_type_regex_str_.empty())
+ {
+ sptr_utils::regex_t_sptr r(new regex_t);
+ if (regcomp(r.get(),
+ return_type_regex_str_.c_str(),
+ REG_EXTENDED) == 0)
+ return_type_regex_ = r;
+ }
+ return return_type_regex_;
+ }
+
+ /// Getter for a pointer to a regular expression object built from
+ /// the regular expression string
+ /// function_suppression::priv::symbol_name_regex_str_.
+ ///
+ /// If that string is empty, then an empty regular expression object
+ /// pointer is returned.
+ ///
+ /// @return a pointer to the regular expression object of
+ /// function_suppression::priv::symbol_name_regex_str_.
+ const sptr_utils::regex_t_sptr
+ get_symbol_name_regex() const
+ {
+ if (!symbol_name_regex_ && !symbol_name_regex_str_.empty())
+ {
+ sptr_utils::regex_t_sptr r(new regex_t);
+ if (regcomp(r.get(),
+ symbol_name_regex_str_.c_str(),
+ REG_EXTENDED) == 0)
+ symbol_name_regex_ = r;
+ }
+ return symbol_name_regex_;
+ }
+
+ /// Getter for a pointer to a regular expression object built from
+ /// the regular expression string
+ /// function_suppression::priv::symbol_version_regex_str_.
+ ///
+ /// If that string is empty, then an empty regular expression object
+ /// pointer is returned.
+ ///
+ /// @return a pointer to the regular expression object of
+ /// function_suppression::priv::symbol_version_regex_str_.
+ const sptr_utils::regex_t_sptr
+ get_symbol_version_regex() const
+ {
+ if (!symbol_version_regex_ && ! symbol_version_regex_str_.empty())
+ {
+ sptr_utils::regex_t_sptr r(new regex_t);
+ if (regcomp(r.get(),
+ symbol_version_regex_str_.c_str(),
+ REG_EXTENDED) == 0)
+ symbol_version_regex_ = r;
+ }
+ return symbol_version_regex_;
+ }
+}; // end class function_suppression::priv
+
+/// Constructor for the @ref function_suppression type.
+///
+/// @param label an informative text string that the evalution code
+/// might use to designate this function suppression specification in
+/// error messages. This parameter might be empty, in which case it's
+/// ignored at evaluation time.
+///
+/// @param the name of the function the user wants the current
+/// specification to designate. This parameter might be empty, in
+/// which case it's ignored at evaluation time.
+///
+/// @param nr if @p name is empty this parameter is a regular
+/// expression for a family of names of functions the user wants the
+/// current specification to designate. If @p name is not empty, this
+/// parameter is ignored at specification evaluation time. This
+/// parameter might be empty, in which case it's ignored at evaluation
+/// time.
+///
+/// @param ret_tn the name of the return type of the function the user
+/// wants this specification to designate. This parameter might be
+/// empty, in which case it's ignored at evaluation time.
+///
+/// @param ret_tr if @p ret_tn is empty, then this is a regular
+/// expression for a family of return type names for functions the
+/// user wants the current specification to designate. If @p ret_tn
+/// is not empty, then this parameter is ignored at specification
+/// evaluation time. This parameter might be empty, in which case
+/// it's ignored at evaluation time.
+///
+/// @param ps a vector of parameter specifications to specify
+/// properties of the parameters of the functions the user wants this
+/// specification to designate. This parameter might be empty, in
+/// which case it's ignored at evaluation time.
+///
+/// @param sym_n the name of symbol of the function the user wants
+/// this specification to designate. This parameter might be empty,
+/// in which case it's ignored at evaluation time.
+///
+/// @param sym_nr if the parameter @p sym_n is empty, then this
+/// parameter is a regular expression for a family of names of symbols
+/// of functions the user wants this specification to designate. If
+/// the parameter @p sym_n is not empty, then this parameter is
+/// ignored at specification evaluation time. This parameter might be
+/// empty, in which case it's ignored at evaluation time.
+///
+/// @param sym_v the name of the version of the symbol of the function
+/// the user wants this specification to designate. This parameter
+/// might be empty, in which case it's ignored at evaluation time.
+///
+/// @param sym_vr if the parameter @p sym_v is empty, then this
+/// parameter is a regular expression for a family of versions of
+/// symbols of functions the user wants the current specification to
+/// designate. If the parameter @p sym_v is non empty, then this
+/// parameter is ignored. This parameter might be empty, in which
+/// case it's ignored at evaluation time.
+function_suppression::function_suppression(const string& label,
+ const string& name,
+ const string& nr,
+ const string& ret_tn,
+ const string& ret_tr,
+ parameter_specs_type& ps,
+ const string& sym_n,
+ const string& sym_nr,
+ const string& sym_v,
+ const string& sym_vr)
+ : suppression_base(label),
+ priv_(new priv(name, nr, ret_tn, ret_tr, ps,
+ sym_n, sym_nr, sym_v, sym_vr))
+{}
+
+function_suppression::~function_suppression()
+{}
+
+/// Getter for the name of the function the user wants the current
+/// specification to designate. This might be empty, in which case
+/// it's ignored at evaluation time.
+///
+/// @return the name of the function.
+const string&
+function_suppression::get_function_name() const
+{return priv_->name_;}
+
+/// Setter for the name of the function the user wants the current
+/// specification to designate. This might be empty, in which case
+/// it's ignored at evaluation time.
+///
+/// @param n the new function name to set.
+void
+function_suppression::set_function_name(const string& n)
+{priv_->name_ = n;}
+
+/// Getter for a regular expression for a family of names of functions
+/// the user wants the current specification to designate.
+///
+/// If the function name as returned by
+/// function_suppression::get_function_name() is not empty, this
+/// property is ignored at specification evaluation time. This
+/// property might be empty, in which case it's ignored at evaluation
+/// time.
+///
+/// @return the regular expression for the possible names of the
+/// function(s).
+const string&
+function_suppression::get_function_name_regex_str() const
+{return priv_->name_regex_str_;}
+
+/// Setter for a regular expression for a family of names of functions
+/// the user wants the current specification to designate.
+///
+/// If the function name as returned by
+/// function_suppression::get_function_name() is not empty, this
+/// property is ignored at specification evaluation time. This
+/// property might be empty, in which case it's ignored at evaluation
+/// time.
+///
+/// @param r the new the regular expression for the possible names of
+/// the function(s).
+void
+function_suppression::set_function_name_regex_str(const string& r)
+{priv_->name_regex_str_ = r;}
+
+/// Getter for the name of the return type of the function the user
+/// wants this specification to designate. This property might be
+/// empty, in which case it's ignored at evaluation time.
+///
+/// @return the name of the return type of the function.
+const string&
+function_suppression::get_return_type_name() const
+{return priv_->return_type_name_;}
+
+/// Setter for the name of the return type of the function the user
+/// wants this specification to designate. This property might be
+/// empty, in which case it's ignored at evaluation time.
+///
+/// @param tr the new name of the return type of the function to set.
+void
+function_suppression::set_return_type_name(const string& tr)
+{priv_->return_type_name_ = tr;}
+
+/// Getter for a regular expression for a family of return type names
+/// for functions the user wants the current specification to
+/// designate.
+///
+/// If the name of the return type of the function as returned by
+/// function_suppression::get_return_type_name() is not empty, then
+/// this property is ignored at specification evaluation time. This
+/// property might be empty, in which case it's ignored at evaluation
+/// time.
+///
+/// @return the regular expression for the possible names of the
+/// return types of the function(s).
+const string&
+function_suppression::get_return_type_regex_str() const
+{return priv_->return_type_regex_str_;}
+
+/// Setter for a regular expression for a family of return type names
+/// for functions the user wants the current specification to
+/// designate.
+///
+/// If the name of the return type of the function as returned by
+/// function_suppression::get_return_type_name() is not empty, then
+/// this property is ignored at specification evaluation time. This
+/// property might be empty, in which case it's ignored at evaluation
+/// time.
+///
+/// @param r the new regular expression for the possible names of the
+/// return types of the function(s) to set.
+void
+function_suppression::set_return_type_regex_str(const string& r)
+{priv_->return_type_regex_str_ = r;}
+
+/// Getter for a vector of parameter specifications to specify
+/// properties of the parameters of the functions the user wants this
+/// specification to designate.
+///
+/// This property might be empty, in which case it's ignored at
+/// evaluation time.
+///
+/// @return the specifications of the parameters of the function(s).
+const function_suppression::parameter_specs_type&
+function_suppression::get_parameter_specs() const
+{return priv_->parm_specs_;}
+
+/// Setter for a vector of parameter specifications to specify
+/// properties of the parameters of the functions the user wants this
+/// specification to designate.
+///
+/// This property might be empty, in which case it's ignored at
+/// evaluation time.
+///
+/// @param p the new specifications of the parameters of the
+/// function(s) to set.
+void
+function_suppression::set_parameter_specs(parameter_specs_type& p)
+{priv_->parm_specs_ = p;}
+
+/// Append a specification of a parameter of the function specification.
+///
+/// @param p the parameter specification to add.
+void
+function_suppression::append_parameter_specs(const parameter_spec_sptr p)
+{priv_->parm_specs_.push_back(p);}
+
+/// Getter for the name of symbol of the function the user wants this
+/// specification to designate.
+///
+/// This property might be empty, in which case it's ignored at
+/// evaluation time.
+///
+/// @return name of the symbol of the function.
+const string&
+function_suppression::get_symbol_name() const
+{return priv_->symbol_name_;}
+
+/// Setter for the name of symbol of the function the user wants this
+/// specification to designate.
+///
+/// This property might be empty, in which case it's ignored at
+/// evaluation time.
+///
+/// @return name of the symbol of the function.
+void
+function_suppression::set_symbol_name(const string& n)
+{priv_->symbol_name_ = n;}
+
+/// Getter for a regular expression for a family of names of symbols
+/// of functions the user wants this specification to designate.
+///
+/// If the symbol name as returned by
+/// function_suppression::get_symbol_name() is not empty, then this
+/// property is ignored at specification evaluation time.
+///
+/// This property might be empty, in which case it's ignored at
+/// evaluation time.
+///
+/// @return the regular expression for a family of names of symbols of
+/// functions to designate.
+const string&
+function_suppression::get_symbol_name_regex_str() const
+{return priv_->symbol_name_regex_str_;}
+
+/// Setter for a regular expression for a family of names of symbols
+/// of functions the user wants this specification to designate.
+///
+/// If the symbol name as returned by
+/// function_suppression::get_symbol_name() is not empty, then this
+/// property is ignored at specification evaluation time.
+///
+/// This property might be empty, in which case it's ignored at
+/// evaluation time.
+///
+/// @param r the new regular expression for a family of names of
+/// symbols of functions to set.
+void
+function_suppression::set_symbol_name_regex_str(const string& r)
+{priv_->symbol_name_regex_str_ = r;}
+
+/// Getter for the name of the version of the symbol of the function
+/// the user wants this specification to designate.
+///
+/// This property might be empty, in which case it's ignored at
+/// evaluation time.
+///
+/// @return the symbol version of the function.
+const string&
+function_suppression::get_symbol_version() const
+{return priv_->symbol_version_;}
+
+/// Setter for the name of the version of the symbol of the function
+/// the user wants this specification to designate.
+///
+/// This property might be empty, in which case it's ignored at
+/// evaluation time.
+///
+/// @param v the new symbol version of the function.
+void
+function_suppression::set_symbol_version(const string& v)
+{priv_->symbol_version_ = v;}
+
+/// Getter for a regular expression for a family of versions of
+/// symbols of functions the user wants the current specification to
+/// designate.
+///
+/// If the symbol version as returned by
+/// function_suppression::get_symbol_version() is non empty, then this
+/// property is ignored. This property might be empty, in which case
+/// it's ignored at evaluation time.
+///
+/// @return the regular expression for the versions of symbols of
+/// functions to designate.
+const string&
+function_suppression::get_symbol_version_regex_str() const
+{return priv_->symbol_version_regex_str_;}
+
+/// Setter for a regular expression for a family of versions of
+/// symbols of functions the user wants the current specification to
+/// designate.
+///
+/// If the symbol version as returned by
+/// function_suppression::get_symbol_version() is non empty, then this
+/// property is ignored. This property might be empty, in which case
+/// it's ignored at evaluation time.
+///
+/// @param the new regular expression for the versions of symbols of
+/// functions to designate.
+void
+function_suppression::set_symbol_version_regex_str(const string& r)
+{priv_->symbol_version_regex_str_ = r;}
+
+/// 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
+function_suppression::suppresses_diff(const diff* diff) const
{
- if (ini::config_sptr config = ini::read_config(input))
- read_suppressions(*config, suppressions);
+ const function_decl_diff* d = dynamic_cast<const function_decl_diff*>(diff);
+ if (!d)
+ return false;
+
+ function_decl_sptr ff = is_function_decl(d->first_function_decl()),
+ sf = is_function_decl(d->second_function_decl());
+ assert(ff && sf);
+
+ string ff_name = ff->get_qualified_name(), sf_name = sf->get_qualified_name();
+
+ // Check if the "name" property matches.
+ if (!get_function_name().empty())
+ {
+ string n = get_function_name();
+ if (n != ff->get_qualified_name()
+ && n != sf->get_qualified_name())
+ return false;
+ }
+
+ // Check if the "name_regexp" property matches.
+ const sptr_utils::regex_t_sptr name_regex = priv_->get_name_regex();
+ if (name_regex
+ && (regexec(name_regex.get(), ff_name.c_str(), 0, NULL, 0) != 0
+ && regexec(name_regex.get(), sf_name.c_str(), 0, NULL, 0) != 0))
+ return false;
+
+ // Check if the "return_type_name" or "return_type_regexp"
+ // properties matches.
+
+ string ff_return_type_name = ff->get_type()->get_return_type()
+ ? (get_type_declaration(ff->get_type()->get_return_type())
+ ->get_qualified_name())
+ : "";
+ string sf_return_type_name = sf->get_type()->get_return_type()
+ ? (get_type_declaration(sf->get_type()->get_return_type())
+ ->get_qualified_name())
+ : "";
+
+ if (!get_return_type_name().empty())
+ {
+ if (ff_return_type_name != get_return_type_name()
+ && sf_return_type_name != get_return_type_name())
+ return false;
+ }
+ else
+ {
+ const sptr_utils::regex_t_sptr return_type_regex =
+ priv_->get_return_type_regex();
+ if (return_type_regex
+ && (regexec(return_type_regex.get(),
+ ff_return_type_name.c_str(),
+ 0, NULL, 0) != 0
+ && regexec(return_type_regex.get(),
+ sf_return_type_name.c_str(),
+ 0, NULL, 0) != 0))
+ return false;
+ }
+
+ // Check if the "symbol_name" and "symbol_name_regexp" properties
+ // match.
+
+ string ff_sym_name, ff_sym_version, sf_sym_name, sf_sym_version;
+ if (ff->get_symbol())
+ {
+ ff_sym_name = ff->get_symbol()->get_name();
+ ff_sym_version = ff->get_symbol()->get_version().str();
+ }
+ if (sf->get_symbol())
+ {
+ sf_sym_name = sf->get_symbol()->get_name();
+ sf_sym_version = sf->get_symbol()->get_version().str();
+ }
+
+ if (!get_symbol_name().empty())
+ {
+ if (ff_sym_name != get_symbol_name()
+ && sf_sym_name != get_symbol_name())
+ return false;
+ }
+ else
+ {
+ const sptr_utils::regex_t_sptr symbol_name_regex =
+ priv_->get_symbol_name_regex();
+ if (symbol_name_regex
+ && (regexec(symbol_name_regex.get(),
+ ff_sym_name.c_str(),
+ 0, NULL, 0) != 0
+ && regexec(symbol_name_regex.get(),
+ sf_sym_name.c_str(),
+ 0, NULL, 0) != 0))
+ return false;
+ }
+
+// Check if the "symbol_version" and "symbol_version_regexp"
+// properties match.
+ if (!get_symbol_version().empty())
+ {
+ if (ff_sym_version != get_symbol_version()
+ && sf_sym_name != get_symbol_version())
+ return false;
+ }
+ else
+ {
+ const sptr_utils::regex_t_sptr symbol_version_regex =
+ priv_->get_symbol_version_regex();
+ if (symbol_version_regex
+ && (regexec(symbol_version_regex.get(),
+ ff_sym_version.c_str(),
+ 0, NULL, 0) != 0
+ && regexec(symbol_version_regex.get(),
+ sf_sym_version.c_str(),
+ 0, NULL, 0) != 0))
+ return false;
+ }
+
+ if (!get_parameter_specs().empty())
+ {
+ function_type_sptr ff_type = ff->get_type();
+ function_type_sptr sf_type = sf->get_type();
+ type_base_sptr parm_type;
+
+ for (parameter_specs_type::const_iterator p =
+ get_parameter_specs().begin();
+ p != get_parameter_specs().end();
+ ++p)
+ {
+ size_t index = (*p)->get_index();
+ function_decl::parameter_sptr ff_parm =
+ ff_type->get_parm_at_index_from_first_non_implicit_parm(index);
+ function_decl::parameter_sptr sf_parm =
+ sf_type->get_parm_at_index_from_first_non_implicit_parm(index);
+ if (!ff_parm && ! sf_parm)
+ return false;
+
+ string ff_parm_type_qualified_name, sf_parm_type_qualified_name;
+ if (ff_parm)
+ {
+ parm_type = ff_parm->get_type();
+ ff_parm_type_qualified_name =
+ get_type_declaration(parm_type)->get_qualified_name();
+ }
+
+ if (sf_parm)
+ {
+ parm_type = sf_parm->get_type();
+ sf_parm_type_qualified_name =
+ get_type_declaration(parm_type)->get_qualified_name();
+ }
+
+ const string& tn = (*p)->get_parameter_type_name();
+ if (!tn.empty())
+ {
+ if (tn != ff_parm_type_qualified_name
+ && tn != sf_parm_type_qualified_name)
+ return false;
+ }
+ else
+ {
+ const sptr_utils::regex_t_sptr parm_type_name_regex =
+ (*p)->priv_->get_type_name_regex();
+ if (parm_type_name_regex)
+ {
+ if ((regexec(parm_type_name_regex.get(),
+ ff_parm_type_qualified_name.c_str(),
+ 0, NULL, 0) != 0
+ && regexec(parm_type_name_regex.get(),
+ sf_parm_type_qualified_name.c_str(),
+ 0, NULL, 0) != 0))
+ return false;
+ }
+ }
+ }
+ }
+
+ return true;
}
-/// Read suppressions specifications from an input file on disk.
+/// Parse a string containing a parameter spec, build an instance of
+/// function_suppression::parameter_spec from it and return a pointer
+/// to that object.
///
-/// @param input the path to the input file to read from.
+/// @return a shared pointer pointer to the newly built instance of
+/// function_suppression::parameter_spec. If the parameter
+/// specification could not be parsed, return a nil object.
+static function_suppression::parameter_spec_sptr
+read_parameter_spec_from_string(const string& str)
+{
+ string::size_type cur = 0;
+ function_suppression::parameter_spec_sptr result;
+
+ // skip leading white spaces.
+ for (; cur < str.size(); ++cur)
+ if (!isspace(str[cur]))
+ break;
+
+ // look for the parameter index
+ string index_str;
+ if (str[cur] == '\'')
+ {
+ ++cur;
+ for (; cur < str.size(); ++cur)
+ if (!isdigit(str[cur]))
+ break;
+ else
+ index_str += str[cur];
+ }
+
+ // skip white spaces.
+ for (; cur < str.size(); ++cur)
+ if (!isspace(str[cur]))
+ break;
+
+ bool is_regex = false;
+ if (str[cur] == '/')
+ {
+ is_regex = true;
+ ++ cur;
+ }
+
+ // look for the type name (regex)
+ string type_name;
+ for (; cur < str.size(); ++cur)
+ if (!isspace(str[cur]))
+ {
+ if (is_regex && str[cur] == '/')
+ break;
+ type_name += str[cur];
+ }
+
+ if (is_regex && str[cur] == '/')
+ ++cur;
+
+ if (!index_str.empty() || !type_name.empty())
+ {
+ function_suppression::parameter_spec* p;
+ if (is_regex)
+ p = new function_suppression::parameter_spec(atoi(index_str.c_str()),
+ "", type_name);
+ else
+ p = new function_suppression::parameter_spec(atoi(index_str.c_str()),
+ type_name, "");
+ result.reset(p);
+ }
+
+ return result;
+}
+
+/// Parse function suppression specification, build a resulting @ref
+/// function_suppression type and return a shared pointer to that
+/// object.
///
-/// @param suppressions the vector of suppressions to append the newly
-/// read suppressions to.
-void
-read_suppressions(const string& file_path,
- suppressions_type& suppressions)
+/// @return a shared pointer to the newly built @ref
+/// function_suppression. If the function suppression specification
+/// could not be parsed then a nil shared pointer is returned.
+static function_suppression_sptr
+read_function_suppression(const ini::config::section& section)
{
- if (ini::config_sptr config = ini::read_config(file_path))
- read_suppressions(*config, suppressions);
+ function_suppression_sptr nil;
+
+ if (section.get_name() != "suppress_function")
+ return nil;
+
+ ini::config::property_sptr label_prop = section.find_property("label");
+ string label_str = (label_prop && !label_prop->second.empty())
+ ? label_prop->second
+ : "";
+
+ ini::config::property_sptr name_prop = section.find_property("name");
+ string name = name_prop && !name_prop->second.empty()
+ ? name_prop->second
+ : "";
+
+ ini::config::property_sptr name_regex_prop =
+ section.find_property("name_regexp");
+ string name_regex_str = name_regex_prop && !name_regex_prop->second.empty()
+ ? name_regex_prop->second
+ : "";
+
+ ini::config::property_sptr return_type_name_prop =
+ section.find_property("return_type_name");
+ string return_type_name = (return_type_name_prop)
+ ? return_type_name_prop->second
+ : "";
+
+ ini::config::property_sptr return_type_regex_prop =
+ section.find_property("return_type_regexp");
+ string return_type_regex_str =
+ (return_type_regex_prop
+ && !return_type_regex_prop->second.empty())
+ ? return_type_regex_prop->second
+ : "";
+
+ ini::config::property_sptr sym_name_prop =
+ section.find_property("symbol_name");
+ string sym_name = (sym_name_prop) ? sym_name_prop->second : "";
+
+ ini::config::property_sptr sym_name_regex_prop =
+ section.find_property("symbol_name_regexp");
+ string sym_name_regex_str =
+ (sym_name_regex_prop && !sym_name_regex_prop->second.empty())
+ ? sym_name_regex_prop->second
+ : "";
+
+ ini::config::property_sptr sym_ver_prop =
+ section.find_property("symbol_version");
+ string sym_version = (sym_ver_prop) ? sym_ver_prop->second : "";
+
+ ini::config::property_sptr sym_ver_regex_prop =
+ section.find_property("symbol_version_regexp");
+ string sym_ver_regex_str =
+ (sym_ver_regex_prop && !sym_ver_regex_prop->second.empty())
+ ? sym_ver_regex_prop->second
+ : "";
+
+ function_suppression::parameter_spec_sptr parm;
+ function_suppression::parameter_specs_type parms;
+ for (ini::config::property_vector::const_iterator p =
+ section.get_properties().begin();
+ p != section.get_properties().end();
+ ++p)
+ if ((*p)->first == "parameter")
+ if (parm = read_parameter_spec_from_string((*p)->second))
+ parms.push_back(parm);
+
+ function_suppression_sptr result;
+ if (!label_str.empty()
+ || !name.empty()
+ || !name_regex_str.empty()
+ || !return_type_name.empty()
+ || !return_type_regex_str.empty()
+ || !sym_name.empty()
+ || !sym_name_regex_str.empty()
+ || !sym_version.empty()
+ || !sym_ver_regex_str.empty()
+ || !parms.empty())
+ result.reset(new function_suppression(label_str, name,
+ name_regex_str,
+ return_type_name,
+ return_type_regex_str,
+ parms,
+ sym_name,
+ sym_name_regex_str,
+ sym_version,
+ sym_ver_regex_str));
+
+ return result;
}
+// </function_suppression stuff>
+
/// The private member (pimpl) for @ref diff_context.
struct diff_context::priv
{
begin_traversing();
v.visit_begin(this);
- if (v.get_visiting_kind() & PRE_VISITING_KIND)
- if (!v.visit(this, /*pre=*/true))
- {
- v.visit_end(this);
- end_traversing();
- context()->mark_diff_as_traversed(this);
- return false;
- }
-
- for (vector<diff*>::const_iterator i = children_nodes().begin();
- i != children_nodes().end();
- ++i)
+ if (!v.visit(this, /*pre=*/true))
{
- if (!(*i)->traverse(v))
- {
- v.visit_end(this);
- end_traversing();
- context()->mark_diff_as_traversed(this);
- return false;
- }
+ v.visit_end(this);
+ end_traversing();
+ context()->mark_diff_as_traversed(this);
+ return false;
}
- if (v.get_visiting_kind() & POST_VISITING_KIND)
- if (!v.visit(this, /*pref=*/false))
+ if (!(v.get_visiting_kind() & SKIP_CHILDREN_VISITING_KIND))
+ for (vector<diff*>::const_iterator i = children_nodes().begin();
+ i != children_nodes().end();
+ ++i)
{
- v.visit_end(this);
- end_traversing();
- context()->mark_diff_as_traversed(this);
- return false;
+ if (!(*i)->traverse(v))
+ {
+ v.visit_end(this);
+ end_traversing();
+ context()->mark_diff_as_traversed(this);
+ return false;
+ }
}
+ if (!v.visit(this, /*pref=*/false))
+ {
+ v.visit_end(this);
+ end_traversing();
+ context()->mark_diff_as_traversed(this);
+ return false;
+ }
+
v.visit_end(this);
end_traversing();
context()->mark_diff_as_traversed(this);
/// appropriate.
struct redundancy_marking_visitor : public diff_node_visitor
{
+ bool skip_children_nodes_;
+
+ redundancy_marking_visitor()
+ : skip_children_nodes_()
+ {}
+
virtual void
visit_begin(diff* d)
{
- // A diff node that carries a change and that has been already
- // traversed elsewhere is considered redundant.
- if (d->context()->diff_has_been_traversed(d)
- && d->length())
- d->add_to_category(REDUNDANT_CATEGORY);
+ if (d->to_be_reported())
+ {
+ // A diff node that carries a change and that has been already
+ // been traversed elsewhere is considered redundant.
+ if (d->context()->diff_has_been_traversed(d) && d->length())
+ d->add_to_category(REDUNDANT_CATEGORY);
+ }
+ else
+ {
+ // If the node is not be reported, do not look at it children.
+ set_visiting_kind(get_visiting_kind() | SKIP_CHILDREN_VISITING_KIND);
+ skip_children_nodes_ = true;
+ }
}
virtual void
virtual void
visit_end(diff* d)
{
- // Propagate the redundancy categorization of the children nodes
- // to this node. But if this node has local changes, then it
- // doesn't inherit redundancy from its children nodes.
- if (!(d->get_category() & REDUNDANT_CATEGORY)
- && !d->has_local_changes())
+ if (skip_children_nodes_)
+ // When visiting this node, we decided to skip its children
+ // node. Now that we are done visiting the node, lets stop
+ // avoiding the children nodes visiting for the other tree
+ // nodes.
{
- bool has_non_redundant_child = false;
- bool has_non_empty_child = false;
- for (vector<diff*>::const_iterator i = d->children_nodes().begin();
- i != d->children_nodes().end();
- ++i)
+ set_visiting_kind(get_visiting_kind() & (~SKIP_CHILDREN_VISITING_KIND));
+ skip_children_nodes_ = false;
+ }
+ else
+ {
+ // Propagate the redundancy categorization of the children nodes
+ // to this node. But if this node has local changes, then it
+ // doesn't inherit redundancy from its children nodes.
+ if (!(d->get_category() & REDUNDANT_CATEGORY)
+ && !d->has_local_changes())
{
- if ((*i)->length())
+ bool has_non_redundant_child = false;
+ bool has_non_empty_child = false;
+ for (vector<diff*>::const_iterator i = d->children_nodes().begin();
+ i != d->children_nodes().end();
+ ++i)
{
- has_non_empty_child = true;
- if (((*i)->get_category() & REDUNDANT_CATEGORY) == 0)
- has_non_redundant_child = true;
+ if ((*i)->length())
+ {
+ has_non_empty_child = true;
+ if (((*i)->get_category() & REDUNDANT_CATEGORY) == 0)
+ has_non_redundant_child = true;
+ }
+ if (has_non_redundant_child)
+ break;
}
- if (has_non_redundant_child)
- break;
- }
- // A diff node for which at least a child node carries a
- // change, and for which all the children are redundant is
- // deemed redundant too, unless it has local changes.
- if (has_non_empty_child
- && !has_non_redundant_child)
- d->add_to_category(REDUNDANT_CATEGORY);
+ // A diff node for which at least a child node carries a
+ // change, and for which all the children are redundant is
+ // deemed redundant too, unless it has local changes.
+ if (has_non_empty_child
+ && !has_non_redundant_child)
+ d->add_to_category(REDUNDANT_CATEGORY);
+ }
}
}
virtual bool
visit(diff*, bool)
- {
- return true;
- }
+ {return true;}
virtual bool
visit(corpus_diff*, bool)