Modernize Type Hierarchy

Don't freeze UI on request for show Type Hierarchy.
Move the lookup code into a separate thread.
Don't clear tree hierarchy when waiting for new one.
Show progress indicator on top of old hierarchy instead.
Add a task to ProgressManager when working on a new hierarchy.
Handle canceling the process of showing Type Hierarchy.
Implement simple progress reporting for this process.
Optimize a bit DerivedHierarchyVisitor.

Change-Id: I3894ac6ed3f4834831831f083f718f8385ca346f
Reviewed-by: Christian Kandeler <christian.kandeler@qt.io>
This commit is contained in:
Jarek Kobus
2020-11-30 17:02:33 +01:00
parent f2dd45d8ce
commit c400923308
6 changed files with 272 additions and 105 deletions

View File

@@ -36,6 +36,8 @@
#include <cplusplus/Icons.h>
#include <cplusplus/TypeOfExpression.h>
#include <utils/runextensions.h>
#include <QDir>
#include <QSet>
#include <QQueue>
@@ -44,6 +46,14 @@ using namespace CPlusPlus;
namespace CppTools {
static void handleLookupItemMatch(QFutureInterface<QSharedPointer<CppElement>> &futureInterface,
const Snapshot &snapshot,
const LookupItem &lookupItem,
const LookupContext &context,
CppTools::SymbolFinder symbolFinder,
bool lookupBaseClasses,
bool lookupDerivedClasses);
static QStringList stripName(const QString &name)
{
QStringList all;
@@ -164,7 +174,8 @@ CppClass *CppClass::toCppClass()
return this;
}
void CppClass::lookupBases(Symbol *declaration, const LookupContext &context)
void CppClass::lookupBases(QFutureInterfaceBase &futureInterface,
Symbol *declaration, const LookupContext &context)
{
using Data = QPair<ClassOrNamespace*, CppClass*>;
@@ -174,6 +185,8 @@ void CppClass::lookupBases(Symbol *declaration, const LookupContext &context)
QQueue<Data> q;
q.enqueue(qMakePair(clazz, this));
while (!q.isEmpty()) {
if (futureInterface.isCanceled())
return;
Data current = q.dequeue();
clazz = current.first;
visited.insert(clazz);
@@ -195,16 +208,19 @@ void CppClass::lookupBases(Symbol *declaration, const LookupContext &context)
}
}
void CppClass::lookupDerived(Symbol *declaration, const Snapshot &snapshot)
void CppClass::lookupDerived(QFutureInterfaceBase &futureInterface,
Symbol *declaration, const Snapshot &snapshot)
{
using Data = QPair<CppClass*, CppTools::TypeHierarchy>;
CppTools::TypeHierarchyBuilder builder(declaration, snapshot);
const CppTools::TypeHierarchy &completeHierarchy = builder.buildDerivedTypeHierarchy();
const CppTools::TypeHierarchy &completeHierarchy = builder.buildDerivedTypeHierarchy(futureInterface);
QQueue<Data> q;
q.enqueue(qMakePair(this, completeHierarchy));
while (!q.isEmpty()) {
if (futureInterface.isCanceled())
return;
const Data &current = q.dequeue();
CppClass *clazz = current.first;
const CppTools::TypeHierarchy &classHierarchy = current.second;
@@ -345,18 +361,43 @@ void CppElementEvaluator::setLookupBaseClasses(const bool lookup)
void CppElementEvaluator::setLookupDerivedClasses(const bool lookup)
{ m_lookupDerivedClasses = lookup; }
// @todo: Consider refactoring code from CppEditor::findLinkAt into here.
// special case for bug QTCREATORBUG-4780
static bool shouldOmitElement(const LookupItem &lookupItem, const Scope *scope)
{
return !lookupItem.declaration() && scope && scope->isFunction()
&& lookupItem.type().match(scope->asFunction()->returnType());
}
void CppElementEvaluator::execute()
{
execute(&CppElementEvaluator::syncExec);
}
QFuture<QSharedPointer<CppElement>> CppElementEvaluator::asyncExecute()
{
return execute(&CppElementEvaluator::asyncExec);
}
static QFuture<QSharedPointer<CppElement>> createFinishedFuture()
{
QFutureInterface<QSharedPointer<CppElement>> futureInterface;
futureInterface.reportStarted();
futureInterface.reportFinished();
return futureInterface.future();
}
// @todo: Consider refactoring code from CppEditor::findLinkAt into here.
QFuture<QSharedPointer<CppElement>> CppElementEvaluator::execute(ExecFunction execFuntion)
{
clear();
if (!m_modelManager)
return;
return createFinishedFuture();
const Snapshot &snapshot = m_modelManager->snapshot();
Document::Ptr doc = snapshot.document(m_editor->textDocument()->filePath());
if (!doc)
return;
return createFinishedFuture();
int line = 0;
int column = 0;
@@ -365,25 +406,52 @@ void CppElementEvaluator::execute()
checkDiagnosticMessage(pos);
if (!matchIncludeFile(doc, line) && !matchMacroInUse(doc, pos)) {
CppTools::moveCursorToEndOfIdentifier(&m_tc);
if (matchIncludeFile(doc, line) || matchMacroInUse(doc, pos))
return createFinishedFuture();
// Fetch the expression's code
ExpressionUnderCursor expressionUnderCursor(doc->languageFeatures());
const QString &expression = expressionUnderCursor(m_tc);
Scope *scope = doc->scopeAt(line, column - 1);
CppTools::moveCursorToEndOfIdentifier(&m_tc);
TypeOfExpression typeOfExpression;
typeOfExpression.init(doc, snapshot);
// make possible to instantiate templates
typeOfExpression.setExpandTemplates(true);
const QList<LookupItem> &lookupItems = typeOfExpression(expression.toUtf8(), scope);
if (lookupItems.isEmpty())
return;
// Fetch the expression's code
ExpressionUnderCursor expressionUnderCursor(doc->languageFeatures());
const QString &expression = expressionUnderCursor(m_tc);
Scope *scope = doc->scopeAt(line, column - 1);
const LookupItem &lookupItem = lookupItems.first(); // ### TODO: select best candidate.
handleLookupItemMatch(snapshot, lookupItem, typeOfExpression.context(), scope);
}
TypeOfExpression typeOfExpression;
typeOfExpression.init(doc, snapshot);
// make possible to instantiate templates
typeOfExpression.setExpandTemplates(true);
const QList<LookupItem> &lookupItems = typeOfExpression(expression.toUtf8(), scope);
if (lookupItems.isEmpty())
return createFinishedFuture();
const LookupItem &lookupItem = lookupItems.first(); // ### TODO: select best candidate.
if (shouldOmitElement(lookupItem, scope))
return createFinishedFuture();
return std::invoke(execFuntion, this, snapshot, lookupItem, typeOfExpression.context());
}
QFuture<QSharedPointer<CppElement>> CppElementEvaluator::syncExec(
const CPlusPlus::Snapshot &snapshot, const CPlusPlus::LookupItem &lookupItem,
const CPlusPlus::LookupContext &lookupContext)
{
QFutureInterface<QSharedPointer<CppElement>> futureInterface;
futureInterface.reportStarted();
handleLookupItemMatch(futureInterface, snapshot, lookupItem, lookupContext,
*m_modelManager->symbolFinder(),
m_lookupBaseClasses, m_lookupDerivedClasses);
futureInterface.reportFinished();
QFuture<QSharedPointer<CppElement>> future = futureInterface.future();
m_element = future.result();
return future;
}
QFuture<QSharedPointer<CppElement>> CppElementEvaluator::asyncExec(
const CPlusPlus::Snapshot &snapshot, const CPlusPlus::LookupItem &lookupItem,
const CPlusPlus::LookupContext &lookupContext)
{
return Utils::runAsync(&handleLookupItemMatch, snapshot, lookupItem, lookupContext,
*m_modelManager->symbolFinder(),
m_lookupBaseClasses, m_lookupDerivedClasses);
}
void CppElementEvaluator::checkDiagnosticMessage(int pos)
@@ -422,24 +490,25 @@ bool CppElementEvaluator::matchMacroInUse(const Document::Ptr &document, int pos
return false;
}
void CppElementEvaluator::handleLookupItemMatch(const Snapshot &snapshot,
const LookupItem &lookupItem,
const LookupContext &context,
const Scope *scope)
static void handleLookupItemMatch(QFutureInterface<QSharedPointer<CppElement>> &futureInterface,
const Snapshot &snapshot,
const LookupItem &lookupItem,
const LookupContext &context,
CppTools::SymbolFinder symbolFinder,
bool lookupBaseClasses,
bool lookupDerivedClasses)
{
if (futureInterface.isCanceled())
return;
QSharedPointer<CppElement> element;
Symbol *declaration = lookupItem.declaration();
if (!declaration) {
const QString &type = Overview().prettyType(lookupItem.type(), QString());
// special case for bug QTCREATORBUG-4780
if (scope && scope->isFunction()
&& lookupItem.type().match(scope->asFunction()->returnType())) {
return;
}
m_element = QSharedPointer<CppElement>(new Unknown(type));
element = QSharedPointer<CppElement>(new Unknown(type));
} else {
const FullySpecifiedType &type = declaration->type();
if (declaration->isNamespace()) {
m_element = QSharedPointer<CppElement>(new CppNamespace(declaration));
element = QSharedPointer<CppElement>(new CppNamespace(declaration));
} else if (declaration->isClass()
|| declaration->isForwardClassDeclaration()
|| (declaration->isTemplate() && declaration->asTemplate()->declaration()
@@ -447,8 +516,7 @@ void CppElementEvaluator::handleLookupItemMatch(const Snapshot &snapshot,
|| declaration->asTemplate()->declaration()->isForwardClassDeclaration()))) {
LookupContext contextToUse = context;
if (declaration->isForwardClassDeclaration()) {
const auto symbolFinder = m_modelManager->symbolFinder();
Symbol *classDeclaration = symbolFinder->findMatchingClassDeclaration(declaration,
Symbol *classDeclaration = symbolFinder.findMatchingClassDeclaration(declaration,
snapshot);
if (classDeclaration) {
declaration = classDeclaration;
@@ -460,29 +528,36 @@ void CppElementEvaluator::handleLookupItemMatch(const Snapshot &snapshot,
}
}
if (futureInterface.isCanceled())
return;
auto cppClass = new CppClass(declaration);
if (m_lookupBaseClasses)
cppClass->lookupBases(declaration, contextToUse);
if (m_lookupDerivedClasses)
cppClass->lookupDerived(declaration, snapshot);
m_element = QSharedPointer<CppElement>(cppClass);
if (lookupBaseClasses)
cppClass->lookupBases(futureInterface, declaration, contextToUse);
if (futureInterface.isCanceled())
return;
if (lookupDerivedClasses)
cppClass->lookupDerived(futureInterface, declaration, snapshot);
if (futureInterface.isCanceled())
return;
element = QSharedPointer<CppElement>(cppClass);
} else if (Enum *enumDecl = declaration->asEnum()) {
m_element = QSharedPointer<CppElement>(new CppEnum(enumDecl));
element = QSharedPointer<CppElement>(new CppEnum(enumDecl));
} else if (auto enumerator = dynamic_cast<EnumeratorDeclaration *>(declaration)) {
m_element = QSharedPointer<CppElement>(new CppEnumerator(enumerator));
element = QSharedPointer<CppElement>(new CppEnumerator(enumerator));
} else if (declaration->isTypedef()) {
m_element = QSharedPointer<CppElement>(new CppTypedef(declaration));
element = QSharedPointer<CppElement>(new CppTypedef(declaration));
} else if (declaration->isFunction()
|| (type.isValid() && type->isFunctionType())
|| declaration->isTemplate()) {
m_element = QSharedPointer<CppElement>(new CppFunction(declaration));
element = QSharedPointer<CppElement>(new CppFunction(declaration));
} else if (declaration->isDeclaration() && type.isValid()) {
m_element = QSharedPointer<CppElement>(
element = QSharedPointer<CppElement>(
new CppVariable(declaration, context, lookupItem.scope()));
} else {
m_element = QSharedPointer<CppElement>(new CppDeclarableElement(declaration));
element = QSharedPointer<CppElement>(new CppDeclarableElement(declaration));
}
}
futureInterface.reportResult(element);
}
bool CppElementEvaluator::identifiedCppElement() const

View File

@@ -32,11 +32,12 @@
#include <cplusplus/CppDocument.h>
#include <QFuture>
#include <QIcon>
#include <QSharedPointer>
#include <QString>
#include <QStringList>
#include <QSharedPointer>
#include <QTextCursor>
#include <QIcon>
namespace CPlusPlus {
class LookupItem;
@@ -57,6 +58,7 @@ public:
void setLookupDerivedClasses(const bool lookup);
void execute();
QFuture<QSharedPointer<CppElement>> asyncExecute();
bool identifiedCppElement() const;
const QSharedPointer<CppElement> &cppElement() const;
bool hasDiagnosis() const;
@@ -64,13 +66,18 @@ public:
private:
void clear();
using ExecFunction = QFuture<QSharedPointer<CppElement>>(CppElementEvaluator::*)
(const CPlusPlus::Snapshot &, const CPlusPlus::LookupItem &,
const CPlusPlus::LookupContext &);
QFuture<QSharedPointer<CppElement>> execute(ExecFunction execFuntion);
QFuture<QSharedPointer<CppElement>> syncExec(const CPlusPlus::Snapshot &,
const CPlusPlus::LookupItem &, const CPlusPlus::LookupContext &);
QFuture<QSharedPointer<CppElement>> asyncExec(const CPlusPlus::Snapshot &,
const CPlusPlus::LookupItem &, const CPlusPlus::LookupContext &);
void checkDiagnosticMessage(int pos);
bool matchIncludeFile(const CPlusPlus::Document::Ptr &document, int line);
bool matchMacroInUse(const CPlusPlus::Document::Ptr &document, int pos);
void handleLookupItemMatch(const CPlusPlus::Snapshot &snapshot,
const CPlusPlus::LookupItem &lookupItem,
const CPlusPlus::LookupContext &lookupContext,
const CPlusPlus::Scope *scope);
TextEditor::TextEditorWidget *m_editor;
CppTools::CppModelManager *m_modelManager;
@@ -123,8 +130,10 @@ public:
CppClass *toCppClass() final;
void lookupBases(CPlusPlus::Symbol *declaration, const CPlusPlus::LookupContext &context);
void lookupDerived(CPlusPlus::Symbol *declaration, const CPlusPlus::Snapshot &snapshot);
void lookupBases(QFutureInterfaceBase &futureInterface,
CPlusPlus::Symbol *declaration, const CPlusPlus::LookupContext &context);
void lookupDerived(QFutureInterfaceBase &futureInterface,
CPlusPlus::Symbol *declaration, const CPlusPlus::Snapshot &snapshot);
public:
QList<CppClass> bases;

View File

@@ -52,7 +52,7 @@ public:
bool visit(CPlusPlus::Class *) override;
const QList<CPlusPlus::Symbol *> &derived() { return _derived; }
const QStringList otherBases() { return _otherBases; }
const QSet<QString> otherBases() { return _otherBases; }
private:
CPlusPlus::LookupContext _context;
@@ -60,7 +60,7 @@ private:
QString _unqualifiedName;
CPlusPlus::Overview _overview;
QHash<CPlusPlus::Symbol *, QString> _actualBases;
QStringList _otherBases;
QSet<QString> _otherBases;
QList<CPlusPlus::Symbol *> _derived;
};
@@ -111,7 +111,7 @@ bool DerivedHierarchyVisitor::visit(CPlusPlus::Class *symbol)
if (_qualifiedName == baseName)
_derived.append(symbol);
else
_otherBases.append(baseName);
_otherBases.insert(baseName);
}
return true;
@@ -146,15 +146,22 @@ void TypeHierarchyBuilder::reset()
}
TypeHierarchy TypeHierarchyBuilder::buildDerivedTypeHierarchy()
{
QFutureInterfaceBase dummy;
return buildDerivedTypeHierarchy(dummy);
}
TypeHierarchy TypeHierarchyBuilder::buildDerivedTypeHierarchy(QFutureInterfaceBase &futureInterface)
{
reset();
TypeHierarchy hierarchy(_symbol);
buildDerived(&hierarchy, filesDependingOn(_symbol));
buildDerived(futureInterface, &hierarchy, filesDependingOn(_symbol));
return hierarchy;
}
void TypeHierarchyBuilder::buildDerived(TypeHierarchy *typeHierarchy,
const QStringList &dependingFiles)
void TypeHierarchyBuilder::buildDerived(QFutureInterfaceBase &futureInterface,
TypeHierarchy *typeHierarchy,
const QStringList &dependingFiles, int depth)
{
CPlusPlus::Symbol *symbol = typeHierarchy->_symbol;
if (_visited.contains(symbol))
@@ -165,7 +172,15 @@ void TypeHierarchyBuilder::buildDerived(TypeHierarchy *typeHierarchy,
const QString &symbolName = _overview.prettyName(CPlusPlus::LookupContext::fullyQualifiedName(symbol));
DerivedHierarchyVisitor visitor(symbolName);
foreach (const QString &fileName, dependingFiles) {
if (depth == 0)
futureInterface.setProgressRange(0, dependingFiles.size());
int i = -1;
for (const QString &fileName : dependingFiles) {
if (futureInterface.isCanceled())
return;
if (depth == 0)
futureInterface.setProgressValue(++i);
CPlusPlus::Document::Ptr doc = _snapshot.document(fileName);
if ((_candidates.contains(fileName) && !_candidates.value(fileName).contains(symbolName))
|| !doc->control()->findIdentifier(symbol->identifier()->chars(),
@@ -174,14 +189,14 @@ void TypeHierarchyBuilder::buildDerived(TypeHierarchy *typeHierarchy,
}
visitor.execute(doc, _snapshot);
_candidates.insert(fileName, QSet<QString>());
_candidates.insert(fileName, visitor.otherBases());
foreach (const QString &candidate, visitor.otherBases())
_candidates[fileName].insert(candidate);
foreach (CPlusPlus::Symbol *s, visitor.derived()) {
const QList<CPlusPlus::Symbol *> &derived = visitor.derived();
for (CPlusPlus::Symbol *s : derived) {
TypeHierarchy derivedHierarchy(s);
buildDerived(&derivedHierarchy, filesDependingOn(s));
buildDerived(futureInterface, &derivedHierarchy, filesDependingOn(s), depth + 1);
if (futureInterface.isCanceled())
return;
typeHierarchy->_hierarchy.append(derivedHierarchy);
}
}
@@ -193,9 +208,10 @@ QStringList TypeHierarchyBuilder::filesDependingOn(CPlusPlus::Symbol *symbol) co
if (!symbol)
return deps;
Utils::FilePath file = Utils::FilePath::fromUtf8(symbol->fileName(), symbol->fileNameLength());
const Utils::FilePath file = Utils::FilePath::fromUtf8(symbol->fileName(), symbol->fileNameLength());
deps << file.toString();
foreach (const Utils::FilePath &fileName, _snapshot.filesDependingOn(file))
const Utils::FilePaths filePaths = _snapshot.filesDependingOn(file);
for (const Utils::FilePath &fileName : filePaths)
deps.append(fileName.toString());
return deps;
}

View File

@@ -30,6 +30,7 @@
#include <cplusplus/CppDocument.h>
#include <cplusplus/Overview.h>
#include <QFutureInterface>
#include <QList>
#include <QSet>
@@ -60,10 +61,12 @@ public:
TypeHierarchyBuilder(CPlusPlus::Symbol *symbol, const CPlusPlus::Snapshot &snapshot);
TypeHierarchy buildDerivedTypeHierarchy();
TypeHierarchy buildDerivedTypeHierarchy(QFutureInterfaceBase &futureInterface);
private:
void reset();
void buildDerived(TypeHierarchy *typeHierarchy, const QStringList &dependencies);
void buildDerived(QFutureInterfaceBase &futureInterface,
TypeHierarchy *typeHierarchy, const QStringList &dependencies, int depth = 0);
QStringList filesDependingOn(CPlusPlus::Symbol *symbol) const;
CPlusPlus::Symbol *_symbol;