CppEditor: refactor FollowSymbol

Create an interface to get the ability to use
another FollowSymbol implementation

Change-Id: I5802f62523ff3ee47b8a14e487adf43edcb6c9b1
Reviewed-by: Nikolai Kosjar <nikolai.kosjar@qt.io>
This commit is contained in:
Ivan Donchevskii
2017-08-03 16:43:38 +02:00
parent 76d12dc2d5
commit a137b08eaa
26 changed files with 442 additions and 164 deletions

View File

@@ -23,6 +23,7 @@ SOURCES += \
clangeditordocumentprocessor.cpp \ clangeditordocumentprocessor.cpp \
clangfixitoperation.cpp \ clangfixitoperation.cpp \
clangfixitoperationsextractor.cpp \ clangfixitoperationsextractor.cpp \
clangfollowsymbol.cpp \
clangfunctionhintmodel.cpp \ clangfunctionhintmodel.cpp \
clanghighlightingmarksreporter.cpp \ clanghighlightingmarksreporter.cpp \
clangmodelmanagersupport.cpp \ clangmodelmanagersupport.cpp \
@@ -54,6 +55,7 @@ HEADERS += \
clangeditordocumentprocessor.h \ clangeditordocumentprocessor.h \
clangfixitoperation.h \ clangfixitoperation.h \
clangfixitoperationsextractor.h \ clangfixitoperationsextractor.h \
clangfollowsymbol.h \
clangfunctionhintmodel.h \ clangfunctionhintmodel.h \
clanghighlightingmarksreporter.h \ clanghighlightingmarksreporter.h \
clangisdiagnosticrelatedtolocation.h \ clangisdiagnosticrelatedtolocation.h \

View File

