From ca291fbc7ba860b1856b45eff5a02daa56a39d65 Mon Sep 17 00:00:00 2001
From: Erik Verbruggen <erik.verbruggen@digia.com>
Date: Thu, 2 May 2013 14:55:56 +0200
Subject: [PATCH] C++: fix functionAt(), moved it, and added test.

Thanks to Jesper K. Pedersen for the fix!

Change-Id: Ie49c3352e26a9632b1500596b00d559bfe932dff
Reviewed-by: Nikolai Kosjar <nikolai.kosjar@digia.com>
---
 src/libs/cplusplus/CppDocument.cpp            | 51 +++++++++++++++++++
 src/libs/cplusplus/CppDocument.h              |  1 +
 .../cpptools/abstracteditorsupport.cpp        | 28 +++-------
 tests/auto/cplusplus/lookup/tst_lookup.cpp    | 25 +++++++++
 4 files changed, 83 insertions(+), 22 deletions(-)

diff --git a/src/libs/cplusplus/CppDocument.cpp b/src/libs/cplusplus/CppDocument.cpp
index ee6d7f3a9d6..84092fc93da 100644
--- a/src/libs/cplusplus/CppDocument.cpp
+++ b/src/libs/cplusplus/CppDocument.cpp
@@ -464,6 +464,57 @@ void Document::setGlobalNamespace(Namespace *globalNamespace)
     _globalNamespace = globalNamespace;
 }
 
+/*!
+ * Extract the function name including scope at the given position.
+ *
+ * Note that a function (scope) starts at the name of that function, not at the return type. The
+ * implication is that this method will return an empty string when the line/column is on the
+ * return type.
+ *
+ * \param line the line number, starting with line 1
+ * \param column the column number, starting with column 1
+ */
+QString Document::functionAt(int line, int column) const
+{
+    if (line < 1 || column < 1)
+        return QString();
+
+    CPlusPlus::Symbol *symbol = lastVisibleSymbolAt(line, column);
+    if (!symbol)
+        return QString();
+
+    // Find the enclosing function scope (which might be several levels up, or we might be standing
+    // on it)
+    Scope *scope;
+    if (symbol->isScope())
+        scope = symbol->asScope();
+    else
+        scope = symbol->enclosingScope();
+
+    while (scope && !scope->isFunction() )
+        scope = scope->enclosingScope();
+
+    if (!scope)
+        return QString();
+
+    // We found the function scope, extract its name.
+    const Overview o;
+    QString rc = o.prettyName(scope->name());
+
+    // Prepend namespace "Foo::Foo::foo()" up to empty root namespace
+    for (const Symbol *owner = scope->enclosingNamespace();
+         owner; owner = owner->enclosingNamespace()) {
+        const QString name = o.prettyName(owner->name());
+        if (name.isEmpty()) {
+            break;
+        } else {
+            rc.prepend(QLatin1String("::"));
+            rc.prepend(name);
+        }
+    }
+    return rc;
+}
+
 Scope *Document::scopeAt(unsigned line, unsigned column)
 {
     FindScopeAt findScopeAt(_translationUnit, line, column);
diff --git a/src/libs/cplusplus/CppDocument.h b/src/libs/cplusplus/CppDocument.h
index 589e2b9503a..b9a58f547fc 100644
--- a/src/libs/cplusplus/CppDocument.h
+++ b/src/libs/cplusplus/CppDocument.h
@@ -99,6 +99,7 @@ public:
     QList<Macro> definedMacros() const
     { return _definedMacros; }
 
+    QString functionAt(int line, int column) const;
     Symbol *lastVisibleSymbolAt(unsigned line, unsigned column = 0) const;
     Scope *scopeAt(unsigned line, unsigned column = 0);
 
diff --git a/src/plugins/cpptools/abstracteditorsupport.cpp b/src/plugins/cpptools/abstracteditorsupport.cpp
index 6410473e34d..cdbd86f342b 100644
--- a/src/plugins/cpptools/abstracteditorsupport.cpp
+++ b/src/plugins/cpptools/abstracteditorsupport.cpp
@@ -55,29 +55,13 @@ QString AbstractEditorSupport::functionAt(const CppModelManagerInterface *modelM
                                           const QString &fileName,
                                           int line, int column)
 {
-    const CPlusPlus::Snapshot snapshot = modelManager->snapshot();
-    const CPlusPlus::Document::Ptr document = snapshot.document(fileName);
-    if (!document)
+    if (!modelManager)
         return QString();
-    if (const CPlusPlus::Symbol *symbol = document->lastVisibleSymbolAt(line, column))
-        if (const CPlusPlus::Scope *scope = symbol->enclosingScope())
-            if (const CPlusPlus::Scope *functionScope = scope->enclosingFunction())
-                if (const CPlusPlus::Symbol *function = functionScope) {
-                    const CPlusPlus::Overview o;
-                    QString rc = o.prettyName(function->name());
-                    // Prepend namespace "Foo::Foo::foo()" up to empty root namespace
-                    for (const CPlusPlus::Symbol *owner = function->enclosingNamespace();
-                         owner; owner = owner->enclosingNamespace()) {
-                        const QString name = o.prettyName(owner->name());
-                        if (name.isEmpty()) {
-                            break;
-                        } else {
-                            rc.prepend(QLatin1String("::"));
-                            rc.prepend(name);
-                        }
-                    }
-                    return rc;
-                }
+
+    const CPlusPlus::Snapshot snapshot = modelManager->snapshot();
+    if (const CPlusPlus::Document::Ptr document = snapshot.document(fileName))
+        return document->functionAt(line, column);
+
     return QString();
 }
 
diff --git a/tests/auto/cplusplus/lookup/tst_lookup.cpp b/tests/auto/cplusplus/lookup/tst_lookup.cpp
index a0df89de24f..c1d448b62bd 100644
--- a/tests/auto/cplusplus/lookup/tst_lookup.cpp
+++ b/tests/auto/cplusplus/lookup/tst_lookup.cpp
@@ -87,6 +87,7 @@ class tst_Lookup: public QObject
 
 private Q_SLOTS:
     void base_class_defined_1();
+    void document_functionAt_1();
 
     // Objective-C
     void simple_class_1();
@@ -153,6 +154,30 @@ void tst_Lookup::base_class_defined_1()
     QVERIFY(classToAST.value(derivedClass) != 0);
 }
 
+void tst_Lookup::document_functionAt_1()
+{
+    const QByteArray source = "\n"
+            "void Foo::Bar() {\n" // line 1
+            "    \n" // line 2
+            "    for (int i=0; i < 10; ++i) {\n"
+            "        \n" // line 4
+            "    }\n"
+            "}\n"; // line 7
+
+    Document::Ptr doc = Document::create("document_functionAt_1");
+    doc->setUtf8Source(source);
+    doc->parse();
+    doc->check();
+
+    QVERIFY(doc->diagnosticMessages().isEmpty());
+    QCOMPARE(doc->functionAt(1,  2), QString());
+    QCOMPARE(doc->functionAt(1, 11), QString(QLatin1String("Foo::Bar")));
+    QCOMPARE(doc->functionAt(2,  2), QString(QLatin1String("Foo::Bar")));
+    QCOMPARE(doc->functionAt(3, 10), QString(QLatin1String("Foo::Bar")));
+    QCOMPARE(doc->functionAt(4, 3), QString(QLatin1String("Foo::Bar")));
+    QCOMPARE(doc->functionAt(6, 1), QString(QLatin1String("Foo::Bar")));
+}
+
 void tst_Lookup::simple_class_1()
 {
     const QByteArray source = "\n"
-- 
GitLab