diff --git a/src/plugins/clangcodemodel/clangcodemodel.pro b/src/plugins/clangcodemodel/clangcodemodel.pro index 4b44cce6c6f..d48ce960ded 100644 --- a/src/plugins/clangcodemodel/clangcodemodel.pro +++ b/src/plugins/clangcodemodel/clangcodemodel.pro @@ -23,6 +23,7 @@ SOURCES += \ clangeditordocumentprocessor.cpp \ clangfixitoperation.cpp \ clangfixitoperationsextractor.cpp \ + clangfollowsymbol.cpp \ clangfunctionhintmodel.cpp \ clanghighlightingmarksreporter.cpp \ clangmodelmanagersupport.cpp \ @@ -54,6 +55,7 @@ HEADERS += \ clangeditordocumentprocessor.h \ clangfixitoperation.h \ clangfixitoperationsextractor.h \ + clangfollowsymbol.h \ clangfunctionhintmodel.h \ clanghighlightingmarksreporter.h \ clangisdiagnosticrelatedtolocation.h \ diff --git a/src/plugins/clangcodemodel/clangcodemodel.qbs b/src/plugins/clangcodemodel/clangcodemodel.qbs index 4c26692c86e..699e5730329 100644 --- a/src/plugins/clangcodemodel/clangcodemodel.qbs +++ b/src/plugins/clangcodemodel/clangcodemodel.qbs @@ -71,6 +71,8 @@ QtcPlugin { "clangfixitoperation.h", "clangfixitoperationsextractor.cpp", "clangfixitoperationsextractor.h", + "clangfollowsymbol.cpp", + "clangfollowsymbol.h", "clangfunctionhintmodel.cpp", "clangfunctionhintmodel.h", "clanghighlightingmarksreporter.cpp", diff --git a/src/plugins/clangcodemodel/clangfollowsymbol.cpp b/src/plugins/clangcodemodel/clangfollowsymbol.cpp new file mode 100644 index 00000000000..ede9cac8245 --- /dev/null +++ b/src/plugins/clangcodemodel/clangfollowsymbol.cpp @@ -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 info + = processor->requestFollowSymbol(static_cast(line), + static_cast(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 diff --git a/src/plugins/clangcodemodel/clangfollowsymbol.h b/src/plugins/clangcodemodel/clangfollowsymbol.h new file mode 100644 index 00000000000..5dcf5d389dd --- /dev/null +++ b/src/plugins/clangcodemodel/clangfollowsymbol.h @@ -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 + +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 diff --git a/src/plugins/clangcodemodel/clangmodelmanagersupport.cpp b/src/plugins/clangcodemodel/clangmodelmanagersupport.cpp index d14fb64e6fb..547f27276bc 100644 --- a/src/plugins/clangcodemodel/clangmodelmanagersupport.cpp +++ b/src/plugins/clangcodemodel/clangmodelmanagersupport.cpp @@ -28,6 +28,7 @@ #include "clangconstants.h" #include "clangeditordocumentprocessor.h" #include "clangutils.h" +#include "clangfollowsymbol.h" #include #include @@ -52,6 +53,12 @@ using namespace ClangCodeModel::Internal; static ModelManagerSupportClang *m_instance = 0; +static bool useClangFollowSymbol() +{ + static bool use = qEnvironmentVariableIntValue("QTC_CLANG_FOLLOW_SYMBOL"); + return use; +} + static CppTools::CppModelManager *cppModelManager() { return CppTools::CppModelManager::instance(); @@ -63,6 +70,9 @@ ModelManagerSupportClang::ModelManagerSupportClang() QTC_CHECK(!m_instance); m_instance = this; + if (useClangFollowSymbol()) + m_followSymbol.reset(new ClangFollowSymbol); + Core::EditorManager *editorManager = Core::EditorManager::instance(); connect(editorManager, &Core::EditorManager::editorOpened, this, &ModelManagerSupportClang::onEditorOpened); @@ -96,6 +106,11 @@ CppTools::CppCompletionAssistProvider *ModelManagerSupportClang::completionAssis return &m_completionAssistProvider; } +CppTools::FollowSymbolInterface *ModelManagerSupportClang::followSymbolInterface() +{ + return m_followSymbol.get(); +} + CppTools::BaseEditorDocumentProcessor *ModelManagerSupportClang::editorDocumentProcessor( TextEditor::TextDocument *baseTextDocument) { diff --git a/src/plugins/clangcodemodel/clangmodelmanagersupport.h b/src/plugins/clangcodemodel/clangmodelmanagersupport.h index d315bbb359e..8728704e1c6 100644 --- a/src/plugins/clangcodemodel/clangmodelmanagersupport.h +++ b/src/plugins/clangcodemodel/clangmodelmanagersupport.h @@ -33,6 +33,8 @@ #include #include +#include + QT_BEGIN_NAMESPACE class QMenu; class QWidget; @@ -44,6 +46,8 @@ namespace TextEditor { class TextEditorWidget; } namespace ClangCodeModel { namespace Internal { +class ClangFollowSymbol; + class ModelManagerSupportClang: public QObject, public CppTools::ModelManagerSupport @@ -57,6 +61,7 @@ public: CppTools::CppCompletionAssistProvider *completionAssistProvider() override; CppTools::BaseEditorDocumentProcessor *editorDocumentProcessor( TextEditor::TextDocument *baseTextDocument) override; + CppTools::FollowSymbolInterface *followSymbolInterface() override; IpcCommunicator &ipcCommunicator(); QString dummyUiHeaderOnDiskDirPath() const; @@ -100,6 +105,7 @@ private: UiHeaderOnDiskManager m_uiHeaderOnDiskManager; IpcCommunicator m_ipcCommunicator; ClangCompletionAssistProvider m_completionAssistProvider; + std::unique_ptr m_followSymbol; }; class ModelManagerSupportProviderClang : public CppTools::ModelManagerSupportProvider diff --git a/src/plugins/cppeditor/cppeditordocument.cpp b/src/plugins/cppeditor/cppeditordocument.cpp index cfc7d33f38d..d31a4eb899c 100644 --- a/src/plugins/cppeditor/cppeditordocument.cpp +++ b/src/plugins/cppeditor/cppeditordocument.cpp @@ -386,12 +386,6 @@ CppEditorDocument::cursorInfo(const CppTools::CursorInfoParams ¶ms) return processor()->cursorInfo(params); } -QFuture -CppEditorDocument::requestFollowSymbol(int line, int column, bool resolveTarget) -{ - return processor()->requestFollowSymbol(line, column, resolveTarget); -} - const MinimizableInfoBars &CppEditorDocument::minimizableInfoBars() const { return m_minimizableInfoBars; diff --git a/src/plugins/cppeditor/cppeditordocument.h b/src/plugins/cppeditor/cppeditordocument.h index 671141b00ad..e2e8a0a28e2 100644 --- a/src/plugins/cppeditor/cppeditordocument.h +++ b/src/plugins/cppeditor/cppeditordocument.h @@ -67,9 +67,6 @@ public: ParseContextModel &parseContextModel(); QFuture cursorInfo(const CppTools::CursorInfoParams ¶ms); - QFuture requestFollowSymbol(int line, - int column, - bool resolveTarget = true); signals: void codeWarningsUpdated(unsigned contentsRevision, diff --git a/src/plugins/cppeditor/cppeditorplugin.h b/src/plugins/cppeditor/cppeditorplugin.h index a6653942879..aa2f06fad76 100644 --- a/src/plugins/cppeditor/cppeditorplugin.h +++ b/src/plugins/cppeditor/cppeditorplugin.h @@ -93,6 +93,9 @@ private slots: void test_FollowSymbolUnderCursor_data(); void test_FollowSymbolUnderCursor(); + void test_FollowSymbolUnderCursor_QTCREATORBUG7903_data(); + void test_FollowSymbolUnderCursor_QTCREATORBUG7903(); + void test_FollowSymbolUnderCursor_followCall_data(); void test_FollowSymbolUnderCursor_followCall(); diff --git a/src/plugins/cppeditor/cppeditorwidget.cpp b/src/plugins/cppeditor/cppeditorwidget.cpp index 2fad8ecd601..fb25a9edd7b 100644 --- a/src/plugins/cppeditor/cppeditorwidget.cpp +++ b/src/plugins/cppeditor/cppeditorwidget.cpp @@ -129,8 +129,8 @@ public: CppLocalRenaming m_localRenaming; CppUseSelectionsUpdater m_useSelectionsUpdater; - QScopedPointer m_followSymbolUnderCursor; CppSelectionChanger m_cppSelectionChanger; + FollowSymbolUnderCursor m_builtinFollowSymbol; CppRefactoringEngine m_builtinRefactoringEngine; }; @@ -141,7 +141,6 @@ CppEditorWidgetPrivate::CppEditorWidgetPrivate(CppEditorWidget *q) , m_declDefLinkFinder(new FunctionDeclDefLinkFinder(q)) , m_localRenaming(q) , m_useSelectionsUpdater(q) - , m_followSymbolUnderCursor(new FollowSymbolUnderCursor(q)) , m_cppSelectionChanger() {} @@ -647,12 +646,23 @@ CppEditorWidget::Link CppEditorWidget::findLinkAt(const QTextCursor &cursor, if (!d->m_modelManager) return Link(); - return d->m_followSymbolUnderCursor->findLink(cursor, - resolveTarget, - d->m_modelManager->snapshot(), - d->m_lastSemanticInfo.doc, - d->m_modelManager->symbolFinder(), - inNextSplit); + const Utils::FileName &filePath = textDocument()->filePath(); + if (!resolveTarget) { + // TODO: get that part also from clang + return d->m_builtinFollowSymbol.findLink(CppTools::CursorInEditor{cursor, filePath, this}, + resolveTarget, + 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 @@ -687,6 +697,14 @@ RefactoringEngineInterface *CppEditorWidget::refactoringEngine() const : static_cast(&d->m_builtinRefactoringEngine); } +CppTools::FollowSymbolInterface *CppEditorWidget::followSymbolInterface() const +{ + CppTools::FollowSymbolInterface *followSymbol + = CppTools::CppModelManager::instance()->followSymbolInterface(); + return followSymbol ? followSymbol + : static_cast(&d->m_builtinFollowSymbol); +} + bool CppEditorWidget::isSemanticInfoValidExceptLocalUses() const { return d->m_lastSemanticInfo.doc && d->m_lastSemanticInfo.revision == documentRevision() @@ -973,11 +991,6 @@ void CppEditorWidget::applyDeclDefLinkChanges(bool jumpToMatch) updateFunctionDeclDefLink(); } -FollowSymbolUnderCursor *CppEditorWidget::followSymbolUnderCursorDelegate() -{ - return d->m_followSymbolUnderCursor.data(); -} - void CppEditorWidget::encourageApply() { if (d->m_localRenaming.encourageApply()) diff --git a/src/plugins/cppeditor/cppeditorwidget.h b/src/plugins/cppeditor/cppeditorwidget.h index 87819b76ee2..74a270db983 100644 --- a/src/plugins/cppeditor/cppeditorwidget.h +++ b/src/plugins/cppeditor/cppeditorwidget.h @@ -32,6 +32,7 @@ namespace CppTools { class CppEditorOutline; class RefactoringEngineInterface; +class FollowSymbolInterface; class SemanticInfo; class ProjectPart; } @@ -42,7 +43,6 @@ namespace Internal { class CppEditorDocument; class CppEditorWidgetPrivate; -class FollowSymbolUnderCursor; class FunctionDeclDefLink; class CppEditorWidget : public TextEditor::TextEditorWidget @@ -67,8 +67,6 @@ public: TextEditor::AssistKind kind, TextEditor::AssistReason reason) const override; - FollowSymbolUnderCursor *followSymbolUnderCursorDelegate(); // exposed for tests - void encourageApply() override; void paste() override; @@ -89,6 +87,8 @@ public: static bool isWidgetHighlighted(QWidget *widget); void updateSemanticInfo(); + + CppTools::FollowSymbolInterface *followSymbolInterface() const; protected: bool event(QEvent *e) override; void contextMenuEvent(QContextMenuEvent *) override; diff --git a/src/plugins/cppeditor/cppfollowsymbolundercursor.cpp b/src/plugins/cppeditor/cppfollowsymbolundercursor.cpp index a33304cfcbd..6bc1cfa30e3 100644 --- a/src/plugins/cppeditor/cppfollowsymbolundercursor.cpp +++ b/src/plugins/cppeditor/cppfollowsymbolundercursor.cpp @@ -55,12 +55,6 @@ typedef TextEditorWidget::Link Link; namespace { -static bool useClangFollowSymbol() -{ - static bool use = qEnvironmentVariableIntValue("QTC_CLANG_FOLLOW_SYMBOL"); - return use; -} - class VirtualFunctionHelper { public: VirtualFunctionHelper(TypeOfExpression &typeOfExpression, @@ -305,9 +299,9 @@ inline LookupItem skipForwardDeclarations(const QList &resolvedSymbo return result; } -CppEditorWidget::Link attemptFuncDeclDef(const QTextCursor &cursor, - CppEditorWidget *, Snapshot snapshot, const Document::Ptr &document, - SymbolFinder *symbolFinder) +CppEditorWidget::Link attemptFuncDeclDef(const QTextCursor &cursor, Snapshot snapshot, + const Document::Ptr &document, + SymbolFinder *symbolFinder) { Link result; QTC_ASSERT(document, return result); @@ -467,17 +461,11 @@ QString expressionUnderCursorAsString(const QTextCursor &textCursor, } // anonymous namespace -FollowSymbolUnderCursor::FollowSymbolUnderCursor(CppEditorWidget *widget) - : m_widget(widget) - , m_virtualFunctionAssistProvider(new VirtualFunctionAssistProvider) +FollowSymbolUnderCursor::FollowSymbolUnderCursor() + : m_virtualFunctionAssistProvider(new VirtualFunctionAssistProvider) { } -FollowSymbolUnderCursor::~FollowSymbolUnderCursor() -{ - delete m_virtualFunctionAssistProvider; -} - static int skipMatchingParentheses(const Tokens &tokens, int idx, int initialDepth) { int j = idx; @@ -495,61 +483,28 @@ static int skipMatchingParentheses(const Tokens &tokens, int idx, int initialDep return j; } -bool FollowSymbolUnderCursor::processorFollowSymbol(uint line, uint column, bool resolveTarget, - Link &linkResult) -{ - if (!useClangFollowSymbol()) - return false; - CppEditorDocument* editorDocument = m_widget->cppEditorDocument(); - if (!editorDocument) - return false; - - QFuture info - = editorDocument->requestFollowSymbol(static_cast(line), - static_cast(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 FollowSymbolUnderCursor::findLink( + const CppTools::CursorInEditor &data, + bool resolveTarget, + const Snapshot &theSnapshot, + const Document::Ptr &documentFromSemanticInfo, + SymbolFinder *symbolFinder, + bool inNextSplit) { Link link; 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 column = positionInBlock + 1; - if (resolveTarget && processorFollowSymbol(line, column, resolveTarget, link)) - return link; - Snapshot snapshot = theSnapshot; // Move to end of identifier QTextCursor tc = cursor; - QTextDocument *document = m_widget->document(); QChar ch = document->characterAt(tc.position()); while (CppTools::isValidIdentifierChar(ch)) { tc.movePosition(QTextCursor::NextCharacter); @@ -564,7 +519,7 @@ TextEditorWidget::Link FollowSymbolUnderCursor::findLink(const QTextCursor &curs while (document->characterAt(pos).isSpace()) ++pos; if (document->characterAt(pos) == QLatin1Char('(')) { - link = attemptFuncDeclDef(cursor, m_widget, snapshot, documentFromSemanticInfo, + link = attemptFuncDeclDef(cursor, snapshot, documentFromSemanticInfo, symbolFinder); if (link.hasValidLinkText()) return link; @@ -636,7 +591,7 @@ TextEditorWidget::Link FollowSymbolUnderCursor::findLink(const QTextCursor &curs && unsigned(positionInBlock) <= tk.utf16charsEnd()) { cursorRegionReached = true; if (tk.is(T_OPERATOR)) { - link = attemptFuncDeclDef(cursor, m_widget, theSnapshot, + link = attemptFuncDeclDef(cursor, theSnapshot, documentFromSemanticInfo, symbolFinder); if (link.hasValidLinkText()) return link; @@ -644,7 +599,7 @@ TextEditorWidget::Link FollowSymbolUnderCursor::findLink(const QTextCursor &curs QTextCursor c = cursor; c.movePosition(QTextCursor::Left, QTextCursor::MoveAnchor, positionInBlock - tokens.at(i - 1).utf16charsBegin()); - link = attemptFuncDeclDef(c, m_widget, theSnapshot, documentFromSemanticInfo, + link = attemptFuncDeclDef(c, theSnapshot, documentFromSemanticInfo, symbolFinder); if (link.hasValidLinkText()) return link; @@ -655,8 +610,11 @@ TextEditorWidget::Link FollowSymbolUnderCursor::findLink(const QTextCursor &curs } } + CppEditorWidget *editorWidget = static_cast(data.editorWidget()); + if (!editorWidget) + return link; // 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) { doc = documentFromSemanticInfo; if (!doc) @@ -705,7 +663,7 @@ TextEditorWidget::Link FollowSymbolUnderCursor::findLink(const QTextCursor &curs } else if (const Document::MacroUse *use = doc->findMacroUseAt(endOfToken - 1)) { const QString fileName = use->macro().fileName(); if (fileName == CppModelManager::editorConfigurationFileName()) { - m_widget->showPreProcessorWidget(); + editorWidget->showPreProcessorWidget(); } else if (fileName != CppModelManager::configurationFileName()) { const Macro ¯o = use->macro(); link.targetFileName = macro.fileName(); @@ -739,7 +697,7 @@ TextEditorWidget::Link FollowSymbolUnderCursor::findLink(const QTextCursor &curs if (Symbol *d = r.declaration()) { if (d->isDeclaration() || d->isFunction()) { 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() && unsigned(positionInBlock) >= d->column()) { // TODO: check the end result = r; // take the symbol under cursor. @@ -748,7 +706,7 @@ TextEditorWidget::Link FollowSymbolUnderCursor::findLink(const QTextCursor &curs } } else if (d->isUsingDeclaration()) { int tokenBeginLineNumber = 0, tokenBeginColumnNumber = 0; - m_widget->convertPosition(beginOfToken, &tokenBeginLineNumber, + editorWidget->convertPosition(beginOfToken, &tokenBeginLineNumber, &tokenBeginColumnNumber); if (unsigned(tokenBeginLineNumber) > d->line() || (unsigned(tokenBeginLineNumber) == d->line() @@ -778,7 +736,7 @@ TextEditorWidget::Link FollowSymbolUnderCursor::findLink(const QTextCursor &curs params.openInNextSplit = inNextSplit; if (m_virtualFunctionAssistProvider->configure(params)) { - m_widget->invokeAssist(FollowSymbol, m_virtualFunctionAssistProvider); + editorWidget->invokeAssist(FollowSymbol, m_virtualFunctionAssistProvider.data()); m_virtualFunctionAssistProvider->clearParams(); } @@ -827,12 +785,13 @@ TextEditorWidget::Link FollowSymbolUnderCursor::findLink(const QTextCursor &curs return Link(); } -VirtualFunctionAssistProvider *FollowSymbolUnderCursor::virtualFunctionAssistProvider() +QSharedPointer FollowSymbolUnderCursor::virtualFunctionAssistProvider() { return m_virtualFunctionAssistProvider; } -void FollowSymbolUnderCursor::setVirtualFunctionAssistProvider(VirtualFunctionAssistProvider *provider) +void FollowSymbolUnderCursor::setVirtualFunctionAssistProvider( + const QSharedPointer &provider) { m_virtualFunctionAssistProvider = provider; } diff --git a/src/plugins/cppeditor/cppfollowsymbolundercursor.h b/src/plugins/cppeditor/cppfollowsymbolundercursor.h index ee1e7433e1a..54c500aaa5b 100644 --- a/src/plugins/cppeditor/cppfollowsymbolundercursor.h +++ b/src/plugins/cppeditor/cppfollowsymbolundercursor.h @@ -25,44 +25,31 @@ #pragma once -#include -#include - -QT_BEGIN_NAMESPACE -class QTextCursor; -QT_END_NAMESPACE - -namespace CppTools { class SymbolFinder; } +#include namespace CppEditor { namespace Internal { -class CppEditorWidget; class VirtualFunctionAssistProvider; -class FollowSymbolUnderCursor +class FollowSymbolUnderCursor : public CppTools::FollowSymbolInterface { public: - typedef TextEditor::TextEditorWidget::Link Link; + FollowSymbolUnderCursor(); - FollowSymbolUnderCursor(CppEditorWidget *widget); - ~FollowSymbolUnderCursor(); - - Link findLink(const QTextCursor &cursor, bool resolveTarget, + Link findLink(const CppTools::CursorInEditor &data, + bool resolveTarget, const CPlusPlus::Snapshot &snapshot, const CPlusPlus::Document::Ptr &documentFromSemanticInfo, - CppTools::SymbolFinder *symbolFinder, bool inNextSplit); + CppTools::SymbolFinder *symbolFinder, + bool inNextSplit) override; - VirtualFunctionAssistProvider *virtualFunctionAssistProvider(); - void setVirtualFunctionAssistProvider(VirtualFunctionAssistProvider *provider); + QSharedPointer virtualFunctionAssistProvider(); + void setVirtualFunctionAssistProvider( + const QSharedPointer &provider); private: - // Try to follow symbol with clang processor - // 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; + QSharedPointer m_virtualFunctionAssistProvider; }; } // namespace Internal diff --git a/src/plugins/cppeditor/followsymbol_switchmethoddecldef_test.cpp b/src/plugins/cppeditor/followsymbol_switchmethoddecldef_test.cpp index 87e0b59bdde..9c45cea0559 100644 --- a/src/plugins/cppeditor/followsymbol_switchmethoddecldef_test.cpp +++ b/src/plugins/cppeditor/followsymbol_switchmethoddecldef_test.cpp @@ -33,6 +33,7 @@ #include "cppvirtualfunctionproposalitem.h" #include +#include #include #include @@ -326,19 +327,37 @@ F2TestCase::F2TestCase(CppEditorAction action, switch (action) { case FollowSymbolUnderCursorAction: { CppEditorWidget *widget = initialTestFile->m_editorWidget; - FollowSymbolUnderCursor *delegate = widget->followSymbolUnderCursorDelegate(); - VirtualFunctionAssistProvider *original = delegate->virtualFunctionAssistProvider(); + FollowSymbolInterface* delegate = widget->followSymbolInterface(); + if (!delegate) + QFAIL("No follow symbol interface"); + auto* builtinFollowSymbol = dynamic_cast(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 original + = builtinFollowSymbol->virtualFunctionAssistProvider(); // Set test provider, run and get results - QScopedPointer testProvider( + QSharedPointer testProvider( new VirtualFunctionTestAssistProvider(widget)); - delegate->setVirtualFunctionAssistProvider(testProvider.data()); + builtinFollowSymbol->setVirtualFunctionAssistProvider(testProvider); initialTestFile->m_editorWidget->openLinkUnderCursor(); immediateVirtualSymbolResults = testProvider->m_immediateItems; finalVirtualSymbolResults = testProvider->m_finalItems; // Restore original test provider - delegate->setVirtualFunctionAssistProvider(original); + builtinFollowSymbol->setVirtualFunctionAssistProvider(original); break; } case SwitchBetweenMethodDeclarationDefinitionAction: @@ -870,41 +889,6 @@ void CppEditorPlugin::test_FollowSymbolUnderCursor_data() "@Container 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") << _( "class Foo {\n" " void @foo(int);\n" @@ -993,6 +977,8 @@ void CppEditorPlugin::test_FollowSymbolUnderCursor_data() ); QTest::newRow("template_alias") << _( + "template" + "class Bar;" "template\n" "using Foo = Bar<@T>;\n" ); @@ -1004,6 +990,51 @@ void CppEditorPlugin::test_FollowSymbolUnderCursor() F2TestCase(F2TestCase::FollowSymbolUnderCursorAction, singleDocument(source)); } +void CppEditorPlugin::test_FollowSymbolUnderCursor_QTCREATORBUG7903_data() +{ + QTest::addColumn("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() { QTest::addColumn("variableDeclaration"); // without semicolon, can be "" diff --git a/src/plugins/cpptools/cppmodelmanager.cpp b/src/plugins/cpptools/cppmodelmanager.cpp index 08799709b16..5a6db12be0b 100644 --- a/src/plugins/cpptools/cppmodelmanager.cpp +++ b/src/plugins/cpptools/cppmodelmanager.cpp @@ -39,6 +39,7 @@ #include "cpptoolsreuse.h" #include "editordocumenthandle.h" #include "symbolfinder.h" +#include "followsymbolinterface.h" #include #include @@ -274,6 +275,11 @@ RefactoringEngineInterface *CppModelManager::refactoringEngine() return instance()->d->m_refactoringEngine; } +FollowSymbolInterface *CppModelManager::followSymbolInterface() const +{ + return d->m_activeModelManagerSupport->followSymbolInterface(); +} + QString CppModelManager::configurationFileName() { return Preprocessor::configurationFileName(); diff --git a/src/plugins/cpptools/cppmodelmanager.h b/src/plugins/cpptools/cppmodelmanager.h index 1cabaf50efb..229cfcef7de 100644 --- a/src/plugins/cpptools/cppmodelmanager.h +++ b/src/plugins/cpptools/cppmodelmanager.h @@ -54,6 +54,7 @@ class CppEditorDocumentHandle; class CppIndexingSupport; class ModelManagerSupportProvider; class RefactoringEngineInterface; +class FollowSymbolInterface; class SymbolFinder; class WorkingCopy; @@ -152,6 +153,7 @@ public: CppCompletionAssistProvider *completionAssistProvider() const; BaseEditorDocumentProcessor *editorDocumentProcessor( TextEditor::TextDocument *baseTextDocument) const; + FollowSymbolInterface *followSymbolInterface() const; void setIndexingSupport(CppIndexingSupport *indexingSupport); CppIndexingSupport *indexingSupport(); diff --git a/src/plugins/cpptools/cppmodelmanagersupport.h b/src/plugins/cpptools/cppmodelmanagersupport.h index cfef02e1a08..0a56de102c9 100644 --- a/src/plugins/cpptools/cppmodelmanagersupport.h +++ b/src/plugins/cpptools/cppmodelmanagersupport.h @@ -36,6 +36,7 @@ namespace CppTools { class BaseEditorDocumentProcessor; class CppCompletionAssistProvider; +class FollowSymbolInterface; class CPPTOOLS_EXPORT ModelManagerSupport { @@ -48,6 +49,7 @@ public: virtual CppCompletionAssistProvider *completionAssistProvider() = 0; virtual BaseEditorDocumentProcessor *editorDocumentProcessor( TextEditor::TextDocument *baseTextDocument) = 0; + virtual FollowSymbolInterface *followSymbolInterface() = 0; }; class CPPTOOLS_EXPORT ModelManagerSupportProvider diff --git a/src/plugins/cpptools/cppmodelmanagersupportinternal.cpp b/src/plugins/cpptools/cppmodelmanagersupportinternal.cpp index e2135a76eb3..a7c48674c9c 100644 --- a/src/plugins/cpptools/cppmodelmanagersupportinternal.cpp +++ b/src/plugins/cpptools/cppmodelmanagersupportinternal.cpp @@ -69,3 +69,8 @@ CppCompletionAssistProvider *ModelManagerSupportInternal::completionAssistProvid { return m_completionAssistProvider.data(); } + +FollowSymbolInterface *ModelManagerSupportInternal::followSymbolInterface() +{ + return nullptr; +} diff --git a/src/plugins/cpptools/cppmodelmanagersupportinternal.h b/src/plugins/cpptools/cppmodelmanagersupportinternal.h index 0a2290f5956..fba0cb23e3c 100644 --- a/src/plugins/cpptools/cppmodelmanagersupportinternal.h +++ b/src/plugins/cpptools/cppmodelmanagersupportinternal.h @@ -43,6 +43,7 @@ public: virtual CppCompletionAssistProvider *completionAssistProvider(); virtual BaseEditorDocumentProcessor *editorDocumentProcessor( TextEditor::TextDocument *baseTextDocument); + FollowSymbolInterface *followSymbolInterface() override; private: QScopedPointer m_completionAssistProvider; diff --git a/src/plugins/cpptools/cpptools.pro b/src/plugins/cpptools/cpptools.pro index 3a0e1f2bcd2..016294dc93e 100644 --- a/src/plugins/cpptools/cpptools.pro +++ b/src/plugins/cpptools/cpptools.pro @@ -63,6 +63,7 @@ HEADERS += \ cppworkingcopy.h \ doxygengenerator.h \ editordocumenthandle.h \ + followsymbolinterface.h \ functionutils.h \ generatedcodemodelsupport.h \ includeutils.h \ diff --git a/src/plugins/cpptools/cpptools.qbs b/src/plugins/cpptools/cpptools.qbs index 640e91d6df0..0af071828a4 100644 --- a/src/plugins/cpptools/cpptools.qbs +++ b/src/plugins/cpptools/cpptools.qbs @@ -164,6 +164,7 @@ Project { "doxygengenerator.h", "editordocumenthandle.cpp", "editordocumenthandle.h", + "followsymbolinterface.h", "functionutils.cpp", "functionutils.h", "generatedcodemodelsupport.cpp", diff --git a/src/plugins/cpptools/followsymbolinterface.h b/src/plugins/cpptools/followsymbolinterface.h new file mode 100644 index 00000000000..1d662ae65a1 --- /dev/null +++ b/src/plugins/cpptools/followsymbolinterface.h @@ -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 + +#include + +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 diff --git a/src/plugins/texteditor/convenience.cpp b/src/plugins/texteditor/convenience.cpp index 8ee92147ddc..f18511d518e 100644 --- a/src/plugins/texteditor/convenience.cpp +++ b/src/plugins/texteditor/convenience.cpp @@ -95,6 +95,13 @@ static bool isValidIdentifierChar(const QChar &c) || 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) { const int originalPosition = textCursor.position(); @@ -108,6 +115,8 @@ QTextCursor wordStartCursor(const QTextCursor &textCursor) if (isValidIdentifierChar(c)) cursor.movePosition(QTextCursor::PreviousWord); } + if (isAfterOperatorKeyword(cursor)) + cursor.movePosition(QTextCursor::PreviousWord); return cursor; } diff --git a/src/tools/clangbackend/ipcsource/clangfollowsymbol.cpp b/src/tools/clangbackend/ipcsource/clangfollowsymbol.cpp index 7074af52ee4..ab6bd824bb6 100644 --- a/src/tools/clangbackend/ipcsource/clangfollowsymbol.cpp +++ b/src/tools/clangbackend/ipcsource/clangfollowsymbol.cpp @@ -90,6 +90,20 @@ private: } // 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, const Utf8String &tokenStr) { @@ -99,10 +113,14 @@ static SourceRangeContainer extractMatchingTokenRange(const Cursor &cursor, if (!(tokenStr == ClangString(clang_getTokenSpelling(tu, tokens.data[i])))) continue; - if (cursor.isFunctionLike() - && (i+1 > tokens.tokenCount - || !(ClangString(clang_getTokenSpelling(tu, tokens.data[i+1])) == "("))) { - continue; + if (cursor.isFunctionLike() || cursor.isConstructorOrDestructor()) { + if (tokenStr == "operator") + return getOperatorRange(tu, tokens, i); + + if (i+1 > tokens.tokenCount + || !(ClangString(clang_getTokenSpelling(tu, tokens.data[i+1])) == "(")) { + continue; + } } return SourceRange(clang_getTokenExtent(tu, tokens.data[i])); } diff --git a/tests/unit/unittest/clangfollowsymbol-test.cpp b/tests/unit/unittest/clangfollowsymbol-test.cpp index 64023116fb3..046932034f0 100644 --- a/tests/unit/unittest/clangfollowsymbol-test.cpp +++ b/tests/unit/unittest/clangfollowsymbol-test.cpp @@ -351,6 +351,34 @@ TEST_F(FollowSymbol, CursorAfterNamespace) 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 FollowSymbol::d; void FollowSymbol::SetUpTestCase() diff --git a/tests/unit/unittest/data/followsymbol_main.cpp b/tests/unit/unittest/data/followsymbol_main.cpp index 3727f494cfb..9b16158e40d 100644 --- a/tests/unit/unittest/data/followsymbol_main.cpp +++ b/tests/unit/unittest/data/followsymbol_main.cpp @@ -65,3 +65,18 @@ FooClass::FooClass() { int main() { 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; +}