Initial implementation of diff tree node filtering

* include/abg-comp-filter.h: New file.
	* include/Makefile.am: Add the new include/abg-comp-filter.h to
	the source distribution.
	* include/abg-comparison.h (enum visiting_kind, diff_category): New enums.
	(operator|): Declare new operator declaration for the new
	visiting_kind enum.
	(operator{|,^,&,~}): Declare new operator decl for the new
	diff_category enum.
	(diff_context::{get_allowed_category, set_allowed_category,
	switch_categories_on, switch_categories_off, diff_filters,
	add_diff_filter, maybe_apply_filters}): Declare new member functions.
	(diff::{parent_, category_}): New members.
	(diff::diff): Adjust.
	(diff::{get_parent, set_parent, get_category, add_to_category,
	is_filtered_out, to_be_reported}):  New members.
	(diff_node_visitor::{get_visiting_kind, set_visiting_kind}): New
	members.
	(diff_node_visitor::visit): Add a new flag to saying if the
	visitor is being called in post or pre children traversing mode.
	* src/abg-comparison.cc (operator|): Declare new operator
	declaration for the new visiting_kind enum.
	(operator{|,^,&,~}): Declare new operator decl for the new
	diff_category enum.
	(diff_context::priv::{allowed_category_, filters_}): New members.
	(diff_context::diff_context): Add all known filters.
	(diff_context::{get_allowed_category, set_allowed_category,
	switch_categories_on, switch_categories_off, diff_filters,
	add_diff_filter, maybe_apply_filters}): Define new member
	functions.
	(diff::{is_filtered_out, to_be_reported}): Define new members.
	(*::report): Use the new diff::to_be_reported function.
	(*::traverse): Adjust for pre/post visiting.
	(var_diff::var_diff): Chain the type diff node to its parent.
	({pointer_diff, reference_diff, qualified_type_diff,
	typedef_diff}::underlying_type_diff): Chain the underlying type
	diff node to its parent.
	(enum_diff::enum_diff): Likewise.
	(base_diff::underlying_class_diff): Likewise.
	({class_diff, corpus_diff}::report): Do not report changed
	(member) functions that have been filtered out.  Rather report
	that they have been filtered out.
	({function_decl_diff, class_diff}::traverse): Chain underlying
	type diff nodes to their parent.
	(diff_node_visitor::visit): Add a new flag to saying if the
	visitor is being called in post or pre children traversing mode.
	Make sure to call the default diff::visit overload.
	* src/abg-comp-filter.cc: New file.
	* src/Makefile.am: Add the new abg-comp-filter.cc to the source
	distribution.
	* tools/bidiff.cc (options::show_harm{ful,less}_changes): New
	members.
	(display_usage): Add usage strings for --no-harmless and
	--no-harmful options.
	(parse_command_line): Parse --no-harmless and --no-harmful command
	line options.
	(set_diff_context_from_opts): Populate the diff context
	accordingly.

Signed-off-by: Dodji Seketeli <dodji@redhat.com>
This commit is contained in:
Dodji Seketeli 2014-03-27 11:03:53 +01:00
parent d2ffdbffca
commit 802f7cbc92
7 changed files with 932 additions and 180 deletions

View File

@ -6,6 +6,7 @@ abg-reader.h \
abg-dwarf-reader.h \
abg-writer.h \
abg-comparison.h \
abg-comp-filter.h \
abg-diff-utils.h \
abg-libxml-utils.h \
abg-libzip-utils.h \

94
include/abg-comp-filter.h Normal file
View File

