From a8179152c9d38bcb73778db533988c6281699e14 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen <erik.verbruggen@nokia.com> Date: Wed, 28 Jul 2010 12:09:50 +0200 Subject: [PATCH] Added tests for the InsertionPointLocator and fixed bugs. --- .gitignore | 1 + src/libs/cplusplus/InsertionPointLocator.cpp | 65 +++-- src/libs/cplusplus/InsertionPointLocator.h | 13 +- src/plugins/designer/qtcreatorintegration.cpp | 47 +-- tests/auto/cplusplus/codegen/codegen.pro | 7 + tests/auto/cplusplus/codegen/tst_codegen.cpp | 267 ++++++++++++++++++ tests/auto/cplusplus/cplusplus.pro | 12 +- 7 files changed, 347 insertions(+), 65 deletions(-) create mode 100644 tests/auto/cplusplus/codegen/codegen.pro create mode 100644 tests/auto/cplusplus/codegen/tst_codegen.cpp diff --git a/.gitignore b/.gitignore index 15bb1bc52ea..85fa07df957 100644 --- a/.gitignore +++ b/.gitignore @@ -79,6 +79,7 @@ share/doc/qtcreator/qtcreator.qch tests/manual/cplusplus/cplusplus0 tests/manual/cplusplus-dump/cplusplus0 tests/manual/qml-ast2dot/qml-ast2dot +tests/auto/cplusplus/codegen/tst_codegen tests/auto/qml/qmldesigner/bauhaustests/tst_bauhaus tests/auto/qml/qmldesigner/coretests/tst_qmldesigner_core tests/auto/qml/qmldesigner/propertyeditortests/tst_propertyeditor diff --git a/src/libs/cplusplus/InsertionPointLocator.cpp b/src/libs/cplusplus/InsertionPointLocator.cpp index 9db5278e244..2b295090375 100644 --- a/src/libs/cplusplus/InsertionPointLocator.cpp +++ b/src/libs/cplusplus/InsertionPointLocator.cpp @@ -64,9 +64,9 @@ static QString generate(InsertionPointLocator::AccessSpec xsSpec) } } -static int distance(InsertionPointLocator::AccessSpec one, InsertionPointLocator::AccessSpec two) +static int ordering(InsertionPointLocator::AccessSpec xsSpec) { - static QList<InsertionPointLocator::AccessSpec> distances = QList<InsertionPointLocator::AccessSpec>() + static QList<InsertionPointLocator::AccessSpec> order = QList<InsertionPointLocator::AccessSpec>() << InsertionPointLocator::Public << InsertionPointLocator::PublicSlot << InsertionPointLocator::Signals @@ -76,7 +76,7 @@ static int distance(InsertionPointLocator::AccessSpec one, InsertionPointLocator << InsertionPointLocator::Private ; - return distances.indexOf(one) - distances.indexOf(two); + return order.indexOf(xsSpec); } struct AccessRange @@ -134,39 +134,62 @@ protected: ast->lbrace_token, ast->rbrace_token); - QPair<unsigned, bool> result = findMatch(ranges, _xsSpec); + unsigned beforeToken = 0; + bool needsPrefix = false; + bool needsSuffix = false; + findMatch(ranges, _xsSpec, beforeToken, needsPrefix, needsSuffix); unsigned line = 0, column = 0; - getTokenStartPosition(result.first, &line, &column); + getTokenStartPosition(beforeToken, &line, &column); QString prefix; - if (!result.second) + if (needsPrefix) prefix = generate(_xsSpec); - _result = InsertionLocation(prefix, line, column); + QString suffix; + if (needsSuffix) + suffix = QLatin1Char('\n'); + + _result = InsertionLocation(prefix, suffix, line, column); return false; } - static QPair<unsigned, bool> findMatch(const QList<AccessRange> &ranges, - InsertionPointLocator::AccessSpec xsSpec) + static void findMatch(const QList<AccessRange> &ranges, + InsertionPointLocator::AccessSpec xsSpec, + unsigned &beforeToken, + bool &needsPrefix, + bool &needsSuffix) { + Q_ASSERT(!ranges.isEmpty()); + const int lastIndex = ranges.size() - 1; + // try an exact match, and ignore the first (default) access spec: - for (int i = ranges.size() - 1; i > 0; --i) { + for (int i = lastIndex; i > 0; --i) { const AccessRange &range = ranges.at(i); - if (range.xsSpec == xsSpec) - return qMakePair(range.end, true); + if (range.xsSpec == xsSpec) { + beforeToken = range.end; + needsPrefix = false; + needsSuffix = (i != lastIndex); + return; + } } - // try to find a fitting access spec to insert in front of: - AccessRange best = ranges.last(); - for (int i = ranges.size() - 2; i > 0; --i) { - const AccessRange &range = ranges.at(i); - if (distance(range.xsSpec, xsSpec) < distance(best.xsSpec, xsSpec)) - best = range; + // try to find a fitting access spec to insert XXX: + for (int i = lastIndex; i > 0; --i) { + const AccessRange ¤t = ranges.at(i); + + if (ordering(xsSpec) > ordering(current.xsSpec)) { + beforeToken = current.end; + needsPrefix = true; + needsSuffix = (i != lastIndex); + return; + } } // otherwise: - return qMakePair(ranges.first().end, false); + beforeToken = ranges.first().end; + needsPrefix = true; + needsSuffix = (ranges.size() != 1); } QList<AccessRange> collectAccessRanges(DeclarationListAST *decls, @@ -237,8 +260,10 @@ InsertionLocation::InsertionLocation() , m_column(0) {} -InsertionLocation::InsertionLocation(const QString &prefix, unsigned line, unsigned column) +InsertionLocation::InsertionLocation(const QString &prefix, const QString &suffix, + unsigned line, unsigned column) : m_prefix(prefix) + , m_suffix(suffix) , m_line(line) , m_column(column) {} diff --git a/src/libs/cplusplus/InsertionPointLocator.h b/src/libs/cplusplus/InsertionPointLocator.h index 5d0d854d525..20d1cfe8302 100644 --- a/src/libs/cplusplus/InsertionPointLocator.h +++ b/src/libs/cplusplus/InsertionPointLocator.h @@ -33,7 +33,7 @@ #include <CPlusPlusForwardDeclarations.h> #include <Symbols.h> -#include <cplusplus/CppDocument.h> +#include "CppDocument.h" namespace CPlusPlus { @@ -41,18 +41,22 @@ class CPLUSPLUS_EXPORT InsertionLocation { public: InsertionLocation(); - InsertionLocation(const QString &prefix, unsigned line, unsigned column); + InsertionLocation(const QString &prefix, const QString &suffix, unsigned line, unsigned column); /// \returns The prefix to insert before any other text. QString prefix() const { return m_prefix; } + /// \returns The suffix to insert after the other inserted text. + QString suffix() const + { return m_suffix; } + /// \returns The line where to insert. The line number is 1-based. - int line() const + unsigned line() const { return m_line; } /// \returns The column where to insert. The column number is 1-based. - int column() const + unsigned column() const { return m_column; } bool isValid() const @@ -60,6 +64,7 @@ public: private: QString m_prefix; + QString m_suffix; unsigned m_line; unsigned m_column; }; diff --git a/src/plugins/designer/qtcreatorintegration.cpp b/src/plugins/designer/qtcreatorintegration.cpp index ff40fd0c1a4..ea51e896a5c 100644 --- a/src/plugins/designer/qtcreatorintegration.cpp +++ b/src/plugins/designer/qtcreatorintegration.cpp @@ -275,44 +275,6 @@ static bool isEndingQuote(const QString &contents, int quoteIndex) return endingQuote; } -// TODO: Wait for robust Roberto's code using AST or whatever for that. Current implementation is hackish. -static int findClassEndPosition(const QString &headerContents, int classStartPosition) -{ - const QString contents = headerContents.mid(classStartPosition); // we start serching from the beginning of class declaration - // We need to find the position of class closing "}" - int openedBlocksCount = 0; // counter of nested {} blocks - int idx = 0; // index of current position in the contents - while (true) { - if (idx < 0 || idx >= contents.length()) // indexOf returned -1, that means we don't have closing comment mark - break; - if (contents.mid(idx, 2) == QLatin1String("//")) { - idx = contents.indexOf(QLatin1Char('\n'), idx + 2) + 1; // drop everything up to the end of line - } else if (contents.mid(idx, 2) == QLatin1String("/*")) { - idx = contents.indexOf(QLatin1String("*/"), idx + 2) + 2; // drop everything up to the nearest */ - } else if (contents.mid(idx, 4) == QLatin1String("'\\\"'")) { - idx += 4; // drop it - } else if (contents.at(idx) == QLatin1Char('\"')) { - do { - idx = contents.indexOf(QLatin1Char('\"'), idx + 1); // drop everything up to the nearest " - } while (idx > 0 && !isEndingQuote(contents, idx)); // if the nearest " is preceded by \ (or by \\\ or by \\\\\, but not by \\ nor \\\\) we find next one - if (idx < 0) - break; - idx++; - } else { - if (contents.at(idx) == QLatin1Char('{')) { - openedBlocksCount++; - } else if (contents.at(idx) == QLatin1Char('}')) { - openedBlocksCount--; - if (openedBlocksCount == 0) { - return classStartPosition + idx; - } - } - idx++; - } - } - return -1; -} - static inline ITextEditable *editableAt(const QString &fileName, int line, int column) { return qobject_cast<ITextEditable *>(TextEditor::BaseTextEditor::openEditorAt(fileName, line, column)); @@ -329,7 +291,7 @@ static void addDeclaration(Document::Ptr doc, const Class *cl, const QString &fu const InsertionLocation loc = find.methodDeclarationInClass(cl, InsertionPointLocator::PrivateSlot); // - // ### FIXME: change this to use the Refactoring changes! + // ### FIXME: change this to use the Refactoring changes. // if (ITextEditable *editable = editableAt(docFileName, loc.line(), loc.column() - 1)) { @@ -338,7 +300,7 @@ static void addDeclaration(Document::Ptr doc, const Class *cl, const QString &fu QTextCursor tc = editor->textCursor(); int pos = tc.position(); tc.beginEditBlock(); - tc.insertText(loc.prefix() + declaration); + tc.insertText(loc.prefix() + declaration + loc.suffix()); tc.setPosition(pos, QTextCursor::KeepAnchor); editor->indentInsertedText(tc); tc.endEditBlock(); @@ -370,6 +332,11 @@ static Document::Ptr addDefinition(const CPlusPlus::Snapshot &docTable, // we take only those documents which have the same filename if (headerBaseName == sourceFI.baseName()) { if (ITextEditable *editable = editableAt(doc->fileName(), 0, 0)) { + + // + // ### FIXME: use the InsertionPointLocator to insert at the correct place. + // + const QString contents = editable->contents(); int column; editable->convertPosition(contents.length(), line, &column); diff --git a/tests/auto/cplusplus/codegen/codegen.pro b/tests/auto/cplusplus/codegen/codegen.pro new file mode 100644 index 00000000000..fa736489c72 --- /dev/null +++ b/tests/auto/cplusplus/codegen/codegen.pro @@ -0,0 +1,7 @@ +TEMPLATE = app +CONFIG += qt warn_on console depend_includepath +CONFIG += qtestlib testcase +CONFIG -= app_bundle +include(../shared/shared.pri) +SOURCES += tst_codegen.cpp +TARGET=tst_$$TARGET diff --git a/tests/auto/cplusplus/codegen/tst_codegen.cpp b/tests/auto/cplusplus/codegen/tst_codegen.cpp new file mode 100644 index 00000000000..60b66b6b97b --- /dev/null +++ b/tests/auto/cplusplus/codegen/tst_codegen.cpp @@ -0,0 +1,267 @@ +#include <AST.h> +#include <Control.h> +#include <CppDocument.h> +#include <DiagnosticClient.h> +#include <InsertionPointLocator.h> +#include <Scope.h> +#include <TranslationUnit.h> +#include <Literals.h> +#include <Semantic.h> +#include <Symbols.h> + +#include <QtTest> +#include <QtDebug> +#include <QTextDocument> + +using namespace CPlusPlus; + +class tst_Codegen: public QObject +{ + Q_OBJECT + +private slots: + void public_in_empty_class(); + void public_in_nonempty_class(); + void public_before_protected(); + void private_after_protected(); + void protected_in_nonempty_class(); + void protected_betwee_public_and_private(); + void qtdesigner_integration(); +}; + +void tst_Codegen::public_in_nonempty_class() +{ + const QByteArray src = "\n" + "class Foo\n" // line 1 + "{\n" + "public:\n" // line 3 + "};\n" // line 4 + "\n"; + + Document::Ptr doc = Document::create("public_in_nonempty_class"); + doc->setSource(src); + doc->parse(); + doc->check(); + + QCOMPARE(doc->diagnosticMessages().size(), 0); + QCOMPARE(doc->globalSymbolCount(), 1U); + + Class *mainWindow = doc->globalSymbolAt(0)->asClass(); + QVERIFY(mainWindow); + QCOMPARE(mainWindow->line(), 1U); + QCOMPARE(mainWindow->column(), 7U); + + InsertionPointLocator find(doc); + InsertionLocation loc = find.methodDeclarationInClass(mainWindow, InsertionPointLocator::Public); + QVERIFY(loc.isValid()); + QVERIFY(loc.prefix().isEmpty()); + QVERIFY(loc.suffix().isEmpty()); + QCOMPARE(loc.line(), 4U); + QCOMPARE(loc.column(), 1U); +} + +void tst_Codegen::public_in_empty_class() +{ + const QByteArray src = "\n" + "class Foo\n" // line 1 + "{\n" + "};\n" + "\n"; + + Document::Ptr doc = Document::create("public_in_empty_class"); + doc->setSource(src); + doc->parse(); + doc->check(); + + QCOMPARE(doc->diagnosticMessages().size(), 0); + QCOMPARE(doc->globalSymbolCount(), 1U); + + Class *mainWindow = doc->globalSymbolAt(0)->asClass(); + QVERIFY(mainWindow); + QCOMPARE(mainWindow->line(), 1U); + QCOMPARE(mainWindow->column(), 7U); + + InsertionPointLocator find(doc); + InsertionLocation loc = find.methodDeclarationInClass(mainWindow, InsertionPointLocator::Public); + QVERIFY(loc.isValid()); + QCOMPARE(loc.prefix(), QLatin1String("public:\n")); + QVERIFY(loc.suffix().isEmpty()); + QCOMPARE(loc.line(), 3U); + QCOMPARE(loc.column(), 1U); +} + +void tst_Codegen::public_before_protected() +{ + const QByteArray src = "\n" + "class Foo\n" // line 1 + "{\n" + "protected:\n" // line 3 + "};\n" + "\n"; + + Document::Ptr doc = Document::create("public_before_protected"); + doc->setSource(src); + doc->parse(); + doc->check(); + + QCOMPARE(doc->diagnosticMessages().size(), 0); + QCOMPARE(doc->globalSymbolCount(), 1U); + + Class *mainWindow = doc->globalSymbolAt(0)->asClass(); + QVERIFY(mainWindow); + QCOMPARE(mainWindow->line(), 1U); + QCOMPARE(mainWindow->column(), 7U); + + InsertionPointLocator find(doc); + InsertionLocation loc = find.methodDeclarationInClass(mainWindow, InsertionPointLocator::Public); + QVERIFY(loc.isValid()); + QCOMPARE(loc.prefix(), QLatin1String("public:\n")); + QCOMPARE(loc.suffix(), QLatin1String("\n")); + QCOMPARE(loc.column(), 1U); + QCOMPARE(loc.line(), 3U); +} + +void tst_Codegen::private_after_protected() +{ + const QByteArray src = "\n" + "class Foo\n" // line 1 + "{\n" + "protected:\n" // line 3 + "};\n" + "\n"; + + Document::Ptr doc = Document::create("private_after_protected"); + doc->setSource(src); + doc->parse(); + doc->check(); + + QCOMPARE(doc->diagnosticMessages().size(), 0); + QCOMPARE(doc->globalSymbolCount(), 1U); + + Class *mainWindow = doc->globalSymbolAt(0)->asClass(); + QVERIFY(mainWindow); + QCOMPARE(mainWindow->line(), 1U); + QCOMPARE(mainWindow->column(), 7U); + + InsertionPointLocator find(doc); + InsertionLocation loc = find.methodDeclarationInClass(mainWindow, InsertionPointLocator::Private); + QVERIFY(loc.isValid()); + QCOMPARE(loc.prefix(), QLatin1String("private:\n")); + QVERIFY(loc.suffix().isEmpty()); + QCOMPARE(loc.column(), 1U); + QCOMPARE(loc.line(), 4U); +} + +void tst_Codegen::protected_in_nonempty_class() +{ + const QByteArray src = "\n" + "class Foo\n" // line 1 + "{\n" + "public:\n" // line 3 + "};\n" // line 4 + "\n"; + + Document::Ptr doc = Document::create("protected_in_nonempty_class"); + doc->setSource(src); + doc->parse(); + doc->check(); + + QCOMPARE(doc->diagnosticMessages().size(), 0); + QCOMPARE(doc->globalSymbolCount(), 1U); + + Class *mainWindow = doc->globalSymbolAt(0)->asClass(); + QVERIFY(mainWindow); + QCOMPARE(mainWindow->line(), 1U); + QCOMPARE(mainWindow->column(), 7U); + + InsertionPointLocator find(doc); + InsertionLocation loc = find.methodDeclarationInClass(mainWindow, InsertionPointLocator::Protected); + QVERIFY(loc.isValid()); + QCOMPARE(loc.prefix(), QLatin1String("protected:\n")); + QVERIFY(loc.suffix().isEmpty()); + QCOMPARE(loc.column(), 1U); + QCOMPARE(loc.line(), 4U); +} + +void tst_Codegen::protected_betwee_public_and_private() +{ + const QByteArray src = "\n" + "class Foo\n" // line 1 + "{\n" + "public:\n" // line 3 + "private:\n" // line 4 + "};\n" // line 5 + "\n"; + + Document::Ptr doc = Document::create("protected_betwee_public_and_private"); + doc->setSource(src); + doc->parse(); + doc->check(); + + QCOMPARE(doc->diagnosticMessages().size(), 0); + QCOMPARE(doc->globalSymbolCount(), 1U); + + Class *mainWindow = doc->globalSymbolAt(0)->asClass(); + QVERIFY(mainWindow); + QCOMPARE(mainWindow->line(), 1U); + QCOMPARE(mainWindow->column(), 7U); + + InsertionPointLocator find(doc); + InsertionLocation loc = find.methodDeclarationInClass(mainWindow, InsertionPointLocator::Protected); + QVERIFY(loc.isValid()); + QCOMPARE(loc.prefix(), QLatin1String("protected:\n")); + QCOMPARE(loc.suffix(), QLatin1String("\n")); + QCOMPARE(loc.column(), 1U); + QCOMPARE(loc.line(), 4U); +} + +void tst_Codegen::qtdesigner_integration() +{ + const QByteArray src = "/**** Some long (C)opyright notice ****/\n" + "#ifndef MAINWINDOW_H\n" + "#define MAINWINDOW_H\n" + "\n" + "#include <QMainWindow>\n" + "\n" + "namespace Ui {\n" + " class MainWindow;\n" + "}\n" + "\n" + "class MainWindow : public QMainWindow\n" // line 10 + "{\n" + " Q_OBJECT\n" + "\n" + "public:\n" // line 14 + " explicit MainWindow(QWidget *parent = 0);\n" + " ~MainWindow();\n" + "\n" + "private:\n" // line 18 + " Ui::MainWindow *ui;\n" + "};\n" + "\n" + "#endif // MAINWINDOW_H\n"; + + Document::Ptr doc = Document::create("qtdesigner_integration"); + doc->setSource(src); + doc->parse(); + doc->check(); + + QCOMPARE(doc->diagnosticMessages().size(), 0); + QCOMPARE(doc->globalSymbolCount(), 2U); + + Class *mainWindow = doc->globalSymbolAt(1)->asClass(); + QVERIFY(mainWindow); + QCOMPARE(mainWindow->line(), 10U); + QCOMPARE(mainWindow->column(), 7U); + + InsertionPointLocator find(doc); + InsertionLocation loc = find.methodDeclarationInClass(mainWindow, InsertionPointLocator::PrivateSlot); + QVERIFY(loc.isValid()); + QCOMPARE(loc.prefix(), QLatin1String("private slots:\n")); + QCOMPARE(loc.suffix(), QLatin1String("\n")); + QCOMPARE(loc.line(), 18U); + QCOMPARE(loc.column(), 1U); +} + +QTEST_APPLESS_MAIN(tst_Codegen) +#include "tst_codegen.moc" diff --git a/tests/auto/cplusplus/cplusplus.pro b/tests/auto/cplusplus/cplusplus.pro index 947a140b4e0..c5d9930c7f1 100644 --- a/tests/auto/cplusplus/cplusplus.pro +++ b/tests/auto/cplusplus/cplusplus.pro @@ -1,3 +1,13 @@ TEMPLATE = subdirs -SUBDIRS = shared ast semantic lookup preprocessor findusages typeprettyprinter codeformatter CONFIG += ordered + +SUBDIRS = \ + shared \ + ast \ + semantic \ + lookup \ + preprocessor \ + findusages \ + typeprettyprinter \ + codeformatter \ + codegen -- GitLab