diff --git a/src/plugins/cmakeprojectmanager/cmakeeditor.cpp b/src/plugins/cmakeprojectmanager/cmakeeditor.cpp
index f21f256a06ee0765dc4e097db9e1b1e5df0bafd8..3ae4a1e730c4899456ca85b824c57c435cc65109 100644
--- a/src/plugins/cmakeprojectmanager/cmakeeditor.cpp
+++ b/src/plugins/cmakeprojectmanager/cmakeeditor.cpp
@@ -159,7 +159,7 @@ static bool isValidFileNameChar(const QChar &c)
 }
 
 CMakeEditorWidget::Link CMakeEditorWidget::findLinkAt(const QTextCursor &cursor,
-                                                      bool/* resolveTarget*/)
+                                                      bool/* resolveTarget*/, bool /*inNextSplit*/)
 {
     Link link;
 
diff --git a/src/plugins/cmakeprojectmanager/cmakeeditor.h b/src/plugins/cmakeprojectmanager/cmakeeditor.h
index c35eebb26d987472243bb1dde864fa006da9abbf..b5102dabfbe4ea5718bd56cbdff69071375c1cac 100644
--- a/src/plugins/cmakeprojectmanager/cmakeeditor.h
+++ b/src/plugins/cmakeprojectmanager/cmakeeditor.h
@@ -78,8 +78,7 @@ public:
     CMakeEditorFactory *factory() { return m_factory; }
     TextEditor::TextEditorActionHandler *actionHandler() const { return m_ah; }
 
-    Link findLinkAt(const QTextCursor &cursor,
-                                     bool resolveTarget = true);
+    Link findLinkAt(const QTextCursor &cursor, bool resolveTarget = true, bool inNextSplit = false);
 
 protected:
     TextEditor::BaseTextEditor *createEditor();
diff --git a/src/plugins/cppeditor/cppeditor.cpp b/src/plugins/cppeditor/cppeditor.cpp
index bf323e1b9fbcff8b5a1d2124084ea49469f67616..4ec6cb4e5a9f2345fd574a5a5d5d2bcc37f1a680 100644
--- a/src/plugins/cppeditor/cppeditor.cpp
+++ b/src/plugins/cppeditor/cppeditor.cpp
@@ -511,6 +511,7 @@ CPPEditorWidget::CPPEditorWidget(QWidget *parent)
     , m_firstRenameChange(false)
     , m_objcEnabled(false)
     , m_commentsSettings(CppTools::CppToolsSettings::instance()->commentsSettings())
+    , m_followSymbolUnderCursor(new FollowSymbolUnderCursor(this))
 {
     qRegisterMetaType<SemanticInfo>("CppTools::SemanticInfo");
 
@@ -1239,14 +1240,14 @@ QString CPPEditorWidget::identifierUnderCursor(QTextCursor *macroCursor)
     return macroCursor->selectedText();
 }
 
-CPPEditorWidget::Link CPPEditorWidget::findLinkAt(const QTextCursor &cursor, bool resolveTarget)
+CPPEditorWidget::Link CPPEditorWidget::findLinkAt(const QTextCursor &cursor, bool resolveTarget,
+                                                  bool inNextSplit)
 {
     if (!m_modelManager)
         return Link();
 
-    FollowSymbolUnderCursor followSymbolUnderCursor(this, cursor, resolveTarget,
-        m_modelManager->snapshot(), m_lastSemanticInfo.doc, symbolFinder());
-    return followSymbolUnderCursor.findLink();
+    return m_followSymbolUnderCursor->findLink(cursor, resolveTarget, m_modelManager->snapshot(),
+                                               m_lastSemanticInfo.doc, symbolFinder(), inNextSplit);
 }
 
 unsigned CPPEditorWidget::editorRevision() const
@@ -1720,6 +1721,8 @@ TextEditor::IAssistInterface *CPPEditorWidget::createAssistInterface(
         if (!semanticInfo().doc || isOutdated())
             return 0;
         return new CppQuickFixAssistInterface(const_cast<CPPEditorWidget *>(this), reason);
+    } else {
+        return BaseTextEditorWidget::createAssistInterface(kind, reason);
     }
     return 0;
 }
@@ -1812,6 +1815,11 @@ void CPPEditorWidget::updateContentsChangedSignal()
             this, SLOT(onContentsChanged(int,int,int)));
 }
 
+FollowSymbolUnderCursor *CPPEditorWidget::followSymbolUnderCursorDelegate()
+{
+    return m_followSymbolUnderCursor.data();
+}
+
 void CPPEditorWidget::abortDeclDefLink()
 {
     if (!m_declDefLink)
diff --git a/src/plugins/cppeditor/cppeditor.h b/src/plugins/cppeditor/cppeditor.h
index 4714886f25d4f6c0a900ff97c7a5eb3750e9ac64..ba8f85db77f6df43419120f39216f11f1ef2d760 100644
--- a/src/plugins/cppeditor/cppeditor.h
+++ b/src/plugins/cppeditor/cppeditor.h
@@ -30,6 +30,7 @@
 #ifndef CPPEDITOR_H
 #define CPPEDITOR_H
 
+#include "cppfollowsymbolundercursor.h"
 #include "cppfunctiondecldeflink.h"
 
 #include <cpptools/commentssettings.h>
@@ -132,6 +133,8 @@ public:
 
     void updateContentsChangedSignal();
 
+    FollowSymbolUnderCursor *followSymbolUnderCursorDelegate(); // exposed for tests
+
 Q_SIGNALS:
     void outlineModelIndexChanged(const QModelIndex &index);
 
@@ -204,7 +207,7 @@ private:
 
     Q_SLOT void abortDeclDefLink();
 
-    Link findLinkAt(const QTextCursor &, bool resolveTarget = true);
+    Link findLinkAt(const QTextCursor &, bool resolveTarget = true, bool inNextSplit = false);
     bool openCppEditorAt(const Link &, bool inNextSplit = false);
 
     QModelIndex indexForPosition(int line, int column,
@@ -254,6 +257,8 @@ private:
     QSharedPointer<FunctionDeclDefLink> m_declDefLink;
 
     CppTools::CommentsSettings m_commentsSettings;
+
+    QScopedPointer<FollowSymbolUnderCursor> m_followSymbolUnderCursor;
 };
 
 } // namespace Internal
diff --git a/src/plugins/cppeditor/cppeditor.pro b/src/plugins/cppeditor/cppeditor.pro
index 740a70d2b98cbd6a6c45f8a0bb6163da7b491144..e793b5d4296192f642ed01017efd5edbb995bb04 100644
--- a/src/plugins/cppeditor/cppeditor.pro
+++ b/src/plugins/cppeditor/cppeditor.pro
@@ -23,7 +23,8 @@ HEADERS += cppeditorplugin.h \
     cppincludehierarchy.h \
     cppincludehierarchymodel.h \
     cppincludehierarchyitem.h \
-    cppincludehierarchytreeview.h
+    cppincludehierarchytreeview.h \
+    cppvirtualfunctionassistprovider.h
 
 SOURCES += cppeditorplugin.cpp \
     cppautocompleter.cpp \
@@ -45,7 +46,8 @@ SOURCES += cppeditorplugin.cpp \
     cppincludehierarchy.cpp \
     cppincludehierarchymodel.cpp \
     cppincludehierarchyitem.cpp \
-    cppincludehierarchytreeview.cpp
+    cppincludehierarchytreeview.cpp \
+    cppvirtualfunctionassistprovider.cpp
 
 RESOURCES += cppeditor.qrc
 