@ -0,0 +1,94 @@
// -*- Mode: C++ -*-
//
// Copyright (C) 2013 Red Hat, Inc.
//
// This file is part of the GNU Application Binary Interface Generic
// Analysis and Instrumentation Library (libabigail). This library is
// free software; you can redistribute it and/or modify it under the
// terms of the GNU Lesser General Public License as published by the
// Free Software Foundation; either version 3, or (at your option) any
// later version.
// This library is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Lesser Public License for more details.
// You should have received a copy of the GNU Lesser General Public
// License along with this program; see the file COPYING-LGPLV3. If
// not, see <http://www.gnu.org/licenses/>.
//
// Author: Dodji Seketeli
/// @file
///
/// This header declares filters for the diff trees resulting from
/// comparing ABI Corpora.
#ifndef __ABG_COMP_FILTER_H__
#define __ABG_COMP_FILTER_H__
#include "abg-comparison.h"
namespace abigail
{
namespace comparison
{
/// Facilities to walk, categorize and possibly filter nodes of the
/// diff tree.
namespace filtering
{
class filter_base;
/// Convenience typedef for a shared pointer to filter_base
typedef shared_ptr<filter_base> filter_base_sptr;
/// Convenience typedef for a vector of filter_base_sptr
typedef std::vector<filter_base_sptr> filters;
/// The base class for the diff tree node filter.
///
/// It's intended to walk a tree of diff nodes and tag each relevant
/// name into one or several categories depending on well choosen
/// properties of the diff nodes.
struct filter_base : public diff_node_visitor
{
friend void
apply_filter(filter_base_sptr f, diff_sptr deef);
}; //end class filter_base
void
apply_filter(filter_base& filter, diff_sptr d);
void
apply_filter(filter_base_sptr filter, diff_sptr d);
class harmless_filter;
/// Convenience typedef for a shared pointer to a harmless_filter.
typedef shared_ptr<harmless_filter> harmless_filter_sptr;
/// A filter that walks the diff nodes tree and tags relevant diff
/// nodes into categories considered to represent harmless changes.
class harmless_filter : public filter_base
{
virtual bool
visit(diff*, bool);
}; // end class harmless_filter
class harmful_filter;
/// A convenience typedef for a shared pointer to harmful_filter.
typedef shared_ptr<harmful_filter> harmful_filter_sptr;
/// A filter that walks the diff nodes tree tags relevant diff nodes
/// into categories considered to represent potentially harmful
/// changes.
class harmful_filter : public filter_base
{
virtual bool
visit(diff*, bool);
}; // end class harmful_filter
} // end namespace filtering
} // end namespace comparison
} // end namespace abigail
#endif // __ABG_COMP_FILTER_H__

View File

