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:
Jarek Kobus
2020-12-22 12:06:26 +01:00
parent 167651b537
commit 9462102245
2 changed files with 57 additions and 31 deletions

View File

@@ -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);

View File

@@ -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;