From ad53fa42b03cb7f3237bd81c4bd92f462d8ec9da Mon Sep 17 00:00:00 2001
From: Erik Verbruggen <erik.verbruggen@nokia.com>
Date: Tue, 7 Dec 2010 16:20:46 +0100
Subject: [PATCH] Improved switching between method declarationand definition.

Reviewed-by: Roberto Raggi
---
 src/plugins/cppeditor/cppeditor.cpp | 144 +++++++++++++++++++++++++++-
 src/plugins/cppeditor/cppeditor.h   |   5 +-
 2 files changed, 147 insertions(+), 2 deletions(-)

diff --git a/src/plugins/cppeditor/cppeditor.cpp b/src/plugins/cppeditor/cppeditor.cpp
index 46a81ef1a93..b132895f060 100644
--- a/src/plugins/cppeditor/cppeditor.cpp
+++ b/src/plugins/cppeditor/cppeditor.cpp
@@ -49,6 +49,7 @@
 #include <ASTVisitor.h>
 #include <SymbolVisitor.h>
 #include <TranslationUnit.h>
+#include <cplusplus/ASTPath.h>
 #include <cplusplus/ExpressionUnderCursor.h>
 #include <cplusplus/TypeOfExpression.h>
 #include <cplusplus/Overview.h>
@@ -1172,6 +1173,139 @@ static inline LookupItem skipForwardDeclarations(const QList<LookupItem> &resolv
     return result;
 }
 
