forked from qt-creator/qt-creator
Add cache to hierarchy visitor, fix following typedefs and usings
In case we already visited the same base class symbol in the same scope we already know the fully qualified name for base class, so no need to search it again. We store all visited symbols with their scopes in cache. This speeds up building type hierarchy, especially for large trees, like for creator project and showing type hierarchy for QObject: build time before this change was about 300 sec, now it's about 130 sec. Sometimes the old code didn't follow properly base classes that were typedefs to other classes (like in creator project: Debugger::Internal::RegisterHandler is derived from RegisterModel, and RegisterModel is a typedef to Utils::TreeModel). This caused that some subclasses were omitted form resulting type hierarchy. E.g. type hierarchy in creator project for QObject lacked about 8 subclasses. Change-Id: I03c5aef65b7d7cb0ed279d3558aebc8a55e856b4 Reviewed-by: hjk <hjk@qt.io>
This commit is contained in:
@@ -42,9 +42,10 @@ QString unqualifyName(const QString &qualifiedName)
|
||||
class DerivedHierarchyVisitor : public CPlusPlus::SymbolVisitor
|
||||
{
|
||||
public:
|
||||
explicit DerivedHierarchyVisitor(const QString &qualifiedName)
|
||||
explicit DerivedHierarchyVisitor(const QString &qualifiedName, QHash<QString, QHash<QString, QString>> &cache)
|
||||
: _qualifiedName(qualifiedName)
|
||||
, _unqualifiedName(unqualifyName(qualifiedName))
|
||||
, _cache(cache)
|
||||
{}
|
||||
|
||||
void execute(const CPlusPlus::Document::Ptr &doc, const CPlusPlus::Snapshot &snapshot);
|
||||
@@ -55,11 +56,14 @@ public:
|
||||
const QSet<QString> otherBases() { return _otherBases; }
|
||||
|
||||
private:
|
||||
CPlusPlus::Symbol *lookup(const CPlusPlus::Name *symbolName, CPlusPlus::Scope *enclosingScope);
|
||||
|
||||
CPlusPlus::LookupContext _context;
|
||||
QString _qualifiedName;
|
||||
QString _unqualifiedName;
|
||||
CPlusPlus::Overview _overview;
|
||||
QHash<CPlusPlus::Symbol *, QString> _actualBases;
|
||||
// full scope name to base symbol name to fully qualified base symbol name
|
||||
QHash<QString, QHash<QString, QString>> &_cache;
|
||||
QSet<QString> _otherBases;
|
||||
QList<CPlusPlus::Symbol *> _derived;
|
||||
};
|
||||
@@ -75,45 +79,64 @@ void DerivedHierarchyVisitor::execute(const CPlusPlus::Document::Ptr &doc,
|
||||
accept(doc->globalSymbolAt(i));
|
||||
}
|
||||
|
||||
CPlusPlus::Symbol *DerivedHierarchyVisitor::lookup(const CPlusPlus::Name *symbolName, CPlusPlus::Scope *enclosingScope)
|
||||
{
|
||||
QList<CPlusPlus::LookupItem> items = _context.lookup(symbolName, enclosingScope);
|
||||
|
||||
CPlusPlus::Symbol *actualBaseSymbol = nullptr;
|
||||
|
||||
for (const CPlusPlus::LookupItem &item : items) {
|
||||
CPlusPlus::Symbol *s = item.declaration();
|
||||
if (!s)
|
||||
continue;
|
||||
if (!s->isClass() && !s->isTemplate() && !s->isTypedef())
|
||||
continue;
|
||||
actualBaseSymbol = s;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!actualBaseSymbol)
|
||||
return nullptr;
|
||||
|
||||
if (actualBaseSymbol->isTypedef()) {
|
||||
CPlusPlus::NamedType *namedType = actualBaseSymbol->type()->asNamedType();
|
||||
if (!namedType) {
|
||||
// Anonymous aggregate such as: typedef struct {} Empty;
|
||||
return nullptr;
|
||||
}
|
||||
actualBaseSymbol = lookup(namedType->name(), actualBaseSymbol->enclosingScope());
|
||||
}
|
||||
|
||||
return actualBaseSymbol;
|
||||
}
|
||||
|
||||
bool DerivedHierarchyVisitor::visit(CPlusPlus::Class *symbol)
|
||||
{
|
||||
const QList<const CPlusPlus::Name *> &fullScope
|
||||
= CPlusPlus::LookupContext::fullyQualifiedName(symbol->enclosingScope());
|
||||
const QString fullScopeName = _overview.prettyName(fullScope);
|
||||
|
||||
for (int i = 0; i < symbol->baseClassCount(); ++i) {
|
||||
CPlusPlus::BaseClass *baseSymbol = symbol->baseClassAt(i);
|
||||
|
||||
QString baseName = _actualBases.value(baseSymbol);
|
||||
if (baseName.isEmpty()) {
|
||||
QList<CPlusPlus::LookupItem> items = _context.lookup(baseSymbol->name(), symbol->enclosingScope());
|
||||
if (items.isEmpty() || !items.first().declaration())
|
||||
const QString &baseName = _overview.prettyName(baseSymbol->name());
|
||||
QString fullBaseName = _cache.value(fullScopeName).value(baseName);
|
||||
if (fullBaseName.isEmpty()) {
|
||||
CPlusPlus::Symbol *actualBaseSymbol = lookup(baseSymbol->name(), symbol->enclosingScope());
|
||||
if (!actualBaseSymbol)
|
||||
continue;
|
||||
|
||||
CPlusPlus::Symbol *actualBaseSymbol = items.first().declaration();
|
||||
if (actualBaseSymbol->isTypedef()) {
|
||||
CPlusPlus::NamedType *namedType = actualBaseSymbol->type()->asNamedType();
|
||||
if (!namedType) {
|
||||
// Anonymous aggregate such as: typedef struct {} Empty;
|
||||
continue;
|
||||
}
|
||||
const QString &typeName = _overview.prettyName(namedType->name());
|
||||
if (typeName == _unqualifiedName || typeName == _qualifiedName) {
|
||||
items = _context.lookup(namedType->name(), actualBaseSymbol->enclosingScope());
|
||||
if (items.isEmpty() || !items.first().declaration())
|
||||
continue;
|
||||
actualBaseSymbol = items.first().declaration();
|
||||
}
|
||||
}
|
||||
|
||||
const QList<const CPlusPlus::Name *> &full
|
||||
= CPlusPlus::LookupContext::fullyQualifiedName(actualBaseSymbol);
|
||||
baseName = _overview.prettyName(full);
|
||||
_actualBases.insert(baseSymbol, baseName);
|
||||
fullBaseName = _overview.prettyName(full);
|
||||
_cache[fullScopeName].insert(baseName, fullBaseName);
|
||||
}
|
||||
|
||||
if (_qualifiedName == baseName)
|
||||
if (_qualifiedName == fullBaseName)
|
||||
_derived.append(symbol);
|
||||
else
|
||||
_otherBases.insert(baseName);
|
||||
_otherBases.insert(fullBaseName);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -147,7 +170,8 @@ TypeHierarchy TypeHierarchyBuilder::buildDerivedTypeHierarchy(QFutureInterfaceBa
|
||||
{
|
||||
TypeHierarchy hierarchy(symbol);
|
||||
TypeHierarchyBuilder builder;
|
||||
builder.buildDerived(futureInterface, &hierarchy, snapshot);
|
||||
QHash<QString, QHash<QString, QString>> cache;
|
||||
builder.buildDerived(futureInterface, &hierarchy, snapshot, cache);
|
||||
return hierarchy;
|
||||
}
|
||||
|
||||
@@ -164,6 +188,7 @@ static Utils::FilePaths filesDependingOn(const CPlusPlus::Snapshot &snapshot,
|
||||
void TypeHierarchyBuilder::buildDerived(QFutureInterfaceBase &futureInterface,
|
||||
TypeHierarchy *typeHierarchy,
|
||||
const CPlusPlus::Snapshot &snapshot,
|
||||
QHash<QString, QHash<QString, QString>> &cache,
|
||||
int depth)
|
||||
{
|
||||
CPlusPlus::Symbol *symbol = typeHierarchy->_symbol;
|
||||
@@ -173,7 +198,7 @@ void TypeHierarchyBuilder::buildDerived(QFutureInterfaceBase &futureInterface,
|
||||
_visited.insert(symbol);
|
||||
|
||||
const QString &symbolName = _overview.prettyName(CPlusPlus::LookupContext::fullyQualifiedName(symbol));
|
||||
DerivedHierarchyVisitor visitor(symbolName);
|
||||
DerivedHierarchyVisitor visitor(symbolName, cache);
|
||||
|
||||
const Utils::FilePaths &dependingFiles = filesDependingOn(snapshot, symbol);
|
||||
if (depth == 0)
|
||||
@@ -198,7 +223,7 @@ void TypeHierarchyBuilder::buildDerived(QFutureInterfaceBase &futureInterface,
|
||||
const QList<CPlusPlus::Symbol *> &derived = visitor.derived();
|
||||
for (CPlusPlus::Symbol *s : derived) {
|
||||
TypeHierarchy derivedHierarchy(s);
|
||||
buildDerived(futureInterface, &derivedHierarchy, snapshot, depth + 1);
|
||||
buildDerived(futureInterface, &derivedHierarchy, snapshot, cache, depth + 1);
|
||||
if (futureInterface.isCanceled())
|
||||
return;
|
||||
typeHierarchy->_hierarchy.append(derivedHierarchy);
|
||||
|
||||
@@ -67,7 +67,8 @@ public:
|
||||
private:
|
||||
TypeHierarchyBuilder() = default;
|
||||
void buildDerived(QFutureInterfaceBase &futureInterface, TypeHierarchy *typeHierarchy,
|
||||
const CPlusPlus::Snapshot &snapshot, int depth = 0);
|
||||
const CPlusPlus::Snapshot &snapshot,
|
||||
QHash<QString, QHash<QString, QString> > &cache, int depth = 0);
|
||||
|
||||
QSet<CPlusPlus::Symbol *> _visited;
|
||||
QHash<Utils::FilePath, QSet<QString> > _candidates;
|
||||
|
||||
Reference in New Issue
Block a user