Support diff for enum_type_decl

* include/abg-ir.h (enum_type_decl_sptr): New typedef.
	(enum_type_decl::enumerator::enumerator): Make default constructor
	public so that enumerators can be stored in vectors.  Maybe I
	should have made stored pointers to enumerators instead ...
	(enum_type_decl::enumerator::get_qualified_name): Define new
	method.
	* include/abg-comparison.h (string_enumerator_map)
	(changed_enumerator, string_changed_enumerator_map)
	(enum_diff_sptr): New convenience typedefs.
	(class enum_diff): Declare new class.
	(compute_diff): New overload for enum_type_decl.
	* src/abg-comparison.cc (enum diff_kind, report_mem_header): Move
	these at the beginning of the file.
	(struct enum_diff::priv): Define this.
	(enum_diff::{clear_lookup_tables, lookup_tables_empty,
	ensure_lookup_tables_populated, enum_diff, first_enum,
	second_enum, underlying_type_diff, deleted_enumerators,
	inserted_enumerators, changed_enumerators, length, report}):
	Define these new methods.
	(compute_diff): New overload for enum_diff.
	(compute_diff_for_types): Add support enum_type_decl here.

Signed-off-by: Dodji Seketeli <dodji@redhat.com>
This commit is contained in:
Dodji Seketeli 2013-11-27 11:47:19 +01:00
parent cb05c8b3d9
commit d186189b43
3 changed files with 417 additions and 59 deletions

View File

@ -78,6 +78,19 @@ typedef unordered_map<string,
/// parameter. The key is the name of the function parm.
typedef unordered_map<string, function_decl::parameter_sptr> string_parm_map;
/// Convenience typedef for a map which value is an enumerator. The
/// key is the name of the enumerator.
typedef unordered_map<string, enum_type_decl::enumerator> string_enumerator_map;
/// Convenience typedef for a changed enumerator. The first element
/// of the pair is the old enumerator and the second one is the new enumerator.
typedef std::pair<enum_type_decl::enumerator,
enum_type_decl::enumerator> changed_enumerator;
/// Convenience typedef for a map which value is a changed enumerator.
/// The key is the name of the changed enumerator.
typedef unordered_map<string, changed_enumerator> string_changed_enumerator_map;
/// This type encapsulates an edit script (a set of insertions and
/// deletions) for two constructs that are to be diff'ed. The two
/// constructs are called the "subjects" of the diff.
@ -257,6 +270,64 @@ qualified_type_diff_sptr
compute_diff(const qualified_type_def_sptr,
const qualified_type_def_sptr);
class enum_diff;
typedef shared_ptr<enum_diff> enum_diff_sptr;
/// Abstraction of a diff between two enums.
class enum_diff : public diff
{
struct priv;
typedef shared_ptr<priv> priv_sptr;
priv_sptr priv_;
void
clear_lookup_tables();
bool
lookup_tables_empty() const;
void
ensure_lookup_tables_populated();
protected:
enum_diff(const enum_type_decl_sptr,
const enum_type_decl_sptr,
const diff_sptr);
public:
const enum_type_decl_sptr
first_enum() const;
const enum_type_decl_sptr
second_enum() const;
diff_sptr
underlying_type_diff() const;
const string_enumerator_map&
deleted_enumerators() const;
const string_enumerator_map&
inserted_enumerators() const;
const string_changed_enumerator_map&
changed_enumerators() const;
virtual unsigned
length() const;
virtual void
report(ostream&, const string& indent = "") const;
friend enum_diff_sptr
compute_diff(const enum_type_decl_sptr,
const enum_type_decl_sptr);
};//end class enum_diff;
enum_diff_sptr
compute_diff(const enum_type_decl_sptr,
const enum_type_decl_sptr);
class class_diff;
/// Convenience typedef for a shared pointer on a @ref class_diff type.

View File

