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 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);
void ShowProgress();
@ -200,7 +200,6 @@ bool IfaceCheckApp::Compare()
{
const wxClassArray& interfaces = m_doxyInterface.GetClasses();
const wxClass* c;
wxClassPtrArray api;
int mcount = 0, ccount = 0;
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();
api.Empty();
// search in the real headers for i-th interface class; we search for
// both class cname and cnameBase since in wxWidgets world tipically
// class cname is platform-specific while the real public interface of
// that class is part of the cnameBase class.
/*c = m_gccInterface.FindClass(cname + "Base");
if (c) api.Add(c);*/
c = m_gccInterface.FindClass(cname);
if (c) api.Add(c);
c = m_gccInterface.FindClass(cname + "Base");
if (c) api.Add(c);
if (!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
// 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 (c) {
if (api.GetCount()>0) {
// there is a class with exactly the same name!
mcount += CompareClasses(&interfaces[i], api);
// there is a class with the same (logic) name!
mcount += CompareClasses(&interfaces[i], c);
} else {
@ -271,18 +271,12 @@ bool IfaceCheckApp::Compare()
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;
int count = 0;
wxASSERT(iface && api.GetCount()>0);
// 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);
wxASSERT(iface && api);
// shorten the name of the header so the log file is more readable
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++)
{
const wxMethod& m = iface->GetMethod(i);
int matches = 0;
// only compare the methods which are available for the port
// 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
for (unsigned int j=0; j<api.GetCount(); j++)
{
real = api[j]->FindMethod(m);
if (real)
matches++; // there is a real matching prototype! It's ok!
}
real = api->RecursiveUpwardFindMethod(m, &m_gccInterface);
if (matches == 0)
if (real)
{
bool exit = false;
wxMethodPtrArray overloads;
// 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);
wxMethodPtrArray overloads =
api->RecursiveUpwardFindMethodsNamed(m.GetName(), &m_gccInterface);
#define HACK_TO_AUTO_CORRECT_ONLY_METHOD_ATTRIBUTES 0
#if HACK_TO_AUTO_CORRECT_ONLY_METHOD_ATTRIBUTES
for (unsigned int k=0; k<results.GetCount(); k++)
if (results[k]->MatchesExceptForAttributes(m) &&
results[k]->IsPureVirtual() == m.IsPureVirtual())
{
// fix default values of results[k]:
wxMethod tmp(*results[k]);
tmp.SetArgumentTypes(m.GetArgumentTypes());
for (unsigned int k=0; k<overloads.GetCount(); k++)
if (overloads[k]->MatchesExceptForAttributes(m) &&
overloads[k]->IsPureVirtual() == m.IsPureVirtual())
{
// fix default values of results[k]:
wxMethod tmp(*overloads[k]);
tmp.SetArgumentTypes(m.GetArgumentTypes());
// modify interface header
if (FixMethod(iface->GetHeader(), &m, &tmp))
LogMessage("Adjusted attributes of '%s' method", m.GetAsString());
// modify interface header
if (FixMethod(iface->GetHeader(), &m, &tmp))
LogMessage("Adjusted attributes of '%s' method", m.GetAsString());
exit = true;
break;
}
if (exit)
exit = true;
break;
#endif
}
}
#if HACK_TO_AUTO_CORRECT_ONLY_METHOD_ATTRIBUTES
if (!exit)
{
#endif
if (overloads.GetCount()==0)
{
/*
TODO: sometimes the interface headers re-document a method
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());
LogMessage("%s: real '%s' class and their parents have no method '%s'",
header, api->GetName(), m.GetAsString());
// we've found no overloads
}
else
@ -375,11 +341,11 @@ int IfaceCheckApp::CompareClasses(const wxClass* iface, const wxClassPtrArray& a
if (overloads.GetCount()>1)
warning += wxString::Format(": in the real headers there are %d overloads of '%s' for "
"'%s' all with different signatures:\n",
overloads.GetCount(), m.GetName(), searchedclasses);
overloads.GetCount(), m.GetName(), api->GetName());
else
warning += wxString::Format(": in the real headers there is a method '%s' for '%s'"
" but has different signature:\n",
m.GetName(), searchedclasses);
m.GetName(), api->GetName());
// get a list of the prototypes with _all_ possible attributes:
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")
m_strDefaultValueForCmp = "0";
m_strDefaultValue.Replace("0x000000001", "1");
m_strDefaultValueForCmp.Replace("0x000000001", "1");
// fix for unicode strings:
m_strDefaultValue.Replace("\\000\\000\\000", "");
m_strDefaultValueForCmp.Replace("\\000\\000\\000", "");
if (m_strDefaultValueForCmp.StartsWith("wxT(") &&
@ -480,6 +484,38 @@ const wxMethod* wxClass::FindMethod(const wxMethod& m) const
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 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
// ----------------------------------------------------------------------------
@ -554,6 +621,10 @@ wxClassPtrArray wxXmlInterface::FindClassesDefinedIn(const wxString& headerfile)
#define ATTRIB_POINTER 4
#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
class toResolveTypeItem
@ -578,6 +649,7 @@ WX_DECLARE_HASH_MAP( unsigned long, toResolveTypeItem,
WX_DECLARE_HASH_MAP( unsigned long, wxClass*,
wxIntegerHash, wxIntegerEqual,
wxClassMemberIdHashMap );
#else
#include <map>
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
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");
if (ids.IsEmpty())
{
@ -746,7 +833,7 @@ bool wxXmlGccInterface::Parse(const wxString& filename)
else
{
// 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",
cname, child->GetAttribute("id"));
return false;
@ -950,6 +1037,30 @@ bool wxXmlGccInterface::Parse(const wxString& filename)
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
child = doc.GetRoot()->GetChildren();
while (child)
@ -1278,6 +1389,7 @@ bool wxXmlDoxygenInterface::Parse(const wxString& filename)
bool wxXmlDoxygenInterface::ParseCompoundDefinition(const wxString& filename)
{
wxClassMemberIdHashMap parents;
wxXmlDocument doc;
wxXmlNode *child;
int nodes = 0;
@ -1375,6 +1487,11 @@ bool wxXmlDoxygenInterface::ParseCompoundDefinition(const wxString& filename)
// identify <onlyfor> custom XML tags
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();
}

View File

@ -288,6 +288,14 @@ WX_DECLARE_OBJARRAY(wxMethod, wxMethodArray);
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.
// ----------------------------------------------------------------------------
@ -307,7 +315,8 @@ public: // setters
{ m_strName=name; }
void SetAvailability(int nAvail)
{ m_nAvailability=nAvail; }
void SetParent(unsigned int k, const wxString& name)
{ m_parents[k]=name; }
public: // getters
@ -333,18 +342,37 @@ public: // getters
int GetAvailability() const
{ 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
void AddMethod(const wxMethod& 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
// one if CheckConsistency() return true)
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
// 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
void Dump(wxTextOutputStream& stream) const;
@ -357,12 +385,17 @@ protected:
wxString m_strHeader;
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
int m_nAvailability;
};
WX_DECLARE_OBJARRAY(wxClass, wxClassArray);
WX_DEFINE_ARRAY(const wxClass*, wxClassPtrArray);