BIG CHANGE: added parsing of base classes; now CompareClasses() is much smarter since it looks recursively in the parents of the class being checked;

git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@55948 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
This commit is contained in:
Francesco Montorsi
2008-09-29 16:11:23 +00:00
parent 6b03a638a4
commit 673ae68a3c
3 changed files with 193 additions and 77 deletions

View File

@@ -79,7 +79,7 @@ public:
bool ParsePreprocessorOutput(const wxString& filename); bool ParsePreprocessorOutput(const wxString& filename);
bool Compare(); bool Compare();
int CompareClasses(const wxClass* iface, const wxClassPtrArray& api); int CompareClasses(const wxClass* iface, const wxClass* api);
bool FixMethod(const wxString& header, const wxMethod* iface, const wxMethod* api); bool FixMethod(const wxString& header, const wxMethod* iface, const wxMethod* api);
void ShowProgress(); void ShowProgress();
@@ -200,7 +200,6 @@ bool IfaceCheckApp::Compare()
{ {
const wxClassArray& interfaces = m_doxyInterface.GetClasses(); const wxClassArray& interfaces = m_doxyInterface.GetClasses();
const wxClass* c; const wxClass* c;
wxClassPtrArray api;
int mcount = 0, ccount = 0; int mcount = 0, ccount = 0;
LogMessage("Comparing the interface API to the real API (%d classes to compare)...", LogMessage("Comparing the interface API to the real API (%d classes to compare)...",
@@ -232,28 +231,29 @@ bool IfaceCheckApp::Compare()
wxString cname = interfaces[i].GetName(); wxString cname = interfaces[i].GetName();
api.Empty();
// search in the real headers for i-th interface class; we search for // search in the real headers for i-th interface class; we search for
// both class cname and cnameBase since in wxWidgets world tipically // both class cname and cnameBase since in wxWidgets world tipically
// class cname is platform-specific while the real public interface of // class cname is platform-specific while the real public interface of
// that class is part of the cnameBase class. // that class is part of the cnameBase class.
/*c = m_gccInterface.FindClass(cname + "Base");
if (c) api.Add(c);*/
c = m_gccInterface.FindClass(cname); c = m_gccInterface.FindClass(cname);
if (c) api.Add(c); if (!c)
c = m_gccInterface.FindClass(cname + "Base"); {
if (c) api.Add(c); // sometimes the platform-specific class is named "wxGeneric" + cname
// or similar:
c = m_gccInterface.FindClass("wxGeneric" + cname.Mid(2));
if (!c)
{
c = m_gccInterface.FindClass("wxGtk" + cname.Mid(2));
}
}
// sometimes the platform-specific class is named "wxGeneric" + cname if (c) {
// or similar:
c = m_gccInterface.FindClass("wxGeneric" + cname.Mid(2));
if (c) api.Add(c);
c = m_gccInterface.FindClass("wxGtk" + cname.Mid(2));
if (c) api.Add(c);
if (api.GetCount()>0) { // there is a class with the same (logic) name!
mcount += CompareClasses(&interfaces[i], c);
// there is a class with exactly the same name!
mcount += CompareClasses(&interfaces[i], api);
} else { } else {
@@ -271,18 +271,12 @@ bool IfaceCheckApp::Compare()
return true; return true;
} }
int IfaceCheckApp::CompareClasses(const wxClass* iface, const wxClassPtrArray& api) int IfaceCheckApp::CompareClasses(const wxClass* iface, const wxClass* api)
{ {
wxString searchedclasses;
const wxMethod *real; const wxMethod *real;
int count = 0; int count = 0;
wxASSERT(iface && api.GetCount()>0); wxASSERT(iface && api);
// build a string with the names of the API classes compared to iface
for (unsigned int j=0; j<api.GetCount(); j++)
searchedclasses += "/" + api[j]->GetName();
searchedclasses.Remove(0, 1);
// shorten the name of the header so the log file is more readable // shorten the name of the header so the log file is more readable
wxString header = wxFileName(iface->GetHeader()).GetFullName(); wxString header = wxFileName(iface->GetHeader()).GetFullName();
@@ -290,7 +284,6 @@ int IfaceCheckApp::CompareClasses(const wxClass* iface, const wxClassPtrArray& a
for (unsigned int i=0; i<iface->GetMethodCount(); i++) for (unsigned int i=0; i<iface->GetMethodCount(); i++)
{ {
const wxMethod& m = iface->GetMethod(i); const wxMethod& m = iface->GetMethod(i);
int matches = 0;
// only compare the methods which are available for the port // only compare the methods which are available for the port
// for which the gcc XML was produced // for which the gcc XML was produced
@@ -305,67 +298,40 @@ int IfaceCheckApp::CompareClasses(const wxClass* iface, const wxClassPtrArray& a
} }
// search in the methods of the api classes provided // search in the methods of the api classes provided
for (unsigned int j=0; j<api.GetCount(); j++) real = api->RecursiveUpwardFindMethod(m, &m_gccInterface);
{
real = api[j]->FindMethod(m);
if (real)
matches++; // there is a real matching prototype! It's ok!
}
if (matches == 0) if (real)
{ {
bool exit = false; bool exit = false;
wxMethodPtrArray overloads; wxMethodPtrArray overloads =
api->RecursiveUpwardFindMethodsNamed(m.GetName(), &m_gccInterface);
// try searching for methods with the same name but with
// different return type / arguments / qualifiers
for (unsigned int j=0; j<api.GetCount(); j++)
{
wxMethodPtrArray results = api[j]->FindMethodsNamed(m.GetName());
// append "results" array to "overloads"
WX_APPEND_ARRAY(overloads, results);
#define HACK_TO_AUTO_CORRECT_ONLY_METHOD_ATTRIBUTES 0 #define HACK_TO_AUTO_CORRECT_ONLY_METHOD_ATTRIBUTES 0
#if HACK_TO_AUTO_CORRECT_ONLY_METHOD_ATTRIBUTES #if HACK_TO_AUTO_CORRECT_ONLY_METHOD_ATTRIBUTES
for (unsigned int k=0; k<results.GetCount(); k++) for (unsigned int k=0; k<overloads.GetCount(); k++)
if (results[k]->MatchesExceptForAttributes(m) && if (overloads[k]->MatchesExceptForAttributes(m) &&
results[k]->IsPureVirtual() == m.IsPureVirtual()) overloads[k]->IsPureVirtual() == m.IsPureVirtual())
{ {
// fix default values of results[k]: // fix default values of results[k]:
wxMethod tmp(*results[k]); wxMethod tmp(*overloads[k]);
tmp.SetArgumentTypes(m.GetArgumentTypes()); tmp.SetArgumentTypes(m.GetArgumentTypes());
// modify interface header // modify interface header
if (FixMethod(iface->GetHeader(), &m, &tmp)) if (FixMethod(iface->GetHeader(), &m, &tmp))
LogMessage("Adjusted attributes of '%s' method", m.GetAsString()); LogMessage("Adjusted attributes of '%s' method", m.GetAsString());
exit = true; exit = true;
break;
}
if (exit)
break; break;
#endif }
}
#if HACK_TO_AUTO_CORRECT_ONLY_METHOD_ATTRIBUTES
if (!exit) if (!exit)
{ {
#endif #endif
if (overloads.GetCount()==0) if (overloads.GetCount()==0)
{ {
/* LogMessage("%s: real '%s' class and their parents have no method '%s'",
TODO: sometimes the interface headers re-document a method header, api->GetName(), m.GetAsString());
inherited from a base class even if the real header does
not actually re-implement it.
To avoid false positives, we'd need to search in the base classes
of api[] classes and search for a matching method.
*/
LogMessage("%s: real '%s' class has no method '%s'",
header, searchedclasses, m.GetAsString());
// we've found no overloads // we've found no overloads
} }
else else
@@ -375,11 +341,11 @@ int IfaceCheckApp::CompareClasses(const wxClass* iface, const wxClassPtrArray& a
if (overloads.GetCount()>1) if (overloads.GetCount()>1)
warning += wxString::Format(": in the real headers there are %d overloads of '%s' for " warning += wxString::Format(": in the real headers there are %d overloads of '%s' for "
"'%s' all with different signatures:\n", "'%s' all with different signatures:\n",
overloads.GetCount(), m.GetName(), searchedclasses); overloads.GetCount(), m.GetName(), api->GetName());
else else
warning += wxString::Format(": in the real headers there is a method '%s' for '%s'" warning += wxString::Format(": in the real headers there is a method '%s' for '%s'"
" but has different signature:\n", " but has different signature:\n",
m.GetName(), searchedclasses); m.GetName(), api->GetName());
// get a list of the prototypes with _all_ possible attributes: // get a list of the prototypes with _all_ possible attributes:
warning += "\tdoxy header: " + m.GetAsString(true, true, true, true); warning += "\tdoxy header: " + m.GetAsString(true, true, true, true);

View File

@@ -147,7 +147,11 @@ void wxArgumentType::SetDefaultValue(const wxString& defval, const wxString& def
if (m_strDefaultValueForCmp == "0u") if (m_strDefaultValueForCmp == "0u")
m_strDefaultValueForCmp = "0"; m_strDefaultValueForCmp = "0";
m_strDefaultValue.Replace("0x000000001", "1");
m_strDefaultValueForCmp.Replace("0x000000001", "1");
// fix for unicode strings: // fix for unicode strings:
m_strDefaultValue.Replace("\\000\\000\\000", "");
m_strDefaultValueForCmp.Replace("\\000\\000\\000", ""); m_strDefaultValueForCmp.Replace("\\000\\000\\000", "");
if (m_strDefaultValueForCmp.StartsWith("wxT(") && if (m_strDefaultValueForCmp.StartsWith("wxT(") &&
@@ -480,6 +484,38 @@ const wxMethod* wxClass::FindMethod(const wxMethod& m) const
return NULL; return NULL;
} }
const wxMethod* wxClass::RecursiveUpwardFindMethod(const wxMethod& m,
const wxXmlInterface* allclasses) const
{
// first, search into *this
const wxMethod* ret = FindMethod(m);
if (ret)
return ret;
// then, search into its parents
for (unsigned int i=0; i<m_parents.GetCount(); i++)
{
// ignore non-wx-classes parents
// AD-HOC FIX: discard wxScrolledT_Helper parent as it always gives errors
if (m_parents[i].StartsWith("wx") || m_parents[i] == "wxScrolledT_Helper")
{
const wxClass *parent = allclasses->FindClass(m_parents[i]);
if (!parent) {
wxLogError("Could not find parent '%s' of class '%s'...",
m_parents[i], GetName());
return false;
}
const wxMethod *parentMethod = parent->RecursiveUpwardFindMethod(m, allclasses);
if (parentMethod)
return parentMethod;
}
}
// could not find anything even in parent classes...
return NULL;
}
wxMethodPtrArray wxClass::FindMethodsNamed(const wxString& name) const wxMethodPtrArray wxClass::FindMethodsNamed(const wxString& name) const
{ {
wxMethodPtrArray ret; wxMethodPtrArray ret;
@@ -492,6 +528,37 @@ wxMethodPtrArray wxClass::FindMethodsNamed(const wxString& name) const
} }
wxMethodPtrArray wxClass::RecursiveUpwardFindMethodsNamed(const wxString& name,
const wxXmlInterface* allclasses) const
{
// first, search into *this
wxMethodPtrArray ret = FindMethodsNamed(name);
if (ret.GetCount()>0)
return ret; // stop here, don't look upward in the parents
// then, search into parents of this class
for (unsigned int i=0; i<m_parents.GetCount(); i++)
{
// AD-HOC FIX: discard wxScrolledT_Helper parent as it always gives errors
if (m_parents[i].StartsWith("wx") || m_parents[i] == "wxScrolledT_Helper")
{
const wxClass *parent = allclasses->FindClass(m_parents[i]);
if (!parent) {
wxLogError("Could not find parent '%s' of class '%s'...",
m_parents[i], GetName());
return false;
}
wxMethodPtrArray temp = parent->RecursiveUpwardFindMethodsNamed(name, allclasses);
WX_APPEND_ARRAY(ret, temp);
}
}
return ret;
}
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// wxXmlInterface // wxXmlInterface
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
@@ -554,6 +621,10 @@ wxClassPtrArray wxXmlInterface::FindClassesDefinedIn(const wxString& headerfile)
#define ATTRIB_POINTER 4 #define ATTRIB_POINTER 4
#define ATTRIB_ARRAY 8 #define ATTRIB_ARRAY 8
// it may sound strange but gccxml, in order to produce shorter ID names
// uses (after the underscore) characters in range 0-9 and a-z in the ID names;
// in order to be able to translate such strings into numbers using strtoul()
// we use as base 10 (possible digits) + 25 (possible characters) = 35
#define GCCXML_BASE 35 #define GCCXML_BASE 35
class toResolveTypeItem class toResolveTypeItem
@@ -578,6 +649,7 @@ WX_DECLARE_HASH_MAP( unsigned long, toResolveTypeItem,
WX_DECLARE_HASH_MAP( unsigned long, wxClass*, WX_DECLARE_HASH_MAP( unsigned long, wxClass*,
wxIntegerHash, wxIntegerEqual, wxIntegerHash, wxIntegerEqual,
wxClassMemberIdHashMap ); wxClassMemberIdHashMap );
#else #else
#include <map> #include <map>
typedef std::map<unsigned long, toResolveTypeItem> wxToResolveTypeHashMap; typedef std::map<unsigned long, toResolveTypeItem> wxToResolveTypeHashMap;
@@ -732,6 +804,21 @@ bool wxXmlGccInterface::Parse(const wxString& filename)
// NB: "file" attribute contains an ID value that we'll resolve later // NB: "file" attribute contains an ID value that we'll resolve later
m_classes.Add(wxClass(cname, child->GetAttribute("file"))); m_classes.Add(wxClass(cname, child->GetAttribute("file")));
// the just-inserted class:
wxClass *newClass = &m_classes.Last();
// now get a list of the base classes:
wxXmlNode *baseNode = child->GetChildren();
while (baseNode)
{
// for now we store as "parents" only the parent IDs...
// later we will resolve them into full class names
if (baseNode->GetName() == "Base")
newClass->AddParent(baseNode->GetAttribute("type"));
baseNode = baseNode->GetNext();
}
const wxString& ids = child->GetAttribute("members"); const wxString& ids = child->GetAttribute("members");
if (ids.IsEmpty()) if (ids.IsEmpty())
{ {
@@ -746,7 +833,7 @@ bool wxXmlGccInterface::Parse(const wxString& filename)
else else
{ {
// decode the non-empty list of IDs: // decode the non-empty list of IDs:
if (!getMemberIDs(&members, &m_classes.Last(), ids)) { if (!getMemberIDs(&members, newClass, ids)) {
LogError("Invalid member IDs for '%s' class node: %s", LogError("Invalid member IDs for '%s' class node: %s",
cname, child->GetAttribute("id")); cname, child->GetAttribute("id"));
return false; return false;
@@ -950,6 +1037,30 @@ bool wxXmlGccInterface::Parse(const wxString& filename)
m_classes[i].SetHeader(idx->second); m_classes[i].SetHeader(idx->second);
} }
// resolve parent names
for (unsigned int i=0; i<m_classes.GetCount(); i++)
{
for (unsigned int k=0; k<m_classes[i].GetParentCount(); k++)
{
unsigned long id;
if (!getID(&id, m_classes[i].GetParent(k))) {
LogError("invalid parent class ID for '%s'", m_classes[i].GetName());
return false;
}
wxTypeIdHashMap::const_iterator idx = types.find(id);
if (idx == types.end())
{
// this is an error!
LogError("couldn't find parent class ID '%d'", id);
}
else
// replace k-th parent with its true name:
m_classes[i].SetParent(k, idx->second);
}
}
// build the list of the wx methods // build the list of the wx methods
child = doc.GetRoot()->GetChildren(); child = doc.GetRoot()->GetChildren();
while (child) while (child)
@@ -1278,6 +1389,7 @@ bool wxXmlDoxygenInterface::Parse(const wxString& filename)
bool wxXmlDoxygenInterface::ParseCompoundDefinition(const wxString& filename) bool wxXmlDoxygenInterface::ParseCompoundDefinition(const wxString& filename)
{ {
wxClassMemberIdHashMap parents;
wxXmlDocument doc; wxXmlDocument doc;
wxXmlNode *child; wxXmlNode *child;
int nodes = 0; int nodes = 0;
@@ -1375,6 +1487,11 @@ bool wxXmlDoxygenInterface::ParseCompoundDefinition(const wxString& filename)
// identify <onlyfor> custom XML tags // identify <onlyfor> custom XML tags
klass.SetAvailability(GetAvailabilityFor(subchild)); klass.SetAvailability(GetAvailabilityFor(subchild));
} }
else if (subchild->GetName() == "basecompoundref")
{
// add the name of this parent to the list of klass' parents
klass.AddParent(subchild->GetNodeContent());
}
subchild = subchild->GetNext(); subchild = subchild->GetNext();
} }

View File

@@ -288,6 +288,14 @@ WX_DECLARE_OBJARRAY(wxMethod, wxMethodArray);
WX_DEFINE_ARRAY(const wxMethod*, wxMethodPtrArray); WX_DEFINE_ARRAY(const wxMethod*, wxMethodPtrArray);
// we need wxClassPtrArray to be defined _before_ wxClass itself,
// since wxClass uses wxClassPtrArray.
class wxClass;
WX_DEFINE_ARRAY(const wxClass*, wxClassPtrArray);
class wxXmlInterface;
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// Represents a class of the wx API/interface. // Represents a class of the wx API/interface.
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
@@ -307,7 +315,8 @@ public: // setters
{ m_strName=name; } { m_strName=name; }
void SetAvailability(int nAvail) void SetAvailability(int nAvail)
{ m_nAvailability=nAvail; } { m_nAvailability=nAvail; }
void SetParent(unsigned int k, const wxString& name)
{ m_parents[k]=name; }
public: // getters public: // getters
@@ -333,18 +342,37 @@ public: // getters
int GetAvailability() const int GetAvailability() const
{ return m_nAvailability; } { return m_nAvailability; }
//const wxClass *GetParent(unsigned int i) const
const wxString& GetParent(unsigned int i) const
{ return m_parents[i]; }
unsigned int GetParentCount() const
{ return m_parents.GetCount(); }
public: // misc public: // misc
void AddMethod(const wxMethod& func) void AddMethod(const wxMethod& func)
{ m_methods.Add(func); } { m_methods.Add(func); }
void AddParent(const wxString& parent)//wxClass* parent)
{ m_parents.Add(parent); }
// returns a single result (the first, which is also the only // returns a single result (the first, which is also the only
// one if CheckConsistency() return true) // one if CheckConsistency() return true)
const wxMethod* FindMethod(const wxMethod& m) const; const wxMethod* FindMethod(const wxMethod& m) const;
// like FindMethod() but this one searches also recursively in
// the parents of this class.
const wxMethod* RecursiveUpwardFindMethod(const wxMethod& m,
const wxXmlInterface* allclasses) const;
// returns an array of pointers to the overloaded methods with the // returns an array of pointers to the overloaded methods with the
// same given name // same given name
wxMethodPtrArray FindMethodsNamed(const wxString& m) const; wxMethodPtrArray FindMethodsNamed(const wxString& name) const;
// like FindMethodsNamed() but this one searches also recursively in
// the parents of this class.
wxMethodPtrArray RecursiveUpwardFindMethodsNamed(const wxString& name,
const wxXmlInterface* allclasses) const;
// dumps all methods to the given output stream // dumps all methods to the given output stream
void Dump(wxTextOutputStream& stream) const; void Dump(wxTextOutputStream& stream) const;
@@ -357,12 +385,17 @@ protected:
wxString m_strHeader; wxString m_strHeader;
wxMethodArray m_methods; wxMethodArray m_methods;
// name of the base classes: we store the names and not the pointers
// because this makes _much_ easier the parsing process!
// (basically because when parsing class X which derives from Y,
// we may have not parsed yet class Y!)
wxArrayString m_parents;
// see the wxMethod::m_nAvailability field for more info // see the wxMethod::m_nAvailability field for more info
int m_nAvailability; int m_nAvailability;
}; };
WX_DECLARE_OBJARRAY(wxClass, wxClassArray); WX_DECLARE_OBJARRAY(wxClass, wxClassArray);
WX_DEFINE_ARRAY(const wxClass*, wxClassPtrArray);