From 466ea4842cf3c84d1aaf43e717cbc66838c63f14 Mon Sep 17 00:00:00 2001
From: Leandro Melo <leandro.melo@nokia.com>
Date: Fri, 20 Jan 2012 14:43:21 +0100
Subject: [PATCH] C++: Improve file accuracy when finding symbols

This patch introduces a priority-based mechanism when searching for certains
symbols in the snapshot. The priority corresponds to how similar the file path
from the "reference" symbol is to the file path from the "candidate" symbol.
This solves a variety of issues when matching "equivalent" symbols but that
are in another file/project, such as when following a function declaration,
a forward class declaration, or adding a definition through a quickfix.

There's now a symbol finder which will compute the "best" search order and cache
the most recent results. A consequence is that following symbols in some cases
is slower, but not apparently significatly.

Note: The "find" functions were moved from the Snapshot to the new SymbolFinder
class.

Task-number: QTCREATORBUG-6697
Task-number: QTCREATORBUG-6792

Change-Id: Ia518f014275fec1f4d0cb3224bd4e06a9df6d557
Reviewed-by: Roberto Raggi <roberto.raggi@nokia.com>
---
 src/libs/cplusplus/CppDocument.cpp            | 236 ------------
 src/libs/cplusplus/CppDocument.h              |  12 -
 src/plugins/cppeditor/cppeditor.cpp           |  27 +-
 src/plugins/cppeditor/cppeditor.h             |   5 +
 src/plugins/cppeditor/cppelementevaluator.cpp |   6 +-
 .../cppeditor/cppfunctiondecldeflink.cpp      |  12 +-
 src/plugins/cppeditor/cppquickfixes.cpp       |   6 +-
 src/plugins/cpptools/cpptools.pro             |   6 +-
 .../cpptools/insertionpointlocator.cpp        |  11 +-
 src/plugins/cpptools/symbolfinder.cpp         | 338 ++++++++++++++++++
 src/plugins/cpptools/symbolfinder.h           |  55 +++
 src/plugins/designer/qtcreatorintegration.cpp |   6 +-
 12 files changed, 451 insertions(+), 269 deletions(-)
 create mode 100644 src/plugins/cpptools/symbolfinder.cpp
 create mode 100644 src/plugins/cpptools/symbolfinder.h

diff --git a/src/libs/cplusplus/CppDocument.cpp b/src/libs/cplusplus/CppDocument.cpp
index 0886416e48d..46586aa5971 100644
--- a/src/libs/cplusplus/CppDocument.cpp
+++ b/src/libs/cplusplus/CppDocument.cpp
@@ -717,239 +717,3 @@ void Snapshot::simplified_helper(Document::Ptr doc, Snapshot *snapshot) const
     }
 }
 
