From f3186690bd39020c85cdee5a5ea6996011ad7158 Mon Sep 17 00:00:00 2001 From: Nikolai Kosjar Date: Thu, 6 Jun 2013 09:27:28 +0200 Subject: [PATCH] CppEditor: Improve finding position for new includes ...by detecting include groups (separated by new lines, include types and same dir prefix). Task-number: QTCREATORBUG-9317 Change-Id: I73e80fdc715104901cb2d4f5b15b4cab5d04d305 Reviewed-by: Erik Verbruggen --- src/plugins/cppeditor/cppeditorplugin.h | 18 +- src/plugins/cppeditor/cppquickfix_test.cpp | 788 ++++++++++++++---- src/plugins/cppeditor/cppquickfixes.cpp | 163 +--- src/plugins/cppeditor/cppquickfixes.h | 13 + src/plugins/cpptools/cppmodelmanager.h | 6 + src/plugins/cpptools/cpptools.pro | 6 +- src/plugins/cpptools/cpptools.qbs | 4 +- src/plugins/cpptools/includeutils.cpp | 495 +++++++++++ src/plugins/cpptools/includeutils.h | 121 +++ .../data/include-data/global/QDebug | 1 + .../data/include-data/global/QDir | 1 + .../data/include-data/global/QString | 1 + .../data/include-data/global/except | 1 + .../data/include-data/global/global.h | 1 + .../data/include-data/global/iostream | 1 + .../data/include-data/global/lib/file.h | 1 + .../data/include-data/global/lib/fileother.h | 1 + .../data/include-data/global/otherlib/file.h | 1 + .../include-data/global/otherlib/fileother.h | 1 + .../data/include-data/global/string | 1 + .../data/include-data/global/utils/file.h | 1 + .../preprocessor/data/include-data/local/a.h | 1 + .../preprocessor/data/include-data/local/b.h | 1 + .../data/include-data/local/file.h | 1 + .../data/include-data/local/fileother.h | 1 + .../data/include-data/local/header.h | 1 + .../data/include-data/local/lib/file.h | 1 + .../data/include-data/local/lib/fileother.h | 1 + .../data/include-data/local/otherlib/file.h | 1 + .../include-data/local/otherlib/fileother.h | 1 + .../data/include-data/local/prefixa.h | 1 + .../data/include-data/local/prefixb.h | 1 + .../data/include-data/local/prefixc.h | 1 + .../data/include-data/local/stuff | 1 + .../data/include-data/local/utils/utils.h | 1 + .../preprocessor/data/include-data/local/y.h | 1 + .../preprocessor/data/include-data/local/z.h | 1 + 37 files changed, 1346 insertions(+), 296 deletions(-) create mode 100644 src/plugins/cpptools/includeutils.cpp create mode 100644 src/plugins/cpptools/includeutils.h create mode 100644 tests/auto/cplusplus/preprocessor/data/include-data/global/QDebug create mode 100644 tests/auto/cplusplus/preprocessor/data/include-data/global/QDir create mode 100644 tests/auto/cplusplus/preprocessor/data/include-data/global/QString create mode 100644 tests/auto/cplusplus/preprocessor/data/include-data/global/except create mode 100644 tests/auto/cplusplus/preprocessor/data/include-data/global/global.h create mode 100644 tests/auto/cplusplus/preprocessor/data/include-data/global/iostream create mode 100644 tests/auto/cplusplus/preprocessor/data/include-data/global/lib/file.h create mode 100644 tests/auto/cplusplus/preprocessor/data/include-data/global/lib/fileother.h create mode 100644 tests/auto/cplusplus/preprocessor/data/include-data/global/otherlib/file.h create mode 100644 tests/auto/cplusplus/preprocessor/data/include-data/global/otherlib/fileother.h create mode 100644 tests/auto/cplusplus/preprocessor/data/include-data/global/string create mode 100644 tests/auto/cplusplus/preprocessor/data/include-data/global/utils/file.h create mode 100644 tests/auto/cplusplus/preprocessor/data/include-data/local/a.h create mode 100644 tests/auto/cplusplus/preprocessor/data/include-data/local/b.h create mode 100644 tests/auto/cplusplus/preprocessor/data/include-data/local/file.h create mode 100644 tests/auto/cplusplus/preprocessor/data/include-data/local/fileother.h create mode 100644 tests/auto/cplusplus/preprocessor/data/include-data/local/header.h create mode 100644 tests/auto/cplusplus/preprocessor/data/include-data/local/lib/file.h create mode 100644 tests/auto/cplusplus/preprocessor/data/include-data/local/lib/fileother.h create mode 100644 tests/auto/cplusplus/preprocessor/data/include-data/local/otherlib/file.h create mode 100644 tests/auto/cplusplus/preprocessor/data/include-data/local/otherlib/fileother.h create mode 100644 tests/auto/cplusplus/preprocessor/data/include-data/local/prefixa.h create mode 100644 tests/auto/cplusplus/preprocessor/data/include-data/local/prefixb.h create mode 100644 tests/auto/cplusplus/preprocessor/data/include-data/local/prefixc.h create mode 100644 tests/auto/cplusplus/preprocessor/data/include-data/local/stuff create mode 100644 tests/auto/cplusplus/preprocessor/data/include-data/local/utils/utils.h create mode 100644 tests/auto/cplusplus/preprocessor/data/include-data/local/y.h create mode 100644 tests/auto/cplusplus/preprocessor/data/include-data/local/z.h diff --git a/src/plugins/cppeditor/cppeditorplugin.h b/src/plugins/cppeditor/cppeditorplugin.h index 9f055fadcf0..a18c528e2a9 100644 --- a/src/plugins/cppeditor/cppeditorplugin.h +++ b/src/plugins/cppeditor/cppeditorplugin.h @@ -162,14 +162,28 @@ private slots: void test_quickfix_InsertDeclFromDef(); + void test_quickfix_AddIncludeForUndefinedIdentifier_detectIncludeGroupsByNewLines(); + void test_quickfix_AddIncludeForUndefinedIdentifier_detectIncludeGroupsByIncludeDir(); + void test_quickfix_AddIncludeForUndefinedIdentifier_detectIncludeGroupsByIncludeType(); void test_quickfix_AddIncludeForUndefinedIdentifier_normal(); void test_quickfix_AddIncludeForUndefinedIdentifier_ignoremoc(); void test_quickfix_AddIncludeForUndefinedIdentifier_sortingTop(); void test_quickfix_AddIncludeForUndefinedIdentifier_sortingMiddle(); void test_quickfix_AddIncludeForUndefinedIdentifier_sortingBottom(); + void test_quickfix_AddIncludeForUndefinedIdentifier_appendToUnsorted(); + void test_quickfix_AddIncludeForUndefinedIdentifier_firstLocalIncludeAtFront(); + void test_quickfix_AddIncludeForUndefinedIdentifier_firstGlobalIncludeAtBack(); + void test_quickfix_AddIncludeForUndefinedIdentifier_preferGroupWithLongerMatchingPrefix(); + void test_quickfix_AddIncludeForUndefinedIdentifier_newGroupIfOnlyDifferentIncludeDirs(); + void test_quickfix_AddIncludeForUndefinedIdentifier_mixedDirsSorted(); + void test_quickfix_AddIncludeForUndefinedIdentifier_mixedDirsUnsorted(); + void test_quickfix_AddIncludeForUndefinedIdentifier_mixedIncludeTypes1(); + void test_quickfix_AddIncludeForUndefinedIdentifier_mixedIncludeTypes2(); + void test_quickfix_AddIncludeForUndefinedIdentifier_mixedIncludeTypes3(); + void test_quickfix_AddIncludeForUndefinedIdentifier_mixedIncludeTypes4(); void test_quickfix_AddIncludeForUndefinedIdentifier_noinclude(); - void test_quickfix_AddIncludeForUndefinedIdentifier_noincludeComment01(); - void test_quickfix_AddIncludeForUndefinedIdentifier_noincludeComment02(); + void test_quickfix_AddIncludeForUndefinedIdentifier_veryFirstIncludeCppStyleCommentOnTop(); + void test_quickfix_AddIncludeForUndefinedIdentifier_veryFirstIncludeCStyleCommentOnTop(); void test_quickfix_MoveFuncDefOutside_MemberFuncToCpp(); void test_quickfix_MoveFuncDefOutside_MemberFuncOutside(); diff --git a/src/plugins/cppeditor/cppquickfix_test.cpp b/src/plugins/cppeditor/cppquickfix_test.cpp index 9cc4f67a9b3..af92494afe8 100644 --- a/src/plugins/cppeditor/cppquickfix_test.cpp +++ b/src/plugins/cppeditor/cppquickfix_test.cpp @@ -33,7 +33,10 @@ #include "cppquickfixes.h" #include +#include +#include #include +#include #include @@ -41,16 +44,16 @@ #include #include - /*! Tests for quick-fixes. */ +using namespace Core; using namespace CPlusPlus; using namespace CppEditor; using namespace CppEditor::Internal; using namespace CppTools; +using namespace IncludeUtils; using namespace TextEditor; -using namespace Core; namespace { @@ -91,9 +94,9 @@ public: QString filePath() const { - if (directoryPath.isEmpty()) - qDebug() << "Warning: No directoryPath set!"; - return directoryPath + QLatin1Char('/') + fileName; + if (!QFileInfo(fileName).isAbsolute()) + return QDir::tempPath() + QLatin1Char('/') + fileName; + return fileName; } void writeToDisk() const @@ -107,7 +110,6 @@ public: QByteArray expectedSource; const QString fileName; - QString directoryPath; const int cursorMarkerPosition; CPPEditor *editor; @@ -118,15 +120,13 @@ public: * Encapsulates the whole process of setting up an editor, getting the * quick-fix, applying it, and checking the result. */ -struct TestCase +class TestCase { - QList testFiles; - - CppCodeStylePreferences *cppCodeStylePreferences; - QString cppCodeStylePreferencesOriginalDelegateId; - - TestCase(const QByteArray &originalSource, const QByteArray &expectedSource); - TestCase(const QList theTestFiles); +public: + TestCase(const QByteArray &originalSource, const QByteArray &expectedSource, + const QStringList &includePaths = QStringList()); + TestCase(const QList theTestFiles, + const QStringList &includePaths = QStringList()); ~TestCase(); QuickFixOperation::Ptr getFix(CppQuickFixFactory *factory, CPPEditorWidget *editorWidget, @@ -139,7 +139,16 @@ private: TestCase(const TestCase &); TestCase &operator=(const TestCase &); - void init(); + void init(const QStringList &includePaths); + +private: + QList testFiles; + + CppCodeStylePreferences *cppCodeStylePreferences; + QString cppCodeStylePreferencesOriginalDelegateId; + + QStringList includePathsToRestore; + bool restoreIncludePaths; }; /// Apply the factory on the source and get back the resultIndex'th result or a null pointer. @@ -153,21 +162,22 @@ QuickFixOperation::Ptr TestCase::getFix(CppQuickFixFactory *factory, CPPEditorWi } /// The '@' in the originalSource is the position from where the quick-fix discovery is triggered. -TestCase::TestCase(const QByteArray &originalSource, const QByteArray &expectedSource) - : cppCodeStylePreferences(0) +TestCase::TestCase(const QByteArray &originalSource, const QByteArray &expectedSource, + const QStringList &includePaths) + : cppCodeStylePreferences(0), restoreIncludePaths(false) { testFiles << TestDocument::create(originalSource, expectedSource, QLatin1String("file.cpp")); - init(); + init(includePaths); } /// Exactly one TestFile must contain the cursor position marker '@' in the originalSource. -TestCase::TestCase(const QList theTestFiles) - : testFiles(theTestFiles), cppCodeStylePreferences(0) +TestCase::TestCase(const QList theTestFiles, const QStringList &includePaths) + : testFiles(theTestFiles), cppCodeStylePreferences(0), restoreIncludePaths(false) { - init(); + init(includePaths); } -void TestCase::init() +void TestCase::init(const QStringList &includePaths) { // Check if there is exactly one cursor marker unsigned cursorMarkersCount = 0; @@ -178,22 +188,28 @@ void TestCase::init() QVERIFY2(cursorMarkersCount == 1, "Exactly one cursor marker is allowed."); // Write files to disk - const QString directoryPath = QDir::tempPath(); - foreach (TestDocumentPtr testFile, testFiles) { - testFile->directoryPath = directoryPath; + foreach (TestDocumentPtr testFile, testFiles) testFile->writeToDisk(); + + CppTools::Internal::CppModelManager *cmm = CppTools::Internal::CppModelManager::instance(); + + // Set appropriate include paths + if (!includePaths.isEmpty()) { + restoreIncludePaths = true; + includePathsToRestore = cmm->includePaths(); + cmm->setIncludePaths(includePaths); } // Update Code Model QStringList filePaths; foreach (const TestDocumentPtr &testFile, testFiles) filePaths << testFile->filePath(); - CppTools::CppModelManagerInterface::instance()->updateSourceFiles(filePaths); + cmm->updateSourceFiles(filePaths); // Wait for the parser in the future to give us the document QStringList filePathsNotYetInSnapshot(filePaths); forever { - Snapshot snapshot = CppTools::CppModelManagerInterface::instance()->snapshot(); + Snapshot snapshot = cmm->snapshot(); foreach (const QString &filePath, filePathsNotYetInSnapshot) { if (snapshot.contains(filePath)) filePathsNotYetInSnapshot.removeOne(filePath); @@ -248,9 +264,17 @@ TestCase::~TestCase() QCoreApplication::processEvents(); // process any pending events // Remove the test files from the code-model - CppModelManagerInterface *mmi = CppTools::CppModelManagerInterface::instance(); + CppModelManagerInterface *mmi = CppModelManagerInterface::instance(); mmi->GC(); QCOMPARE(mmi->snapshot().size(), 0); + + // Restore include paths + if (restoreIncludePaths) + CppTools::Internal::CppModelManager::instance()->setIncludePaths(includePathsToRestore); + + // Remove created files from file system + foreach (const TestDocumentPtr &testDocument, testFiles) + QVERIFY(QFile::remove(testDocument->filePath())); } /// Leading whitespace is not removed, so we can check if the indetation ranges @@ -303,6 +327,37 @@ void TestCase::run(CppQuickFixFactory *factory, int resultIndex) } } +/// Delegates directly to AddIncludeForUndefinedIdentifierOp for easier testing. +class AddIncludeForUndefinedIdentifierTestFactory : public CppQuickFixFactory +{ +public: + AddIncludeForUndefinedIdentifierTestFactory(const QString &include) + : m_include(include) {} + + void match(const CppQuickFixInterface &interface, QuickFixOperations &result) + { + result += CppQuickFixOperation::Ptr( + new AddIncludeForUndefinedIdentifierOp(interface, 0, m_include)); + } + +private: + const QString m_include; +}; + +QString globalIncludePath() +{ + const QString path = QLatin1String(SRCDIR) + + QLatin1String("/../../../tests/auto/cplusplus/preprocessor/data/include-data/global"); + return QDir::cleanPath(path); +} + +QString directoryOfTestFile() +{ + const QString path = QLatin1String(SRCDIR) + + QLatin1String("/../../../tests/auto/cplusplus/preprocessor/data/include-data/local"); + return QDir::cleanPath(path); +} + } // anonymous namespace /// Checks: @@ -975,7 +1030,165 @@ void CppEditorPlugin::test_quickfix_InsertDeclFromDef() insertToSectionDeclFromDef("private slots", 5); } -/// Check normal add include if there is already a include +QList includesForSource(const QByteArray &source) +{ + const QString fileName = directoryOfTestFile() + QLatin1String("/file.cpp"); + Utils::FileSaver srcSaver(fileName); + srcSaver.write(source); + srcSaver.finalize(); + + using namespace CppTools::Internal; + + CppModelManager *cmm = CppModelManager::instance(); + cmm->GC(); + CppPreprocessor pp((QPointer(cmm))); + pp.setIncludePaths(QStringList(globalIncludePath())); + pp.run(fileName); + + Document::Ptr document = cmm->snapshot().document(fileName); + return document->includes(); +} + +/// Check: Detection of include groups separated by new lines +void CppEditorPlugin::test_quickfix_AddIncludeForUndefinedIdentifier_detectIncludeGroupsByNewLines() +{ + // Source referencing those files + QByteArray source = + "#include \"header.h\"\n" + "\n" + "#include \"file.h\"\n" + "#include \"fileother.h\"\n" + "\n" + "#include \n" + "#include \n" + "\n" + "#include \"otherlib/file.h\"\n" + "#include \"otherlib/fileother.h\"\n" + "\n" + "#include \"utils/utils.h\"\n" + "\n" + "#include \n" + "#include \n" + "#include \n" + "\n" + "#include \n" + "#include \n" + "#include \n" + "\n" + "#include \n" + "#include \"stuff\"\n" + "#include \n" + "\n" + ; + + QList includes = includesForSource(source); + QCOMPARE(includes.size(), 17); + QList includeGroups + = IncludeGroup::detectIncludeGroupsByNewLines(includes); + QCOMPARE(includeGroups.size(), 8); + + QCOMPARE(includeGroups.at(0).size(), 1); + QVERIFY(includeGroups.at(0).commonPrefix().isEmpty()); + QVERIFY(includeGroups.at(0).hasOnlyIncludesOfType(Client::IncludeLocal)); + QVERIFY(includeGroups.at(0).isSorted()); + + QCOMPARE(includeGroups.at(1).size(), 2); + QVERIFY(!includeGroups.at(1).commonPrefix().isEmpty()); + QVERIFY(includeGroups.at(1).hasOnlyIncludesOfType(Client::IncludeLocal)); + QVERIFY(includeGroups.at(1).isSorted()); + + QCOMPARE(includeGroups.at(2).size(), 2); + QVERIFY(!includeGroups.at(2).commonPrefix().isEmpty()); + QVERIFY(includeGroups.at(2).hasOnlyIncludesOfType(Client::IncludeGlobal)); + QVERIFY(!includeGroups.at(2).isSorted()); + + QCOMPARE(includeGroups.at(6).size(), 3); + QVERIFY(includeGroups.at(6).commonPrefix().isEmpty()); + QVERIFY(includeGroups.at(6).hasOnlyIncludesOfType(Client::IncludeGlobal)); + QVERIFY(!includeGroups.at(6).isSorted()); + + QCOMPARE(includeGroups.at(7).size(), 3); + QVERIFY(includeGroups.at(7).commonPrefix().isEmpty()); + QVERIFY(!includeGroups.at(7).hasOnlyIncludesOfType(Client::IncludeLocal)); + QVERIFY(!includeGroups.at(7).hasOnlyIncludesOfType(Client::IncludeGlobal)); + QVERIFY(!includeGroups.at(7).isSorted()); + + QCOMPARE(IncludeGroup::filterIncludeGroups(includeGroups, Client::IncludeLocal).size(), 4); + QCOMPARE(IncludeGroup::filterIncludeGroups(includeGroups, Client::IncludeGlobal).size(), 3); + QCOMPARE(IncludeGroup::filterMixedIncludeGroups(includeGroups).size(), 1); +} + +/// Check: Detection of include groups separated by include dirs +void CppEditorPlugin::test_quickfix_AddIncludeForUndefinedIdentifier_detectIncludeGroupsByIncludeDir() +{ + QByteArray source = + "#include \"file.h\"\n" + "#include \"fileother.h\"\n" + "#include \n" + "#include \n" + "#include \"otherlib/file.h\"\n" + "#include \"otherlib/fileother.h\"\n" + "#include \n" + "#include \n" + "#include \n" + "\n" + ; + + QList includes = includesForSource(source); + QCOMPARE(includes.size(), 9); + QList includeGroups + = IncludeGroup::detectIncludeGroupsByIncludeDir(includes); + QCOMPARE(includeGroups.size(), 4); + + QCOMPARE(includeGroups.at(0).size(), 2); + QVERIFY(includeGroups.at(0).commonIncludeDir().isEmpty()); + + QCOMPARE(includeGroups.at(1).size(), 2); + QCOMPARE(includeGroups.at(1).commonIncludeDir(), QLatin1String("lib/")); + + QCOMPARE(includeGroups.at(2).size(), 2); + QCOMPARE(includeGroups.at(2).commonIncludeDir(), QLatin1String("otherlib/")); + + QCOMPARE(includeGroups.at(3).size(), 3); + QCOMPARE(includeGroups.at(3).commonIncludeDir(), QLatin1String("")); +} + +/// Check: Detection of include groups separated by include types +void CppEditorPlugin::test_quickfix_AddIncludeForUndefinedIdentifier_detectIncludeGroupsByIncludeType() +{ + QByteArray source = + "#include \"file.h\"\n" + "#include \"fileother.h\"\n" + "#include \n" + "#include \n" + "#include \"otherlib/file.h\"\n" + "#include \"otherlib/fileother.h\"\n" + "#include \n" + "#include \n" + "#include \n" + "\n" + ; + + QList includes = includesForSource(source); + QCOMPARE(includes.size(), 9); + QList includeGroups + = IncludeGroup::detectIncludeGroupsByIncludeDir(includes); + QCOMPARE(includeGroups.size(), 4); + + QCOMPARE(includeGroups.at(0).size(), 2); + QVERIFY(includeGroups.at(0).hasOnlyIncludesOfType(Client::IncludeLocal)); + + QCOMPARE(includeGroups.at(1).size(), 2); + QVERIFY(includeGroups.at(1).hasOnlyIncludesOfType(Client::IncludeGlobal)); + + QCOMPARE(includeGroups.at(2).size(), 2); + QVERIFY(includeGroups.at(2).hasOnlyIncludesOfType(Client::IncludeLocal)); + + QCOMPARE(includeGroups.at(3).size(), 3); + QVERIFY(includeGroups.at(3).hasOnlyIncludesOfType(Client::IncludeGlobal)); +} + +/// Check: Add include if there is already an include void CppEditorPlugin::test_quickfix_AddIncludeForUndefinedIdentifier_normal() { QList testFiles; @@ -986,11 +1199,12 @@ void CppEditorPlugin::test_quickfix_AddIncludeForUndefinedIdentifier_normal() // Header File original = "class Foo {};\n"; expected = original + "\n"; - testFiles << TestDocument::create(original, expected, QLatin1String("file.h")); + testFiles << TestDocument::create(original, expected, directoryOfTestFile() + QLatin1Char('/') + + QLatin1String("afile.h")); // Source File original = - "#include \"someheader.h\"\n" + "#include \"header.h\"\n" "\n" "void f()\n" "{\n" @@ -998,8 +1212,8 @@ void CppEditorPlugin::test_quickfix_AddIncludeForUndefinedIdentifier_normal() "}\n" ; expected = - "#include \"file.h\"\n" - "#include \"someheader.h\"\n" + "#include \"afile.h\"\n" + "#include \"header.h\"\n" "\n" "void f()\n" "{\n" @@ -1007,14 +1221,16 @@ void CppEditorPlugin::test_quickfix_AddIncludeForUndefinedIdentifier_normal() "}\n" "\n" ; - testFiles << TestDocument::create(original, expected, QLatin1String("file.cpp")); + testFiles << TestDocument::create(original, expected, directoryOfTestFile() + QLatin1Char('/') + + QLatin1String("afile.cpp")); + // Do not use the test factory, at least once we want to go through the "full stack". AddIncludeForUndefinedIdentifier factory; - TestCase data(testFiles); + TestCase data(testFiles, QStringList(globalIncludePath())); data.run(&factory); } -/// Check add include, ignoring any moc includes. +/// Check: Ignore *.moc includes void CppEditorPlugin::test_quickfix_AddIncludeForUndefinedIdentifier_ignoremoc() { QList testFiles; @@ -1022,37 +1238,26 @@ void CppEditorPlugin::test_quickfix_AddIncludeForUndefinedIdentifier_ignoremoc() QByteArray original; QByteArray expected; - // Header File - original = "class Foo {};\n"; - expected = original + "\n"; - testFiles << TestDocument::create(original, expected, QLatin1String("file.h")); - - // Source File original = - "void f()\n" - "{\n" - " Fo@o foo;\n" - "}\n" + "void @f();\n" "#include \"file.moc\";\n" ; expected = "#include \"file.h\"\n" "\n" - "void f()\n" - "{\n" - " Foo foo;\n" - "}\n" + "void f();\n" "#include \"file.moc\";\n" "\n" ; - testFiles << TestDocument::create(original, expected, QLatin1String("file.cpp")); + testFiles << TestDocument::create(original, expected, directoryOfTestFile() + QLatin1Char('/') + + QLatin1String("file.cpp")); - AddIncludeForUndefinedIdentifier factory; - TestCase data(testFiles); + AddIncludeForUndefinedIdentifierTestFactory factory(QLatin1String("\"file.h\"")); + TestCase data(testFiles, QStringList(globalIncludePath())); data.run(&factory); } -/// Check add include sorting top +/// Check: Insert include at top for a sorted group void CppEditorPlugin::test_quickfix_AddIncludeForUndefinedIdentifier_sortingTop() { QList testFiles; @@ -1060,40 +1265,26 @@ void CppEditorPlugin::test_quickfix_AddIncludeForUndefinedIdentifier_sortingTop( QByteArray original; QByteArray expected; - // Header File - original = "class Foo {};\n"; - expected = original + "\n"; - testFiles << TestDocument::create(original, expected, QLatin1String("file.h")); - - // Source File original = "#include \"y.h\"\n" "#include \"z.h\"\n" - "void f()\n" - "{\n" - " Fo@o foo;\n" - "}\n" - "#include \"file.moc\";\n" + "\n@" ; expected = "#include \"file.h\"\n" "#include \"y.h\"\n" "#include \"z.h\"\n" - "void f()\n" - "{\n" - " Foo foo;\n" - "}\n" - "#include \"file.moc\";\n" - "\n" + "\n\n" ; - testFiles << TestDocument::create(original, expected, QLatin1String("file.cpp")); + testFiles << TestDocument::create(original, expected, directoryOfTestFile() + QLatin1Char('/') + + QLatin1String("file.cpp")); - AddIncludeForUndefinedIdentifier factory; - TestCase data(testFiles); + AddIncludeForUndefinedIdentifierTestFactory factory(QLatin1String("\"file.h\"")); + TestCase data(testFiles, QStringList(globalIncludePath())); data.run(&factory); } -/// Check add include sorting middle +/// Check: Insert include in the middle for a sorted group void CppEditorPlugin::test_quickfix_AddIncludeForUndefinedIdentifier_sortingMiddle() { QList testFiles; @@ -1101,42 +1292,26 @@ void CppEditorPlugin::test_quickfix_AddIncludeForUndefinedIdentifier_sortingMidd QByteArray original; QByteArray expected; - // Header File - original = "class Foo {};\n"; - expected = original + "\n"; - testFiles << TestDocument::create(original, expected, QLatin1String("file.h")); - - // Source File original = "#include \"a.h\"\n" "#include \"z.h\"\n" - "void f()\n" - "{\n" - " Fo@o foo;\n" - "}\n" - "#include \"file.moc\";\n" + "\n@" ; expected = "#include \"a.h\"\n" "#include \"file.h\"\n" "#include \"z.h\"\n" - "void f()\n" - "{\n" - " Foo foo;\n" - "}\n" - "#include \"file.moc\";\n" - "\n" + "\n\n" ; - testFiles << TestDocument::create(original, expected, QLatin1String("file.cpp")); + testFiles << TestDocument::create(original, expected, directoryOfTestFile() + QLatin1Char('/') + + QLatin1String("file.cpp")); - AddIncludeForUndefinedIdentifier factory; - TestCase data(testFiles); + AddIncludeForUndefinedIdentifierTestFactory factory(QLatin1String("\"file.h\"")); + TestCase data(testFiles, QStringList(globalIncludePath())); data.run(&factory); } - - -/// Check add include sorting bottom +/// Check: Insert include at bottom for a sorted group void CppEditorPlugin::test_quickfix_AddIncludeForUndefinedIdentifier_sortingBottom() { QList testFiles; @@ -1144,40 +1319,337 @@ void CppEditorPlugin::test_quickfix_AddIncludeForUndefinedIdentifier_sortingBott QByteArray original; QByteArray expected; - // Header File - original = "class Foo {};\n"; - expected = original + "\n"; - testFiles << TestDocument::create(original, expected, QLatin1String("file.h")); - - // Source File original = "#include \"a.h\"\n" "#include \"b.h\"\n" - "void f()\n" - "{\n" - " Fo@o foo;\n" - "}\n" - "#include \"file.moc\";\n" + "\n@" ; expected = "#include \"a.h\"\n" "#include \"b.h\"\n" "#include \"file.h\"\n" - "void f()\n" - "{\n" - " Foo foo;\n" - "}\n" - "#include \"file.moc\";\n" - "\n" + "\n\n" ; - testFiles << TestDocument::create(original, expected, QLatin1String("file.cpp")); + testFiles << TestDocument::create(original, expected, directoryOfTestFile() + QLatin1Char('/') + + QLatin1String("file.cpp")); - AddIncludeForUndefinedIdentifier factory; - TestCase data(testFiles); + AddIncludeForUndefinedIdentifierTestFactory factory(QLatin1String("\"file.h\"")); + TestCase data(testFiles, QStringList(globalIncludePath())); data.run(&factory); } -/// Check add include if no include is present +/// Check: For an unsorted group the new include is appended +void CppEditorPlugin::test_quickfix_AddIncludeForUndefinedIdentifier_appendToUnsorted() +{ + QList testFiles; + + QByteArray original; + QByteArray expected; + + original = + "#include \"b.h\"\n" + "#include \"a.h\"\n" + "\n@" + ; + expected = + "#include \"b.h\"\n" + "#include \"a.h\"\n" + "#include \"file.h\"\n" + "\n\n" + ; + testFiles << TestDocument::create(original, expected, directoryOfTestFile() + QLatin1Char('/') + + QLatin1String("file.cpp")); + + AddIncludeForUndefinedIdentifierTestFactory factory(QLatin1String("\"file.h\"")); + TestCase data(testFiles, QStringList(globalIncludePath())); + data.run(&factory); +} + +/// Check: Insert a local include at front if there are only global includes +void CppEditorPlugin::test_quickfix_AddIncludeForUndefinedIdentifier_firstLocalIncludeAtFront() +{ + QList testFiles; + + QByteArray original; + QByteArray expected; + + original = + "#include \n" + "#include \n" + "\n@" + ; + expected = + "#include \"file.h\"\n" + "\n" + "#include \n" + "#include \n" + "\n\n" + ; + testFiles << TestDocument::create(original, expected, directoryOfTestFile() + QLatin1Char('/') + + QLatin1String("file.cpp")); + + AddIncludeForUndefinedIdentifierTestFactory factory(QLatin1String("\"file.h\"")); + TestCase data(testFiles, QStringList(globalIncludePath())); + data.run(&factory); +} + +/// Check: Insert a global include at back if there are only local includes +void CppEditorPlugin::test_quickfix_AddIncludeForUndefinedIdentifier_firstGlobalIncludeAtBack() +{ + QList testFiles; + + QByteArray original; + QByteArray expected; + + original = + "#include \"a.h\"\n" + "#include \"b.h\"\n" + "\n" + "void @f();\n" + ; + expected = + "#include \"a.h\"\n" + "#include \"b.h\"\n" + "\n" + "#include \n" + "\n" + "void f();\n" + "\n" + ; + testFiles << TestDocument::create(original, expected, directoryOfTestFile() + QLatin1Char('/') + + QLatin1String("file.cpp")); + + AddIncludeForUndefinedIdentifierTestFactory factory(QLatin1String("")); + TestCase data(testFiles, QStringList(globalIncludePath())); + data.run(&factory); +} + +/// Check: Prefer group with longest matching prefix +void CppEditorPlugin::test_quickfix_AddIncludeForUndefinedIdentifier_preferGroupWithLongerMatchingPrefix() +{ + QList testFiles; + + QByteArray original; + QByteArray expected; + + original = + "#include \"prefixa.h\"\n" + "#include \"prefixb.h\"\n" + "\n" + "#include \"foo.h\"\n" + "\n@" + ; + expected = + "#include \"prefixa.h\"\n" + "#include \"prefixb.h\"\n" + "#include \"prefixc.h\"\n" + "\n" + "#include \"foo.h\"\n" + "\n\n" + ; + testFiles << TestDocument::create(original, expected, directoryOfTestFile() + QLatin1Char('/') + + QLatin1String("file.cpp")); + + AddIncludeForUndefinedIdentifierTestFactory factory(QLatin1String("\"prefixc.h\"")); + TestCase data(testFiles, QStringList(globalIncludePath())); + data.run(&factory); +} + +/// Check: Create a new include group if there are only include groups with a different include dir +void CppEditorPlugin::test_quickfix_AddIncludeForUndefinedIdentifier_newGroupIfOnlyDifferentIncludeDirs() +{ + QList testFiles; + + QByteArray original; + QByteArray expected; + + original = + "#include \"lib/file.h\"\n" + "#include \"lib/fileother.h\"\n" + "\n@" + ; + expected = + "#include \"lib/file.h\"\n" + "#include \"lib/fileother.h\"\n" + "\n" + "#include \"file.h\"\n" + "\n\n" + ; + testFiles << TestDocument::create(original, expected, directoryOfTestFile() + QLatin1Char('/') + + QLatin1String("file.cpp")); + + AddIncludeForUndefinedIdentifierTestFactory factory(QLatin1String("\"file.h\"")); + TestCase data(testFiles, QStringList(globalIncludePath())); + data.run(&factory); +} + +/// Check: Include group with mixed include dirs, sorted --> insert properly +void CppEditorPlugin::test_quickfix_AddIncludeForUndefinedIdentifier_mixedDirsSorted() +{ + QList testFiles; + + QByteArray original; + QByteArray expected; + + original = + "#include \n" + "#include \n" + "#include \n" + "\n@" + ; + expected = + "#include \n" + "#include \n" + "#include \n" + "#include \n" + "\n\n" + ; + testFiles << TestDocument::create(original, expected, directoryOfTestFile() + QLatin1Char('/') + + QLatin1String("file.cpp")); + + AddIncludeForUndefinedIdentifierTestFactory factory(QLatin1String("")); + TestCase data(testFiles, QStringList(globalIncludePath())); + data.run(&factory); +} + +/// Check: Include group with mixed include dirs, unsorted --> append +void CppEditorPlugin::test_quickfix_AddIncludeForUndefinedIdentifier_mixedDirsUnsorted() +{ + QList testFiles; + + QByteArray original; + QByteArray expected; + + original = + "#include \n" + "#include \n" + "#include \n" + "\n@" + ; + expected = + "#include \n" + "#include \n" + "#include \n" + "#include \n" + "\n\n" + ; + testFiles << TestDocument::create(original, expected, directoryOfTestFile() + QLatin1Char('/') + + QLatin1String("file.cpp")); + + AddIncludeForUndefinedIdentifierTestFactory factory(QLatin1String("")); + TestCase data(testFiles, QStringList(globalIncludePath())); + data.run(&factory); +} + +/// Check: Include group with mixed include types +void CppEditorPlugin::test_quickfix_AddIncludeForUndefinedIdentifier_mixedIncludeTypes1() +{ + QList testFiles; + + QByteArray original; + QByteArray expected; + + original = + "#include \"a.h\"\n" + "#include \n" + "\n@" + ; + expected = + "#include \"a.h\"\n" + "#include \"z.h\"\n" + "#include \n" + "\n\n" + ; + testFiles << TestDocument::create(original, expected, directoryOfTestFile() + QLatin1Char('/') + + QLatin1String("file.cpp")); + + AddIncludeForUndefinedIdentifierTestFactory factory(QLatin1String("\"z.h\"")); + TestCase data(testFiles, QStringList(globalIncludePath())); + data.run(&factory); +} + +/// Check: Include group with mixed include types +void CppEditorPlugin::test_quickfix_AddIncludeForUndefinedIdentifier_mixedIncludeTypes2() +{ + QList testFiles; + + QByteArray original; + QByteArray expected; + + original = + "#include \"z.h\"\n" + "#include \n" + "\n@" + ; + expected = + "#include \"a.h\"\n" + "#include \"z.h\"\n" + "#include \n" + "\n\n" + ; + testFiles << TestDocument::create(original, expected, directoryOfTestFile() + QLatin1Char('/') + + QLatin1String("file.cpp")); + + AddIncludeForUndefinedIdentifierTestFactory factory(QLatin1String("\"a.h\"")); + TestCase data(testFiles, QStringList(globalIncludePath())); + data.run(&factory); +} + +/// Check: Include group with mixed include types +void CppEditorPlugin::test_quickfix_AddIncludeForUndefinedIdentifier_mixedIncludeTypes3() +{ + QList testFiles; + + QByteArray original; + QByteArray expected; + + original = + "#include \"z.h\"\n" + "#include \n" + "\n@" + ; + expected = + "#include \"z.h\"\n" + "#include \"lib/file.h\"\n" + "#include \n" + "\n\n" + ; + testFiles << TestDocument::create(original, expected, directoryOfTestFile() + QLatin1Char('/') + + QLatin1String("file.cpp")); + + AddIncludeForUndefinedIdentifierTestFactory factory(QLatin1String("\"lib/file.h\"")); + TestCase data(testFiles, QStringList(globalIncludePath())); + data.run(&factory); +} + +/// Check: Include group with mixed include types +void CppEditorPlugin::test_quickfix_AddIncludeForUndefinedIdentifier_mixedIncludeTypes4() +{ + QList testFiles; + + QByteArray original; + QByteArray expected; + + original = + "#include \"z.h\"\n" + "#include \n" + "\n@" + ; + expected = + "#include \"z.h\"\n" + "#include \n" + "#include \n" + "\n\n" + ; + testFiles << TestDocument::create(original, expected, directoryOfTestFile() + QLatin1Char('/') + + QLatin1String("file.cpp")); + + AddIncludeForUndefinedIdentifierTestFactory factory(QLatin1String("")); + TestCase data(testFiles, QStringList(globalIncludePath())); + data.run(&factory); +} + +/// Check: Insert very first include void CppEditorPlugin::test_quickfix_AddIncludeForUndefinedIdentifier_noinclude() { QList testFiles; @@ -1185,56 +1657,36 @@ void CppEditorPlugin::test_quickfix_AddIncludeForUndefinedIdentifier_noinclude() QByteArray original; QByteArray expected; - // Header File - original = "class Foo {};\n"; - expected = original + "\n"; - testFiles << TestDocument::create(original, expected, QLatin1String("file.h")); - - // Source File original = - "void f()\n" - "{\n" - " Fo@o foo;\n" - "}\n" + "void @f();\n" ; expected = "#include \"file.h\"\n" "\n" - "void f()\n" - "{\n" - " Foo foo;\n" - "}\n" + "void f();\n" "\n" ; - testFiles << TestDocument::create(original, expected, QLatin1String("file.cpp")); + testFiles << TestDocument::create(original, expected, directoryOfTestFile() + QLatin1Char('/') + + QLatin1String("file.cpp")); - AddIncludeForUndefinedIdentifier factory; - TestCase data(testFiles); + AddIncludeForUndefinedIdentifierTestFactory factory(QLatin1String("\"file.h\"")); + TestCase data(testFiles, QStringList(globalIncludePath())); data.run(&factory); } -/// Check add include if no include is present with comment on top -void CppEditorPlugin::test_quickfix_AddIncludeForUndefinedIdentifier_noincludeComment01() +/// Check: Insert very first include if there is a c++ style comment on top +void CppEditorPlugin::test_quickfix_AddIncludeForUndefinedIdentifier_veryFirstIncludeCppStyleCommentOnTop() { QList testFiles; QByteArray original; QByteArray expected; - // Header File - original = "class Foo {};\n"; - expected = original + "\n"; - testFiles << TestDocument::create(original, expected, QLatin1String("file.h")); - - // Source File original = "\n" "// comment\n" "\n" - "void f()\n" - "{\n" - " Fo@o foo;\n" - "}\n" + "void @f();\n" ; expected = "\n" @@ -1242,42 +1694,32 @@ void CppEditorPlugin::test_quickfix_AddIncludeForUndefinedIdentifier_noincludeCo "\n" "#include \"file.h\"\n" "\n" - "void f()\n" - "{\n" - " Foo foo;\n" - "}\n" + "void @f();\n" "\n" ; - testFiles << TestDocument::create(original, expected, QLatin1String("file.cpp")); + testFiles << TestDocument::create(original, expected, directoryOfTestFile() + QLatin1Char('/') + + QLatin1String("file.cpp")); - AddIncludeForUndefinedIdentifier factory; - TestCase data(testFiles); + AddIncludeForUndefinedIdentifierTestFactory factory(QLatin1String("\"file.h\"")); + TestCase data(testFiles, QStringList(globalIncludePath())); data.run(&factory); } -/// Check add include if no include is present with comment on top -void CppEditorPlugin::test_quickfix_AddIncludeForUndefinedIdentifier_noincludeComment02() + +/// Check: Insert very first include if there is a c style comment on top +void CppEditorPlugin::test_quickfix_AddIncludeForUndefinedIdentifier_veryFirstIncludeCStyleCommentOnTop() { QList testFiles; QByteArray original; QByteArray expected; - // Header File - original = "class Foo {};\n"; - expected = original + "\n"; - testFiles << TestDocument::create(original, expected, QLatin1String("file.h")); - - // Source File original = "\n" "/*\n" " comment\n" " */\n" "\n" - "void f()\n" - "{\n" - " Fo@o foo;\n" - "}\n" + "void @f();\n" ; expected = "\n" @@ -1287,16 +1729,14 @@ void CppEditorPlugin::test_quickfix_AddIncludeForUndefinedIdentifier_noincludeCo "\n" "#include \"file.h\"\n" "\n" - "void f()\n" - "{\n" - " Foo foo;\n" - "}\n" + "void @f();\n" "\n" ; - testFiles << TestDocument::create(original, expected, QLatin1String("file.cpp")); + testFiles << TestDocument::create(original, expected, directoryOfTestFile() + QLatin1Char('/') + + QLatin1String("file.cpp")); - AddIncludeForUndefinedIdentifier factory; - TestCase data(testFiles); + AddIncludeForUndefinedIdentifierTestFactory factory(QLatin1String("\"file.h\"")); + TestCase data(testFiles, QStringList(globalIncludePath())); data.run(&factory); } diff --git a/src/plugins/cppeditor/cppquickfixes.cpp b/src/plugins/cppeditor/cppquickfixes.cpp index 1d2a38a9c39..d157767609e 100644 --- a/src/plugins/cppeditor/cppquickfixes.cpp +++ b/src/plugins/cppeditor/cppquickfixes.cpp @@ -40,6 +40,7 @@ #include #include #include +#include #include #include @@ -182,6 +183,37 @@ Class *isMemberFunction(const LookupContext &context, Function *function) return 0; } +// Given include is e.g. "afile.h" or (quotes/angle brackets included!). +void insertNewIncludeDirective(const QString &include, CppRefactoringFilePtr file) +{ + // Find optimal position + using namespace IncludeUtils; + LineForNewIncludeDirective finder(file->document(), file->cppDocument()->includes(), + LineForNewIncludeDirective::IgnoreMocIncludes, + LineForNewIncludeDirective::AutoDetect); + unsigned newLinesToPrepend = 0; + unsigned newLinesToAppend = 0; + const int insertLine = finder(include, &newLinesToPrepend, &newLinesToAppend); + QTC_ASSERT(insertLine >= 1, return); + const int insertPosition = file->position(insertLine, 1); + QTC_ASSERT(insertPosition >= 0, return); + + // Construct text to insert + const QString includeLine = QLatin1String("#include ") + include + QLatin1Char('\n'); + QString prependedNewLines, appendedNewLines; + while (newLinesToAppend--) + appendedNewLines += QLatin1String("\n"); + while (newLinesToPrepend--) + prependedNewLines += QLatin1String("\n"); + const QString textToInsert = prependedNewLines + includeLine + appendedNewLines; + + // Insert + ChangeSet changes; + changes.insert(insertPosition, textToInsert); + file->setChangeSet(changes); + file->apply(); +} + } // anonymous namespace namespace { @@ -1479,24 +1511,8 @@ public: if (best.isEmpty()) best = headerFile; - int pos = currentFile->startOf(1); - - unsigned currentLine = currentFile->cursor().blockNumber() + 1; - unsigned bestLine = 0; - foreach (const Document::Include &incl, - assistInterface()->semanticInfo().doc->includes()) { - if (incl.line() < currentLine) - bestLine = incl.line(); - } - - if (bestLine) - pos = currentFile->document()->findBlockByNumber(bestLine).position(); - - ChangeSet changes; - changes.insert(pos, QLatin1String("#include <") - + QFileInfo(best).fileName() + QLatin1String(">\n")); - currentFile->setChangeSet(changes); - currentFile->apply(); + const QString include = QString::fromLatin1("<%1>").arg(QFileInfo(best).fileName()); + insertNewIncludeDirective(include, currentFile); } } @@ -1731,108 +1747,21 @@ void ConvertToCamelCase::match(const CppQuickFixInterface &interface, QuickFixOp } } -namespace { - -class AddIncludeForUndefinedIdentifierOp: public CppQuickFixOperation +AddIncludeForUndefinedIdentifierOp::AddIncludeForUndefinedIdentifierOp( + const CppQuickFixInterface &interface, int priority, const QString &include) + : CppQuickFixOperation(interface, priority) + , m_include(include) { -public: - AddIncludeForUndefinedIdentifierOp(const CppQuickFixInterface &interface, int priority, - const QString &include) - : CppQuickFixOperation(interface, priority) - , m_include(include) - { - setDescription(QApplication::translate("CppTools::QuickFix", - "Add #include %1").arg(m_include)); - } + setDescription(QApplication::translate("CppTools::QuickFix", "Add #include %1").arg(m_include)); +} - void perform() - { - CppRefactoringChanges refactoring(snapshot()); - CppRefactoringFilePtr file = refactoring.file(fileName()); +void AddIncludeForUndefinedIdentifierOp::perform() +{ + CppRefactoringChanges refactoring(snapshot()); + CppRefactoringFilePtr file = refactoring.file(fileName()); - QList includes = file->cppDocument()->includes(); - if (!includes.isEmpty()) { - QHash includePositions; - foreach (const Document::Include &include, includes) { - const QString fileName = include.unresolvedFileName(); - if (fileName.endsWith(QLatin1String(".moc"))) - continue; - includePositions.insert(fileName, include.line()); - } - - if (!includePositions.isEmpty()) { - const QString include = m_include.mid(1, m_include.length() - 2); - QList keys = includePositions.keys(); - keys << include; - qSort(keys); - const int pos = keys.indexOf(include); - - ChangeSet changes; - if (pos + 1 != keys.count()) { - const int insertPos = qMax(0, file->position( - includePositions.value(keys.at(pos + 1)), 1)); - changes.insert(insertPos, - QLatin1String("#include ") + m_include + QLatin1String("\n")); - - } else { - const int insertPos = qMax(0, file->position(includePositions.value( - keys.at(pos - 1)) + 1, 1) - 1); - changes.insert(insertPos, QLatin1String("\n#include ") + m_include); - } - file->setChangeSet(changes); - file->apply(); - return; - } - } - - // No includes or no matching include, find possible first/multi line comment - int insertPos = 0; - QTextBlock block = file->document()->firstBlock(); - while (block.isValid()) { - const QString trimmedText = block.text().trimmed(); - - // Only skip the first comment! - if (trimmedText.startsWith(QLatin1String("/*"))) { - do { - const int pos = block.text().indexOf(QLatin1String("*/")); - if (pos > -1) { - insertPos = block.position() + pos + 2; - break; - } - block = block.next(); - } while (block.isValid()); - break; - } else if (trimmedText.startsWith(QLatin1String("//"))) { - block = block.next(); - while (block.isValid()) { - if (!block.text().trimmed().startsWith(QLatin1String("//"))) { - insertPos = block.position() - 1; - break; - } - block = block.next(); - } - break; - } - - if (!trimmedText.isEmpty()) - break; - block = block.next(); - } - - ChangeSet changes; - if (insertPos != 0) - changes.insert(insertPos, QLatin1String("\n\n#include ") + m_include); - else - changes.insert(insertPos, QString::fromLatin1("#include %1\n\n").arg(m_include)); - file->setChangeSet(changes); - file->apply(); - } - -private: - QString m_include; -}; - -} // anonymous namespace + insertNewIncludeDirective(m_include, file); +} void AddIncludeForUndefinedIdentifier::match(const CppQuickFixInterface &interface, QuickFixOperations &result) diff --git a/src/plugins/cppeditor/cppquickfixes.h b/src/plugins/cppeditor/cppquickfixes.h index 63ba308ef3f..fa9c7cc97dc 100644 --- a/src/plugins/cppeditor/cppquickfixes.h +++ b/src/plugins/cppeditor/cppquickfixes.h @@ -71,6 +71,18 @@ public: void match(const CppQuickFixInterface &interface, QuickFixOperations &result); }; +// Exposed for tests +class AddIncludeForUndefinedIdentifierOp: public CppQuickFixOperation +{ +public: + AddIncludeForUndefinedIdentifierOp(const CppQuickFixInterface &interface, int priority, + const QString &include); + void perform(); + +private: + QString m_include; +}; + /*! Can be triggered on a class forward declaration to add the matching #include. @@ -493,6 +505,7 @@ public: /*! Insert (pure) virtual functions of a base class. + Exposed for tests. */ class InsertVirtualMethodsDialog : public QDialog { diff --git a/src/plugins/cpptools/cppmodelmanager.h b/src/plugins/cpptools/cppmodelmanager.h index 08328b316bc..7691daeab32 100644 --- a/src/plugins/cpptools/cppmodelmanager.h +++ b/src/plugins/cpptools/cppmodelmanager.h @@ -124,6 +124,12 @@ public: return m_includePaths; } + // Use this *only* for auto tests + void setIncludePaths(const QStringList &includePaths) + { + m_includePaths = includePaths; + } + QStringList frameworkPaths() { ensureUpdated(); diff --git a/src/plugins/cpptools/cpptools.pro b/src/plugins/cpptools/cpptools.pro index efeff55f250..89415fdea7c 100644 --- a/src/plugins/cpptools/cpptools.pro +++ b/src/plugins/cpptools/cpptools.pro @@ -45,7 +45,8 @@ HEADERS += completionsettingspage.h \ builtinindexingsupport.h \ cpppointerdeclarationformatter.h \ cppprojectfile.h \ - cpppreprocessor.h + cpppreprocessor.h \ + includeutils.h SOURCES += completionsettingspage.cpp \ cppclassesfilter.cpp \ @@ -89,7 +90,8 @@ SOURCES += completionsettingspage.cpp \ builtinindexingsupport.cpp \ cpppointerdeclarationformatter.cpp \ cppprojectfile.cpp \ - cpppreprocessor.cpp + cpppreprocessor.cpp \ + includeutils.cpp FORMS += completionsettingspage.ui \ cppfilesettingspage.ui \ diff --git a/src/plugins/cpptools/cpptools.qbs b/src/plugins/cpptools/cpptools.qbs index 192f02d9d5b..2275812162f 100644 --- a/src/plugins/cpptools/cpptools.qbs +++ b/src/plugins/cpptools/cpptools.qbs @@ -107,7 +107,9 @@ QtcPlugin { "builtinindexingsupport.cpp", "builtinindexingsupport.h", "cpppreprocessor.cpp", - "cpppreprocessor.h" + "cpppreprocessor.h", + "includeutils.cpp", + "includeutils.h" ] Group { diff --git a/src/plugins/cpptools/includeutils.cpp b/src/plugins/cpptools/includeutils.cpp new file mode 100644 index 00000000000..da0e3ce3db8 --- /dev/null +++ b/src/plugins/cpptools/includeutils.cpp @@ -0,0 +1,495 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + + +#include "includeutils.h" + +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +using namespace CPlusPlus; +using namespace CppTools; +using namespace CppTools::IncludeUtils; +using namespace Utils; + +static bool includeLineLessThan(const Include &left, const Include &right) +{ return left.line() < right.line(); } + +static bool includeFileNamelessThen(const Include & left, const Include & right) +{ return left.unresolvedFileName() < right.unresolvedFileName(); } + +LineForNewIncludeDirective::LineForNewIncludeDirective(const QTextDocument *textDocument, + QList includes, + MocIncludeMode mocIncludeMode, + IncludeStyle includeStyle) + : m_textDocument(textDocument) + , m_includeStyle(includeStyle) +{ + // Ignore *.moc includes if requested + if (mocIncludeMode == IgnoreMocIncludes) { + foreach (const Document::Include &include, includes) { + if (!include.unresolvedFileName().endsWith(QLatin1String(".moc"))) + m_includes << include; + } + } else { + m_includes = includes; + } + + // TODO: Remove this filter loop once FastPreprocessor::sourceNeeded does not add + // extra includes anymore. + for (int i = m_includes.count() - 1; i >= 0; --i) { + if (!QFileInfo(m_includes.at(i).resolvedFileName()).isAbsolute()) + m_includes.removeAt(i); + } + + // Detect include style + if (m_includeStyle == AutoDetect) { + unsigned timesIncludeStyleChanged = 0; + if (m_includes.isEmpty() || m_includes.size() == 1) { + m_includeStyle = LocalBeforeGlobal; // Fallback + } else { + for (int i = 1, size = m_includes.size(); i < size; ++i) { + if (m_includes.at(i - 1).type() != m_includes.at(i).type()) { + if (++timesIncludeStyleChanged > 1) + break; + } + } + if (timesIncludeStyleChanged == 1) { + m_includeStyle = m_includes.first().type() == Client::IncludeLocal + ? LocalBeforeGlobal + : GlobalBeforeLocal; + } else { + m_includeStyle = LocalBeforeGlobal; // Fallback + } + } + } +} + +int LineForNewIncludeDirective::operator()(const QString &newIncludeFileName, + unsigned *newLinesToPrepend, + unsigned *newLinesToAppend) +{ + if (newLinesToPrepend) + *newLinesToPrepend = false; + if (newLinesToAppend) + *newLinesToAppend = false; + + const QString pureIncludeFileName = newIncludeFileName.mid(1, newIncludeFileName.length() - 2); + const CPlusPlus::Client::IncludeType newIncludeType = + newIncludeFileName.startsWith(QLatin1Char('"')) ? Client::IncludeLocal + : Client::IncludeGlobal; + + // Handle no includes + if (m_includes.empty()) { + unsigned insertLine = 0; + + QTextBlock block = m_textDocument->firstBlock(); + while (block.isValid()) { + const QString trimmedText = block.text().trimmed(); + + // Only skip the first comment! + if (trimmedText.startsWith(QLatin1String("/*"))) { + do { + const int pos = block.text().indexOf(QLatin1String("*/")); + if (pos > -1) { + insertLine = block.blockNumber() + 2; + break; + } + block = block.next(); + } while (block.isValid()); + break; + } else if (trimmedText.startsWith(QLatin1String("//"))) { + block = block.next(); + while (block.isValid()) { + if (!block.text().trimmed().startsWith(QLatin1String("//"))) { + insertLine = block.blockNumber() + 1; + break; + } + block = block.next(); + } + break; + } + + if (!trimmedText.isEmpty()) + break; + block = block.next(); + } + + if (insertLine == 0) { + if (newLinesToAppend) + *newLinesToAppend += 1; + insertLine = 1; + } else { + if (newLinesToPrepend) + *newLinesToPrepend = 1; + } + return insertLine; + } + + typedef QList IncludeGroups; + + const IncludeGroups groupsNewline = IncludeGroup::detectIncludeGroupsByNewLines(m_includes); + const bool includeAtTop + = (newIncludeType == Client::IncludeLocal && m_includeStyle == LocalBeforeGlobal) + || (newIncludeType == Client::IncludeGlobal && m_includeStyle == GlobalBeforeLocal); + IncludeGroup bestGroup = includeAtTop ? groupsNewline.first() : groupsNewline.last(); + + IncludeGroups groupsMatchingIncludeType = getGroupsByIncludeType(groupsNewline, newIncludeType); + if (groupsMatchingIncludeType.isEmpty()) { + const IncludeGroups groupsMixedIncludeType + = IncludeGroup::filterMixedIncludeGroups(groupsNewline); + // case: The new include goes into an own include group + if (groupsMixedIncludeType.isEmpty()) { + return includeAtTop + ? IncludeGroup::lineForPrependedIncludeGroup(groupsNewline, newLinesToAppend) + : IncludeGroup::lineForAppendedIncludeGroup(groupsNewline, newLinesToPrepend); + // case: add to mixed group + } else { + const IncludeGroup bestMixedGroup = groupsMixedIncludeType.last(); // TODO: flaterize + const IncludeGroups groupsIncludeType + = IncludeGroup::detectIncludeGroupsByIncludeType(bestMixedGroup.includes()); + groupsMatchingIncludeType = getGroupsByIncludeType(groupsIncludeType, newIncludeType); + // Avoid extra new lines for include groups which are not separated by new lines + newLinesToPrepend = 0; + newLinesToAppend = 0; + } + } + + IncludeGroups groupsSameIncludeDir; + IncludeGroups groupsMixedIncludeDirs; + foreach (const IncludeGroup &group, groupsMatchingIncludeType) { + if (group.hasCommonIncludeDir()) + groupsSameIncludeDir << group; + else + groupsMixedIncludeDirs << group; + } + + IncludeGroups groupsMatchingIncludeDir; + foreach (const IncludeGroup &group, groupsSameIncludeDir) { + if (group.commonIncludeDir() == IncludeGroup::includeDir(pureIncludeFileName)) + groupsMatchingIncludeDir << group; + } + + // case: There are groups with a matching include dir, insert the new include + // at the best position of the best group + if (!groupsMatchingIncludeDir.isEmpty()) { + // The group with the longest common matching prefix is the best group + int longestPrefixSoFar = 0; + foreach (const IncludeGroup &group, groupsMatchingIncludeDir) { + const int groupPrefixLength = group.commonPrefix().length(); + if (groupPrefixLength >= longestPrefixSoFar) { + bestGroup = group; + longestPrefixSoFar = groupPrefixLength; + } + } + } else { + // case: The new include goes into an own include group + if (groupsMixedIncludeDirs.isEmpty()) { + if (includeAtTop) { + return groupsSameIncludeDir.isEmpty() + ? IncludeGroup::lineForPrependedIncludeGroup(groupsNewline, newLinesToAppend) + : IncludeGroup::lineForAppendedIncludeGroup(groupsSameIncludeDir, newLinesToPrepend); + } else { + return IncludeGroup::lineForAppendedIncludeGroup(groupsNewline, newLinesToPrepend); + } + // case: The new include is inserted at the best position of the best + // group with mixed include dirs + } else { + IncludeGroups groupsIncludeDir; + foreach (const IncludeGroup &group, groupsMixedIncludeDirs) { + groupsIncludeDir.append( + IncludeGroup::detectIncludeGroupsByIncludeDir(group.includes())); + } + IncludeGroup localBestIncludeGroup = IncludeGroup(QList()); + foreach (const IncludeGroup &group, groupsIncludeDir) { + if (group.commonIncludeDir() == IncludeGroup::includeDir(pureIncludeFileName)) + localBestIncludeGroup = group; + } + if (!localBestIncludeGroup.isEmpty()) { + bestGroup = localBestIncludeGroup; + } else { + bestGroup = groupsMixedIncludeDirs.last(); + } + } + } + + return bestGroup.lineForNewInclude(pureIncludeFileName, newIncludeType); +} + +QList LineForNewIncludeDirective::getGroupsByIncludeType( + const QList &groups, IncludeType includeType) +{ + return includeType == Client::IncludeLocal + ? IncludeGroup::filterIncludeGroups(groups, Client::IncludeLocal) + : IncludeGroup::filterIncludeGroups(groups, Client::IncludeGlobal); +} + +/// includes will be modified! +QList IncludeGroup::detectIncludeGroupsByNewLines(QList &includes) +{ + // Sort by line + qSort(includes.begin(), includes.end(), includeLineLessThan); + + // Create groups + QList result; + unsigned lastLine = 0; + QList currentIncludes; + bool isFirst = true; + foreach (const Include &include, includes) { + // First include... + if (isFirst) { + isFirst = false; + currentIncludes << include; + } + // Include belongs to current group + else if (lastLine + 1 == include.line()) { + currentIncludes << include; + } + // Include is member of new group + else { + result << IncludeGroup(currentIncludes); + currentIncludes.clear(); + currentIncludes << include; + } + + lastLine = include.line(); + } + + if (!currentIncludes.isEmpty()) + result << IncludeGroup(currentIncludes); + + return result; +} + +QList IncludeGroup::detectIncludeGroupsByIncludeDir(const QList &includes) +{ + // Create sub groups + QList result; + QString lastDir; + QList currentIncludes; + bool isFirst = true; + foreach (const Include &include, includes) { + const QString currentDirPrefix = includeDir(include.unresolvedFileName()); + + // First include... + if (isFirst) { + isFirst = false; + currentIncludes << include; + } + // Include belongs to current group + else if (lastDir == currentDirPrefix) { + currentIncludes << include; + } + // Include is member of new group + else { + result << IncludeGroup(currentIncludes); + currentIncludes.clear(); + currentIncludes << include; + } + + lastDir = currentDirPrefix; + } + + if (!currentIncludes.isEmpty()) + result << IncludeGroup(currentIncludes); + + return result; +} + +QList IncludeGroup::detectIncludeGroupsByIncludeType(const QList &includes) +{ + // Create sub groups + QList result; + CPlusPlus::Client::IncludeType lastIncludeType; + QList currentIncludes; + bool isFirst = true; + foreach (const Include &include, includes) { + const CPlusPlus::Client::IncludeType currentIncludeType = include.type(); + + // First include... + if (isFirst) { + isFirst = false; + currentIncludes << include; + } + // Include belongs to current group + else if (lastIncludeType == currentIncludeType) { + currentIncludes << include; + } + // Include is member of new group + else { + result << IncludeGroup(currentIncludes); + currentIncludes.clear(); + currentIncludes << include; + } + + lastIncludeType = currentIncludeType; + } + + if (!currentIncludes.isEmpty()) + result << IncludeGroup(currentIncludes); + + return result; +} + +QString IncludeGroup::includeDir(const QString &include) +{ + QString dirPrefix = QFileInfo(include).dir().path(); + if (dirPrefix == QLatin1String(".")) + return QString(); + dirPrefix.append(QLatin1Char('/')); + return dirPrefix; +} + +/// returns groups that solely contains includes of the given include type +QList IncludeGroup::filterIncludeGroups(const QList &groups, + Client::IncludeType includeType) +{ + QList result; + foreach (const IncludeGroup &group, groups) { + if (group.hasOnlyIncludesOfType(includeType)) + result << group; + } + return result; +} + +/// returns groups that contains includes with local and globale include type +QList IncludeGroup::filterMixedIncludeGroups(const QList &groups) +{ + QList result; + foreach (const IncludeGroup &group, groups) { + if (!group.hasOnlyIncludesOfType(Client::IncludeLocal) + && !group.hasOnlyIncludesOfType(Client::IncludeGlobal)) { + result << group; + } + } + return result; +} + +bool IncludeGroup::hasOnlyIncludesOfType(Client::IncludeType includeType) const +{ + foreach (const Include &include, m_includes) { + if (include.type() != includeType) + return false; + } + return true; +} + +bool IncludeGroup::isSorted() const +{ + const QStringList names = filesNames(); + if (names.isEmpty() || names.size() == 1) + return true; + for (int i = 1, total = names.size(); i < total; ++i) { + if (names.at(i) < names.at(i - 1)) + return false; + } + return true; +} + +int IncludeGroup::lineForNewInclude(const QString &newIncludeFileName, + Client::IncludeType newIncludeType) const +{ + if (m_includes.empty()) + return -1; + + if (isSorted()) { + const Include newInclude(newIncludeFileName, QString(), -1, newIncludeType); + const QList::const_iterator it = std::lower_bound(m_includes.begin(), + m_includes.end(), newInclude, includeFileNamelessThen); + if (it == m_includes.end()) + return m_includes.last().line() + 1; + else + return (*it).line(); + } else { + return m_includes.last().line() + 1; + } + + return -1; +} + +QStringList IncludeGroup::filesNames() const +{ + QStringList names; + foreach (const Include &include, m_includes) + names << include.unresolvedFileName(); + return names; +} + +QString IncludeGroup::commonPrefix() const +{ + const QStringList files = filesNames(); + if (files.size() <= 1) + return QString(); // no prefix for single item groups + return Utils::commonPrefix(files); +} + +QString IncludeGroup::commonIncludeDir() const +{ + if (m_includes.isEmpty()) + return QString(); + return includeDir(m_includes.first().unresolvedFileName()); +} + +bool IncludeGroup::hasCommonIncludeDir() const +{ + if (m_includes.isEmpty()) + return false; + + const QString candidate = includeDir(m_includes.first().unresolvedFileName()); + for (int i = 1, size = m_includes.size(); i < size; ++i) { + if (includeDir(m_includes.at(i).unresolvedFileName()) != candidate) + return false; + } + return true; +} + +int IncludeGroup::lineForAppendedIncludeGroup(const QList &groups, + unsigned *newLinesToPrepend) +{ + if (newLinesToPrepend) + *newLinesToPrepend += 1; + return groups.last().last().line() + 1; +} + +int IncludeGroup::lineForPrependedIncludeGroup(const QList &groups, + unsigned *newLinesToAppend) +{ + if (newLinesToAppend) + *newLinesToAppend += 1; + return groups.first().first().line(); +} diff --git a/src/plugins/cpptools/includeutils.h b/src/plugins/cpptools/includeutils.h new file mode 100644 index 00000000000..b45f1f0d4c1 --- /dev/null +++ b/src/plugins/cpptools/includeutils.h @@ -0,0 +1,121 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + + +#ifndef INCLUDEUTILS_H +#define INCLUDEUTILS_H + +#include "cpptools_global.h" + +#include +#include + +#include +#include + +QT_FORWARD_DECLARE_CLASS(QStringList) +QT_FORWARD_DECLARE_CLASS(QTextDocument) + +namespace CppTools { +namespace IncludeUtils { + +typedef CPlusPlus::Document::Include Include; +typedef CPlusPlus::Client::IncludeType IncludeType; + +class CPPTOOLS_EXPORT IncludeGroup +{ +public: + static QList detectIncludeGroupsByNewLines(QList &includes); + static QList detectIncludeGroupsByIncludeDir(const QList &includes); + static QList detectIncludeGroupsByIncludeType(const QList &includes); + + static int lineForAppendedIncludeGroup(const QList &groups, + unsigned *newLinesToPrepend); + static int lineForPrependedIncludeGroup(const QList &groups, + unsigned *newLinesToAppend); + + static QList filterMixedIncludeGroups(const QList &groups); + static QList filterIncludeGroups(const QList &groups, + CPlusPlus::Client::IncludeType includeType); + + static QString includeDir(const QString &include); + +public: + IncludeGroup(const QList &includes) : m_includes(includes) {} + + QList includes() const { return m_includes; } + Include first() const { return m_includes.first(); } + Include last() const { return m_includes.last(); } + int size() const { return m_includes.size(); } + bool isEmpty() const { return m_includes.isEmpty(); } + + QString commonPrefix() const; + QString commonIncludeDir() const; /// only valid if hasCommonDir() == true + bool hasCommonIncludeDir() const; + bool hasOnlyIncludesOfType(CPlusPlus::Client::IncludeType includeType) const; + bool isSorted() const; /// name-wise + + int lineForNewInclude(const QString &newIncludeFileName, + CPlusPlus::Client::IncludeType newIncludeType) const; + +private: + QStringList filesNames() const; + + QList m_includes; +}; + +class CPPTOOLS_EXPORT LineForNewIncludeDirective +{ +public: + enum MocIncludeMode { RespectMocIncludes, IgnoreMocIncludes }; + enum IncludeStyle { LocalBeforeGlobal, GlobalBeforeLocal, AutoDetect }; + + LineForNewIncludeDirective(const QTextDocument *textDocument, + QList includes, + MocIncludeMode mocIncludeMode = IgnoreMocIncludes, + IncludeStyle includeStyle = AutoDetect); + + /// Returns the line (1-based) at which the include directive should be inserted. + /// On error, -1 is returned. + int operator()(const QString &newIncludeFileName, unsigned *newLinesToPrepend = 0, + unsigned *newLinesToAppend = 0); + +private: + QList getGroupsByIncludeType(const QList &groups, + IncludeType includeType); + + const QTextDocument *m_textDocument; + IncludeStyle m_includeStyle; + QList m_includes; +}; + +} // namespace IncludeUtils +} // namespace CppTools + +#endif // INCLUDEUTILS_H diff --git a/tests/auto/cplusplus/preprocessor/data/include-data/global/QDebug b/tests/auto/cplusplus/preprocessor/data/include-data/global/QDebug new file mode 100644 index 00000000000..fef83a9cfe6 --- /dev/null +++ b/tests/auto/cplusplus/preprocessor/data/include-data/global/QDebug @@ -0,0 +1 @@ +// comment diff --git a/tests/auto/cplusplus/preprocessor/data/include-data/global/QDir b/tests/auto/cplusplus/preprocessor/data/include-data/global/QDir new file mode 100644 index 00000000000..fef83a9cfe6 --- /dev/null +++ b/tests/auto/cplusplus/preprocessor/data/include-data/global/QDir @@ -0,0 +1 @@ +// comment diff --git a/tests/auto/cplusplus/preprocessor/data/include-data/global/QString b/tests/auto/cplusplus/preprocessor/data/include-data/global/QString new file mode 100644 index 00000000000..fef83a9cfe6 --- /dev/null +++ b/tests/auto/cplusplus/preprocessor/data/include-data/global/QString @@ -0,0 +1 @@ +// comment diff --git a/tests/auto/cplusplus/preprocessor/data/include-data/global/except b/tests/auto/cplusplus/preprocessor/data/include-data/global/except new file mode 100644 index 00000000000..fef83a9cfe6 --- /dev/null +++ b/tests/auto/cplusplus/preprocessor/data/include-data/global/except @@ -0,0 +1 @@ +// comment diff --git a/tests/auto/cplusplus/preprocessor/data/include-data/global/global.h b/tests/auto/cplusplus/preprocessor/data/include-data/global/global.h new file mode 100644 index 00000000000..fef83a9cfe6 --- /dev/null +++ b/tests/auto/cplusplus/preprocessor/data/include-data/global/global.h @@ -0,0 +1 @@ +// comment diff --git a/tests/auto/cplusplus/preprocessor/data/include-data/global/iostream b/tests/auto/cplusplus/preprocessor/data/include-data/global/iostream new file mode 100644 index 00000000000..fef83a9cfe6 --- /dev/null +++ b/tests/auto/cplusplus/preprocessor/data/include-data/global/iostream @@ -0,0 +1 @@ +// comment diff --git a/tests/auto/cplusplus/preprocessor/data/include-data/global/lib/file.h b/tests/auto/cplusplus/preprocessor/data/include-data/global/lib/file.h new file mode 100644 index 00000000000..fef83a9cfe6 --- /dev/null +++ b/tests/auto/cplusplus/preprocessor/data/include-data/global/lib/file.h @@ -0,0 +1 @@ +// comment diff --git a/tests/auto/cplusplus/preprocessor/data/include-data/global/lib/fileother.h b/tests/auto/cplusplus/preprocessor/data/include-data/global/lib/fileother.h new file mode 100644 index 00000000000..fef83a9cfe6 --- /dev/null +++ b/tests/auto/cplusplus/preprocessor/data/include-data/global/lib/fileother.h @@ -0,0 +1 @@ +// comment diff --git a/tests/auto/cplusplus/preprocessor/data/include-data/global/otherlib/file.h b/tests/auto/cplusplus/preprocessor/data/include-data/global/otherlib/file.h new file mode 100644 index 00000000000..fef83a9cfe6 --- /dev/null +++ b/tests/auto/cplusplus/preprocessor/data/include-data/global/otherlib/file.h @@ -0,0 +1 @@ +// comment diff --git a/tests/auto/cplusplus/preprocessor/data/include-data/global/otherlib/fileother.h b/tests/auto/cplusplus/preprocessor/data/include-data/global/otherlib/fileother.h new file mode 100644 index 00000000000..fef83a9cfe6 --- /dev/null +++ b/tests/auto/cplusplus/preprocessor/data/include-data/global/otherlib/fileother.h @@ -0,0 +1 @@ +// comment diff --git a/tests/auto/cplusplus/preprocessor/data/include-data/global/string b/tests/auto/cplusplus/preprocessor/data/include-data/global/string new file mode 100644 index 00000000000..fef83a9cfe6 --- /dev/null +++ b/tests/auto/cplusplus/preprocessor/data/include-data/global/string @@ -0,0 +1 @@ +// comment diff --git a/tests/auto/cplusplus/preprocessor/data/include-data/global/utils/file.h b/tests/auto/cplusplus/preprocessor/data/include-data/global/utils/file.h new file mode 100644 index 00000000000..fef83a9cfe6 --- /dev/null +++ b/tests/auto/cplusplus/preprocessor/data/include-data/global/utils/file.h @@ -0,0 +1 @@ +// comment diff --git a/tests/auto/cplusplus/preprocessor/data/include-data/local/a.h b/tests/auto/cplusplus/preprocessor/data/include-data/local/a.h new file mode 100644 index 00000000000..fef83a9cfe6 --- /dev/null +++ b/tests/auto/cplusplus/preprocessor/data/include-data/local/a.h @@ -0,0 +1 @@ +// comment diff --git a/tests/auto/cplusplus/preprocessor/data/include-data/local/b.h b/tests/auto/cplusplus/preprocessor/data/include-data/local/b.h new file mode 100644 index 00000000000..fef83a9cfe6 --- /dev/null +++ b/tests/auto/cplusplus/preprocessor/data/include-data/local/b.h @@ -0,0 +1 @@ +// comment diff --git a/tests/auto/cplusplus/preprocessor/data/include-data/local/file.h b/tests/auto/cplusplus/preprocessor/data/include-data/local/file.h new file mode 100644 index 00000000000..fef83a9cfe6 --- /dev/null +++ b/tests/auto/cplusplus/preprocessor/data/include-data/local/file.h @@ -0,0 +1 @@ +// comment diff --git a/tests/auto/cplusplus/preprocessor/data/include-data/local/fileother.h b/tests/auto/cplusplus/preprocessor/data/include-data/local/fileother.h new file mode 100644 index 00000000000..fef83a9cfe6 --- /dev/null +++ b/tests/auto/cplusplus/preprocessor/data/include-data/local/fileother.h @@ -0,0 +1 @@ +// comment diff --git a/tests/auto/cplusplus/preprocessor/data/include-data/local/header.h b/tests/auto/cplusplus/preprocessor/data/include-data/local/header.h new file mode 100644 index 00000000000..fef83a9cfe6 --- /dev/null +++ b/tests/auto/cplusplus/preprocessor/data/include-data/local/header.h @@ -0,0 +1 @@ +// comment diff --git a/tests/auto/cplusplus/preprocessor/data/include-data/local/lib/file.h b/tests/auto/cplusplus/preprocessor/data/include-data/local/lib/file.h new file mode 100644 index 00000000000..fef83a9cfe6 --- /dev/null +++ b/tests/auto/cplusplus/preprocessor/data/include-data/local/lib/file.h @@ -0,0 +1 @@ +// comment diff --git a/tests/auto/cplusplus/preprocessor/data/include-data/local/lib/fileother.h b/tests/auto/cplusplus/preprocessor/data/include-data/local/lib/fileother.h new file mode 100644 index 00000000000..fef83a9cfe6 --- /dev/null +++ b/tests/auto/cplusplus/preprocessor/data/include-data/local/lib/fileother.h @@ -0,0 +1 @@ +// comment diff --git a/tests/auto/cplusplus/preprocessor/data/include-data/local/otherlib/file.h b/tests/auto/cplusplus/preprocessor/data/include-data/local/otherlib/file.h new file mode 100644 index 00000000000..fef83a9cfe6 --- /dev/null +++ b/tests/auto/cplusplus/preprocessor/data/include-data/local/otherlib/file.h @@ -0,0 +1 @@ +// comment diff --git a/tests/auto/cplusplus/preprocessor/data/include-data/local/otherlib/fileother.h b/tests/auto/cplusplus/preprocessor/data/include-data/local/otherlib/fileother.h new file mode 100644 index 00000000000..fef83a9cfe6 --- /dev/null +++ b/tests/auto/cplusplus/preprocessor/data/include-data/local/otherlib/fileother.h @@ -0,0 +1 @@ +// comment diff --git a/tests/auto/cplusplus/preprocessor/data/include-data/local/prefixa.h b/tests/auto/cplusplus/preprocessor/data/include-data/local/prefixa.h new file mode 100644 index 00000000000..fef83a9cfe6 --- /dev/null +++ b/tests/auto/cplusplus/preprocessor/data/include-data/local/prefixa.h @@ -0,0 +1 @@ +// comment diff --git a/tests/auto/cplusplus/preprocessor/data/include-data/local/prefixb.h b/tests/auto/cplusplus/preprocessor/data/include-data/local/prefixb.h new file mode 100644 index 00000000000..fef83a9cfe6 --- /dev/null +++ b/tests/auto/cplusplus/preprocessor/data/include-data/local/prefixb.h @@ -0,0 +1 @@ +// comment diff --git a/tests/auto/cplusplus/preprocessor/data/include-data/local/prefixc.h b/tests/auto/cplusplus/preprocessor/data/include-data/local/prefixc.h new file mode 100644 index 00000000000..fef83a9cfe6 --- /dev/null +++ b/tests/auto/cplusplus/preprocessor/data/include-data/local/prefixc.h @@ -0,0 +1 @@ +// comment diff --git a/tests/auto/cplusplus/preprocessor/data/include-data/local/stuff b/tests/auto/cplusplus/preprocessor/data/include-data/local/stuff new file mode 100644 index 00000000000..fef83a9cfe6 --- /dev/null +++ b/tests/auto/cplusplus/preprocessor/data/include-data/local/stuff @@ -0,0 +1 @@ +// comment diff --git a/tests/auto/cplusplus/preprocessor/data/include-data/local/utils/utils.h b/tests/auto/cplusplus/preprocessor/data/include-data/local/utils/utils.h new file mode 100644 index 00000000000..fef83a9cfe6 --- /dev/null +++ b/tests/auto/cplusplus/preprocessor/data/include-data/local/utils/utils.h @@ -0,0 +1 @@ +// comment diff --git a/tests/auto/cplusplus/preprocessor/data/include-data/local/y.h b/tests/auto/cplusplus/preprocessor/data/include-data/local/y.h new file mode 100644 index 00000000000..fef83a9cfe6 --- /dev/null +++ b/tests/auto/cplusplus/preprocessor/data/include-data/local/y.h @@ -0,0 +1 @@ +// comment diff --git a/tests/auto/cplusplus/preprocessor/data/include-data/local/z.h b/tests/auto/cplusplus/preprocessor/data/include-data/local/z.h new file mode 100644 index 00000000000..fef83a9cfe6 --- /dev/null +++ b/tests/auto/cplusplus/preprocessor/data/include-data/local/z.h @@ -0,0 +1 @@ +// comment