@ -39,6 +39,13 @@ namespace abigail
namespace comparison
{
namespace filtering
{
class filter_base;
typedef shared_ptr<filter_base> filter_base_sptr;
typedef std::vector<filter_base_sptr> filters;
}
// Inject types we need into this namespace.
using std::ostream;
using std::vector;
@ -155,20 +162,78 @@ class diff_context;
/// Convenience typedef for a shared pointer of @ref diff_context.
typedef shared_ptr<diff_context> diff_context_sptr;
struct diff_node_visitor;
class diff_node_visitor;
struct diff_traversable_base;
/// Convenience typedef for shared_ptr on diff_traversable_base.
typedef shared_ptr<diff_traversable_base> diff_traversable_base_sptr;
/// The base class for the diff classes that are to be traversed.
struct diff_traversable_base : public traversable_base
/// An enum for the different ways to visit a diff tree node.
///
/// This is used by the node traversing code to know when to invoke a
/// visitor on a diff tree node.
enum visiting_kind
{
/// The default enumerator value of this enum. It doesn't have any
/// particular meaning yet.
NO_VISITING_KIND = 0,
/// This says that a visitor should be invoked on a tree node
/// *before* visiting the node's children.
PRE_VISITING_KIND = 1,
/// This says that a visotor should be invoked on a tree node
/// *after* visiting the node's children.
POST_VISITING_KIND = 1 << 1
};
visiting_kind
operator|(visiting_kind l, visiting_kind r);
/// The base class for the diff classes that are to be traversed.
class diff_traversable_base : public traversable_base
{
public:
virtual bool
traverse(diff_node_visitor& v);
}; // end struct diff_traversable_base
/// An enum for the different categories that a diff tree node falls
/// into, regarding the kind of changes it represents.
enum diff_category
{
/// This means the diff node does not carry any (meaningful) change.
NO_CHANGE_CATEGORY = 0,
/// This means the diff node (or at least one of its descendant
/// nodes) carries access related changes, e.g, a private member
/// that becomes public.
ACCESS_CHANGED_CATEGORY = 1,
/// This means the diff node (or at least one of its descendant
/// nodes) carries a change that modifies the size of a type.
SIZE_CHANGED_CATEGORY = 1 << 1,
/// A special enumerator that is the logical 'or' all the
/// enumerators above.
///
/// This one must stay the last enumerator. Please update it each
/// time you add a new enumerator above.
EVERYTHING_CATEGORY =
ACCESS_CHANGED_CATEGORY
| SIZE_CHANGED_CATEGORY
};
diff_category
operator|(diff_category c1, diff_category c2);
diff_category
operator^(diff_category c1, diff_category c2);
diff_category
operator&(diff_category c1, diff_category c2);
diff_category
operator~(diff_category c);
/// The context of the diff. This type holds various bits of
/// information that is going to be used throughout the diffing of two
/// entities and the reporting that follows.
@ -196,6 +261,27 @@ public:
const decl_base_sptr second,
diff_sptr d);
diff_category
get_allowed_category() const;
void
set_allowed_category(diff_category c);
void
switch_categories_on(diff_category c);
void
switch_categories_off(diff_category c);
const filtering::filters&
diff_filters() const;
void
add_diff_filter(filtering::filter_base_sptr);
void
maybe_apply_filters(diff_sptr dif);
void
show_stats_only(bool f);
@ -244,33 +330,69 @@ public:
/// constructs are called the "subjects" of the diff.
class diff : public diff_traversable_base
{
decl_base_sptr first_subject_;
decl_base_sptr second_subject_;
diff_context_sptr ctxt_;
mutable bool reported_once_;
mutable bool currently_reporting_;
diff* parent_;
decl_base_sptr first_subject_;
decl_base_sptr second_subject_;
diff_context_sptr ctxt_;
diff_category category_;
mutable bool reported_once_;
mutable bool currently_reporting_;
// Forbidden
diff();
protected:
diff(decl_base_sptr first_subject,
decl_base_sptr second_subject)
: first_subject_(first_subject),
: parent_(0),
first_subject_(first_subject),
second_subject_(second_subject),
category_(NO_CHANGE_CATEGORY),
reported_once_(false),
currently_reporting_(false)
{}
diff(decl_base_sptr first_subject,
decl_base_sptr second_subject,
diff_context_sptr ctxt)
: first_subject_(first_subject),
diff(decl_base_sptr first_subject,
decl_base_sptr second_subject,
diff_context_sptr ctxt)
: parent_(0),
first_subject_(first_subject),
second_subject_(second_subject),
ctxt_(ctxt),
category_(NO_CHANGE_CATEGORY),
reported_once_(false),
currently_reporting_(false)
{}
diff(diff* parent,
decl_base_sptr first_subject,
decl_base_sptr second_subject,
diff_context_sptr ctxt)
: parent_(parent),
first_subject_(first_subject),
second_subject_(second_subject),
ctxt_(ctxt),
category_(NO_CHANGE_CATEGORY),
reported_once_(false),
currently_reporting_(false)
{}
public:
/// Getter of the parent diff node of this one.
///
/// Return the parent diff node if any, null otherwise.
diff*
get_parent() const
{return parent_;}
/// Setter for the parent diff node of this one.
///
/// @param parent the parent diff node.
void
set_parent(diff* parent)
{parent_ = parent;}
/// Getter of the first subject of the diff.
///
/// @return the first subject of the diff.
@ -334,6 +456,34 @@ public:
reported_once(bool f) const
{reported_once_ = f;}
/// Getter for the category of the current diff tree node.
///
/// @return the category of the current diff tree node.
diff_category
get_category() const
{return category_;}
/// Adds the current diff tree node to an additional set of
/// categories.
///
/// @param c a bit-map representing the set of categories to add the
/// current diff tree node to.
///
/// @return the resulting bit-map representing the categories this
/// current diff tree node belongs to.
diff_category
add_to_category(diff_category c)
{
category_ = category_ | c;
return category_;
}
bool
is_filtered_out() const;
bool
to_be_reported() const;
/// Pure interface to get the length of the changes
/// encapsulated by this diff. This is to be implemented by all
/// descendants of this class.
@ -1177,49 +1327,90 @@ compute_diff(const corpus_sptr,
/// The base class for the node visitors. These are the types used to
/// visit each node traversed by the diff_traversable_base::traverse() method.
struct diff_node_visitor
class diff_node_visitor : public node_visitor_base
{
virtual bool
visit(distinct_diff*);
protected:
visiting_kind visiting_kind_;
public:
diff_node_visitor()
: visiting_kind_(PRE_VISITING_KIND)
{}
/// Getter for the visiting policy of the traversing code while
/// invoking this visitor.
///
/// @return the visiting policy used by the traversing code when
/// invoking this visitor.
visiting_kind
get_visiting_kind() const
{return visiting_kind_;}
/// Setter for the visiting policy of the traversing code while
/// invoking this visitor.
///
/// @param v a bit map representing the new visiting policy used by
/// the traversing code when invoking this visitor.
void
set_visiting_kind(visiting_kind v)
{visiting_kind_ = v;}
/// Setter for the visiting policy of the traversing code while
/// invoking this visitor. This one makes a logical or between the
/// current policy and the bitmap given in argument and assigns the
/// current policy to the result.
///
/// @param v a bitmap representing the visiting policy to or with
/// the current policy.
void
or_visiting_kind(visiting_kind v)
{visiting_kind_ = visiting_kind_ | v;}
virtual bool
visit(var_diff*);
visit(diff*, bool);
virtual bool
visit(pointer_diff*);
visit(distinct_diff*, bool);
virtual bool
visit(reference_diff*);
visit(var_diff*, bool);
virtual bool
visit(qualified_type_diff*);
visit(pointer_diff*, bool);
virtual bool
visit(enum_diff*);
visit(reference_diff*, bool);
virtual bool
visit(class_diff*);
visit(qualified_type_diff*, bool);
virtual bool
visit(base_diff*);
visit(enum_diff*, bool);
virtual bool
visit(scope_diff*);
visit(class_diff*, bool);
virtual bool
visit(function_decl_diff*);
visit(base_diff*, bool);
virtual bool
visit(type_decl_diff*);
visit(scope_diff*, bool);
virtual bool
visit(typedef_diff*);
visit(function_decl_diff*, bool);
virtual bool
visit(translation_unit_diff*);
visit(type_decl_diff*, bool);
virtual bool
visit(corpus_diff*);
visit(typedef_diff*, bool);
virtual bool
visit(translation_unit_diff*, bool);
virtual bool
visit(corpus_diff*, bool);
}; // end struct diff_node_visitor
}// end namespace comparison

View File

@ -8,6 +8,7 @@ $(h)/abg-ir.cc \
$(h)/abg-corpus.cc \
$(h)/abg-diff-utils.cc \
$(h)/abg-comparison.cc \
$(h)/abg-comp-filter.cc \
$(h)/abg-reader.cc \
$(h)/abg-dwarf-reader.cc \
$(h)/abg-libxml-utils.cc \

137
src/abg-comp-filter.cc Normal file
View File

@ -0,0 +1,137 @@
// -*- Mode: C++ -*-
//
// Copyright (C) 2013 Red Hat, Inc.
//
// This file is part of the GNU Application Binary Interface Generic
// Analysis and Instrumentation Library (libabigail). This library is
// free software; you can redistribute it and/or modify it under the
// terms of the GNU Lesser General Public License as published by the
// Free Software Foundation; either version 3, or (at your option) any
// later version.
// This library is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Lesser Public License for more details.
// You should have received a copy of the GNU Lesser General Public
// License along with this program; see the file COPYING-LGPLV3. If
// not, see <http://www.gnu.org/licenses/>.
//
// Author: Dodji Seketeli
/// @file
///
/// This file contains definitions of diff objects filtering
/// facilities.
#include "abg-comp-filter.h"
namespace abigail
{
namespace comparison
{
namespace filtering
{
/// Walk and categorize the nodes of a diff sub-tree.
///
/// @param filter the filter invoked on each node of the walked
/// sub-tree.
///
/// @param d the diff sub-tree node to start the walk from.
void
apply_filter(filter_base& filter, diff_sptr d)
{
filter.set_visiting_kind(PRE_VISITING_KIND | POST_VISITING_KIND);
d->traverse(filter);
}
/// Walk and categorize the nodes of a diff sub-tree.
///
/// @param filter the filter invoked on each node of the walked
/// sub-tree.
///
/// @param d the diff sub-tree node to start the walk from.
void
apply_filter(filter_base_sptr filter, diff_sptr d)
{apply_filter(*filter, d);}
/// The visiting code of the harmless_filter.
///
/// @param d the diff node being visited.
///
/// @param pre this is true iff the node is being visited *before* the
/// children nodes of @p d.
///
/// @return true iff the traversal shall keep going after the
/// completion of this function.
bool
harmless_filter::visit(diff* d, bool pre)
{
if (pre)
{
decl_base_sptr f = d->first_subject(),
s = d->second_subject();
if (!is_member_decl(f)
&& !is_member_decl(s))
return true;
access_specifier fa = get_member_access_specifier(f),
sa = get_member_access_specifier(s);
if (sa != fa)
d->add_to_category(ACCESS_CHANGED_CATEGORY);
// Add non-virtual member function deletions and changes due to
// details that are being reported or got reported earlier.
}
// Propagate the categorization to the parent nodes.
if (d->get_parent())
d->get_parent()->add_to_category(d->get_category()
& ACCESS_CHANGED_CATEGORY);
return true;
}
/// The visiting code of the harmful_filter.
///
/// @param d the diff node being visited.
///
/// @param pre this is true iff the node is being visited *before* the
/// children nodes of @p d.
///
/// @return true iff the traversal shall keep going after the
/// completion of this function.
bool
harmful_filter::visit(diff* d, bool pre)
{
if (pre)
{
decl_base_sptr f = d->first_subject(),
s = d->second_subject();
bool size_changed = false;
type_base_sptr tf, ts;
if (is_type(f) && is_type(s))
{
tf= is_type(f), ts = is_type(s);
if (tf->get_size_in_bits() != ts->get_size_in_bits())
size_changed = true;
}
if (size_changed)
d->add_to_category(SIZE_CHANGED_CATEGORY);
}
// Propagate the categorization to the parent nodes.
if (d->get_parent())
d->get_parent()->add_to_category(d->get_category() & SIZE_CHANGED_CATEGORY);
return true;
}
} // end namespace filtering
} // end namespace comparison
} // end namespace abigail

File diff suppressed because it is too large Load Diff

View File

@ -26,7 +26,7 @@
#include <vector>
#include <string>
#include <iostream>
#include "abg-comparison.h"
#include "abg-comp-filter.h"
#include "abg-tools-utils.h"
#include "abg-reader.h"
#include "abg-dwarf-reader.h"
@ -63,6 +63,8 @@ struct options
bool show_changed_vars;
bool show_added_vars;
bool show_all_vars;
bool show_harmful_changes;
bool show_harmless_changes;
options()
: show_stats_only(false),
@ -74,7 +76,9 @@ struct options
show_deleted_vars(false),
show_changed_vars(false),
show_added_vars(false),
show_all_vars(true)
show_all_vars(true),
show_harmful_changes(true),
show_harmless_changes(true)
{}
};//end struct options;
@ -98,6 +102,8 @@ display_usage(const string prog_name, ostream& out)
<< " --keep <regex> keep only functions and variables matching a regex\n"
<< " --keep-fn <regex> keep only functions matching a regex\n"
<< " --keep-var <regex> keep only variables matching a regex\n"
<< " --no-harmless do not display the harmless changes\n"
<< " --no-harmful do not display the harmful changes\n"
<< " --help display this message\n";
}
@ -219,6 +225,10 @@ parse_command_line(int argc, char* argv[], options& opts)
return false;
opts.keep_var_regex_patterns.push_back(argv[j]);
}
else if (!strcmp(argv[i], "--no-harmless"))
opts.show_harmless_changes = false;
else if (!strcmp(argv[i], "--no-harmful"))
opts.show_harmful_changes = false;
else
return false;
}
@ -281,6 +291,11 @@ set_diff_context_from_opts(diff_context_sptr ctxt,
ctxt->show_deleted_vars(opts.show_all_vars || opts.show_deleted_vars);
ctxt->show_changed_vars(opts.show_all_vars || opts.show_changed_vars);
ctxt->show_added_vars(opts.show_all_vars || opts.show_added_vars);
if (!opts.show_harmless_changes)
ctxt->switch_categories_off(abigail::comparison::ACCESS_CHANGED_CATEGORY);
if (!opts.show_harmful_changes)
ctxt->switch_categories_off(abigail::comparison::SIZE_CHANGED_CATEGORY);
}
/// Set the regex patterns describing the functions to drop from the