/// The default enumerator value of this enum. It doesn't have any
/// particular meaning yet.
DEFAULT_VISITING_KIND = 0,
+
/// This says that the traversing code should avoid visiting the
/// children nodes of the current node being visited.
- SKIP_CHILDREN_VISITING_KIND = 1
+ SKIP_CHILDREN_VISITING_KIND = 1,
+
+ /// 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
};
visiting_kind
void
show_added_symbols_unreferenced_by_debug_info(bool f);
+ void
+ default_output_stream(ostream*);
+
+ ostream*
+ default_output_stream();
+
+ void
+ error_output_stream(ostream*);
+
+ ostream*
+ error_output_stream();
+
+ bool
+ dump_diff_tree() const;
+
+ void
+ dump_diff_tree(bool f);
+
friend class_diff_sptr
compute_diff(const class_decl_sptr first,
const class_decl_sptr second,
is_traversing() const;
void
- end_traversing();
+ end_traversing(bool mark_as_traversed = true);
protected:
public:
+ /// Default constructor of the @ref diff_node_visitor type.
diff_node_visitor()
: visiting_kind_()
{}
+ /// Constructor of the @ref diff_node_visitor type.
+ ///
+ /// @param k how the visiting has to be performed.
+ diff_node_visitor(visiting_kind k)
+ : visiting_kind_(k)
+ {}
+
/// Getter for the visiting policy of the traversing code while
/// invoking this visitor.
///
print_diff_tree(corpus_diff* diff_tree,
std::ostream&);
+void
+print_diff_tree(diff_sptr diff_tree,
+ std::ostream&);
+
void
print_diff_tree(corpus_diff_sptr diff_tree,
std::ostream&);
pointer_map traversed_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 show_stats_only_;
bool show_soname_change_;
bool show_redundant_changes_;
bool show_syms_unreferenced_by_di_;
bool show_added_syms_unreferenced_by_di_;
+ bool dump_diff_tree_;
priv()
: allowed_category_(EVERYTHING_CATEGORY),
+ default_output_stream_(),
+ error_output_stream_(),
forbid_traversing_a_node_twice_(true),
show_stats_only_(false),
show_soname_change_(true),
show_linkage_names_(false),
show_redundant_changes_(true),
show_syms_unreferenced_by_di_(true),
- show_added_syms_unreferenced_by_di_(true)
+ show_added_syms_unreferenced_by_di_(true),
+ dump_diff_tree_()
{}
};// end struct diff_context::priv
diff_context::show_added_symbols_unreferenced_by_debug_info(bool f)
{priv_->show_added_syms_unreferenced_by_di_ = f;}
+/// Setter for the default output stream used by code of the
+/// comparison engine. By default the default output stream is a NULL
+/// pointer.
+///
+/// @param o a pointer to the default output stream.
+void
+diff_context::default_output_stream(ostream* o)
+{priv_->default_output_stream_ = o;}
+
+/// Getter for the default output stream used by code of the
+/// comparison engine. By default the default output stream is a NULL
+/// pointer.
+///
+/// @return a pointer to the default output stream.
+ostream*
+diff_context::default_output_stream()
+{return priv_->default_output_stream_;}
+
+/// Setter for the errror output stream used by code of the comparison
+/// engine. By default the error output stream is a NULL pointer.
+///
+/// @param o a pointer to the error output stream.
+void
+diff_context::error_output_stream(ostream* o)
+{priv_->error_output_stream_ = o;}
+
+/// Getter for the errror output stream used by code of the comparison
+/// engine. By default the error output stream is a NULL pointer.
+///
+/// @return a pointer to the error output stream.
+ostream*
+diff_context::error_output_stream()
+{return priv_->error_output_stream_;}
+
+/// Test if the comparison engine should dump the diff tree for the
+/// changed functions and variables it has.
+///
+/// @return true if after the comparison, the engine should dump the
+/// diff tree for the changed functions and variables it has.
+bool
+diff_context::dump_diff_tree() const
+{return priv_->dump_diff_tree_;}
+
+/// Set if the comparison engine should dump the diff tree for the
+/// changed functions and variables it has.
+///
+/// @param f true if after the comparison, the engine should dump the
+/// diff tree for the changed functions and variables it has.
+void
+diff_context::dump_diff_tree(bool f)
+{priv_->dump_diff_tree_ = f;}
+
// </diff_context stuff>
// <diff stuff>
/// Flag a given diff node as not being traversed anymore.
///
/// Please read the comments of the function diff::begin_traversing()
-/// for mode context;
+/// 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()
+diff::end_traversing(bool mark_as_traversed)
{
assert(is_traversing());
if (priv_->canonical_diff_)
// Make this node as having been traversed, to allow the detection
// of nodes that might have been visited more than once.
- context()->mark_diff_as_traversed(this);
+ if (mark_as_traversed)
+ context()->mark_diff_as_traversed(this);
}
/// Finish the building of a given kind of a diff tree node.
begin_traversing();
v.visit_begin(this);
+ bool mark_visited_nodes_as_traversed =
+ !(v.get_visiting_kind() & DO_NOT_MARK_VISITED_NODES_AS_TRAVERSED);
+
if (!v.visit(this, /*pre=*/true))
{
v.visit_end(this);
- end_traversing();
+ end_traversing(mark_visited_nodes_as_traversed);
return false;
}
if (!(*i)->traverse(v))
{
v.visit_end(this);
- end_traversing();
+ end_traversing(mark_visited_nodes_as_traversed);
return false;
}
}
if (!v.visit(this, /*pref=*/false))
{
v.visit_end(this);
- end_traversing();
+ end_traversing(mark_visited_nodes_as_traversed);
return false;
}
v.visit_end(this);
- end_traversing();
+ end_traversing(mark_visited_nodes_as_traversed);
return true;
}
void
clear_redundancy_categorization();
+ void
+ maybe_dump_diff_tree();
}; // end corpus::priv
/// Tests if the lookup tables are empty.
}
}
+/// If the user asked to dump the diff tree node (for changed
+/// variables and functions) on the error output stream, then just do
+/// that.
+///
+/// This function is used for debugging purposes.
+void
+corpus_diff::priv::maybe_dump_diff_tree()
+{
+ if (!ctxt_->dump_diff_tree()
+ || ctxt_->error_output_stream() == 0)
+ return;
+
+ if (!changed_fns_.empty())
+ {
+ *ctxt_->error_output_stream() << "changed functions diff tree: \n\n";
+ for (string_function_decl_diff_sptr_map::const_iterator i =
+ changed_fns_.begin();
+ i != changed_fns_.end();
+ ++i)
+ {
+ diff_sptr d = i->second;
+ print_diff_tree(d, *ctxt_->error_output_stream());
+ }
+ }
+
+ if (!changed_vars_.empty())
+ {
+ *ctxt_->error_output_stream() << "\nchanged variables diff tree: \n\n";
+ for (string_var_diff_sptr_map::const_iterator i = changed_vars_.begin();
+ i != changed_vars_.end();
+ ++i)
+ {
+ diff_sptr d = i->second;
+ print_diff_tree(d, *ctxt_->error_output_stream());
+ }
+ }
+}
+
/// Populate the vector of children node of the @ref corpus_diff type.
///
/// The children node can then later be retrieved using
out << '\n';
}
+ priv_->maybe_dump_diff_tree();
}
/// Traverse the diff sub-tree under the current instance corpus_diff.
unsigned level_;
diff_node_printer(ostream& out)
- : out_(out),
+ : diff_node_visitor(DO_NOT_MARK_VISITED_NODES_AS_TRAVERSED),
+ out_(out),
level_(0)
{}
}
virtual bool
- visit(diff* d, bool)
+ visit(diff* d, bool pre)
{
+ if (!pre)
+ // We are post-visiting the diff node D. Which means, we have
+ // printed a pretty representation for it already. So do
+ // nothing now.
+ return true;
+
// indent
for (unsigned i = 0; i < level_; ++i)
out_ << ' ';
}
virtual bool
- visit(corpus_diff* d, bool)
+ visit(corpus_diff* d, bool pre)
{
+ if (!pre)
+ // We are post-visiting the diff node D. Which means, we have
+ // printed a pretty representation for it already. So do
+ // nothing now.
+ return true;
+
// indent
for (unsigned i = 0; i < level_; ++i)
out_ << ' ';
diff_tree->context()->forbid_traversing_a_node_twice(s);
}
+/// Emit a textual representation of a @ref diff sub-tree to an
+/// output stream.
+///
+/// @param diff_tree the sub-tree to emit the textual representation
+/// for.
+///
+/// @param out the output stream to emit the textual representation
+/// for @p diff_tree to.
+void
+print_diff_tree(diff_sptr diff_tree,
+ std::ostream& o)
+{print_diff_tree(diff_tree.get(), o);}
+
/// Emit a textual representation of a @ref corpus_diff tree to an
/// output stream.
///
bool show_harmless_changes;
bool show_redundant_changes;
bool show_symbols_not_referenced_by_debug_info;
+ bool dump_diff_tree;
shared_ptr<char> di_root_path1;
shared_ptr<char> di_root_path2;
show_harmful_changes(true),
show_harmless_changes(false),
show_redundant_changes(false),
- show_symbols_not_referenced_by_debug_info(true)
+ show_symbols_not_referenced_by_debug_info(true),
+ dump_diff_tree()
{}
};//end struct options;
<< " --redundant display redundant changes\n"
<< " --no-redundant do not display redundant changes "
"(this is the default)\n"
+ << " --dump-diff-tree emit a debug dump of the internal diff tree to "
+ "the error output stream\n"
<< " --help display this message\n";
}
opts.show_redundant_changes = true;
else if (!strcmp(argv[i], "--no-redundant"))
opts.show_redundant_changes = false;
+ else if (!strcmp(argv[i], "--dump-diff-tree"))
+ opts.dump_diff_tree = true;
else
return false;
}
set_diff_context_from_opts(diff_context_sptr ctxt,
options& opts)
{
+ ctxt->default_output_stream(&cout);
+ ctxt->error_output_stream(&cerr);
ctxt->show_stats_only(opts.show_stats_only);
ctxt->show_deleted_fns(opts.show_all_fns || opts.show_deleted_fns);
ctxt->show_changed_fns(opts.show_all_fns || opts.show_changed_fns);
++i)
read_suppressions(*i, supprs);
ctxt->add_suppressions(supprs);
+
+ ctxt->dump_diff_tree(opts.dump_diff_tree);
}
/// Set the regex patterns describing the functions to drop from the