Recognize cyclic diff tree nodes as being redundant

Okay I need to introduce some vocabulary here.  Suppose we have the
version 1 of a library named library-v1.so which source code is:

    struct S
    {
     int m0;
     struct S* m2;
    };

    int
    foo(struct S* ptr)
    {
      return ptr;
    }

And now suppose we have a version 2 of that library named
library-v2.so which source code is modified so that a new data member
is inserted into struct S:

    struct S
    {
     int m0;
     char m1; /* <--- a new data member is inserted here.  */
     struct S* m2;
    };

    int
    foo(struct S* ptr)
    {
      return ptr;
    }

struct S is said to be a cyclic type because it contains a (data)
member which type refers to struct S itself, namely, the type of the
data member S::m2 is struct S*, which refers to struct S.

So, by analogy, the diff node tree that represents the changes of
struct S is also said to be cyclic, for similar reasons: the diff
node of the change of S::m2 refers to the diff node of the change of
the type of S::m2, namely the diff node of struct S*, which refers to
the diff node for the change of struct S itself.

Now let's talk about redundancy.  When walking the diff node tree of
struct S in a depth-first manner, at some point, we look at the diff
node for the data member S::m2, and we end up looking at the diff node
of its type which is the diff node for struct S*; we keep walking and
eventually we look the diff node of the change of the underlying type
of struct S, which is the diff node of struct S, and hah! that is a
redundant node because it's the first node that we visited when
visiting the diff node of ...  struct S!  So the diff tree node for
the change of struct S is not only a cyclic node, it's a redundant
diff node as well, and its second occurrence is located at the point
of appearance of data member S::m2.  Hence the wording "cyclic
redundant diff tree node".  There! We have our vocabulary all set now.

This patch enhances the code of the comparison engine so that a cyclic
diff tree node is marked as redundant from the point of its second
occurrence, onward.

First the patch separates the notion of visiting a diff node from the
notion of traversing it.  Now traversing a diff node means visiting it
and visiting its children nodes.  So one can visit a node without
traversing it, but one can not traverse a node without visiting it.

So, when walking diff node trees, we need to avoid ending up in
infinite loop in presence of cyclic nodes.  This is why re-traversing
a node that is already being traversed is forbidden by this patch, but
visiting a node that is being visited is allowed.  Before this patch,
the notions of visiting and traversing were conflated in one and were
not very clear; and one couldn't visit a node that was currently being
visited.  As a result, in presence of a cyclic node, its redundant
nature wasn't being recognized, and so the diff tree node was not
being flagged as being redundant.  Diff reports were then cluttered by
redundant references to changes involving cyclic types.

	* include/abg-comparison.h (enum visiting_kind): Rename
	enumerator DO_NOT_MARK_VISITED_NODES_AS_TRAVERSED into
	DO_NOT_MARK_VISITED_NODES_AS_VISITED.
	(diff_context::diff_has_been_visited): Rename
	diff_context::diff_has_been_traversed into this.
	(diff_context::mark_diff_as_visited): Rename
	diff_context::mark_diff_as_traversed into this.
	(diff_context::forget_visited_diffs): Rename
	diff_context::forget_traversed_diffs into this.
	(diff_context::forbid_visiting_a_node_twice): Rename
	diff_context::forbid_traversing_a_node_twice into this.
	(diff_context::visiting_a_node_twice_is_forbidden): Rename
	diff_context::traversing_a_node_twice_is_forbidden into this.
	(diff::is_traversing): Move this from protected to public.
	* src/abg-comparison.cc (diff_context::priv::visited_diff_nodes_):
	Rename diff_context::priv::traversed_diff_nodes_ into this.
	(diff_context::priv::forbid_visiting_a_node_twice_): Rename
	diff_context::priv::forbid_traversing_a_node_twice_ into this.
	(diff_context::priv::priv): Adjust.
	(diff_context::diff_has_been_visited): Rename
	diff_context::diff_has_been_traversed into this.  Adjust.
	(diff_context::mark_diff_as_visited): Rename
	diff_context::mark_diff_as_traversed into this.  Adjust.
	(diff_context::forget_visited_diffs): Rename
	diff_context::forget_traversed_diffs into this.  Adjust.
	(diff_context::forbid_visiting_a_node_twice): Rename
	diff_context::forbid_traversing_a_node_twice into this.
	(diff_context::visiting_a_node_twice_is_forbidden): Rename
	diff_context::traversing_a_node_twice_is_forbidden into this.
	(diff_context::maybe_apply_filters): Adjust.
	(diff::end_traversing): Remove the 'mark_as_traversed' parameter
	of this.  Remove the visited-marking code.
	(diff::traverse): This is the crux of the changes of this patch.
	Avoid traversing a node that is being traversed, but one can visit
	a node being visited.  Also, traversing a node means visiting it
	and visiting its children nodes.
	(diff::is_filtered_out):  Simplify logic for filtering redundant
	code.  Basically all nodes that are redundant are filtered.  All
	the complicated logic that was due when diff nodes were shared is
	not relevant anymore.
	(corpus_diff::priv::categorize_redundant_changed_sub_nodes)
	(propagate_categories, apply_suppressions)
	(diff_node_printer::diff_node_printer, print_diff_tree)
	(categorize_redundant_changed_sub_nodes)
	(clear_redundancy_categorization)
	(clear_redundancy_categorization): Adjust.
	(redundancy_marking_visitor::visit_begin): Adjust.  Also, if the
	current diff node is already being traversed (that's a clyclic
	node) then mark it as redundant.
	* src/abg-comp-filter.cc (apply_filter): Adjust.
	* tests/data/test-diff-filter/test16-report-2.txt: New test input data.
	* tests/data/test-diff-filter/libtest25-cyclic-type-v{0,1}.so: New
	test input binaries.
	* tests/data/test-diff-filter/test25-cyclic-type-v{0,1}.cc: Source
	code for the test input binaries.
	* tests/data/test-diff-filter/test25-cyclic-type-report-0.txt: New
	test input data.
	* tests/data/test-diff-filter/test25-cyclic-type-report-1.txt:
	Likewise.
	* tests/test-diff-filter.cc (in_out_specs): Add the new test
	inputs above to the list of test input data over which to run this
	test harness.
	* tests/data/Makefile.am: Add the new test files above to source
	distribution.
	* tests/data/test-diff-filter/test16-report.txt: Adjust.
	* tests/data/test-diff-filter/test17-0-report.txt: Likewise.