@@ -71,6 +71,8 @@ QtcPlugin {
"clangfixitoperation.h", "clangfixitoperation.h",
"clangfixitoperationsextractor.cpp", "clangfixitoperationsextractor.cpp",
"clangfixitoperationsextractor.h", "clangfixitoperationsextractor.h",
"clangfollowsymbol.cpp",
"clangfollowsymbol.h",
"clangfunctionhintmodel.cpp", "clangfunctionhintmodel.cpp",
"clangfunctionhintmodel.h", "clangfunctionhintmodel.h",
"clanghighlightingmarksreporter.cpp", "clanghighlightingmarksreporter.cpp",

View File

@@ -0,0 +1,83 @@
/****************************************************************************
**
** Copyright (C) 2017 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** 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 The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#include "clangfollowsymbol.h"
#include "clangeditordocumentprocessor.h"
#include "texteditor/texteditor.h"
#include "texteditor/convenience.h"
namespace ClangCodeModel {
namespace Internal {
TextEditor::TextEditorWidget::Link ClangFollowSymbol::findLink(
const CppTools::CursorInEditor &data,
bool resolveTarget,
const CPlusPlus::Snapshot &,
const CPlusPlus::Document::Ptr &,
CppTools::SymbolFinder *,
bool)
{
Link link;
int lineNumber = 0, positionInBlock = 0;
QTextCursor cursor = TextEditor::Convenience::wordStartCursor(data.cursor());
TextEditor::Convenience::convertPosition(cursor.document(), cursor.position(), &lineNumber,
&positionInBlock);
const unsigned line = lineNumber;
const unsigned column = positionInBlock + 1;
if (!resolveTarget)
return link;
ClangEditorDocumentProcessor *processor = ClangEditorDocumentProcessor::get(
data.filePath().toString());
if (!processor)
return link;
QFuture<CppTools::SymbolInfo> info
= processor->requestFollowSymbol(static_cast<int>(line),
static_cast<int>(column),
resolveTarget);
if (info.isCanceled())
return link;
while (!info.isFinished()) {
if (info.isCanceled())
return link;
QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
}
CppTools::SymbolInfo result = info.result();
if (result.failedToFollow)
return link;
// We did not fail but the result is empty
if (result.fileName.isEmpty())
return link;
return Link(result.fileName, result.startLine, result.startColumn - 1);
}
} // namespace Internal
} // namespace ClangCodeModel

View File

@@ -0,0 +1,45 @@
/****************************************************************************
**
** Copyright (C) 2017 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** 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 The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#pragma once
#include <cpptools/followsymbolinterface.h>
namespace ClangCodeModel {
namespace Internal {
class ClangFollowSymbol : public CppTools::FollowSymbolInterface
{
public:
Link findLink(const CppTools::CursorInEditor &data,
bool resolveTarget,
const CPlusPlus::Snapshot &,
const CPlusPlus::Document::Ptr &,
CppTools::SymbolFinder *,
bool) override;
};
} // namespace Internal
} // namespace ClangCodeModel

View File

@@ -28,6 +28,7 @@
#include "clangconstants.h" #include "clangconstants.h"
#include "clangeditordocumentprocessor.h" #include "clangeditordocumentprocessor.h"
#include "clangutils.h" #include "clangutils.h"
#include "clangfollowsymbol.h"
#include <coreplugin/editormanager/editormanager.h> #include <coreplugin/editormanager/editormanager.h>
#include <cpptools/cppmodelmanager.h> #include <cpptools/cppmodelmanager.h>
@@ -52,6 +53,12 @@ using namespace ClangCodeModel::Internal;
static ModelManagerSupportClang *m_instance = 0; static ModelManagerSupportClang *m_instance = 0;
static bool useClangFollowSymbol()
{
static bool use = qEnvironmentVariableIntValue("QTC_CLANG_FOLLOW_SYMBOL");
return use;
}
static CppTools::CppModelManager *cppModelManager() static CppTools::CppModelManager *cppModelManager()
{ {
return CppTools::CppModelManager::instance(); return CppTools::CppModelManager::instance();
@@ -63,6 +70,9 @@ ModelManagerSupportClang::ModelManagerSupportClang()
QTC_CHECK(!m_instance); QTC_CHECK(!m_instance);
m_instance = this; m_instance = this;
if (useClangFollowSymbol())
m_followSymbol.reset(new ClangFollowSymbol);
Core::EditorManager *editorManager = Core::EditorManager::instance(); Core::EditorManager *editorManager = Core::EditorManager::instance();
connect(editorManager, &Core::EditorManager::editorOpened, connect(editorManager, &Core::EditorManager::editorOpened,
this, &ModelManagerSupportClang::onEditorOpened); this, &ModelManagerSupportClang::onEditorOpened);
@@ -96,6 +106,11 @@ CppTools::CppCompletionAssistProvider *ModelManagerSupportClang::completionAssis
return &m_completionAssistProvider; return &m_completionAssistProvider;
} }
CppTools::FollowSymbolInterface *ModelManagerSupportClang::followSymbolInterface()
{
return m_followSymbol.get();
}
CppTools::BaseEditorDocumentProcessor *ModelManagerSupportClang::editorDocumentProcessor( CppTools::BaseEditorDocumentProcessor *ModelManagerSupportClang::editorDocumentProcessor(
TextEditor::TextDocument *baseTextDocument) TextEditor::TextDocument *baseTextDocument)
{ {

View File

@@ -33,6 +33,8 @@
#include <QObject> #include <QObject>
#include <QScopedPointer> #include <QScopedPointer>
#include <memory>
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
class QMenu; class QMenu;
class QWidget; class QWidget;
@@ -44,6 +46,8 @@ namespace TextEditor { class TextEditorWidget; }
namespace ClangCodeModel { namespace ClangCodeModel {
namespace Internal { namespace Internal {
class ClangFollowSymbol;
class ModelManagerSupportClang: class ModelManagerSupportClang:
public QObject, public QObject,
public CppTools::ModelManagerSupport public CppTools::ModelManagerSupport
@@ -57,6 +61,7 @@ public:
CppTools::CppCompletionAssistProvider *completionAssistProvider() override; CppTools::CppCompletionAssistProvider *completionAssistProvider() override;
CppTools::BaseEditorDocumentProcessor *editorDocumentProcessor( CppTools::BaseEditorDocumentProcessor *editorDocumentProcessor(
TextEditor::TextDocument *baseTextDocument) override; TextEditor::TextDocument *baseTextDocument) override;
CppTools::FollowSymbolInterface *followSymbolInterface() override;
IpcCommunicator &ipcCommunicator(); IpcCommunicator &ipcCommunicator();
QString dummyUiHeaderOnDiskDirPath() const; QString dummyUiHeaderOnDiskDirPath() const;
@@ -100,6 +105,7 @@ private:
UiHeaderOnDiskManager m_uiHeaderOnDiskManager; UiHeaderOnDiskManager m_uiHeaderOnDiskManager;
IpcCommunicator m_ipcCommunicator; IpcCommunicator m_ipcCommunicator;
ClangCompletionAssistProvider m_completionAssistProvider; ClangCompletionAssistProvider m_completionAssistProvider;
std::unique_ptr<ClangFollowSymbol> m_followSymbol;
}; };
class ModelManagerSupportProviderClang : public CppTools::ModelManagerSupportProvider class ModelManagerSupportProviderClang : public CppTools::ModelManagerSupportProvider

View File

@@ -386,12 +386,6 @@ CppEditorDocument::cursorInfo(const CppTools::CursorInfoParams &params)
return processor()->cursorInfo(params); return processor()->cursorInfo(params);
} }
QFuture<CppTools::SymbolInfo>
CppEditorDocument::requestFollowSymbol(int line, int column, bool resolveTarget)
{
return processor()->requestFollowSymbol(line, column, resolveTarget);
}
const MinimizableInfoBars &CppEditorDocument::minimizableInfoBars() const const MinimizableInfoBars &CppEditorDocument::minimizableInfoBars() const
{ {
return m_minimizableInfoBars; return m_minimizableInfoBars;

View File

@@ -67,9 +67,6 @@ public:
ParseContextModel &parseContextModel(); ParseContextModel &parseContextModel();
QFuture<CppTools::CursorInfo> cursorInfo(const CppTools::CursorInfoParams &params); QFuture<CppTools::CursorInfo> cursorInfo(const CppTools::CursorInfoParams &params);
QFuture<CppTools::SymbolInfo> requestFollowSymbol(int line,
int column,
bool resolveTarget = true);
signals: signals:
void codeWarningsUpdated(unsigned contentsRevision, void codeWarningsUpdated(unsigned contentsRevision,

View File

@@ -93,6 +93,9 @@ private slots:
void test_FollowSymbolUnderCursor_data(); void test_FollowSymbolUnderCursor_data();
void test_FollowSymbolUnderCursor(); void test_FollowSymbolUnderCursor();
void test_FollowSymbolUnderCursor_QTCREATORBUG7903_data();
void test_FollowSymbolUnderCursor_QTCREATORBUG7903();
void test_FollowSymbolUnderCursor_followCall_data(); void test_FollowSymbolUnderCursor_followCall_data();
void test_FollowSymbolUnderCursor_followCall(); void test_FollowSymbolUnderCursor_followCall();

View File

@@ -129,8 +129,8 @@ public:
CppLocalRenaming m_localRenaming; CppLocalRenaming m_localRenaming;
CppUseSelectionsUpdater m_useSelectionsUpdater; CppUseSelectionsUpdater m_useSelectionsUpdater;
QScopedPointer<FollowSymbolUnderCursor> m_followSymbolUnderCursor;
CppSelectionChanger m_cppSelectionChanger; CppSelectionChanger m_cppSelectionChanger;
FollowSymbolUnderCursor m_builtinFollowSymbol;
CppRefactoringEngine m_builtinRefactoringEngine; CppRefactoringEngine m_builtinRefactoringEngine;
}; };
@@ -141,7 +141,6 @@ CppEditorWidgetPrivate::CppEditorWidgetPrivate(CppEditorWidget *q)
, m_declDefLinkFinder(new FunctionDeclDefLinkFinder(q)) , m_declDefLinkFinder(new FunctionDeclDefLinkFinder(q))
, m_localRenaming(q) , m_localRenaming(q)
, m_useSelectionsUpdater(q) , m_useSelectionsUpdater(q)
, m_followSymbolUnderCursor(new FollowSymbolUnderCursor(q))
, m_cppSelectionChanger() , m_cppSelectionChanger()
{} {}
@@ -647,12 +646,23 @@ CppEditorWidget::Link CppEditorWidget::findLinkAt(const QTextCursor &cursor,
if (!d->m_modelManager) if (!d->m_modelManager)
return Link(); return Link();
return d->m_followSymbolUnderCursor->findLink(cursor, const Utils::FileName &filePath = textDocument()->filePath();
resolveTarget, if (!resolveTarget) {
d->m_modelManager->snapshot(), // TODO: get that part also from clang
d->m_lastSemanticInfo.doc, return d->m_builtinFollowSymbol.findLink(CppTools::CursorInEditor{cursor, filePath, this},
d->m_modelManager->symbolFinder(), resolveTarget,
inNextSplit); d->m_modelManager->snapshot(),
d->m_lastSemanticInfo.doc,
d->m_modelManager->symbolFinder(),
inNextSplit);
}
return followSymbolInterface()->findLink(CppTools::CursorInEditor{cursor, filePath, this},
resolveTarget,
d->m_modelManager->snapshot(),
d->m_lastSemanticInfo.doc,
d->m_modelManager->symbolFinder(),
inNextSplit);
} }
unsigned CppEditorWidget::documentRevision() const unsigned CppEditorWidget::documentRevision() const
@@ -687,6 +697,14 @@ RefactoringEngineInterface *CppEditorWidget::refactoringEngine() const
: static_cast<RefactoringEngineInterface *>(&d->m_builtinRefactoringEngine); : static_cast<RefactoringEngineInterface *>(&d->m_builtinRefactoringEngine);
} }
CppTools::FollowSymbolInterface *CppEditorWidget::followSymbolInterface() const
{
CppTools::FollowSymbolInterface *followSymbol
= CppTools::CppModelManager::instance()->followSymbolInterface();
return followSymbol ? followSymbol
: static_cast<CppTools::FollowSymbolInterface *>(&d->m_builtinFollowSymbol);
}
bool CppEditorWidget::isSemanticInfoValidExceptLocalUses() const bool CppEditorWidget::isSemanticInfoValidExceptLocalUses() const
{ {
return d->m_lastSemanticInfo.doc && d->m_lastSemanticInfo.revision == documentRevision() return d->m_lastSemanticInfo.doc && d->m_lastSemanticInfo.revision == documentRevision()
@@ -973,11 +991,6 @@ void CppEditorWidget::applyDeclDefLinkChanges(bool jumpToMatch)
updateFunctionDeclDefLink(); updateFunctionDeclDefLink();
} }
FollowSymbolUnderCursor *CppEditorWidget::followSymbolUnderCursorDelegate()
{
return d->m_followSymbolUnderCursor.data();
}
void CppEditorWidget::encourageApply() void CppEditorWidget::encourageApply()
{ {
if (d->m_localRenaming.encourageApply()) if (d->m_localRenaming.encourageApply())

View File

@@ -32,6 +32,7 @@
namespace CppTools { namespace CppTools {
class CppEditorOutline; class CppEditorOutline;
class RefactoringEngineInterface; class RefactoringEngineInterface;
class FollowSymbolInterface;
class SemanticInfo; class SemanticInfo;
class ProjectPart; class ProjectPart;
} }
@@ -42,7 +43,6 @@ namespace Internal {
class CppEditorDocument; class CppEditorDocument;
class CppEditorWidgetPrivate; class CppEditorWidgetPrivate;
class FollowSymbolUnderCursor;
class FunctionDeclDefLink; class FunctionDeclDefLink;
class CppEditorWidget : public TextEditor::TextEditorWidget class CppEditorWidget : public TextEditor::TextEditorWidget
@@ -67,8 +67,6 @@ public:
TextEditor::AssistKind kind, TextEditor::AssistKind kind,
TextEditor::AssistReason reason) const override; TextEditor::AssistReason reason) const override;
FollowSymbolUnderCursor *followSymbolUnderCursorDelegate(); // exposed for tests
void encourageApply() override; void encourageApply() override;
void paste() override; void paste() override;
@@ -89,6 +87,8 @@ public:
static bool isWidgetHighlighted(QWidget *widget); static bool isWidgetHighlighted(QWidget *widget);
void updateSemanticInfo(); void updateSemanticInfo();
CppTools::FollowSymbolInterface *followSymbolInterface() const;
protected: protected:
bool event(QEvent *e) override; bool event(QEvent *e) override;
void contextMenuEvent(QContextMenuEvent *) override; void contextMenuEvent(QContextMenuEvent *) override;

View File

@@ -55,12 +55,6 @@ typedef TextEditorWidget::Link Link;
namespace { namespace {
static bool useClangFollowSymbol()
{
static bool use = qEnvironmentVariableIntValue("QTC_CLANG_FOLLOW_SYMBOL");
return use;
}
class VirtualFunctionHelper { class VirtualFunctionHelper {
public: public:
VirtualFunctionHelper(TypeOfExpression &typeOfExpression, VirtualFunctionHelper(TypeOfExpression &typeOfExpression,
@@ -305,9 +299,9 @@ inline LookupItem skipForwardDeclarations(const QList<LookupItem> &resolvedSymbo
return result; return result;
} }
CppEditorWidget::Link attemptFuncDeclDef(const QTextCursor &cursor, CppEditorWidget::Link attemptFuncDeclDef(const QTextCursor &cursor, Snapshot snapshot,
CppEditorWidget *, Snapshot snapshot, const Document::Ptr &document, const Document::Ptr &document,
SymbolFinder *symbolFinder) SymbolFinder *symbolFinder)
{ {
Link result; Link result;
QTC_ASSERT(document, return result); QTC_ASSERT(document, return result);
@@ -467,17 +461,11 @@ QString expressionUnderCursorAsString(const QTextCursor &textCursor,
} // anonymous namespace } // anonymous namespace
FollowSymbolUnderCursor::FollowSymbolUnderCursor(CppEditorWidget *widget) FollowSymbolUnderCursor::FollowSymbolUnderCursor()
: m_widget(widget) : m_virtualFunctionAssistProvider(new VirtualFunctionAssistProvider)
, m_virtualFunctionAssistProvider(new VirtualFunctionAssistProvider)
{ {
} }
FollowSymbolUnderCursor::~FollowSymbolUnderCursor()
{
delete m_virtualFunctionAssistProvider;
}
static int skipMatchingParentheses(const Tokens &tokens, int idx, int initialDepth) static int skipMatchingParentheses(const Tokens &tokens, int idx, int initialDepth)
{ {
int j = idx; int j = idx;
@@ -495,61 +483,28 @@ static int skipMatchingParentheses(const Tokens &tokens, int idx, int initialDep
return j; return j;
} }
bool FollowSymbolUnderCursor::processorFollowSymbol(uint line, uint column, bool resolveTarget, Link FollowSymbolUnderCursor::findLink(
Link &linkResult) const CppTools::CursorInEditor &data,
{ bool resolveTarget,
if (!useClangFollowSymbol()) const Snapshot &theSnapshot,
return false; const Document::Ptr &documentFromSemanticInfo,
CppEditorDocument* editorDocument = m_widget->cppEditorDocument(); SymbolFinder *symbolFinder,
if (!editorDocument) bool inNextSplit)
return false;
QFuture<CppTools::SymbolInfo> info
= editorDocument->requestFollowSymbol(static_cast<int>(line),
static_cast<int>(column),
resolveTarget);
if (info.isCanceled())
return false;
while (!info.isFinished()) {
if (info.isCanceled())
return false;
QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
}
CppTools::SymbolInfo result = info.result();
// Try again with built-in code model (happens with some includes)
if (result.failedToFollow)
return false;
// We did not fail but the result is empty
if (result.fileName.isEmpty())
return true;
linkResult = Link(result.fileName, result.startLine, result.startColumn - 1);
return true;
}
TextEditorWidget::Link FollowSymbolUnderCursor::findLink(const QTextCursor &cursor,
bool resolveTarget, const Snapshot &theSnapshot, const Document::Ptr &documentFromSemanticInfo,
SymbolFinder *symbolFinder, bool inNextSplit)
{ {
Link link; Link link;
int lineNumber = 0, positionInBlock = 0; int lineNumber = 0, positionInBlock = 0;
m_widget->convertPosition(cursor.position(), &lineNumber, &positionInBlock); QTextCursor cursor = data.cursor();
QTextDocument *document = cursor.document();
TextEditor::Convenience::convertPosition(document, cursor.position(), &lineNumber,
&positionInBlock);
const unsigned line = lineNumber; const unsigned line = lineNumber;
const unsigned column = positionInBlock + 1; const unsigned column = positionInBlock + 1;
if (resolveTarget && processorFollowSymbol(line, column, resolveTarget, link))
return link;
Snapshot snapshot = theSnapshot; Snapshot snapshot = theSnapshot;
// Move to end of identifier // Move to end of identifier
QTextCursor tc = cursor; QTextCursor tc = cursor;
QTextDocument *document = m_widget->document();
QChar ch = document->characterAt(tc.position()); QChar ch = document->characterAt(tc.position());
while (CppTools::isValidIdentifierChar(ch)) { while (CppTools::isValidIdentifierChar(ch)) {
tc.movePosition(QTextCursor::NextCharacter); tc.movePosition(QTextCursor::NextCharacter);
@@ -564,7 +519,7 @@ TextEditorWidget::Link FollowSymbolUnderCursor::findLink(const QTextCursor &curs
while (document->characterAt(pos).isSpace()) while (document->characterAt(pos).isSpace())
++pos; ++pos;
if (document->characterAt(pos) == QLatin1Char('(')) { if (document->characterAt(pos) == QLatin1Char('(')) {
link = attemptFuncDeclDef(cursor, m_widget, snapshot, documentFromSemanticInfo, link = attemptFuncDeclDef(cursor, snapshot, documentFromSemanticInfo,
symbolFinder); symbolFinder);
if (link.hasValidLinkText()) if (link.hasValidLinkText())
return link; return link;
@@ -636,7 +591,7 @@ TextEditorWidget::Link FollowSymbolUnderCursor::findLink(const QTextCursor &curs
&& unsigned(positionInBlock) <= tk.utf16charsEnd()) { && unsigned(positionInBlock) <= tk.utf16charsEnd()) {
cursorRegionReached = true; cursorRegionReached = true;
if (tk.is(T_OPERATOR)) { if (tk.is(T_OPERATOR)) {
link = attemptFuncDeclDef(cursor, m_widget, theSnapshot, link = attemptFuncDeclDef(cursor, theSnapshot,
documentFromSemanticInfo, symbolFinder); documentFromSemanticInfo, symbolFinder);
if (link.hasValidLinkText()) if (link.hasValidLinkText())
return link; return link;
@@ -644,7 +599,7 @@ TextEditorWidget::Link FollowSymbolUnderCursor::findLink(const QTextCursor &curs
QTextCursor c = cursor; QTextCursor c = cursor;
c.movePosition(QTextCursor::Left, QTextCursor::MoveAnchor, c.movePosition(QTextCursor::Left, QTextCursor::MoveAnchor,
positionInBlock - tokens.at(i - 1).utf16charsBegin()); positionInBlock - tokens.at(i - 1).utf16charsBegin());
link = attemptFuncDeclDef(c, m_widget, theSnapshot, documentFromSemanticInfo, link = attemptFuncDeclDef(c, theSnapshot, documentFromSemanticInfo,
symbolFinder); symbolFinder);
if (link.hasValidLinkText()) if (link.hasValidLinkText())
return link; return link;
@@ -655,8 +610,11 @@ TextEditorWidget::Link FollowSymbolUnderCursor::findLink(const QTextCursor &curs
} }
} }
CppEditorWidget *editorWidget = static_cast<CppEditorWidget *>(data.editorWidget());
if (!editorWidget)
return link;
// Now we prefer the doc from the snapshot with macros expanded. // Now we prefer the doc from the snapshot with macros expanded.
Document::Ptr doc = snapshot.document(m_widget->textDocument()->filePath()); Document::Ptr doc = snapshot.document(editorWidget->textDocument()->filePath());
if (!doc) { if (!doc) {
doc = documentFromSemanticInfo; doc = documentFromSemanticInfo;
if (!doc) if (!doc)
@@ -705,7 +663,7 @@ TextEditorWidget::Link FollowSymbolUnderCursor::findLink(const QTextCursor &curs
} else if (const Document::MacroUse *use = doc->findMacroUseAt(endOfToken - 1)) { } else if (const Document::MacroUse *use = doc->findMacroUseAt(endOfToken - 1)) {
const QString fileName = use->macro().fileName(); const QString fileName = use->macro().fileName();
if (fileName == CppModelManager::editorConfigurationFileName()) { if (fileName == CppModelManager::editorConfigurationFileName()) {
m_widget->showPreProcessorWidget(); editorWidget->showPreProcessorWidget();
} else if (fileName != CppModelManager::configurationFileName()) { } else if (fileName != CppModelManager::configurationFileName()) {
const Macro &macro = use->macro(); const Macro &macro = use->macro();
link.targetFileName = macro.fileName(); link.targetFileName = macro.fileName();
@@ -739,7 +697,7 @@ TextEditorWidget::Link FollowSymbolUnderCursor::findLink(const QTextCursor &curs
if (Symbol *d = r.declaration()) { if (Symbol *d = r.declaration()) {
if (d->isDeclaration() || d->isFunction()) { if (d->isDeclaration() || d->isFunction()) {
const QString fileName = QString::fromUtf8(d->fileName(), d->fileNameLength()); const QString fileName = QString::fromUtf8(d->fileName(), d->fileNameLength());
if (m_widget->textDocument()->filePath().toString() == fileName) { if (editorWidget->textDocument()->filePath().toString() == fileName) {
if (unsigned(lineNumber) == d->line() if (unsigned(lineNumber) == d->line()
&& unsigned(positionInBlock) >= d->column()) { // TODO: check the end && unsigned(positionInBlock) >= d->column()) { // TODO: check the end
result = r; // take the symbol under cursor. result = r; // take the symbol under cursor.
@@ -748,7 +706,7 @@ TextEditorWidget::Link FollowSymbolUnderCursor::findLink(const QTextCursor &curs
} }
} else if (d->isUsingDeclaration()) { } else if (d->isUsingDeclaration()) {
int tokenBeginLineNumber = 0, tokenBeginColumnNumber = 0; int tokenBeginLineNumber = 0, tokenBeginColumnNumber = 0;
m_widget->convertPosition(beginOfToken, &tokenBeginLineNumber, editorWidget->convertPosition(beginOfToken, &tokenBeginLineNumber,
&tokenBeginColumnNumber); &tokenBeginColumnNumber);
if (unsigned(tokenBeginLineNumber) > d->line() if (unsigned(tokenBeginLineNumber) > d->line()
|| (unsigned(tokenBeginLineNumber) == d->line() || (unsigned(tokenBeginLineNumber) == d->line()
@@ -778,7 +736,7 @@ TextEditorWidget::Link FollowSymbolUnderCursor::findLink(const QTextCursor &curs
params.openInNextSplit = inNextSplit; params.openInNextSplit = inNextSplit;
if (m_virtualFunctionAssistProvider->configure(params)) { if (m_virtualFunctionAssistProvider->configure(params)) {
m_widget->invokeAssist(FollowSymbol, m_virtualFunctionAssistProvider); editorWidget->invokeAssist(FollowSymbol, m_virtualFunctionAssistProvider.data());
m_virtualFunctionAssistProvider->clearParams(); m_virtualFunctionAssistProvider->clearParams();
} }
@@ -827,12 +785,13 @@ TextEditorWidget::Link FollowSymbolUnderCursor::findLink(const QTextCursor &curs
return Link(); return Link();
} }
VirtualFunctionAssistProvider *FollowSymbolUnderCursor::virtualFunctionAssistProvider() QSharedPointer<VirtualFunctionAssistProvider> FollowSymbolUnderCursor::virtualFunctionAssistProvider()
{ {
return m_virtualFunctionAssistProvider; return m_virtualFunctionAssistProvider;
} }
void FollowSymbolUnderCursor::setVirtualFunctionAssistProvider(VirtualFunctionAssistProvider *provider) void FollowSymbolUnderCursor::setVirtualFunctionAssistProvider(
const QSharedPointer<VirtualFunctionAssistProvider> &provider)
{ {
m_virtualFunctionAssistProvider = provider; m_virtualFunctionAssistProvider = provider;
} }

View File

@@ -25,44 +25,31 @@
#pragma once #pragma once
#include <cplusplus/CppDocument.h> #include <cpptools/followsymbolinterface.h>
#include <texteditor/texteditor.h>
QT_BEGIN_NAMESPACE
class QTextCursor;
QT_END_NAMESPACE
namespace CppTools { class SymbolFinder; }
namespace CppEditor { namespace CppEditor {
namespace Internal { namespace Internal {
class CppEditorWidget;
class VirtualFunctionAssistProvider; class VirtualFunctionAssistProvider;
class FollowSymbolUnderCursor class FollowSymbolUnderCursor : public CppTools::FollowSymbolInterface
{ {
public: public:
typedef TextEditor::TextEditorWidget::Link Link; FollowSymbolUnderCursor();
FollowSymbolUnderCursor(CppEditorWidget *widget); Link findLink(const CppTools::CursorInEditor &data,
~FollowSymbolUnderCursor(); bool resolveTarget,
Link findLink(const QTextCursor &cursor, bool resolveTarget,
const CPlusPlus::Snapshot &snapshot, const CPlusPlus::Snapshot &snapshot,
const CPlusPlus::Document::Ptr &documentFromSemanticInfo, const CPlusPlus::Document::Ptr &documentFromSemanticInfo,
CppTools::SymbolFinder *symbolFinder, bool inNextSplit); CppTools::SymbolFinder *symbolFinder,
bool inNextSplit) override;
VirtualFunctionAssistProvider *virtualFunctionAssistProvider(); QSharedPointer<VirtualFunctionAssistProvider> virtualFunctionAssistProvider();
void setVirtualFunctionAssistProvider(VirtualFunctionAssistProvider *provider); void setVirtualFunctionAssistProvider(
const QSharedPointer<VirtualFunctionAssistProvider> &provider);
private: private:
// Try to follow symbol with clang processor QSharedPointer<VirtualFunctionAssistProvider> m_virtualFunctionAssistProvider;
// Returns false if it has failed and we want to try again with built-in one
bool processorFollowSymbol(uint line, uint column, bool resolveTarget,
Link &result);
CppEditorWidget *m_widget;
VirtualFunctionAssistProvider *m_virtualFunctionAssistProvider;
}; };
} // namespace Internal } // namespace Internal

View File

@@ -33,6 +33,7 @@
#include "cppvirtualfunctionproposalitem.h" #include "cppvirtualfunctionproposalitem.h"
#include <cpptools/cpptoolstestcase.h> #include <cpptools/cpptoolstestcase.h>
#include <cpptools/cppmodelmanager.h>
#include <texteditor/codeassist/genericproposalmodel.h> #include <texteditor/codeassist/genericproposalmodel.h>
#include <texteditor/codeassist/iassistprocessor.h> #include <texteditor/codeassist/iassistprocessor.h>
@@ -326,19 +327,37 @@ F2TestCase::F2TestCase(CppEditorAction action,
switch (action) { switch (action) {
case FollowSymbolUnderCursorAction: { case FollowSymbolUnderCursorAction: {
CppEditorWidget *widget = initialTestFile->m_editorWidget; CppEditorWidget *widget = initialTestFile->m_editorWidget;
FollowSymbolUnderCursor *delegate = widget->followSymbolUnderCursorDelegate(); FollowSymbolInterface* delegate = widget->followSymbolInterface();
VirtualFunctionAssistProvider *original = delegate->virtualFunctionAssistProvider(); if (!delegate)
QFAIL("No follow symbol interface");
auto* builtinFollowSymbol = dynamic_cast<FollowSymbolUnderCursor *>(delegate);
if (!builtinFollowSymbol) {
if (filePaths.size() > 1)
QSKIP("Clang FollowSymbol does not currently support multiple files (except cpp+header)");
const QString curTestName = QLatin1String(QTest::currentTestFunction());
if (curTestName == "test_FollowSymbolUnderCursor_QObject_connect"
|| curTestName == "test_FollowSymbolUnderCursor_virtualFunctionCall"
|| curTestName == "test_FollowSymbolUnderCursor_QTCREATORBUG7903") {
QSKIP((curTestName + " is not supported by Clang FollowSymbol").toLatin1());
}
initialTestFile->m_editorWidget->openLinkUnderCursor();
break;
}
QSharedPointer<VirtualFunctionAssistProvider> original
= builtinFollowSymbol->virtualFunctionAssistProvider();
// Set test provider, run and get results // Set test provider, run and get results
QScopedPointer<VirtualFunctionTestAssistProvider> testProvider( QSharedPointer<VirtualFunctionTestAssistProvider> testProvider(
new VirtualFunctionTestAssistProvider(widget)); new VirtualFunctionTestAssistProvider(widget));
delegate->setVirtualFunctionAssistProvider(testProvider.data()); builtinFollowSymbol->setVirtualFunctionAssistProvider(testProvider);
initialTestFile->m_editorWidget->openLinkUnderCursor(); initialTestFile->m_editorWidget->openLinkUnderCursor();
immediateVirtualSymbolResults = testProvider->m_immediateItems; immediateVirtualSymbolResults = testProvider->m_immediateItems;
finalVirtualSymbolResults = testProvider->m_finalItems; finalVirtualSymbolResults = testProvider->m_finalItems;
// Restore original test provider // Restore original test provider
delegate->setVirtualFunctionAssistProvider(original); builtinFollowSymbol->setVirtualFunctionAssistProvider(original);
break; break;
} }
case SwitchBetweenMethodDeclarationDefinitionAction: case SwitchBetweenMethodDeclarationDefinitionAction:
@@ -870,41 +889,6 @@ void CppEditorPlugin::test_FollowSymbolUnderCursor_data()
"@Container<int> container;\n" "@Container<int> container;\n"
); );
QTest::newRow("using_QTCREATORBUG7903_globalNamespace") << _(
"namespace NS {\n"
"class Foo {};\n"
"}\n"
"using NS::$Foo;\n"
"void fun()\n"
"{\n"
" @Foo foo;\n"
"}\n"
);
QTest::newRow("using_QTCREATORBUG7903_namespace") << _(
"namespace NS {\n"
"class Foo {};\n"
"}\n"
"namespace NS1 {\n"
"void fun()\n"
"{\n"
" using NS::$Foo;\n"
" @Foo foo;\n"
"}\n"
"}\n"
);
QTest::newRow("using_QTCREATORBUG7903_insideFunction") << _(
"namespace NS {\n"
"class Foo {};\n"
"}\n"
"void fun()\n"
"{\n"
" using NS::$Foo;\n"
" @Foo foo;\n"
"}\n"
);
QTest::newRow("matchFunctionSignature_Follow_1") << _( QTest::newRow("matchFunctionSignature_Follow_1") << _(
"class Foo {\n" "class Foo {\n"
" void @foo(int);\n" " void @foo(int);\n"
@@ -993,6 +977,8 @@ void CppEditorPlugin::test_FollowSymbolUnderCursor_data()
); );
QTest::newRow("template_alias") << _( QTest::newRow("template_alias") << _(
"template<class T>"
"class Bar;"
"template<class $T>\n" "template<class $T>\n"
"using Foo = Bar<@T>;\n" "using Foo = Bar<@T>;\n"
); );
@@ -1004,6 +990,51 @@ void CppEditorPlugin::test_FollowSymbolUnderCursor()
F2TestCase(F2TestCase::FollowSymbolUnderCursorAction, singleDocument(source)); F2TestCase(F2TestCase::FollowSymbolUnderCursorAction, singleDocument(source));
} }
void CppEditorPlugin::test_FollowSymbolUnderCursor_QTCREATORBUG7903_data()
{
QTest::addColumn<QByteArray>("source");
QTest::newRow("using_QTCREATORBUG7903_globalNamespace") << _(
"namespace NS {\n"
"class Foo {};\n"
"}\n"
"using NS::$Foo;\n"
"void fun()\n"
"{\n"
" @Foo foo;\n"
"}\n"
);
QTest::newRow("using_QTCREATORBUG7903_namespace") << _(
"namespace NS {\n"
"class Foo {};\n"
"}\n"
"namespace NS1 {\n"
"void fun()\n"
"{\n"
" using NS::$Foo;\n"
" @Foo foo;\n"
"}\n"
"}\n"
);
QTest::newRow("using_QTCREATORBUG7903_insideFunction") << _(
"namespace NS {\n"
"class Foo {};\n"
"}\n"
"void fun()\n"
"{\n"
" using NS::$Foo;\n"
" @Foo foo;\n"
"}\n"
);
}
void CppEditorPlugin::test_FollowSymbolUnderCursor_QTCREATORBUG7903()
{
QFETCH(QByteArray, source);
F2TestCase(F2TestCase::FollowSymbolUnderCursorAction, singleDocument(source));
}
void CppEditorPlugin::test_FollowSymbolUnderCursor_followCall_data() void CppEditorPlugin::test_FollowSymbolUnderCursor_followCall_data()
{ {
QTest::addColumn<QByteArray>("variableDeclaration"); // without semicolon, can be "" QTest::addColumn<QByteArray>("variableDeclaration"); // without semicolon, can be ""

View File

@@ -39,6 +39,7 @@
#include "cpptoolsreuse.h" #include "cpptoolsreuse.h"
#include "editordocumenthandle.h" #include "editordocumenthandle.h"
#include "symbolfinder.h" #include "symbolfinder.h"
#include "followsymbolinterface.h"
#include <coreplugin/documentmanager.h> #include <coreplugin/documentmanager.h>
#include <coreplugin/icore.h> #include <coreplugin/icore.h>
@@ -274,6 +275,11 @@ RefactoringEngineInterface *CppModelManager::refactoringEngine()
return instance()->d->m_refactoringEngine; return instance()->d->m_refactoringEngine;
} }
FollowSymbolInterface *CppModelManager::followSymbolInterface() const
{
return d->m_activeModelManagerSupport->followSymbolInterface();
}
QString CppModelManager::configurationFileName() QString CppModelManager::configurationFileName()
{ {
return Preprocessor::configurationFileName(); return Preprocessor::configurationFileName();

View File

@@ -54,6 +54,7 @@ class CppEditorDocumentHandle;
class CppIndexingSupport; class CppIndexingSupport;
class ModelManagerSupportProvider; class ModelManagerSupportProvider;
class RefactoringEngineInterface; class RefactoringEngineInterface;
class FollowSymbolInterface;
class SymbolFinder; class SymbolFinder;
class WorkingCopy; class WorkingCopy;
@@ -152,6 +153,7 @@ public:
CppCompletionAssistProvider *completionAssistProvider() const; CppCompletionAssistProvider *completionAssistProvider() const;
BaseEditorDocumentProcessor *editorDocumentProcessor( BaseEditorDocumentProcessor *editorDocumentProcessor(
TextEditor::TextDocument *baseTextDocument) const; TextEditor::TextDocument *baseTextDocument) const;
FollowSymbolInterface *followSymbolInterface() const;
void setIndexingSupport(CppIndexingSupport *indexingSupport); void setIndexingSupport(CppIndexingSupport *indexingSupport);
CppIndexingSupport *indexingSupport(); CppIndexingSupport *indexingSupport();

View File

@@ -36,6 +36,7 @@ namespace CppTools {
class BaseEditorDocumentProcessor; class BaseEditorDocumentProcessor;
class CppCompletionAssistProvider; class CppCompletionAssistProvider;
class FollowSymbolInterface;
class CPPTOOLS_EXPORT ModelManagerSupport class CPPTOOLS_EXPORT ModelManagerSupport
{ {
@@ -48,6 +49,7 @@ public:
virtual CppCompletionAssistProvider *completionAssistProvider() = 0; virtual CppCompletionAssistProvider *completionAssistProvider() = 0;
virtual BaseEditorDocumentProcessor *editorDocumentProcessor( virtual BaseEditorDocumentProcessor *editorDocumentProcessor(
TextEditor::TextDocument *baseTextDocument) = 0; TextEditor::TextDocument *baseTextDocument) = 0;
virtual FollowSymbolInterface *followSymbolInterface() = 0;
}; };
class CPPTOOLS_EXPORT ModelManagerSupportProvider class CPPTOOLS_EXPORT ModelManagerSupportProvider

View File

@@ -69,3 +69,8 @@ CppCompletionAssistProvider *ModelManagerSupportInternal::completionAssistProvid
{ {
return m_completionAssistProvider.data(); return m_completionAssistProvider.data();
} }
FollowSymbolInterface *ModelManagerSupportInternal::followSymbolInterface()
{
return nullptr;
}

View File

@@ -43,6 +43,7 @@ public:
virtual CppCompletionAssistProvider *completionAssistProvider(); virtual CppCompletionAssistProvider *completionAssistProvider();
virtual BaseEditorDocumentProcessor *editorDocumentProcessor( virtual BaseEditorDocumentProcessor *editorDocumentProcessor(
TextEditor::TextDocument *baseTextDocument); TextEditor::TextDocument *baseTextDocument);
FollowSymbolInterface *followSymbolInterface() override;
private: private:
QScopedPointer<CppCompletionAssistProvider> m_completionAssistProvider; QScopedPointer<CppCompletionAssistProvider> m_completionAssistProvider;

View File

@@ -63,6 +63,7 @@ HEADERS += \
cppworkingcopy.h \ cppworkingcopy.h \
doxygengenerator.h \ doxygengenerator.h \
editordocumenthandle.h \ editordocumenthandle.h \
followsymbolinterface.h \
functionutils.h \ functionutils.h \
generatedcodemodelsupport.h \ generatedcodemodelsupport.h \
includeutils.h \ includeutils.h \

View File

@@ -164,6 +164,7 @@ Project {
"doxygengenerator.h", "doxygengenerator.h",
"editordocumenthandle.cpp", "editordocumenthandle.cpp",
"editordocumenthandle.h", "editordocumenthandle.h",
"followsymbolinterface.h",
"functionutils.cpp", "functionutils.cpp",
"functionutils.h", "functionutils.h",
"generatedcodemodelsupport.cpp", "generatedcodemodelsupport.cpp",

View File

@@ -0,0 +1,53 @@
/****************************************************************************
**
** Copyright (C) 2017 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** 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 The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#pragma once
#include "cpptools_global.h"
#include "cursorineditor.h"
#include <cplusplus/CppDocument.h>
#include <texteditor/texteditor.h>
namespace CppTools {
class SymbolFinder;
class CPPTOOLS_EXPORT FollowSymbolInterface
{
public:
using Link = TextEditor::TextEditorWidget::Link;
virtual ~FollowSymbolInterface() {}
virtual Link findLink(const CursorInEditor &data,
bool resolveTarget,
const CPlusPlus::Snapshot &snapshot,
const CPlusPlus::Document::Ptr &documentFromSemanticInfo,
SymbolFinder *symbolFinder,
bool inNextSplit) = 0;
};
} // namespace CppTools

View File

@@ -95,6 +95,13 @@ static bool isValidIdentifierChar(const QChar &c)
|| c.isLowSurrogate(); || c.isLowSurrogate();
} }
static bool isAfterOperatorKeyword(QTextCursor cursor)
{
cursor.movePosition(QTextCursor::PreviousWord);
cursor.movePosition(QTextCursor::EndOfWord, QTextCursor::KeepAnchor);
return cursor.selectedText() == "operator";
}
QTextCursor wordStartCursor(const QTextCursor &textCursor) QTextCursor wordStartCursor(const QTextCursor &textCursor)
{ {
const int originalPosition = textCursor.position(); const int originalPosition = textCursor.position();
@@ -108,6 +115,8 @@ QTextCursor wordStartCursor(const QTextCursor &textCursor)
if (isValidIdentifierChar(c)) if (isValidIdentifierChar(c))
cursor.movePosition(QTextCursor::PreviousWord); cursor.movePosition(QTextCursor::PreviousWord);
} }
if (isAfterOperatorKeyword(cursor))
cursor.movePosition(QTextCursor::PreviousWord);
return cursor; return cursor;
} }

View File

@@ -90,6 +90,20 @@ private:
} // anonymous namespace } // anonymous namespace
static SourceRange getOperatorRange(const CXTranslationUnit tu,
const Tokens &tokens,
uint operatorIndex)
{
const CXSourceLocation start = clang_getTokenLocation(tu, tokens.data[operatorIndex]);
operatorIndex += 2;
while (operatorIndex < tokens.tokenCount
&& !(ClangString(clang_getTokenSpelling(tu, tokens.data[operatorIndex])) == "(")) {
++operatorIndex;
}
const CXSourceLocation end = clang_getTokenLocation(tu, tokens.data[operatorIndex]);
return SourceRange(clang_getRange(start, end));
}
static SourceRangeContainer extractMatchingTokenRange(const Cursor &cursor, static SourceRangeContainer extractMatchingTokenRange(const Cursor &cursor,
const Utf8String &tokenStr) const Utf8String &tokenStr)
{ {
@@ -99,10 +113,14 @@ static SourceRangeContainer extractMatchingTokenRange(const Cursor &cursor,
if (!(tokenStr == ClangString(clang_getTokenSpelling(tu, tokens.data[i])))) if (!(tokenStr == ClangString(clang_getTokenSpelling(tu, tokens.data[i]))))
continue; continue;
if (cursor.isFunctionLike() if (cursor.isFunctionLike() || cursor.isConstructorOrDestructor()) {
&& (i+1 > tokens.tokenCount if (tokenStr == "operator")
|| !(ClangString(clang_getTokenSpelling(tu, tokens.data[i+1])) == "("))) { return getOperatorRange(tu, tokens, i);
continue;
if (i+1 > tokens.tokenCount
|| !(ClangString(clang_getTokenSpelling(tu, tokens.data[i+1])) == "(")) {
continue;
}
} }
return SourceRange(clang_getTokenExtent(tu, tokens.data[i])); return SourceRange(clang_getTokenExtent(tu, tokens.data[i]));
} }

View File

@@ -351,6 +351,34 @@ TEST_F(FollowSymbol, CursorAfterNamespace)
ASSERT_THAT(namespaceDefinition, MatchesFileSourceRange(QString(""), 0, 0, 0)); ASSERT_THAT(namespaceDefinition, MatchesFileSourceRange(QString(""), 0, 0, 0));
} }
TEST_F(FollowSymbol, CursorOnOneSymbolOperatorDefinition)
{
const auto namespaceDefinition = followSymbol(76, 13);
ASSERT_THAT(namespaceDefinition, MatchesSourceRange(72, 9, 9));
}
TEST_F(FollowSymbol, CursorOnTwoSymbolOperatorDefinition)
{
const auto namespaceDefinition = followSymbol(80, 15);
ASSERT_THAT(namespaceDefinition, MatchesSourceRange(73, 10, 10));
}
TEST_F(FollowSymbol, CursorOnOneSymbolOperatorDeclaration)
{
const auto namespaceDefinition = followSymbol(72, 12);
ASSERT_THAT(namespaceDefinition, MatchesSourceRange(76, 10, 9));
}
TEST_F(FollowSymbol, CursorOnTwoSymbolOperatorDeclaration)
{
const auto namespaceDefinition = followSymbol(73, 12);
ASSERT_THAT(namespaceDefinition, MatchesSourceRange(80, 11, 10));
}
std::unique_ptr<Data> FollowSymbol::d; std::unique_ptr<Data> FollowSymbol::d;
void FollowSymbol::SetUpTestCase() void FollowSymbol::SetUpTestCase()

View File

@@ -65,3 +65,18 @@ FooClass::FooClass() {
int main() { int main() {
return foo() + FooClass::mememember + TEST_DEFINE; return foo() + FooClass::mememember + TEST_DEFINE;
} }
class Bar
{
public:
int operator&();
Bar& operator[](int);
};
int Bar::operator&() {
return 0;
}
Bar& Bar::operator[](int) {
return *this;
}