diff --git a/src/plugins/cppeditor/cppeditor.qbs b/src/plugins/cppeditor/cppeditor.qbs
index fd3da1fe8408e456229a6c9ebf6fc8a6b2e441a5..941600b6b24df5762e507dc81aa9c75c4bd8e5e7 100644
--- a/src/plugins/cppeditor/cppeditor.qbs
+++ b/src/plugins/cppeditor/cppeditor.qbs
@@ -60,6 +60,8 @@ QtcPlugin {
         "cppsnippetprovider.h",
         "cpptypehierarchy.cpp",
         "cpptypehierarchy.h",
+        "cppvirtualfunctionassistprovider.cpp",
+        "cppvirtualfunctionassistprovider.h",
     ]
 
     Group {
diff --git a/src/plugins/cppeditor/cppeditorplugin.h b/src/plugins/cppeditor/cppeditorplugin.h
index 7442d6692403312974605ee540e139c56f71b898..389ccd43e65ce09df8ee5d773ba8343b3ed9b8cf 100644
--- a/src/plugins/cppeditor/cppeditorplugin.h
+++ b/src/plugins/cppeditor/cppeditorplugin.h
@@ -128,6 +128,13 @@ private slots:
     void test_FollowSymbolUnderCursor_using_QTCREATORBUG7903_globalNamespace();
     void test_FollowSymbolUnderCursor_using_QTCREATORBUG7903_namespace();
     void test_FollowSymbolUnderCursor_using_QTCREATORBUG7903_insideFunction();
+    void test_FollowSymbolUnderCursor_virtualFunctionCall_allOverrides();
+    void test_FollowSymbolUnderCursor_virtualFunctionCall_possibleOverrides1();
+    void test_FollowSymbolUnderCursor_virtualFunctionCall_possibleOverrides2();
+    void test_FollowSymbolUnderCursor_virtualFunctionCall_notOnQualified();
+    void test_FollowSymbolUnderCursor_virtualFunctionCall_notOnDeclaration();
+    void test_FollowSymbolUnderCursor_virtualFunctionCall_notOnDefinition();
+    void test_FollowSymbolUnderCursor_virtualFunctionCall_notOnNonPointerNonReference();
 
     void test_doxygen_comments_qt_style();
     void test_doxygen_comments_qt_style_continuation();
diff --git a/src/plugins/cppeditor/cppelementevaluator.cpp b/src/plugins/cppeditor/cppelementevaluator.cpp
index c7cc6dfa68f15499f5d2fdd1e70d31f4bc7e6749..fe7dacbbd8a29ff758ed99cea14e9be590ea33aa 100644
--- a/src/plugins/cppeditor/cppelementevaluator.cpp
+++ b/src/plugins/cppeditor/cppelementevaluator.cpp
@@ -272,10 +272,11 @@ CppMacro::CppMacro(const Macro &macro)
 
 // CppDeclarableElement
 
-CppDeclarableElement::CppDeclarableElement(Symbol *declaration) : CppElement()
+CppDeclarableElement::CppDeclarableElement(Symbol *declaration)
+    : CppElement()
+    , declaration(declaration)
+    , icon(Icons().iconForSymbol(declaration))
 {
-    icon = Icons().iconForSymbol(declaration);
-
     Overview overview;
     overview.showArgumentNames = true;
     overview.showReturnTypes = true;
@@ -309,6 +310,11 @@ CppClass::CppClass(Symbol *declaration) : CppDeclarableElement(declaration)
     tooltip = qualifiedName;
 }
 
+bool CppClass::operator==(const CppClass &other)
+{
+    return this->declaration == other.declaration;
+}
+
 void CppClass::lookupBases(Symbol *declaration, const CPlusPlus::LookupContext &context)
 {
     typedef QPair<ClassOrNamespace *, CppClass *> Data;
diff --git a/src/plugins/cppeditor/cppelementevaluator.h b/src/plugins/cppeditor/cppelementevaluator.h
index 33baf0eac67fb7e695334d6713544fe1cdc4dfd5..498d58ba1f44f0a21f9ceec22afe368513bcf69f 100644
--- a/src/plugins/cppeditor/cppelementevaluator.h
+++ b/src/plugins/cppeditor/cppelementevaluator.h
@@ -135,6 +135,7 @@ public:
     explicit CppDeclarableElement(CPlusPlus::Symbol *declaration);
 
 public:
+    CPlusPlus::Symbol *declaration;
     QString name;
     QString qualifiedName;
     QString type;
@@ -153,6 +154,8 @@ public:
     CppClass();
     explicit CppClass(CPlusPlus::Symbol *declaration);
 
+    bool operator==(const CppClass &other);
+
     void lookupBases(CPlusPlus::Symbol *declaration, const CPlusPlus::LookupContext &context);
     void lookupDerived(CPlusPlus::Symbol *declaration, const CPlusPlus::Snapshot &snapshot);
 
diff --git a/src/plugins/cppeditor/cppfollowsymbolundercursor.cpp b/src/plugins/cppeditor/cppfollowsymbolundercursor.cpp
index 4446c14ac06fa8dc5e58e3b0e3655e9678677220..4b91d3ce82140b02037a1fc813409ac5c3f436c9 100644
--- a/src/plugins/cppeditor/cppfollowsymbolundercursor.cpp
+++ b/src/plugins/cppeditor/cppfollowsymbolundercursor.cpp
@@ -30,6 +30,7 @@
 
 #include "cppfollowsymbolundercursor.h"
 #include "cppeditor.h"
+#include "cppvirtualfunctionassistprovider.h"
 
 #include <cplusplus/ASTPath.h>
 #include <cplusplus/BackwardsScanner.h>
@@ -54,6 +55,37 @@ typedef BaseTextEditorWidget::Link Link;
 
 namespace {
 
+bool lookupVirtualFunctionOverrides(const QString &expression, Function *function, Scope *scope,
+                                    const Snapshot &snapshot)
+{
+    if (expression.isEmpty() || !function || !scope || scope->isClass() || snapshot.isEmpty())
+        return false;
+
+    bool result = false;
+
+    Document::Ptr expressionDocument = documentForExpression(expression.toUtf8());
+    if (ExpressionAST *expressionAST = extractExpressionAST(expressionDocument)) {
+        if (CallAST *callAST = expressionAST->asCall()) {
+            if (ExpressionAST *baseExpressionAST = callAST->base_expression) {
+                if (IdExpressionAST *idExpressionAST = baseExpressionAST->asIdExpression()) {
+                    NameAST *name = idExpressionAST->name;
+                    result = name && !name->asQualifiedName();
+                } else if (MemberAccessAST *memberAccessAST = baseExpressionAST->asMemberAccess()) {
+                    NameAST *name = memberAccessAST->member_name;
+                    const bool nameIsQualified = name && name->asQualifiedName();
+
+                    TranslationUnit *unit = expressionDocument->translationUnit();
+                    QTC_ASSERT(unit, return false);
+                    const int tokenKind = unit->tokenKind(memberAccessAST->access_token);
+                    result = tokenKind == T_ARROW && !nameIsQualified;
+                }
+            }
+        }
+    }
+
+    return result && FunctionHelper::isVirtualFunction(function, snapshot);
+}
+
 Link findMacroLink_helper(const QByteArray &name, Document::Ptr doc, const Snapshot &snapshot,
                           QSet<QString> *processed)
 {
@@ -138,48 +170,139 @@ inline LookupItem skipForwardDeclarations(const QList<LookupItem> &resolvedSymbo
     return result;
 }
 
+CPPEditorWidget::Link attemptFuncDeclDef(const QTextCursor &cursor,
+    CPPEditorWidget *widget, CPlusPlus::Snapshot snapshot, const CPlusPlus::Document::Ptr &document,
+    SymbolFinder *symbolFinder)
+{
+    snapshot.insert(document);
+
+    Link result;
+
+    QList<AST *> path = ASTPath(document)(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;
+    }
+
+    for (int i = path.size() - 1; i != -1; --i) {
+        AST *node = path.at(i);
+
+        if (node->asParameterDeclaration() != 0)
+            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 =
+                symbolFinder->findMatchingDeclaration(LookupContext(document, snapshot),
+                                                        funDef->symbol);
+        if (!candidates.isEmpty()) // TODO: improve disambiguation
+            target = candidates.first();
+    } else if (declParent->asSimpleDeclaration()) {
+        target = symbolFinder->findMatchingDefinition(funcDecl->symbol, snapshot);
+    }
+
+    if (target) {
+        result = widget->linkToSymbol(target);
+
+        unsigned startLine, startColumn, endLine, endColumn;
+        document->translationUnit()->getTokenStartPosition(name->firstToken(), &startLine,
+                                                           &startColumn);
+        document->translationUnit()->getTokenEndPosition(name->lastToken() - 1, &endLine,
+                                                         &endColumn);
+
+        QTextDocument *textDocument = cursor.document();
+        result.linkTextStart =
+                textDocument->findBlockByNumber(startLine - 1).position() + startColumn - 1;
+        result.linkTextEnd =
+                textDocument->findBlockByNumber(endLine - 1).position() + endColumn - 1;
+    }
+
+    return result;
+}
+
+Symbol *findDefinition(Symbol *symbol, const Snapshot &snapshot, SymbolFinder *symbolFinder)
+{
+    if (symbol->isFunction())
+        return 0; // symbol is a function definition.
+
+    else if (!symbol->type()->isFunctionType())
+        return 0; // not a function declaration
+
+    return symbolFinder->findMatchingDefinition(symbol, snapshot);
+}
+
 } // anonymous namespace
 
-FollowSymbolUnderCursor::FollowSymbolUnderCursor(CPPEditorWidget *widget, const QTextCursor &cursor,
-    bool resolveTarget, const Snapshot &snapshot, const Document::Ptr &documentFromSemanticInfo,
-    CppTools::SymbolFinder *symbolFinder)
+FollowSymbolUnderCursor::FollowSymbolUnderCursor(CPPEditorWidget *widget)
     : m_widget(widget)
-    , m_cursor(cursor)
-    , m_resolveTarget(resolveTarget)
-    , m_snapshot(snapshot)
-    , m_document(documentFromSemanticInfo)
-    , m_symbolFinder(symbolFinder)
+    , m_virtualFunctionAssistProvider(new VirtualFunctionAssistProvider)
+{
+}
+
+FollowSymbolUnderCursor::~FollowSymbolUnderCursor()
 {
+    delete m_virtualFunctionAssistProvider;
 }
 
-BaseTextEditorWidget::Link FollowSymbolUnderCursor::findLink()
+BaseTextEditorWidget::Link FollowSymbolUnderCursor::findLink(const QTextCursor &cursor,
+    bool resolveTarget, const Snapshot &theSnapshot, const Document::Ptr &documentFromSemanticInfo,
+    SymbolFinder *symbolFinder, bool inNextSplit)
 {
     Link link;
 
+    Snapshot snapshot = theSnapshot;
+
     // Move to end of identifier
-    QTextCursor tc = m_cursor;
+    QTextCursor tc = cursor;
     QChar ch = m_widget->document()->characterAt(tc.position());
     while (ch.isLetterOrNumber() || ch == QLatin1Char('_')) {
         tc.movePosition(QTextCursor::NextCharacter);
         ch = m_widget->document()->characterAt(tc.position());
     }
 
-    // Try to match decl/def. For this we need the semantic doc with the AST.
-    if (m_document
-            && m_document->translationUnit()
-            && m_document->translationUnit()->ast()) {
+    // Try to macth decl/def. For this we need the semantic doc with the AST.
+    if (documentFromSemanticInfo
+            && documentFromSemanticInfo->translationUnit()
+            && documentFromSemanticInfo->translationUnit()->ast()) {
         int pos = tc.position();
         while (m_widget->document()->characterAt(pos).isSpace())
             ++pos;
         if (m_widget->document()->characterAt(pos) == QLatin1Char('(')) {
-            link = attemptFuncDeclDef(m_cursor);
+            link = attemptFuncDeclDef(cursor, m_widget, snapshot, documentFromSemanticInfo,
+                                      symbolFinder);
             if (link.hasValidLinkText())
                 return link;
         }
     }
 
     int lineNumber = 0, positionInBlock = 0;
-    m_widget->convertPosition(m_cursor.position(), &lineNumber, &positionInBlock);
+    m_widget->convertPosition(cursor.position(), &lineNumber, &positionInBlock);
     const unsigned line = lineNumber;
     const unsigned column = positionInBlock + 1;
 
@@ -189,9 +312,9 @@ BaseTextEditorWidget::Link FollowSymbolUnderCursor::findLink()
 
     SimpleLexer tokenize;
     tokenize.setQtMocRunEnabled(true);
-    const QString blockText = m_cursor.block().text();
+    const QString blockText = cursor.block().text();
     const QList<Token> tokens = tokenize(blockText,
-                                         BackwardsScanner::previousBlockState(m_cursor.block()));
+                                         BackwardsScanner::previousBlockState(cursor.block()));
 
     bool recognizedQtMethod = false;
 
@@ -223,7 +346,7 @@ BaseTextEditorWidget::Link FollowSymbolUnderCursor::findLink()
                     }
 
                     if (j < tokens.size()) {
-                        QTextBlock block = m_cursor.block();
+                        QTextBlock block = cursor.block();
 
                         beginOfToken = block.position() + tokens.at(i).begin();
                         endOfToken = block.position() + tokens.at(i).end();
@@ -238,17 +361,17 @@ BaseTextEditorWidget::Link FollowSymbolUnderCursor::findLink()
     }
 
     // Now we prefer the doc from the snapshot with macros expanded.
-    Document::Ptr doc = m_snapshot.document(m_widget->editorDocument()->filePath());
+    Document::Ptr doc = snapshot.document(m_widget->editorDocument()->filePath());
     if (!doc) {
-        doc = m_document;
+        doc = documentFromSemanticInfo;
         if (!doc)
             return link;
     }
 
     if (!recognizedQtMethod) {
         const QTextBlock block = tc.block();
-        int pos = m_cursor.positionInBlock();
-        QChar ch = m_widget->document()->characterAt(m_cursor.position());
+        int pos = cursor.positionInBlock();
+        QChar ch = m_widget->document()->characterAt(cursor.position());
         if (pos > 0 && !(ch.isLetterOrNumber() || ch == QLatin1Char('_')))
             --pos; // positionInBlock points to a delimiter character.
         const Token tk = SimpleLexer::tokenAt(block.text(), pos,
@@ -259,7 +382,7 @@ BaseTextEditorWidget::Link FollowSymbolUnderCursor::findLink()
 
         // Handle include directives
         if (tk.is(T_STRING_LITERAL) || tk.is(T_ANGLE_STRING_LITERAL)) {
-            const unsigned lineno = m_cursor.blockNumber() + 1;
+            const unsigned lineno = cursor.blockNumber() + 1;
             foreach (const Document::Include &incl, doc->resolvedIncludes()) {
                 if (incl.line() == lineno) {
                     link.targetFileName = incl.resolvedFileName();
@@ -279,7 +402,7 @@ BaseTextEditorWidget::Link FollowSymbolUnderCursor::findLink()
     // Handle macro uses
     const Macro *macro = doc->findMacroDefinitionAt(line);
     if (macro) {
-        QTextCursor macroCursor = m_cursor;
+        QTextCursor macroCursor = cursor;
         const QByteArray name = CPPEditorWidget::identifierUnderCursor(&macroCursor).toLatin1();
         if (macro->name() == name)
             return link;    //already on definition!
@@ -318,7 +441,7 @@ BaseTextEditorWidget::Link FollowSymbolUnderCursor::findLink()
     }
 
     TypeOfExpression typeOfExpression;
-    typeOfExpression.init(doc, m_snapshot);
+    typeOfExpression.init(doc, snapshot);
     // make possible to instantiate templates
     typeOfExpression.setExpandTemplates(true);
     const QList<LookupItem> resolvedSymbols =
@@ -355,16 +478,30 @@ BaseTextEditorWidget::Link FollowSymbolUnderCursor::findLink()
         if (Symbol *symbol = result.declaration()) {
             Symbol *def = 0;
 
-            if (m_resolveTarget) {
+            // Consider to show a pop-up displaying overrides for the function
+            Function *function = symbol->type()->asFunctionType();
+            if (lookupVirtualFunctionOverrides(expression, function, scope, snapshot)) {
+                Class *klass = symbolFinder->findMatchingClassDeclaration(function, snapshot);
+                QTC_CHECK(klass);
+
+                if (m_virtualFunctionAssistProvider->configure(klass, function, snapshot,
+                                                               inNextSplit)) {
+                    m_widget->invokeAssist(TextEditor::FollowSymbol,
+                                           m_virtualFunctionAssistProvider);
+                }
+                return Link();
+            }
+
+            if (resolveTarget) {
                 Symbol *lastVisibleSymbol = doc->lastVisibleSymbolAt(line, column);
 
-                def = findDefinition(symbol, m_snapshot);
+                def = findDefinition(symbol, snapshot, symbolFinder);
 
                 if (def == lastVisibleSymbol)
                     def = 0; // jump to declaration then.
 
                 if (symbol->isForwardClassDeclaration())
-                    def = m_symbolFinder->findMatchingClassDeclaration(symbol, m_snapshot);
+                    def = symbolFinder->findMatchingClassDeclaration(symbol, snapshot);
             }
 
             link = m_widget->linkToSymbol(def ? def : symbol);
@@ -375,9 +512,9 @@ BaseTextEditorWidget::Link FollowSymbolUnderCursor::findLink()
     }
 
     // Handle macro uses
-    QTextCursor macroCursor = m_cursor;
+    QTextCursor macroCursor = cursor;
     const QByteArray name = CPPEditorWidget::identifierUnderCursor(&macroCursor).toLatin1();
-    link = findMacroLink(name, m_document);
+    link = findMacroLink(name, documentFromSemanticInfo);
     if (link.hasValidTarget()) {
         link.linkTextStart = macroCursor.selectionStart();
         link.linkTextEnd = macroCursor.selectionEnd();
@@ -387,87 +524,12 @@ BaseTextEditorWidget::Link FollowSymbolUnderCursor::findLink()
     return Link();
 }
 
-CPPEditorWidget::Link FollowSymbolUnderCursor::attemptFuncDeclDef(const QTextCursor &cursor)
+VirtualFunctionAssistProvider *FollowSymbolUnderCursor::virtualFunctionAssistProvider()
 {
-    m_snapshot.insert(m_document);
-
-    Link result;
-
-    QList<AST *> path = ASTPath(m_document)(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;
-    }
-
-    for (int i = path.size() - 1; i != -1; --i) {
-        AST *node = path.at(i);
-
-        if (node->asParameterDeclaration() != 0)
-            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 =
-                m_symbolFinder->findMatchingDeclaration(LookupContext(m_document, m_snapshot),
-                                                        funDef->symbol);
-        if (!candidates.isEmpty()) // TODO: improve disambiguation
-            target = candidates.first();
-    } else if (declParent->asSimpleDeclaration()) {
-        target = m_symbolFinder->findMatchingDefinition(funcDecl->symbol, m_snapshot);
-    }
-
-    if (target) {
-        result = m_widget->linkToSymbol(target);
-
-        unsigned startLine, startColumn, endLine, endColumn;
-        m_document->translationUnit()->getTokenStartPosition(name->firstToken(), &startLine,
-                                                             &startColumn);
-        m_document->translationUnit()->getTokenEndPosition(name->lastToken() - 1, &endLine,
-                                                           &endColumn);
-
-        QTextDocument *textDocument = cursor.document();
-        result.linkTextStart =
-                textDocument->findBlockByNumber(startLine - 1).position() + startColumn - 1;
-        result.linkTextEnd =
-                textDocument->findBlockByNumber(endLine - 1).position() + endColumn - 1;
-    }
-
-    return result;
+    return m_virtualFunctionAssistProvider;
 }
 
-Symbol *FollowSymbolUnderCursor::findDefinition(Symbol *symbol, const Snapshot &snapshot) const
+void FollowSymbolUnderCursor::setVirtualFunctionAssistProvider(VirtualFunctionAssistProvider *provider)
 {
-    if (symbol->isFunction())
-        return 0; // symbol is a function definition.
-
-    else if (!symbol->type()->isFunctionType())
-        return 0; // not a function declaration
-
-    return m_symbolFinder->findMatchingDefinition(symbol, snapshot);
+    m_virtualFunctionAssistProvider = provider;
 }
diff --git a/src/plugins/cppeditor/cppfollowsymbolundercursor.h b/src/plugins/cppeditor/cppfollowsymbolundercursor.h
index 5ef1f1902ad3de913626b0c4fb4b11131bfbee8a..4d88ecf0cd78c48026bb51815e5b130011167065 100644
--- a/src/plugins/cppeditor/cppfollowsymbolundercursor.h
+++ b/src/plugins/cppeditor/cppfollowsymbolundercursor.h
@@ -34,7 +34,9 @@
 #include <cplusplus/CppDocument.h>
 #include <texteditor/basetexteditor.h>
 
-#include <QTextCursor>
+QT_BEGIN_NAMESPACE
+class QTextCursor;
+QT_END_NAMESPACE
 
 namespace CppTools { class SymbolFinder; }
 
@@ -42,32 +44,27 @@ namespace CppEditor {
 namespace Internal {
 
 class CPPEditorWidget;
+class VirtualFunctionAssistProvider;
 
 class FollowSymbolUnderCursor
 {
 public:
     typedef TextEditor::BaseTextEditorWidget::Link Link;
 
-    // Ownership of widget and symbolFinder is *not* transferred.
-    FollowSymbolUnderCursor(CPPEditorWidget *widget, const QTextCursor &cursor, bool resolveTarget,
-                            const CPlusPlus::Snapshot &snapshot,
-                            const CPlusPlus::Document::Ptr &documentFromSemanticInfo,
-                            CppTools::SymbolFinder *symbolFinder);
+    FollowSymbolUnderCursor(CPPEditorWidget *widget);
+    ~FollowSymbolUnderCursor();
 
-    Link findLink();
+    Link findLink(const QTextCursor &cursor, bool resolveTarget,
+                  const CPlusPlus::Snapshot &snapshot,
+                  const CPlusPlus::Document::Ptr &documentFromSemanticInfo,
+                  CppTools::SymbolFinder *symbolFinder, bool inNextSplit);
 
-private:
-    Link attemptFuncDeclDef(const QTextCursor &cursor);
-    CPlusPlus::Symbol *findDefinition(CPlusPlus::Symbol *symbol,
-        const CPlusPlus::Snapshot &snapshot) const;
+    VirtualFunctionAssistProvider *virtualFunctionAssistProvider();
+    void setVirtualFunctionAssistProvider(VirtualFunctionAssistProvider *provider);
 
 private:
-    CppEditor::Internal::CPPEditorWidget *m_widget;
-    const QTextCursor m_cursor;
-    const bool m_resolveTarget;
-    CPlusPlus::Snapshot m_snapshot;
-    CPlusPlus::Document::Ptr m_document; // from SemanticInfo, i.e. with AST
-    CppTools::SymbolFinder *m_symbolFinder;
+    CPPEditorWidget *m_widget;
+    VirtualFunctionAssistProvider *m_virtualFunctionAssistProvider;
 };
 
 } // namespace Internal
diff --git a/src/plugins/cppeditor/cppvirtualfunctionassistprovider.cpp b/src/plugins/cppeditor/cppvirtualfunctionassistprovider.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..3cbb4f99dff19764d8b46b295f3c744ec425e040
--- /dev/null
+++ b/src/plugins/cppeditor/cppvirtualfunctionassistprovider.cpp
@@ -0,0 +1,324 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.  For licensing terms and
+** conditions see http://qt.digia.com/licensing.  For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights.  These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+
+#include "cppvirtualfunctionassistprovider.h"
+
+#include "cppeditorconstants.h"
+#include "cppelementevaluator.h"
+
+#include <cplusplus/Icons.h>
+#include <cplusplus/Overview.h>
+
+#include <coreplugin/actionmanager/actionmanager.h>
+#include <coreplugin/actionmanager/command.h>
+
+#include <texteditor/codeassist/basicproposalitem.h>
+#include <texteditor/codeassist/basicproposalitemlistmodel.h>
+#include <texteditor/codeassist/genericproposal.h>
+#include <texteditor/codeassist/genericproposalwidget.h>
+#include <texteditor/codeassist/iassistinterface.h>
+#include <texteditor/codeassist/iassistprocessor.h>
+#include <texteditor/codeassist/iassistproposal.h>
+
+#include <utils/qtcassert.h>
+
+using namespace CPlusPlus;
+using namespace CppEditor::Internal;
+using namespace TextEditor;
+
+class VirtualFunctionProposalItem: public BasicProposalItem {
+public:
+    VirtualFunctionProposalItem(const BaseTextEditorWidget::Link &link, bool openInSplit = true)
+        : m_link(link), m_openInSplit(openInSplit) {}
+
+    void apply(BaseTextEditor */*editor*/, int /*basePosition*/) const
+    {
+        if (!m_link.hasValidTarget())
+            return;
+
+        Core::EditorManager::OpenEditorFlags flags;
+        if (m_openInSplit)
+            flags |= Core::EditorManager::OpenInOtherSplit;
+        Core::EditorManager::openEditorAt(m_link.targetFileName,
+                                          m_link.targetLine,
+                                          m_link.targetColumn,
+                                          CppEditor::Constants::CPPEDITOR_ID,
+                                          flags);
+    }
+
+private:
+    BaseTextEditorWidget::Link m_link;
+    bool m_openInSplit;
+};
+
+/// Activate current item with the same shortcut that is configured for Follow Symbol Under Cursor.
+/// This is limited to single-key shortcuts without modifiers.
+class VirtualFunctionProposalWidget : public GenericProposalWidget
+{
+public:
+    VirtualFunctionProposalWidget(bool openInSplit)
+    {
+        const char *id = openInSplit
+            ? TextEditor::Constants::FOLLOW_SYMBOL_UNDER_CURSOR_IN_NEXT_SPLIT
+            : TextEditor::Constants::FOLLOW_SYMBOL_UNDER_CURSOR;
+        if (Core::Command *command = Core::ActionManager::command(id))
+            m_sequence = command->keySequence();
+    }
+
+protected:
+    bool eventFilter(QObject *o, QEvent *e)
+    {
+        if (e->type() == QEvent::ShortcutOverride && m_sequence.count() == 1) {
+            QKeyEvent *ke = static_cast<QKeyEvent *>(e);
+            const QKeySequence seq(ke->key());
+            if (seq == m_sequence) {
+                activateCurrentProposalItem();
+                e->accept();
+                return true;
+            }
+        }
+        return GenericProposalWidget::eventFilter(o, e);
+    }
+
+private:
+    QKeySequence m_sequence;
+};
+
+class VirtualFunctionProposal : public GenericProposal
+{
+public:
+    VirtualFunctionProposal(int cursorPos, IGenericProposalModel *model, bool openInSplit)
+        : GenericProposal(cursorPos, model)
+        , m_openInSplit(openInSplit)
+    {}
+
+    bool isFragile() const
+    { return true; }
+
+    IAssistProposalWidget *createWidget() const
+    { return new VirtualFunctionProposalWidget(m_openInSplit); }
+
+private:
+    bool m_openInSplit;
+};
+
+class VirtualFunctionsAssistProcessor : public IAssistProcessor
+{
+public:
+    VirtualFunctionsAssistProcessor(const VirtualFunctionAssistProvider *provider)
+        : m_startClass(provider->startClass())
+        , m_function(provider->function())
+        , m_snapshot(provider->snapshot())
+        , m_openInNextSplit(provider->openInNextSplit())
+    {}
+
+    IAssistProposal *immediateProposal(const TextEditor::IAssistInterface *interface)
+    {
+        QTC_ASSERT(m_function, return 0);
+
+        BasicProposalItem *hintItem = new VirtualFunctionProposalItem(CPPEditorWidget::Link());
+        hintItem->setText(QCoreApplication::translate("VirtualFunctionsAssistProcessor",
+                                                      "...searching overrides"));
+        hintItem->setOrder(-1000);
+
+        QList<BasicProposalItem *> items;
+        items << itemFromSymbol(m_function, m_function);
+        items << hintItem;
+        return new VirtualFunctionProposal(interface->position(),
+                                           new BasicProposalItemListModel(items),
+                                           m_openInNextSplit);
+    }
+
+    IAssistProposal *perform(const IAssistInterface *interface)
+    {
+        if (!interface)
+            return 0;
+
+        QTC_ASSERT(m_startClass, return 0);
+        QTC_ASSERT(m_function, return 0);
+        QTC_ASSERT(!m_snapshot.isEmpty(), return 0);
+
+        const QList<Symbol *> overrides = FunctionHelper::overrides(m_startClass, m_function,
+                                                                     m_snapshot);
+        QList<BasicProposalItem *> items;
+        foreach (Symbol *symbol, overrides)
+            items << itemFromSymbol(symbol, m_function);
+
+        return new VirtualFunctionProposal(interface->position(),
+                                           new BasicProposalItemListModel(items),
+                                           m_openInNextSplit);
+    }
+
+    BasicProposalItem *itemFromSymbol(Symbol *symbol, Symbol *firstSymbol) const
+    {
+        const QString text = m_overview.prettyName(LookupContext::fullyQualifiedName(symbol));
+        const CPPEditorWidget::Link link = CPPEditorWidget::linkToSymbol(symbol);
+
+        BasicProposalItem *item = new VirtualFunctionProposalItem(link, m_openInNextSplit);
+        item->setText(text);
+        item->setIcon(m_icons.iconForSymbol(symbol));
+        if (symbol == firstSymbol)
+            item->setOrder(1000); // Ensure top position for function of static type
+
+        return item;
+    }
+
+private:
+    Class *m_startClass;
+    Function *m_function;
+    Snapshot m_snapshot;
+    bool m_openInNextSplit;
+    Overview m_overview;
+    Icons m_icons;
+};
+
+VirtualFunctionAssistProvider::VirtualFunctionAssistProvider()
+    : m_function(0)
+    , m_openInNextSplit(false)
+{
+}
+
+bool VirtualFunctionAssistProvider::configure(Class *startClass, Function *function,
+                                              const Snapshot &snapshot, bool openInNextSplit)
+{
+    m_startClass = startClass;
+    m_function = function;
+    m_snapshot = snapshot;
+    m_openInNextSplit = openInNextSplit;
+    return true;
+}
+
+bool VirtualFunctionAssistProvider::isAsynchronous() const
+{
+    return true;
+}
+
+bool VirtualFunctionAssistProvider::supportsEditor(const Core::Id &editorId) const
+{
+    return editorId == CppEditor::Constants::CPPEDITOR_ID;
+}
+
+IAssistProcessor *VirtualFunctionAssistProvider::createProcessor() const
+{
+    return new VirtualFunctionsAssistProcessor(this);
+}
+
+enum VirtualType { Virtual, PureVirtual };
+
+static bool isVirtualFunction_helper(Function *function, const Snapshot &snapshot,
+                                     VirtualType virtualType)
+{
+    if (!function)
+        return false;
+
+    if ((virtualType == Virtual && function->isVirtual())
+            || (virtualType == PureVirtual && function->isPureVirtual())) {
+        return true;
+    }
+
+    const QString filePath = QString::fromUtf8(function->fileName(), function->fileNameLength());
+    if (Document::Ptr document = snapshot.document(filePath)) {
+        LookupContext context(document, snapshot);
+        QList<LookupItem> results = context.lookup(function->name(), function->enclosingScope());
+        if (!results.isEmpty()) {
+            foreach (const LookupItem &item, results) {
+                if (Symbol *symbol = item.declaration()) {
+                    if (Function *functionType = symbol->type()->asFunctionType()) {
+                        if (!functionType) {
+                            if (Template *t = item.type()->asTemplateType())
+                                if ((symbol = t->declaration()))
+                                    functionType = symbol->type()->asFunctionType();
+                        }
+                        const bool foundSuitable = virtualType == Virtual
+                            ? functionType->isVirtual()
+                            : functionType->isPureVirtual();
+                        if (foundSuitable)
+                            return true;
+                    }
+                }
+            }
+        }
+    }
+
+    return false;
+}
+
+bool FunctionHelper::isVirtualFunction(Function *function, const Snapshot &snapshot)
+{
+    return isVirtualFunction_helper(function, snapshot, Virtual);
+}
+
+bool FunctionHelper::isPureVirtualFunction(Function *function, const Snapshot &snapshot)
+{
+    return isVirtualFunction_helper(function, snapshot, PureVirtual);
+}
+
+QList<Symbol *> FunctionHelper::overrides(Class *startClass, Function *function,
+                                           const Snapshot &snapshot)
+{
+    QList<Symbol *> result;
+    QTC_ASSERT(function && startClass, return result);
+
+    FullySpecifiedType referenceType = function->type();
+    const Name *referenceName = function->name();
+
+    // Add itself
+    result << function;
+
+    // Find overrides
+    CppEditor::Internal::CppClass cppClass = CppClass(startClass);
+    cppClass.lookupDerived(startClass, snapshot);
+
+    QList<CppClass> l;
+    l << cppClass;
+
+    while (!l.isEmpty()) {
+        // Add derived
+        CppClass clazz = l.takeFirst();
+        foreach (const CppClass &d, clazz.derived) {
+            if (!l.contains(d))
+                l << d;
+        }
+
+        // Check member functions
+        QTC_ASSERT(clazz.declaration, continue);
+        Class *c = clazz.declaration->asClass();
+        QTC_ASSERT(c, continue);
+        for (int i = 0, total = c->memberCount(); i < total; ++i) {
+            Symbol *candidate = c->memberAt(i);
+            const Name *candidateName = candidate->name();
+            FullySpecifiedType candidateType = candidate->type();
+            if (candidateName->isEqualTo(referenceName) && candidateType.isEqualTo(referenceType))
+                result << candidate;
+        }
+    }
+
+    return result;
+}
diff --git a/src/plugins/cppeditor/cppvirtualfunctionassistprovider.h b/src/plugins/cppeditor/cppvirtualfunctionassistprovider.h
new file mode 100644
index 0000000000000000000000000000000000000000..b3bb76c24d1f91e6580384e3d5e075369c5f35d0
--- /dev/null
+++ b/src/plugins/cppeditor/cppvirtualfunctionassistprovider.h
@@ -0,0 +1,80 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.  For licensing terms and
+** conditions see http://qt.digia.com/licensing.  For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights.  These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#ifndef CPPFOLLOWVIRTUALSYMBOLS_H
+#define CPPFOLLOWVIRTUALSYMBOLS_H
+
+#include <texteditor/codeassist/iassistprovider.h>
+
+#include <cplusplus/CppDocument.h>
+#include <cplusplus/Symbols.h>
+
+namespace CppEditor {
+namespace Internal {
+
+class VirtualFunctionAssistProvider : public TextEditor::IAssistProvider
+{
+public:
+    VirtualFunctionAssistProvider();
+
+    virtual bool configure(CPlusPlus::Class *startClass, CPlusPlus::Function *function,
+        const CPlusPlus::Snapshot &snapshot, bool openInNextSplit);
+    CPlusPlus::Class *startClass() const { return m_startClass; }
+    CPlusPlus::Function *function() const { return m_function; }
+    CPlusPlus::Snapshot snapshot() const { return m_snapshot; }
+    bool openInNextSplit() const { return m_openInNextSplit; }
+
+    bool isAsynchronous() const;
+    bool supportsEditor(const Core::Id &editorId) const;
+    TextEditor::IAssistProcessor *createProcessor() const;
+
+private:
+    CPlusPlus::Class *m_startClass;
+    CPlusPlus::Function *m_function;
+    CPlusPlus::Snapshot m_snapshot;
+    bool m_openInNextSplit;
+};
+
+class FunctionHelper
+{
+public:
+    static bool isVirtualFunction(CPlusPlus::Function *function,
+        const CPlusPlus::Snapshot &snapshot);
+
+    static bool isPureVirtualFunction(CPlusPlus::Function *function,
+        const CPlusPlus::Snapshot &snapshot);
+
+    static QList<CPlusPlus::Symbol *> overrides(CPlusPlus::Class *startClass,
+        CPlusPlus::Function *function, const CPlusPlus::Snapshot &snapshot);
+};
+
+} // namespace Internal
+} // namespace CppEditor
+
+#endif // CPPFOLLOWVIRTUALSYMBOLS_H
diff --git a/src/plugins/cppeditor/followsymbol_switchmethoddecldef_test.cpp b/src/plugins/cppeditor/followsymbol_switchmethoddecldef_test.cpp
index 46ff3e159ba5664222015984a444cf6f0fa5ab6b..f922b20de3c55346212dac393e8ec4d90aca6214 100644
--- a/src/plugins/cppeditor/followsymbol_switchmethoddecldef_test.cpp
+++ b/src/plugins/cppeditor/followsymbol_switchmethoddecldef_test.cpp
@@ -29,8 +29,13 @@
 
 #include "cppeditor.h"
 #include "cppeditorplugin.h"
+#include "cppelementevaluator.h"
+#include "cppvirtualfunctionassistprovider.h"
 
 #include <coreplugin/plugintestutils.h>
+#include <texteditor/codeassist/iassistproposal.h>
+#include <texteditor/codeassist/iassistprocessor.h>
+#include <texteditor/codeassist/basicproposalitemlistmodel.h>
 #include <utils/fileutils.h>
 
 #include <QDebug>
@@ -57,6 +62,60 @@ using namespace Core;
 
 namespace {
 
+/// A fake virtual functions assist provider that runs processor->perform() already in configure()
+class VirtualFunctionTestAssistProvider : public VirtualFunctionAssistProvider
+{
+public:
+    VirtualFunctionTestAssistProvider(CPPEditorWidget *editorWidget)
+        : m_editorWidget(editorWidget)
+    {}
+
+    // Invoke the processor already here to calculate the proposals. Return false in order to
+    // indicate that configure failed, so the actual code assist invocation leading to a pop-up
+    // will not happen.
+    bool configure(CPlusPlus::Class *startClass, CPlusPlus::Function *function,
+                   const CPlusPlus::Snapshot &snapshot, bool openInNextSplit)
+    {
+        VirtualFunctionAssistProvider::configure(startClass, function, snapshot, openInNextSplit);
+
+        IAssistProcessor *processor = createProcessor();
+        IAssistInterface *interface = m_editorWidget->createAssistInterface(FollowSymbol,
+                                                                            ExplicitlyInvoked);
+        IAssistProposal *immediateProposal = processor->immediateProposal(interface);
+        IAssistProposal *finalProposal = processor->perform(interface);
+
+        m_immediateItems = itemList(immediateProposal->model());
+        m_finalItems = itemList(finalProposal->model());
+
+        return false;
+    }
+
+    static QStringList itemList(IAssistProposalModel *imodel)
+    {
+        QStringList immediateItems;
+        BasicProposalItemListModel *model = dynamic_cast<BasicProposalItemListModel *>(imodel);
+        if (!model)
+            return immediateItems;
+        if (model->isSortable(QString()))
+            model->sort(QString());
+        model->removeDuplicates();
+
+        for (int i = 0, size = model->size(); i < size; ++i) {
+            const QString text = model->text(i);
+            immediateItems.append(text);
+        }
+
+        return immediateItems;
+    }
+
+public:
+    QStringList m_immediateItems;
+    QStringList m_finalItems;
+
+private:
+    CPPEditorWidget *m_editorWidget;
+};
+
 class TestDocument;
 typedef QSharedPointer<TestDocument> TestDocumentPtr;
 
@@ -136,12 +195,16 @@ class TestCase
 {
 public:
     enum CppEditorAction {
-        FollowSymbolUnderCursor,
-        SwitchBetweenMethodDeclarationDefinition
+        FollowSymbolUnderCursorAction,
+        SwitchBetweenMethodDeclarationDefinitionAction
     };
 
-    TestCase(CppEditorAction action, const QByteArray &source);
-    TestCase(CppEditorAction action, const QList<TestDocumentPtr> theTestFiles);
+    TestCase(CppEditorAction action, const QByteArray &source,
+             const QStringList &expectedVirtualFunctionImmediateProposal = QStringList(),
+             const QStringList &expectedVirtualFunctionFinalProposal = QStringList());
+    TestCase(CppEditorAction action, const QList<TestDocumentPtr> theTestFiles,
+             const QStringList &expectedVirtualSymbolsImmediateProposal = QStringList(),
+             const QStringList &expectedVirtualSymbolsFinalProposal = QStringList());
     ~TestCase();
 
     void run(bool expectedFail = false);
@@ -158,12 +221,18 @@ private:
 private:
     CppEditorAction m_action;
     QList<TestDocumentPtr> m_testFiles;
+    QStringList m_expectedVirtualSymbolsImmediateProposal; // for virtual functions
+    QStringList m_expectedVirtualSymbolsFinalProposals;    // for virtual functions
 };
 
 /// Convenience function for creating a TestDocument.
 /// See TestDocument.
-TestCase::TestCase(CppEditorAction action, const QByteArray &source)
+TestCase::TestCase(CppEditorAction action, const QByteArray &source,
+                   const QStringList &expectedVirtualFunctionImmediateProposal,
+                   const QStringList &expectedVirtualFunctionFinalProposal)
     : m_action(action)
+    , m_expectedVirtualSymbolsImmediateProposal(expectedVirtualFunctionImmediateProposal)
+    , m_expectedVirtualSymbolsFinalProposals(expectedVirtualFunctionFinalProposal)
 {
     m_testFiles << TestDocument::create(source, QLatin1String("file.cpp"));
     init();
@@ -173,9 +242,13 @@ TestCase::TestCase(CppEditorAction action, const QByteArray &source)
 /// Exactly one test document must be provided that contains '@', the initial position marker.
 /// Exactly one test document must be provided that contains '$', the target position marker.
 /// It can be the same document.
-TestCase::TestCase(CppEditorAction action, const QList<TestDocumentPtr> theTestFiles)
+TestCase::TestCase(CppEditorAction action, const QList<TestDocumentPtr> theTestFiles,
+                   const QStringList &expectedVirtualSymbolsImmediateProposal,
+                   const QStringList &expectedVirtualSymbolsFinalProposal)
     : m_action(action)
     , m_testFiles(theTestFiles)
+    , m_expectedVirtualSymbolsImmediateProposal(expectedVirtualSymbolsImmediateProposal)
+    , m_expectedVirtualSymbolsFinalProposals(expectedVirtualSymbolsFinalProposal)
 {
     init();
 }
@@ -292,12 +365,29 @@ void TestCase::run(bool expectedFail)
 //    qDebug() << "Initial line:" << initialTestFile->editor->currentLine();
 //    qDebug() << "Initial column:" << initialTestFile->editor->currentColumn() - 1;
 
+    QStringList immediateVirtualSymbolResults;
+    QStringList finalVirtualSymbolResults;
+
     // Trigger the action
     switch (m_action) {
-    case FollowSymbolUnderCursor:
+    case FollowSymbolUnderCursorAction: {
+        CPPEditorWidget *widget = initialTestFile->editorWidget;
+        FollowSymbolUnderCursor *delegate = widget->followSymbolUnderCursorDelegate();
+        VirtualFunctionAssistProvider *original = delegate->virtualFunctionAssistProvider();
+
+        // Set test provider, run and get results
+        QScopedPointer<VirtualFunctionTestAssistProvider> testProvider(
+            new VirtualFunctionTestAssistProvider(widget));
+        delegate->setVirtualFunctionAssistProvider(testProvider.data());
         initialTestFile->editorWidget->openLinkUnderCursor();
+        immediateVirtualSymbolResults = testProvider->m_immediateItems;
+        finalVirtualSymbolResults = testProvider->m_finalItems;
+
+        // Restore original test provider
+        delegate->setVirtualFunctionAssistProvider(original);
         break;
-    case SwitchBetweenMethodDeclarationDefinition:
+    }
+    case SwitchBetweenMethodDeclarationDefinitionAction:
         CppEditorPlugin::instance()->switchDeclarationDefinition();
         break;
     default:
@@ -318,10 +408,16 @@ void TestCase::run(bool expectedFail)
                                        &expectedLine, &expectedColumn);
 //    qDebug() << "Expected line:" << expectedLine;
 //    qDebug() << "Expected column:" << expectedColumn;
+
     if (expectedFail)
         QEXPECT_FAIL("", "Contributor works on a fix.", Abort);
     QCOMPARE(currentTextEditor->currentLine(), expectedLine);
     QCOMPARE(currentTextEditor->currentColumn() - 1, expectedColumn);
+
+//    qDebug() << immediateVirtualSymbolResults;
+//    qDebug() << finalVirtualSymbolResults;
+    QCOMPARE(immediateVirtualSymbolResults, m_expectedVirtualSymbolsImmediateProposal);
+    QCOMPARE(finalVirtualSymbolResults, m_expectedVirtualSymbolsFinalProposals);
 }
 
 } // anonymous namespace
@@ -354,7 +450,7 @@ void CppEditorPlugin::test_SwitchMethodDeclarationDefinition_fromFunctionDeclara
         ;
     testFiles << TestDocument::create(sourceContents, QLatin1String("file.cpp"));
 
-    TestCase test(TestCase::SwitchBetweenMethodDeclarationDefinition, testFiles);
+    TestCase test(TestCase::SwitchBetweenMethodDeclarationDefinitionAction, testFiles);
     test.run();
 }
 
@@ -386,7 +482,7 @@ void CppEditorPlugin::test_SwitchMethodDeclarationDefinition_fromFunctionDefinit
         ;
     testFiles << TestDocument::create(sourceContents, QLatin1String("file.cpp"));
 
-    TestCase test(TestCase::SwitchBetweenMethodDeclarationDefinition, testFiles);
+    TestCase test(TestCase::SwitchBetweenMethodDeclarationDefinitionAction, testFiles);
     test.run();
 }
 
@@ -418,7 +514,7 @@ void CppEditorPlugin::test_SwitchMethodDeclarationDefinition_fromFunctionBody()
         ;
     testFiles << TestDocument::create(sourceContents, QLatin1String("file.cpp"));
 
-    TestCase test(TestCase::SwitchBetweenMethodDeclarationDefinition, testFiles);
+    TestCase test(TestCase::SwitchBetweenMethodDeclarationDefinitionAction, testFiles);
     test.run();
 }
 
@@ -450,7 +546,7 @@ void CppEditorPlugin::test_SwitchMethodDeclarationDefinition_fromReturnType()
         ;
     testFiles << TestDocument::create(sourceContents, QLatin1String("file.cpp"));
 
-    TestCase test(TestCase::SwitchBetweenMethodDeclarationDefinition, testFiles);
+    TestCase test(TestCase::SwitchBetweenMethodDeclarationDefinitionAction, testFiles);
     test.run();
 }
 
@@ -465,7 +561,7 @@ void CppEditorPlugin::test_FollowSymbolUnderCursor_globalVarFromFunction()
         "}\n"           // Line 5
         ;
 
-    TestCase test(TestCase::FollowSymbolUnderCursor, source);
+    TestCase test(TestCase::FollowSymbolUnderCursorAction, source);
     test.run();
 }
 
@@ -483,7 +579,7 @@ void CppEditorPlugin::test_FollowSymbolUnderCursor_funLocalVarHidesClassMember()
         "};\n"
         ;
 
-    TestCase test(TestCase::FollowSymbolUnderCursor, source);
+    TestCase test(TestCase::FollowSymbolUnderCursorAction, source);
     test.run();
 }
 
@@ -503,7 +599,7 @@ void CppEditorPlugin::test_FollowSymbolUnderCursor_funLocalVarHidesNamespaceMemb
         "}\n"                                // Line 10
         ;
 
-    TestCase test(TestCase::FollowSymbolUnderCursor, source);
+    TestCase test(TestCase::FollowSymbolUnderCursorAction, source);
     test.run();
 }
 
@@ -521,7 +617,7 @@ void CppEditorPlugin::test_FollowSymbolUnderCursor_loopLocalVarHidesOuterScopeVa
         "}\n";
         ;
 
-    TestCase test(TestCase::FollowSymbolUnderCursor, source);
+    TestCase test(TestCase::FollowSymbolUnderCursorAction, source);
     test.run();
 }
 
@@ -539,7 +635,7 @@ void CppEditorPlugin::test_FollowSymbolUnderCursor_loopLocalVarHidesOuterScopeVa
         "}\n";
         ;
 
-    TestCase test(TestCase::FollowSymbolUnderCursor, source);
+    TestCase test(TestCase::FollowSymbolUnderCursorAction, source);
     test.run();
 }
 
@@ -553,7 +649,7 @@ void CppEditorPlugin::test_FollowSymbolUnderCursor_subsequentDefinedClassMember(
         "};\n"
         ;
 
-    TestCase test(TestCase::FollowSymbolUnderCursor, source);
+    TestCase test(TestCase::FollowSymbolUnderCursorAction, source);
     test.run();
 }
 
@@ -569,7 +665,7 @@ void CppEditorPlugin::test_FollowSymbolUnderCursor_classMemberHidesOuterTypeDef(
         "};\n"                                                 // Line 5
         ;
 
-    TestCase test(TestCase::FollowSymbolUnderCursor, source);
+    TestCase test(TestCase::FollowSymbolUnderCursorAction, source);
     test.run();
 }
 
@@ -584,7 +680,7 @@ void CppEditorPlugin::test_FollowSymbolUnderCursor_globalVarFromEnum()
         "}\n"                                             // Line 5
         ;
 
-    TestCase test(TestCase::FollowSymbolUnderCursor, source);
+    TestCase test(TestCase::FollowSymbolUnderCursorAction, source);
     test.run(/*expectedFail =*/ true);
 }
 
