diff --git a/src/plugins/cppeditor/cppeditorplugin.h b/src/plugins/cppeditor/cppeditorplugin.h index 74e031fc3fcc19785c17f4613feadf2242afc151..427d7302876e1ff89e961f08b747b641e1cc8c31 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 4b91d3ce82140b02037a1fc813409ac5c3f436c9..bbc6129c29f13059f96108c9c77f4d580e2d0883 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 f922b20de3c55346212dac393e8ec4d90aca6214..0ceb0446762c00d4c08553abf0093b25c2150ffc 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 d3565d7b154452487c87ef5905ec8b872cb8f37f..5994ededbf3117d1f5ba87e14fe872988ce8117a 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); } } }