-namespace {
-class FindMatchingDefinition: public SymbolVisitor
-{
-    Symbol *_declaration;
-    const OperatorNameId *_oper;
-    QList<Function *> _result;
-
-public:
-    FindMatchingDefinition(Symbol *declaration)
-        : _declaration(declaration)
-        , _oper(0)
-    {
-        if (_declaration->name())
-            _oper = _declaration->name()->asOperatorNameId();
-    }
-
-    QList<Function *> result() const { return _result; }
-
-    using SymbolVisitor::visit;
-
-    virtual bool visit(Function *fun)
-    {
-        if (_oper) {
-            if (const Name *name = fun->unqualifiedName()) {
-                    if (_oper->isEqualTo(name))
-                        _result.append(fun);
-            }
-        } else if (const Identifier *id = _declaration->identifier()) {
-            if (id->isEqualTo(fun->identifier()))
-                _result.append(fun);
-        }
-
-        return false;
-    }
-
-    virtual bool visit(Block *)
-    {
-        return false;
-    }
-};
-} // end of anonymous namespace
-
-// strict means the returned symbol has to match exactly,
-// including argument count and argument types
-Symbol *Snapshot::findMatchingDefinition(Symbol *declaration, bool strict) const
-{
-    if (!declaration)
-        return 0;
-
-    Document::Ptr thisDocument = document(QString::fromUtf8(declaration->fileName(), declaration->fileNameLength()));
-    if (! thisDocument) {
-        qWarning() << "undefined document:" << declaration->fileName();
-        return 0;
-    }
-
-    Function *declarationTy = declaration->type()->asFunctionType();
-    if (! declarationTy) {
-        qWarning() << "not a function:" << declaration->fileName() << declaration->line() << declaration->column();
-        return 0;
-    }
-
-    foreach (Document::Ptr doc, *this) {
-        const Identifier *id = declaration->identifier();
-        if (id && ! doc->control()->findIdentifier(id->chars(),
-                                                   id->size()))
-            continue;
-        if (!id) {
-            if (!declaration->name())
-                continue;
-            const OperatorNameId *oper = declaration->name()->asOperatorNameId();
-            if (!oper)
-                continue;
-            if (!doc->control()->findOperatorNameId(oper->kind()))
-                continue;
-        }
-
-        FindMatchingDefinition candidates(declaration);
-        candidates.accept(doc->globalNamespace());
-
-        const QList<Function *> result = candidates.result();
-        if (! result.isEmpty()) {
-            LookupContext context(doc, *this);
-
-            QList<Function *> viableFunctions;
-
-            ClassOrNamespace *enclosingType = context.lookupType(declaration);
-            if (! enclosingType)
-                continue; // nothing to do
-
-            foreach (Function *fun, result) {
-                const QList<LookupItem> declarations = context.lookup(fun->name(), fun->enclosingScope());
-                if (declarations.isEmpty())
-                    continue;
-
-                const LookupItem best = declarations.first();
-                if (enclosingType == context.lookupType(best.declaration()))
-                    viableFunctions.append(fun);
-            }
-
-            if (viableFunctions.isEmpty())
-                continue;
-
-            else if (! strict && viableFunctions.length() == 1)
-                return viableFunctions.first();
-
-            Function *best = 0;
-
-            foreach (Function *fun, viableFunctions) {
-                if (! (fun->unqualifiedName() && fun->unqualifiedName()->isEqualTo(declaration->unqualifiedName())))
-                    continue;
-                else if (fun->argumentCount() == declarationTy->argumentCount()) {
-                    if (! strict && ! best)
-                        best = fun;
-
-                    unsigned argc = 0;
-                    for (; argc < declarationTy->argumentCount(); ++argc) {
-                        Symbol *arg = fun->argumentAt(argc);
-                        Symbol *otherArg = declarationTy->argumentAt(argc);
-                        if (! arg->type().isEqualTo(otherArg->type()))
-                            break;
-                    }
-
-                    if (argc == declarationTy->argumentCount())
-                        best = fun;
-                }
-            }
-
-            if (strict && ! best)
-                continue;
-
-            if (! best)
-                best = viableFunctions.first();
-            return best;
-        }
-    }
-
-    return 0;
-}
-
-Class *Snapshot::findMatchingClassDeclaration(Symbol *declaration) const
-{
-    if (! declaration->identifier())
-        return 0;
-
-    foreach (Document::Ptr doc, *this) {
-        if (! doc->control()->findIdentifier(declaration->identifier()->chars(),
-                                             declaration->identifier()->size()))
-            continue;
-
-        LookupContext context(doc, *this);
-
-        ClassOrNamespace *type = context.lookupType(declaration);
-        if (!type)
-            continue;
-
-        foreach (Symbol *s, type->symbols()) {
-            if (Class *c = s->asClass())
-                return c;
-        }
-    }
-
-    return 0;
-}
-
-void CPlusPlus::findMatchingDeclaration(const LookupContext &context,
-                                        Function *functionType,
-                                        QList<Declaration *> *typeMatch,
-                                        QList<Declaration *> *argumentCountMatch,
-                                        QList<Declaration *> *nameMatch)
-{
-    Scope *enclosingScope = functionType->enclosingScope();
-    while (! (enclosingScope->isNamespace() || enclosingScope->isClass()))
-        enclosingScope = enclosingScope->enclosingScope();
-    Q_ASSERT(enclosingScope != 0);
-
-    const Name *functionName = functionType->name();
-    if (! functionName)
-        return; // anonymous function names are not valid c++
-
-    ClassOrNamespace *binding = 0;
-    const QualifiedNameId *qName = functionName->asQualifiedNameId();
-    if (qName) {
-        if (qName->base())
-            binding = context.lookupType(qName->base(), enclosingScope);
-        else
-            binding = context.globalNamespace();
-        functionName = qName->name();
-    }
-
-    if (!binding) { // declaration for a global function
-        binding = context.lookupType(enclosingScope);
-
-        if (!binding)
-            return;
-    }
-
-    const Identifier *funcId = functionName->identifier();
-    if (!funcId) // E.g. operator, which we might be able to handle in the future...
-        return;
-
-    foreach (Symbol *s, binding->symbols()) {
-        Scope *scope = s->asScope();
-        if (!scope)
-            continue;
-
-        for (Symbol *s = scope->find(funcId); s; s = s->next()) {
-            if (! s->name())
-                continue;
-            else if (! funcId->isEqualTo(s->identifier()))
-                continue;
-            else if (! s->type()->isFunctionType())
-                continue;
-            else if (Declaration *decl = s->asDeclaration()) {
-                if (Function *declFunTy = decl->type()->asFunctionType()) {
-                    if (functionType->isEqualTo(declFunTy))
-                        typeMatch->prepend(decl);
-                    else if (functionType->argumentCount() == declFunTy->argumentCount())
-                        argumentCountMatch->prepend(decl);
-                    else
-                        nameMatch->append(decl);
-                }
-            }
-        }
-    }
-}
-
-QList<Declaration *> CPlusPlus::findMatchingDeclaration(const LookupContext &context, Function *functionType)
-{
-    QList<Declaration *> result;
-    QList<Declaration *> nameMatch, argumentCountMatch, typeMatch;
-    findMatchingDeclaration(context, functionType, &typeMatch, &argumentCountMatch, &nameMatch);
-    result.append(typeMatch);
-    result.append(argumentCountMatch);
-    result.append(nameMatch);
-    return result;
-}
diff --git a/src/libs/cplusplus/CppDocument.h b/src/libs/cplusplus/CppDocument.h
index fdd75447774..33447a49692 100644
--- a/src/libs/cplusplus/CppDocument.h
+++ b/src/libs/cplusplus/CppDocument.h
@@ -381,9 +381,6 @@ public:
     Document::Ptr documentFromSource(const QByteArray &preprocessedCode,
                                      const QString &fileName) const;
 