@@ -599,7 +695,7 @@ void CppEditorPlugin::test_FollowSymbolUnderCursor_selfInitialization()
         "}\n"                                              // Line 5
         ;
 
-    TestCase test(TestCase::FollowSymbolUnderCursor, source);
+    TestCase test(TestCase::FollowSymbolUnderCursorAction, source);
     test.run();
 }
 
@@ -612,7 +708,7 @@ void CppEditorPlugin::test_FollowSymbolUnderCursor_pointerToClassInClassDefiniti
         "};\n"
         ;
 
-    TestCase test(TestCase::FollowSymbolUnderCursor, source);
+    TestCase test(TestCase::FollowSymbolUnderCursorAction, source);
     test.run();
 }
 
@@ -626,7 +722,7 @@ void CppEditorPlugin::test_FollowSymbolUnderCursor_previouslyDefinedMemberFromAr
         "};\n"
         ;
 
-    TestCase test(TestCase::FollowSymbolUnderCursor, source);
+    TestCase test(TestCase::FollowSymbolUnderCursorAction, source);
     test.run();
 }
 
@@ -648,7 +744,7 @@ void CppEditorPlugin::test_FollowSymbolUnderCursor_outerStaticMemberVariableFrom
         "};\n"
         ;
 
-    TestCase test(TestCase::FollowSymbolUnderCursor, source);
+    TestCase test(TestCase::FollowSymbolUnderCursorAction, source);
     test.run();
 }
 