+namespace {
+
+QList<Declaration *> findMatchingDeclaration(const LookupContext &context,
+                                             Function *functionType)
+{
+    QList<Declaration *> result;
+
+    Scope *enclosingScope = functionType->enclosingScope();
+    while (! (enclosingScope->isNamespace() || enclosingScope->isClass()))
+        enclosingScope = enclosingScope->enclosingScope();
+    Q_ASSERT(enclosingScope != 0);
+
+    const Name *functionName = functionType->name();
+    if (! functionName)
+        return result; // 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);
+        functionName = qName->name();
+    }
+
+    if (!binding) { // declaration for a global function
+        binding = context.lookupType(enclosingScope);
+
+        if (!binding)
+            return result;
+    }
+
+    const Identifier *funcId = functionName->identifier();
+    if (!funcId) // E.g. operator, which we might be able to handle in the future...
+        return result;
+
+    QList<Declaration *> good, better, best;
+
+    foreach (Symbol *s, binding->symbols()) {
+        Class *matchingClass = s->asClass();
+        if (!matchingClass)
+            continue;
+
+        for (Symbol *s = matchingClass->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))
+                        best.prepend(decl);
+                    else if (functionType->argumentCount() == declFunTy->argumentCount() && result.isEmpty())
+                        better.prepend(decl);
+                    else
+                        good.append(decl);
+                }
+            }
+        }
+    }
+
+    result.append(best);
+    result.append(better);
+    result.append(good);
+
+    return result;
+}
+
+} // end of anonymous namespace
+
+CPPEditor::Link CPPEditor::attemptFuncDeclDef(const QTextCursor &cursor, const Document::Ptr &doc, Snapshot snapshot) const
+{
+    snapshot.insert(doc);
+
+    Link result;
+
+    QList<AST *> path = ASTPath(doc)(cursor);
+
+    if (path.size() < 5)
+        return result;
+
+    NameAST *name = path.last()->asName();
+    if (!name)
+        return result;
+
+    if (QualifiedNameAST *qName = path.at(path.size() - 2)->asQualifiedName()) {
+        // TODO: check which part of the qualified name we're on
+        if (qName->unqualified_name != name)
+            return result;
+    }
+
+    AST *declParent = 0;
+    DeclaratorAST *decl = 0;
+    for (int i = path.size() - 2; i > 0; --i) {
+        if ((decl = path.at(i)->asDeclarator()) != 0) {
+            declParent = path.at(i - 1);
+            break;
+        }
+    }
+    if (!decl || !declParent)
+        return result;
+    if (!decl->postfix_declarator_list || !decl->postfix_declarator_list->value)
+        return result;
+    FunctionDeclaratorAST *funcDecl = decl->postfix_declarator_list->value->asFunctionDeclarator();
+    if (!funcDecl)
+        return result;
+
+    Symbol *target = 0;
+    if (FunctionDefinitionAST *funDef = declParent->asFunctionDefinition()) {
+        QList<Declaration *> candidates = findMatchingDeclaration(LookupContext(doc, snapshot),
+                                                                  funDef->symbol);
+        if (!candidates.isEmpty()) // TODO: improve disambiguation
+            target = candidates.first();
+    } else if (declParent->asSimpleDeclaration()) {
+        target = snapshot.findMatchingDefinition(funcDecl->symbol);
+    }
+
+    if (target) {
+        result = linkToSymbol(target);
+
+        unsigned startLine, startColumn, endLine, endColumn;
+        doc->translationUnit()->getTokenStartPosition(name->firstToken(), &startLine, &startColumn);
+        doc->translationUnit()->getTokenEndPosition(name->lastToken() - 1, &endLine, &endColumn);
+
+        QTextDocument *textDocument = cursor.document();
+        result.begin = textDocument->findBlockByNumber(startLine - 1).position() + startColumn - 1;
+        result.end = textDocument->findBlockByNumber(endLine - 1).position() + endColumn - 1;
+    }
+
+    return result;
+}
+
 CPPEditor::Link CPPEditor::findLinkAt(const QTextCursor &cursor,
                                       bool resolveTarget)
 {
@@ -1181,6 +1315,14 @@ CPPEditor::Link CPPEditor::findLinkAt(const QTextCursor &cursor,
         return link;
 
     const Snapshot snapshot = m_modelManager->snapshot();
+
+    if (m_lastSemanticInfo.doc){
+        Link l = attemptFuncDeclDef(cursor, m_lastSemanticInfo.doc, snapshot);
+        if (l.isValid()) {
+            return l;
+        }
+    }
+
     int lineNumber = 0, positionInBlock = 0;
     convertPosition(cursor.position(), &lineNumber, &positionInBlock);
     Document::Ptr doc = snapshot.document(file()->fileName());
@@ -1363,7 +1505,7 @@ void CPPEditor::jumpToDefinition()
     openLink(findLinkAt(textCursor()));
 }
 
-Symbol *CPPEditor::findDefinition(Symbol *symbol, const Snapshot &snapshot)
+Symbol *CPPEditor::findDefinition(Symbol *symbol, const Snapshot &snapshot) const
 {
     if (symbol->isFunction())
         return 0; // symbol is a function definition.
diff --git a/src/plugins/cppeditor/cppeditor.h b/src/plugins/cppeditor/cppeditor.h
index 332fc1f0dce..a781e57362b 100644
--- a/src/plugins/cppeditor/cppeditor.h
+++ b/src/plugins/cppeditor/cppeditor.h
@@ -237,7 +237,7 @@ private:
 
     void markSymbols(const QTextCursor &tc, const SemanticInfo &info);
     bool sortedOutline() const;
-    CPlusPlus::Symbol *findDefinition(CPlusPlus::Symbol *symbol, const CPlusPlus::Snapshot &snapshot);
+    CPlusPlus::Symbol *findDefinition(CPlusPlus::Symbol *symbol, const CPlusPlus::Snapshot &snapshot) const;
 
     TextEditor::ITextEditor *openCppEditorAt(const QString &fileName, int line,
                                              int column = 0);
@@ -254,6 +254,9 @@ private:
     void finishRename();
     void abortRename();
 
+    Link attemptFuncDeclDef(const QTextCursor &cursor,
+                            const CPlusPlus::Document::Ptr &doc,
+                            CPlusPlus::Snapshot snapshot) const;
     Link findLinkAt(const QTextCursor &, bool resolveTarget = true);
     bool openCppEditorAt(const Link &);
 
-- 
GitLab