-    Symbol *findMatchingDefinition(Symbol *symbol, bool strict = false) const;
-    Class *findMatchingClassDeclaration(Symbol *symbol) const;
-
 private:
     void simplified_helper(Document::Ptr doc, Snapshot *snapshot) const;
 
@@ -391,15 +388,6 @@ private:
     _Base _documents;
 };
 
-void CPLUSPLUS_EXPORT findMatchingDeclaration(
-        const LookupContext &context,
-        Function *functionType,
-        QList<Declaration *> *typeMatch,
-        QList<Declaration *> *argumentCountMatch,
-        QList<Declaration *> *nameMatch);
-QList<Declaration *> CPLUSPLUS_EXPORT findMatchingDeclaration(
-        const LookupContext &context, Function *functionType);
-
 } // namespace CPlusPlus
 
 #endif // CPLUSPLUS_CPPDOCUMENT_H
diff --git a/src/plugins/cppeditor/cppeditor.cpp b/src/plugins/cppeditor/cppeditor.cpp
index 5b311ed3d5b..e9d25f2e238 100644
--- a/src/plugins/cppeditor/cppeditor.cpp
+++ b/src/plugins/cppeditor/cppeditor.cpp
@@ -71,6 +71,7 @@
 #include <cpptools/cpptoolsreuse.h>
 #include <cpptools/doxygengenerator.h>
 #include <cpptools/cpptoolssettings.h>
+#include <cpptools/symbolfinder.h>
 
 #include <coreplugin/icore.h>
 #include <coreplugin/actionmanager/actionmanager.h>
@@ -419,6 +420,7 @@ CPPEditorWidget::CPPEditorWidget(QWidget *parent)
     , m_firstRenameChange(false)
     , m_objcEnabled(false)
     , m_commentsSettings(CppTools::CppToolsSettings::instance()->commentsSettings())
+    , m_symbolFinder(new CppTools::SymbolFinder(QString()))
 {
     m_initialized = false;
     qRegisterMetaType<CppEditor::Internal::SemanticInfo>("CppEditor::Internal::SemanticInfo");
@@ -862,7 +864,9 @@ void CPPEditorWidget::onContentsChanged(int position, int charsRemoved, int char
 }
 
 void CPPEditorWidget::updateFileName()
-{ }
+{
+    m_symbolFinder.reset(new CppTools::SymbolFinder(file()->fileName()));
+}
 
 void CPPEditorWidget::jumpToOutlineElement(int)
 {
@@ -1076,7 +1080,7 @@ void CPPEditorWidget::switchDeclarationDefinition()
                 openCppEditorAt(linkToSymbol(best.first()));
 
         } else if (lastVisibleSymbol && lastVisibleSymbol->isDeclaration() && lastVisibleSymbol->type()->isFunctionType()) {
-            if (Symbol *def = snapshot.findMatchingDefinition(lastVisibleSymbol))
+            if (Symbol *def = m_symbolFinder->findMatchingDefinition(lastVisibleSymbol, snapshot))
                 openCppEditorAt(linkToSymbol(def));
         }
     }