@@ -668,7 +764,7 @@ void CppEditorPlugin::test_FollowSymbolUnderCursor_memberVariableFollowingDotOpe
         "}\n"                // Line 10
         ;
 
-    TestCase test(TestCase::FollowSymbolUnderCursor, source);
+    TestCase test(TestCase::FollowSymbolUnderCursorAction, source);
     test.run();
 }
 
@@ -688,7 +784,7 @@ void CppEditorPlugin::test_FollowSymbolUnderCursor_memberVariableFollowingArrowO
         "}\n"                   // Line 10
         ;
 
-    TestCase test(TestCase::FollowSymbolUnderCursor, source);
+    TestCase test(TestCase::FollowSymbolUnderCursorAction, source);
     test.run();
 }
 
@@ -707,7 +803,7 @@ void CppEditorPlugin::test_FollowSymbolUnderCursor_staticMemberVariableFollowing
         "}\n"
         ;
 
-    TestCase test(TestCase::FollowSymbolUnderCursor, source);
+    TestCase test(TestCase::FollowSymbolUnderCursorAction, source);
     test.run();
 }
 
@@ -727,7 +823,7 @@ void CppEditorPlugin::test_FollowSymbolUnderCursor_staticMemberVariableFollowing
         "}\n"                       // Line 10
         ;
 
