From 321ac6cc51009ed93ba4d6dbc3b684b3f00e437e Mon Sep 17 00:00:00 2001 From: Joerg Bornemann <joerg.bornemann@digia.com> Date: Mon, 16 Sep 2013 12:50:13 +0200 Subject: [PATCH] CppEditor: fix "follow symbol under cursor" for operators Like for functions we can call attemptFuncDeclDef for operators. The check for function names has been enhanced to take operators into account. For the switch from definition to declaration SymbolFinder::findMatchingDeclaration has been enriched with operator knowledge. Task-number: QTCREATORBUG-7485 Change-Id: I29eebee337e26c8bf67dc8b4a15c43883045589d Reviewed-by: Nikolai Kosjar <nikolai.kosjar@digia.com> --- src/plugins/cppeditor/cppeditorplugin.h | 6 ++ .../cppeditor/cppfollowsymbolundercursor.cpp | 30 ++++++++ .../followsymbol_switchmethoddecldef_test.cpp | 76 +++++++++++++++++++ src/plugins/cpptools/symbolfinder.cpp | 53 +++++++++---- 4 files changed, 151 insertions(+), 14 deletions(-) diff --git a/src/plugins/cppeditor/cppeditorplugin.h b/src/plugins/cppeditor/cppeditorplugin.h index 74e031fc3fc..427d7302876 100644 --- a/src/plugins/cppeditor/cppeditorplugin.h +++ b/src/plugins/cppeditor/cppeditorplugin.h @@ -125,6 +125,12 @@ private slots: void test_FollowSymbolUnderCursor_classDestructor(); void test_FollowSymbolUnderCursor_QObject_connect_data(); void test_FollowSymbolUnderCursor_QObject_connect(); + void test_FollowSymbolUnderCursor_classOperator_onOperatorToken_data(); + void test_FollowSymbolUnderCursor_classOperator_onOperatorToken(); + void test_FollowSymbolUnderCursor_classOperator_data(); + void test_FollowSymbolUnderCursor_classOperator(); + void test_FollowSymbolUnderCursor_classOperator_inOp_data(); + void test_FollowSymbolUnderCursor_classOperator_inOp(); void test_FollowSymbolUnderCursor_using_QTCREATORBUG7903_globalNamespace(); void test_FollowSymbolUnderCursor_using_QTCREATORBUG7903_namespace(); void test_FollowSymbolUnderCursor_using_QTCREATORBUG7903_insideFunction(); diff --git a/src/plugins/cppeditor/cppfollowsymbolundercursor.cpp b/src/plugins/cppeditor/cppfollowsymbolundercursor.cpp index 4b91d3ce821..bbc6129c29f 100644 --- a/src/plugins/cppeditor/cppfollowsymbolundercursor.cpp +++ b/src/plugins/cppeditor/cppfollowsymbolundercursor.cpp @@ -360,6 +360,36 @@ BaseTextEditorWidget::Link FollowSymbolUnderCursor::findLink(const QTextCursor & } } + // Check if we're on an operator declaration or definition. + if (!recognizedQtMethod) { + bool cursorRegionReached = false; + for (int i = 0; i < tokens.size(); ++i) { + const Token &tk = tokens.at(i); + + // In this case we want to look at one token before the current position to recognize + // an operator if the cursor is inside the actual operator: operator[$] + if (unsigned(positionInBlock) >= tk.begin() && unsigned(positionInBlock) <= tk.end()) { + cursorRegionReached = true; + if (tk.is(T_OPERATOR)) { + link = attemptFuncDeclDef(cursor, m_widget, theSnapshot, + documentFromSemanticInfo, symbolFinder); + if (link.hasValidLinkText()) + return link; + } else if (tk.isOperator() && i > 0 && tokens.at(i - 1).is(T_OPERATOR)) { + QTextCursor c = cursor; + c.movePosition(QTextCursor::Left, QTextCursor::MoveAnchor, + positionInBlock - tokens.at(i - 1).begin()); + link = attemptFuncDeclDef(c, m_widget, theSnapshot, documentFromSemanticInfo, + symbolFinder); + if (link.hasValidLinkText()) + return link; + } + } else if (cursorRegionReached) { + break; + } + } + } + // Now we prefer the doc from the snapshot with macros expanded. Document::Ptr doc = snapshot.document(m_widget->editorDocument()->filePath()); if (!doc) { diff --git a/src/plugins/cppeditor/followsymbol_switchmethoddecldef_test.cpp b/src/plugins/cppeditor/followsymbol_switchmethoddecldef_test.cpp index f922b20de3c..0ceb0446762 100644 --- a/src/plugins/cppeditor/followsymbol_switchmethoddecldef_test.cpp +++ b/src/plugins/cppeditor/followsymbol_switchmethoddecldef_test.cpp @@ -1088,6 +1088,82 @@ void CppEditorPlugin::test_FollowSymbolUnderCursor_QObject_connect() test.run(); } +void CppEditorPlugin::test_FollowSymbolUnderCursor_classOperator_onOperatorToken_data() +{ + QTest::addColumn<bool>("toDeclaration"); + QTest::newRow("forward") << false; + QTest::newRow("backward") << true; +} + +void CppEditorPlugin::test_FollowSymbolUnderCursor_classOperator_onOperatorToken() +{ + QFETCH(bool, toDeclaration); + + QByteArray source = + "class Foo {\n" + " void @operator[](size_t idx);\n" + "};\n\n" + "void Foo::$operator[](size_t idx)\n" + "{\n" + "}\n"; + if (toDeclaration) + source.replace('@', '#').replace('$', '@').replace('#', '$'); + TestCase test(TestCase::FollowSymbolUnderCursorAction, source); + test.run(); +} + +void CppEditorPlugin::test_FollowSymbolUnderCursor_classOperator_data() +{ + test_FollowSymbolUnderCursor_classOperator_onOperatorToken_data(); +} + +void CppEditorPlugin::test_FollowSymbolUnderCursor_classOperator() +{ + QFETCH(bool, toDeclaration); + + QByteArray source = + "class Foo {\n" + " void $2operator@1[](size_t idx);\n" + "};\n\n" + "void Foo::$1operator@2[](size_t idx)\n" + "{\n" + "}\n"; + if (toDeclaration) + source.replace("@1", QByteArray()).replace("$1", QByteArray()) + .replace("@2", "@").replace("$2", "$"); + else + source.replace("@2", QByteArray()).replace("$2", QByteArray()) + .replace("@1", "@").replace("$1", "$"); + TestCase test(TestCase::FollowSymbolUnderCursorAction, source); + test.run(); +} + +void CppEditorPlugin::test_FollowSymbolUnderCursor_classOperator_inOp_data() +{ + test_FollowSymbolUnderCursor_classOperator_onOperatorToken_data(); +} + +void CppEditorPlugin::test_FollowSymbolUnderCursor_classOperator_inOp() +{ + QFETCH(bool, toDeclaration); + + QByteArray source = + "class Foo {\n" + " void $2operator[@1](size_t idx);\n" + "};\n\n" + "void Foo::$1operator[@2](size_t idx)\n" + "{\n" + "}\n"; + if (toDeclaration) + source.replace("@1", QByteArray()).replace("$1", QByteArray()) + .replace("@2", "@").replace("$2", "$"); + else + source.replace("@2", QByteArray()).replace("$2", QByteArray()) + .replace("@1", "@").replace("$1", "$"); + TestCase test(TestCase::FollowSymbolUnderCursorAction, source); + test.run(); +} + void CppEditorPlugin::test_FollowSymbolUnderCursor_using_QTCREATORBUG7903_globalNamespace() { const QByteArray source = diff --git a/src/plugins/cpptools/symbolfinder.cpp b/src/plugins/cpptools/symbolfinder.cpp index d3565d7b154..5994ededbf3 100644 --- a/src/plugins/cpptools/symbolfinder.cpp +++ b/src/plugins/cpptools/symbolfinder.cpp @@ -244,6 +244,24 @@ Class *SymbolFinder::findMatchingClassDeclaration(Symbol *declaration, const Sna return 0; } +static void findDeclarationOfSymbol(Symbol *s, + Function *functionType, + QList<Declaration *> *typeMatch, + QList<Declaration *> *argumentCountMatch, + QList<Declaration *> *nameMatch) +{ + if (Declaration *decl = s->asDeclaration()) { + if (Function *declFunTy = decl->type()->asFunctionType()) { + if (functionType->isEqualTo(declFunTy)) + typeMatch->prepend(decl); + else if (functionType->argumentCount() == declFunTy->argumentCount()) + argumentCountMatch->prepend(decl); + else + nameMatch->append(decl); + } + } +} + void SymbolFinder::findMatchingDeclaration(const LookupContext &context, Function *functionType, QList<Declaration *> *typeMatch, @@ -280,26 +298,33 @@ void SymbolFinder::findMatchingDeclaration(const LookupContext &context, } const Identifier *funcId = functionName->identifier(); - if (!funcId) // E.g. operator, which we might be able to handle in the future... - return; + OperatorNameId::Kind operatorNameId = OperatorNameId::InvalidOp; + + if (!funcId) { + if (!qName) + return; + const OperatorNameId * const onid = qName->name()->asOperatorNameId(); + if (!onid) + return; + operatorNameId = onid->kind(); + } foreach (Symbol *s, binding->symbols()) { Scope *scope = s->asScope(); if (!scope) continue; - for (Symbol *s = scope->find(funcId); s; s = s->next()) { - if (!s->name() || !funcId->isEqualTo(s->identifier()) || !s->type()->isFunctionType()) - continue; - if (Declaration *decl = s->asDeclaration()) { - if (Function *declFunTy = decl->type()->asFunctionType()) { - if (functionType->isEqualTo(declFunTy)) - typeMatch->prepend(decl); - else if (functionType->argumentCount() == declFunTy->argumentCount()) - argumentCountMatch->prepend(decl); - else - nameMatch->append(decl); - } + if (funcId) { + for (Symbol *s = scope->find(funcId); s; s = s->next()) { + if (!s->name() || !funcId->isEqualTo(s->identifier()) || !s->type()->isFunctionType()) + continue; + findDeclarationOfSymbol(s, functionType, typeMatch, argumentCountMatch, nameMatch); + } + } else { + for (Symbol *s = scope->find(operatorNameId); s; s = s->next()) { + if (!s->name() || !s->type()->isFunctionType()) + continue; + findDeclarationOfSymbol(s, functionType, typeMatch, argumentCountMatch, nameMatch); } } } -- GitLab