@@ -1171,12 +1175,13 @@ CPPEditorWidget::Link CPPEditorWidget::attemptFuncDeclDef(const QTextCursor &cur
 
     Symbol *target = 0;
     if (FunctionDefinitionAST *funDef = declParent->asFunctionDefinition()) {
-        QList<Declaration *> candidates = findMatchingDeclaration(LookupContext(doc, snapshot),
-                                                                  funDef->symbol);
+        QList<Declaration *> candidates =
+                m_symbolFinder->findMatchingDeclaration(LookupContext(doc, snapshot),
+                                                        funDef->symbol);
         if (!candidates.isEmpty()) // TODO: improve disambiguation
             target = candidates.first();
     } else if (declParent->asSimpleDeclaration()) {
-        target = snapshot.findMatchingDefinition(funcDecl->symbol);
+        target = m_symbolFinder->findMatchingDefinition(funcDecl->symbol, snapshot);
     }
 
     if (target) {
@@ -1426,9 +1431,8 @@ CPPEditorWidget::Link CPPEditorWidget::findLinkAt(const QTextCursor &cursor,
                 if (def == lastVisibleSymbol)
                     def = 0; // jump to declaration then.
 
-                if (symbol->isForwardClassDeclaration()) {
-                    def = snapshot.findMatchingClassDeclaration(symbol);
-                }
+                if (symbol->isForwardClassDeclaration())
+                    def = m_symbolFinder->findMatchingClassDeclaration(symbol, snapshot);
             }
 
             link = linkToSymbol(def ? def : symbol);
@@ -1464,7 +1468,7 @@ Symbol *CPPEditorWidget::findDefinition(Symbol *symbol, const Snapshot &snapshot
     else if (! symbol->type()->isFunctionType())
         return 0; // not a function declaration
 
-    return snapshot.findMatchingDefinition(symbol);
+    return m_symbolFinder->findMatchingDefinition(symbol, snapshot);
 }
 
 unsigned CPPEditorWidget::editorRevision() const
@@ -2254,6 +2258,11 @@ void CPPEditorWidget::applyDeclDefLinkChanges(bool jumpToMatch)
     updateFunctionDeclDefLink();
 }
 
+CppTools::SymbolFinder *CPPEditorWidget::symbolFinder() const
+{
+    return m_symbolFinder.data();
+}
+
 void CPPEditorWidget::abortDeclDefLink()
 {
     if (!m_declDefLink)
diff --git a/src/plugins/cppeditor/cppeditor.h b/src/plugins/cppeditor/cppeditor.h
index 2cc792006c6..eb55422bcbf 100644
--- a/src/plugins/cppeditor/cppeditor.h
+++ b/src/plugins/cppeditor/cppeditor.h
@@ -50,6 +50,7 @@
 #include <QtCore/QFutureWatcher>
 #include <QtCore/QModelIndex>
 #include <QtCore/QVector>
+#include <QtCore/QScopedPointer>
 
 QT_BEGIN_NAMESPACE
 class QComboBox;
@@ -65,6 +66,7 @@ class CppModelManagerInterface;
 namespace CppTools {
 class CppCodeStyleSettings;
 class CppRefactoringFile;
+class SymbolFinder;
 }
 
 namespace TextEditor {
@@ -200,6 +202,8 @@ public:
     QSharedPointer<FunctionDeclDefLink> declDefLink() const;
     void applyDeclDefLinkChanges(bool jumpToMatch);
 
+    CppTools::SymbolFinder *symbolFinder() const;
+
 Q_SIGNALS:
     void outlineModelIndexChanged(const QModelIndex &index);
 
@@ -333,6 +337,7 @@ private:
     QSharedPointer<FunctionDeclDefLink> m_declDefLink;
 
     CppTools::CommentsSettings m_commentsSettings;
+    QScopedPointer<CppTools::SymbolFinder> m_symbolFinder;
 };
 
 
diff --git a/src/plugins/cppeditor/cppelementevaluator.cpp b/src/plugins/cppeditor/cppelementevaluator.cpp
index 8e0c7bb4614..76dc4ec6eb2 100644
--- a/src/plugins/cppeditor/cppelementevaluator.cpp
+++ b/src/plugins/cppeditor/cppelementevaluator.cpp
@@ -34,6 +34,7 @@
 
 #include <coreplugin/ifile.h>
 #include <cpptools/cpptoolsreuse.h>
+#include <cpptools/symbolfinder.h>
 
 #include <FullySpecifiedType.h>
 #include <Literals.h>
@@ -184,8 +185,11 @@ void CppElementEvaluator::handleLookupItemMatch(const Snapshot &snapshot,
                        && (declaration->asTemplate()->declaration()->isClass()
                            || declaration->asTemplate()->declaration()->isForwardClassDeclaration()))) {
             if (declaration->isForwardClassDeclaration())
-                if (Symbol *classDeclaration = snapshot.findMatchingClassDeclaration(declaration))
+                if (Symbol *classDeclaration =
+                        m_editor->symbolFinder()->findMatchingClassDeclaration(
+                            declaration, snapshot)) {
                     declaration = classDeclaration;
+                }
             CppClass *cppClass = new CppClass(declaration);
             if (m_lookupBaseClasses)
                 cppClass->lookupBases(declaration, context);
diff --git a/src/plugins/cppeditor/cppfunctiondecldeflink.cpp b/src/plugins/cppeditor/cppfunctiondecldeflink.cpp
index e1dae57f518..ee9bcb1fffd 100644
--- a/src/plugins/cppeditor/cppfunctiondecldeflink.cpp
+++ b/src/plugins/cppeditor/cppfunctiondecldeflink.cpp
@@ -45,6 +45,7 @@
 #include <cplusplus/LookupContext.h>
 #include <cplusplus/Overview.h>
 #include <cpptools/cpprefactoringchanges.h>
+#include <cpptools/symbolfinder.h>
 #include <texteditor/refactoroverlay.h>
 #include <texteditor/tooltip/tooltip.h>
 #include <texteditor/tooltip/tipcontents.h>
@@ -165,13 +166,16 @@ static QSharedPointer<FunctionDeclDefLink> findLinkHelper(QSharedPointer<Functio
     Symbol *target = 0;
     if (FunctionDefinitionAST *funcDef = link->sourceDeclaration->asFunctionDefinition()) {
         QList<Declaration *> nameMatch, argumentCountMatch, typeMatch;
-        findMatchingDeclaration(LookupContext(link->sourceDocument, snapshot),
-                                funcDef->symbol,
-                                &typeMatch, &argumentCountMatch, &nameMatch);
+        CppTools::SymbolFinder finder(funcDef->symbol->fileName(), funcDef->symbol->fileNameLength());
+        finder.findMatchingDeclaration(LookupContext(link->sourceDocument, snapshot),
+                                       funcDef->symbol,
+                                       &typeMatch, &argumentCountMatch, &nameMatch);
         if (!typeMatch.isEmpty())
             target = typeMatch.first();
     } else if (link->sourceDeclaration->asSimpleDeclaration()) {
-        target = snapshot.findMatchingDefinition(link->sourceFunctionDeclarator->symbol, true);
+        CppTools::SymbolFinder finder(link->sourceFunctionDeclarator->symbol->fileName(),
+                                      link->sourceFunctionDeclarator->symbol->fileNameLength());
+        target = finder.findMatchingDefinition(link->sourceFunctionDeclarator->symbol, snapshot, true);
     }
     if (!target) {
         return noResult;
diff --git a/src/plugins/cppeditor/cppquickfixes.cpp b/src/plugins/cppeditor/cppquickfixes.cpp
index 34844936e88..113583e5225 100644
--- a/src/plugins/cppeditor/cppquickfixes.cpp
+++ b/src/plugins/cppeditor/cppquickfixes.cpp
@@ -64,6 +64,7 @@
 #include <cpptools/cpptoolsreuse.h>
 #include <cpptools/cppclassesfilter.h>
 #include <cpptools/searchsymbols.h>
+#include <cpptools/symbolfinder.h>
 #include <extensionsystem/iplugin.h>
 #include <extensionsystem/pluginmanager.h>
 
@@ -1552,7 +1553,10 @@ private:
         {
             Q_ASSERT(fwdClass != 0);
 
-            if (Class *k = assistInterface()->snapshot().findMatchingClassDeclaration(fwdClass)) {
+            CppTools::SymbolFinder symbolFinder(fwdClass->fileName(), fwdClass->fileNameLength());
+            if (Class *k =
+                    symbolFinder.findMatchingClassDeclaration(fwdClass,
+                                                              assistInterface()->snapshot())) {
                 const QString headerFile = QString::fromUtf8(k->fileName(), k->fileNameLength());
 
                 // collect the fwd headers
diff --git a/src/plugins/cpptools/cpptools.pro b/src/plugins/cpptools/cpptools.pro
index cbf4d3cd702..8bce1abd05d 100644
--- a/src/plugins/cpptools/cpptools.pro
+++ b/src/plugins/cpptools/cpptools.pro
@@ -37,7 +37,8 @@ HEADERS += completionsettingspage.h \
     cppcodestylepreferences.h \
     cpptoolsreuse.h \
     doxygengenerator.h \
-    commentssettings.h
+    commentssettings.h \
+    symbolfinder.h
 
 SOURCES += completionsettingspage.cpp \
     cppclassesfilter.cpp \
@@ -66,7 +67,8 @@ SOURCES += completionsettingspage.cpp \
     cppcodestylepreferences.cpp \
     cpptoolsreuse.cpp \
     doxygengenerator.cpp \
-    commentssettings.cpp
+    commentssettings.cpp \
+    symbolfinder.cpp
 
 FORMS += completionsettingspage.ui \
     cppfilesettingspage.ui \
diff --git a/src/plugins/cpptools/insertionpointlocator.cpp b/src/plugins/cpptools/insertionpointlocator.cpp
index 855e5075a07..baecd498c1f 100644
--- a/src/plugins/cpptools/insertionpointlocator.cpp
+++ b/src/plugins/cpptools/insertionpointlocator.cpp
@@ -33,6 +33,7 @@
 #include "cpptoolsplugin.h"
 #include "cpprefactoringchanges.h"
 #include "insertionpointlocator.h"
+#include "symbolfinder.h"
 
 #include <AST.h>
 #include <ASTVisitor.h>
@@ -516,7 +517,10 @@ static InsertionLocation nextToSurroundingDefinitions(Declaration *declaration,
     }
 
     // find the declaration's definition
-    Symbol *definition = changes.snapshot().findMatchingDefinition(surroundingFunctionDecl);
+    CppTools::SymbolFinder symbolFinder(surroundingFunctionDecl->fileName(),
+                                        surroundingFunctionDecl->fileNameLength());
+    Symbol *definition = symbolFinder.findMatchingDefinition(surroundingFunctionDecl,
+                                                             changes.snapshot());
     if (!definition)
         return noResult;
 
@@ -555,7 +559,10 @@ QList<InsertionLocation> InsertionPointLocator::methodDefinition(
     if (!declaration)
         return result;
 
-    if (Symbol *s = m_refactoringChanges.snapshot().findMatchingDefinition(declaration, true)) {
+    CppTools::SymbolFinder symbolFinder(declaration->fileName(), declaration->fileNameLength());
+    if (Symbol *s = symbolFinder.findMatchingDefinition(declaration,
+                                                        m_refactoringChanges.snapshot(),
+                                                        true)) {
         if (Function *f = s->asFunction()) {
             if (f->isConst() == declaration->type().isConst()
                     && f->isVolatile() == declaration->type().isVolatile())
diff --git a/src/plugins/cpptools/symbolfinder.cpp b/src/plugins/cpptools/symbolfinder.cpp
new file mode 100644
index 00000000000..bb7a7958172
--- /dev/null
+++ b/src/plugins/cpptools/symbolfinder.cpp
@@ -0,0 +1,338 @@
+#if defined(_MSC_VER)
+#pragma warning(disable:4996)
+#endif
+
+#include "symbolfinder.h"
+
+#include <Symbols.h>
+#include <Names.h>
+#include <Literals.h>
+#include <SymbolVisitor.h>
+#include <Control.h>
+#include <LookupContext.h>
+
+#include <QtCore/QDebug>
+
+#include <algorithm>
+#include <utility>
+
+using namespace CPlusPlus;
+using namespace CppTools;
+
+namespace {
+
+class FindMatchingDefinition: public SymbolVisitor
+{
+    Symbol *_declaration;
+    const OperatorNameId *_oper;
+    QList<Function *> _result;
+
+public:
+    FindMatchingDefinition(Symbol *declaration)
+        : _declaration(declaration)
+        , _oper(0)
+    {
+        if (_declaration->name())
+            _oper = _declaration->name()->asOperatorNameId();
+    }
+
+    QList<Function *> result() const { return _result; }
+
+    using SymbolVisitor::visit;
+
+    virtual bool visit(Function *fun)
+    {
+        if (_oper) {
+            if (const Name *name = fun->unqualifiedName()) {
+                    if (_oper->isEqualTo(name))
+                        _result.append(fun);
+            }
+        } else if (const Identifier *id = _declaration->identifier()) {
+            if (id->isEqualTo(fun->identifier()))
+                _result.append(fun);
+        }
+
+        return false;
+    }
+
+    virtual bool visit(Block *)
+    {
+        return false;
+    }
+};
+
+} // end of anonymous namespace
+
+
+SymbolFinder::SymbolFinder(const QString &referenceFileName)
+    : m_referenceFile(referenceFileName)
+{}
+
+SymbolFinder::SymbolFinder(const char *referenceFileName, unsigned referenceFileLength)
+    : m_referenceFile(QString::fromUtf8(referenceFileName, referenceFileLength))
+{}
+
+// strict means the returned symbol has to match exactly,
+// including argument count and argument types
+Symbol *SymbolFinder::findMatchingDefinition(Symbol *declaration,
+                                             const Snapshot &snapshot,
+                                             bool strict)
+{
+    if (!declaration)
+        return 0;
+
+    QString declFile = QString::fromUtf8(declaration->fileName(), declaration->fileNameLength());
+    Q_ASSERT(declFile == m_referenceFile);
+
+    Document::Ptr thisDocument = snapshot.document(declFile);
+    if (! thisDocument) {
+        qWarning() << "undefined document:" << declaration->fileName();
+        return 0;
+    }
+
+    Function *declarationTy = declaration->type()->asFunctionType();
+    if (! declarationTy) {
+        qWarning() << "not a function:" << declaration->fileName()
+                   << declaration->line() << declaration->column();
+        return 0;
+    }
+
+    foreach (const QString &fileName, fileIterationOrder(snapshot)) {
+        Document::Ptr doc = snapshot.document(fileName);
+        if (!doc) {
+            clear(fileName);
+            continue;
+        }
+
+        const Identifier *id = declaration->identifier();
+        if (id && ! doc->control()->findIdentifier(id->chars(), id->size()))
+            continue;
+
+        if (!id) {
+            if (!declaration->name())
+                continue;
+            const OperatorNameId *oper = declaration->name()->asOperatorNameId();
+            if (!oper)
+                continue;
+            if (!doc->control()->findOperatorNameId(oper->kind()))
+                continue;
+        }
+
+        FindMatchingDefinition candidates(declaration);
+        candidates.accept(doc->globalNamespace());
+
+        const QList<Function *> result = candidates.result();
+        if (! result.isEmpty()) {
+            LookupContext context(doc, snapshot);
+
+            QList<Function *> viableFunctions;
+
+            ClassOrNamespace *enclosingType = context.lookupType(declaration);
+            if (! enclosingType)
+                continue; // nothing to do
+
+            foreach (Function *fun, result) {
+                const QList<LookupItem> declarations = context.lookup(fun->name(), fun->enclosingScope());
+                if (declarations.isEmpty())
+                    continue;
+
+                const LookupItem best = declarations.first();
+                if (enclosingType == context.lookupType(best.declaration()))
+                    viableFunctions.append(fun);
+            }
+
+            if (viableFunctions.isEmpty())
+                continue;
+
+            else if (! strict && viableFunctions.length() == 1)
+                return viableFunctions.first();
+
+            Function *best = 0;
+
+            foreach (Function *fun, viableFunctions) {
+                if (! (fun->unqualifiedName() && fun->unqualifiedName()->isEqualTo(declaration->unqualifiedName())))
+                    continue;
+                else if (fun->argumentCount() == declarationTy->argumentCount()) {
+                    if (! strict && ! best)
+                        best = fun;
+
+                    unsigned argc = 0;
+                    for (; argc < declarationTy->argumentCount(); ++argc) {
+                        Symbol *arg = fun->argumentAt(argc);
+                        Symbol *otherArg = declarationTy->argumentAt(argc);
+                        if (! arg->type().isEqualTo(otherArg->type()))
+                            break;
+                    }
+
+                    if (argc == declarationTy->argumentCount())
+                        best = fun;
+                }
+            }
+
+            if (strict && ! best)
+                continue;
+
+            if (! best)
+                best = viableFunctions.first();
+            return best;
+        }
+    }
+
+    return 0;
+}
+
+Class *SymbolFinder::findMatchingClassDeclaration(Symbol *declaration, const Snapshot &snapshot)
+{
+    if (! declaration->identifier())
+        return 0;
+
+    QString declFile = QString::fromUtf8(declaration->fileName(), declaration->fileNameLength());
+    Q_ASSERT(declFile == m_referenceFile);
+
+    foreach (const QString &file, fileIterationOrder(snapshot)) {
+        Document::Ptr doc = snapshot.document(file);
+        if (!doc) {
+            clear(file);
+            continue;
+        }
+
+        if (! doc->control()->findIdentifier(declaration->identifier()->chars(),
+                                             declaration->identifier()->size()))
+            continue;
+
+        LookupContext context(doc, snapshot);
+
+        ClassOrNamespace *type = context.lookupType(declaration);
+        if (!type)
+            continue;
+
+        foreach (Symbol *s, type->symbols()) {
+            if (Class *c = s->asClass())
+                return c;
+        }
+    }
+
+    return 0;
+}
+
+void SymbolFinder::findMatchingDeclaration(const LookupContext &context,
+                                           Function *functionType,
+                                           QList<Declaration *> *typeMatch,
+                                           QList<Declaration *> *argumentCountMatch,
+                                           QList<Declaration *> *nameMatch)
+{
+    Scope *enclosingScope = functionType->enclosingScope();
+    while (! (enclosingScope->isNamespace() || enclosingScope->isClass()))
+        enclosingScope = enclosingScope->enclosingScope();
+    Q_ASSERT(enclosingScope != 0);
+
+    const Name *functionName = functionType->name();
+    if (! functionName)
+        return; // anonymous function names are not valid c++
+
+    ClassOrNamespace *binding = 0;
+    const QualifiedNameId *qName = functionName->asQualifiedNameId();
+    if (qName) {
+        if (qName->base())
+            binding = context.lookupType(qName->base(), enclosingScope);
+        else
+            binding = context.globalNamespace();
+        functionName = qName->name();
+    }
+
+    if (!binding) { // declaration for a global function
+        binding = context.lookupType(enclosingScope);
+
+        if (!binding)
+            return;
+    }
+
+    const Identifier *funcId = functionName->identifier();
+    if (!funcId) // E.g. operator, which we might be able to handle in the future...
+        return;
+
+    foreach (Symbol *s, binding->symbols()) {
+        Scope *scope = s->asScope();
+        if (!scope)
+            continue;
+
+        for (Symbol *s = scope->find(funcId); s; s = s->next()) {
+            if (! s->name())
+                continue;
+            else if (! funcId->isEqualTo(s->identifier()))
+                continue;
+            else if (! s->type()->isFunctionType())
+                continue;
+            else if (Declaration *decl = s->asDeclaration()) {
+                if (Function *declFunTy = decl->type()->asFunctionType()) {
+                    if (functionType->isEqualTo(declFunTy))
+                        typeMatch->prepend(decl);
+                    else if (functionType->argumentCount() == declFunTy->argumentCount())
+                        argumentCountMatch->prepend(decl);
+                    else
+                        nameMatch->append(decl);
+                }
+            }
+        }
+    }
+}
+
+QList<Declaration *> SymbolFinder::findMatchingDeclaration(const LookupContext &context,
+                                                           Function *functionType)
+{
+    QList<Declaration *> result;
+    QList<Declaration *> nameMatch, argumentCountMatch, typeMatch;
+    findMatchingDeclaration(context, functionType, &typeMatch, &argumentCountMatch, &nameMatch);
+    result.append(typeMatch);
+    result.append(argumentCountMatch);
+    result.append(nameMatch);
+    return result;
+}
+
+#include <QtCore/QThread>
+QStringList SymbolFinder::fileIterationOrder(const Snapshot &snapshot)
+{
+    if (m_filePriorityCache.isEmpty()) {
+        foreach (const Document::Ptr &doc, snapshot)
+            insert(doc->fileName());
+    } else {
+        checkCacheConsistency(snapshot);
+    }
+
+    return m_filePriorityCache.values();
+}
+
+void SymbolFinder::checkCacheConsistency(const Snapshot &snapshot)
+{
+    // We only check for "new" files, which which are in the snapshot but not in the cache.
+    // The counterpart validation for "old" files is done when one tries to access the
+    // corresponding document and notices it's now null.
+    foreach (const Document::Ptr &doc, snapshot) {
+        if (!m_fileMetaCache.contains(doc->fileName()))
+            insert(doc->fileName());
+    }
+}
+
+void SymbolFinder::clear(const QString &comparingFile)
+{
+    m_filePriorityCache.remove(computeKey(m_referenceFile, comparingFile), comparingFile);
+    m_fileMetaCache.remove(comparingFile);
+}
+
+void SymbolFinder::insert(const QString &comparingFile)
+{
+    // We want an ordering such that the documents with the most common path appear first.
+    m_filePriorityCache.insert(computeKey(m_referenceFile, comparingFile), comparingFile);
+    m_fileMetaCache.insert(comparingFile);
+}
+
+int SymbolFinder::computeKey(const QString &referenceFile, const QString &comparingFile)
+{
+    // As similar the path from the comparing file is to the path from the reference file,
+    // the smaller the key is, which is then used for sorting the map.
+    std::pair<QString::const_iterator,
+              QString::const_iterator> r = std::mismatch(referenceFile.begin(),
+                                                         referenceFile.end(),
+                                                         comparingFile.begin());
+    return referenceFile.length() - (r.first - referenceFile.begin());
+}
diff --git a/src/plugins/cpptools/symbolfinder.h b/src/plugins/cpptools/symbolfinder.h
new file mode 100644
index 00000000000..80330e98c68
--- /dev/null
+++ b/src/plugins/cpptools/symbolfinder.h
@@ -0,0 +1,55 @@
+#ifndef SYMBOLFINDER_H
+#define SYMBOLFINDER_H
+
+#include "cpptools_global.h"
+
+#include <CppDocument.h>
+#include <CPlusPlusForwardDeclarations.h>
+
+#include <QtCore/QHash>
+#include <QtCore/QStringList>
+#include <QtCore/QQueue>
+#include <QtCore/QMultiMap>
+
+namespace CppTools {
+
+class CPPTOOLS_EXPORT SymbolFinder
+{
+public:
+    SymbolFinder(const QString &referenceFileName);
+    SymbolFinder(const char *referenceFileName, unsigned referenceFileLength);
+
+    CPlusPlus::Symbol *findMatchingDefinition(CPlusPlus::Symbol *symbol,
+                                              const CPlusPlus::Snapshot &snapshot,
+                                              bool strict = false);
+
+    CPlusPlus::Class *findMatchingClassDeclaration(CPlusPlus::Symbol *declaration,
+                                                   const CPlusPlus::Snapshot &snapshot);
+
+    void findMatchingDeclaration(const CPlusPlus::LookupContext &context,
+                                 CPlusPlus::Function *functionType,
+                                 QList<CPlusPlus::Declaration *> *typeMatch,
+                                 QList<CPlusPlus::Declaration *> *argumentCountMatch,
+                                 QList<CPlusPlus::Declaration *> *nameMatch);
+
+    QList<CPlusPlus::Declaration *> findMatchingDeclaration(const CPlusPlus::LookupContext &context,
+                                                            CPlusPlus::Function *functionType);
+
+private:
+    QStringList fileIterationOrder(const CPlusPlus::Snapshot &snapshot);
+
+    void checkCacheConsistency(const CPlusPlus::Snapshot &snapshot);
+    void clear(const QString &comparingFile);
+    void insert(const QString &comparingFile);
+
+    static int computeKey(const QString &referenceFile, const QString &comparingFile);
+
+    QMultiMap<int, QString> m_filePriorityCache;
+    QSet<QString> m_fileMetaCache;
+    QString m_referenceFile;
+};
+
+} // namespace CppTools
+
+#endif // SYMBOLFINDER_H
+
diff --git a/src/plugins/designer/qtcreatorintegration.cpp b/src/plugins/designer/qtcreatorintegration.cpp
index e63074b8ec9..c5c34c014b6 100644
--- a/src/plugins/designer/qtcreatorintegration.cpp
+++ b/src/plugins/designer/qtcreatorintegration.cpp
@@ -42,6 +42,7 @@
 #include <cpptools/cpprefactoringchanges.h>
 #include <cpptools/cpptoolsconstants.h>
 #include <cpptools/insertionpointlocator.h>
+#include <cpptools/symbolfinder.h>
 #include <cplusplus/ModelManagerInterface.h>
 #include <cplusplus/Symbols.h>
 #include <cplusplus/Overview.h>
@@ -273,8 +274,9 @@ static Document::Ptr findDefinition(Function *functionDeclaration, int *line)
 {
     if (CppModelManagerInterface *cppModelManager = CppModelManagerInterface::instance()) {
         const Snapshot snapshot = cppModelManager->snapshot();
-
-        if (Symbol *def = snapshot.findMatchingDefinition(functionDeclaration)) {
+        CppTools::SymbolFinder symbolFinder(functionDeclaration->fileName(),
+                                            functionDeclaration->fileNameLength());
+        if (Symbol *def = symbolFinder.findMatchingDefinition(functionDeclaration, snapshot)) {
             if (line)
                 *line = def->line();
 
-- 
GitLab