-    TestCase test(TestCase::FollowSymbolUnderCursor, source);
+    TestCase test(TestCase::FollowSymbolUnderCursorAction, source);
     test.run();
 }
 
@@ -748,7 +844,7 @@ void CppEditorPlugin::test_FollowSymbolUnderCursor_staticMemberVariableFollowing
         "}\n"                        // Line 10
         ;
 
-    TestCase test(TestCase::FollowSymbolUnderCursor, source);
+    TestCase test(TestCase::FollowSymbolUnderCursorAction, source);
     test.run();
 }
 
@@ -762,7 +858,7 @@ void CppEditorPlugin::test_FollowSymbolUnderCursor_previouslyDefinedEnumValueFro
         "};\n"
         ;
 
-    TestCase test(TestCase::FollowSymbolUnderCursor, source);
+    TestCase test(TestCase::FollowSymbolUnderCursorAction, source);
     test.run();
 }
 
@@ -785,7 +881,7 @@ void CppEditorPlugin::test_FollowSymbolUnderCursor_nsMemberHidesNsMemberIntroduc
         "}\n"
         ;
 
-    TestCase test(TestCase::FollowSymbolUnderCursor, source);
+    TestCase test(TestCase::FollowSymbolUnderCursorAction, source);
     test.run();
 }
 