Signed-off-by: Dodji Seketeli <dodji@redhat.com>
This commit is contained in:
Dodji Seketeli 2015-01-24 20:51:16 +01:00
parent f3344623d3
commit ddfb37ab17
14 changed files with 238 additions and 136 deletions

View File

@ -254,7 +254,7 @@ enum visiting_kind
/// This says that the traversing code should not mark visited nodes
/// as having been traversed. This is useful, for instance, for
/// visitors which have debugging purposes.
DO_NOT_MARK_VISITED_NODES_AS_TRAVERSED = 1 << 1
DO_NOT_MARK_VISITED_NODES_AS_VISITED = 1 << 1
};
visiting_kind
@ -792,22 +792,22 @@ public:
initialize_canonical_diff(const diff_sptr diff);
bool
diff_has_been_traversed(const diff*) const;
diff_has_been_visited(const diff*) const;
bool
diff_has_been_traversed(const diff_sptr) const;
diff_has_been_visited(const diff_sptr) const;
void
mark_diff_as_traversed(const diff*);
mark_diff_as_visited(const diff*);
void
forget_traversed_diffs();
forget_visited_diffs();
void
forbid_traversing_a_node_twice(bool f);
forbid_visiting_a_node_twice(bool f);
bool
traversing_a_node_twice_is_forbidden() const;
visiting_a_node_twice_is_forbidden() const;
diff_category
get_allowed_category() const;
@ -977,13 +977,8 @@ protected:
void
begin_traversing();
bool
is_traversing() const;
void
end_traversing(bool mark_as_traversed = true);
protected:
end_traversing();
virtual void
finish_diff_type();
@ -1006,6 +1001,9 @@ public:
diff* get_canonical_diff() const;
bool
is_traversing() const;
void
append_child_node(diff_sptr);

View File

