diff --git a/src/plugins/cpptools/insertionpointlocator.cpp b/src/plugins/cpptools/insertionpointlocator.cpp index 5247f9fb060..3cd40271448 100644 --- a/src/plugins/cpptools/insertionpointlocator.cpp +++ b/src/plugins/cpptools/insertionpointlocator.cpp @@ -414,10 +414,141 @@ protected: return false; } }; + +class FindFunctionDefinition : protected ASTVisitor +{ + FunctionDefinitionAST *_result; + unsigned _line, _column; +public: + FindFunctionDefinition(TranslationUnit *translationUnit) + : ASTVisitor(translationUnit) + { + } + + FunctionDefinitionAST *operator()(unsigned line, unsigned column) + { + _result = 0; + _line = line; + _column = column; + accept(translationUnit()->ast()); + return _result; + } + +protected: + bool preVisit(AST *ast) + { + if (_result) + return false; + unsigned line, column; + translationUnit()->getTokenStartPosition(ast->firstToken(), &line, &column); + if (line > _line || (line == _line && column > _column)) + return false; + translationUnit()->getTokenEndPosition(ast->lastToken() - 1, &line, &column); + if (line < _line || (line == _line && column < _column)) + return false; + return true; + } + + bool visit(FunctionDefinitionAST *ast) + { + _result = ast; + return false; + } +}; + } // anonymous namespace +static Declaration *isNonVirtualFunctionDeclaration(Symbol *s) +{ + if (!s) + return 0; + Declaration *declaration = s->asDeclaration(); + if (!declaration) + return 0; + Function *type = s->type()->asFunctionType(); + if (!type || type->isPureVirtual()) + return 0; + return declaration; +} + +static InsertionLocation nextToSurroundingDefinitions(Declaration *declaration, const CppRefactoringChanges &changes) +{ + InsertionLocation noResult; + Class *klass = declaration->enclosingClass(); + if (!klass) + return noResult; + + // find the index of declaration + int declIndex = -1; + for (unsigned i = 0; i < klass->memberCount(); ++i) { + Symbol *s = klass->memberAt(i); + if (s == declaration) { + declIndex = i; + break; + } + } + if (declIndex == -1) + return noResult; + + // scan preceding declarations for a function declaration + QString prefix, suffix; + Declaration *surroundingFunctionDecl = 0; + for (int i = declIndex - 1; i >= 0; --i) { + Symbol *s = klass->memberAt(i); + surroundingFunctionDecl = isNonVirtualFunctionDeclaration(s); + if (surroundingFunctionDecl) { + prefix = QLatin1String("\n\n"); + break; + } + } + if (!surroundingFunctionDecl) { + // try to find one below + for (unsigned i = declIndex + 1; i < klass->memberCount(); ++i) { + Symbol *s = klass->memberAt(i); + surroundingFunctionDecl = isNonVirtualFunctionDeclaration(s); + if (surroundingFunctionDecl) { + suffix = QLatin1String("\n\n"); + break; + } + } + if (!surroundingFunctionDecl) + return noResult; + } + + // find the declaration's definition + Symbol *definition = changes.snapshot().findMatchingDefinition(surroundingFunctionDecl); + if (!definition) + return noResult; + + unsigned line, column; + if (suffix.isEmpty()) { + Function *definitionFunction = definition->asFunction(); + if (!definitionFunction) + return noResult; + + Document::Ptr targetDoc = changes.snapshot().document(definition->fileName()); + if (!targetDoc) + return noResult; + + targetDoc->translationUnit()->getPosition(definitionFunction->endOffset(), &line, &column); + } else { + // we don't have an offset to the start of the function definition, so we need to manually find it... + CppRefactoringFilePtr targetFile = changes.file(definition->fileName()); + if (!targetFile->isValid()) + return noResult; + + FindFunctionDefinition finder(targetFile->cppDocument()->translationUnit()); + FunctionDefinitionAST *functionDefinition = finder(definition->line(), definition->column()); + if (!functionDefinition) + return noResult; + + targetFile->cppDocument()->translationUnit()->getTokenStartPosition(functionDefinition->firstToken(), &line, &column); + } + + return InsertionLocation(definition->fileName(), prefix, suffix, line, column); +} + /// Currently, we return the end of fileName.cpp -/// \todo take the definitions of the surrounding declarations into account QList InsertionPointLocator::methodDefinition( Declaration *declaration) const { @@ -425,6 +556,20 @@ QList InsertionPointLocator::methodDefinition( if (!declaration) return result; + if (Symbol *s = m_refactoringChanges.snapshot().findMatchingDefinition(declaration, true)) { + if (Function *f = s->asFunction()) { + if (f->isConst() == declaration->type().isConst() + && f->isVolatile() == declaration->type().isVolatile()) + return result; + } + } + + const InsertionLocation location = nextToSurroundingDefinitions(declaration, m_refactoringChanges); + if (location.isValid()) { + result += location; + return result; + } + const QString declFileName = QString::fromUtf8(declaration->fileName(), declaration->fileNameLength()); QString target = declFileName; @@ -438,15 +583,6 @@ QList InsertionPointLocator::methodDefinition( if (doc.isNull()) return result; - Snapshot simplified = m_refactoringChanges.snapshot().simplified(doc); - if (Symbol *s = simplified.findMatchingDefinition(declaration)) { - if (Function *f = s->asFunction()) { - if (f->isConst() == declaration->type().isConst() - && f->isVolatile() == declaration->type().isVolatile()) - return result; - } - } - unsigned line = 0, column = 0; FindMethodDefinitionInsertPoint finder(doc->translationUnit()); finder(declaration, &line, &column); diff --git a/tests/auto/cplusplus/codegen/tst_codegen.cpp b/tests/auto/cplusplus/codegen/tst_codegen.cpp index 158669db0b4..9d71984dcfa 100644 --- a/tests/auto/cplusplus/codegen/tst_codegen.cpp +++ b/tests/auto/cplusplus/codegen/tst_codegen.cpp @@ -43,6 +43,7 @@ #include #include #include +#include #include #include @@ -71,6 +72,11 @@ private slots: void protected_in_nonempty_class(); void protected_betwee_public_and_private(); void qtdesigner_integration(); + void definition_empty_class(); + void definition_first_member(); + void definition_last_member(); + void definition_middle_member(); + private: ExtensionSystem::PluginManager *pluginManager; }; @@ -88,8 +94,9 @@ void tst_Codegen::initTestCase() void tst_Codegen::cleanupTestCase() { - pluginManager->shutdown(); - delete pluginManager; + // gives me a qFatal... +// pluginManager->shutdown(); +// delete pluginManager; } /*! Should insert at line 3, column 1, with "public:\n" as prefix and without suffix. @@ -118,7 +125,7 @@ void tst_Codegen::public_in_empty_class() Snapshot snapshot; snapshot.insert(doc); CppRefactoringChanges changes(snapshot); - InsertionPointLocator find(&changes); + InsertionPointLocator find(changes); InsertionLocation loc = find.methodDeclarationInClass( doc->fileName(), foo, @@ -158,7 +165,7 @@ void tst_Codegen::public_in_nonempty_class() Snapshot snapshot; snapshot.insert(doc); CppRefactoringChanges changes(snapshot); - InsertionPointLocator find(&changes); + InsertionPointLocator find(changes); InsertionLocation loc = find.methodDeclarationInClass( doc->fileName(), foo, @@ -198,7 +205,7 @@ void tst_Codegen::public_before_protected() Snapshot snapshot; snapshot.insert(doc); CppRefactoringChanges changes(snapshot); - InsertionPointLocator find(&changes); + InsertionPointLocator find(changes); InsertionLocation loc = find.methodDeclarationInClass( doc->fileName(), foo, @@ -239,7 +246,7 @@ void tst_Codegen::private_after_protected() Snapshot snapshot; snapshot.insert(doc); CppRefactoringChanges changes(snapshot); - InsertionPointLocator find(&changes); + InsertionPointLocator find(changes); InsertionLocation loc = find.methodDeclarationInClass( doc->fileName(), foo, @@ -280,7 +287,7 @@ void tst_Codegen::protected_in_nonempty_class() Snapshot snapshot; snapshot.insert(doc); CppRefactoringChanges changes(snapshot); - InsertionPointLocator find(&changes); + InsertionPointLocator find(changes); InsertionLocation loc = find.methodDeclarationInClass( doc->fileName(), foo, @@ -321,7 +328,7 @@ void tst_Codegen::protected_betwee_public_and_private() Snapshot snapshot; snapshot.insert(doc); CppRefactoringChanges changes(snapshot); - InsertionPointLocator find(&changes); + InsertionPointLocator find(changes); InsertionLocation loc = find.methodDeclarationInClass( doc->fileName(), foo, @@ -382,7 +389,7 @@ void tst_Codegen::qtdesigner_integration() Snapshot snapshot; snapshot.insert(doc); CppRefactoringChanges changes(snapshot); - InsertionPointLocator find(&changes); + InsertionPointLocator find(changes); InsertionLocation loc = find.methodDeclarationInClass( doc->fileName(), foo, @@ -394,5 +401,274 @@ void tst_Codegen::qtdesigner_integration() QCOMPARE(loc.column(), 1U); } +void tst_Codegen::definition_empty_class() +{ + const QByteArray srcText = "\n" + "class Foo\n" // line 1 + "{\n" + "void foo();\n" // line 3 + "};\n" + "\n"; + + const QByteArray dstText = "\n" + "int x;\n" // line 1 + "\n"; + + Document::Ptr src = Document::create(QLatin1String("/tmp/file.h")); + Utils::FileSaver srcSaver(src->fileName()); + srcSaver.write(srcText); + srcSaver.finalize(); + src->setSource(srcText); + src->parse(); + src->check(); + QCOMPARE(src->diagnosticMessages().size(), 0); + QCOMPARE(src->globalSymbolCount(), 1U); + + Document::Ptr dst = Document::create(QLatin1String("/tmp/file.cpp")); + Utils::FileSaver dstSaver(dst->fileName()); + dstSaver.write(dstText); + dstSaver.finalize(); + dst->setSource(dstText); + dst->parse(); + dst->check(); + QCOMPARE(dst->diagnosticMessages().size(), 0); + QCOMPARE(dst->globalSymbolCount(), 1U); + + Snapshot snapshot; + snapshot.insert(src); + snapshot.insert(dst); + + Class *foo = src->globalSymbolAt(0)->asClass(); + QVERIFY(foo); + QCOMPARE(foo->line(), 1U); + QCOMPARE(foo->column(), 7U); + QCOMPARE(foo->memberCount(), 1U); + Declaration *decl = foo->memberAt(0)->asDeclaration(); + QVERIFY(decl); + QCOMPARE(decl->line(), 3U); + QCOMPARE(decl->column(), 6U); + + CppRefactoringChanges changes(snapshot); + InsertionPointLocator find(changes); + QList locList = find.methodDefinition(decl); + QVERIFY(locList.size() == 1); + InsertionLocation loc = locList.first(); + QCOMPARE(loc.fileName(), QLatin1String("/tmp/file.cpp")); + QCOMPARE(loc.prefix(), QLatin1String("\n\n")); + QCOMPARE(loc.suffix(), QString()); + QCOMPARE(loc.line(), 1U); + QCOMPARE(loc.column(), 7U); +} + +void tst_Codegen::definition_first_member() +{ + const QByteArray srcText = "\n" + "class Foo\n" // line 1 + "{\n" + "void foo();\n" // line 3 + "void bar();\n" // line 4 + "};\n" + "\n"; + + const QByteArray dstText = "\n" + "#include \"/tmp/file.h\"\n" // line 1 + "int x;\n" + "\n" + "void Foo::bar()\n" // line 4 + "{\n" + "\n" + "}\n" + "\n" + "int y;\n"; + + Document::Ptr src = Document::create(QLatin1String("/tmp/file.h")); + Utils::FileSaver srcSaver(src->fileName()); + srcSaver.write(srcText); + srcSaver.finalize(); + src->setSource(srcText); + src->parse(); + src->check(); + QCOMPARE(src->diagnosticMessages().size(), 0); + QCOMPARE(src->globalSymbolCount(), 1U); + + Document::Ptr dst = Document::create(QLatin1String("/tmp/file.cpp")); + dst->addIncludeFile("/tmp/file.h", 1); + Utils::FileSaver dstSaver(dst->fileName()); + dstSaver.write(dstText); + dstSaver.finalize(); + dst->setSource(dstText); + dst->parse(); + dst->check(); + QCOMPARE(dst->diagnosticMessages().size(), 0); + QCOMPARE(dst->globalSymbolCount(), 3U); + + Snapshot snapshot; + snapshot.insert(src); + snapshot.insert(dst); + + Class *foo = src->globalSymbolAt(0)->asClass(); + QVERIFY(foo); + QCOMPARE(foo->line(), 1U); + QCOMPARE(foo->column(), 7U); + QCOMPARE(foo->memberCount(), 2U); + Declaration *decl = foo->memberAt(0)->asDeclaration(); + QVERIFY(decl); + QCOMPARE(decl->line(), 3U); + QCOMPARE(decl->column(), 6U); + + CppRefactoringChanges changes(snapshot); + InsertionPointLocator find(changes); + QList locList = find.methodDefinition(decl); + QVERIFY(locList.size() == 1); + InsertionLocation loc = locList.first(); + QCOMPARE(loc.fileName(), QLatin1String("/tmp/file.cpp")); + QCOMPARE(loc.line(), 4U); + QCOMPARE(loc.column(), 1U); + QCOMPARE(loc.suffix(), QLatin1String("\n\n")); + QCOMPARE(loc.prefix(), QString()); +} + +void tst_Codegen::definition_last_member() +{ + const QByteArray srcText = "\n" + "class Foo\n" // line 1 + "{\n" + "void foo();\n" // line 3 + "void bar();\n" // line 4 + "};\n" + "\n"; + + const QByteArray dstText = "\n" + "#include \"/tmp/file.h\"\n" // line 1 + "int x;\n" + "\n" + "void Foo::foo()\n" // line 4 + "{\n" + "\n" + "}\n" // line 7 + "\n" + "int y;\n"; + + Document::Ptr src = Document::create(QLatin1String("/tmp/file.h")); + Utils::FileSaver srcSaver(src->fileName()); + srcSaver.write(srcText); + srcSaver.finalize(); + src->setSource(srcText); + src->parse(); + src->check(); + QCOMPARE(src->diagnosticMessages().size(), 0); + QCOMPARE(src->globalSymbolCount(), 1U); + + Document::Ptr dst = Document::create(QLatin1String("/tmp/file.cpp")); + dst->addIncludeFile("/tmp/file.h", 1); + Utils::FileSaver dstSaver(dst->fileName()); + dstSaver.write(dstText); + dstSaver.finalize(); + dst->setSource(dstText); + dst->parse(); + dst->check(); + QCOMPARE(dst->diagnosticMessages().size(), 0); + QCOMPARE(dst->globalSymbolCount(), 3U); + + Snapshot snapshot; + snapshot.insert(src); + snapshot.insert(dst); + + Class *foo = src->globalSymbolAt(0)->asClass(); + QVERIFY(foo); + QCOMPARE(foo->line(), 1U); + QCOMPARE(foo->column(), 7U); + QCOMPARE(foo->memberCount(), 2U); + Declaration *decl = foo->memberAt(1)->asDeclaration(); + QVERIFY(decl); + QCOMPARE(decl->line(), 4U); + QCOMPARE(decl->column(), 6U); + + CppRefactoringChanges changes(snapshot); + InsertionPointLocator find(changes); + QList locList = find.methodDefinition(decl); + QVERIFY(locList.size() == 1); + InsertionLocation loc = locList.first(); + QCOMPARE(loc.fileName(), QLatin1String("/tmp/file.cpp")); + QCOMPARE(loc.line(), 7U); + QCOMPARE(loc.column(), 2U); + QCOMPARE(loc.prefix(), QLatin1String("\n\n")); + QCOMPARE(loc.suffix(), QString()); +} + +void tst_Codegen::definition_middle_member() +{ + const QByteArray srcText = "\n" + "class Foo\n" // line 1 + "{\n" + "void foo();\n" // line 3 + "void bar();\n" // line 4 + "void car();\n" // line 5 + "};\n" + "\n"; + + const QByteArray dstText = "\n" + "#include \"/tmp/file.h\"\n" // line 1 + "int x;\n" + "\n" + "void Foo::foo()\n" // line 4 + "{\n" + "\n" + "}\n" // line 7 + "\n" + "void Foo::car()\n" // line 9 + "{\n" + "\n" + "}\n" + "\n" + "int y;\n"; + + Document::Ptr src = Document::create(QLatin1String("/tmp/file.h")); + Utils::FileSaver srcSaver(src->fileName()); + srcSaver.write(srcText); + srcSaver.finalize(); + src->setSource(srcText); + src->parse(); + src->check(); + QCOMPARE(src->diagnosticMessages().size(), 0); + QCOMPARE(src->globalSymbolCount(), 1U); + + Document::Ptr dst = Document::create(QLatin1String("/tmp/file.cpp")); + dst->addIncludeFile("/tmp/file.h", 1); + Utils::FileSaver dstSaver(dst->fileName()); + dstSaver.write(dstText); + dstSaver.finalize(); + dst->setSource(dstText); + dst->parse(); + dst->check(); + QCOMPARE(dst->diagnosticMessages().size(), 0); + QCOMPARE(dst->globalSymbolCount(), 4U); + + Snapshot snapshot; + snapshot.insert(src); + snapshot.insert(dst); + + Class *foo = src->globalSymbolAt(0)->asClass(); + QVERIFY(foo); + QCOMPARE(foo->line(), 1U); + QCOMPARE(foo->column(), 7U); + QCOMPARE(foo->memberCount(), 3U); + Declaration *decl = foo->memberAt(1)->asDeclaration(); + QVERIFY(decl); + QCOMPARE(decl->line(), 4U); + QCOMPARE(decl->column(), 6U); + + CppRefactoringChanges changes(snapshot); + InsertionPointLocator find(changes); + QList locList = find.methodDefinition(decl); + QVERIFY(locList.size() == 1); + InsertionLocation loc = locList.first(); + QCOMPARE(loc.fileName(), QLatin1String("/tmp/file.cpp")); + QCOMPARE(loc.line(), 7U); + QCOMPARE(loc.column(), 2U); + QCOMPARE(loc.prefix(), QLatin1String("\n\n")); + QCOMPARE(loc.suffix(), QString()); +} + QTEST_MAIN(tst_Codegen) #include "tst_codegen.moc"