@@ -812,7 +908,7 @@ void CppEditorPlugin::test_FollowSymbolUnderCursor_baseClassFunctionIntroducedBy
         "}\n"
         ;
 
-    TestCase test(TestCase::FollowSymbolUnderCursor, source);
+    TestCase test(TestCase::FollowSymbolUnderCursorAction, source);
     test.run();
 }
 
@@ -839,7 +935,7 @@ void CppEditorPlugin::test_FollowSymbolUnderCursor_funWithSameNameAsBaseClassFun
         "}\n"
         ;
 
-    TestCase test(TestCase::FollowSymbolUnderCursor, source);
+    TestCase test(TestCase::FollowSymbolUnderCursorAction, source);
     test.run();
 }
 
@@ -858,7 +954,7 @@ void CppEditorPlugin::test_FollowSymbolUnderCursor_funLocalVarHidesOuterClass()
         "}\n"
         ;
 
-    TestCase test(TestCase::FollowSymbolUnderCursor, source);
+    TestCase test(TestCase::FollowSymbolUnderCursorAction, source);
     test.run();
 }
 
@@ -876,7 +972,7 @@ void CppEditorPlugin::test_FollowSymbolUnderCursor_classConstructor()
             "{\n"
             "}\n";
 
-    TestCase test(TestCase::FollowSymbolUnderCursor, source);
+    TestCase test(TestCase::FollowSymbolUnderCursorAction, source);
     test.run();
 }
 