@ -46,10 +46,10 @@ using std::tr1::dynamic_pointer_cast;
void
apply_filter(filter_base& filter, corpus_diff_sptr d)
{
bool s = d->context()->traversing_a_node_twice_is_forbidden();
d->context()->forbid_traversing_a_node_twice(false);
bool s = d->context()->visiting_a_node_twice_is_forbidden();
d->context()->forbid_visiting_a_node_twice(false);
d->traverse(filter);
d->context()->forbid_traversing_a_node_twice(s);
d->context()->forbid_visiting_a_node_twice(s);
}
/// Walk a diff sub-tree and apply a filter to the nodes visted. The
@ -62,10 +62,10 @@ apply_filter(filter_base& filter, corpus_diff_sptr d)
void
apply_filter(filter_base& filter, diff_sptr d)
{
bool s = d->context()->traversing_a_node_twice_is_forbidden();
d->context()->forbid_traversing_a_node_twice(false);
bool s = d->context()->visiting_a_node_twice_is_forbidden();
d->context()->forbid_visiting_a_node_twice(false);
d->traverse(filter);
d->context()->forbid_traversing_a_node_twice(s);
d->context()->forbid_visiting_a_node_twice(s);
}
/// Walk a diff sub-tree and apply a filter to the nodes visted. The

View File