@ -631,6 +631,9 @@ public:
virtual ~reference_type_def();
}; // end class reference_type_def
/// Convenience typedef for shared pointer on enum_type_decl.
typedef shared_ptr<enum_type_decl> enum_type_decl_sptr;
/// Abstracts a declaration for an enum type.
class enum_type_decl : public virtual type_base, public virtual decl_base
{
@ -645,11 +648,12 @@ public:
string name_;
size_t value_;
//Forbidden
enumerator();
public:
enumerator()
: value_(0)
{}
enumerator(const string& name, size_t value)
: name_(name), value_(value) { }
@ -663,6 +667,10 @@ public:
get_name() const
{return name_;}
const string
get_qualified_name(const enum_type_decl_sptr enum_type) const
{return enum_type->get_qualified_name() + "::" + get_name();}
void
set_name(const string& n)
{name_ = n;}

View File

@ -85,6 +85,7 @@ compute_diff_for_types(const decl_base_sptr first, const decl_base_sptr second)
diff_sptr d;
((d = try_to_diff_types<type_decl>(first, second))
||(d = try_to_diff_types<enum_type_decl>(first, second))
||(d = try_to_diff_types<class_decl>(first, second))
||(d = try_to_diff_types<pointer_type_def>(first, second))
||(d = try_to_diff_types<reference_type_def>(first, second))
@ -322,6 +323,61 @@ report_size_and_alignment_changes(decl_base_sptr first,
return false;
}
/// Represent the kind of difference we want report_mem_header() to
/// report.
enum diff_kind
{
del_kind,
ins_kind,
change_kind
};
/// Output the header preceding the the report for
/// insertion/deletion/change of a part of a class. This is a
/// subroutine of class_diff::report.
///
/// @param out the output stream to output the report to.
///
/// @param number the number of insertion/deletion to refer to in the
/// header.
///
/// @param k the kind of diff (insertion/deletion/change) we want the
/// head to introduce.
///
/// @param section_name the name of the sub-part of the class to
/// report about.
///
/// @param indent the string to use as indentation prefix in the
/// header.
static void
report_mem_header(ostream& out,
int number,
diff_kind k,
const string& section_name,
const string& indent)
{
string change;
switch (k)
{
case del_kind:
change = (number > 1) ? "deletions" : "deletion";
break;
case ins_kind:
change = (number > 1) ? "insertions" : "insertion";
break;
case change_kind:
change = (number > 1) ? "changes" : "change";
}
if (number == 0)
out << indent << "no " << section_name << " " << change << "\n";
else if (number == 1)
out << indent << "1 " << section_name << " " << change << ":\n";
else
out << indent << number << " " << section_name
<< " " << change << ":\n";
}
// <pointer_type_def stuff>
struct pointer_diff::priv
{
@ -653,7 +709,285 @@ compute_diff(const qualified_type_def_sptr first,
return result;
}
// </qualified_type_diff>
// </qualified_type_diff stuff>
// <enum_diff stuff>
struct enum_diff::priv
{
diff_sptr underlying_type_diff_;
edit_script enumerators_changes_;
string_enumerator_map deleted_enumerators_;
string_enumerator_map inserted_enumerators_;
string_changed_enumerator_map changed_enumerators_;
};//end struct enum_diff::priv
/// Clear the lookup tables useful for reporting an enum_diff.
///
/// This function must be updated each time a lookup table is added or
/// removed from the class_diff::priv.
void
enum_diff::clear_lookup_tables()
{
priv_->deleted_enumerators_.clear();
priv_->inserted_enumerators_.clear();
priv_->changed_enumerators_.clear();
}
/// Tests if the lookup tables are empty.
///
/// @return true if the lookup tables are empty, false otherwise.
bool
enum_diff::lookup_tables_empty() const
{
return (priv_->deleted_enumerators_.empty()
&& priv_->inserted_enumerators_.empty()
&& priv_->changed_enumerators_.empty());
}
/// If the lookup tables are not yet built, walk the differences and
/// fill the lookup tables.
void
enum_diff::ensure_lookup_tables_populated()
{
if (!lookup_tables_empty())
return;
{
edit_script e = priv_->enumerators_changes_;
for (vector<deletion>::const_iterator it = e.deletions().begin();
it != e.deletions().end();
++it)
{
unsigned i = it->index();
const enum_type_decl::enumerator& n =
first_enum()->get_enumerators()[i];
const string& name = n.get_name();
assert(priv_->deleted_enumerators_.find(n.get_name())
== priv_->deleted_enumerators_.end());
priv_->deleted_enumerators_[name] = n;
}
for (vector<insertion>::const_iterator it = e.insertions().begin();
it != e.insertions().end();
++it)
{
for (vector<unsigned>::const_iterator iit =
it->inserted_indexes().begin();
iit != it->inserted_indexes().end();
++iit)
{
unsigned i = *iit;
const enum_type_decl::enumerator& n =
second_enum()->get_enumerators()[i];
const string& name = n.get_name();
assert(priv_->inserted_enumerators_.find(n.get_name())
== priv_->inserted_enumerators_.end());
priv_->inserted_enumerators_[name] = n;
}
}
for (string_enumerator_map::const_iterator i =
priv_->deleted_enumerators_.begin();
i != priv_->deleted_enumerators_.end();
++i)
{
string_enumerator_map::const_iterator r =
priv_->inserted_enumerators_.find(i->first);
if (r != priv_->inserted_enumerators_.end())
priv_->changed_enumerators_[i->first] =
std::make_pair(i->second, r->second);
}
}
}
/// Constructor for enum_diff.
///
/// @param first the first enum type of the diff.
///
/// @param second the second enum type of the diff.
///
/// @param underlying_type_diff the diff of the two underlying types
/// of the two enum types.
enum_diff::enum_diff(const enum_type_decl_sptr first,
const enum_type_decl_sptr second,
const diff_sptr underlying_type_diff)
: diff(first, second),
priv_(new priv)
{priv_->underlying_type_diff_ = underlying_type_diff;}
/// @return the first enum of the diff.
const enum_type_decl_sptr
enum_diff::first_enum() const
{return dynamic_pointer_cast<enum_type_decl>(first_subject());}
/// @return the second enum of the diff.
const enum_type_decl_sptr
enum_diff::second_enum() const
{return dynamic_pointer_cast<enum_type_decl>(second_subject());}
/// @return the diff of the two underlying enum types.
diff_sptr
enum_diff::underlying_type_diff() const
{return priv_->underlying_type_diff_;}
/// @return a map of the enumerators that were deleted.
const string_enumerator_map&
enum_diff::deleted_enumerators() const
{return priv_->deleted_enumerators_;}
/// @return a map of the enumerators that were inserted
const string_enumerator_map&
enum_diff::inserted_enumerators() const
{return priv_->inserted_enumerators_;}
/// @return a map the enumerators that were changed
const string_changed_enumerator_map&
enum_diff::changed_enumerators() const
{return priv_->changed_enumerators_;}
/// @return the length of the diff.
unsigned
enum_diff::length() const
{
return (underlying_type_diff()->length()
+ priv_->enumerators_changes_.length());
}
/// Report the differences between the two enums.
///
/// @param out the output stream to send the report to.
///
/// @param the string to use for indentation.
void
enum_diff::report(ostream& out, const string& indent) const
{
string name = first_enum()->get_pretty_representation();
if (length() == 0)
{
out << indent << "the two versions of '" << name << "are identical\n\n";
return;
}
enum_type_decl_sptr first = first_enum(), second = second_enum();
if (report_size_and_alignment_changes(first, second, out, indent,
/*start_with_num_line=*/false))
out << "\n";
// name
if (first->get_name() != second->get_name())
out << indent << "enum name changed from '"
<< first->get_qualified_name() << "' to '"
<< second->get_qualified_name() << "'\n";
//underlying type
underlying_type_diff()->report(out, indent);
//report deletions/insertions/change of enumerators
unsigned numdels = deleted_enumerators().size();
unsigned numins = inserted_enumerators().size();
unsigned numchanges = changed_enumerators().size();
assert(numchanges <= numdels
&& numchanges <= numins);
numdels -= numchanges;
numins -= numchanges;
if (numdels)
{
report_mem_header(out, numdels, del_kind, "enumerator", indent);
for (string_enumerator_map::const_iterator i =
deleted_enumerators().begin();
i != deleted_enumerators().end();
++i)
{
if (changed_enumerators().find(i->first)
!= changed_enumerators().end())
continue;
if (i != deleted_enumerators().begin())
out << "\n";
out << indent
<< " '"
<< i->second.get_qualified_name(first)
<< "' value '"
<< i->second.get_value()
<< "'";
}
out << "\n\n";
}
if (numins)
{
report_mem_header(out, numins, ins_kind, "enumerator", indent);
for (string_enumerator_map::const_iterator i =
inserted_enumerators().begin();
i != inserted_enumerators().end();
++i)
{
if (changed_enumerators().find(i->first)
!= changed_enumerators().end())
continue;
if (i != inserted_enumerators().begin())
out << "\n";
out << indent
<< " '"
<< i->second.get_qualified_name(second)
<< "' value '"
<< i->second.get_value()
<< "'";
}
out << "\n\n";
}
if (numchanges)
{
report_mem_header(out, numchanges, change_kind, "enumerator", indent);
for (string_changed_enumerator_map::const_iterator i =
changed_enumerators().begin();
i != changed_enumerators().end();
++i)
{
if (i != changed_enumerators().begin())
out << "\n";
out << indent
<< " '"
<< i->second.first.get_qualified_name(first)
<< "' from value '"
<< i->second.first.get_value() << "' to '"
<< i->second.second.get_value() << "'";
}
out << "\n\n";
}
}
/// Compute the set of changes between two instances of @ref
/// enum_type_decl.
///
/// @param first a pointer to the first enum_type_decl to consider.
///
/// @param second a pointer to the second enum_type_decl to consider.
///
/// @return the resulting diff of the two enums @ref first and @ref
/// second.
enum_diff_sptr
compute_diff(const enum_type_decl_sptr first,
const enum_type_decl_sptr second)
{
diff_sptr ud = compute_diff_for_types(first->get_underlying_type(),
second->get_underlying_type());
enum_diff_sptr d(new enum_diff(first, second, ud));
compute_diff(first->get_enumerators().begin(),
first->get_enumerators().end(),
second->get_enumerators().begin(),
second->get_enumerators().end(),
d->priv_->enumerators_changes_);
d->ensure_lookup_tables_populated();
return d;
}
// </enum_diff stuff>
//<class_diff stuff>
struct class_diff::priv
@ -1116,61 +1450,6 @@ edit_script&
class_diff::member_class_tmpls_changes()
{return priv_->member_class_tmpls_changes_;}
/// Represent the kind of difference we want report_mem_header() to
/// report.
enum diff_kind
{
del_kind,
ins_kind,
change_kind
};
/// Output the header preceding the the report for
/// insertion/deletion/change of a part of a class. This is a
/// subroutine of class_diff::report.
///
/// @param out the output stream to output the report to.
///
/// @param number the number of insertion/deletion to refer to in the
/// header.
///
/// @param k the kind of diff (insertion/deletion/change) we want the
/// head to introduce.
///
/// @param section_name the name of the sub-part of the class to
/// report about.
///
/// @param indent the string to use as indentation prefix in the
/// header.
static void
report_mem_header(ostream& out,
int number,
diff_kind k,
const string& section_name,
const string& indent)
{
string change;
switch (k)
{
case del_kind:
change = (number > 1) ? "deletions" : "deletion";
break;
case ins_kind:
change = (number > 1) ? "insertions" : "insertion";
break;
case change_kind:
change = (number > 1) ? "changes" : "change";
}
if (number == 0)
out << indent << "no " << section_name << " " << change << "\n";
else if (number == 1)
out << indent << "1 " << section_name << " " << change << ":\n";
else
out << indent << number << " " << section_name
<< " " << change << ":\n";
}
/// Produce a basic report about the changes between two class_decl.
///
/// @param out the output stream to report the changes to.