@@ -894,7 +990,7 @@ void CppEditorPlugin::test_FollowSymbolUnderCursor_classDestructor()
             "{\n"
             "}\n";
 
-    TestCase test(TestCase::FollowSymbolUnderCursor, source);
+    TestCase test(TestCase::FollowSymbolUnderCursorAction, source);
     test.run();
 }
 
@@ -988,7 +1084,7 @@ void CppEditorPlugin::test_FollowSymbolUnderCursor_QObject_connect()
         return;
     }
 
-    TestCase test(TestCase::FollowSymbolUnderCursor, source);
+    TestCase test(TestCase::FollowSymbolUnderCursorAction, source);
     test.run();
 }
 
@@ -1005,7 +1101,7 @@ void CppEditorPlugin::test_FollowSymbolUnderCursor_using_QTCREATORBUG7903_global
             "}\n"
         ;
 
-    TestCase test(TestCase::FollowSymbolUnderCursor, source);
+    TestCase test(TestCase::FollowSymbolUnderCursorAction, source);
     test.run();
 }
 
@@ -1024,7 +1120,7 @@ void CppEditorPlugin::test_FollowSymbolUnderCursor_using_QTCREATORBUG7903_namesp
             "}\n"
         ;
 
-    TestCase test(TestCase::FollowSymbolUnderCursor, source);
+    TestCase test(TestCase::FollowSymbolUnderCursorAction, source);
     test.run();
 }
 
@@ -1041,7 +1137,177 @@ void CppEditorPlugin::test_FollowSymbolUnderCursor_using_QTCREATORBUG7903_inside
             "}\n"
         ;
 
-    TestCase test(TestCase::FollowSymbolUnderCursor, source);
+    TestCase test(TestCase::FollowSymbolUnderCursorAction, source);
+    test.run();
+}
+
+/// Check: Static type is base class pointer, all overrides are presented.
+void CppEditorPlugin::test_FollowSymbolUnderCursor_virtualFunctionCall_allOverrides()
+{
+    const QByteArray source =
+            "struct A { virtual void virt() = 0; };\n"
+            "void A::virt() {}\n"
+            "\n"
+            "struct B : A { void virt(); };\n"
+            "void B::virt() {}\n"
+            "\n"
+            "struct C : B { void virt(); };\n"
+            "void C::virt() {}\n"
+            "\n"
+            "struct CD1 : C { void virt(); };\n"
+            "void CD1::virt() {}\n"
+            "\n"
+            "struct CD2 : C { void virt(); };\n"
+            "void CD2::virt() {}\n"
+            "\n"
+            "int f(A *o)\n"
+            "{\n"
+            "    o->$@virt();\n"
+            "}\n"
+            ;
+
+    const QStringList immediateResults = QStringList()
+            << QLatin1String("A::virt")
+            << QLatin1String("...searching overrides");
+    const QStringList finalResults = QStringList()
+            << QLatin1String("A::virt")
+            << QLatin1String("A::virt") // TODO: Double entry
+            << QLatin1String("B::virt")
+            << QLatin1String("C::virt")
+            << QLatin1String("CD1::virt")
+            << QLatin1String("CD2::virt");
+
+    TestCase test(TestCase::FollowSymbolUnderCursorAction, source, immediateResults, finalResults);
+    test.run();
+}
+
+/// Check: Static type is derived class pointer, only overrides of sub classes are presented.
+void CppEditorPlugin::test_FollowSymbolUnderCursor_virtualFunctionCall_possibleOverrides1()
+{
+    const QByteArray source =
+            "struct A { virtual void virt() = 0; };\n"
+            "void A::virt() {}\n"
+            "\n"
+            "struct B : A { void virt(); };\n"
+            "void B::virt() {}\n"
+            "\n"
+            "struct C : B { void virt(); };\n"
+            "void C::virt() {}\n"
+            "\n"
+            "struct CD1 : C { void virt(); };\n"
+            "void CD1::virt() {}\n"
+            "\n"
+            "struct CD2 : C { void virt(); };\n"
+            "void CD2::virt() {}\n"
+            "\n"
+            "int f(B *o)\n"
+            "{\n"
+            "   o->$@virt();\n"
+            "}\n"
+            ;
+
+    const QStringList immediateResults = QStringList()
+            << QLatin1String("B::virt")
+            << QLatin1String("...searching overrides");
+    const QStringList finalResults = QStringList()
+            << QLatin1String("B::virt")
+            << QLatin1String("B::virt") // Double entry
+            << QLatin1String("C::virt")
+            << QLatin1String("CD1::virt")
+            << QLatin1String("CD2::virt");
+
+    TestCase test(TestCase::FollowSymbolUnderCursorAction, source, immediateResults, finalResults);
+    test.run();
+}
+
+/// Check: Virtual function call in member of class hierarchy, only possible overrides are presented.
+void CppEditorPlugin::test_FollowSymbolUnderCursor_virtualFunctionCall_possibleOverrides2()
+{
+    const QByteArray source =
+            "struct A { virtual void f(); };\n"
+            "void A::f() {}\n"
+            "\n"
+            "struct B : public A { void f(); };\n"
+            "void B::f() {}\n"
+            "\n"
+            "struct C : public B { void g() { f$@(); } }; \n"
+            "\n"
+            "struct D : public C { void f(); };\n"
+            "void D::f() {}\n"
+            ;
+
+    const QStringList immediateResults = QStringList()
+            << QLatin1String("B::f")
+            << QLatin1String("...searching overrides");
+    const QStringList finalResults = QStringList()
+            << QLatin1String("B::f")
+            << QLatin1String("B::f")
+            << QLatin1String("D::f");
+
+    TestCase test(TestCase::FollowSymbolUnderCursorAction, source, immediateResults, finalResults);
+    test.run();
+}
+
+/// Check: Do not trigger on qualified function calls.
+void CppEditorPlugin::test_FollowSymbolUnderCursor_virtualFunctionCall_notOnQualified()
+{
+    const QByteArray source =
+            "struct A { virtual void f(); };\n"
+            "void A::$f() {}\n"
+            "\n"
+            "struct B : public A {\n"
+            "    void f();\n"
+            "    void g() { A::@f(); }\n"
+            "};\n"
+            ;
+
+    TestCase test(TestCase::FollowSymbolUnderCursorAction, source);
+    test.run();
+}
+
+/// Check: Do not trigger on member function declaration.
+void CppEditorPlugin::test_FollowSymbolUnderCursor_virtualFunctionCall_notOnDeclaration()
+{
+    const QByteArray source =
+            "struct A { virtual void f(); };\n"
+            "void A::f() {}\n"
+            "\n"
+            "struct B : public A { void f@(); };\n"
+            "void B::$f() {}\n"
+            ;
+
+    TestCase test(TestCase::FollowSymbolUnderCursorAction, source);
+    test.run();
+}
+
+/// Check: Do not trigger on function definition.
+void CppEditorPlugin::test_FollowSymbolUnderCursor_virtualFunctionCall_notOnDefinition()
+{
+    const QByteArray source =
+            "struct A { virtual void f(); };\n"
+            "void A::f() {}\n"
+            "\n"
+            "struct B : public A { void $f(); };\n"
+            "void B::@f() {}\n"
+            ;
+
+    TestCase test(TestCase::FollowSymbolUnderCursorAction, source);
+    test.run();
+}
+
+void CppEditorPlugin::test_FollowSymbolUnderCursor_virtualFunctionCall_notOnNonPointerNonReference()
+{
+    const QByteArray source =
+            "struct A { virtual void f(); };\n"
+            "void A::f() {}\n"
+            "\n"
+            "struct B : public A { void f(); };\n"
+            "void B::$f() {}\n"
+            "\n"
+            "void client(B b) { b.@f(); }\n"
+            ;
+
+    TestCase test(TestCase::FollowSymbolUnderCursorAction, source);
     test.run();
 }
 