@ -2273,12 +2273,12 @@ struct diff_context::priv
vector<diff_sptr> canonical_diffs;
vector<filtering::filter_base_sptr> filters_;
suppressions_type suppressions_;
pointer_map traversed_diff_nodes_;
pointer_map visited_diff_nodes_;
corpus_sptr first_corpus_;
corpus_sptr second_corpus_;
ostream* default_output_stream_;
ostream* error_output_stream_;
bool forbid_traversing_a_node_twice_;
bool forbid_visiting_a_node_twice_;
bool show_stats_only_;
bool show_soname_change_;
bool show_architecture_change_;
@ -2298,7 +2298,7 @@ struct diff_context::priv
: allowed_category_(EVERYTHING_CATEGORY),
default_output_stream_(),
error_output_stream_(),
forbid_traversing_a_node_twice_(true),
forbid_visiting_a_node_twice_(true),
show_stats_only_(false),
show_soname_change_(true),
show_architecture_change_(true),
@ -2598,61 +2598,61 @@ diff_context::initialize_canonical_diff(const diff_sptr diff)
///
/// @param d the diff node to consider.
bool
diff_context::diff_has_been_traversed(const diff* d) const
diff_context::diff_has_been_visited(const diff* d) const
{
const diff* canonical = d->get_canonical_diff();
assert(canonical);
size_t ptr_value = reinterpret_cast<size_t>(canonical);
return (priv_->traversed_diff_nodes_.find(ptr_value)
!= priv_->traversed_diff_nodes_.end());
return (priv_->visited_diff_nodes_.find(ptr_value)
!= priv_->visited_diff_nodes_.end());
}
/// Test if a diff node has been traversed.
///
/// @param d the diff node to consider.
bool
diff_context::diff_has_been_traversed(const diff_sptr d) const
{return diff_has_been_traversed(d.get());}
diff_context::diff_has_been_visited(const diff_sptr d) const
{return diff_has_been_visited(d.get());}
/// Mark a diff node as traversed by a traversing algorithm.
///
/// Actually, it's the @ref CanonicalDiff "canonical diff" of this
/// node that is marked as traversed.
///
/// Subsequent invocations of diff_has_been_traversed() on the diff
/// Subsequent invocations of diff_has_been_visited() on the diff
/// node will yield true.
void
diff_context::mark_diff_as_traversed(const diff* d)
diff_context::mark_diff_as_visited(const diff* d)
{
const diff* canonical = d->get_canonical_diff();
assert(canonical);
size_t ptr_value = reinterpret_cast<size_t>(canonical);
priv_->traversed_diff_nodes_[ptr_value] = true;
priv_->visited_diff_nodes_[ptr_value] = true;
}
/// Unmark all the diff nodes that were marked as being traversed.
void
diff_context::forget_traversed_diffs()
{priv_->traversed_diff_nodes_.clear();}
diff_context::forget_visited_diffs()
{priv_->visited_diff_nodes_.clear();}
/// This sets a flag that, if it's true, then during the traversing of
/// a diff nodes tree each node is traversed at most once.
/// a diff nodes tree each node is visited at most once.
///
/// @param f if true then during the traversing of a diff nodes tree
/// each node is traversed at most once.
/// each node is visited at most once.
void
diff_context::forbid_traversing_a_node_twice(bool f)
{priv_->forbid_traversing_a_node_twice_ = f;}
diff_context::forbid_visiting_a_node_twice(bool f)
{priv_->forbid_visiting_a_node_twice_ = f;}
/// Return a flag that, if true, then during the traversing of a diff
/// nodes tree each node is traversed at most once.
/// nodes tree each node is visited at most once.
///
/// @return the boolean flag.
bool
diff_context::traversing_a_node_twice_is_forbidden() const
{return priv_->forbid_traversing_a_node_twice_;}
diff_context::visiting_a_node_twice_is_forbidden() const
{return priv_->forbid_visiting_a_node_twice_;}
/// Getter for the diff tree nodes filters to apply to diff sub-trees.
///
@ -2678,14 +2678,14 @@ diff_context::add_diff_filter(filtering::filter_base_sptr f)
/// @param diff the diff sub-tree to apply the filters to.
void
diff_context::maybe_apply_filters(diff_sptr diff,
bool traverse_nodes_once)
bool visit_nodes_once)
{
if (get_allowed_category() == EVERYTHING_CATEGORY)
return;
bool s = traversing_a_node_twice_is_forbidden();
if (!traverse_nodes_once)
forbid_traversing_a_node_twice(false);
bool s = visiting_a_node_twice_is_forbidden();
if (!visit_nodes_once)
forbid_visiting_a_node_twice(false);
for (filtering::filters::const_iterator i = diff_filters().begin();
i != diff_filters().end();
@ -2695,8 +2695,8 @@ diff_context::maybe_apply_filters(diff_sptr diff,
propagate_categories(diff);
}
if (!traverse_nodes_once)
forbid_traversing_a_node_twice(s);
if (!visit_nodes_once)
forbid_visiting_a_node_twice(s);
}
/// Apply the diff filters to the diff nodes of a @ref corpus_diff
@ -2709,11 +2709,11 @@ diff_context::maybe_apply_filters(diff_sptr diff,
/// @param diff the corpus diff to apply the filters to.
void
diff_context::maybe_apply_filters(corpus_diff_sptr diff,
bool traverse_nodes_once)
bool visit_nodes_once)
{
bool s = traversing_a_node_twice_is_forbidden();
if (!traverse_nodes_once)
forbid_traversing_a_node_twice(false);
bool s = visiting_a_node_twice_is_forbidden();
if (!visit_nodes_once)
forbid_visiting_a_node_twice(false);
for (filtering::filters::const_iterator i = diff_filters().begin();
i != diff_filters().end();
@ -2723,8 +2723,8 @@ diff_context::maybe_apply_filters(corpus_diff_sptr diff,
propagate_categories(diff);
}
if (!traverse_nodes_once)
forbid_traversing_a_node_twice(s);
if (!visit_nodes_once)
forbid_visiting_a_node_twice(s);
}
/// Getter for the vector of suppressions that specify which diff node
@ -3104,6 +3104,9 @@ diff::diff(decl_base_sptr first_subject,
/// given node as being traversed (or not), so that
/// diff::is_traversing() can tell if the node is being traversed.
///
/// Note that traversing a node means visiting it *and* visiting its
/// children nodes.
///
/// The canonical node is marked as being traversed too.
///
/// These functions are called by the traversing code.
@ -3118,6 +3121,9 @@ diff::begin_traversing()
/// Tell if a given node is being traversed or not.
///
/// Note that traversing a node means visiting it *and* visiting its
/// children nodes.
///
/// It's the canonical node which is looked at, actually.
///
/// Please read the comments for the diff::begin_traversing() for mode
@ -3134,25 +3140,18 @@ diff::is_traversing() const
/// Flag a given diff node as not being traversed anymore.
///
/// Note that traversing a node means visiting it *and* visiting its
/// children nodes.
///
/// Please read the comments of the function diff::begin_traversing()
/// for mode context.
///
/// @param mark_as_traversed if set to true mark the current @ref diff
/// node has having been traversed. That way, subsequent calls to
/// diff_context::diff_has_been_traversed() on the current instance of
/// @ref diff will yield true.
void
diff::end_traversing(bool mark_as_traversed)
diff::end_traversing()
{
assert(is_traversing());
if (priv_->canonical_diff_)
priv_->canonical_diff_->priv_->traversing_ = false;
priv_->traversing_ = false;
// Make this node as having been traversed, to allow the detection
// of nodes that might have been visited more than once.
if (mark_as_traversed)
context()->mark_diff_as_traversed(this);
}
/// Finish the building of a given kind of a diff tree node.
@ -3286,6 +3285,12 @@ diff::reported_once() const
/// The generic traversing code that walks a given diff sub-tree.
///
/// Note that there is a difference between traversing a diff node and
/// visiting it. Basically, traversing a diff node means visiting it
/// and visiting its children nodes too. So one can visit a node
/// without traversing it. But traversing a node without visiting it
/// is not possible.
///
/// @param v the entity that visits each node of the diff sub-tree.
///
/// @return true to tell the caller that all of the sub-tree could be
@ -3296,51 +3301,58 @@ diff::traverse(diff_node_visitor& v)
{
finish_diff_type();
if (context()->traversing_a_node_twice_is_forbidden()
&& context()->diff_has_been_traversed(this))
if (context()->visiting_a_node_twice_is_forbidden()
&& context()->diff_has_been_visited(this))
return true;
if (is_traversing())
return true;
begin_traversing();
v.visit_begin(this);
bool mark_visited_nodes_as_traversed =
!(v.get_visiting_kind() & DO_NOT_MARK_VISITED_NODES_AS_TRAVERSED);
!(v.get_visiting_kind() & DO_NOT_MARK_VISITED_NODES_AS_VISITED);
if (!v.visit(this, /*pre=*/true))
{
v.visit_end(this);
end_traversing(mark_visited_nodes_as_traversed);
if (mark_visited_nodes_as_traversed)
context()->mark_diff_as_visited(this);
return false;
}
if (!(v.get_visiting_kind() & SKIP_CHILDREN_VISITING_KIND))
for (vector<diff_sptr>::const_iterator i = children_nodes().begin();
i != children_nodes().end();
++i)
{
if (!(*i)->traverse(v))
{
v.visit_end(this);
end_traversing(mark_visited_nodes_as_traversed);
return false;
}
}
if (!(v.get_visiting_kind() & SKIP_CHILDREN_VISITING_KIND)
&& !is_traversing())
{
begin_traversing();
for (vector<diff_sptr>::const_iterator i = children_nodes().begin();
i != children_nodes().end();
++i)
{
if (!(*i)->traverse(v))
{
v.visit_end(this);
if (mark_visited_nodes_as_traversed)
context()->mark_diff_as_visited(this);
end_traversing();
return false;
}
}
end_traversing();
}
if (!v.visit(this, /*pref=*/false))
{
v.visit_end(this);
end_traversing(mark_visited_nodes_as_traversed);
if (mark_visited_nodes_as_traversed)
context()->mark_diff_as_visited(this);
return false;
}
v.visit_end(this);
end_traversing(mark_visited_nodes_as_traversed);
if (mark_visited_nodes_as_traversed)
context()->mark_diff_as_visited(this);
return true;
}
/// Sets a flag saying if a report has already been emitted for the
/// current diff.
///
@ -3416,17 +3428,11 @@ diff::is_filtered_out() const
if (get_category() & SUPPRESSED_CATEGORY)
return true;
// We don't want to display redundant top-level function or variable
// diff nodes, when the user asked to avoid seeing redundant diff
// nodes.
if ((dynamic_cast<const function_decl_diff*>(this)
|| (dynamic_cast<const var_diff*>(this)
// We are talking about top-level variables diff nodes, not
// member variables diff nodes.
&& !is_member_decl(first_subject())))
&& !context()->show_redundant_changes())
if (get_category() & REDUNDANT_CATEGORY)
return true;
// We don't want to display redundant diff nodes, when the user
// asked to avoid seeing redundant diff nodes.
if (!context()->show_redundant_changes()
&& (get_category() & REDUNDANT_CATEGORY))
return true;
if (get_category() == NO_CHANGE_CATEGORY)
return false;
@ -10467,7 +10473,7 @@ corpus_diff::priv::categorize_redundant_changed_sub_nodes()
{
diff_sptr diff;
ctxt_->forget_traversed_diffs();
ctxt_->forget_visited_diffs();
for (function_decl_diff_sptrs_type::const_iterator i =
changed_fns_.begin();
i!= changed_fns_.end();
@ -11802,10 +11808,10 @@ void
propagate_categories(diff* diff_tree)
{
category_propagation_visitor v;
bool s = diff_tree->context()->traversing_a_node_twice_is_forbidden();
diff_tree->context()->forbid_traversing_a_node_twice(false);
bool s = diff_tree->context()->visiting_a_node_twice_is_forbidden();
diff_tree->context()->forbid_visiting_a_node_twice(false);
diff_tree->traverse(v);
diff_tree->context()->forbid_traversing_a_node_twice(s);
diff_tree->context()->forbid_visiting_a_node_twice(s);
}
/// Visit all the nodes of a given sub-tree. For each node that has a
@ -11828,10 +11834,10 @@ void
propagate_categories(corpus_diff* diff_tree)
{
category_propagation_visitor v;
bool s = diff_tree->context()->traversing_a_node_twice_is_forbidden();
diff_tree->context()->forbid_traversing_a_node_twice(false);
bool s = diff_tree->context()->visiting_a_node_twice_is_forbidden();
diff_tree->context()->forbid_visiting_a_node_twice(false);
diff_tree->traverse(v);
diff_tree->context()->forbid_traversing_a_node_twice(s);
diff_tree->context()->forbid_visiting_a_node_twice(s);
}
/// Visit all the nodes of a given corpus tree. For each node that
@ -11919,10 +11925,10 @@ void
apply_suppressions(diff* diff_tree)
{
suppression_categorization_visitor v;
bool s = diff_tree->context()->traversing_a_node_twice_is_forbidden();
diff_tree->context()->forbid_traversing_a_node_twice(false);
bool s = diff_tree->context()->visiting_a_node_twice_is_forbidden();
diff_tree->context()->forbid_visiting_a_node_twice(false);
diff_tree->traverse(v);
diff_tree->context()->forbid_traversing_a_node_twice(s);
diff_tree->context()->forbid_visiting_a_node_twice(s);
}
/// Walk a given diff-sub tree and appply the suppressions carried by
@ -11945,10 +11951,10 @@ void
apply_suppressions(const corpus_diff* diff_tree)
{
suppression_categorization_visitor v;
bool s = diff_tree->context()->traversing_a_node_twice_is_forbidden();
diff_tree->context()->forbid_traversing_a_node_twice(false);
bool s = diff_tree->context()->visiting_a_node_twice_is_forbidden();
diff_tree->context()->forbid_visiting_a_node_twice(false);
const_cast<corpus_diff*>(diff_tree)->traverse(v);
diff_tree->context()->forbid_traversing_a_node_twice(s);
diff_tree->context()->forbid_visiting_a_node_twice(s);
}
/// Walk a diff tree and appply the suppressions carried by the
@ -11984,7 +11990,7 @@ struct diff_node_printer : public diff_node_visitor
}
diff_node_printer(ostream& out)
: diff_node_visitor(DO_NOT_MARK_VISITED_NODES_AS_TRAVERSED),
: diff_node_visitor(DO_NOT_MARK_VISITED_NODES_AS_VISITED),
out_(out),
level_(0)
{}
@ -12073,10 +12079,10 @@ void
print_diff_tree(diff* diff_tree, ostream& out)
{
diff_node_printer p(out);
bool s = diff_tree->context()->traversing_a_node_twice_is_forbidden();
diff_tree->context()->forbid_traversing_a_node_twice(false);
bool s = diff_tree->context()->visiting_a_node_twice_is_forbidden();
diff_tree->context()->forbid_visiting_a_node_twice(false);
diff_tree->traverse(p);
diff_tree->context()->forbid_traversing_a_node_twice(s);
diff_tree->context()->forbid_visiting_a_node_twice(s);
}
/// Emit a textual representation of a @ref corpus_diff tree to an
@ -12091,10 +12097,10 @@ void
print_diff_tree(corpus_diff* diff_tree, std::ostream& out)
{
diff_node_printer p(out);
bool s = diff_tree->context()->traversing_a_node_twice_is_forbidden();
diff_tree->context()->forbid_traversing_a_node_twice(false);
bool s = diff_tree->context()->visiting_a_node_twice_is_forbidden();
diff_tree->context()->forbid_visiting_a_node_twice(false);
diff_tree->traverse(p);
diff_tree->context()->forbid_traversing_a_node_twice(s);
diff_tree->context()->forbid_visiting_a_node_twice(s);
}
/// Emit a textual representation of a @ref diff sub-tree to an
@ -12146,7 +12152,9 @@ struct redundancy_marking_visitor : public diff_node_visitor
{
// 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())
if ((d->context()->diff_has_been_visited(d)
|| d->get_canonical_diff()->is_traversing())
&& d->length())
{
// But if two diff nodes are redundant sibbling, do not
// mark them as being redundant. This is to avoid marking
@ -12299,10 +12307,10 @@ categorize_redundancy(diff* diff_tree)
if (diff_tree->context()->show_redundant_changes())
return;
redundancy_marking_visitor v;
bool s = diff_tree->context()->traversing_a_node_twice_is_forbidden();
diff_tree->context()->forbid_traversing_a_node_twice(false);
bool s = diff_tree->context()->visiting_a_node_twice_is_forbidden();
diff_tree->context()->forbid_visiting_a_node_twice(false);
diff_tree->traverse(v);
diff_tree->context()->forbid_traversing_a_node_twice(s);
diff_tree->context()->forbid_visiting_a_node_twice(s);
}
/// Walk a given @ref diff sub-tree to categorize each of the nodes
@ -12321,11 +12329,11 @@ void
categorize_redundancy(corpus_diff* diff_tree)
{
redundancy_marking_visitor v;
diff_tree->context()->forget_traversed_diffs();
bool s = diff_tree->context()->traversing_a_node_twice_is_forbidden();
diff_tree->context()->forbid_traversing_a_node_twice(false);
diff_tree->context()->forget_visited_diffs();
bool s = diff_tree->context()->visiting_a_node_twice_is_forbidden();
diff_tree->context()->forbid_visiting_a_node_twice(false);
diff_tree->traverse(v);
diff_tree->context()->forbid_traversing_a_node_twice(s);
diff_tree->context()->forbid_visiting_a_node_twice(s);
}
/// Walk a given @ref corpus_diff tree to categorize each of the nodes
@ -12346,11 +12354,11 @@ void
clear_redundancy_categorization(diff* diff_tree)
{
redundancy_clearing_visitor v;
bool s = diff_tree->context()->traversing_a_node_twice_is_forbidden();
diff_tree->context()->forbid_traversing_a_node_twice(false);
bool s = diff_tree->context()->visiting_a_node_twice_is_forbidden();
diff_tree->context()->forbid_visiting_a_node_twice(false);
diff_tree->traverse(v);
diff_tree->context()->forbid_traversing_a_node_twice(s);
diff_tree->context()->forget_traversed_diffs();
diff_tree->context()->forbid_visiting_a_node_twice(s);
diff_tree->context()->forget_visited_diffs();
}
/// Walk a given @ref diff sub-tree to clear the REDUNDANT_CATEGORY
@ -12369,11 +12377,11 @@ void
clear_redundancy_categorization(corpus_diff* diff_tree)
{
redundancy_clearing_visitor v;
bool s = diff_tree->context()->traversing_a_node_twice_is_forbidden();
diff_tree->context()->forbid_traversing_a_node_twice(false);
bool s = diff_tree->context()->visiting_a_node_twice_is_forbidden();
diff_tree->context()->forbid_visiting_a_node_twice(false);
diff_tree->traverse(v);
diff_tree->context()->forbid_traversing_a_node_twice(s);
diff_tree->context()->forget_traversed_diffs();
diff_tree->context()->forbid_visiting_a_node_twice(s);
diff_tree->context()->forget_visited_diffs();
}
/// Walk a given @ref corpus_diff tree to clear the REDUNDANT_CATEGORY

View File

@ -307,7 +307,8 @@ test-diff-filter/test16-v0.cc \
test-diff-filter/test16-v1.cc \
test-diff-filter/test16-v0.o \
test-diff-filter/test16-v1.o \
test-diff-filter/test16-report.txt \
test-diff-filter/test16-report.txt \
test-diff-filter/test16-report-2.txt \
test-diff-filter/test17-v0.cc \
test-diff-filter/test17-v1.cc \
test-diff-filter/test17-v0.o \
@ -354,6 +355,12 @@ test-diff-filter/test24-compatible-vars-report-0.txt \
test-diff-filter/test24-compatible-vars-report-1.txt \
test-diff-filter/test24-compatible-vars-v0.c \
test-diff-filter/test24-compatible-vars-v1.c \
test-diff-filter/libtest25-cyclic-type-v0.so \
test-diff-filter/libtest25-cyclic-type-v1.so \
test-diff-filter/test25-cyclic-type-report-0.txt \
test-diff-filter/test25-cyclic-type-report-1.txt \
test-diff-filter/test25-cyclic-type-v0.cc \
test-diff-filter/test25-cyclic-type-v1.cc \
\
test-diff-suppr/test0-type-suppr-v0.cc \
test-diff-suppr/test0-type-suppr-v1.cc \

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,17 @@
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 foo(S&)' has some indirect sub-type changes:
parameter 0 of type 'S&' has sub-type changes:
in referenced type 'struct S':
size changed from 64 to 128 bits
1 data member insertion:
'int S::m0', at offset 0 (in bits)
1 data member change:
'S* S::m2' offset changed from 0 to 64
and its type 'S*' changed:
pointed to type 'struct S' changed; details are being reported

View File

@ -11,7 +11,5 @@ Variables changes summary: 0 Removed, 0 Changed, 0 Added variable
'int S::m0', at offset 0 (in bits)
1 data member change:
'S* S::m2' offset changed from 0 to 64
and its type 'S*' changed:
pointed to type 'struct S' changed; details are being reported

View File

@ -11,7 +11,5 @@ Variables changes summary: 0 Removed, 0 Changed, 0 Added variable
'int S::m0', at offset 0 (in bits)
1 data member change:
'S* S::m2' offset changed from 0 to 64
and its type 'S*' changed:
pointed to type 'struct S' changed; details are being reported

View File

@ -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 foo(S&)' has some indirect sub-type changes:
parameter 0 of type 'S&' has sub-type changes:
in referenced type 'struct S':
1 data member insertion:
'char S::m1', at offset 32 (in bits)
no data member change (1 filtered);

View File

@ -0,0 +1,15 @@
Functions changes summary: 0 Removed, 1 Changed, 0 Added function
Variables changes summary: 0 Removed, 0 Changed, 0 Added variable
1 function with some indirect sub-type change:
[C]'function void foo(S&)' has some indirect sub-type changes:
parameter 0 of type 'S&' has sub-type changes:
in referenced type 'struct S':
1 data member insertion:
'char S::m1', at offset 32 (in bits)
1 data member change:
type of 'S* S::m2' changed:
pointed to type 'struct S' changed; details are being reported

View File

@ -0,0 +1,13 @@
// Compile with:
// g++ -g -shared -o libtest25-cyclic-type-v0.so test25-cyclic-type-v0.cc
struct S
{
int m0;
S* m2;
};
void
foo(S&)
{
}

View File

@ -0,0 +1,14 @@
// Compile with:
// g++ -g -shared -o libtest25-cyclic-type-v1.so test25-cyclic-type-v1.cc
struct S
{
int m0;
char m1;
S* m2;
};
void
foo(S&)
{
}

View File

@ -194,6 +194,13 @@ InOutSpec in_out_specs[] =
"data/test-diff-filter/test16-report.txt",
"output/test-diff-filter/test16-report.txt",
},
{
"data/test-diff-filter/test16-v0.o",
"data/test-diff-filter/test16-v1.o",
"--redundant",
"data/test-diff-filter/test16-report-2.txt",
"output/test-diff-filter/test16-report-2.txt",
},
{
"data/test-diff-filter/test17-v0.o",
"data/test-diff-filter/test17-v1.o",
@ -292,6 +299,20 @@ InOutSpec in_out_specs[] =
"data/test-diff-filter/test24-compatible-vars-report-1.txt ",
"output/test-diff-filter/test24-compatible-vars-report-1.txt ",
},
{
"data/test-diff-filter/libtest25-cyclic-type-v0.so",
"data/test-diff-filter/libtest25-cyclic-type-v1.so",
"",
"data/test-diff-filter/test25-cyclic-type-report-0.txt ",
"output/test-diff-filter/test25-cyclic-type-report-0.txt "
},
{
"data/test-diff-filter/libtest25-cyclic-type-v0.so",
"data/test-diff-filter/libtest25-cyclic-type-v1.so",
"--redundant",
"data/test-diff-filter/test25-cyclic-type-report-1.txt ",
"output/test-diff-filter/test25-cyclic-type-report-1.txt "
},
// This should be the last entry
{NULL, NULL, NULL, NULL, NULL}
};