diff --git a/src/plugins/qmljseditor/qmljseditor.cpp b/src/plugins/qmljseditor/qmljseditor.cpp
index f54d440834bab915a1fdae91ca6b65325826b056..c8b71c9bdad15ded748c18e3773e425bdd1f2128 100644
--- a/src/plugins/qmljseditor/qmljseditor.cpp
+++ b/src/plugins/qmljseditor/qmljseditor.cpp
@@ -1063,7 +1063,9 @@ void QmlJSTextEditorWidget::createToolBar(QmlJSEditor *editor)
     editor->insertExtraToolBarWidget(TextEditor::BaseTextEditor::Left, m_outlineCombo);
 }
 
-TextEditor::BaseTextEditorWidget::Link QmlJSTextEditorWidget::findLinkAt(const QTextCursor &cursor, bool /*resolveTarget*/)
+TextEditor::BaseTextEditorWidget::Link QmlJSTextEditorWidget::findLinkAt(const QTextCursor &cursor,
+                                                                         bool /*resolveTarget*/,
+                                                                         bool /*inNextSplit*/)
 {
     const SemanticInfo semanticInfo = m_semanticInfo;
     if (! semanticInfo.isValid())
diff --git a/src/plugins/qmljseditor/qmljseditor.h b/src/plugins/qmljseditor/qmljseditor.h
index 596bbcf4276f2b7506ed7d056124485201d8e54e..5ce152d3d31fb1e99c43cceb54ff4487f6cb3830 100644
--- a/src/plugins/qmljseditor/qmljseditor.h
+++ b/src/plugins/qmljseditor/qmljseditor.h
@@ -161,7 +161,9 @@ protected:
     void scrollContentsBy(int dx, int dy);
     TextEditor::BaseTextEditor *createEditor();
     void createToolBar(QmlJSEditor *editable);
-    TextEditor::BaseTextEditorWidget::Link findLinkAt(const QTextCursor &cursor, bool resolveTarget = true);
+    TextEditor::BaseTextEditorWidget::Link findLinkAt(const QTextCursor &cursor,
+                                                      bool resolveTarget = true,
+                                                      bool inNextSplit = false);
     QString foldReplacementText(const QTextBlock &block) const;
 
 private:
diff --git a/src/plugins/qt4projectmanager/profileeditor.cpp b/src/plugins/qt4projectmanager/profileeditor.cpp
index ab35944aaa720d7d201072af1ba580616c20d4bb..3460c76b982612cf35970ac1ae8a1600995e5892 100644
--- a/src/plugins/qt4projectmanager/profileeditor.cpp
+++ b/src/plugins/qt4projectmanager/profileeditor.cpp
@@ -112,7 +112,8 @@ static bool isValidFileNameChar(const QChar &c)
 }
 
 ProFileEditorWidget::Link ProFileEditorWidget::findLinkAt(const QTextCursor &cursor,
-                                      bool /* resolveTarget */)
+                                                          bool /*resolveTarget*/,
+                                                          bool /*inNextSplit*/)
 {
     Link link;
 
diff --git a/src/plugins/qt4projectmanager/profileeditor.h b/src/plugins/qt4projectmanager/profileeditor.h
index e8a8fb3ad08a85e20ba43804d1be74964ba24869..3e4150b2aeaeda0ebca19cb146ae06df8a24b426 100644
--- a/src/plugins/qt4projectmanager/profileeditor.h
+++ b/src/plugins/qt4projectmanager/profileeditor.h
@@ -72,7 +72,8 @@ public:
     void unCommentSelection();
 
 protected:
-    virtual Link findLinkAt(const QTextCursor &, bool resolveTarget = true);
+    virtual Link findLinkAt(const QTextCursor &, bool resolveTarget = true,
+                            bool inNextSplit = false);
     TextEditor::BaseTextEditor *createEditor();
     void contextMenuEvent(QContextMenuEvent *);
 
diff --git a/src/plugins/texteditor/basetexteditor.cpp b/src/plugins/texteditor/basetexteditor.cpp
index 504404b97970b62bf1c76d74a29122cc1da9989e..9588585f8f4bc746f36c25d236da07e418f9584f 100644
--- a/src/plugins/texteditor/basetexteditor.cpp
+++ b/src/plugins/texteditor/basetexteditor.cpp
@@ -1047,16 +1047,16 @@ void BaseTextEditorWidget::unindent()
 
 void BaseTextEditorWidget::openLinkUnderCursor()
 {
-    Link symbolLink = findLinkAt(textCursor());
-
-    openLink(symbolLink, alwaysOpenLinksInNextSplit());
+    const bool openInNextSplit = alwaysOpenLinksInNextSplit();
+    Link symbolLink = findLinkAt(textCursor(), true, openInNextSplit);
+    openLink(symbolLink, openInNextSplit);
 }
 
 void BaseTextEditorWidget::openLinkUnderCursorInNextSplit()
 {
-    Link symbolLink = findLinkAt(textCursor());
-
-    openLink(symbolLink, !alwaysOpenLinksInNextSplit());
+    const bool openInNextSplit = !alwaysOpenLinksInNextSplit();
+    Link symbolLink = findLinkAt(textCursor(), true, openInNextSplit);
+    openLink(symbolLink, openInNextSplit);
 }
 
 void BaseTextEditorWidget::abortAssist()
@@ -4803,7 +4803,7 @@ void BaseTextEditorWidget::reindent(QTextDocument *doc, const QTextCursor &curso
     d->m_indenter->reindent(doc, cursor, tabSettings());
 }
 
-BaseTextEditorWidget::Link BaseTextEditorWidget::findLinkAt(const QTextCursor &, bool)
+BaseTextEditorWidget::Link BaseTextEditorWidget::findLinkAt(const QTextCursor &, bool, bool)
 {
     return Link();
 }
diff --git a/src/plugins/texteditor/basetexteditor.h b/src/plugins/texteditor/basetexteditor.h
index 2f7ee2ee9310c814499e6c405844e4559727197d..3db73fb48267cdc12f9525e80491b6f3dc4ddb28 100644
--- a/src/plugins/texteditor/basetexteditor.h
+++ b/src/plugins/texteditor/basetexteditor.h
@@ -503,7 +503,8 @@ protected:
        \a resolveTarget is set to true when the target of the link is relevant
        (it isn't until the link is used).
      */
-    virtual Link findLinkAt(const QTextCursor &, bool resolveTarget = true);
+    virtual Link findLinkAt(const QTextCursor &, bool resolveTarget = true,
+                            bool inNextSplit = false);
 
     /*!
        Reimplement this function if you want to customize the way a link is
diff --git a/src/plugins/texteditor/codeassist/assistenums.h b/src/plugins/texteditor/codeassist/assistenums.h
index 31447cec1bc05eb6ee2125028cf9be1bd193b261..b71a96ce467a90fc3c431ef1b45a3cb0306ffe40 100644
--- a/src/plugins/texteditor/codeassist/assistenums.h
+++ b/src/plugins/texteditor/codeassist/assistenums.h
@@ -35,7 +35,8 @@ namespace TextEditor {
 enum AssistKind
 {
     Completion,
-    QuickFix
+    QuickFix,
+    FollowSymbol
 };
 
 enum AssistReason
diff --git a/src/plugins/texteditor/codeassist/genericproposalwidget.cpp b/src/plugins/texteditor/codeassist/genericproposalwidget.cpp
index 22b7d158806c4b1e88aac865982a885918ad3dcb..52303c1dc0d0adc1e08a0bb9f57254a37a97d3b6 100644
--- a/src/plugins/texteditor/codeassist/genericproposalwidget.cpp
+++ b/src/plugins/texteditor/codeassist/genericproposalwidget.cpp
@@ -547,9 +547,7 @@ bool GenericProposalWidget::eventFilter(QObject *o, QEvent *e)
             if (fe->reason() == Qt::OtherFocusReason) {
                 // Qt/carbon workaround
                 // focus out is received before the key press event.
-                if (d->m_completionListView->currentIndex().isValid())
-                    emit proposalItemActivated(d->m_model->proposalItem(
-                                                   d->m_completionListView->currentIndex().row()));
+                activateCurrentProposalItem();
             }
         }
         if (d->m_infoFrame)
@@ -593,9 +591,7 @@ bool GenericProposalWidget::eventFilter(QObject *o, QEvent *e)
         case Qt::Key_Return:
             if (!useCarbonWorkaround()) {
                 abort();
-                if (d->m_completionListView->currentIndex().isValid())
-                    emit proposalItemActivated(d->m_model->proposalItem(
-                                                   d->m_completionListView->currentIndex().row()));
+                activateCurrentProposalItem();
             }
             return true;
 
@@ -658,6 +654,16 @@ bool GenericProposalWidget::eventFilter(QObject *o, QEvent *e)
     return false;
 }
 
+bool GenericProposalWidget::activateCurrentProposalItem()
+{
+    if (d->m_completionListView->currentIndex().isValid()) {
+        const int currentRow = d->m_completionListView->currentIndex().row();
+        emit proposalItemActivated(d->m_model->proposalItem(currentRow));
+        return true;
+    }
+    return false;
+}
+
 #include "genericproposalwidget.moc"
 
 } // TextEditor
diff --git a/src/plugins/texteditor/codeassist/genericproposalwidget.h b/src/plugins/texteditor/codeassist/genericproposalwidget.h
index cc68c0253eb293aeff25c5fcb4b9eb4ddc3673e5..f1f5f5a61ece95598db42ee8ef96a8d29b36226b 100644
--- a/src/plugins/texteditor/codeassist/genericproposalwidget.h
+++ b/src/plugins/texteditor/codeassist/genericproposalwidget.h
@@ -32,11 +32,13 @@
 
 #include "iassistproposalwidget.h"
 
+#include <texteditor/texteditor_global.h>
+
 namespace TextEditor {
 
 class GenericProposalWidgetPrivate;
 
-class GenericProposalWidget : public IAssistProposalWidget
+class TEXTEDITOR_EXPORT GenericProposalWidget : public IAssistProposalWidget
 {
     Q_OBJECT
     friend class GenericProposalWidgetPrivate;
@@ -69,6 +71,7 @@ private slots:
 
 protected:
     virtual bool eventFilter(QObject *o, QEvent *e);
+    bool activateCurrentProposalItem();
 
 private:
     GenericProposalWidgetPrivate *d;