diff options
Diffstat (limited to 'src/plugins/cpptools')
69 files changed, 3409 insertions, 960 deletions
diff --git a/src/plugins/cpptools/CMakeLists.txt b/src/plugins/cpptools/CMakeLists.txt index fe481af941..6e08f283f8 100644 --- a/src/plugins/cpptools/CMakeLists.txt +++ b/src/plugins/cpptools/CMakeLists.txt @@ -110,17 +110,19 @@ add_qtc_plugin(CppTools extend_qtc_plugin(CppTools CONDITION WITH_TESTS SOURCES - cppcodegen_test.cpp - cppcompletion_test.cpp - cppheadersource_test.cpp - cpplocalsymbols_test.cpp - cpplocatorfilter_test.cpp - cppmodelmanager_test.cpp - cpppointerdeclarationformatter_test.cpp + compileroptionsbuilder_test.cpp compileroptionsbuilder_test.h + cppcodegen_test.cpp cppcodegen_test.h + cppcompletion_test.cpp cppcompletion_test.h + cppheadersource_test.cpp cppheadersource_test.h + cpplocalsymbols_test.cpp cpplocalsymbols_test.h + cpplocatorfilter_test.cpp cpplocatorfilter_test.h + cppmodelmanager_test.cpp cppmodelmanager_test.h + cpppointerdeclarationformatter_test.cpp cpppointerdeclarationformatter_test.h cppsourceprocessertesthelper.cpp cppsourceprocessertesthelper.h - cppsourceprocessor_test.cpp + cppsourceprocessor_test.cpp cppsourceprocessor_test.h cpptoolstestcase.cpp cpptoolstestcase.h modelmanagertesthelper.cpp modelmanagertesthelper.h - symbolsearcher_test.cpp - typehierarchybuilder_test.cpp + projectinfo_test.cpp projectinfo_test.h + symbolsearcher_test.cpp symbolsearcher_test.h + typehierarchybuilder_test.cpp typehierarchybuilder_test.h ) diff --git a/src/plugins/cpptools/abstractoverviewmodel.h b/src/plugins/cpptools/abstractoverviewmodel.h index 1035494a37..89280cf09c 100644 --- a/src/plugins/cpptools/abstractoverviewmodel.h +++ b/src/plugins/cpptools/abstractoverviewmodel.h @@ -84,7 +84,8 @@ public: const QVariant lineNumber = data(index, LineNumberRole); if (!lineNumber.canConvert<unsigned>()) continue; - mimeData->addFile(fileName.toString(), static_cast<int>(lineNumber.value<unsigned>())); + mimeData->addFile(Utils::FilePath::fromVariant(fileName), + static_cast<int>(lineNumber.value<unsigned>())); } return mimeData; } diff --git a/src/plugins/cpptools/builtineditordocumentparser.cpp b/src/plugins/cpptools/builtineditordocumentparser.cpp index 390dd0420d..1d2a187432 100644 --- a/src/plugins/cpptools/builtineditordocumentparser.cpp +++ b/src/plugins/cpptools/builtineditordocumentparser.cpp @@ -100,7 +100,7 @@ void BuiltinEditorDocumentParser::updateImpl(const QFutureInterface<void> &futur configFile += overwrittenToolchainDefines(*part.data()); configFile += ProjectExplorer::Macro::toByteArray(part->projectMacros); if (!part->projectConfigFile.isEmpty()) - configFile += ProjectPart::readProjectConfigFile(part); + configFile += ProjectPart::readProjectConfigFile(part->projectConfigFile); headerPaths = part->headerPaths; projectConfigFile = part->projectConfigFile; includedFiles = part->includedFiles; diff --git a/src/plugins/cpptools/compileroptionsbuilder.cpp b/src/plugins/cpptools/compileroptionsbuilder.cpp index 7ee9b6bdf8..0c8c5d9305 100644 --- a/src/plugins/cpptools/compileroptionsbuilder.cpp +++ b/src/plugins/cpptools/compileroptionsbuilder.cpp @@ -45,36 +45,39 @@ #include <QRegularExpression> #include <QtGlobal> +using namespace ProjectExplorer; +using namespace Utils; + namespace CppTools { -static const char defineOption[] = "-D"; -static const char undefineOption[] = "-U"; +const char defineOption[] = "-D"; +const char undefineOption[] = "-U"; -static const char includeUserPathOption[] = "-I"; -static const char includeUserPathOptionWindows[] = "/I"; -static const char includeSystemPathOption[] = "-isystem"; +const char includeUserPathOption[] = "-I"; +const char includeUserPathOptionWindows[] = "/I"; +const char includeSystemPathOption[] = "-isystem"; -static const char includeFileOptionGcc[] = "-include"; -static const char includeFileOptionCl[] = "/FI"; +const char includeFileOptionGcc[] = "-include"; +const char includeFileOptionCl[] = "/FI"; -static QByteArray macroOption(const ProjectExplorer::Macro ¯o) +static QByteArray macroOption(const Macro ¯o) { switch (macro.type) { - case ProjectExplorer::MacroType::Define: + case MacroType::Define: return defineOption; - case ProjectExplorer::MacroType::Undefine: + case MacroType::Undefine: return undefineOption; default: return QByteArray(); } } -static QByteArray toDefineOption(const ProjectExplorer::Macro ¯o) +static QByteArray toDefineOption(const Macro ¯o) { return macro.toKeyValue(macroOption(macro)); } -static QString defineDirectiveToDefineOption(const ProjectExplorer::Macro ¯o) +static QString defineDirectiveToDefineOption(const Macro ¯o) { const QByteArray option = toDefineOption(macro); return QString::fromUtf8(option); @@ -104,7 +107,7 @@ CompilerOptionsBuilder::CompilerOptionsBuilder(const ProjectPart &projectPart, UseLanguageDefines useLanguageDefines, UseBuildSystemWarnings useBuildSystemWarnings, const QString &clangVersion, - const QString &clangIncludeDirectory) + const FilePath &clangIncludeDirectory) : m_projectPart(projectPart) , m_useSystemHeader(useSystemHeader) , m_useTweakedHeaderPaths(useTweakedHeaderPaths) @@ -122,13 +125,11 @@ QStringList CompilerOptionsBuilder::build(ProjectFile::Kind fileKind, evaluateCompilerFlags(); if (fileKind == ProjectFile::CHeader || fileKind == ProjectFile::CSource) { - QTC_ASSERT(m_projectPart.languageVersion <= Utils::LanguageVersion::LatestC, - return QStringList();); + QTC_ASSERT(m_projectPart.languageVersion <= LanguageVersion::LatestC, return {}); } if (fileKind == ProjectFile::CXXHeader || fileKind == ProjectFile::CXXSource) { - QTC_ASSERT(m_projectPart.languageVersion > Utils::LanguageVersion::LatestC, - return QStringList();); + QTC_ASSERT(m_projectPart.languageVersion > LanguageVersion::LatestC, return {}); } addCompilerFlags(); @@ -276,7 +277,7 @@ void CompilerOptionsBuilder::addMsvcExceptions() { if (!m_clStyle) return; - if (Utils::anyOf(m_projectPart.toolChainMacros, [](const ProjectExplorer::Macro ¯o) { + if (Utils::anyOf(m_projectPart.toolChainMacros, [](const Macro ¯o) { return macro.key == "_CPPUNWIND"; })) { enableExceptions(); @@ -289,7 +290,7 @@ void CompilerOptionsBuilder::enableExceptions() // This is most likely due to incomplete exception support of clang. // However, as we need exception support only in the frontend, // enabling them explicitly should be fine. - if (m_projectPart.languageVersion > ::Utils::LanguageVersion::LatestC) + if (m_projectPart.languageVersion > LanguageVersion::LatestC) add("-fcxx-exceptions"); add("-fexceptions"); } @@ -345,9 +346,6 @@ void CompilerOptionsBuilder::addHeaderPathOptions() filter.process(); - using ProjectExplorer::HeaderPath; - using ProjectExplorer::HeaderPathType; - for (const HeaderPath &headerPath : qAsConst(filter.userHeaderPaths)) addIncludeDirOptionForPath(headerPath); for (const HeaderPath &headerPath : qAsConst(filter.systemHeaderPaths)) @@ -408,11 +406,11 @@ void CompilerOptionsBuilder::addProjectMacros() addMacros(m_projectPart.projectMacros); } -void CompilerOptionsBuilder::addMacros(const ProjectExplorer::Macros ¯os) +void CompilerOptionsBuilder::addMacros(const Macros ¯os) { QStringList options; - for (const ProjectExplorer::Macro ¯o : macros) { + for (const Macro ¯o : macros) { if (excludeDefineDirective(macro)) continue; @@ -445,8 +443,7 @@ void CompilerOptionsBuilder::updateFileLanguage(ProjectFile::Kind fileKind) return; } - const bool objcExt = m_projectPart.languageExtensions - & Utils::LanguageExtension::ObjectiveC; + const bool objcExt = m_projectPart.languageExtensions & LanguageExtension::ObjectiveC; const QStringList options = createLanguageOptionGcc(fileKind, objcExt); if (options.isEmpty()) return; @@ -461,9 +458,6 @@ void CompilerOptionsBuilder::updateFileLanguage(ProjectFile::Kind fileKind) void CompilerOptionsBuilder::addLanguageVersionAndExtensions() { - using Utils::LanguageExtension; - using Utils::LanguageVersion; - if (m_compilerFlags.isLanguageVersionSpecified) return; @@ -494,7 +488,7 @@ void CompilerOptionsBuilder::addLanguageVersionAndExtensions() // Continue in case no cl-style option could be chosen. } - const Utils::LanguageExtensions languageExtensions = m_projectPart.languageExtensions; + const LanguageExtensions languageExtensions = m_projectPart.languageExtensions; const bool gnuExtensions = languageExtensions & LanguageExtension::Gnu; switch (m_projectPart.languageVersion) { @@ -546,9 +540,9 @@ static QByteArray toMsCompatibilityVersionFormat(const QByteArray &mscFullVer) + mscFullVer.mid(2, 2); } -static QByteArray msCompatibilityVersionFromDefines(const ProjectExplorer::Macros ¯os) +static QByteArray msCompatibilityVersionFromDefines(const Macros ¯os) { - for (const ProjectExplorer::Macro ¯o : macros) { + for (const Macro ¯o : macros) { if (macro.key == "_MSC_FULL_VER") return toMsCompatibilityVersionFormat(macro.value); } @@ -656,24 +650,24 @@ void CompilerOptionsBuilder::addDefineFunctionMacrosMsvc() } } -void CompilerOptionsBuilder::addIncludeDirOptionForPath(const ProjectExplorer::HeaderPath &path) +void CompilerOptionsBuilder::addIncludeDirOptionForPath(const HeaderPath &path) { - if (path.type == ProjectExplorer::HeaderPathType::Framework) { + if (path.type == HeaderPathType::Framework) { QTC_ASSERT(!isClStyle(), return;); add({"-F", QDir::toNativeSeparators(path.path)}); return; } bool systemPath = false; - if (path.type == ProjectExplorer::HeaderPathType::BuiltIn) { + if (path.type == HeaderPathType::BuiltIn) { systemPath = true; - } else if (path.type == ProjectExplorer::HeaderPathType::System) { + } else if (path.type == HeaderPathType::System) { if (m_useSystemHeader == UseSystemHeader::Yes) systemPath = true; } else { // ProjectExplorer::HeaderPathType::User - if (m_useSystemHeader == UseSystemHeader::Yes - && !path.path.startsWith(m_projectPart.project->rootProjectDirectory().toString())) { + if (m_useSystemHeader == UseSystemHeader::Yes && m_projectPart.hasProject() + && !Utils::FilePath::fromString(path.path).isChildOf(m_projectPart.topLevelProject)) { systemPath = true; } } @@ -686,7 +680,7 @@ void CompilerOptionsBuilder::addIncludeDirOptionForPath(const ProjectExplorer::H add({includeUserPathOption, QDir::toNativeSeparators(path.path)}); } -bool CompilerOptionsBuilder::excludeDefineDirective(const ProjectExplorer::Macro ¯o) const +bool CompilerOptionsBuilder::excludeDefineDirective(const Macro ¯o) const { // Avoid setting __cplusplus & co as this might conflict with other command line flags. // Clang should set __cplusplus based on -std= and -fms-compatibility-version version. @@ -730,7 +724,7 @@ bool CompilerOptionsBuilder::excludeDefineDirective(const ProjectExplorer::Macro QStringList CompilerOptionsBuilder::wrappedQtHeadersIncludePath() const { - if (m_projectPart.qtVersion == Utils::QtVersion::None) + if (m_projectPart.qtVersion == QtVersion::None) return {}; return {"wrappedQtHeaders", "wrappedQtHeaders/QtCore"}; } @@ -786,7 +780,7 @@ void CompilerOptionsBuilder::evaluateCompilerFlags() qgetenv("QTC_CLANG_CMD_OPTIONS_BLACKLIST")) .split(';', Qt::SkipEmptyParts); - const Utils::Id &toolChain = m_projectPart.toolchainType; + const Id toolChain = m_projectPart.toolchainType; bool containsDriverMode = false; bool skipNext = false; const QStringList allFlags = m_projectPart.compilerFlags + m_projectPart.extraCodeModelFlags; diff --git a/src/plugins/cpptools/compileroptionsbuilder.h b/src/plugins/cpptools/compileroptionsbuilder.h index c4d8589f17..69e2470096 100644 --- a/src/plugins/cpptools/compileroptionsbuilder.h +++ b/src/plugins/cpptools/compileroptionsbuilder.h @@ -50,8 +50,8 @@ public: UseTweakedHeaderPaths useTweakedHeaderPaths = UseTweakedHeaderPaths::No, UseLanguageDefines useLanguageDefines = UseLanguageDefines::No, UseBuildSystemWarnings useBuildSystemWarnings = UseBuildSystemWarnings::No, - const QString &clangVersion = QString(), - const QString &clangIncludeDirectory = QString()); + const QString &clangVersion = {}, + const Utils::FilePath &clangIncludeDirectory = {}); QStringList build(ProjectFile::Kind fileKind, UsePrecompiledHeaders usePrecompiledHeaders); QStringList options() const { return m_options; } @@ -113,7 +113,7 @@ private: const UseBuildSystemWarnings m_useBuildSystemWarnings; const QString m_clangVersion; - const QString m_clangIncludeDirectory; + const Utils::FilePath m_clangIncludeDirectory; struct { QStringList flags; diff --git a/src/plugins/cpptools/compileroptionsbuilder_test.cpp b/src/plugins/cpptools/compileroptionsbuilder_test.cpp new file mode 100644 index 0000000000..4847237c23 --- /dev/null +++ b/src/plugins/cpptools/compileroptionsbuilder_test.cpp @@ -0,0 +1,684 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#include "compileroptionsbuilder_test.h" + +#include "compileroptionsbuilder.h" +#include "projectinfo.h" +#include "projectpart.h" + +#include <projectexplorer/headerpath.h> +#include <projectexplorer/project.h> +#include <projectexplorer/projectexplorerconstants.h> +#include <utils/algorithm.h> +#include <utils/temporarydirectory.h> + +#include <QtTest> + +#include <memory> + +using namespace ProjectExplorer; + +namespace CppTools { +namespace Internal { + +namespace { +class TestHelper +{ +public: + const ProjectPart &finalize() + { + QFile pchFile(pchFileNativePath()); + pchFile.open(QIODevice::WriteOnly); + RawProjectPart rpp; + rpp.setPreCompiledHeaders({pchFileNativePath()}); + rpp.setMacros({Macro{"projectFoo", "projectBar"}}); + rpp.setQtVersion(Utils::QtVersion::Qt5); + rpp.setHeaderPaths(headerPaths); + rpp.setConfigFileName(projectConfigFile); + ToolChainInfo tcInfo; + tcInfo.type = toolchainType; + tcInfo.wordWidth = 64; + tcInfo.targetTriple = targetTriple; + tcInfo.isMsvc2015ToolChain = isMsvc2015; + tcInfo.extraCodeModelFlags = extraFlags; + tcInfo.macroInspectionRunner = [this](const QStringList &) { + return ToolChain::MacroInspectionReport{toolchainMacros, languageVersion}; + }; + RawProjectPartFlags rppFlags; + rppFlags.commandLineFlags = flags; + projectPart = ProjectPart::create({}, rpp, {}, {}, Utils::Language::Cxx, languageExtensions, + rppFlags, tcInfo); + compilerOptionsBuilder.emplace(CompilerOptionsBuilder(*projectPart)); + return *projectPart; + } + + static HeaderPath builtIn(const QString &path) + { + return HeaderPath{path, HeaderPathType::BuiltIn}; + } + + QString toNative(const QString &toNative) const + { + return QDir::toNativeSeparators(toNative); + } + + QString pchFileNativePath() const + { + return toNative(Utils::TemporaryDirectory::masterDirectoryPath() + + "/compileroptionsbuilder.pch"); + } + + QStringList flags; + Utils::Id toolchainType = Constants::CLANG_TOOLCHAIN_TYPEID; + QString targetTriple = "x86_64-apple-darwin10"; + HeaderPaths headerPaths = {HeaderPath{"/tmp/builtin_path", HeaderPathType::BuiltIn}, + HeaderPath{"/tmp/system_path", HeaderPathType::System}, + HeaderPath{"/tmp/path", HeaderPathType::User}}; + Utils::LanguageVersion languageVersion = Utils::LanguageVersion::CXX17; + Utils::LanguageExtensions languageExtensions; + Macros toolchainMacros{ + Macro{"foo", "bar"}, Macro{"__cplusplus", "2"}, Macro{"__STDC_VERSION__", "2"}, + Macro{"_MSVC_LANG", "2"}, Macro{"_MSC_BUILD", "2"}, Macro{"_MSC_FULL_VER", "1900"}, + Macro{"_MSC_VER", "19"}}; + QString projectConfigFile; + QStringList extraFlags; + bool isMsvc2015 = false; + + Utils::optional<CompilerOptionsBuilder> compilerOptionsBuilder; + +private: + ProjectPart::Ptr projectPart; +}; +} + +void CompilerOptionsBuilderTest::testAddProjectMacros() +{ + TestHelper t; + t.finalize(); + t.compilerOptionsBuilder->addProjectMacros(); + + QCOMPARE(t.compilerOptionsBuilder->options(), QStringList("-DprojectFoo=projectBar")); +} + +void CompilerOptionsBuilderTest::testUnknownFlagsAreForwarded() +{ + TestHelper t; + t.flags = QStringList{"-fancyFlag"}; + ProjectPart part = t.finalize(); + CompilerOptionsBuilder compilerOptionsBuilder{part, UseSystemHeader::No, + UseTweakedHeaderPaths::No, UseLanguageDefines::Yes}; + compilerOptionsBuilder.build(ProjectFile::CXXSource, UsePrecompiledHeaders::No); + + QVERIFY(compilerOptionsBuilder.options().contains(part.compilerFlags.first())); +} + +void CompilerOptionsBuilderTest::testWarningsFlagsAreNotFilteredIfRequested() +{ + TestHelper t; + t.flags = QStringList{"-Whello"}; + ProjectPart part = t.finalize(); + CompilerOptionsBuilder compilerOptionsBuilder{part, UseSystemHeader::No, + UseTweakedHeaderPaths::No, UseLanguageDefines::No, + UseBuildSystemWarnings::Yes}; + compilerOptionsBuilder.build(ProjectFile::CXXSource, UsePrecompiledHeaders::No); + + QVERIFY(compilerOptionsBuilder.options().contains(part.compilerFlags.first())); +} + +void CompilerOptionsBuilderTest::testDiagnosticOptionsAreRemoved() +{ + TestHelper t; + t.flags = QStringList{"-Wbla", "-pedantic"}; + ProjectPart part = t.finalize(); + CompilerOptionsBuilder compilerOptionsBuilder{part, UseSystemHeader::No, + UseTweakedHeaderPaths::No, UseLanguageDefines::Yes}; + compilerOptionsBuilder.build(ProjectFile::CXXSource, UsePrecompiledHeaders::No); + + QVERIFY(!compilerOptionsBuilder.options().contains(part.compilerFlags.at(0))); + QVERIFY(!compilerOptionsBuilder.options().contains(part.compilerFlags.at(1))); +} + +void CompilerOptionsBuilderTest::testCLanguageVersionIsRewritten() +{ + TestHelper t; + // We need to set the language version here to overcome a QTC_ASSERT checking + // consistency between ProjectFile::Kind and ProjectPart::LanguageVersion + t.flags = QStringList{"-std=c18"}; + t.languageVersion = Utils::LanguageVersion::C18; + ProjectPart part = t.finalize(); + + CompilerOptionsBuilder compilerOptionsBuilder{part, UseSystemHeader::No, + UseTweakedHeaderPaths::No, UseLanguageDefines::Yes}; + compilerOptionsBuilder.build(ProjectFile::CSource, UsePrecompiledHeaders::No); + + QVERIFY(!compilerOptionsBuilder.options().contains(part.compilerFlags.first())); + QVERIFY(compilerOptionsBuilder.options().contains("-std=c17")); +} + +void CompilerOptionsBuilderTest::testLanguageVersionIsExplicitlySetIfNotProvided() +{ + TestHelper t; + CompilerOptionsBuilder compilerOptionsBuilder{t.finalize(), UseSystemHeader::No, + UseTweakedHeaderPaths::No, UseLanguageDefines::Yes}; + compilerOptionsBuilder.build(ProjectFile::CXXSource, UsePrecompiledHeaders::No); + + QVERIFY(compilerOptionsBuilder.options().contains("-std=c++17")); +} + +void CompilerOptionsBuilderTest::testLanguageVersionIsExplicitlySetIfNotProvidedMsvc() +{ + TestHelper t; + t.toolchainType = Constants::MSVC_TOOLCHAIN_TYPEID; + CompilerOptionsBuilder compilerOptionsBuilder{t.finalize(), UseSystemHeader::No, + UseTweakedHeaderPaths::No, UseLanguageDefines::Yes}; + compilerOptionsBuilder.build(ProjectFile::CXXSource, UsePrecompiledHeaders::No); + + QVERIFY(compilerOptionsBuilder.options().contains("/std:c++17")); +} + +void CompilerOptionsBuilderTest::testAddWordWidth() +{ + TestHelper t; + t.finalize(); + t.compilerOptionsBuilder->addWordWidth(); + + QCOMPARE(t.compilerOptionsBuilder->options(), QStringList("-m64")); +} + +void CompilerOptionsBuilderTest::testHeaderPathOptionsOrder() +{ + TestHelper t; + CompilerOptionsBuilder compilerOptionsBuilder{t.finalize(), UseSystemHeader::No, + UseTweakedHeaderPaths::Yes, UseLanguageDefines::No, UseBuildSystemWarnings::No, + "dummy_version", ""}; + compilerOptionsBuilder.addHeaderPathOptions(); + + QCOMPARE(compilerOptionsBuilder.options(), + (QStringList{"-nostdinc", "-nostdinc++", "-I", t.toNative("/tmp/path"), + "-I", t.toNative("/tmp/system_path"), "-isystem", "", "-isystem", + t.toNative("/tmp/builtin_path")})); +} + +void CompilerOptionsBuilderTest::testHeaderPathOptionsOrderMsvc() +{ + TestHelper t; + t.toolchainType = Constants::MSVC_TOOLCHAIN_TYPEID; + CompilerOptionsBuilder compilerOptionsBuilder{t.finalize(), UseSystemHeader::No, + UseTweakedHeaderPaths::Yes, UseLanguageDefines::No, UseBuildSystemWarnings::No, + "dummy_version", ""}; + compilerOptionsBuilder.evaluateCompilerFlags(); + compilerOptionsBuilder.addHeaderPathOptions(); + + QCOMPARE(compilerOptionsBuilder.options(), + (QStringList{"-nostdinc", "-nostdinc++", "-I", t.toNative("/tmp/path"), + "-I", t.toNative("/tmp/system_path"), "/clang:-isystem", + "/clang:", "/clang:-isystem", + "/clang:" + t.toNative("/tmp/builtin_path")})); +} + +void CompilerOptionsBuilderTest::testUseSystemHeader() +{ + TestHelper t; + CompilerOptionsBuilder compilerOptionsBuilder{t.finalize(), UseSystemHeader::Yes, + UseTweakedHeaderPaths::Yes, UseLanguageDefines::No, UseBuildSystemWarnings::No, + "dummy_version", ""}; + compilerOptionsBuilder.addHeaderPathOptions(); + + QCOMPARE(compilerOptionsBuilder.options(), + (QStringList{"-nostdinc", "-nostdinc++", "-I", t.toNative("/tmp/path"), + "-isystem", t.toNative("/tmp/system_path"), + "-isystem", "", "-isystem", t.toNative("/tmp/builtin_path")})); +} + +void CompilerOptionsBuilderTest::testNoClangHeadersPath() +{ + TestHelper t; + t.finalize(); + t.compilerOptionsBuilder->addHeaderPathOptions(); + + QCOMPARE(t.compilerOptionsBuilder->options(), + (QStringList{"-I", t.toNative("/tmp/path"), "-I", t.toNative("/tmp/system_path")})); +} + +void CompilerOptionsBuilderTest::testClangHeadersAndCppIncludePathsOrderMacOs() +{ + TestHelper t; + const HeaderPaths additionalHeaderPaths = { + t.builtIn("/usr/include/c++/4.2.1"), + t.builtIn("/usr/include/c++/4.2.1/backward"), + t.builtIn("/usr/local/include"), + t.builtIn("/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/clang/6.0/include"), + t.builtIn("/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include"), + t.builtIn("/usr/include")}; + t.headerPaths = additionalHeaderPaths + t.headerPaths; + CompilerOptionsBuilder compilerOptionsBuilder(t.finalize(), UseSystemHeader::No, + UseTweakedHeaderPaths::Yes, UseLanguageDefines::No, UseBuildSystemWarnings::No, + "dummy_version", ""); + compilerOptionsBuilder.addHeaderPathOptions(); + + QCOMPARE(compilerOptionsBuilder.options(), + (QStringList{"-nostdinc", "-nostdinc++", "-I", t.toNative("/tmp/path"), + "-I", t.toNative("/tmp/system_path"), + "-isystem", t.toNative("/usr/include/c++/4.2.1"), + "-isystem", t.toNative("/usr/include/c++/4.2.1/backward"), + "-isystem", t.toNative("/usr/local/include"), + "-isystem", "", + "-isystem", t.toNative("/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include"), + "-isystem", t.toNative("/usr/include"), + "-isystem", t.toNative("/tmp/builtin_path")})); +} + +void CompilerOptionsBuilderTest::testClangHeadersAndCppIncludePathsOrderLinux() +{ + TestHelper t; + t.targetTriple = "x86_64-linux-gnu"; + t.headerPaths = { + t.builtIn("/usr/include/c++/4.8"), + t.builtIn("/usr/include/c++/4.8/backward"), + t.builtIn("/usr/include/x86_64-linux-gnu/c++/4.8"), + t.builtIn("/usr/local/include"), + t.builtIn("/usr/lib/gcc/x86_64-linux-gnu/4.8/include"), + t.builtIn("/usr/include/x86_64-linux-gnu"), + t.builtIn("/usr/include")}; + CompilerOptionsBuilder compilerOptionsBuilder(t.finalize(), UseSystemHeader::No, + UseTweakedHeaderPaths::Yes, UseLanguageDefines::No, UseBuildSystemWarnings::No, + "dummy_version", ""); + compilerOptionsBuilder.addHeaderPathOptions(); + + QCOMPARE(compilerOptionsBuilder.options(), + (QStringList{"-nostdinc", "-nostdinc++", + "-isystem", t.toNative("/usr/include/c++/4.8"), + "-isystem", t.toNative("/usr/include/c++/4.8/backward"), + "-isystem", t.toNative("/usr/include/x86_64-linux-gnu/c++/4.8"), + "-isystem", t.toNative("/usr/local/include"), + "-isystem", "", + "-isystem", t.toNative("/usr/lib/gcc/x86_64-linux-gnu/4.8/include"), + "-isystem", t.toNative("/usr/include/x86_64-linux-gnu"), + "-isystem", t.toNative("/usr/include")})); +} + +void CompilerOptionsBuilderTest::testClangHeadersAndCppIncludePathsOrderNoVersion() +{ + TestHelper t; + t.targetTriple = "x86_64-w64-windows-gnu"; + t.headerPaths = { + t.builtIn("C:/mingw530/i686-w64-mingw32/include"), + t.builtIn("C:/mingw530/i686-w64-mingw32/include/c++"), + t.builtIn("C:/mingw530/i686-w64-mingw32/include/c++/i686-w64-mingw32"), + t.builtIn("C:/mingw530/i686-w64-mingw32/include/c++/backward")}; + CompilerOptionsBuilder compilerOptionsBuilder(t.finalize(), UseSystemHeader::No, + UseTweakedHeaderPaths::Yes, UseLanguageDefines::No, UseBuildSystemWarnings::No, + "dummy_version", ""); + compilerOptionsBuilder.addHeaderPathOptions(); + + QCOMPARE(compilerOptionsBuilder.options(), + (QStringList{"-nostdinc", "-nostdinc++", + "-isystem", t.toNative("C:/mingw530/i686-w64-mingw32/include/c++"), + "-isystem", t.toNative("C:/mingw530/i686-w64-mingw32/include/c++/i686-w64-mingw32"), + "-isystem", t.toNative("C:/mingw530/i686-w64-mingw32/include/c++/backward"), + "-isystem", "", + "-isystem", t.toNative("C:/mingw530/i686-w64-mingw32/include")})); +} + +void CompilerOptionsBuilderTest::testClangHeadersAndCppIncludePathsOrderAndroidClang() +{ + TestHelper t; + t.targetTriple = "i686-linux-android"; + t.headerPaths = { + t.builtIn("C:/Android/sdk/ndk-bundle/sysroot/usr/include/i686-linux-android"), + t.builtIn("C:/Android/sdk/ndk-bundle/sources/cxx-stl/llvm-libc++/include"), + t.builtIn("C:/Android/sdk/ndk-bundle/sources/android/support/include"), + t.builtIn("C:/Android/sdk/ndk-bundle/sources/cxx-stl/llvm-libc++abi/include"), + t.builtIn("C:/Android/sdk/ndk-bundle/sysroot/usr/include")}; + CompilerOptionsBuilder compilerOptionsBuilder(t.finalize(), UseSystemHeader::No, + UseTweakedHeaderPaths::Yes, UseLanguageDefines::No, UseBuildSystemWarnings::No, + "dummy_version", ""); + compilerOptionsBuilder.addHeaderPathOptions(); + + QCOMPARE(compilerOptionsBuilder.options(), + (QStringList{"-nostdinc", "-nostdinc++", + "-isystem", t.toNative("C:/Android/sdk/ndk-bundle/sources/cxx-stl/llvm-libc++/include"), + "-isystem", t.toNative("C:/Android/sdk/ndk-bundle/sources/cxx-stl/llvm-libc++abi/include"), + "-isystem", t.toNative(""), + "-isystem", t.toNative("C:/Android/sdk/ndk-bundle/sysroot/usr/include/i686-linux-android"), + "-isystem", t.toNative("C:/Android/sdk/ndk-bundle/sources/android/support/include"), + "-isystem", t.toNative("C:/Android/sdk/ndk-bundle/sysroot/usr/include")})); +} + +void CompilerOptionsBuilderTest::testNoPrecompiledHeader() +{ + TestHelper t; + t.finalize(); + t.compilerOptionsBuilder->addPrecompiledHeaderOptions(UsePrecompiledHeaders::No); + + QVERIFY(t.compilerOptionsBuilder->options().empty()); +} + +void CompilerOptionsBuilderTest::testUsePrecompiledHeader() +{ + TestHelper t; + t.finalize(); + t.compilerOptionsBuilder->addPrecompiledHeaderOptions(UsePrecompiledHeaders::Yes); + + QCOMPARE(t.compilerOptionsBuilder->options(), (QStringList{"-include", t.pchFileNativePath()})); +} + +void CompilerOptionsBuilderTest::testUsePrecompiledHeaderMsvc() +{ + TestHelper t; + t.toolchainType = Constants::MSVC_TOOLCHAIN_TYPEID; + CompilerOptionsBuilder compilerOptionsBuilder{t.finalize()}; + compilerOptionsBuilder.evaluateCompilerFlags(); + compilerOptionsBuilder.addPrecompiledHeaderOptions(UsePrecompiledHeaders::Yes); + + QCOMPARE(compilerOptionsBuilder.options(), (QStringList{"/FI", t.pchFileNativePath()})); +} + +void CompilerOptionsBuilderTest::testAddMacros() +{ + TestHelper t; + t.finalize(); + t.compilerOptionsBuilder->addMacros(Macros{Macro{"key", "value"}}); + + QCOMPARE(t.compilerOptionsBuilder->options(), QStringList("-Dkey=value")); +} + +void CompilerOptionsBuilderTest::testAddTargetTriple() +{ + TestHelper t; + t.finalize(); + t.compilerOptionsBuilder->addTargetTriple(); + + QCOMPARE(t.compilerOptionsBuilder->options(), QStringList("--target=x86_64-apple-darwin10")); +} + +void CompilerOptionsBuilderTest::testEnableCExceptions() +{ + TestHelper t; + t.languageVersion = Utils::LanguageVersion::C99; + t.finalize(); + t.compilerOptionsBuilder->enableExceptions(); + + QCOMPARE(t.compilerOptionsBuilder->options(), QStringList("-fexceptions")); +} + +void CompilerOptionsBuilderTest::testEnableCxxExceptions() +{ + TestHelper t; + t.finalize(); + t.compilerOptionsBuilder->enableExceptions(); + + QCOMPARE(t.compilerOptionsBuilder->options(), (QStringList{"-fcxx-exceptions", "-fexceptions"})); +} + +void CompilerOptionsBuilderTest::testInsertWrappedQtHeaders() +{ + TestHelper t; + CompilerOptionsBuilder compilerOptionsBuilder{t.finalize(), UseSystemHeader::Yes, + UseTweakedHeaderPaths::Yes, UseLanguageDefines::No, UseBuildSystemWarnings::No, + "dummy_version", ""}; + compilerOptionsBuilder.insertWrappedQtHeaders(); + + QVERIFY(Utils::contains(compilerOptionsBuilder.options(), + [](const QString &o) { return o.contains("wrappedQtHeaders"); })); +} + +void CompilerOptionsBuilderTest::testInsertWrappedMingwHeadersWithNonMingwToolchain() +{ + TestHelper t; + CompilerOptionsBuilder builder{t.finalize(), UseSystemHeader::Yes, UseTweakedHeaderPaths::Yes, + UseLanguageDefines::No, UseBuildSystemWarnings::No, "dummy_version", ""}; + builder.insertWrappedMingwHeaders(); + + QVERIFY(!Utils::contains(builder.options(), + [](const QString &o) { return o.contains("wrappedMingwHeaders"); })); +} + +void CompilerOptionsBuilderTest::testInsertWrappedMingwHeadersWithMingwToolchain() +{ + TestHelper t; + t.toolchainType = Constants::MINGW_TOOLCHAIN_TYPEID; + CompilerOptionsBuilder builder{t.finalize(), UseSystemHeader::Yes, UseTweakedHeaderPaths::Yes, + UseLanguageDefines::No, UseBuildSystemWarnings::No, "dummy_version", ""}; + builder.insertWrappedMingwHeaders(); + + QVERIFY(Utils::contains(builder.options(), + [](const QString &o) { return o.contains("wrappedMingwHeaders"); })); +} + +void CompilerOptionsBuilderTest::testSetLanguageVersion() +{ + TestHelper t; + t.finalize(); + t.compilerOptionsBuilder->updateFileLanguage(ProjectFile::CXXSource); + + QCOMPARE(t.compilerOptionsBuilder->options(), (QStringList{"-x", "c++"})); +} + +void CompilerOptionsBuilderTest::testSetLanguageVersionMsvc() +{ + TestHelper t; + t.toolchainType = Constants::MSVC_TOOLCHAIN_TYPEID; + CompilerOptionsBuilder compilerOptionsBuilder{t.finalize()}; + compilerOptionsBuilder.evaluateCompilerFlags(); + compilerOptionsBuilder.updateFileLanguage(ProjectFile::CXXSource); + + QCOMPARE(compilerOptionsBuilder.options(), QStringList("/TP")); +} + +void CompilerOptionsBuilderTest::testHandleLanguageExtension() +{ + TestHelper t; + t.languageVersion = Utils::LanguageVersion::CXX17; + t.languageExtensions = Utils::LanguageExtension::ObjectiveC; + t.finalize(); + t.compilerOptionsBuilder->updateFileLanguage(ProjectFile::CXXSource); + + QCOMPARE(t.compilerOptionsBuilder->options(), (QStringList{"-x", "objective-c++"})); +} + +void CompilerOptionsBuilderTest::testUpdateLanguageVersion() +{ + TestHelper t; + t.finalize(); + t.compilerOptionsBuilder->updateFileLanguage(ProjectFile::CXXSource); + t.compilerOptionsBuilder->updateFileLanguage(ProjectFile::CXXHeader); + + QCOMPARE(t.compilerOptionsBuilder->options(), (QStringList{"-x", "c++-header"})); +} + +void CompilerOptionsBuilderTest::testUpdateLanguageVersionMsvc() +{ + TestHelper t; + t.toolchainType = Constants::MSVC_TOOLCHAIN_TYPEID; + CompilerOptionsBuilder compilerOptionsBuilder{t.finalize()}; + compilerOptionsBuilder.evaluateCompilerFlags(); + compilerOptionsBuilder.updateFileLanguage(ProjectFile::CXXSource); + compilerOptionsBuilder.updateFileLanguage(ProjectFile::CSource); + + QCOMPARE(compilerOptionsBuilder.options(), QStringList("/TC")); +} + +void CompilerOptionsBuilderTest::testAddMsvcCompatibilityVersion() +{ + TestHelper t; + t.toolchainType = Constants::MSVC_TOOLCHAIN_TYPEID; + t.toolchainMacros.append(Macro{"_MSC_FULL_VER", "190000000"}); + t.finalize(); + t.compilerOptionsBuilder->addMsvcCompatibilityVersion(); + + QCOMPARE(t.compilerOptionsBuilder->options(), QStringList("-fms-compatibility-version=19.00")); +} + +void CompilerOptionsBuilderTest::testUndefineCppLanguageFeatureMacrosForMsvc2015() +{ + TestHelper t; + t.toolchainType = Constants::MSVC_TOOLCHAIN_TYPEID; + t.isMsvc2015 = true; + t.finalize(); + t.compilerOptionsBuilder->undefineCppLanguageFeatureMacrosForMsvc2015(); + + QVERIFY(t.compilerOptionsBuilder->options().contains("-U__cpp_aggregate_bases")); +} + +void CompilerOptionsBuilderTest::testAddDefineFunctionMacrosMsvc() +{ + TestHelper t; + t.toolchainType = Constants::MSVC_TOOLCHAIN_TYPEID; + t.finalize(); + t.compilerOptionsBuilder->addDefineFunctionMacrosMsvc(); + + QVERIFY(t.compilerOptionsBuilder->options().contains( + "-D__FUNCTION__=\"someLegalAndLongishFunctionNameThatWorksAroundQTCREATORBUG-24580\"")); +} + +void CompilerOptionsBuilderTest::testAddProjectConfigFileInclude() +{ + TestHelper t; + t.projectConfigFile = "dummy_file.h"; + t.finalize(); + t.compilerOptionsBuilder->addProjectConfigFileInclude(); + + QCOMPARE(t.compilerOptionsBuilder->options(), (QStringList{"-include", "dummy_file.h"})); +} + +void CompilerOptionsBuilderTest::testAddProjectConfigFileIncludeMsvc() +{ + TestHelper t; + t.projectConfigFile = "dummy_file.h"; + t.toolchainType = Constants::MSVC_TOOLCHAIN_TYPEID; + CompilerOptionsBuilder compilerOptionsBuilder{t.finalize()}; + compilerOptionsBuilder.evaluateCompilerFlags(); + compilerOptionsBuilder.addProjectConfigFileInclude(); + + QCOMPARE(compilerOptionsBuilder.options(), (QStringList{"/FI", "dummy_file.h"})); +} + +void CompilerOptionsBuilderTest::testNoUndefineClangVersionMacrosForNewMsvc() +{ + TestHelper t; + t.toolchainType = Constants::MSVC_TOOLCHAIN_TYPEID; + t.finalize(); + t.compilerOptionsBuilder->undefineClangVersionMacrosForMsvc(); + + QVERIFY(!t.compilerOptionsBuilder->options().contains("-U__clang__")); +} + +void CompilerOptionsBuilderTest::testUndefineClangVersionMacrosForOldMsvc() +{ + TestHelper t; + t.toolchainType = Constants::MSVC_TOOLCHAIN_TYPEID; + t.toolchainMacros = {Macro{"_MSC_FULL_VER", "1300"}, Macro{"_MSC_VER", "13"}}; + t.finalize(); + t.compilerOptionsBuilder->undefineClangVersionMacrosForMsvc(); + + QVERIFY(t.compilerOptionsBuilder->options().contains("-U__clang__")); +} + +void CompilerOptionsBuilderTest::testBuildAllOptions() +{ + TestHelper t; + t.extraFlags = QStringList{"-arch", "x86_64"}; + CompilerOptionsBuilder compilerOptionsBuilder(t.finalize(), UseSystemHeader::No, + UseTweakedHeaderPaths::Yes, UseLanguageDefines::No, UseBuildSystemWarnings::No, + "dummy_version", ""); + compilerOptionsBuilder.build(ProjectFile::CXXSource, UsePrecompiledHeaders::No); + + const QString wrappedQtHeadersPath = Utils::findOrDefault(compilerOptionsBuilder.options(), + [](const QString &o) { return o.contains("wrappedQtHeaders"); }); + const QString wrappedQtCoreHeadersPath = Utils::findOrDefault(compilerOptionsBuilder.options(), + [&t](const QString &o) { return o.contains(t.toNative("wrappedQtHeaders/QtCore")); }); + QCOMPARE(compilerOptionsBuilder.options(), + (QStringList{"-nostdinc", "-nostdinc++", "-arch", "x86_64", "-fsyntax-only", "-m64", + "--target=x86_64-apple-darwin10", "-x", "c++", "-std=c++17", + "-DprojectFoo=projectBar", "-I", wrappedQtHeadersPath, + "-I", wrappedQtCoreHeadersPath, + "-I", t.toNative("/tmp/path"), + "-I", t.toNative("/tmp/system_path"), + "-isystem", "", + "-isystem", t.toNative("/tmp/builtin_path")})); +} + +void CompilerOptionsBuilderTest::testBuildAllOptionsMsvc() +{ + TestHelper t; + t.toolchainType = Constants::MSVC_TOOLCHAIN_TYPEID; + CompilerOptionsBuilder compilerOptionsBuilder(t.finalize(), UseSystemHeader::No, + UseTweakedHeaderPaths::Yes, UseLanguageDefines::No, UseBuildSystemWarnings::No, + "dummy_version", ""); + compilerOptionsBuilder.build(ProjectFile::CXXSource, UsePrecompiledHeaders::No); + + const QString wrappedQtHeadersPath = Utils::findOrDefault(compilerOptionsBuilder.options(), + [](const QString &o) { return o.contains("wrappedQtHeaders"); }); + const QString wrappedQtCoreHeadersPath = Utils::findOrDefault(compilerOptionsBuilder.options(), + [&t](const QString &o) { return o.contains(t.toNative("wrappedQtHeaders/QtCore")); }); + QCOMPARE(compilerOptionsBuilder.options(), + (QStringList{"-nostdinc", "-nostdinc++", "--driver-mode=cl", "/Zs", "-m64", + "--target=x86_64-apple-darwin10", "/TP", "/std:c++17", + "-fms-compatibility-version=19.00", "-DprojectFoo=projectBar", + "-D__FUNCSIG__=\"void __cdecl someLegalAndLongishFunctionNameThatWorksAroundQTCREATORBUG-24580(void)\"", + "-D__FUNCTION__=\"someLegalAndLongishFunctionNameThatWorksAroundQTCREATORBUG-24580\"", + "-D__FUNCDNAME__=\"?someLegalAndLongishFunctionNameThatWorksAroundQTCREATORBUG-24580@@YAXXZ\"", + "-I", wrappedQtHeadersPath, + "-I", wrappedQtCoreHeadersPath, + "-I", t.toNative("/tmp/path"), + "-I", t.toNative("/tmp/system_path"), + "/clang:-isystem", "/clang:", + "/clang:-isystem", "/clang:" + t.toNative("/tmp/builtin_path")})); +} + +void CompilerOptionsBuilderTest::testBuildAllOptionsMsvcWithExceptions() +{ + TestHelper t; + t.toolchainType = Constants::MSVC_TOOLCHAIN_TYPEID; + t.toolchainMacros.append(Macro{"_CPPUNWIND", "1"}); + CompilerOptionsBuilder compilerOptionsBuilder(t.finalize(), UseSystemHeader::No, + UseTweakedHeaderPaths::Yes, UseLanguageDefines::No, UseBuildSystemWarnings::No, + "dummy_version", ""); + compilerOptionsBuilder.build(ProjectFile::CXXSource, UsePrecompiledHeaders::No); + + const QString wrappedQtHeadersPath = Utils::findOrDefault(compilerOptionsBuilder.options(), + [](const QString &o) { return o.contains("wrappedQtHeaders"); }); + const QString wrappedQtCoreHeadersPath = Utils::findOrDefault(compilerOptionsBuilder.options(), + [&t](const QString &o) { return o.contains(t.toNative("wrappedQtHeaders/QtCore")); }); + QCOMPARE(compilerOptionsBuilder.options(), + (QStringList{"-nostdinc", "-nostdinc++", "--driver-mode=cl", "/Zs", "-m64", + "--target=x86_64-apple-darwin10", "/TP", "/std:c++17", "-fcxx-exceptions", + "-fexceptions", "-fms-compatibility-version=19.00", + "-DprojectFoo=projectBar", + "-D__FUNCSIG__=\"void __cdecl someLegalAndLongishFunctionNameThatWorksAroundQTCREATORBUG-24580(void)\"", + "-D__FUNCTION__=\"someLegalAndLongishFunctionNameThatWorksAroundQTCREATORBUG-24580\"", + "-D__FUNCDNAME__=\"?someLegalAndLongishFunctionNameThatWorksAroundQTCREATORBUG-24580@@YAXXZ\"", + "-I", wrappedQtHeadersPath, + "-I", wrappedQtCoreHeadersPath, + "-I", t.toNative("/tmp/path"), + "-I", t.toNative("/tmp/system_path"), + "/clang:-isystem", "/clang:", + "/clang:-isystem", "/clang:" + t.toNative("/tmp/builtin_path")})); +} + +} // namespace Internal +} // namespace CppTools diff --git a/src/plugins/cpptools/compileroptionsbuilder_test.h b/src/plugins/cpptools/compileroptionsbuilder_test.h new file mode 100644 index 0000000000..83de91a461 --- /dev/null +++ b/src/plugins/cpptools/compileroptionsbuilder_test.h @@ -0,0 +1,80 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#pragma once + +#include <QObject> + +namespace CppTools::Internal { + +class CompilerOptionsBuilderTest : public QObject +{ + Q_OBJECT + +private slots: + void testAddProjectMacros(); + void testUnknownFlagsAreForwarded(); + void testWarningsFlagsAreNotFilteredIfRequested(); + void testDiagnosticOptionsAreRemoved(); + void testCLanguageVersionIsRewritten(); + void testLanguageVersionIsExplicitlySetIfNotProvided(); + void testLanguageVersionIsExplicitlySetIfNotProvidedMsvc(); + void testAddWordWidth(); + void testHeaderPathOptionsOrder(); + void testHeaderPathOptionsOrderMsvc(); + void testUseSystemHeader(); + void testNoClangHeadersPath(); + void testClangHeadersAndCppIncludePathsOrderMacOs(); + void testClangHeadersAndCppIncludePathsOrderLinux(); + void testClangHeadersAndCppIncludePathsOrderNoVersion(); + void testClangHeadersAndCppIncludePathsOrderAndroidClang(); + void testNoPrecompiledHeader(); + void testUsePrecompiledHeader(); + void testUsePrecompiledHeaderMsvc(); + void testAddMacros(); + void testAddTargetTriple(); + void testEnableCExceptions(); + void testEnableCxxExceptions(); + void testInsertWrappedQtHeaders(); + void testInsertWrappedMingwHeadersWithNonMingwToolchain(); + void testInsertWrappedMingwHeadersWithMingwToolchain(); + void testSetLanguageVersion(); + void testSetLanguageVersionMsvc(); + void testHandleLanguageExtension(); + void testUpdateLanguageVersion(); + void testUpdateLanguageVersionMsvc(); + void testAddMsvcCompatibilityVersion(); + void testUndefineCppLanguageFeatureMacrosForMsvc2015(); + void testAddDefineFunctionMacrosMsvc(); + void testAddProjectConfigFileInclude(); + void testAddProjectConfigFileIncludeMsvc(); + void testNoUndefineClangVersionMacrosForNewMsvc(); + void testUndefineClangVersionMacrosForOldMsvc(); + void testBuildAllOptions(); + void testBuildAllOptionsMsvc(); + void testBuildAllOptionsMsvcWithExceptions(); +}; + +} // namespace CppTools::Internal diff --git a/src/plugins/cpptools/cppcodegen_test.cpp b/src/plugins/cpptools/cppcodegen_test.cpp index 088e55c9de..6c4ac101eb 100644 --- a/src/plugins/cpptools/cppcodegen_test.cpp +++ b/src/plugins/cpptools/cppcodegen_test.cpp @@ -23,7 +23,8 @@ ** ****************************************************************************/ -#include "cpptoolsplugin.h" +#include "cppcodegen_test.h" + #include "cpptoolstestcase.h" #include "insertionpointlocator.h" @@ -73,7 +74,7 @@ Document::Ptr createDocumentAndFile(Tests::TemporaryDir *temporaryDir, /*! Should insert at line 3, column 1, with "public:\n" as prefix and without suffix. */ -void CppToolsPlugin::test_codegen_public_in_empty_class() +void CodegenTest::testPublicInEmptyClass() { const QByteArray src = "\n" "class Foo\n" // line 1 @@ -106,7 +107,7 @@ void CppToolsPlugin::test_codegen_public_in_empty_class() /*! Should insert at line 3, column 1, without prefix and without suffix. */ -void CppToolsPlugin::test_codegen_public_in_nonempty_class() +void CodegenTest::testPublicInNonemptyClass() { const QByteArray src = "\n" "class Foo\n" // line 1 @@ -140,7 +141,7 @@ void CppToolsPlugin::test_codegen_public_in_nonempty_class() /*! Should insert at line 3, column 1, with "public:\n" as prefix and "\n suffix. */ -void CppToolsPlugin::test_codegen_public_before_protected() +void CodegenTest::testPublicBeforeProtected() { const QByteArray src = "\n" "class Foo\n" // line 1 @@ -175,7 +176,7 @@ void CppToolsPlugin::test_codegen_public_before_protected() Should insert at line 4, column 1, with "private:\n" as prefix and without suffix. */ -void CppToolsPlugin::test_codegen_private_after_protected() +void CodegenTest::testPrivateAfterProtected() { const QByteArray src = "\n" "class Foo\n" // line 1 @@ -210,7 +211,7 @@ void CppToolsPlugin::test_codegen_private_after_protected() Should insert at line 4, column 1, with "protected:\n" as prefix and without suffix. */ -void CppToolsPlugin::test_codegen_protected_in_nonempty_class() +void CodegenTest::testProtectedInNonemptyClass() { const QByteArray src = "\n" "class Foo\n" // line 1 @@ -244,7 +245,7 @@ void CppToolsPlugin::test_codegen_protected_in_nonempty_class() /*! Should insert at line 4, column 1, with "protected\n" as prefix and "\n" suffix. */ -void CppToolsPlugin::test_codegen_protected_between_public_and_private() +void CodegenTest::testProtectedBetweenPublicAndPrivate() { const QByteArray src = "\n" "class Foo\n" // line 1 @@ -283,7 +284,7 @@ void CppToolsPlugin::test_codegen_protected_between_public_and_private() This is the typical Qt Designer case, with test-input like what the integration generates. */ -void CppToolsPlugin::test_codegen_qtdesigner_integration() +void CodegenTest::testQtdesignerIntegration() { const QByteArray src = "/**** Some long (C)opyright notice ****/\n" "#ifndef MAINWINDOW_H\n" @@ -332,7 +333,7 @@ void CppToolsPlugin::test_codegen_qtdesigner_integration() QCOMPARE(loc.column(), 1); } -void CppToolsPlugin::test_codegen_definition_empty_class() +void CodegenTest::testDefinitionEmptyClass() { Tests::TemporaryDir temporaryDir; QVERIFY(temporaryDir.isValid()); @@ -378,7 +379,7 @@ void CppToolsPlugin::test_codegen_definition_empty_class() QCOMPARE(loc.column(), 1); } -void CppToolsPlugin::test_codegen_definition_first_member() +void CodegenTest::testDefinitionFirstMember() { Tests::TemporaryDir temporaryDir; QVERIFY(temporaryDir.isValid()); @@ -436,7 +437,7 @@ void CppToolsPlugin::test_codegen_definition_first_member() QCOMPARE(loc.prefix(), QString()); } -void CppToolsPlugin::test_codegen_definition_last_member() +void CodegenTest::testDefinitionLastMember() { Tests::TemporaryDir temporaryDir; QVERIFY(temporaryDir.isValid()); @@ -495,7 +496,7 @@ void CppToolsPlugin::test_codegen_definition_last_member() QCOMPARE(loc.suffix(), QString()); } -void CppToolsPlugin::test_codegen_definition_middle_member() +void CodegenTest::testDefinitionMiddleMember() { Tests::TemporaryDir temporaryDir; QVERIFY(temporaryDir.isValid()); @@ -561,7 +562,7 @@ void CppToolsPlugin::test_codegen_definition_middle_member() QCOMPARE(loc.suffix(), QString()); } -void CppToolsPlugin::test_codegen_definition_middle_member_surrounded_by_undefined() +void CodegenTest::testDefinitionMiddleMemberSurroundedByUndefined() { Tests::TemporaryDir temporaryDir; QVERIFY(temporaryDir.isValid()); @@ -621,7 +622,7 @@ void CppToolsPlugin::test_codegen_definition_middle_member_surrounded_by_undefin QCOMPARE(loc.suffix(), QLatin1String("\n\n")); } -void CppToolsPlugin::test_codegen_definition_member_specific_file() +void CodegenTest::testDefinitionMemberSpecificFile() { Tests::TemporaryDir temporaryDir; QVERIFY(temporaryDir.isValid()); diff --git a/src/plugins/cpptools/cppcodegen_test.h b/src/plugins/cpptools/cppcodegen_test.h new file mode 100644 index 0000000000..f3dfd1426b --- /dev/null +++ b/src/plugins/cpptools/cppcodegen_test.h @@ -0,0 +1,53 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#pragma once + +#include <QObject> + +namespace CppTools::Internal { + +class CodegenTest : public QObject +{ + Q_OBJECT +public: + +private slots: + void testPublicInEmptyClass(); + void testPublicInNonemptyClass(); + void testPublicBeforeProtected(); + void testPrivateAfterProtected(); + void testProtectedInNonemptyClass(); + void testProtectedBetweenPublicAndPrivate(); + void testQtdesignerIntegration(); + void testDefinitionEmptyClass(); + void testDefinitionFirstMember(); + void testDefinitionLastMember(); + void testDefinitionMiddleMember(); + void testDefinitionMiddleMemberSurroundedByUndefined(); + void testDefinitionMemberSpecificFile(); +}; + +} // namespace CppTools::Internal diff --git a/src/plugins/cpptools/cppcodemodelinspectordumper.cpp b/src/plugins/cpptools/cppcodemodelinspectordumper.cpp index 12ed4b3f16..44c968e10b 100644 --- a/src/plugins/cpptools/cppcodemodelinspectordumper.cpp +++ b/src/plugins/cpptools/cppcodemodelinspectordumper.cpp @@ -26,6 +26,7 @@ #include "cppcodemodelinspectordumper.h" #include "cppmodelmanager.h" +#include "cpptoolsreuse.h" #include "cppworkingcopy.h" #include <app/app_version.h> @@ -502,7 +503,7 @@ static void printIncludeType(QTextStream &out, ProjectExplorer::HeaderPathType t } } -void Dumper::dumpProjectInfos( const QList<ProjectInfo> &projectInfos) +void Dumper::dumpProjectInfos(const QList<ProjectInfo::Ptr> &projectInfos) { const QByteArray i1 = indent(1); const QByteArray i2 = indent(2); @@ -510,18 +511,18 @@ void Dumper::dumpProjectInfos( const QList<ProjectInfo> &projectInfos) const QByteArray i4 = indent(4); m_out << "Projects loaded: " << projectInfos.size() << "{{{1\n"; - foreach (const ProjectInfo &info, projectInfos) { - const QPointer<ProjectExplorer::Project> project = info.project(); - m_out << i1 << "Project " << project->displayName() - << " (" << project->projectFilePath().toUserOutput() << "){{{2\n"; + foreach (const ProjectInfo::Ptr &info, projectInfos) { + m_out << i1 << "Project " << info->projectName() + << " (" << info->projectFilePath().toUserOutput() << "){{{2\n"; - const QVector<ProjectPart::Ptr> projectParts = info.projectParts(); + const QVector<ProjectPart::Ptr> projectParts = info->projectParts(); foreach (const ProjectPart::Ptr &part, projectParts) { QString projectName = QLatin1String("<None>"); - QString projectFilePath = QLatin1String("<None>"); - if (ProjectExplorer::Project *project = part->project) { - projectName = project->displayName(); - projectFilePath = project->projectFilePath().toUserOutput(); + QString projectFilePath = "<None>"; + if (part->hasProject()) { + projectFilePath = part->topLevelProject.toUserOutput(); + if (const ProjectExplorer::Project * const project = projectForProjectPart(*part)) + projectName = project->displayName(); } if (!part->projectConfigFile.isEmpty()) m_out << i3 << "Project Config File: " << part->projectConfigFile << "\n"; diff --git a/src/plugins/cpptools/cppcodemodelinspectordumper.h b/src/plugins/cpptools/cppcodemodelinspectordumper.h index c7769315c1..ef48abbfd5 100644 --- a/src/plugins/cpptools/cppcodemodelinspectordumper.h +++ b/src/plugins/cpptools/cppcodemodelinspectordumper.h @@ -70,7 +70,7 @@ public: const QString &logFileId = QString()); ~Dumper(); - void dumpProjectInfos(const QList<CppTools::ProjectInfo> &projectInfos); + void dumpProjectInfos(const QList<CppTools::ProjectInfo::Ptr> &projectInfos); void dumpSnapshot(const CPlusPlus::Snapshot &snapshot, const QString &title, bool isGlobalSnapshot = false); diff --git a/src/plugins/cpptools/cppcodemodelsettings.cpp b/src/plugins/cpptools/cppcodemodelsettings.cpp index c7cfc2d0cb..9a07c85ef2 100644 --- a/src/plugins/cpptools/cppcodemodelsettings.cpp +++ b/src/plugins/cpptools/cppcodemodelsettings.cpp @@ -69,6 +69,7 @@ static QString useClangdKey() { return QLatin1String("UseClangd"); } static QString clangdPathKey() { return QLatin1String("ClangdPath"); } static QString clangdIndexingKey() { return QLatin1String("ClangdIndexing"); } static QString clangdThreadLimitKey() { return QLatin1String("ClangdThreadLimit"); } +static QString clangdDocumentThresholdKey() { return QLatin1String("ClangdDocumentThreshold"); } static QString clangdUseGlobalSettingsKey() { return QLatin1String("useGlobalSettings"); } static FilePath g_defaultClangdFilePath; @@ -76,14 +77,14 @@ static FilePath fallbackClangdFilePath() { if (g_defaultClangdFilePath.exists()) return g_defaultClangdFilePath; - return FilePath::fromString("clangd"); + return "clangd"; } -static Utils::Id clangDiagnosticConfigIdFromSettings(QSettings *s) +static Id clangDiagnosticConfigIdFromSettings(QSettings *s) { - QTC_ASSERT(s->group() == QLatin1String(Constants::CPPTOOLS_SETTINGSGROUP), return Utils::Id()); + QTC_ASSERT(s->group() == QLatin1String(Constants::CPPTOOLS_SETTINGSGROUP), return Id()); - return Utils::Id::fromSetting( + return Id::fromSetting( s->value(clangDiagnosticConfigKey(), initialClangDiagnosticConfigId().toSetting())); } @@ -132,7 +133,7 @@ static ClangDiagnosticConfigs removedBuiltinConfigs() return configs; } -static ClangDiagnosticConfig convertToCustomConfig(const Utils::Id &id) +static ClangDiagnosticConfig convertToCustomConfig(const Id &id) { const ClangDiagnosticConfig config = Utils::findOrDefault(removedBuiltinConfigs(), [id](const ClangDiagnosticConfig &config) { @@ -150,7 +151,7 @@ void CppCodeModelSettings::fromSettings(QSettings *s) // Qt Creator 4.11 removes some built-in configs. bool write = false; - const Utils::Id id = m_clangDiagnosticConfigId; + const Id id = m_clangDiagnosticConfigId; if (id == "Builtin.Pedantic" || id == "Builtin.EverythingWithExceptions") { // If one of them was used, continue to use it, but convert it to a custom config. const ClangDiagnosticConfig customConfig = convertToCustomConfig(id); @@ -191,7 +192,7 @@ void CppCodeModelSettings::toSettings(QSettings *s) { s->beginGroup(QLatin1String(Constants::CPPTOOLS_SETTINGSGROUP)); const ClangDiagnosticConfigs previousConfigs = diagnosticConfigsFromSettings(s); - const Utils::Id previousConfigId = clangDiagnosticConfigIdFromSettings(s); + const Id previousConfigId = clangDiagnosticConfigIdFromSettings(s); diagnosticConfigsToSettings(s, m_clangCustomDiagnosticConfigs); @@ -205,7 +206,7 @@ void CppCodeModelSettings::toSettings(QSettings *s) s->endGroup(); - QVector<Utils::Id> invalidated + QVector<Id> invalidated = ClangDiagnosticConfigsModel::changedOrRemovedConfigs(previousConfigs, m_clangCustomDiagnosticConfigs); @@ -217,19 +218,19 @@ void CppCodeModelSettings::toSettings(QSettings *s) emit changed(); } -Utils::Id CppCodeModelSettings::clangDiagnosticConfigId() const +Id CppCodeModelSettings::clangDiagnosticConfigId() const { if (!diagnosticConfigsModel().hasConfigWithId(m_clangDiagnosticConfigId)) return defaultClangDiagnosticConfigId(); return m_clangDiagnosticConfigId; } -void CppCodeModelSettings::setClangDiagnosticConfigId(const Utils::Id &configId) +void CppCodeModelSettings::setClangDiagnosticConfigId(const Id &configId) { m_clangDiagnosticConfigId = configId; } -Utils::Id CppCodeModelSettings::defaultClangDiagnosticConfigId() +Id CppCodeModelSettings::defaultClangDiagnosticConfigId() { return initialClangDiagnosticConfigId(); } @@ -309,7 +310,7 @@ ClangdSettings &ClangdSettings::instance() return settings; } -void ClangdSettings::setDefaultClangdPath(const Utils::FilePath &filePath) +void ClangdSettings::setDefaultClangdPath(const FilePath &filePath) { g_defaultClangdFilePath = filePath; } @@ -342,7 +343,8 @@ void ClangdSettings::saveSettings() #ifdef WITH_TESTS void ClangdSettings::setUseClangd(bool use) { instance().m_data.useClangd = use; } -void ClangdSettings::setClangdFilePath(const Utils::FilePath &filePath) + +void ClangdSettings::setClangdFilePath(const FilePath &filePath) { instance().m_data.executableFilePath = filePath; } @@ -402,6 +404,7 @@ QVariantMap ClangdSettings::Data::toMap() const map.insert(clangdPathKey(), executableFilePath.toString()); map.insert(clangdIndexingKey(), enableIndexing); map.insert(clangdThreadLimitKey(), workerThreadLimit); + map.insert(clangdDocumentThresholdKey(), documentUpdateThreshold); return map; } @@ -411,4 +414,5 @@ void ClangdSettings::Data::fromMap(const QVariantMap &map) executableFilePath = FilePath::fromString(map.value(clangdPathKey()).toString()); enableIndexing = map.value(clangdIndexingKey(), true).toBool(); workerThreadLimit = map.value(clangdThreadLimitKey(), 0).toInt(); + documentUpdateThreshold = map.value(clangdDocumentThresholdKey(), 500).toInt(); } diff --git a/src/plugins/cpptools/cppcodemodelsettings.h b/src/plugins/cpptools/cppcodemodelsettings.h index d4da25f3b0..c229638d95 100644 --- a/src/plugins/cpptools/cppcodemodelsettings.h +++ b/src/plugins/cpptools/cppcodemodelsettings.h @@ -112,6 +112,7 @@ public: int workerThreadLimit = 0; bool useClangd = false; bool enableIndexing = true; + int documentUpdateThreshold = 500; }; ClangdSettings(const Data &data) : m_data(data) {} @@ -123,6 +124,7 @@ public: Utils::FilePath clangdFilePath() const; bool indexingEnabled() const { return m_data.enableIndexing; } int workerThreadLimit() const { return m_data.workerThreadLimit; } + int documentUpdateThreshold() const { return m_data.documentUpdateThreshold; } void setData(const Data &data); Data data() const { return m_data; } @@ -149,7 +151,8 @@ inline bool operator==(const ClangdSettings::Data &s1, const ClangdSettings::Dat return s1.useClangd == s2.useClangd && s1.executableFilePath == s2.executableFilePath && s1.workerThreadLimit == s2.workerThreadLimit - && s1.enableIndexing == s2.enableIndexing; + && s1.enableIndexing == s2.enableIndexing + && s1.documentUpdateThreshold == s2.documentUpdateThreshold; } inline bool operator!=(const ClangdSettings::Data &s1, const ClangdSettings::Data &s2) { diff --git a/src/plugins/cpptools/cppcodemodelsettingspage.cpp b/src/plugins/cpptools/cppcodemodelsettingspage.cpp index dc04dc3cb8..123497d4af 100644 --- a/src/plugins/cpptools/cppcodemodelsettingspage.cpp +++ b/src/plugins/cpptools/cppcodemodelsettingspage.cpp @@ -95,9 +95,8 @@ void CppCodeModelSettingsWidget::setupClangCodeModelWidgets() m_ui->clangDiagnosticConfigsSelectionWidget ->refresh(diagnosticConfigsModel(), m_settings->clangDiagnosticConfigId(), - [](const CppTools::ClangDiagnosticConfigs &configs, - const Utils::Id &configToSelect) { - return new CppTools::ClangDiagnosticConfigsWidget(configs, configToSelect); + [](const ClangDiagnosticConfigs &configs, const Utils::Id &configToSelect) { + return new ClangDiagnosticConfigsWidget(configs, configToSelect); }); const bool isClangActive = CppModelManager::instance()->isClangCodeModelActive(); @@ -197,6 +196,7 @@ public: QCheckBox useClangdCheckBox; QCheckBox indexingCheckBox; QSpinBox threadLimitSpinBox; + QSpinBox documentUpdateThreshold; Utils::PathChooser clangdChooser; }; @@ -216,6 +216,15 @@ ClangdSettingsWidget::ClangdSettingsWidget(const ClangdSettings::Data &settingsD "the project is first opened.")); d->threadLimitSpinBox.setValue(settings.workerThreadLimit()); d->threadLimitSpinBox.setSpecialValueText("Automatic"); + d->documentUpdateThreshold.setMinimum(50); + d->documentUpdateThreshold.setMaximum(10000); + d->documentUpdateThreshold.setValue(settings.documentUpdateThreshold()); + d->documentUpdateThreshold.setSingleStep(100); + d->documentUpdateThreshold.setSuffix(" ms"); + d->documentUpdateThreshold.setToolTip( + tr("Defines the amount of time Qt Creator waits before sending document changes to the " + "server.\n" + "If the document changes again while waiting, this timeout resets.\n")); const auto layout = new QVBoxLayout(this); layout->addWidget(&d->useClangdCheckBox); @@ -227,8 +236,13 @@ ClangdSettingsWidget::ClangdSettingsWidget(const ClangdSettings::Data &settingsD const auto threadLimitLayout = new QHBoxLayout; threadLimitLayout->addWidget(&d->threadLimitSpinBox); threadLimitLayout->addStretch(1); - const auto threadLimitLabel = new QLabel(tr("Set worker thread count:")); + const auto threadLimitLabel = new QLabel(tr("Worker thread count:")); formLayout->addRow(threadLimitLabel, threadLimitLayout); + const auto documentUpdateThresholdLayout = new QHBoxLayout; + documentUpdateThresholdLayout->addWidget(&d->documentUpdateThreshold); + documentUpdateThresholdLayout->addStretch(1); + const auto documentUpdateThresholdLabel = new QLabel(tr("Document update threshold:")); + formLayout->addRow(documentUpdateThresholdLabel, documentUpdateThresholdLayout); layout->addLayout(formLayout); layout->addStretch(1); @@ -265,6 +279,7 @@ ClangdSettings::Data ClangdSettingsWidget::settingsData() const data.executableFilePath = d->clangdChooser.filePath(); data.enableIndexing = d->indexingCheckBox.isChecked(); data.workerThreadLimit = d->threadLimitSpinBox.value(); + data.documentUpdateThreshold = d->documentUpdateThreshold.value(); return data; } diff --git a/src/plugins/cpptools/cppcompletion_test.cpp b/src/plugins/cpptools/cppcompletion_test.cpp index 73a4035d53..1af079083b 100644 --- a/src/plugins/cpptools/cppcompletion_test.cpp +++ b/src/plugins/cpptools/cppcompletion_test.cpp @@ -23,10 +23,11 @@ ** ****************************************************************************/ +#include "cppcompletion_test.h" + #include "cppcompletionassist.h" #include "cppdoxygen.h" #include "cppmodelmanager.h" -#include "cpptoolsplugin.h" #include "cpptoolstestcase.h" #include <texteditor/codeassist/iassistproposal.h> @@ -34,6 +35,7 @@ #include <texteditor/textdocument.h> #include <coreplugin/editormanager/editormanager.h> +#include <utils/algorithm.h> #include <utils/changeset.h> #include <utils/textutils.h> #include <utils/fileutils.h> @@ -185,7 +187,7 @@ bool isDoxygenTagCompletion(const QStringList &list) } // anonymous namespace -void CppToolsPlugin::test_completion_basic_1() +void CompletionTest::testCompletionBasic1() { const QByteArray source = "class Foo\n" @@ -217,7 +219,7 @@ void CppToolsPlugin::test_completion_basic_1() QVERIFY(!memberCompletions.contains(QLatin1String("f"))); } -void CppToolsPlugin::test_completion_prefix_first_QTCREATORBUG_8737() +void CompletionTest::testCompletionPrefixFirstQTCREATORBUG_8737() { const QByteArray source = "void f()\n" @@ -237,7 +239,7 @@ void CppToolsPlugin::test_completion_prefix_first_QTCREATORBUG_8737() QVERIFY(completions.contains(QLatin1String("a_b_c"))); } -void CppToolsPlugin::test_completion_prefix_first_QTCREATORBUG_9236() +void CompletionTest::testCompletionPrefixFirstQTCREATORBUG_9236() { const QByteArray source = "class r_etclass\n" @@ -266,7 +268,7 @@ void CppToolsPlugin::test_completion_prefix_first_QTCREATORBUG_9236() QVERIFY(completions.contains(QLatin1String("r_et"))); } -void CppToolsPlugin::test_completion_template_function() +void CompletionTest::testCompletionTemplateFunction() { QFETCH(QByteArray, code); QFETCH(QStringList, expectedCompletions); @@ -282,7 +284,7 @@ void CppToolsPlugin::test_completion_template_function() } } -void CppToolsPlugin::test_completion_template_function_data() +void CompletionTest::testCompletionTemplateFunction_data() { QTest::addColumn<QByteArray>("code"); QTest::addColumn<QStringList>("expectedCompletions"); @@ -322,7 +324,7 @@ void CppToolsPlugin::test_completion_template_function_data() << code << completions; } -void CppToolsPlugin::test_completion() +void CompletionTest::testCompletion() { QFETCH(QByteArray, code); QFETCH(QByteArray, prefix); @@ -348,7 +350,7 @@ void CppToolsPlugin::test_completion() QCOMPARE(actualCompletions, expectedCompletions); } -void CppToolsPlugin::test_global_completion_data() +void CompletionTest::testGlobalCompletion_data() { QTest::addColumn<QByteArray>("code"); QTest::addColumn<QByteArray>("prefix"); @@ -378,7 +380,7 @@ void CppToolsPlugin::test_global_completion_data() } } -void CppToolsPlugin::test_global_completion() +void CompletionTest::testGlobalCompletion() { QFETCH(QByteArray, code); QFETCH(QByteArray, prefix); @@ -391,7 +393,7 @@ void CppToolsPlugin::test_global_completion() QVERIFY(Utils::toSet(completions).contains(Utils::toSet(requiredCompletionItems))); } -void CppToolsPlugin::test_doxygen_tag_completion_data() +void CompletionTest::testDoxygenTagCompletion_data() { QTest::addColumn<QByteArray>("code"); @@ -407,7 +409,7 @@ void CppToolsPlugin::test_doxygen_tag_completion_data() " */\n"); } -void CppToolsPlugin::test_doxygen_tag_completion() +void CompletionTest::testDoxygenTagCompletion() { QFETCH(QByteArray, code); @@ -435,7 +437,7 @@ static void enumTestCase(const QByteArray &tag, const QByteArray &source, << QStringList({"val1", "val2", "val3"}); } -void CppToolsPlugin::test_completion_data() +void CompletionTest::testCompletion_data() { QTest::addColumn<QByteArray>("code"); QTest::addColumn<QByteArray>("prefix"); @@ -2747,7 +2749,7 @@ void CppToolsPlugin::test_completion_data() ) << _("s.begin()->") << QStringList({"Foo", "bar"}); } -void CppToolsPlugin::test_completion_member_access_operator() +void CompletionTest::testCompletionMemberAccessOperator() { QFETCH(QByteArray, code); QFETCH(QByteArray, prefix); @@ -2768,7 +2770,7 @@ void CppToolsPlugin::test_completion_member_access_operator() QCOMPARE(replaceAccessOperator, expectedReplaceAccessOperator); } -void CppToolsPlugin::test_completion_member_access_operator_data() +void CompletionTest::testCompletionMemberAccessOperator_data() { QTest::addColumn<QByteArray>("code"); QTest::addColumn<QByteArray>("prefix"); diff --git a/src/plugins/cpptools/cppcompletion_test.h b/src/plugins/cpptools/cppcompletion_test.h new file mode 100644 index 0000000000..c27b45223c --- /dev/null +++ b/src/plugins/cpptools/cppcompletion_test.h @@ -0,0 +1,58 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#pragma once + +#include <QObject> + +namespace CppTools::Internal { + +class CompletionTest : public QObject +{ + Q_OBJECT + +private slots: + void testCompletionBasic1(); + + void testCompletionTemplateFunction_data(); + void testCompletionTemplateFunction(); + + void testCompletion_data(); + void testCompletion(); + + void testGlobalCompletion_data(); + void testGlobalCompletion(); + + void testDoxygenTagCompletion_data(); + void testDoxygenTagCompletion(); + + void testCompletionMemberAccessOperator_data(); + void testCompletionMemberAccessOperator(); + + void testCompletionPrefixFirstQTCREATORBUG_8737(); + void testCompletionPrefixFirstQTCREATORBUG_9236(); +}; + +} // namespace CppTools::Internal diff --git a/src/plugins/cpptools/cppfilesettingspage.cpp b/src/plugins/cpptools/cppfilesettingspage.cpp index f63eb41b24..d289e6334e 100644 --- a/src/plugins/cpptools/cppfilesettingspage.cpp +++ b/src/plugins/cpptools/cppfilesettingspage.cpp @@ -51,6 +51,8 @@ #include <QTextCodec> #include <QTextStream> +using namespace Utils; + namespace CppTools { namespace Internal { @@ -271,8 +273,8 @@ public: private: void slotEdit(); - QString licenseTemplatePath() const; - void setLicenseTemplatePath(const QString &); + FilePath licenseTemplatePath() const; + void setLicenseTemplatePath(const FilePath &); Ui::CppFileSettingsPage m_ui; CppFileSettings *m_settings = nullptr; @@ -301,14 +303,14 @@ CppFileSettingsWidget::CppFileSettingsWidget(CppFileSettings *settings) setSettings(*m_settings); } -QString CppFileSettingsWidget::licenseTemplatePath() const +FilePath CppFileSettingsWidget::licenseTemplatePath() const { - return m_ui.licenseTemplatePathChooser->filePath().toString(); + return m_ui.licenseTemplatePathChooser->filePath(); } -void CppFileSettingsWidget::setLicenseTemplatePath(const QString &lp) +void CppFileSettingsWidget::setLicenseTemplatePath(const FilePath &lp) { - m_ui.licenseTemplatePathChooser->setPath(lp); + m_ui.licenseTemplatePathChooser->setFilePath(lp); } static QStringList trimmedPaths(const QString &paths) @@ -330,7 +332,7 @@ void CppFileSettingsWidget::apply() rc.sourceSuffix = m_ui.sourceSuffixComboBox->currentText(); rc.headerSearchPaths = trimmedPaths(m_ui.headerSearchPathsEdit->text()); rc.sourceSearchPaths = trimmedPaths(m_ui.sourceSearchPathsEdit->text()); - rc.licenseTemplatePath = licenseTemplatePath(); + rc.licenseTemplatePath = licenseTemplatePath().toString(); if (rc == *m_settings) return; @@ -358,18 +360,18 @@ void CppFileSettingsWidget::setSettings(const CppFileSettings &s) setComboText(m_ui.sourceSuffixComboBox, s.sourceSuffix); m_ui.headerSearchPathsEdit->setText(s.headerSearchPaths.join(comma)); m_ui.sourceSearchPathsEdit->setText(s.sourceSearchPaths.join(comma)); - setLicenseTemplatePath(s.licenseTemplatePath); + setLicenseTemplatePath(FilePath::fromString(s.licenseTemplatePath)); } void CppFileSettingsWidget::slotEdit() { - QString path = licenseTemplatePath(); + FilePath path = licenseTemplatePath(); if (path.isEmpty()) { // Pick a file name and write new template, edit with C++ - path = QFileDialog::getSaveFileName(this, tr("Choose Location for New License Template File")); + path = FileUtils::getSaveFilePath(this, tr("Choose Location for New License Template File")); if (path.isEmpty()) return; - Utils::FileSaver saver(Utils::FilePath::fromString(path), QIODevice::Text); + FileSaver saver(path, QIODevice::Text); saver.write(tr(licenseTemplateTemplate).arg(Core::Constants::IDE_DISPLAY_NAME).toUtf8()); if (!saver.finalize(this)) return; diff --git a/src/plugins/cpptools/cppfindreferences.cpp b/src/plugins/cpptools/cppfindreferences.cpp index 9180e7d15a..9c96b5e44e 100644 --- a/src/plugins/cpptools/cppfindreferences.cpp +++ b/src/plugins/cpptools/cppfindreferences.cpp @@ -58,6 +58,7 @@ using namespace Core; using namespace ProjectExplorer; +using namespace Utils; namespace CppTools { @@ -618,7 +619,7 @@ CPlusPlus::Symbol *CppFindReferences::findSymbol(const CppFindReferencesParamete QByteArray source = getSource(Utils::FilePath::fromString(newSymbolDocument->fileName()), m_modelManager->workingCopy()); CPlusPlus::Document::Ptr doc = - snapshot.preprocessedDocument(source, newSymbolDocument->fileName()); + snapshot.preprocessedDocument(source, FilePath::fromString(newSymbolDocument->fileName())); doc->check(); // find matching symbol in new document and return the new parameters diff --git a/src/plugins/cpptools/cppheadersource_test.cpp b/src/plugins/cpptools/cppheadersource_test.cpp index be0bdd03c2..1900040419 100644 --- a/src/plugins/cpptools/cppheadersource_test.cpp +++ b/src/plugins/cpptools/cppheadersource_test.cpp @@ -23,6 +23,8 @@ ** ****************************************************************************/ +#include "cppheadersource_test.h" + #include "cpptoolsplugin.h" #include "cpptoolsreuse.h" #include "cpptoolstestcase.h" @@ -52,7 +54,7 @@ static QString baseTestDir() namespace CppTools { namespace Internal { -void CppToolsPlugin::test_headersource() +void HeaderSourceTest::test() { QFETCH(QString, sourceFileName); QFETCH(QString, headerFileName); @@ -67,15 +69,15 @@ void CppToolsPlugin::test_headersource() createTempFile(headerPath); bool wasHeader; - clearHeaderSourceCache(); + CppToolsPlugin::clearHeaderSourceCache(); QCOMPARE(correspondingHeaderOrSource(sourcePath, &wasHeader), headerPath); QVERIFY(!wasHeader); - clearHeaderSourceCache(); + CppToolsPlugin::clearHeaderSourceCache(); QCOMPARE(correspondingHeaderOrSource(headerPath, &wasHeader), sourcePath); QVERIFY(wasHeader); } -void CppToolsPlugin::test_headersource_data() +void HeaderSourceTest::test_data() { QTest::addColumn<QString>("sourceFileName"); QTest::addColumn<QString>("headerFileName"); @@ -86,10 +88,10 @@ void CppToolsPlugin::test_headersource_data() QTest::newRow("sourceAndHeaderPrefixWithBothsub") << _("src/testc_foo.cpp") << _("include/testh_foo.h"); } -void CppToolsPlugin::initTestCase() +void HeaderSourceTest::initTestCase() { QDir(baseTestDir()).mkpath(_(".")); - CppFileSettings *fs = fileSettings(); + CppFileSettings *fs = CppToolsPlugin::fileSettings(); fs->headerSearchPaths.append(QLatin1String("include")); fs->headerSearchPaths.append(QLatin1String("../include")); fs->sourceSearchPaths.append(QLatin1String("src")); @@ -98,10 +100,10 @@ void CppToolsPlugin::initTestCase() fs->sourcePrefixes.append(QLatin1String("testc_")); } -void CppToolsPlugin::cleanupTestCase() +void HeaderSourceTest::cleanupTestCase() { Utils::FilePath::fromString(baseTestDir()).removeRecursively(); - CppFileSettings *fs = fileSettings(); + CppFileSettings *fs = CppToolsPlugin::fileSettings(); fs->headerSearchPaths.removeLast(); fs->headerSearchPaths.removeLast(); fs->sourceSearchPaths.removeLast(); diff --git a/src/plugins/cpptools/cppheadersource_test.h b/src/plugins/cpptools/cppheadersource_test.h new file mode 100644 index 0000000000..d3066e3415 --- /dev/null +++ b/src/plugins/cpptools/cppheadersource_test.h @@ -0,0 +1,44 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#pragma once + +#include <QObject> + +namespace CppTools::Internal { + +class HeaderSourceTest : public QObject +{ + Q_OBJECT + +private slots: + void initTestCase(); + void cleanupTestCase(); + + void test_data(); + void test(); +}; + +} // namespace CppTools::Internal diff --git a/src/plugins/cpptools/cpplocalsymbols_test.cpp b/src/plugins/cpptools/cpplocalsymbols_test.cpp index 7a21b1bc4d..978d06d3b9 100644 --- a/src/plugins/cpptools/cpplocalsymbols_test.cpp +++ b/src/plugins/cpptools/cpplocalsymbols_test.cpp @@ -23,7 +23,7 @@ ** ****************************************************************************/ -#include "cpptoolsplugin.h" +#include "cpplocalsymbols_test.h" #include "cpplocalsymbols.h" #include "cppsemanticinfo.h" @@ -131,7 +131,7 @@ QT_END_NAMESPACE namespace CppTools { namespace Internal { -void CppToolsPlugin::test_cpplocalsymbols_data() +void LocalSymbolsTest::test_data() { QTest::addColumn<QByteArray>("source"); QTest::addColumn<QList<Result>>("expectedUses"); @@ -165,7 +165,7 @@ void CppToolsPlugin::test_cpplocalsymbols_data() << Result(_("func"), 3, 5, 4)); } -void CppToolsPlugin::test_cpplocalsymbols() +void LocalSymbolsTest::test() { QFETCH(QByteArray, source); QFETCH(QList<Result>, expectedUses); diff --git a/src/plugins/cpptools/cpplocalsymbols_test.h b/src/plugins/cpptools/cpplocalsymbols_test.h new file mode 100644 index 0000000000..aa487b029a --- /dev/null +++ b/src/plugins/cpptools/cpplocalsymbols_test.h @@ -0,0 +1,41 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#pragma once + +#include <QObject> + +namespace CppTools::Internal { + +class LocalSymbolsTest : public QObject +{ + Q_OBJECT + +private slots: + void test_data(); + void test(); +}; + +} // namespace CppTools::Internal diff --git a/src/plugins/cpptools/cpplocatorfilter_test.cpp b/src/plugins/cpptools/cpplocatorfilter_test.cpp index 71bdf5ba40..9aff8dd649 100644 --- a/src/plugins/cpptools/cpplocatorfilter_test.cpp +++ b/src/plugins/cpptools/cpplocatorfilter_test.cpp @@ -23,7 +23,7 @@ ** ****************************************************************************/ -#include "cpptoolsplugin.h" +#include "cpplocatorfilter_test.h" #include "cppclassesfilter.h" #include "cppcurrentdocumentfilter.h" @@ -137,7 +137,7 @@ private: } // anonymous namespace -void CppToolsPlugin::test_cpplocatorfilters_CppLocatorFilter() +void LocatorFilterTest::testLocatorFilter() { QFETCH(QString, testFile); QFETCH(ILocatorFilter *, filter); @@ -149,7 +149,7 @@ void CppToolsPlugin::test_cpplocatorfilters_CppLocatorFilter() CppLocatorFilterTestCase(filter, testFile, searchText, expectedResults); } -void CppToolsPlugin::test_cpplocatorfilters_CppLocatorFilter_data() +void LocatorFilterTest::testLocatorFilter_data() { QTest::addColumn<QString>("testFile"); QTest::addColumn<ILocatorFilter *>("filter"); @@ -321,7 +321,7 @@ void CppToolsPlugin::test_cpplocatorfilters_CppLocatorFilter_data() }; } -void CppToolsPlugin::test_cpplocatorfilters_CppCurrentDocumentFilter() +void LocatorFilterTest::testCurrentDocumentFilter() { MyTestDataDir testDirectory("testdata_basic"); const QString testFile = testDirectory.file("file1.cpp"); @@ -375,7 +375,7 @@ void CppToolsPlugin::test_cpplocatorfilters_CppCurrentDocumentFilter() CppCurrentDocumentFilterTestCase(testFile, expectedResults); } -void CppToolsPlugin::test_cpplocatorfilters_CppCurrentDocumentHighlighting() +void LocatorFilterTest::testCurrentDocumentHighlighting() { MyTestDataDir testDirectory("testdata_basic"); const QString testFile = testDirectory.file("file1.cpp"); @@ -399,7 +399,7 @@ void CppToolsPlugin::test_cpplocatorfilters_CppCurrentDocumentHighlighting() CppCurrentDocumentFilterTestCase(testFile, expectedResults, searchText); } -void CppToolsPlugin::test_cpplocatorfilters_CppFunctionsFilterHighlighting() +void LocatorFilterTest::testFunctionsFilterHighlighting() { MyTestDataDir testDirectory("testdata_basic"); const QString testFile = testDirectory.file("file1.cpp"); diff --git a/src/plugins/cpptools/cpplocatorfilter_test.h b/src/plugins/cpptools/cpplocatorfilter_test.h new file mode 100644 index 0000000000..ddda9a394b --- /dev/null +++ b/src/plugins/cpptools/cpplocatorfilter_test.h @@ -0,0 +1,44 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#pragma once + +#include <QObject> + +namespace CppTools::Internal { + +class LocatorFilterTest : public QObject +{ + Q_OBJECT + +private slots: + void testLocatorFilter(); + void testLocatorFilter_data(); + void testCurrentDocumentFilter(); + void testCurrentDocumentHighlighting(); + void testFunctionsFilterHighlighting(); +}; + +} // namespace CppTools::Internal diff --git a/src/plugins/cpptools/cppmodelmanager.cpp b/src/plugins/cpptools/cppmodelmanager.cpp index 8d58f429b9..74ac1557fc 100644 --- a/src/plugins/cpptools/cppmodelmanager.cpp +++ b/src/plugins/cpptools/cppmodelmanager.cpp @@ -144,17 +144,27 @@ namespace Internal { static CppModelManager *m_instance; +class ProjectData +{ +public: + ProjectInfo::Ptr projectInfo; + QFutureWatcher<void> *indexer = nullptr; + bool fullyIndexed = false; +}; + class CppModelManagerPrivate { public: + void setupWatcher(const QFuture<void> &future, ProjectExplorer::Project *project, + ProjectData *projectData, CppModelManager *q); + // Snapshot mutable QMutex m_snapshotMutex; Snapshot m_snapshot; // Project integration mutable QMutex m_projectMutex; - QMap<ProjectExplorer::Project *, ProjectInfo> m_projectToProjectsInfo; - QHash<ProjectExplorer::Project *, bool> m_projectToIndexerCanceled; + QHash<ProjectExplorer::Project *, ProjectData> m_projectData; QMap<Utils::FilePath, QList<ProjectPart::Ptr> > m_fileToProjectParts; QMap<QString, ProjectPart::Ptr> m_projectPartIdToProjectProjectPart; // The members below are cached/(re)calculated from the projects and/or their project parts @@ -173,7 +183,6 @@ public: ModelManagerSupport::Ptr m_activeModelManagerSupport; // Indexing - CppIndexingSupport *m_indexingSupporter; CppIndexingSupport *m_internalIndexingSupport; bool m_indexerEnabled; @@ -364,7 +373,8 @@ bool CppModelManager::positionRequiresSignal(const QString &filePath, const QByt fixedContent.insert(position, 'x'); const Snapshot snapshot = this->snapshot(); - const Document::Ptr document = snapshot.preprocessedDocument(fixedContent, filePath); + const Document::Ptr document = snapshot.preprocessedDocument(fixedContent, + Utils::FilePath::fromString(filePath)); document->check(); QTextDocument textDocument(QString::fromUtf8(fixedContent)); QTextCursor cursor(&textDocument); @@ -643,7 +653,6 @@ CppModelManager::CppModelManager() setObjectName("CppModelManager"); ExtensionSystem::PluginManager::addObject(this); - d->m_indexingSupporter = nullptr; d->m_enableGC = true; // Visual C++ has 1MiB, macOSX has 512KiB @@ -748,9 +757,9 @@ void CppModelManager::ensureUpdated() QStringList CppModelManager::internalProjectFiles() const { QStringList files; - for (const ProjectInfo &pinfo : qAsConst(d->m_projectToProjectsInfo)) { - foreach (const ProjectPart::Ptr &part, pinfo.projectParts()) { - foreach (const ProjectFile &file, part->files) + for (const ProjectData &projectData : qAsConst(d->m_projectData)) { + for (const ProjectPart::Ptr &part : projectData.projectInfo->projectParts()) { + for (const ProjectFile &file : part->files) files += file.path; } } @@ -761,9 +770,9 @@ QStringList CppModelManager::internalProjectFiles() const ProjectExplorer::HeaderPaths CppModelManager::internalHeaderPaths() const { ProjectExplorer::HeaderPaths headerPaths; - for (const ProjectInfo &pinfo : qAsConst(d->m_projectToProjectsInfo)) { - foreach (const ProjectPart::Ptr &part, pinfo.projectParts()) { - foreach (const ProjectExplorer::HeaderPath &path, part->headerPaths) { + for (const ProjectData &projectData: qAsConst(d->m_projectData)) { + for (const ProjectPart::Ptr &part : projectData.projectInfo->projectParts()) { + for (const ProjectExplorer::HeaderPath &path : part->headerPaths) { ProjectExplorer::HeaderPath hp(QDir::cleanPath(path.path), path.type); if (!headerPaths.contains(hp)) headerPaths.push_back(std::move(hp)); @@ -789,8 +798,8 @@ ProjectExplorer::Macros CppModelManager::internalDefinedMacros() const { ProjectExplorer::Macros macros; QSet<ProjectExplorer::Macro> alreadyIn; - for (const ProjectInfo &pinfo : qAsConst(d->m_projectToProjectsInfo)) { - for (const ProjectPart::Ptr &part : pinfo.projectParts()) { + for (const ProjectData &projectData : qAsConst(d->m_projectData)) { + for (const ProjectPart::Ptr &part : projectData.projectInfo->projectParts()) { addUnique(part->toolChainMacros, macros, alreadyIn); addUnique(part->projectMacros, macros, alreadyIn); } @@ -967,29 +976,25 @@ QFuture<void> CppModelManager::updateSourceFiles(const QSet<QString> &sourceFile const QSet<QString> filteredFiles = tooBigFilesRemoved(sourceFiles, indexerFileSizeLimitInMb()); - if (d->m_indexingSupporter) - d->m_indexingSupporter->refreshSourceFiles(filteredFiles, mode); return d->m_internalIndexingSupport->refreshSourceFiles(filteredFiles, mode); } -QList<ProjectInfo> CppModelManager::projectInfos() const +QList<ProjectInfo::Ptr> CppModelManager::projectInfos() const { QMutexLocker locker(&d->m_projectMutex); - return d->m_projectToProjectsInfo.values(); + return Utils::transform<QList<ProjectInfo::Ptr>>(d->m_projectData, + [](const ProjectData &d) { return d.projectInfo; }); } -ProjectInfo CppModelManager::projectInfo(ProjectExplorer::Project *project) const +ProjectInfo::Ptr CppModelManager::projectInfo(ProjectExplorer::Project *project) const { QMutexLocker locker(&d->m_projectMutex); - return d->m_projectToProjectsInfo.value(project, ProjectInfo()); + return d->m_projectData.value(project).projectInfo; } /// \brief Remove all files and their includes (recursively) of given ProjectInfo from the snapshot. void CppModelManager::removeProjectInfoFilesAndIncludesFromSnapshot(const ProjectInfo &projectInfo) { - if (!projectInfo.isValid()) - return; - QMutexLocker snapshotLocker(&d->m_snapshotMutex); foreach (const ProjectPart::Ptr &projectPart, projectInfo.projectParts()) { foreach (const ProjectFile &cxxFile, projectPart->files) { @@ -1089,38 +1094,35 @@ void CppModelManager::recalculateProjectPartMappings() { d->m_projectPartIdToProjectProjectPart.clear(); d->m_fileToProjectParts.clear(); - foreach (const ProjectInfo &projectInfo, d->m_projectToProjectsInfo) { - foreach (const ProjectPart::Ptr &projectPart, projectInfo.projectParts()) { + for (const ProjectData &projectData : qAsConst(d->m_projectData)) { + for (const ProjectPart::Ptr &projectPart : projectData.projectInfo->projectParts()) { d->m_projectPartIdToProjectProjectPart[projectPart->id()] = projectPart; - foreach (const ProjectFile &cxxFile, projectPart->files) + for (const ProjectFile &cxxFile : projectPart->files) d->m_fileToProjectParts[Utils::FilePath::fromString(cxxFile.path)].append( projectPart); - } } d->m_symbolFinder.clearCache(); } -void CppModelManager::watchForCanceledProjectIndexer(const QFuture<void> &future, - ProjectExplorer::Project *project) +void CppModelManagerPrivate::setupWatcher(const QFuture<void> &future, + ProjectExplorer::Project *project, + ProjectData *projectData, CppModelManager *q) { - if (future.isCanceled() || future.isFinished()) - return; - - auto watcher = new QFutureWatcher<void>(this); - connect(watcher, &QFutureWatcher<void>::canceled, this, [this, project, watcher]() { - if (d->m_projectToIndexerCanceled.contains(project)) // Project not yet removed - d->m_projectToIndexerCanceled.insert(project, true); - watcher->disconnect(this); - watcher->deleteLater(); - }); - connect(watcher, &QFutureWatcher<void>::finished, this, [this, project, watcher]() { - d->m_projectToIndexerCanceled.remove(project); - watcher->disconnect(this); + projectData->indexer = new QFutureWatcher<void>(q); + const auto handleFinished = [this, project, watcher = projectData->indexer, q] { + if (const auto it = m_projectData.find(project); + it != m_projectData.end() && it->indexer == watcher) { + it->indexer = nullptr; + it->fullyIndexed = !watcher->isCanceled(); + } + watcher->disconnect(q); watcher->deleteLater(); - }); - watcher->setFuture(future); + }; + q->connect(projectData->indexer, &QFutureWatcher<void>::canceled, q, handleFinished); + q->connect(projectData->indexer, &QFutureWatcher<void>::finished, q, handleFinished); + projectData->indexer->setFuture(future); } void CppModelManager::updateCppEditorDocuments(bool projectsUpdated) const @@ -1152,37 +1154,37 @@ void CppModelManager::updateCppEditorDocuments(bool projectsUpdated) const } } -QFuture<void> CppModelManager::updateProjectInfo(const ProjectInfo &newProjectInfo, +QFuture<void> CppModelManager::updateProjectInfo(const ProjectInfo::Ptr &newProjectInfo, const QSet<QString> &additionalFiles) { - if (!newProjectInfo.isValid()) - return QFuture<void>(); - - ProjectInfo theNewProjectInfo = newProjectInfo; - theNewProjectInfo.finish(); + if (!newProjectInfo) + return {}; QSet<QString> filesToReindex; QStringList removedProjectParts; bool filesRemoved = false; - ProjectExplorer::Project *project = theNewProjectInfo.project().data(); + ProjectExplorer::Project * const project = projectForProjectInfo(*newProjectInfo); + if (!project) + return {}; + + ProjectData *projectData = nullptr; { // Only hold the mutex for a limited scope, so the dumping afterwards does not deadlock. QMutexLocker projectLocker(&d->m_projectMutex); - const QSet<QString> newSourceFiles = theNewProjectInfo.sourceFiles(); + const QSet<QString> newSourceFiles = newProjectInfo->sourceFiles(); // Check if we can avoid a full reindexing - ProjectInfo oldProjectInfo = d->m_projectToProjectsInfo.value(project); - const bool previousIndexerCanceled = d->m_projectToIndexerCanceled.value(project, false); - if (!previousIndexerCanceled && oldProjectInfo.isValid()) { - ProjectInfoComparer comparer(oldProjectInfo, theNewProjectInfo); + const auto it = d->m_projectData.find(project); + if (it != d->m_projectData.end() && it->projectInfo && it->fullyIndexed) { + ProjectInfoComparer comparer(*it->projectInfo, *newProjectInfo); if (comparer.configurationOrFilesChanged()) { d->m_dirty = true; // If the project configuration changed, do a full reindexing if (comparer.configurationChanged()) { - removeProjectInfoFilesAndIncludesFromSnapshot(oldProjectInfo); + removeProjectInfoFilesAndIncludesFromSnapshot(*it->projectInfo); filesToReindex.unite(newSourceFiles); // The "configuration file" includes all defines and therefore should be updated @@ -1218,9 +1220,16 @@ QFuture<void> CppModelManager::updateProjectInfo(const ProjectInfo &newProjectIn } // Update Project/ProjectInfo and File/ProjectPart table - d->m_projectToProjectsInfo.insert(project, theNewProjectInfo); + if (it != d->m_projectData.end()) { + if (it->indexer) + it->indexer->cancel(); + it->projectInfo = newProjectInfo; + it->fullyIndexed = false; + } + projectData = it == d->m_projectData.end() + ? &(d->m_projectData[project] = ProjectData{newProjectInfo, nullptr, false}) + : &(*it); recalculateProjectPartMappings(); - } // Mutex scope // If requested, dump everything we got @@ -1236,7 +1245,7 @@ QFuture<void> CppModelManager::updateProjectInfo(const ProjectInfo &newProjectIn emit projectPartsRemoved(removedProjectParts); // Announce added project parts - emit projectPartsUpdated(theNewProjectInfo.project().data()); + emit projectPartsUpdated(project); // Ideally, we would update all the editor documents that depend on the 'filesToReindex'. // However, on e.g. a session restore first the editor documents are created and then the @@ -1248,10 +1257,12 @@ QFuture<void> CppModelManager::updateProjectInfo(const ProjectInfo &newProjectIn // Trigger reindexing const QFuture<void> indexingFuture = updateSourceFiles(filesToReindex, ForcedProgressNotification); - if (!filesToReindex.isEmpty()) { - d->m_projectToIndexerCanceled.insert(project, false); - } - watchForCanceledProjectIndexer(indexingFuture, project); + + // It's safe to do this here, as only the UI thread writes to the map and no other thread + // uses the indexer value. + // FIXME: Use a read/write lock instead of a mutex. + d->setupWatcher(indexingFuture, project, projectData, this); + return indexingFuture; } @@ -1342,14 +1353,12 @@ void CppModelManager::onAboutToRemoveProject(ProjectExplorer::Project *project) { QStringList idsOfRemovedProjectParts; - d->m_projectToIndexerCanceled.remove(project); - { QMutexLocker locker(&d->m_projectMutex); d->m_dirty = true; const QStringList projectPartsIdsBefore = d->m_projectPartIdToProjectProjectPart.keys(); - d->m_projectToProjectsInfo.remove(project); + d->m_projectData.remove(project); recalculateProjectPartMappings(); const QStringList projectPartsIdsAfter = d->m_projectPartIdToProjectProjectPart.keys(); @@ -1369,7 +1378,7 @@ void CppModelManager::onActiveProjectChanged(ProjectExplorer::Project *project) { QMutexLocker locker(&d->m_projectMutex); - if (!d->m_projectToProjectsInfo.contains(project)) + if (!d->m_projectData.contains(project)) return; // Not yet known to us. } @@ -1531,18 +1540,16 @@ void CppModelManager::onCoreAboutToClose() void CppModelManager::setupFallbackProjectPart() { - ProjectPart::Ptr part(new ProjectPart); - - part->projectMacros = definedMacros(); - part->headerPaths = headerPaths(); + ToolChainInfo tcInfo; + RawProjectPart rpp; + rpp.setMacros(definedMacros()); + rpp.setHeaderPaths(headerPaths()); + rpp.setQtVersion(Utils::QtVersion::Qt5); // Do not activate ObjectiveCExtensions since this will lead to the // "objective-c++" language option for a project-less *.cpp file. - part->languageExtensions = Utils::LanguageExtension::All; - part->languageExtensions &= ~Utils::LanguageExtensions( - Utils::LanguageExtension::ObjectiveC); - - part->qtVersion = Utils::QtVersion::Qt5; + Utils::LanguageExtensions langExtensions = Utils::LanguageExtension::All; + langExtensions &= ~Utils::LanguageExtensions(Utils::LanguageExtension::ObjectiveC); // TODO: Use different fallback toolchain for different kinds of files? const Kit * const defaultKit = KitManager::isLoaded() ? KitManager::defaultKit() : nullptr; @@ -1553,15 +1560,17 @@ void CppModelManager::setupFallbackProjectPart() if (sysroot.isEmpty()) sysroot = Utils::FilePath::fromString(defaultTc->sysRoot()); Utils::Environment env = defaultKit->buildEnvironment(); - ToolChainInfo tcInfo(defaultTc, sysroot.toString(), env); - part->setupToolchainProperties(tcInfo, {}); - if (part->language == Language::C) - part->languageVersion = Utils::LanguageVersion::LatestC; - else - part->languageVersion = Utils::LanguageVersion::LatestCxx; + tcInfo = ToolChainInfo(defaultTc, sysroot.toString(), env); + const auto macroInspectionWrapper = [runner = tcInfo.macroInspectionRunner]( + const QStringList &flags) { + ToolChain::MacroInspectionReport report = runner(flags); + report.languageVersion = Utils::LanguageVersion::LatestCxx; + return report; + }; + tcInfo.macroInspectionRunner = macroInspectionWrapper; } - part->updateLanguageFeatures(); + const auto part = ProjectPart::create({}, rpp, {}, {}, {}, langExtensions, {}, tcInfo); QMutexLocker locker(&d->m_fallbackProjectPartMutex); d->m_fallbackProjectPart = part; } @@ -1654,19 +1663,9 @@ BaseEditorDocumentProcessor *CppModelManager::createEditorDocumentProcessor( return d->m_activeModelManagerSupport->createEditorDocumentProcessor(baseTextDocument); } -void CppModelManager::setIndexingSupport(CppIndexingSupport *indexingSupport) -{ - if (indexingSupport) { - if (dynamic_cast<BuiltinIndexingSupport *>(indexingSupport)) - d->m_indexingSupporter = nullptr; - else - d->m_indexingSupporter = indexingSupport; - } -} - CppIndexingSupport *CppModelManager::indexingSupport() { - return d->m_indexingSupporter ? d->m_indexingSupporter : d->m_internalIndexingSupport; + return d->m_internalIndexingSupport; } QStringList CppModelManager::projectFiles() diff --git a/src/plugins/cpptools/cppmodelmanager.h b/src/plugins/cpptools/cppmodelmanager.h index bc7f345ea1..0af91d702b 100644 --- a/src/plugins/cpptools/cppmodelmanager.h +++ b/src/plugins/cpptools/cppmodelmanager.h @@ -114,9 +114,9 @@ public: QByteArray codeModelConfiguration() const; CppLocatorData *locatorData() const; - QList<ProjectInfo> projectInfos() const; - ProjectInfo projectInfo(ProjectExplorer::Project *project) const; - QFuture<void> updateProjectInfo(const ProjectInfo &newProjectInfo, + QList<ProjectInfo::Ptr> projectInfos() const; + ProjectInfo::Ptr projectInfo(ProjectExplorer::Project *project) const; + QFuture<void> updateProjectInfo(const ProjectInfo::Ptr &newProjectInfo, const QSet<QString> &additionalFiles = {}); /// \return The project part with the given project file @@ -192,7 +192,6 @@ public: FollowSymbolInterface &followSymbolInterface() const; std::unique_ptr<AbstractOverviewModel> createOverviewModel() const; - void setIndexingSupport(CppIndexingSupport *indexingSupport); CppIndexingSupport *indexingSupport(); QStringList projectFiles(); @@ -287,8 +286,6 @@ private: void initializeBuiltinModelManagerSupport(); void delayedGC(); void recalculateProjectPartMappings(); - void watchForCanceledProjectIndexer(const QFuture<void> &future, - ProjectExplorer::Project *project); void replaceSnapshot(const CPlusPlus::Snapshot &newSnapshot); void removeFilesFromSnapshot(const QSet<QString> &removedFiles); diff --git a/src/plugins/cpptools/cppmodelmanager_test.cpp b/src/plugins/cpptools/cppmodelmanager_test.cpp index 8e2700c2a2..1f3fdfe932 100644 --- a/src/plugins/cpptools/cppmodelmanager_test.cpp +++ b/src/plugins/cpptools/cppmodelmanager_test.cpp @@ -23,10 +23,11 @@ ** ****************************************************************************/ +#include "cppmodelmanager_test.h" + #include "baseeditordocumentprocessor.h" #include "builtineditordocumentparser.h" #include "cppsourceprocessor.h" -#include "cpptoolsplugin.h" #include "cpptoolstestcase.h" #include "editordocumenthandle.h" #include "modelmanagertesthelper.h" @@ -104,20 +105,20 @@ public: foreach (const QString &file, files) projectFiles << projectDir.file(file); - Project *project = modelManagerTestHelper->createProject(name); - projectInfo = ProjectInfo(project); + RawProjectPart rpp; + rpp.setQtVersion(Utils::QtVersion::Qt5); + const ProjectFiles rppFiles = Utils::transform<ProjectFiles>(projectFiles, + [](const QString &file) { return ProjectFile(file, ProjectFile::classify(file)); }); + const auto project = modelManagerTestHelper->createProject( + name, Utils::FilePath::fromString(dir).pathAppended(name + ".pro")); - ProjectPart::Ptr part(new ProjectPart); - part->qtVersion = Utils::QtVersion::Qt5; - foreach (const QString &file, projectFiles) { - ProjectFile projectFile(file, ProjectFile::classify(file)); - part->files.append(projectFile); - } - projectInfo.appendProjectPart(part); + const auto part = ProjectPart::create(project->projectFilePath(), rpp, {}, rppFiles); + projectInfo = ProjectInfo::create(ProjectUpdateInfo(project, KitInfo(nullptr), {}, {}), + {part}); } ModelManagerTestHelper *modelManagerTestHelper; - ProjectInfo projectInfo; + ProjectInfo::Ptr projectInfo; QStringList projectFiles; }; @@ -173,22 +174,23 @@ ProjectPart::Ptr projectPartOfEditorDocument(const QString &filePath) } // anonymous namespace /// Check: The preprocessor cleans include and framework paths. -void CppToolsPlugin::test_modelmanager_paths_are_clean() +void ModelManagerTest::testPathsAreClean() { ModelManagerTestHelper helper; CppModelManager *mm = CppModelManager::instance(); const MyTestDataDir testDataDir(_("testdata")); - Project *project = helper.createProject(_("test_modelmanager_paths_are_clean")); - ProjectInfo pi = ProjectInfo(project); - - ProjectPart::Ptr part(new ProjectPart); - part->qtVersion = Utils::QtVersion::Qt5; - part->projectMacros = {ProjectExplorer::Macro("OH_BEHAVE", "-1")}; - part->headerPaths = {{testDataDir.includeDir(false), HeaderPathType::User}, - {testDataDir.frameworksDir(false), HeaderPathType::Framework}}; - pi.appendProjectPart(part); + const auto project = helper.createProject(_("test_modelmanager_paths_are_clean"), + Utils::FilePath::fromString("blubb.pro")); + RawProjectPart rpp; + rpp.setQtVersion(Utils::QtVersion::Qt5); + rpp.setMacros({ProjectExplorer::Macro("OH_BEHAVE", "-1")}); + rpp.setHeaderPaths({{testDataDir.includeDir(false), HeaderPathType::User}, + {testDataDir.frameworksDir(false), HeaderPathType::Framework}}); + const auto part = ProjectPart::create(project->projectFilePath(), rpp); + const auto pi = ProjectInfo::create(ProjectUpdateInfo(project, KitInfo(nullptr), {}, {}), + {part}); mm->updateProjectInfo(pi); @@ -199,7 +201,7 @@ void CppToolsPlugin::test_modelmanager_paths_are_clean() } /// Check: Frameworks headers are resolved. -void CppToolsPlugin::test_modelmanager_framework_headers() +void ModelManagerTest::testFrameworkHeaders() { if (Utils::HostOsInfo::isWindowsHost()) QSKIP("Can't resolve framework soft links on Windows."); @@ -209,18 +211,19 @@ void CppToolsPlugin::test_modelmanager_framework_headers() const MyTestDataDir testDataDir(_("testdata")); - Project *project = helper.createProject(_("test_modelmanager_framework_headers")); - ProjectInfo pi = ProjectInfo(project); - - ProjectPart::Ptr part(new ProjectPart); - part->qtVersion = Utils::QtVersion::Qt5; - part->projectMacros = {{"OH_BEHAVE", "-1"}}; - part->headerPaths = {{testDataDir.includeDir(false), HeaderPathType::User}, - {testDataDir.frameworksDir(false), HeaderPathType::Framework}}; + const auto project = helper.createProject(_("test_modelmanager_framework_headers"), + Utils::FilePath::fromString("blubb.pro")); + RawProjectPart rpp; + rpp.setQtVersion(Utils::QtVersion::Qt5); + rpp.setMacros({{"OH_BEHAVE", "-1"}}); + rpp.setHeaderPaths({{testDataDir.includeDir(false), HeaderPathType::User}, + {testDataDir.frameworksDir(false), HeaderPathType::Framework}}); const QString &source = testDataDir.fileFromSourcesDir( _("test_modelmanager_framework_headers.cpp")); - part->files << ProjectFile(source, ProjectFile::CXXSource); - pi.appendProjectPart(part); + const auto part = ProjectPart::create(project->projectFilePath(), rpp, {}, + {ProjectFile(source, ProjectFile::CXXSource)}); + const auto pi = ProjectInfo::create(ProjectUpdateInfo(project, KitInfo(nullptr), {}, {}), + {part}); mm->updateProjectInfo(pi).waitForFinished(); QCoreApplication::processEvents(); @@ -245,7 +248,7 @@ void CppToolsPlugin::test_modelmanager_framework_headers() /// QTCREATORBUG-9056 /// Check: If the project configuration changes, all project files and their /// includes have to be reparsed. -void CppToolsPlugin::test_modelmanager_refresh_also_includes_of_project_files() +void ModelManagerTest::testRefreshAlsoIncludesOfProjectFiles() { ModelManagerTestHelper helper; CppModelManager *mm = CppModelManager::instance(); @@ -255,16 +258,16 @@ void CppToolsPlugin::test_modelmanager_refresh_also_includes_of_project_files() const QString testCpp(testDataDir.fileFromSourcesDir(_("test_modelmanager_refresh.cpp"))); const QString testHeader(testDataDir.fileFromSourcesDir( _("test_modelmanager_refresh.h"))); - Project *project = helper.createProject( - _("test_modelmanager_refresh_also_includes_of_project_files")); - ProjectInfo pi = ProjectInfo(project); - - ProjectPart::Ptr part(new ProjectPart); - part->qtVersion = Utils::QtVersion::Qt5; - part->projectMacros = {{"OH_BEHAVE", "-1"}}; - part->headerPaths = {{testDataDir.includeDir(false), HeaderPathType::User}}; - part->files.append(ProjectFile(testCpp, ProjectFile::CXXSource)); - pi.appendProjectPart(part); + const auto project + = helper.createProject(_("test_modelmanager_refresh_also_includes_of_project_files"), + Utils::FilePath::fromString("blubb.pro")); + RawProjectPart rpp; + rpp.setQtVersion(Utils::QtVersion::Qt5); + rpp.setMacros({{"OH_BEHAVE", "-1"}}); + rpp.setHeaderPaths({{testDataDir.includeDir(false), HeaderPathType::User}}); + auto part = ProjectPart::create(project->projectFilePath(), rpp, {}, + {ProjectFile(testCpp, ProjectFile::CXXSource)}); + auto pi = ProjectInfo::create(ProjectUpdateInfo(project, KitInfo(nullptr), {}, {}), {part}); QSet<QString> refreshedFiles = helper.updateProjectInfo(pi); QCOMPARE(refreshedFiles.size(), 1); @@ -279,9 +282,10 @@ void CppToolsPlugin::test_modelmanager_refresh_also_includes_of_project_files() QVERIFY(macrosInHeaderBefore.first().name() == "test_modelmanager_refresh_h"); // Introduce a define that will enable another define once the document is reparsed. - part->projectMacros = {{"TEST_DEFINE", "1"}}; - pi = ProjectInfo(project); - pi.appendProjectPart(part); + rpp.setMacros({{"TEST_DEFINE", "1"}}); + part = ProjectPart::create(project->projectFilePath(), rpp, {}, + {ProjectFile(testCpp, ProjectFile::CXXSource)}); + pi = ProjectInfo::create(ProjectUpdateInfo(project, KitInfo(nullptr), {}, {}), {part}); refreshedFiles = helper.updateProjectInfo(pi); @@ -301,7 +305,7 @@ void CppToolsPlugin::test_modelmanager_refresh_also_includes_of_project_files() /// QTCREATORBUG-9205 /// Check: When reparsing the same files again, no errors occur /// (The CppSourceProcessor's already seen files are properly cleared!). -void CppToolsPlugin::test_modelmanager_refresh_several_times() +void ModelManagerTest::testRefreshSeveralTimes() { ModelManagerTestHelper helper; CppModelManager *mm = CppModelManager::instance(); @@ -312,15 +316,17 @@ void CppToolsPlugin::test_modelmanager_refresh_several_times() const QString testHeader2(testDataDir.file(_("header.h"))); const QString testCpp(testDataDir.file(_("source.cpp"))); - Project *project = helper.createProject(_("test_modelmanager_refresh_several_times")); - ProjectInfo pi = ProjectInfo(project); - - ProjectPart::Ptr part(new ProjectPart); - part->qtVersion = Utils::QtVersion::Qt5; - part->files.append(ProjectFile(testHeader1, ProjectFile::CXXHeader)); - part->files.append(ProjectFile(testHeader2, ProjectFile::CXXHeader)); - part->files.append(ProjectFile(testCpp, ProjectFile::CXXSource)); - pi.appendProjectPart(part); + const auto project = helper.createProject(_("test_modelmanager_refresh_several_times"), + Utils::FilePath::fromString("blubb.pro")); + RawProjectPart rpp; + rpp.setQtVersion(Utils::QtVersion::Qt5); + const ProjectFiles files = { + ProjectFile(testHeader1, ProjectFile::CXXHeader), + ProjectFile(testHeader2, ProjectFile::CXXHeader), + ProjectFile(testCpp, ProjectFile::CXXSource) + }; + const auto part = ProjectPart::create(project->projectFilePath(), rpp, {}, files); + auto pi = ProjectInfo::create(ProjectUpdateInfo(project, KitInfo(nullptr), {}, {}), {part}); mm->updateProjectInfo(pi); CPlusPlus::Snapshot snapshot; @@ -329,16 +335,11 @@ void CppToolsPlugin::test_modelmanager_refresh_several_times() ProjectExplorer::Macros macros = {{"FIRST_DEFINE"}}; for (int i = 0; i < 2; ++i) { - pi = ProjectInfo(project); - ProjectPart::Ptr part(new ProjectPart); // Simulate project configuration change by having different defines each time. macros += {"ANOTHER_DEFINE"}; - part->projectMacros = macros; - part->qtVersion = Utils::QtVersion::Qt5; - part->files.append(ProjectFile(testHeader1, ProjectFile::CXXHeader)); - part->files.append(ProjectFile(testHeader2, ProjectFile::CXXHeader)); - part->files.append(ProjectFile(testCpp, ProjectFile::CXXSource)); - pi.appendProjectPart(part); + rpp.setMacros(macros); + const auto part = ProjectPart::create(project->projectFilePath(), rpp, {}, files); + pi = ProjectInfo::create({project, KitInfo(nullptr), {}, {}}, {part}); refreshedFiles = helper.updateProjectInfo(pi); QCOMPARE(refreshedFiles.size(), 3); @@ -366,7 +367,7 @@ void CppToolsPlugin::test_modelmanager_refresh_several_times() /// QTCREATORBUG-9581 /// Check: If nothing has changes, nothing should be reindexed. -void CppToolsPlugin::test_modelmanager_refresh_test_for_changes() +void ModelManagerTest::testRefreshTestForChanges() { ModelManagerTestHelper helper; CppModelManager *mm = CppModelManager::instance(); @@ -374,13 +375,13 @@ void CppToolsPlugin::test_modelmanager_refresh_test_for_changes() const MyTestDataDir testDataDir(_("testdata_refresh")); const QString testCpp(testDataDir.file(_("source.cpp"))); - Project *project = helper.createProject(_("test_modelmanager_refresh_2")); - ProjectInfo pi = ProjectInfo(project); - - ProjectPart::Ptr part(new ProjectPart); - part->qtVersion = Utils::QtVersion::Qt5; - part->files.append(ProjectFile(testCpp, ProjectFile::CXXSource)); - pi.appendProjectPart(part); + const auto project = helper.createProject(_("test_modelmanager_refresh_2"), + Utils::FilePath::fromString("blubb.pro")); + RawProjectPart rpp; + rpp.setQtVersion(Utils::QtVersion::Qt5); + const auto part = ProjectPart::create(project->projectFilePath(), rpp, {}, + {ProjectFile(testCpp, ProjectFile::CXXSource)}); + const auto pi = ProjectInfo::create({project, KitInfo(nullptr), {}, {}}, {part}); // Reindexing triggers a reparsing thread helper.resetRefreshedSourceFiles(); @@ -398,7 +399,7 @@ void CppToolsPlugin::test_modelmanager_refresh_test_for_changes() /// Check: (1) Added project files are recognized and parsed. /// Check: (2) Removed project files are recognized and purged from the snapshot. -void CppToolsPlugin::test_modelmanager_refresh_added_and_purge_removed() +void ModelManagerTest::testRefreshAddedAndPurgeRemoved() { ModelManagerTestHelper helper; CppModelManager *mm = CppModelManager::instance(); @@ -409,14 +410,13 @@ void CppToolsPlugin::test_modelmanager_refresh_added_and_purge_removed() const QString testHeader2(testDataDir.file(_("defines.h"))); const QString testCpp(testDataDir.file(_("source.cpp"))); - Project *project = helper.createProject(_("test_modelmanager_refresh_3")); - ProjectInfo pi = ProjectInfo(project); - - ProjectPart::Ptr part(new ProjectPart); - part->qtVersion = Utils::QtVersion::Qt5; - part->files.append(ProjectFile(testCpp, ProjectFile::CXXSource)); - part->files.append(ProjectFile(testHeader1, ProjectFile::CXXHeader)); - pi.appendProjectPart(part); + const auto project = helper.createProject(_("test_modelmanager_refresh_3"), + Utils::FilePath::fromString("blubb.pro")); + RawProjectPart rpp; + rpp.setQtVersion(Utils::QtVersion::Qt5); + const auto part = ProjectPart::create(project->projectFilePath(), rpp, {}, + {{testCpp, ProjectFile::CXXSource}, {testHeader1, ProjectFile::CXXHeader}}); + auto pi = ProjectInfo::create({project, KitInfo(nullptr), {}, {}}, {part}); CPlusPlus::Snapshot snapshot; QSet<QString> refreshedFiles; @@ -432,12 +432,9 @@ void CppToolsPlugin::test_modelmanager_refresh_added_and_purge_removed() QVERIFY(snapshot.contains(testCpp)); // Now add testHeader2 and remove testHeader1 - pi = ProjectInfo(project); - ProjectPart::Ptr newPart(new ProjectPart); - newPart->qtVersion = Utils::QtVersion::Qt5; - newPart->files.append(ProjectFile(testCpp, ProjectFile::CXXSource)); - newPart->files.append(ProjectFile(testHeader2, ProjectFile::CXXHeader)); - pi.appendProjectPart(newPart); + const auto newPart = ProjectPart::create(project->projectFilePath(), rpp, {}, + {{testCpp, ProjectFile::CXXSource}, {testHeader2, ProjectFile::CXXHeader}}); + pi = ProjectInfo::create({project, KitInfo(nullptr), {}, {}}, {newPart}); refreshedFiles = helper.updateProjectInfo(pi); @@ -454,7 +451,7 @@ void CppToolsPlugin::test_modelmanager_refresh_added_and_purge_removed() /// Check: Timestamp modified files are reparsed if project files are added or removed /// while the project configuration stays the same -void CppToolsPlugin::test_modelmanager_refresh_timeStampModified_if_sourcefiles_change() +void ModelManagerTest::testRefreshTimeStampModifiedIfSourcefilesChange() { QFETCH(QString, fileToChange); QFETCH(QStringList, initialProjectFiles); @@ -469,15 +466,15 @@ void CppToolsPlugin::test_modelmanager_refresh_timeStampModified_if_sourcefiles_ ModelManagerTestHelper helper; CppModelManager *mm = CppModelManager::instance(); - Project *project = helper.createProject(_("test_modelmanager_refresh_timeStampModified")); - ProjectInfo pi = ProjectInfo(project); - - ProjectPart::Ptr part(new ProjectPart); - part->qtVersion = Utils::QtVersion::Qt5; - foreach (const QString &file, initialProjectFiles) - part->files.append(ProjectFile(file, ProjectFile::CXXSource)); - pi = ProjectInfo(project); - pi.appendProjectPart(part); + const auto project = helper.createProject(_("test_modelmanager_refresh_timeStampModified"), + Utils::FilePath::fromString("blubb.pro")); + RawProjectPart rpp; + rpp.setQtVersion(Utils::QtVersion::Qt5); + auto files = Utils::transform<ProjectFiles>(initialProjectFiles, [](const QString &f) { + return ProjectFile(f, ProjectFile::CXXSource); + }); + auto part = ProjectPart::create(project->projectFilePath(), rpp, {}, files); + auto pi = ProjectInfo::create({project, KitInfo(nullptr), {}, {}}, {part}); Document::Ptr document; CPlusPlus::Snapshot snapshot; @@ -506,11 +503,11 @@ void CppToolsPlugin::test_modelmanager_refresh_timeStampModified_if_sourcefiles_ QVERIFY(fileChangerAndRestorer.writeContents(newFileContentes)); // Add or remove source file. The configuration stays the same. - part->files.clear(); - foreach (const QString &file, finalProjectFiles) - part->files.append(ProjectFile(file, ProjectFile::CXXSource)); - pi = ProjectInfo(project); - pi.appendProjectPart(part); + files = Utils::transform<ProjectFiles>(finalProjectFiles, [](const QString &f) { + return ProjectFile(f, ProjectFile::CXXSource); + }); + part = ProjectPart::create(project->projectFilePath(), rpp, {}, files); + pi = ProjectInfo::create({project, KitInfo(nullptr), {}, {}}, {part}); refreshedFiles = helper.updateProjectInfo(pi); @@ -528,7 +525,7 @@ void CppToolsPlugin::test_modelmanager_refresh_timeStampModified_if_sourcefiles_ QCOMPARE(document->globalSymbolAt(1)->name()->identifier()->chars(), "addedOtherGlobal"); } -void CppToolsPlugin::test_modelmanager_refresh_timeStampModified_if_sourcefiles_change_data() +void ModelManagerTest::testRefreshTimeStampModifiedIfSourcefilesChange_data() { QTest::addColumn<QString>("fileToChange"); QTest::addColumn<QStringList>("initialProjectFiles"); @@ -550,7 +547,7 @@ void CppToolsPlugin::test_modelmanager_refresh_timeStampModified_if_sourcefiles_ /// Check: If a second project is opened, the code model is still aware of /// files of the first project. -void CppToolsPlugin::test_modelmanager_snapshot_after_two_projects() +void ModelManagerTest::testSnapshotAfterTwoProjects() { QSet<QString> refreshedFiles; ModelManagerTestHelper helper; @@ -594,7 +591,7 @@ void CppToolsPlugin::test_modelmanager_snapshot_after_two_projects() /// though it might not be actually generated in the build dir. /// -void CppToolsPlugin::test_modelmanager_extraeditorsupport_uiFiles() +void ModelManagerTest::testExtraeditorsupportUiFiles() { VerifyCleanCppModelManager verify; @@ -603,8 +600,7 @@ void CppToolsPlugin::test_modelmanager_extraeditorsupport_uiFiles() const QString projectFile = temporaryDir.absolutePath("testdata_guiproject1.pro"); ProjectOpenerAndCloser projects; - ProjectInfo projectInfo = projects.open(projectFile, /*configureAsExampleProject=*/ true); - QVERIFY(projectInfo.isValid()); + QVERIFY(projects.open(projectFile, /*configureAsExampleProject=*/ true)); // Check working copy. // An AbstractEditorSupport object should have been added for the ui_* file. @@ -640,7 +636,7 @@ void CppToolsPlugin::test_modelmanager_extraeditorsupport_uiFiles() /// QTCREATORBUG-9828: Locator shows symbols of closed files /// Check: The garbage collector should be run if the last CppEditor is closed. -void CppToolsPlugin::test_modelmanager_gc_if_last_cppeditor_closed() +void ModelManagerTest::testGcIfLastCppeditorClosed() { ModelManagerTestHelper helper; @@ -671,7 +667,7 @@ void CppToolsPlugin::test_modelmanager_gc_if_last_cppeditor_closed() } /// Check: Files that are open in the editor are not garbage collected. -void CppToolsPlugin::test_modelmanager_dont_gc_opened_files() +void ModelManagerTest::testDontGcOpenedFiles() { ModelManagerTestHelper helper; @@ -734,7 +730,7 @@ QString nameOfFirstDeclaration(const Document::Ptr &doc) } } -void CppToolsPlugin::test_modelmanager_defines_per_project() +void ModelManagerTest::testDefinesPerProject() { ModelManagerTestHelper helper; @@ -745,28 +741,26 @@ void CppToolsPlugin::test_modelmanager_defines_per_project() CppModelManager *mm = CppModelManager::instance(); - Project *project = helper.createProject(_("test_modelmanager_defines_per_project")); - - ProjectPart::Ptr part1(new ProjectPart); - part1->projectFile = QLatin1String("project1.projectfile"); - part1->files.append(ProjectFile(main1File, ProjectFile::CXXSource)); - part1->files.append(ProjectFile(header, ProjectFile::CXXHeader)); - part1->qtVersion = Utils::QtVersion::None; - part1->projectMacros = {{"SUB1"}}; - part1->headerPaths = {{testDataDirectory.includeDir(false), HeaderPathType::User}}; - - ProjectPart::Ptr part2(new ProjectPart); - part2->projectFile = QLatin1String("project1.projectfile"); - part2->files.append(ProjectFile(main2File, ProjectFile::CXXSource)); - part2->files.append(ProjectFile(header, ProjectFile::CXXHeader)); - part2->qtVersion = Utils::QtVersion::None; - part2->projectMacros = {{"SUB2"}}; - part2->headerPaths = {{testDataDirectory.includeDir(false), HeaderPathType::User}}; - - ProjectInfo pi = ProjectInfo(project); - pi.appendProjectPart(part1); - pi.appendProjectPart(part2); - + const auto project = helper.createProject(_("test_modelmanager_defines_per_project"), + Utils::FilePath::fromString("blubb.pro")); + + RawProjectPart rpp1; + rpp1.setProjectFileLocation("project1.projectfile"); + rpp1.setQtVersion(Utils::QtVersion::None); + rpp1.setMacros({{"SUB1"}}); + rpp1.setHeaderPaths({{testDataDirectory.includeDir(false), HeaderPathType::User}}); + const auto part1 = ProjectPart::create(project->projectFilePath(), rpp1, {}, + {{main1File, ProjectFile::CXXSource}, {header, ProjectFile::CXXHeader}}); + + RawProjectPart rpp2; + rpp2.setProjectFileLocation("project1.projectfile"); + rpp2.setQtVersion(Utils::QtVersion::None); + rpp2.setMacros({{"SUB2"}}); + rpp2.setHeaderPaths({{testDataDirectory.includeDir(false), HeaderPathType::User}}); + const auto part2 = ProjectPart::create(project->projectFilePath(), rpp2, {}, + {{main2File, ProjectFile::CXXSource}, {header, ProjectFile::CXXHeader}}); + + const auto pi = ProjectInfo::create({project, KitInfo(nullptr), {}, {}}, {part1, part2}); helper.updateProjectInfo(pi); QCOMPARE(mm->snapshot().size(), 4); @@ -796,7 +790,7 @@ void CppToolsPlugin::test_modelmanager_defines_per_project() } } -void CppToolsPlugin::test_modelmanager_precompiled_headers() +void ModelManagerTest::testPrecompiledHeaders() { ModelManagerTestHelper helper; @@ -809,29 +803,26 @@ void CppToolsPlugin::test_modelmanager_precompiled_headers() CppModelManager *mm = CppModelManager::instance(); - Project *project = helper.createProject(_("test_modelmanager_defines_per_project_pch")); - - ProjectPart::Ptr part1(new ProjectPart); - part1->projectFile = QLatin1String("project1.projectfile"); - part1->files.append(ProjectFile(main1File, ProjectFile::CXXSource)); - part1->files.append(ProjectFile(header, ProjectFile::CXXHeader)); - part1->qtVersion = Utils::QtVersion::None; - part1->precompiledHeaders.append(pch1File); - part1->headerPaths = {{testDataDirectory.includeDir(false), HeaderPathType::User}}; - part1->updateLanguageFeatures(); - - ProjectPart::Ptr part2(new ProjectPart); - part2->projectFile = QLatin1String("project2.projectfile"); - part2->files.append(ProjectFile(main2File, ProjectFile::CXXSource)); - part2->files.append(ProjectFile(header, ProjectFile::CXXHeader)); - part2->qtVersion = Utils::QtVersion::None; - part2->precompiledHeaders.append(pch2File); - part2->headerPaths = {{testDataDirectory.includeDir(false), HeaderPathType::User}}; - part2->updateLanguageFeatures(); - - ProjectInfo pi = ProjectInfo(project); - pi.appendProjectPart(part1); - pi.appendProjectPart(part2); + const auto project = helper.createProject(_("test_modelmanager_defines_per_project_pch"), + Utils::FilePath::fromString("blubb.pro")); + + RawProjectPart rpp1; + rpp1.setProjectFileLocation("project1.projectfile"); + rpp1.setQtVersion(Utils::QtVersion::None); + rpp1.setPreCompiledHeaders({pch1File}); + rpp1.setHeaderPaths({{testDataDirectory.includeDir(false), HeaderPathType::User}}); + const auto part1 = ProjectPart::create(project->projectFilePath(), rpp1, {}, + {{main1File, ProjectFile::CXXSource}, {header, ProjectFile::CXXHeader}}); + + RawProjectPart rpp2; + rpp2.setProjectFileLocation("project2.projectfile"); + rpp2.setQtVersion(Utils::QtVersion::None); + rpp2.setPreCompiledHeaders({pch2File}); + rpp2.setHeaderPaths({{testDataDirectory.includeDir(false), HeaderPathType::User}}); + const auto part2 = ProjectPart::create(project->projectFilePath(), rpp2, {}, + {{main2File, ProjectFile::CXXSource}, {header, ProjectFile::CXXHeader}}); + + const auto pi = ProjectInfo::create({project, KitInfo(nullptr), {}, {}}, {part1, part2}); helper.updateProjectInfo(pi); QCOMPARE(mm->snapshot().size(), 4); @@ -880,7 +871,7 @@ void CppToolsPlugin::test_modelmanager_precompiled_headers() } } -void CppToolsPlugin::test_modelmanager_defines_per_editor() +void ModelManagerTest::testDefinesPerEditor() { ModelManagerTestHelper helper; @@ -891,24 +882,22 @@ void CppToolsPlugin::test_modelmanager_defines_per_editor() CppModelManager *mm = CppModelManager::instance(); - Project *project = helper.createProject(_("test_modelmanager_defines_per_editor")); - - ProjectPart::Ptr part1(new ProjectPart); - part1->files.append(ProjectFile(main1File, ProjectFile::CXXSource)); - part1->files.append(ProjectFile(header, ProjectFile::CXXHeader)); - part1->qtVersion = Utils::QtVersion::None; - part1->headerPaths = {{testDataDirectory.includeDir(false), HeaderPathType::User}}; + const auto project = helper.createProject(_("test_modelmanager_defines_per_editor"), + Utils::FilePath::fromString("blubb.pro")); - ProjectPart::Ptr part2(new ProjectPart); - part2->files.append(ProjectFile(main2File, ProjectFile::CXXSource)); - part2->files.append(ProjectFile(header, ProjectFile::CXXHeader)); - part2->qtVersion = Utils::QtVersion::None; - part2->headerPaths = {{testDataDirectory.includeDir(false), HeaderPathType::User}}; + RawProjectPart rpp1; + rpp1.setQtVersion(Utils::QtVersion::None); + rpp1.setHeaderPaths({{testDataDirectory.includeDir(false), HeaderPathType::User}}); + const auto part1 = ProjectPart::create(project->projectFilePath(), rpp1, {}, + {{main1File, ProjectFile::CXXSource}, {header, ProjectFile::CXXHeader}}); - ProjectInfo pi = ProjectInfo(project); - pi.appendProjectPart(part1); - pi.appendProjectPart(part2); + RawProjectPart rpp2; + rpp2.setQtVersion(Utils::QtVersion::None); + rpp2.setHeaderPaths({{testDataDirectory.includeDir(false), HeaderPathType::User}}); + const auto part2 = ProjectPart::create(project->projectFilePath(), rpp2, {}, + {{main2File, ProjectFile::CXXSource}, {header, ProjectFile::CXXHeader}}); + const auto pi = ProjectInfo::create({project, KitInfo(nullptr), {}, {}}, {part1, part2}); helper.updateProjectInfo(pi); QCOMPARE(mm->snapshot().size(), 4); @@ -945,7 +934,7 @@ void CppToolsPlugin::test_modelmanager_defines_per_editor() } } -void CppToolsPlugin::test_modelmanager_updateEditorsAfterProjectUpdate() +void ModelManagerTest::testUpdateEditorsAfterProjectUpdate() { ModelManagerTestHelper helper; @@ -960,7 +949,7 @@ void CppToolsPlugin::test_modelmanager_updateEditorsAfterProjectUpdate() QCOMPARE(Core::DocumentModel::openedDocuments().size(), 1); QVERIFY(TestCase::waitForProcessedEditorDocument(fileA)); ProjectPart::Ptr documentAProjectPart = projectPartOfEditorDocument(fileA); - QVERIFY(!documentAProjectPart->project); + QVERIFY(!documentAProjectPart->hasProject()); // Open file B in editor Core::IEditor *editorB = Core::EditorManager::openEditor(fileB); @@ -969,37 +958,35 @@ void CppToolsPlugin::test_modelmanager_updateEditorsAfterProjectUpdate() QCOMPARE(Core::DocumentModel::openedDocuments().size(), 2); QVERIFY(TestCase::waitForProcessedEditorDocument(fileB)); ProjectPart::Ptr documentBProjectPart = projectPartOfEditorDocument(fileB); - QVERIFY(!documentBProjectPart->project); + QVERIFY(!documentBProjectPart->hasProject()); // Switch back to document A Core::EditorManager::activateEditor(editorA); // Open/update related project - Project *project = helper.createProject(_("test_modelmanager_updateEditorsAfterProjectUpdate")); - - ProjectPart::Ptr part(new ProjectPart); - part->project = project; - part->files.append(ProjectFile(fileA, ProjectFile::CXXSource)); - part->files.append(ProjectFile(fileB, ProjectFile::CXXSource)); - part->qtVersion = Utils::QtVersion::None; - - ProjectInfo pi = ProjectInfo(project); - pi.appendProjectPart(part); + const auto project + = helper.createProject(_("test_modelmanager_updateEditorsAfterProjectUpdate"), + Utils::FilePath::fromString("blubb.pro")); + RawProjectPart rpp; + rpp.setQtVersion(Utils::QtVersion::None); + const auto part = ProjectPart::create(project->projectFilePath(), rpp, {}, + {{fileA, ProjectFile::CXXSource}, {fileB, ProjectFile::CXXSource}}); + const auto pi = ProjectInfo::create({project, KitInfo(nullptr), {}, {}}, {part}); helper.updateProjectInfo(pi); // ... and check for updated editor document A QVERIFY(TestCase::waitForProcessedEditorDocument(fileA)); documentAProjectPart = projectPartOfEditorDocument(fileA); - QCOMPARE(documentAProjectPart->project, project); + QCOMPARE(documentAProjectPart->topLevelProject, pi->projectFilePath()); // Switch back to document B and check if that's updated, too Core::EditorManager::activateEditor(editorB); QVERIFY(TestCase::waitForProcessedEditorDocument(fileB)); documentBProjectPart = projectPartOfEditorDocument(fileB); - QCOMPARE(documentBProjectPart->project, project); + QCOMPARE(documentBProjectPart->topLevelProject, pi->projectFilePath()); } -void CppToolsPlugin::test_modelmanager_renameIncludes() +void ModelManagerTest::testRenameIncludes() { struct ModelManagerGCHelper { ~ModelManagerGCHelper() { CppModelManager::instance()->GC(); } @@ -1047,7 +1034,7 @@ void CppToolsPlugin::test_modelmanager_renameIncludes() QCOMPARE(snapshot.allIncludesForDocument(sourceFile), QSet<QString>() << newHeader); } -void CppToolsPlugin::test_modelmanager_renameIncludesInEditor() +void ModelManagerTest::testRenameIncludesInEditor() { struct ModelManagerGCHelper { ~ModelManagerGCHelper() { CppModelManager::instance()->GC(); } @@ -1171,7 +1158,7 @@ void CppToolsPlugin::test_modelmanager_renameIncludesInEditor() QCOMPARE(snapshot.allIncludesForDocument(sourceFile), QSet<QString>() << renamedHeaderWithPragmaOnce); } -void CppToolsPlugin::test_modelmanager_documentsAndRevisions() +void ModelManagerTest::testDocumentsAndRevisions() { TestCase helper; diff --git a/src/plugins/cpptools/cppmodelmanager_test.h b/src/plugins/cpptools/cppmodelmanager_test.h new file mode 100644 index 0000000000..292b93e680 --- /dev/null +++ b/src/plugins/cpptools/cppmodelmanager_test.h @@ -0,0 +1,58 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#pragma once + +#include <QObject> + +namespace CppTools::Internal { + +class ModelManagerTest : public QObject +{ + Q_OBJECT + +private slots: + void testPathsAreClean(); + void testFrameworkHeaders(); + void testRefreshAlsoIncludesOfProjectFiles(); + void testRefreshSeveralTimes(); + void testRefreshTestForChanges(); + void testRefreshAddedAndPurgeRemoved(); + void testRefreshTimeStampModifiedIfSourcefilesChange(); + void testRefreshTimeStampModifiedIfSourcefilesChange_data(); + void testSnapshotAfterTwoProjects(); + void testExtraeditorsupportUiFiles(); + void testGcIfLastCppeditorClosed(); + void testDontGcOpenedFiles(); + void testDefinesPerProject(); + void testDefinesPerEditor(); + void testUpdateEditorsAfterProjectUpdate(); + void testPrecompiledHeaders(); + void testRenameIncludes(); + void testRenameIncludesInEditor(); + void testDocumentsAndRevisions(); +}; + +} // namespace CppTools::Internal diff --git a/src/plugins/cpptools/cpppointerdeclarationformatter_test.cpp b/src/plugins/cpptools/cpppointerdeclarationformatter_test.cpp index 6e8bf332e6..19a16d5e05 100644 --- a/src/plugins/cpptools/cpppointerdeclarationformatter_test.cpp +++ b/src/plugins/cpptools/cpppointerdeclarationformatter_test.cpp @@ -23,8 +23,9 @@ ** ****************************************************************************/ +#include "cpppointerdeclarationformatter_test.h" + #include "cpppointerdeclarationformatter.h" -#include "cpptoolsplugin.h" #include "cpptoolstestcase.h" #include <coreplugin/coreconstants.h> @@ -141,7 +142,7 @@ public: } // anonymous namespace -void CppToolsPlugin::test_format_pointerdeclaration_in_simpledeclarations() +void PointerDeclarationFormatterTest::testInSimpledeclarations() { QFETCH(QString, source); QFETCH(QString, reformattedSource); @@ -152,7 +153,7 @@ void CppToolsPlugin::test_format_pointerdeclaration_in_simpledeclarations() PointerDeclarationFormatter::RespectCursor); } -void CppToolsPlugin::test_format_pointerdeclaration_in_simpledeclarations_data() +void PointerDeclarationFormatterTest::testInSimpledeclarations_data() { QTest::addColumn<QString>("source"); QTest::addColumn<QString>("reformattedSource"); @@ -365,7 +366,7 @@ void CppToolsPlugin::test_format_pointerdeclaration_in_simpledeclarations_data() << "C & C::operator = (const C &) {}"; } -void CppToolsPlugin::test_format_pointerdeclaration_in_controlflowstatements() +void PointerDeclarationFormatterTest::testInControlflowstatements() { QFETCH(QString, source); QFETCH(QString, reformattedSource); @@ -376,7 +377,7 @@ void CppToolsPlugin::test_format_pointerdeclaration_in_controlflowstatements() PointerDeclarationFormatter::RespectCursor); } -void CppToolsPlugin::test_format_pointerdeclaration_in_controlflowstatements_data() +void PointerDeclarationFormatterTest::testInControlflowstatements_data() { QTest::addColumn<QString>("source"); QTest::addColumn<QString>("reformattedSource"); @@ -440,7 +441,7 @@ void CppToolsPlugin::test_format_pointerdeclaration_in_controlflowstatements_dat QTest::newRow("precondition-fail-no-pointer") << source << stripCursor(source); } -void CppToolsPlugin::test_format_pointerdeclaration_multiple_declarators() +void PointerDeclarationFormatterTest::testMultipleDeclarators() { QFETCH(QString, source); QFETCH(QString, reformattedSource); @@ -451,7 +452,7 @@ void CppToolsPlugin::test_format_pointerdeclaration_multiple_declarators() PointerDeclarationFormatter::RespectCursor); } -void CppToolsPlugin::test_format_pointerdeclaration_multiple_declarators_data() +void PointerDeclarationFormatterTest::testMultipleDeclarators_data() { QTest::addColumn<QString>("source"); QTest::addColumn<QString>("reformattedSource"); @@ -495,7 +496,7 @@ void CppToolsPlugin::test_format_pointerdeclaration_multiple_declarators_data() << "char *s, * (*foo)(char * s) = 0;"; } -void CppToolsPlugin::test_format_pointerdeclaration_multiple_matches() +void PointerDeclarationFormatterTest::testMultipleMatches() { QFETCH(QString, source); QFETCH(QString, reformattedSource); @@ -506,7 +507,7 @@ void CppToolsPlugin::test_format_pointerdeclaration_multiple_matches() PointerDeclarationFormatter::IgnoreCursor); } -void CppToolsPlugin::test_format_pointerdeclaration_multiple_matches_data() +void PointerDeclarationFormatterTest::testMultipleMatches_data() { QTest::addColumn<QString>("source"); QTest::addColumn<QString>("reformattedSource"); @@ -578,7 +579,7 @@ void CppToolsPlugin::test_format_pointerdeclaration_multiple_matches_data() "}\n"; } -void CppToolsPlugin::test_format_pointerdeclaration_macros() +void PointerDeclarationFormatterTest::testMacros() { QFETCH(QString, source); QFETCH(QString, reformattedSource); @@ -589,7 +590,7 @@ void CppToolsPlugin::test_format_pointerdeclaration_macros() PointerDeclarationFormatter::RespectCursor); } -void CppToolsPlugin::test_format_pointerdeclaration_macros_data() +void PointerDeclarationFormatterTest::testMacros_data() { QTest::addColumn<QString>("source"); QTest::addColumn<QString>("reformattedSource"); diff --git a/src/plugins/cpptools/cpppointerdeclarationformatter_test.h b/src/plugins/cpptools/cpppointerdeclarationformatter_test.h new file mode 100644 index 0000000000..8ebf00c514 --- /dev/null +++ b/src/plugins/cpptools/cpppointerdeclarationformatter_test.h @@ -0,0 +1,49 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#pragma once + +#include <QObject> + +namespace CppTools::Internal { + +class PointerDeclarationFormatterTest : public QObject +{ + Q_OBJECT + +private slots: + void testInSimpledeclarations(); + void testInSimpledeclarations_data(); + void testInControlflowstatements(); + void testInControlflowstatements_data(); + void testMultipleDeclarators(); + void testMultipleDeclarators_data(); + void testMultipleMatches(); + void testMultipleMatches_data(); + void testMacros(); + void testMacros_data(); +}; + +} // namespace CppTools::Internal diff --git a/src/plugins/cpptools/cppprojectinfogenerator.cpp b/src/plugins/cpptools/cppprojectinfogenerator.cpp index cf3152163f..f81524f102 100644 --- a/src/plugins/cpptools/cppprojectinfogenerator.cpp +++ b/src/plugins/cpptools/cppprojectinfogenerator.cpp @@ -42,24 +42,23 @@ using namespace ProjectExplorer; namespace CppTools { namespace Internal { -ProjectInfoGenerator::ProjectInfoGenerator(const QFutureInterface<ProjectInfo> &futureInterface, +ProjectInfoGenerator::ProjectInfoGenerator(const QFutureInterface<ProjectInfo::Ptr> &futureInterface, const ProjectUpdateInfo &projectUpdateInfo) : m_futureInterface(futureInterface) , m_projectUpdateInfo(projectUpdateInfo) { } -ProjectInfo ProjectInfoGenerator::generate() +ProjectInfo::Ptr ProjectInfoGenerator::generate() { - ProjectInfo projectInfo(m_projectUpdateInfo.project); - + QVector<ProjectPart::Ptr> projectParts; for (const RawProjectPart &rpp : m_projectUpdateInfo.rawProjectParts) { if (m_futureInterface.isCanceled()) - return ProjectInfo(); - - for (const ProjectPart::Ptr &part : createProjectParts(rpp)) - projectInfo.appendProjectPart(part); + return {}; + for (const ProjectPart::Ptr &part : createProjectParts(rpp, m_projectUpdateInfo.projectFilePath)) + projectParts << part; } + const auto projectInfo = ProjectInfo::create(m_projectUpdateInfo, projectParts); static const auto showWarning = [](const QString &message) { QTimer::singleShot(0, TaskHub::instance(), [message] { @@ -79,40 +78,8 @@ ProjectInfo ProjectInfoGenerator::generate() return projectInfo; } -static ProjectPart::Ptr projectPartFromRawProjectPart( - const RawProjectPart &rawProjectPart, Project *project) -{ - ProjectPart::Ptr part(new ProjectPart); - part->project = project; - part->projectFile = rawProjectPart.projectFile; - part->projectConfigFile = rawProjectPart.projectConfigFile; - part->projectFileLine = rawProjectPart.projectFileLine; - part->projectFileColumn = rawProjectPart.projectFileColumn; - part->callGroupId = rawProjectPart.callGroupId; - part->buildSystemTarget = rawProjectPart.buildSystemTarget; - part->buildTargetType = rawProjectPart.buildTargetType; - part->qtVersion = rawProjectPart.qtVersion; - part->projectMacros = rawProjectPart.projectMacros; - if (!part->projectConfigFile.isEmpty()) - part->projectMacros += Macro::toMacros(ProjectPart::readProjectConfigFile(part)); - - // Prevent duplicate include paths. - std::set<QString> seenPaths; - for (const HeaderPath &p : qAsConst(rawProjectPart.headerPaths)) { - const QString cleanPath = QDir::cleanPath(p.path); - if (seenPaths.insert(cleanPath).second) - part->headerPaths << HeaderPath(cleanPath, p.type); - } - - part->precompiledHeaders = rawProjectPart.precompiledHeaders; - part->includedFiles = rawProjectPart.includedFiles; - part->selectedForBuilding = rawProjectPart.selectedForBuilding; - - return part; -} - const QVector<ProjectPart::Ptr> ProjectInfoGenerator::createProjectParts( - const RawProjectPart &rawProjectPart) + const RawProjectPart &rawProjectPart, const Utils::FilePath &projectFilePath) { using Utils::LanguageExtension; @@ -124,21 +91,18 @@ const QVector<ProjectPart::Ptr> ProjectInfoGenerator::createProjectParts( if (!cat.hasParts()) return result; - const ProjectPart::Ptr part = projectPartFromRawProjectPart(rawProjectPart, - m_projectUpdateInfo.project); - - if (m_projectUpdateInfo.cxxToolChain) { + if (m_projectUpdateInfo.cxxToolChainInfo.isValid()) { if (cat.hasCxxSources()) { - result << createProjectPart(rawProjectPart, - part, + result << createProjectPart(projectFilePath, + rawProjectPart, cat.cxxSources(), cat.partName("C++"), Language::Cxx, LanguageExtension::None); } if (cat.hasObjcxxSources()) { - result << createProjectPart(rawProjectPart, - part, + result << createProjectPart(projectFilePath, + rawProjectPart, cat.objcxxSources(), cat.partName("Obj-C++"), Language::Cxx, @@ -148,10 +112,10 @@ const QVector<ProjectPart::Ptr> ProjectInfoGenerator::createProjectParts( m_cxxToolchainMissing = true; } - if (m_projectUpdateInfo.cToolChain) { + if (m_projectUpdateInfo.cToolChainInfo.isValid()) { if (cat.hasCSources()) { - result << createProjectPart(rawProjectPart, - part, + result << createProjectPart(projectFilePath, + rawProjectPart, cat.cSources(), cat.partName("C"), Language::C, @@ -159,8 +123,8 @@ const QVector<ProjectPart::Ptr> ProjectInfoGenerator::createProjectParts( } if (cat.hasObjcSources()) { - result << createProjectPart(rawProjectPart, - part, + result << createProjectPart(projectFilePath, + rawProjectPart, cat.objcSources(), cat.partName("Obj-C"), Language::C, @@ -174,12 +138,12 @@ const QVector<ProjectPart::Ptr> ProjectInfoGenerator::createProjectParts( } ProjectPart::Ptr ProjectInfoGenerator::createProjectPart( - const RawProjectPart &rawProjectPart, - const ProjectPart::Ptr &templateProjectPart, - const ProjectFiles &projectFiles, - const QString &partName, - Language language, - Utils::LanguageExtensions languageExtensions) + const Utils::FilePath &projectFilePath, + const RawProjectPart &rawProjectPart, + const ProjectFiles &projectFiles, + const QString &partName, + Language language, + Utils::LanguageExtensions languageExtensions) { RawProjectPartFlags flags; ToolChainInfo tcInfo; @@ -193,16 +157,8 @@ ProjectPart::Ptr ProjectInfoGenerator::createProjectPart( tcInfo = m_projectUpdateInfo.cxxToolChainInfo; } - ProjectPart::Ptr part(templateProjectPart->copy()); - part->displayName = partName; - part->files = projectFiles; - part->warningFlags = flags.warningFlags; - part->language = language; - part->languageExtensions = flags.languageExtensions | languageExtensions; - part->setupToolchainProperties(tcInfo, flags.commandLineFlags); - part->updateLanguageFeatures(); - - return part; + return ProjectPart::create(projectFilePath, rawProjectPart, partName, projectFiles, + language, languageExtensions, flags, tcInfo); } } // namespace Internal diff --git a/src/plugins/cpptools/cppprojectinfogenerator.h b/src/plugins/cpptools/cppprojectinfogenerator.h index 7e97f1c5ca..64fc60c238 100644 --- a/src/plugins/cpptools/cppprojectinfogenerator.h +++ b/src/plugins/cpptools/cppprojectinfogenerator.h @@ -36,23 +36,22 @@ namespace Internal { class ProjectInfoGenerator { public: - ProjectInfoGenerator(const QFutureInterface<ProjectInfo> &futureInterface, + ProjectInfoGenerator(const QFutureInterface<ProjectInfo::Ptr> &futureInterface, const ProjectExplorer::ProjectUpdateInfo &projectUpdateInfo); - ProjectInfo generate(); + ProjectInfo::Ptr generate(); private: - const QVector<ProjectPart::Ptr> createProjectParts( - const ProjectExplorer::RawProjectPart &rawProjectPart); - ProjectPart::Ptr createProjectPart(const ProjectExplorer::RawProjectPart &rawProjectPart, - const ProjectPart::Ptr &templateProjectPart, + const QVector<ProjectPart::Ptr> createProjectParts(const ProjectExplorer::RawProjectPart &rawProjectPart, const Utils::FilePath &projectFilePath); + ProjectPart::Ptr createProjectPart(const Utils::FilePath &projectFilePath, + const ProjectExplorer::RawProjectPart &rawProjectPart, const ProjectFiles &projectFiles, const QString &partName, Language language, Utils::LanguageExtensions languageExtensions); private: - const QFutureInterface<ProjectInfo> m_futureInterface; + const QFutureInterface<ProjectInfo::Ptr> m_futureInterface; const ProjectExplorer::ProjectUpdateInfo &m_projectUpdateInfo; bool m_cToolchainMissing = false; bool m_cxxToolchainMissing = false; diff --git a/src/plugins/cpptools/cppprojectpartchooser.cpp b/src/plugins/cpptools/cppprojectpartchooser.cpp index 548ccbe6f2..03a540ea32 100644 --- a/src/plugins/cpptools/cppprojectpartchooser.cpp +++ b/src/plugins/cpptools/cppprojectpartchooser.cpp @@ -102,7 +102,7 @@ private: if (!m_preferredProjectPartId.isEmpty() && projectPart.id() == m_preferredProjectPartId) thePriority += 1000; - if (projectPart.project == m_activeProject) + if (projectPart.belongsToProject(m_activeProject)) thePriority += 100; if (projectPart.selectedForBuilding) diff --git a/src/plugins/cpptools/cppprojectupdater.cpp b/src/plugins/cpptools/cppprojectupdater.cpp index f34628af51..9b48335951 100644 --- a/src/plugins/cpptools/cppprojectupdater.cpp +++ b/src/plugins/cpptools/cppprojectupdater.cpp @@ -74,13 +74,10 @@ void CppProjectUpdater::update(const ProjectUpdateInfo &projectUpdateInfo, }); m_projectUpdateInfo = projectUpdateInfo; - // Ensure that we do not operate on a deleted toolchain. using namespace ProjectExplorer; - connect(ToolChainManager::instance(), &ToolChainManager::toolChainRemoved, - this, &CppProjectUpdater::onToolChainRemoved); // Run the project info generator in a worker thread and continue if that one is finished. - auto generateFuture = Utils::runAsync([=](QFutureInterface<ProjectInfo> &futureInterface) { + auto generateFuture = Utils::runAsync([=](QFutureInterface<ProjectInfo::Ptr> &futureInterface) { ProjectUpdateInfo fullProjectUpdateInfo = projectUpdateInfo; if (fullProjectUpdateInfo.rppGenerator) fullProjectUpdateInfo.rawProjectParts = fullProjectUpdateInfo.rppGenerator(); @@ -134,20 +131,8 @@ void CppProjectUpdater::cancel() m_futureSynchronizer.cancelAllFutures(); } -void CppProjectUpdater::onToolChainRemoved(ToolChain *t) -{ - QTC_ASSERT(t, return); - if (t == m_projectUpdateInfo.cToolChain || t == m_projectUpdateInfo.cxxToolChain) - cancel(); -} - void CppProjectUpdater::onProjectInfoGenerated() { - // From now on we do not access the toolchain anymore, so disconnect. - using namespace ProjectExplorer; - disconnect(ToolChainManager::instance(), &ToolChainManager::toolChainRemoved, - this, &CppProjectUpdater::onToolChainRemoved); - if (m_generateFutureWatcher.isCanceled() || m_generateFutureWatcher.future().resultCount() < 1) return; diff --git a/src/plugins/cpptools/cppprojectupdater.h b/src/plugins/cpptools/cppprojectupdater.h index c356ab63d1..a76122d4b4 100644 --- a/src/plugins/cpptools/cppprojectupdater.h +++ b/src/plugins/cpptools/cppprojectupdater.h @@ -63,7 +63,6 @@ public: void cancel() override; private: - void onToolChainRemoved(ProjectExplorer::ToolChain *); void onProjectInfoGenerated(); void checkForExtraCompilersFinished(); @@ -71,7 +70,7 @@ private: ProjectExplorer::ProjectUpdateInfo m_projectUpdateInfo; QList<QPointer<ProjectExplorer::ExtraCompiler>> m_extraCompilers; - QFutureWatcher<ProjectInfo> m_generateFutureWatcher; + QFutureWatcher<ProjectInfo::Ptr> m_generateFutureWatcher; bool m_isProjectInfoGenerated = false; QSet<QFutureWatcher<void> *> m_extraCompilersFutureWatchers; std::unique_ptr<QFutureInterface<void>> m_projectUpdateFutureInterface; diff --git a/src/plugins/cpptools/cpprefactoringchanges.cpp b/src/plugins/cpptools/cpprefactoringchanges.cpp index d452204756..c05b264730 100644 --- a/src/plugins/cpptools/cpprefactoringchanges.cpp +++ b/src/plugins/cpptools/cpprefactoringchanges.cpp @@ -163,10 +163,9 @@ Document::Ptr CppRefactoringFile::cppDocument() const if (!m_cppDocument || !m_cppDocument->translationUnit() || !m_cppDocument->translationUnit()->ast()) { const QByteArray source = document()->toPlainText().toUtf8(); - const QString name = filePath().toString(); const Snapshot &snapshot = data()->m_snapshot; - m_cppDocument = snapshot.preprocessedDocument(source, name); + m_cppDocument = snapshot.preprocessedDocument(source, filePath()); m_cppDocument->check(); } diff --git a/src/plugins/cpptools/cppsemanticinfoupdater.cpp b/src/plugins/cpptools/cppsemanticinfoupdater.cpp index 0c94c7715c..64819115aa 100644 --- a/src/plugins/cpptools/cppsemanticinfoupdater.cpp +++ b/src/plugins/cpptools/cppsemanticinfoupdater.cpp @@ -118,7 +118,8 @@ SemanticInfo SemanticInfoUpdaterPrivate::update(const SemanticInfo::Source &sour newSemanticInfo.revision = source.revision; newSemanticInfo.snapshot = source.snapshot; - Document::Ptr doc = newSemanticInfo.snapshot.preprocessedDocument(source.code, source.fileName); + Document::Ptr doc = newSemanticInfo.snapshot.preprocessedDocument(source.code, + Utils::FilePath::fromString(source.fileName)); if (processor) doc->control()->setTopLevelDeclarationProcessor(processor); doc->check(); diff --git a/src/plugins/cpptools/cppsourceprocessor_test.cpp b/src/plugins/cpptools/cppsourceprocessor_test.cpp index 67a1b9f702..a30c3e4280 100644 --- a/src/plugins/cpptools/cppsourceprocessor_test.cpp +++ b/src/plugins/cpptools/cppsourceprocessor_test.cpp @@ -23,7 +23,7 @@ ** ****************************************************************************/ -#include "cpptoolsplugin.h" +#include "cppsourceprocessor_test.h" #include "baseeditordocumentprocessor.h" #include "cppmodelmanager.h" @@ -90,7 +90,7 @@ private: }; /// Check: Resolved and unresolved includes are properly tracked. -void CppToolsPlugin::test_cppsourceprocessor_includes_resolvedUnresolved() +void SourceProcessorTest::testIncludesResolvedUnresolved() { const QString testFilePath = TestIncludePaths::testFilePath(QLatin1String("test_main_resolvedUnresolved.cpp")); @@ -115,7 +115,7 @@ void CppToolsPlugin::test_cppsourceprocessor_includes_resolvedUnresolved() } /// Check: Avoid self-include entries due to cyclic includes. -void CppToolsPlugin::test_cppsourceprocessor_includes_cyclic() +void SourceProcessorTest::testIncludesCyclic() { const QString fileName1 = TestIncludePaths::testFilePath(QLatin1String("cyclic1.h")); const QString fileName2 = TestIncludePaths::testFilePath(QLatin1String("cyclic2.h")); @@ -154,7 +154,7 @@ void CppToolsPlugin::test_cppsourceprocessor_includes_cyclic() } /// Check: All include errors are reported as diagnostic messages. -void CppToolsPlugin::test_cppsourceprocessor_includes_allDiagnostics() +void SourceProcessorTest::testIncludesAllDiagnostics() { const QString testFilePath = TestIncludePaths::testFilePath(QLatin1String("test_main_allDiagnostics.cpp")); @@ -168,7 +168,7 @@ void CppToolsPlugin::test_cppsourceprocessor_includes_allDiagnostics() QCOMPARE(document->diagnosticMessages().size(), 3); } -void CppToolsPlugin::test_cppsourceprocessor_macroUses() +void SourceProcessorTest::testMacroUses() { const QString testFilePath = TestIncludePaths::testFilePath(QLatin1String("test_main_macroUses.cpp")); @@ -198,7 +198,7 @@ static bool isMacroDefinedInDocument(const QByteArray ¯oName, const Document static inline QString _(const QByteArray &ba) { return QString::fromLatin1(ba, ba.size()); } -void CppToolsPlugin::test_cppsourceprocessor_includeNext() +void SourceProcessorTest::testIncludeNext() { const Core::Tests::TestDataDir data( _(SRCDIR "/../../../tests/auto/cplusplus/preprocessor/data/include_next-data/")); diff --git a/src/plugins/cpptools/cppsourceprocessor_test.h b/src/plugins/cpptools/cppsourceprocessor_test.h new file mode 100644 index 0000000000..c0175cf8b3 --- /dev/null +++ b/src/plugins/cpptools/cppsourceprocessor_test.h @@ -0,0 +1,44 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#pragma once + +#include <QObject> + +namespace CppTools::Internal { + +class SourceProcessorTest : public QObject +{ + Q_OBJECT + +private slots: + void testIncludesResolvedUnresolved(); + void testIncludesCyclic(); + void testIncludesAllDiagnostics(); + void testMacroUses(); + void testIncludeNext(); +}; + +} // namespace CppTools::Internal diff --git a/src/plugins/cpptools/cpptools.pro b/src/plugins/cpptools/cpptools.pro index 7052d01ab6..3cb0e6c9f9 100644 --- a/src/plugins/cpptools/cpptools.pro +++ b/src/plugins/cpptools/cpptools.pro @@ -199,9 +199,21 @@ FORMS += \ equals(TEST, 1) { HEADERS += \ + compileroptionsbuilder_test.h \ + cppcodegen_test.h \ + cppcompletion_test.h \ + cppheadersource_test.h \ + cpplocalsymbols_test.h \ + cpplocatorfilter_test.h \ + cppmodelmanager_test.h \ + cpppointerdeclarationformatter_test.h \ + cppsourceprocessor_test.h \ cppsourceprocessertesthelper.h \ cpptoolstestcase.h \ - modelmanagertesthelper.h + modelmanagertesthelper.h \ + projectinfo_test.h \ + symbolsearcher_test.h \ + typehierarchybuilder_test.h SOURCES += \ cppcodegen_test.cpp \ @@ -214,7 +226,9 @@ equals(TEST, 1) { cppsourceprocessertesthelper.cpp \ cppsourceprocessor_test.cpp \ cpptoolstestcase.cpp \ + compileroptionsbuilder_test.cpp \ modelmanagertesthelper.cpp \ + projectinfo_test.cpp \ symbolsearcher_test.cpp \ typehierarchybuilder_test.cpp diff --git a/src/plugins/cpptools/cpptools.qbs b/src/plugins/cpptools/cpptools.qbs index 13382138bf..13c79ccff5 100644 --- a/src/plugins/cpptools/cpptools.qbs +++ b/src/plugins/cpptools/cpptools.qbs @@ -229,20 +229,34 @@ Project { name: "Tests" condition: qtc.testsEnabled files: [ + "compileroptionsbuilder_test.cpp", + "compileroptionsbuilder_test.h", "cppcodegen_test.cpp", + "cppcodegen_test.h", "cppcompletion_test.cpp", + "cppcompletion_test.h", "cppheadersource_test.cpp", + "cppheadersource_test.h", "cpplocalsymbols_test.cpp", + "cpplocalsymbols_test.h", "cpplocatorfilter_test.cpp", + "cpplocatorfilter_test.h", "cppmodelmanager_test.cpp", + "cppmodelmanager_test.h", "cpppointerdeclarationformatter_test.cpp", + "cpppointerdeclarationformatter_test.h", "cppsourceprocessertesthelper.cpp", "cppsourceprocessertesthelper.h", "cppsourceprocessor_test.cpp", + "cppsourceprocessor_test.h", "modelmanagertesthelper.cpp", "modelmanagertesthelper.h", + "projectinfo_test.cpp", + "projectinfo_test.h", "symbolsearcher_test.cpp", + "symbolsearcher_test.h", "typehierarchybuilder_test.cpp", + "typehierarchybuilder_test.h", ] cpp.defines: outer.concat(['SRCDIR="' + FileInfo.path(filePath) + '"']) diff --git a/src/plugins/cpptools/cpptoolsjsextension.cpp b/src/plugins/cpptools/cpptoolsjsextension.cpp index 64df64c101..04104d1526 100644 --- a/src/plugins/cpptools/cpptoolsjsextension.cpp +++ b/src/plugins/cpptools/cpptoolsjsextension.cpp @@ -164,7 +164,8 @@ bool CppToolsJsExtension::hasQObjectParent(const QString &klassName) const return false; source = file.readAll(); } - const auto doc = snapshot.preprocessedDocument(source, item->fileName()); + const auto doc = snapshot.preprocessedDocument(source, + Utils::FilePath::fromString(item->fileName())); if (!doc) return false; doc->check(); diff --git a/src/plugins/cpptools/cpptoolsplugin.cpp b/src/plugins/cpptools/cpptoolsplugin.cpp index 1e77501c0f..7eb0c916ee 100644 --- a/src/plugins/cpptools/cpptoolsplugin.cpp +++ b/src/plugins/cpptools/cpptoolsplugin.cpp @@ -24,6 +24,7 @@ ****************************************************************************/ #include "cpptoolsplugin.h" + #include "cppcodemodelsettingspage.h" #include "cppcodestylesettingspage.h" #include "cppfilesettingspage.h" @@ -59,6 +60,24 @@ #include <utils/mimetypes/mimedatabase.h> #include <utils/qtcassert.h> +#ifdef WITH_TESTS +#include "compileroptionsbuilder_test.h" +#include "cppcodegen_test.h" +#include "cppcompletion_test.h" +#include "cppheadersource_test.h" +#include "cpplocalsymbols_test.h" +#include "cpplocatorfilter_test.h" +#include "cppmodelmanager_test.h" +#include "cpppointerdeclarationformatter_test.h" +#include "cppsourceprocessor_test.h" +#include "functionutils.h" +#include "includeutils.h" +#include "projectinfo_test.h" +#include "senddocumenttracker.h" +#include "symbolsearcher_test.h" +#include "typehierarchybuilder_test.h" +#endif + #include <QFileInfo> #include <QDir> #include <QDebug> @@ -67,6 +86,7 @@ using namespace Core; using namespace CPlusPlus; +using namespace ProjectExplorer; using namespace Utils; namespace CppTools { @@ -125,9 +145,9 @@ void CppToolsPlugin::clearHeaderSourceCache() m_headerSourceMapping.clear(); } -Utils::FilePath CppToolsPlugin::licenseTemplatePath() +FilePath CppToolsPlugin::licenseTemplatePath() { - return Utils::FilePath::fromString(m_instance->d->m_fileSettings.licenseTemplatePath); + return FilePath::fromString(m_instance->d->m_fileSettings.licenseTemplatePath); } QString CppToolsPlugin::licenseTemplate() @@ -191,14 +211,14 @@ bool CppToolsPlugin::initialize(const QStringList &arguments, QString *error) QAction *openInNextSplitAction = new QAction(tr("Open Corresponding Header/Source in Next Split"), this); command = ActionManager::registerAction(openInNextSplitAction, Constants::OPEN_HEADER_SOURCE_IN_NEXT_SPLIT, context, true); - command->setDefaultKeySequence(QKeySequence(Utils::HostOsInfo::isMacHost() + command->setDefaultKeySequence(QKeySequence(HostOsInfo::isMacHost() ? tr("Meta+E, F4") : tr("Ctrl+E, F4"))); mcpptools->addAction(command); connect(openInNextSplitAction, &QAction::triggered, this, &CppToolsPlugin::switchHeaderSourceInNextSplit); - Utils::MacroExpander *expander = Utils::globalMacroExpander(); + MacroExpander *expander = globalMacroExpander(); expander->registerVariable("Cpp:LicenseTemplate", tr("The license template."), []() { return CppToolsPlugin::licenseTemplate(); }); @@ -211,13 +231,13 @@ bool CppToolsPlugin::initialize(const QStringList &arguments, QString *error) tr("Insert \"#pragma once\" instead of \"#ifndef\" include guards into header file"), [] { return usePragmaOnce() ? QString("true") : QString(); }); - const auto panelFactory = new ProjectExplorer::ProjectPanelFactory; + const auto panelFactory = new ProjectPanelFactory; panelFactory->setPriority(100); panelFactory->setDisplayName(tr("Clangd")); - panelFactory->setCreateWidgetFunction([](ProjectExplorer::Project *project) { + panelFactory->setCreateWidgetFunction([](Project *project) { return new ClangdProjectSettingsWidget(project); }); - ProjectExplorer::ProjectPanelFactory::registerFactory(panelFactory); + ProjectPanelFactory::registerFactory(panelFactory); return true; } @@ -233,6 +253,32 @@ void CppToolsPlugin::extensionsInitialized() d->m_clangdSettingsPage = new ClangdSettingsPage; } +QVector<QObject *> CppToolsPlugin::createTestObjects() const +{ + return { +#ifdef WITH_TESTS + new CodegenTest, + new CompilerOptionsBuilderTest, + new CompletionTest, + new FunctionUtilsTest, + new HeaderPathFilterTest, + new HeaderSourceTest, + new IncludeGroupsTest, + new LocalSymbolsTest, + new LocatorFilterTest, + new ModelManagerTest, + new PointerDeclarationFormatterTest, + new ProjectFileCategorizerTest, + new ProjectInfoGeneratorTest, + new ProjectPartChooserTest, + new DocumentTrackerTest, + new SourceProcessorTest, + new SymbolSearcherTest, + new TypeHierarchyBuilderTest, +#endif + }; +} + CppCodeModelSettings *CppToolsPlugin::codeModelSettings() { return &d->m_codeModelSettings; @@ -240,7 +286,7 @@ CppCodeModelSettings *CppToolsPlugin::codeModelSettings() CppFileSettings *CppToolsPlugin::fileSettings() { - return &d->m_fileSettings; + return &instance()->d->m_fileSettings; } void CppToolsPlugin::switchHeaderSource() @@ -256,8 +302,7 @@ void CppToolsPlugin::switchHeaderSourceInNextSplit() EditorManager::openEditor(otherFile, Id(), EditorManager::OpenInOtherSplit); } -static QStringList findFilesInProject(const QString &name, - const ProjectExplorer::Project *project) +static QStringList findFilesInProject(const QString &name, const Project *project) { if (debug) qDebug() << Q_FUNC_INFO << name << project; @@ -268,11 +313,11 @@ static QStringList findFilesInProject(const QString &name, QString pattern = QString(1, QLatin1Char('/')); pattern += name; const QStringList projectFiles - = Utils::transform(project->files(ProjectExplorer::Project::AllFiles), &Utils::FilePath::toString); + = transform(project->files(Project::AllFiles), &FilePath::toString); const QStringList::const_iterator pcend = projectFiles.constEnd(); QStringList candidateList; for (QStringList::const_iterator it = projectFiles.constBegin(); it != pcend; ++it) { - if (it->endsWith(pattern, Utils::HostOsInfo::fileNameCaseSensitivity())) + if (it->endsWith(pattern, HostOsInfo::fileNameCaseSensitivity())) candidateList.append(*it); } return candidateList; @@ -310,7 +355,7 @@ static QStringList baseNameWithAllSuffixes(const QString &baseName, const QStrin { QStringList result; const QChar dot = QLatin1Char('.'); - foreach (const QString &suffix, suffixes) { + for (const QString &suffix : suffixes) { QString fileName = baseName; fileName += dot; fileName += suffix; @@ -325,16 +370,16 @@ static QStringList baseNamesWithAllPrefixes(const QStringList &baseNames, bool i const QStringList &sourcePrefixes = m_instance->sourcePrefixes(); const QStringList &headerPrefixes = m_instance->headerPrefixes(); - foreach (const QString &name, baseNames) { - foreach (const QString &prefix, isHeader ? headerPrefixes : sourcePrefixes) { + for (const QString &name : baseNames) { + for (const QString &prefix : isHeader ? headerPrefixes : sourcePrefixes) { if (name.startsWith(prefix)) { QString nameWithoutPrefix = name.mid(prefix.size()); result += nameWithoutPrefix; - foreach (const QString &prefix, isHeader ? sourcePrefixes : headerPrefixes) + for (const QString &prefix : isHeader ? sourcePrefixes : headerPrefixes) result += prefix + nameWithoutPrefix; } } - foreach (const QString &prefix, isHeader ? sourcePrefixes : headerPrefixes) + for (const QString &prefix : isHeader ? sourcePrefixes : headerPrefixes) result += prefix + name; } @@ -344,7 +389,7 @@ static QStringList baseNamesWithAllPrefixes(const QStringList &baseNames, bool i static QStringList baseDirWithAllDirectories(const QDir &baseDir, const QStringList &directories) { QStringList result; - foreach (const QString &dir, directories) + for (const QString &dir : directories) result << QDir::cleanPath(baseDir.absoluteFilePath(dir)); return result; } @@ -353,7 +398,7 @@ static int commonFilePathLength(const QString &s1, const QString &s2) { int length = qMin(s1.length(), s2.length()); for (int i = 0; i < length; ++i) - if (Utils::HostOsInfo::fileNameCaseSensitivity() == Qt::CaseSensitive) { + if (HostOsInfo::fileNameCaseSensitivity() == Qt::CaseSensitive) { if (s1[i] != s2[i]) return i; } else { @@ -365,16 +410,16 @@ static int commonFilePathLength(const QString &s1, const QString &s2) static QString correspondingHeaderOrSourceInProject(const QFileInfo &fileInfo, const QStringList &candidateFileNames, - const ProjectExplorer::Project *project, + const Project *project, CacheUsage cacheUsage) { QString bestFileName; int compareValue = 0; const QString filePath = fileInfo.filePath(); - foreach (const QString &candidateFileName, candidateFileNames) { + for (const QString &candidateFileName : candidateFileNames) { const QStringList projectFiles = findFilesInProject(candidateFileName, project); // Find the file having the most common path with fileName - foreach (const QString &projectFile, projectFiles) { + for (const QString &projectFile : projectFiles) { int value = commonFilePathLength(filePath, projectFile); if (value > compareValue) { compareValue = value; @@ -442,10 +487,10 @@ QString correspondingHeaderOrSource(const QString &fileName, bool *wasHeader, Ca candidateFileNames += baseNamesWithAllPrefixes(candidateFileNames, isHeader); // Try to find a file in the same or sibling directories first - foreach (const QString &candidateDir, candidateDirs) { - foreach (const QString &candidateFileName, candidateFileNames) { + for (const QString &candidateDir : qAsConst(candidateDirs)) { + for (const QString &candidateFileName : qAsConst(candidateFileNames)) { const QString candidateFilePath = candidateDir + QLatin1Char('/') + candidateFileName; - const QString normalized = Utils::FileUtils::normalizePathName(candidateFilePath); + const QString normalized = FileUtils::normalizedPathName(candidateFilePath); const QFileInfo candidateFi(normalized); if (candidateFi.isFile()) { if (cacheUsage == CacheUsage::ReadWrite) { @@ -459,7 +504,7 @@ QString correspondingHeaderOrSource(const QString &fileName, bool *wasHeader, Ca } // Find files in the current project - ProjectExplorer::Project *currentProject = ProjectExplorer::ProjectTree::currentProject(); + Project *currentProject = ProjectTree::currentProject(); if (currentProject) { const QString path = correspondingHeaderOrSourceInProject(fi, candidateFileNames, currentProject, cacheUsage); @@ -469,9 +514,9 @@ QString correspondingHeaderOrSource(const QString &fileName, bool *wasHeader, Ca // Find files in other projects } else { CppModelManager *modelManager = CppModelManager::instance(); - QList<ProjectInfo> projectInfos = modelManager->projectInfos(); - foreach (const ProjectInfo &projectInfo, projectInfos) { - const ProjectExplorer::Project *project = projectInfo.project().data(); + const QList<ProjectInfo::Ptr> projectInfos = modelManager->projectInfos(); + for (const ProjectInfo::Ptr &projectInfo : projectInfos) { + const Project *project = projectForProjectInfo(*projectInfo); if (project == currentProject) continue; // We have already checked the current project. diff --git a/src/plugins/cpptools/cpptoolsplugin.h b/src/plugins/cpptools/cpptoolsplugin.h index 24f981d15f..d5610265d0 100644 --- a/src/plugins/cpptools/cpptoolsplugin.h +++ b/src/plugins/cpptools/cpptoolsplugin.h @@ -58,120 +58,18 @@ public: static QString licenseTemplate(); static bool usePragmaOnce(); - bool initialize(const QStringList &arguments, QString *errorMessage) final; - void extensionsInitialized() final; - CppCodeModelSettings *codeModelSettings(); + static CppFileSettings *fileSettings(); public slots: void switchHeaderSource(); void switchHeaderSourceInNextSplit(); -#ifdef WITH_TESTS -private slots: - // Init/Cleanup methods implemented in cppheadersource_test.cpp - void initTestCase(); - void cleanupTestCase(); - - void test_codegen_public_in_empty_class(); - void test_codegen_public_in_nonempty_class(); - void test_codegen_public_before_protected(); - void test_codegen_private_after_protected(); - void test_codegen_protected_in_nonempty_class(); - void test_codegen_protected_between_public_and_private(); - void test_codegen_qtdesigner_integration(); - void test_codegen_definition_empty_class(); - void test_codegen_definition_first_member(); - void test_codegen_definition_last_member(); - void test_codegen_definition_middle_member(); - void test_codegen_definition_middle_member_surrounded_by_undefined(); - void test_codegen_definition_member_specific_file(); - - void test_completion_basic_1(); - - void test_completion_template_function_data(); - void test_completion_template_function(); - - void test_completion_data(); - void test_completion(); - - void test_global_completion_data(); - void test_global_completion(); - - void test_doxygen_tag_completion_data(); - void test_doxygen_tag_completion(); - - void test_completion_member_access_operator_data(); - void test_completion_member_access_operator(); - - void test_completion_prefix_first_QTCREATORBUG_8737(); - void test_completion_prefix_first_QTCREATORBUG_9236(); - - void test_format_pointerdeclaration_in_simpledeclarations(); - void test_format_pointerdeclaration_in_simpledeclarations_data(); - void test_format_pointerdeclaration_in_controlflowstatements(); - void test_format_pointerdeclaration_in_controlflowstatements_data(); - void test_format_pointerdeclaration_multiple_declarators(); - void test_format_pointerdeclaration_multiple_declarators_data(); - void test_format_pointerdeclaration_multiple_matches(); - void test_format_pointerdeclaration_multiple_matches_data(); - void test_format_pointerdeclaration_macros(); - void test_format_pointerdeclaration_macros_data(); - - void test_cppsourceprocessor_includes_resolvedUnresolved(); - void test_cppsourceprocessor_includes_cyclic(); - void test_cppsourceprocessor_includes_allDiagnostics(); - void test_cppsourceprocessor_macroUses(); - void test_cppsourceprocessor_includeNext(); - - void test_functionutils_virtualFunctions(); - void test_functionutils_virtualFunctions_data(); - - void test_modelmanager_paths_are_clean(); - void test_modelmanager_framework_headers(); - void test_modelmanager_refresh_also_includes_of_project_files(); - void test_modelmanager_refresh_several_times(); - void test_modelmanager_refresh_test_for_changes(); - void test_modelmanager_refresh_added_and_purge_removed(); - void test_modelmanager_refresh_timeStampModified_if_sourcefiles_change(); - void test_modelmanager_refresh_timeStampModified_if_sourcefiles_change_data(); - void test_modelmanager_snapshot_after_two_projects(); - void test_modelmanager_extraeditorsupport_uiFiles(); - void test_modelmanager_gc_if_last_cppeditor_closed(); - void test_modelmanager_dont_gc_opened_files(); - void test_modelmanager_defines_per_project(); - void test_modelmanager_defines_per_editor(); - void test_modelmanager_updateEditorsAfterProjectUpdate(); - void test_modelmanager_precompiled_headers(); - void test_modelmanager_renameIncludes(); - void test_modelmanager_renameIncludesInEditor(); - void test_modelmanager_documentsAndRevisions(); - - void test_cpplocatorfilters_CppLocatorFilter(); - void test_cpplocatorfilters_CppLocatorFilter_data(); - void test_cpplocatorfilters_CppCurrentDocumentFilter(); - void test_cpplocatorfilters_CppCurrentDocumentHighlighting(); - void test_cpplocatorfilters_CppFunctionsFilterHighlighting(); - - void test_builtinsymbolsearcher(); - void test_builtinsymbolsearcher_data(); - - void test_headersource_data(); - void test_headersource(); - - void test_typehierarchy_data(); - void test_typehierarchy(); - - void test_cpplocalsymbols_data(); - void test_cpplocalsymbols(); - - void test_includeGroups_detectIncludeGroupsByNewLines(); - void test_includeGroups_detectIncludeGroupsByIncludeDir(); - void test_includeGroups_detectIncludeGroupsByIncludeType(); -#endif - private: - CppFileSettings *fileSettings(); + bool initialize(const QStringList &arguments, QString *errorMessage) final; + void extensionsInitialized() final; + QVector<QObject *> createTestObjects() const final; + class CppToolsPluginPrivate *d = nullptr; }; diff --git a/src/plugins/cpptools/cpptoolsreuse.cpp b/src/plugins/cpptools/cpptoolsreuse.cpp index 5294f1314b..de7dbcaeee 100644 --- a/src/plugins/cpptools/cpptoolsreuse.cpp +++ b/src/plugins/cpptools/cpptoolsreuse.cpp @@ -29,11 +29,13 @@ #include "cpprefactoringchanges.h" #include "cpptoolsconstants.h" #include "cpptoolsplugin.h" +#include "projectinfo.h" #include <coreplugin/documentmanager.h> #include <coreplugin/editormanager/editormanager.h> #include <coreplugin/idocument.h> #include <coreplugin/messagemanager.h> +#include <projectexplorer/session.h> #include <cplusplus/Overview.h> #include <cplusplus/LookupContext.h> @@ -572,4 +574,14 @@ NamespaceAST *NSCheckerVisitor::currentNamespace() return m_enteredNamespaces.empty() ? nullptr : m_enteredNamespaces.back(); } +ProjectExplorer::Project *projectForProjectPart(const ProjectPart &part) +{ + return ProjectExplorer::SessionManager::projectWithProjectFilePath(part.topLevelProject); +} + +ProjectExplorer::Project *projectForProjectInfo(const ProjectInfo &info) +{ + return ProjectExplorer::SessionManager::projectWithProjectFilePath(info.projectFilePath()); +} + } // CppTools diff --git a/src/plugins/cpptools/cpptoolsreuse.h b/src/plugins/cpptools/cpptoolsreuse.h index 02633f91ea..1946a93176 100644 --- a/src/plugins/cpptools/cpptoolsreuse.h +++ b/src/plugins/cpptools/cpptoolsreuse.h @@ -49,6 +49,7 @@ class LookupContext; namespace CppTools { class CppRefactoringFile; +class ProjectInfo; void CPPTOOLS_EXPORT moveCursorToEndOfIdentifier(QTextCursor *tc); void CPPTOOLS_EXPORT moveCursorToStartOfIdentifier(QTextCursor *tc); @@ -83,6 +84,9 @@ UsePrecompiledHeaders CPPTOOLS_EXPORT getPchUsage(); int indexerFileSizeLimitInMb(); bool fileSizeExceedsLimit(const QFileInfo &fileInfo, int sizeLimitInMb); +ProjectExplorer::Project CPPTOOLS_EXPORT *projectForProjectInfo(const CppTools::ProjectInfo &info); +ProjectExplorer::Project CPPTOOLS_EXPORT *projectForProjectPart(const CppTools::ProjectPart &part); + class ClangDiagnosticConfigsModel; ClangDiagnosticConfigsModel CPPTOOLS_EXPORT diagnosticConfigsModel(); ClangDiagnosticConfigsModel CPPTOOLS_EXPORT diff --git a/src/plugins/cpptools/cpptoolstestcase.cpp b/src/plugins/cpptools/cpptoolstestcase.cpp index e9a6e85d0d..fca2d11eed 100644 --- a/src/plugins/cpptools/cpptoolstestcase.cpp +++ b/src/plugins/cpptools/cpptoolstestcase.cpp @@ -50,30 +50,30 @@ #include <QtTest> using namespace ProjectExplorer; +using namespace Utils; + +namespace CppTools { +namespace Tests { static bool closeEditorsWithoutGarbageCollectorInvocation(const QList<Core::IEditor *> &editors) { - CppTools::CppModelManager::instance()->enableGarbageCollector(false); + CppModelManager::instance()->enableGarbageCollector(false); const bool closeEditorsSucceeded = Core::EditorManager::closeEditors(editors, false); - CppTools::CppModelManager::instance()->enableGarbageCollector(true); + CppModelManager::instance()->enableGarbageCollector(true); return closeEditorsSucceeded; } static bool snapshotContains(const CPlusPlus::Snapshot &snapshot, const QSet<QString> &filePaths) { - foreach (const QString &filePath, filePaths) { + for (const QString &filePath : filePaths) { if (!snapshot.contains(filePath)) { - const QString warning = QLatin1String("Missing file in snapshot: ") + filePath; - QWARN(qPrintable(warning)); + qWarning() << "Missing file in snapshot:" << qPrintable(filePath); return false; } } return true; } -namespace CppTools { -namespace Tests { - TestDocument::TestDocument(const QByteArray &fileName, const QByteArray &source, char cursorMarker) : m_fileName(QString::fromUtf8(fileName)) , m_source(QString::fromUtf8(source)) @@ -174,11 +174,11 @@ bool TestCase::parseFiles(const QSet<QString> &filePaths) QCoreApplication::processEvents(); const CPlusPlus::Snapshot snapshot = globalSnapshot(); if (snapshot.isEmpty()) { - QWARN("After parsing: snapshot is empty."); + qWarning("After parsing: snapshot is empty."); return false; } if (!snapshotContains(snapshot, filePaths)) { - QWARN("After parsing: snapshot does not contain all expected files."); + qWarning("After parsing: snapshot does not contain all expected files."); return false; } return true; @@ -214,7 +214,7 @@ QList<CPlusPlus::Document::Ptr> TestCase::waitForFilesInGlobalSnapshot(const QSt t.start(); QList<CPlusPlus::Document::Ptr> result; - foreach (const QString &filePath, filePaths) { + for (const QString &filePath : filePaths) { forever { if (CPlusPlus::Document::Ptr document = globalSnapshot().document(filePath)) { result.append(document); @@ -237,7 +237,7 @@ bool TestCase::waitUntilProjectIsFullyOpened(Project *project, int timeOutInMs) [project]() { return SessionManager::startupBuildSystem() && !SessionManager::startupBuildSystem()->isParsing() - && CppModelManager::instance()->projectInfo(project).isValid(); + && CppModelManager::instance()->projectInfo(project); }, timeOutInMs); } @@ -246,8 +246,7 @@ bool TestCase::writeFile(const QString &filePath, const QByteArray &contents) { Utils::FileSaver saver(Utils::FilePath::fromString(filePath)); if (!saver.write(contents) || !saver.finalize()) { - const QString warning = QLatin1String("Failed to write file to disk: ") + filePath; - QWARN(qPrintable(warning)); + qWarning() << "Failed to write file to disk:" << qPrintable(filePath); return false; } return true; @@ -279,13 +278,14 @@ ProjectOpenerAndCloser::~ProjectOpenerAndCloser() QCoreApplication::processEvents(); } -ProjectInfo ProjectOpenerAndCloser::open(const QString &projectFile, bool configureAsExampleProject, - Kit *kit) +ProjectInfo::Ptr ProjectOpenerAndCloser::open(const QString &projectFile, + bool configureAsExampleProject, Kit *kit) { - ProjectExplorerPlugin::OpenProjectResult result = ProjectExplorerPlugin::openProject(projectFile); + ProjectExplorerPlugin::OpenProjectResult result = + ProjectExplorerPlugin::openProject(FilePath::fromString(projectFile)); if (!result) { qWarning() << result.errorMessage() << result.alreadyOpen(); - return ProjectInfo(); + return {}; } Project *project = result.project(); @@ -297,7 +297,7 @@ ProjectInfo ProjectOpenerAndCloser::open(const QString &projectFile, bool config return CppModelManager::instance()->projectInfo(project); } - return ProjectInfo(); + return {}; } TemporaryDir::TemporaryDir() @@ -322,21 +322,18 @@ static bool copyRecursively(const QString &sourceDirPath, const QString &targetDirPath, QString *error) { - auto copyHelper = [](QFileInfo sourceInfo, QFileInfo targetInfo, QString *error) -> bool { - const QString sourcePath = sourceInfo.absoluteFilePath(); - const QString targetPath = targetInfo.absoluteFilePath(); - if (!QFile::copy(sourcePath, targetPath)) { + auto copyHelper = [](const FilePath &sourcePath, const FilePath &targetPath, QString *error) -> bool { + if (!sourcePath.copyFile(targetPath)) { if (error) { *error = QString::fromLatin1("copyRecursively() failed: \"%1\" to \"%2\".") - .arg(sourcePath, targetPath); + .arg(sourcePath.toUserOutput(), targetPath.toUserOutput()); } return false; } // Copied files from Qt resources are read-only. Make them writable // so that their parent directory can be removed without warnings. - QFile file(targetPath); - return file.setPermissions(file.permissions() | QFile::WriteUser); + return targetPath.setPermissions(targetPath.permissions() | QFile::WriteUser); }; return Utils::FileUtils::copyRecursively(Utils::FilePath::fromString(sourceDirPath), @@ -361,7 +358,7 @@ TemporaryCopiedDir::TemporaryCopiedDir(const QString &sourceDirPath) QString errorMessage; if (!copyRecursively(sourceDirPath, path(), &errorMessage)) { - QWARN(qPrintable(errorMessage)); + qWarning() << qPrintable(errorMessage); m_isValid = false; } } @@ -375,11 +372,8 @@ FileWriterAndRemover::FileWriterAndRemover(const QString &filePath, const QByteA : m_filePath(filePath) { if (QFileInfo::exists(filePath)) { - const QString warning = QString::fromLatin1( - "Will not overwrite existing file: \"%1\"." - " If this file is left over due to a(n) abort/crash, please remove manually.") - .arg(m_filePath); - QWARN(qPrintable(warning)); + qWarning().nospace() << "Will not overwrite existing file: " << m_filePath << "." + << " If this file is left over due to a(n) abort/crash, please remove manually."; m_writtenSuccessfully = false; } else { m_writtenSuccessfully = TestCase::writeFile(filePath, contents); @@ -388,10 +382,8 @@ FileWriterAndRemover::FileWriterAndRemover(const QString &filePath, const QByteA FileWriterAndRemover::~FileWriterAndRemover() { - if (m_writtenSuccessfully && !QFile::remove(m_filePath)) { - const QString warning = QLatin1String("Failed to remove file from disk: ") + m_filePath; - QWARN(qPrintable(warning)); - } + if (m_writtenSuccessfully && !QFile::remove(m_filePath)) + qWarning() << "Failed to remove file from disk:" << qPrintable(m_filePath); } VerifyCleanCppModelManager::VerifyCleanCppModelManager() diff --git a/src/plugins/cpptools/cpptoolstestcase.h b/src/plugins/cpptools/cpptoolstestcase.h index d03a1b297c..7919f3c921 100644 --- a/src/plugins/cpptools/cpptoolstestcase.h +++ b/src/plugins/cpptools/cpptoolstestcase.h @@ -27,6 +27,8 @@ #include "cpptools_global.h" +#include "projectinfo.h" + #include <cplusplus/CppDocument.h> #include <utils/temporarydirectory.h> @@ -52,7 +54,6 @@ class IAssistProposal; namespace CppTools { class CppModelManager; -class ProjectInfo; namespace Tests { @@ -140,7 +141,7 @@ public: ProjectOpenerAndCloser(); ~ProjectOpenerAndCloser(); // Closes opened projects - ProjectInfo open(const QString &projectFile, bool configureAsExampleProject = false, + CppTools::ProjectInfo::Ptr open(const QString &projectFile, bool configureAsExampleProject = false, ProjectExplorer::Kit *kit = nullptr); private: diff --git a/src/plugins/cpptools/cpptoolsunittestfiles.pri b/src/plugins/cpptools/cpptoolsunittestfiles.pri index 5d09a1d130..85f6d91ab2 100644 --- a/src/plugins/cpptools/cpptoolsunittestfiles.pri +++ b/src/plugins/cpptools/cpptoolsunittestfiles.pri @@ -5,23 +5,7 @@ shared { } HEADERS += \ - $$PWD/cppprojectfile.h \ - $$PWD/senddocumenttracker.h \ - $$PWD/projectpart.h \ - $$PWD/compileroptionsbuilder.h \ - $$PWD/cppprojectfilecategorizer.h \ - $$PWD/projectinfo.h \ - $$PWD/cppprojectinfogenerator.cpp \ - $$PWD/cppprojectpartchooser.h \ - $$PWD/headerpathfilter.h + $$PWD/cppprojectfile.h SOURCES += \ - $$PWD/cppprojectfile.cpp \ - $$PWD/senddocumenttracker.cpp \ - $$PWD/projectpart.cpp \ - $$PWD/compileroptionsbuilder.cpp \ - $$PWD/cppprojectfilecategorizer.cpp \ - $$PWD/projectinfo.cpp \ - $$PWD/cppprojectinfogenerator.cpp \ - $$PWD/cppprojectpartchooser.cpp \ - $$PWD/headerpathfilter.cpp + $$PWD/cppprojectfile.cpp diff --git a/src/plugins/cpptools/functionutils.cpp b/src/plugins/cpptools/functionutils.cpp index ff7c1ecb6d..981169ea97 100644 --- a/src/plugins/cpptools/functionutils.cpp +++ b/src/plugins/cpptools/functionutils.cpp @@ -215,7 +215,6 @@ QList<Function *> FunctionUtils::overrides(Function *function, Class *functionsC } #ifdef WITH_TESTS -#include "cpptoolsplugin.h" #include <QTest> @@ -237,7 +236,7 @@ Q_DECLARE_METATYPE(CppTools::Internal::Virtuality) namespace CppTools { namespace Internal { -void CppToolsPlugin::test_functionutils_virtualFunctions() +void FunctionUtilsTest::testVirtualFunctions() { // Create and parse document QFETCH(QByteArray, source); @@ -292,7 +291,7 @@ void CppToolsPlugin::test_functionutils_virtualFunctions() QVERIFY(firstVirtualList.isEmpty()); } -void CppToolsPlugin::test_functionutils_virtualFunctions_data() +void FunctionUtilsTest::testVirtualFunctions_data() { using _ = QByteArray; QTest::addColumn<QByteArray>("source"); diff --git a/src/plugins/cpptools/functionutils.h b/src/plugins/cpptools/functionutils.h index 56c13f0da6..90c2be2d3f 100644 --- a/src/plugins/cpptools/functionutils.h +++ b/src/plugins/cpptools/functionutils.h @@ -28,6 +28,7 @@ #include "cpptools_global.h" #include <QList> +#include <QObject> namespace CPlusPlus { class Class; @@ -56,4 +57,17 @@ public: const CPlusPlus::Snapshot &snapshot); }; +#ifdef WITH_TESTS +namespace Internal { +class FunctionUtilsTest : public QObject +{ + Q_OBJECT + +private slots: + void testVirtualFunctions(); + void testVirtualFunctions_data(); +}; +} // namespace Internal +#endif // WITH_TESTS + } // namespace CppTools diff --git a/src/plugins/cpptools/headerpathfilter.cpp b/src/plugins/cpptools/headerpathfilter.cpp index 2e010ead5a..73913ed7b2 100644 --- a/src/plugins/cpptools/headerpathfilter.cpp +++ b/src/plugins/cpptools/headerpathfilter.cpp @@ -36,11 +36,10 @@ #include <utils/algorithm.h> -namespace CppTools { +using namespace ProjectExplorer; +using namespace Utils; -using ProjectExplorer::HeaderPath; -using ProjectExplorer::HeaderPaths; -using ProjectExplorer::HeaderPathType; +namespace CppTools { void HeaderPathFilter::process() { @@ -104,7 +103,7 @@ void HeaderPathFilter::filterHeaderPath(const ProjectExplorer::HeaderPath &heade namespace { -QString clangIncludeDirectory(const QString &clangVersion, const QString &clangFallbackIncludeDir) +FilePath clangIncludeDirectory(const QString &clangVersion, const FilePath &clangFallbackIncludeDir) { #ifndef UNIT_TESTS return Core::ICore::clangIncludeDirectory(clangVersion, clangFallbackIncludeDir); @@ -158,9 +157,9 @@ void HeaderPathFilter::tweakHeaderPaths() auto split = resourceIterator(builtInHeaderPaths); if (!clangVersion.isEmpty()) { - const QString clangIncludePath + const FilePath clangIncludePath = clangIncludeDirectory(clangVersion, clangFallbackIncludeDirectory); - builtInHeaderPaths.insert(split, HeaderPath{clangIncludePath, HeaderPathType::BuiltIn}); + builtInHeaderPaths.insert(split, HeaderPath{clangIncludePath.toString(), HeaderPathType::BuiltIn}); } } diff --git a/src/plugins/cpptools/headerpathfilter.h b/src/plugins/cpptools/headerpathfilter.h index 4482c230ae..49fefd721c 100644 --- a/src/plugins/cpptools/headerpathfilter.h +++ b/src/plugins/cpptools/headerpathfilter.h @@ -28,14 +28,17 @@ #include "compileroptionsbuilder.h" #include "projectpart.h" +#include <utils/filepath.h> + namespace CppTools { + class CPPTOOLS_EXPORT HeaderPathFilter { public: HeaderPathFilter(const ProjectPart &projectPart, UseTweakedHeaderPaths useTweakedHeaderPaths = UseTweakedHeaderPaths::Yes, const QString &clangVersion = {}, - const QString &clangIncludeDirectory = {}, + const Utils::FilePath &clangIncludeDirectory = {}, const QString &projectDirectory = {}, const QString &buildDirectory = {}) : projectPart{projectPart} @@ -67,7 +70,7 @@ public: ProjectExplorer::HeaderPaths userHeaderPaths; const ProjectPart &projectPart; const QString clangVersion; - const QString clangFallbackIncludeDirectory; + const Utils::FilePath clangFallbackIncludeDirectory; const QString projectDirectory; const QString buildDirectory; const UseTweakedHeaderPaths useTweakedHeaderPaths; diff --git a/src/plugins/cpptools/includeutils.cpp b/src/plugins/cpptools/includeutils.cpp index c42b104acf..114093ce0f 100644 --- a/src/plugins/cpptools/includeutils.cpp +++ b/src/plugins/cpptools/includeutils.cpp @@ -33,6 +33,15 @@ #include <utils/qtcassert.h> #include <utils/stringutils.h> +#ifdef WITH_TESTS +#include "cppmodelmanager.h" +#include "cppsourceprocessertesthelper.h" +#include "cppsourceprocessor.h" +#include "cpptoolsplugin.h" +#include "cpptoolstestcase.h" +#include <QtTest> +#endif // WITH_TESTS + #include <QDir> #include <QFileInfo> #include <QStringList> @@ -42,7 +51,6 @@ #include <algorithm> using namespace CPlusPlus; -using namespace CppTools; using namespace CppTools::IncludeUtils; using namespace Utils; @@ -117,6 +125,8 @@ int lineAfterFirstComment(const QTextDocument *textDocument) } // anonymous namespace +namespace CppTools { + LineForNewIncludeDirective::LineForNewIncludeDirective(const QTextDocument *textDocument, const Document::Ptr cppDocument, MocIncludeMode mocIncludeMode, @@ -516,17 +526,7 @@ bool IncludeGroup::hasCommonIncludeDir() const } #ifdef WITH_TESTS - -#include "cppmodelmanager.h" -#include "cppsourceprocessertesthelper.h" -#include "cppsourceprocessor.h" -#include "cpptoolsplugin.h" -#include "cpptoolstestcase.h" - -#include <QtTest> - using namespace Tests; -using CppTools::Internal::CppToolsPlugin; static QList<Include> includesForSource(const QString &filePath) { @@ -542,7 +542,8 @@ static QList<Include> includesForSource(const QString &filePath) return document->resolvedIncludes(); } -void CppToolsPlugin::test_includeGroups_detectIncludeGroupsByNewLines() +namespace Internal { +void IncludeGroupsTest::testDetectIncludeGroupsByNewLines() { const QString testFilePath = TestIncludePaths::testFilePath( QLatin1String("test_main_detectIncludeGroupsByNewLines.cpp")); @@ -584,7 +585,7 @@ void CppToolsPlugin::test_includeGroups_detectIncludeGroupsByNewLines() QCOMPARE(IncludeGroup::filterMixedIncludeGroups(includeGroups).size(), 1); } -void CppToolsPlugin::test_includeGroups_detectIncludeGroupsByIncludeDir() +void IncludeGroupsTest::testDetectIncludeGroupsByIncludeDir() { const QString testFilePath = TestIncludePaths::testFilePath( QLatin1String("test_main_detectIncludeGroupsByIncludeDir.cpp")); @@ -608,7 +609,7 @@ void CppToolsPlugin::test_includeGroups_detectIncludeGroupsByIncludeDir() QCOMPARE(includeGroups.at(3).commonIncludeDir(), QLatin1String("")); } -void CppToolsPlugin::test_includeGroups_detectIncludeGroupsByIncludeType() +void IncludeGroupsTest::testDetectIncludeGroupsByIncludeType() { const QString testFilePath = TestIncludePaths::testFilePath( QLatin1String("test_main_detectIncludeGroupsByIncludeType.cpp")); @@ -632,4 +633,8 @@ void CppToolsPlugin::test_includeGroups_detectIncludeGroupsByIncludeType() QVERIFY(includeGroups.at(3).hasOnlyIncludesOfType(Client::IncludeGlobal)); } +} // namespace Internal + #endif // WITH_TESTS + +} // namespace CppTools diff --git a/src/plugins/cpptools/includeutils.h b/src/plugins/cpptools/includeutils.h index 59ff8b81a7..508047c168 100644 --- a/src/plugins/cpptools/includeutils.h +++ b/src/plugins/cpptools/includeutils.h @@ -31,6 +31,7 @@ #include <cplusplus/PreprocessorClient.h> #include <QList> +#include <QObject> #include <QString> QT_FORWARD_DECLARE_CLASS(QTextDocument) @@ -105,4 +106,19 @@ private: }; } // namespace IncludeUtils + +#ifdef WITH_TESTS +namespace Internal { +class IncludeGroupsTest : public QObject +{ + Q_OBJECT + +private slots: + void testDetectIncludeGroupsByNewLines(); + void testDetectIncludeGroupsByIncludeDir(); + void testDetectIncludeGroupsByIncludeType(); +}; +} // namespace Internal +#endif // WITH_TESTS + } // namespace CppTools diff --git a/src/plugins/cpptools/modelmanagertesthelper.cpp b/src/plugins/cpptools/modelmanagertesthelper.cpp index 08d01c02e0..b80ce25564 100644 --- a/src/plugins/cpptools/modelmanagertesthelper.cpp +++ b/src/plugins/cpptools/modelmanagertesthelper.cpp @@ -29,6 +29,8 @@ #include "cppworkingcopy.h" #include "projectinfo.h" +#include <projectexplorer/session.h> + #include <QtTest> #include <cassert> @@ -36,8 +38,8 @@ using namespace CppTools::Internal; using namespace CppTools::Tests; -TestProject::TestProject(const QString &name, QObject *parent) : - ProjectExplorer::Project("x-binary/foo", Utils::FilePath()), +TestProject::TestProject(const QString &name, QObject *parent, const Utils::FilePath &filePath) : + ProjectExplorer::Project("x-binary/foo", filePath), m_name(name) { setParent(parent); @@ -75,22 +77,28 @@ ModelManagerTestHelper::~ModelManagerTestHelper() void ModelManagerTestHelper::cleanup() { CppModelManager *mm = CppModelManager::instance(); - QList<ProjectInfo> pies = mm->projectInfos(); - foreach (const ProjectInfo &pie, pies) - emit aboutToRemoveProject(pie.project().data()); + QList<ProjectInfo::Ptr> pies = mm->projectInfos(); + for (Project * const p : qAsConst(m_projects)) { + ProjectExplorer::SessionManager::removeProject(p); + emit aboutToRemoveProject(p); + } if (!pies.isEmpty()) waitForFinishedGc(); } -ModelManagerTestHelper::Project *ModelManagerTestHelper::createProject(const QString &name) +ModelManagerTestHelper::Project *ModelManagerTestHelper::createProject( + const QString &name, const Utils::FilePath &filePath) { - auto tp = new TestProject(name, this); + auto tp = new TestProject(name, this, filePath); + m_projects.push_back(tp); + ProjectExplorer::SessionManager::addProject(tp); emit projectAdded(tp); return tp; } -QSet<QString> ModelManagerTestHelper::updateProjectInfo(const CppTools::ProjectInfo &projectInfo) +QSet<QString> ModelManagerTestHelper::updateProjectInfo( + const CppTools::ProjectInfo::Ptr &projectInfo) { resetRefreshedSourceFiles(); CppModelManager::instance()->updateProjectInfo(projectInfo).waitForFinished(); diff --git a/src/plugins/cpptools/modelmanagertesthelper.h b/src/plugins/cpptools/modelmanagertesthelper.h index 9fe887006d..e5ee32cca8 100644 --- a/src/plugins/cpptools/modelmanagertesthelper.h +++ b/src/plugins/cpptools/modelmanagertesthelper.h @@ -40,7 +40,7 @@ class CPPTOOLS_EXPORT TestProject: public ProjectExplorer::Project Q_OBJECT public: - TestProject(const QString &name, QObject *parent); + TestProject(const QString &name, QObject *parent, const Utils::FilePath &filePath = {}); bool needsConfiguration() const final { return false; } @@ -61,9 +61,9 @@ public: void cleanup(); - Project *createProject(const QString &name); + Project *createProject(const QString &name, const Utils::FilePath &filePath = {}); - QSet<QString> updateProjectInfo(const ProjectInfo &projectInfo); + QSet<QString> updateProjectInfo(const ProjectInfo::Ptr &projectInfo); void resetRefreshedSourceFiles(); QSet<QString> waitForRefreshedSourceFiles(); @@ -82,6 +82,7 @@ private: bool m_refreshHappened; bool m_testOnlyForCleanedProjects; QSet<QString> m_lastRefreshedSourceFiles; + QList<Project *> m_projects; }; } // namespace Tests diff --git a/src/plugins/cpptools/projectinfo.cpp b/src/plugins/cpptools/projectinfo.cpp index a7047dc972..2125862be4 100644 --- a/src/plugins/cpptools/projectinfo.cpp +++ b/src/plugins/cpptools/projectinfo.cpp @@ -33,19 +33,10 @@ namespace CppTools { -ProjectInfo::ProjectInfo(QPointer<ProjectExplorer::Project> project) - : m_project(project) +ProjectInfo::Ptr ProjectInfo::create(const ProjectExplorer::ProjectUpdateInfo &updateInfo, + const QVector<ProjectPart::Ptr> &projectParts) { -} - -bool ProjectInfo::isValid() const -{ - return !m_project.isNull(); -} - -QPointer<ProjectExplorer::Project> ProjectInfo::project() const -{ - return m_project; + return Ptr(new ProjectInfo(updateInfo, projectParts)); } const QVector<ProjectPart::Ptr> ProjectInfo::projectParts() const @@ -60,7 +51,9 @@ const QSet<QString> ProjectInfo::sourceFiles() const bool ProjectInfo::operator ==(const ProjectInfo &other) const { - return m_project == other.m_project + return m_projectName == other.m_projectName + && m_projectFilePath == other.m_projectFilePath + && m_buildRoot == other.m_buildRoot && m_projectParts == other.m_projectParts && m_headerPaths == other.m_headerPaths && m_sourceFiles == other.m_sourceFiles @@ -87,35 +80,46 @@ bool ProjectInfo::configurationOrFilesChanged(const ProjectInfo &other) const return configurationChanged(other) || m_sourceFiles != other.m_sourceFiles; } -void ProjectInfo::appendProjectPart(const ProjectPart::Ptr &projectPart) +static QSet<QString> getSourceFiles(const QVector<ProjectPart::Ptr> &projectParts) { - if (projectPart) - m_projectParts.append(projectPart); + QSet<QString> sourceFiles; + for (const ProjectPart::Ptr &part : projectParts) { + for (const ProjectFile &file : qAsConst(part->files)) + sourceFiles.insert(file.path); + } + return sourceFiles; } -void ProjectInfo::finish() +static ProjectExplorer::Macros getDefines(const QVector<ProjectPart::Ptr> &projectParts) { - QSet<ProjectExplorer::HeaderPath> uniqueHeaderPaths; + ProjectExplorer::Macros defines; + for (const ProjectPart::Ptr &part : projectParts) { + defines.append(part->toolChainMacros); + defines.append(part->projectMacros); + } + return defines; +} - foreach (const ProjectPart::Ptr &part, m_projectParts) { - // Update header paths - foreach (const ProjectExplorer::HeaderPath &headerPath, part->headerPaths) { - const int count = uniqueHeaderPaths.count(); +static ProjectExplorer::HeaderPaths getHeaderPaths(const QVector<ProjectPart::Ptr> &projectParts) +{ + QSet<ProjectExplorer::HeaderPath> uniqueHeaderPaths; + for (const ProjectPart::Ptr &part : projectParts) { + for (const ProjectExplorer::HeaderPath &headerPath : qAsConst(part->headerPaths)) uniqueHeaderPaths.insert(headerPath); - if (count < uniqueHeaderPaths.count()) - m_headerPaths += headerPath; - } - - // Update source files - foreach (const ProjectFile &file, part->files) - m_sourceFiles.insert(file.path); - - // Update defines - m_defines.append(part->toolChainMacros); - m_defines.append(part->projectMacros); - if (!part->projectConfigFile.isEmpty()) - m_defines += ProjectExplorer::Macro::toMacros(ProjectPart::readProjectConfigFile(part)); } + return ProjectExplorer::HeaderPaths(uniqueHeaderPaths.cbegin(), uniqueHeaderPaths.cend()); +} + +ProjectInfo::ProjectInfo(const ProjectExplorer::ProjectUpdateInfo &updateInfo, + const QVector<ProjectPart::Ptr> &projectParts) + : m_projectParts(projectParts), + m_projectName(updateInfo.projectName), + m_projectFilePath(updateInfo.projectFilePath), + m_buildRoot(updateInfo.buildRoot), + m_headerPaths(getHeaderPaths(projectParts)), + m_sourceFiles(getSourceFiles(projectParts)), + m_defines(getDefines(projectParts)) +{ } } // namespace CppTools diff --git a/src/plugins/cpptools/projectinfo.h b/src/plugins/cpptools/projectinfo.h index 347e72f477..bf73bd71bb 100644 --- a/src/plugins/cpptools/projectinfo.h +++ b/src/plugins/cpptools/projectinfo.h @@ -32,25 +32,29 @@ #include <projectexplorer/project.h> #include <projectexplorer/rawprojectpart.h> #include <projectexplorer/toolchain.h> +#include <utils/fileutils.h> #include <QHash> -#include <QPointer> #include <QSet> #include <QVector> +#include <memory> + namespace CppTools { class CPPTOOLS_EXPORT ProjectInfo { public: - ProjectInfo() = default; - explicit ProjectInfo(QPointer<ProjectExplorer::Project> project); - - bool isValid() const; + using Ptr = std::shared_ptr<ProjectInfo>; + static Ptr create(const ProjectExplorer::ProjectUpdateInfo &updateInfo, + const QVector<ProjectPart::Ptr> &projectParts); - QPointer<ProjectExplorer::Project> project() const; const QVector<ProjectPart::Ptr> projectParts() const; const QSet<QString> sourceFiles() const; + QString projectName() const { return m_projectName; } + Utils::FilePath projectFilePath() const { return m_projectFilePath; } + Utils::FilePath projectRoot() const { return m_projectFilePath.parentDir(); } + Utils::FilePath buildRoot() const { return m_buildRoot; } // Comparisons bool operator ==(const ProjectInfo &other) const; @@ -59,18 +63,17 @@ public: bool configurationChanged(const ProjectInfo &other) const; bool configurationOrFilesChanged(const ProjectInfo &other) const; - // Construction - void appendProjectPart(const ProjectPart::Ptr &projectPart); - void finish(); - private: - QPointer<ProjectExplorer::Project> m_project; - QVector<ProjectPart::Ptr> m_projectParts; + ProjectInfo(const ProjectExplorer::ProjectUpdateInfo &updateInfo, + const QVector<ProjectPart::Ptr> &projectParts); - // The members below are (re)calculated from the project parts with finish() - ProjectExplorer::HeaderPaths m_headerPaths; - QSet<QString> m_sourceFiles; - ProjectExplorer::Macros m_defines; + const QVector<ProjectPart::Ptr> m_projectParts; + const QString m_projectName; + const Utils::FilePath m_projectFilePath; + const Utils::FilePath m_buildRoot; + const ProjectExplorer::HeaderPaths m_headerPaths; + const QSet<QString> m_sourceFiles; + const ProjectExplorer::Macros m_defines; }; } // namespace CppTools diff --git a/src/plugins/cpptools/projectinfo_test.cpp b/src/plugins/cpptools/projectinfo_test.cpp new file mode 100644 index 0000000000..fb24554684 --- /dev/null +++ b/src/plugins/cpptools/projectinfo_test.cpp @@ -0,0 +1,865 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#include "projectinfo_test.h" + +#include "cppprojectfilecategorizer.h" +#include "cppprojectinfogenerator.h" +#include "cppprojectpartchooser.h" +#include "headerpathfilter.h" +#include "projectinfo.h" + +#include <projectexplorer/projectexplorerconstants.h> +#include <projectexplorer/toolchainconfigwidget.h> +#include <utils/algorithm.h> + +#include <QtTest> + +using namespace ProjectExplorer; + +namespace CppTools { +namespace Internal { + +namespace { +class ProjectPartChooserTestHelper +{ +public: + ProjectPartChooserTestHelper() + { + chooser.setFallbackProjectPart([&]() { + return fallbackProjectPart; + }); + chooser.setProjectPartsForFile([&](const QString &) { + return projectPartsForFile; + }); + chooser.setProjectPartsFromDependenciesForFile([&](const QString &) { + return projectPartsFromDependenciesForFile; + }); + } + + const ProjectPartInfo choose() + { + return chooser.choose(filePath, currentProjectPartInfo, preferredProjectPartId, + projectMap.value(activeProject).get(), + languagePreference, projectsChanged); + } + + static QList<ProjectPart::Ptr> createProjectPartsWithDifferentProjects() + { + QList<ProjectPart::Ptr> projectParts; + + const auto p1 = std::make_shared<Project>( + QString(), Utils::FilePath::fromString("p1.pro")); + projectMap.insert(p1->projectFilePath(), p1); + projectParts.append(ProjectPart::create(p1->projectFilePath())); + const auto p2 = std::make_shared<Project>( + QString(), Utils::FilePath::fromString("p2.pro")); + projectMap.insert(p2->projectFilePath(), p2); + projectParts.append(ProjectPart::create(p2->projectFilePath())); + + return projectParts; + } + + static QList<ProjectPart::Ptr> createCAndCxxProjectParts() + { + QList<ProjectPart::Ptr> projectParts; + ToolChainInfo tcInfo; + + // Create project part for C + tcInfo.macroInspectionRunner = [](const QStringList &) { + return ToolChain::MacroInspectionReport{{}, Utils::LanguageVersion::C11}; + }; + const ProjectPart::Ptr cprojectpart = ProjectPart::create({}, {}, {}, {}, {}, {}, {}, + tcInfo); + projectParts.append(cprojectpart); + + // Create project part for CXX + tcInfo.macroInspectionRunner = [](const QStringList &) { + return ToolChain::MacroInspectionReport{{}, Utils::LanguageVersion::CXX98}; + }; + const ProjectPart::Ptr cxxprojectpart = ProjectPart::create({}, {}, {}, {}, {}, {}, {}, + tcInfo); + projectParts.append(cxxprojectpart); + + return projectParts; + } + + QString filePath; + ProjectPart::Ptr currentProjectPart = ProjectPart::create({}); + ProjectPartInfo currentProjectPartInfo{currentProjectPart, + {currentProjectPart}, + ProjectPartInfo::NoHint}; + QString preferredProjectPartId; + Utils::FilePath activeProject; + Language languagePreference = Language::Cxx; + bool projectsChanged = false; + ProjectPartChooser chooser; + + QList<ProjectPart::Ptr> projectPartsForFile; + QList<ProjectPart::Ptr> projectPartsFromDependenciesForFile; + ProjectPart::Ptr fallbackProjectPart; + + static QHash<Utils::FilePath, std::shared_ptr<Project>> projectMap; +}; + +QHash<Utils::FilePath, std::shared_ptr<Project>> +ProjectPartChooserTestHelper::projectMap; +} + +void ProjectPartChooserTest::testChooseManuallySet() +{ + ProjectPart::Ptr p1 = ProjectPart::create({}); + RawProjectPart rpp2; + rpp2.setProjectFileLocation("someId"); + ProjectPart::Ptr p2 = ProjectPart::create({}, rpp2); + ProjectPartChooserTestHelper t; + t.preferredProjectPartId = p2->projectFile; + t.projectPartsForFile += {p1, p2}; + + QCOMPARE(t.choose().projectPart, p2); +} + +void ProjectPartChooserTest::testIndicateManuallySet() +{ + ProjectPart::Ptr p1 = ProjectPart::create({}); + RawProjectPart rpp2; + rpp2.setProjectFileLocation("someId"); + ProjectPart::Ptr p2 = ProjectPart::create({}, rpp2); + ProjectPartChooserTestHelper t; + t.preferredProjectPartId = p2->projectFile; + t.projectPartsForFile += {p1, p2}; + + QVERIFY(t.choose().hints & ProjectPartInfo::IsPreferredMatch); +} + +void ProjectPartChooserTest::testIndicateManuallySetForFallbackToProjectPartFromDependencies() +{ + ProjectPart::Ptr p1 = ProjectPart::create({}); + RawProjectPart rpp2; + rpp2.setProjectFileLocation("someId"); + ProjectPart::Ptr p2 = ProjectPart::create({}, rpp2); + ProjectPartChooserTestHelper t; + t.preferredProjectPartId = p2->projectFile; + t.projectPartsFromDependenciesForFile += {p1, p2}; + + QVERIFY(t.choose().hints & ProjectPartInfo::IsPreferredMatch); +} + +void ProjectPartChooserTest::testDoNotIndicateNotManuallySet() +{ + QVERIFY(!(ProjectPartChooserTestHelper().choose().hints & ProjectPartInfo::IsPreferredMatch)); +} + +void ProjectPartChooserTest::testForMultipleChooseFromActiveProject() +{ + ProjectPartChooserTestHelper t; + const QList<ProjectPart::Ptr> projectParts = t.createProjectPartsWithDifferentProjects(); + const ProjectPart::Ptr secondProjectPart = projectParts.at(1); + t.projectPartsForFile += projectParts; + t.activeProject = secondProjectPart->topLevelProject; + + QCOMPARE(t.choose().projectPart, secondProjectPart); +} + +void ProjectPartChooserTest::testForMultiplePreferSelectedForBuilding() +{ + RawProjectPart rpp1; + rpp1.setSelectedForBuilding(false); + RawProjectPart rpp2; + rpp2.setSelectedForBuilding(true); + const ProjectPart::Ptr firstProjectPart = ProjectPart::create({}, rpp1); + const ProjectPart::Ptr secondProjectPart = ProjectPart::create({}, rpp2); + ProjectPartChooserTestHelper t; + t.projectPartsForFile += firstProjectPart; + t.projectPartsForFile += secondProjectPart; + + QCOMPARE(t.choose().projectPart, secondProjectPart); +} + +void ProjectPartChooserTest::testForMultipleFromDependenciesChooseFromActiveProject() +{ + ProjectPartChooserTestHelper t; + const QList<ProjectPart::Ptr> projectParts = t.createProjectPartsWithDifferentProjects(); + const ProjectPart::Ptr secondProjectPart = projectParts.at(1); + t.projectPartsFromDependenciesForFile += projectParts; + t.activeProject = secondProjectPart->topLevelProject; + + QCOMPARE(t.choose().projectPart, secondProjectPart); +} + +void ProjectPartChooserTest::testForMultipleCheckIfActiveProjectChanged() +{ + ProjectPartChooserTestHelper t; + const QList<ProjectPart::Ptr> projectParts = t.createProjectPartsWithDifferentProjects(); + const ProjectPart::Ptr firstProjectPart = projectParts.at(0); + const ProjectPart::Ptr secondProjectPart = projectParts.at(1); + t.projectPartsForFile += projectParts; + t.currentProjectPartInfo.projectPart = firstProjectPart; + t.activeProject = secondProjectPart->topLevelProject; + + QCOMPARE(t.choose().projectPart, secondProjectPart); +} + +void ProjectPartChooserTest::testForMultipleAndAmbigiousHeaderPreferCProjectPart() +{ + ProjectPartChooserTestHelper t; + t.languagePreference = Language::C; + t.projectPartsForFile = t.createCAndCxxProjectParts(); + const ProjectPart::Ptr cProjectPart = t.projectPartsForFile.at(0); + + QCOMPARE(t.choose().projectPart, cProjectPart); +} + +void ProjectPartChooserTest::testForMultipleAndAmbigiousHeaderPreferCxxProjectPart() +{ + ProjectPartChooserTestHelper t; + t.languagePreference = Language::Cxx; + t.projectPartsForFile = t.createCAndCxxProjectParts(); + const ProjectPart::Ptr cxxProjectPart = t.projectPartsForFile.at(1); + + QCOMPARE(t.choose().projectPart, cxxProjectPart); +} + +void ProjectPartChooserTest::testIndicateMultiple() +{ + const ProjectPart::Ptr p1 = ProjectPart::create({}); + const ProjectPart::Ptr p2 = ProjectPart::create({}); + ProjectPartChooserTestHelper t; + t.projectPartsForFile += {p1, p2}; + + QVERIFY(t.choose().hints & ProjectPartInfo::IsAmbiguousMatch); +} + +void ProjectPartChooserTest::testIndicateMultipleForFallbackToProjectPartFromDependencies() +{ + const ProjectPart::Ptr p1 = ProjectPart::create({}); + const ProjectPart::Ptr p2 = ProjectPart::create({}); + ProjectPartChooserTestHelper t; + t.projectPartsFromDependenciesForFile += {p1, p2}; + + QVERIFY(t.choose().hints & ProjectPartInfo::IsAmbiguousMatch); +} + +void ProjectPartChooserTest::testForMultipleChooseNewIfPreviousIsGone() +{ + const ProjectPart::Ptr newProjectPart = ProjectPart::create({}); + ProjectPartChooserTestHelper t; + t.projectPartsForFile += newProjectPart; + + QCOMPARE(t.choose().projectPart, newProjectPart); +} + +void ProjectPartChooserTest::testFallbackToProjectPartFromDependencies() +{ + const ProjectPart::Ptr fromDependencies = ProjectPart::create({}); + ProjectPartChooserTestHelper t; + t.projectPartsFromDependenciesForFile += fromDependencies; + + QCOMPARE(t.choose().projectPart, fromDependencies); +} + +void ProjectPartChooserTest::testFallbackToProjectPartFromModelManager() +{ + ProjectPartChooserTestHelper t; + t.fallbackProjectPart = ProjectPart::create({}); + + QCOMPARE(t.choose().projectPart, t.fallbackProjectPart); +} + +void ProjectPartChooserTest::testContinueUsingFallbackFromModelManagerIfProjectDoesNotChange() +{ + // ...without re-calculating the dependency table. + ProjectPartChooserTestHelper t; + t.fallbackProjectPart = ProjectPart::create({}); + t.currentProjectPartInfo.projectPart = t.fallbackProjectPart; + t.currentProjectPartInfo.hints |= ProjectPartInfo::IsFallbackMatch; + t.projectPartsFromDependenciesForFile += ProjectPart::create({}); + + QCOMPARE(t.choose().projectPart, t.fallbackProjectPart); +} + +void ProjectPartChooserTest::testStopUsingFallbackFromModelManagerIfProjectChanges1() +{ + ProjectPartChooserTestHelper t; + t.fallbackProjectPart = ProjectPart::create({}); + t.currentProjectPartInfo.projectPart = t.fallbackProjectPart; + t.currentProjectPartInfo.hints |= ProjectPartInfo::IsFallbackMatch; + const ProjectPart::Ptr addedProject = ProjectPart::create({}); + t.projectPartsForFile += addedProject; + + QCOMPARE(t.choose().projectPart, addedProject); +} + +void ProjectPartChooserTest::testStopUsingFallbackFromModelManagerIfProjectChanges2() +{ + ProjectPartChooserTestHelper t; + t.fallbackProjectPart = ProjectPart::create({}); + t.currentProjectPartInfo.projectPart = t.fallbackProjectPart; + t.currentProjectPartInfo.hints |= ProjectPartInfo::IsFallbackMatch; + const ProjectPart::Ptr addedProject = ProjectPart::create({}); + t.projectPartsFromDependenciesForFile += addedProject; + t.projectsChanged = true; + + QCOMPARE(t.choose().projectPart, addedProject); +} + +void ProjectPartChooserTest::testIndicateFallbacktoProjectPartFromModelManager() +{ + ProjectPartChooserTestHelper t; + t.fallbackProjectPart = ProjectPart::create({}); + + QVERIFY(t.choose().hints & ProjectPartInfo::IsFallbackMatch); +} + +void ProjectPartChooserTest::testIndicateFromDependencies() +{ + ProjectPartChooserTestHelper t; + t.projectPartsFromDependenciesForFile += ProjectPart::create({}); + + QVERIFY(t.choose().hints & ProjectPartInfo::IsFromDependenciesMatch); +} + +void ProjectPartChooserTest::testDoNotIndicateFromDependencies() +{ + ProjectPartChooserTestHelper t; + t.projectPartsForFile += ProjectPart::create({}); + + QVERIFY(!(t.choose().hints & ProjectPartInfo::IsFromDependenciesMatch)); +} + +namespace { +class TestToolchain : public ToolChain +{ +public: + TestToolchain() : ToolChain("dummy") {} + +private: + MacroInspectionRunner createMacroInspectionRunner() const override { return {}; } + Utils::LanguageExtensions languageExtensions(const QStringList &) const override { return {}; } + Utils::WarningFlags warningFlags(const QStringList &) const override { return {}; } + BuiltInHeaderPathsRunner createBuiltInHeaderPathsRunner( + const Utils::Environment &) const override { return {}; } + void addToEnvironment(Utils::Environment &) const override {} + Utils::FilePath makeCommand(const Utils::Environment &) const override { return {}; } + QList<Utils::OutputLineParser *> createOutputParsers() const override { return {}; } + std::unique_ptr<ToolChainConfigWidget> createConfigurationWidget() override + { + return {}; + }; +}; + +class ProjectInfoGeneratorTestHelper +{ +public: + ProjectInfoGeneratorTestHelper() + { + TestToolchain aToolChain; + projectUpdateInfo.cxxToolChainInfo = {&aToolChain, {}, {}}; + projectUpdateInfo.cToolChainInfo = {&aToolChain, {}, {}}; + } + + ProjectInfo::Ptr generate() + { + QFutureInterface<ProjectInfo::Ptr> fi; + + projectUpdateInfo.rawProjectParts += rawProjectPart; + ProjectInfoGenerator generator(fi, projectUpdateInfo); + + return generator.generate(); + } + + ProjectUpdateInfo projectUpdateInfo; + RawProjectPart rawProjectPart; +}; +} + +void ProjectInfoGeneratorTest::testCreateNoProjectPartsForEmptyFileList() +{ + ProjectInfoGeneratorTestHelper t; + const ProjectInfo::Ptr projectInfo = t.generate(); + + QVERIFY(projectInfo->projectParts().isEmpty()); +} + +void ProjectInfoGeneratorTest::testCreateSingleProjectPart() +{ + ProjectInfoGeneratorTestHelper t; + t.rawProjectPart.files = QStringList{ "foo.cpp", "foo.h"}; + const ProjectInfo::Ptr projectInfo = t.generate(); + + QCOMPARE(projectInfo->projectParts().size(), 1); +} + +void ProjectInfoGeneratorTest::testCreateMultipleProjectParts() +{ + ProjectInfoGeneratorTestHelper t; + t.rawProjectPart.files = QStringList{ "foo.cpp", "foo.h", "bar.c", "bar.h" }; + const ProjectInfo::Ptr projectInfo = t.generate(); + + QCOMPARE(projectInfo->projectParts().size(), 2); +} + +void ProjectInfoGeneratorTest::testProjectPartIndicatesObjectiveCExtensionsByDefault() +{ + ProjectInfoGeneratorTestHelper t; + t.rawProjectPart.files = QStringList{ "foo.mm" }; + const ProjectInfo::Ptr projectInfo = t.generate(); + QCOMPARE(projectInfo->projectParts().size(), 1); + + const ProjectPart &projectPart = *projectInfo->projectParts().at(0); + QVERIFY(projectPart.languageExtensions & Utils::LanguageExtension::ObjectiveC); +} + +void ProjectInfoGeneratorTest::testProjectPartHasLatestLanguageVersionByDefault() +{ + ProjectInfoGeneratorTestHelper t; + t.rawProjectPart.files = QStringList{ "foo.cpp" }; + const ProjectInfo::Ptr projectInfo = t.generate(); + QCOMPARE(projectInfo->projectParts().size(), 1); + + const ProjectPart &projectPart = *projectInfo->projectParts().at(0); + QCOMPARE(projectPart.languageVersion, Utils::LanguageVersion::LatestCxx); +} + +void ProjectInfoGeneratorTest::testUseMacroInspectionReportForLanguageVersion() +{ + ProjectInfoGeneratorTestHelper t; + t.projectUpdateInfo.cxxToolChainInfo.macroInspectionRunner = [](const QStringList &) { + return TestToolchain::MacroInspectionReport{Macros(), Utils::LanguageVersion::CXX17}; + }; + t.rawProjectPart.files = QStringList{ "foo.cpp" }; + const ProjectInfo::Ptr projectInfo = t.generate(); + + QCOMPARE(projectInfo->projectParts().size(), 1); + + const ProjectPart &projectPart = *projectInfo->projectParts().at(0); + QCOMPARE(projectPart.languageVersion, Utils::LanguageVersion::CXX17); +} + +void ProjectInfoGeneratorTest::testUseCompilerFlagsForLanguageExtensions() +{ + ProjectInfoGeneratorTestHelper t; + t.rawProjectPart.files = QStringList{ "foo.cpp" }; + t.rawProjectPart.flagsForCxx.languageExtensions = Utils::LanguageExtension::Microsoft; + const ProjectInfo::Ptr projectInfo = t.generate(); + + QCOMPARE(projectInfo->projectParts().size(), 1); + + const ProjectPart &projectPart = *projectInfo->projectParts().at(0); + QVERIFY(projectPart.languageExtensions & Utils::LanguageExtension::Microsoft); +} + +void ProjectInfoGeneratorTest::testProjectFileKindsMatchProjectPartVersion() +{ + ProjectInfoGeneratorTestHelper t; + t.rawProjectPart.files = QStringList{ "foo.h" }; + const ProjectInfo::Ptr projectInfo = t.generate(); + + QCOMPARE(projectInfo->projectParts().size(), 4); + QVERIFY(Utils::contains(projectInfo->projectParts(), [](const ProjectPart::Ptr &p) { + return p->languageVersion == Utils::LanguageVersion::LatestC + && p->files.first().kind == ProjectFile::CHeader; + })); + QVERIFY(Utils::contains(projectInfo->projectParts(), [](const ProjectPart::Ptr &p) { + return p->languageVersion == Utils::LanguageVersion::LatestC + && p->files.first().kind == ProjectFile::ObjCHeader; + })); + QVERIFY(Utils::contains(projectInfo->projectParts(), [](const ProjectPart::Ptr &p) { + return p->languageVersion == Utils::LanguageVersion::LatestCxx + && p->files.first().kind == ProjectFile::CXXHeader; + })); + QVERIFY(Utils::contains(projectInfo->projectParts(), [](const ProjectPart::Ptr &p) { + return p->languageVersion == Utils::LanguageVersion::LatestCxx + && p->files.first().kind == ProjectFile::ObjCXXHeader; + })); +} + +namespace { +class HeaderPathFilterTestHelper +{ +public: + const ProjectPart &finalize() + { + RawProjectPart rpp; + rpp.setHeaderPaths(headerPaths); + ToolChainInfo tcInfo; + tcInfo.type = toolchainType; + tcInfo.targetTriple = targetTriple; + tcInfo.installDir = toolchainInstallDir; + projectPart = ProjectPart::create({}, rpp, {}, {}, {}, {}, {}, tcInfo); + filter.emplace(HeaderPathFilter(*projectPart, UseTweakedHeaderPaths::No, {}, {}, + "/project", "/build")); + return *projectPart; + } + + static HeaderPath builtIn(const QString &path) + { + return HeaderPath{path, HeaderPathType::BuiltIn}; + } + static HeaderPath system(const QString &path) + { + return HeaderPath{path, HeaderPathType::System}; + } + static HeaderPath framework(const QString &path) + { + return HeaderPath{path, HeaderPathType::Framework}; + } + static HeaderPath user(const QString &path) + { + return HeaderPath{path, HeaderPathType::User}; + } + + QString targetTriple; + Utils::Id toolchainType; + Utils::FilePath toolchainInstallDir; + HeaderPaths headerPaths = { + HeaderPath{"", HeaderPathType::BuiltIn}, + HeaderPath{"/builtin_path", HeaderPathType::BuiltIn}, + HeaderPath{"/system_path", HeaderPathType::System}, + HeaderPath{"/framework_path", HeaderPathType::Framework}, + HeaderPath{"/outside_project_user_path", HeaderPathType::User}, + HeaderPath{"/build/user_path", HeaderPathType::User}, + HeaderPath{"/buildb/user_path", HeaderPathType::User}, + HeaderPath{"/projectb/user_path", HeaderPathType::User}, + HeaderPath{"/project/user_path", HeaderPathType::User}}; + + Utils::optional<HeaderPathFilter> filter; + +private: + ProjectPart::Ptr projectPart; +}; +} + +void HeaderPathFilterTest::testBuiltin() +{ + HeaderPathFilterTestHelper t; + t.finalize(); + t.filter->process(); + + QCOMPARE(t.filter->builtInHeaderPaths, (HeaderPaths{t.builtIn("/builtin_path")})); +} + +void HeaderPathFilterTest::testSystem() +{ + HeaderPathFilterTestHelper t; + t.finalize(); + t.filter->process(); + + QCOMPARE(t.filter->systemHeaderPaths, (HeaderPaths{ + t.system("/project/.pre_includes"), t.system("/system_path"), + t.framework("/framework_path"), t.user("/outside_project_user_path"), + t.user("/buildb/user_path"), t.user("/projectb/user_path")})); +} + +void HeaderPathFilterTest::testUser() +{ + HeaderPathFilterTestHelper t; + t.finalize(); + t.filter->process(); + + QCOMPARE(t.filter->userHeaderPaths, (HeaderPaths{t.user("/build/user_path"), + t.user("/project/user_path")})); +} + +void HeaderPathFilterTest::testNoProjectPathSet() +{ + HeaderPathFilterTestHelper t; + HeaderPathFilter filter{t.finalize(), UseTweakedHeaderPaths::No}; + filter.process(); + + QCOMPARE(filter.userHeaderPaths, (HeaderPaths{ + t.user("/outside_project_user_path"), t.user("/build/user_path"), + t.user("/buildb/user_path"), t.user("/projectb/user_path"), + t.user("/project/user_path")})); +} + +void HeaderPathFilterTest::testDontAddInvalidPath() +{ + HeaderPathFilterTestHelper t; + t.finalize(); + t.filter->process(); + QCOMPARE(t.filter->builtInHeaderPaths, (HeaderPaths{t.builtIn("/builtin_path")})); + QCOMPARE(t.filter->systemHeaderPaths, HeaderPaths({ + t.system("/project/.pre_includes"), t.system("/system_path"), + t.framework("/framework_path"), t.user("/outside_project_user_path"), + t.user("/buildb/user_path"), t.user("/projectb/user_path")})); + QCOMPARE(t.filter->userHeaderPaths, HeaderPaths({t.user("/build/user_path"), + t.user("/project/user_path")})); +} + +void HeaderPathFilterTest::testClangHeadersPath() +{ + HeaderPathFilterTestHelper t; + HeaderPathFilter filter(t.finalize(), UseTweakedHeaderPaths::Yes, "6.0", "clang_dir"); + filter.process(); + + QCOMPARE(filter.builtInHeaderPaths, (HeaderPaths{t.builtIn("clang_dir"), + t.builtIn("/builtin_path")})); +} + +void HeaderPathFilterTest::testClangHeadersPathWitoutClangVersion() +{ + HeaderPathFilterTestHelper t; + HeaderPathFilter filter(t.finalize(), UseTweakedHeaderPaths::Yes); + filter.process(); + + QCOMPARE(filter.builtInHeaderPaths, (HeaderPaths{t.builtIn("/builtin_path")})); +} + +void HeaderPathFilterTest::testClangHeadersAndCppIncludesPathsOrderMacOs() +{ + HeaderPathFilterTestHelper t; + t.targetTriple = "x86_64-apple-darwin10"; + const auto builtIns = { + t.builtIn("/usr/include/c++/4.2.1"), t.builtIn("/usr/include/c++/4.2.1/backward"), + t.builtIn("/usr/local/include"), + t.builtIn("/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/clang/6.0/include"), + t.builtIn("/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include"), + t.builtIn("/usr/include") + }; + std::copy(builtIns.begin(), builtIns.end(), + std::inserter(t.headerPaths, t.headerPaths.begin())); + HeaderPathFilter filter(t.finalize(), UseTweakedHeaderPaths::Yes, "6.0", "clang_dir"); + filter.process(); + + QCOMPARE(filter.builtInHeaderPaths, (HeaderPaths{ + t.builtIn("/usr/include/c++/4.2.1"), t.builtIn("/usr/include/c++/4.2.1/backward"), + t.builtIn("/usr/local/include"), t.builtIn("clang_dir"), + t.builtIn("/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include"), + t.builtIn("/usr/include"), + t.builtIn("/builtin_path")})); +} + +void HeaderPathFilterTest::testClangHeadersAndCppIncludesPathsOrderLinux() +{ + HeaderPathFilterTestHelper t; + t.targetTriple = "x86_64-linux-gnu"; + const auto builtIns = { + t.builtIn("/usr/include/c++/4.8"), t.builtIn("/usr/include/c++/4.8/backward"), + t.builtIn("/usr/include/x86_64-linux-gnu/c++/4.8"), + t.builtIn("/usr/local/include"), t.builtIn("/usr/lib/gcc/x86_64-linux-gnu/4.8/include"), + t.builtIn("/usr/include/x86_64-linux-gnu"), t.builtIn("/usr/include")}; + std::copy(builtIns.begin(), builtIns.end(), + std::inserter(t.headerPaths, t.headerPaths.begin())); + HeaderPathFilter filter(t.finalize(), UseTweakedHeaderPaths::Yes, "6.0", "clang_dir"); + filter.process(); + + QCOMPARE(filter.builtInHeaderPaths, (HeaderPaths{ + t.builtIn("/usr/include/c++/4.8"), t.builtIn("/usr/include/c++/4.8/backward"), + t.builtIn("/usr/include/x86_64-linux-gnu/c++/4.8"), t.builtIn("/usr/local/include"), + t.builtIn("clang_dir"), t.builtIn("/usr/lib/gcc/x86_64-linux-gnu/4.8/include"), + t.builtIn("/usr/include/x86_64-linux-gnu"), t.builtIn("/usr/include"), + t.builtIn("/builtin_path")})); +} + +// GCC-internal include paths like <installdir>/include and <installdir/include-next> might confuse +// clang and should be filtered out. clang on the command line filters them out, too. +void HeaderPathFilterTest::testRemoveGccInternalPaths() +{ + HeaderPathFilterTestHelper t; + t.toolchainInstallDir = Utils::FilePath::fromUtf8("/usr/lib/gcc/x86_64-linux-gnu/7"); + t.toolchainType = Constants::GCC_TOOLCHAIN_TYPEID; + t.headerPaths = { + t.builtIn("/usr/lib/gcc/x86_64-linux-gnu/7/include"), + t.builtIn("/usr/lib/gcc/x86_64-linux-gnu/7/include-fixed"), + }; + HeaderPathFilter filter(t.finalize(), UseTweakedHeaderPaths::Yes, "6.0", "clang_dir"); + filter.process(); + + QCOMPARE(filter.builtInHeaderPaths, (HeaderPaths{t.builtIn("clang_dir")})); +} + +// Some distributions ship the standard library headers in "<installdir>/include/c++" (MinGW) +// or e.g. "<installdir>/include/g++-v8" (Gentoo). +// Ensure that we do not remove include paths pointing there. +void HeaderPathFilterTest::testRemoveGccInternalPathsExceptForStandardPaths() +{ + HeaderPathFilterTestHelper t; + t.toolchainInstallDir = Utils::FilePath::fromUtf8("c:/mingw/lib/gcc/x86_64-w64-mingw32/7.3.0"); + t.toolchainType = Constants::MINGW_TOOLCHAIN_TYPEID; + t.headerPaths = { + t.builtIn("c:/mingw/lib/gcc/x86_64-w64-mingw32/7.3.0/include/c++"), + t.builtIn("c:/mingw/lib/gcc/x86_64-w64-mingw32/7.3.0/include/c++/x86_64-w64-mingw32"), + t.builtIn("c:/mingw/lib/gcc/x86_64-w64-mingw32/7.3.0/include/c++/backward"), + }; + + HeaderPaths expected = t.headerPaths; + expected.append(t.builtIn("clang_dir")); + HeaderPathFilter filter(t.finalize(), UseTweakedHeaderPaths::Yes, "6.0", "clang_dir"); + filter.process(); + + QCOMPARE(filter.builtInHeaderPaths, expected); +} + +void HeaderPathFilterTest::testClangHeadersAndCppIncludesPathsOrderNoVersion() +{ + HeaderPathFilterTestHelper t; + t.headerPaths = { + t.builtIn("C:/mingw/i686-w64-mingw32/include"), + t.builtIn("C:/mingw/i686-w64-mingw32/include/c++"), + t.builtIn("C:/mingw/i686-w64-mingw32/include/c++/i686-w64-mingw32"), + t.builtIn("C:/mingw/i686-w64-mingw32/include/c++/backward"), + }; + t.targetTriple = "x86_64-w64-windows-gnu"; + HeaderPathFilter filter(t.finalize(), UseTweakedHeaderPaths::Yes, "6.0", "clang_dir"); + filter.process(); + + QCOMPARE(filter.builtInHeaderPaths, (HeaderPaths{ + t.builtIn("C:/mingw/i686-w64-mingw32/include/c++"), + t.builtIn("C:/mingw/i686-w64-mingw32/include/c++/i686-w64-mingw32"), + t.builtIn("C:/mingw/i686-w64-mingw32/include/c++/backward"), + t.builtIn("clang_dir"), + t.builtIn("C:/mingw/i686-w64-mingw32/include")})); +} + +void HeaderPathFilterTest::testClangHeadersAndCppIncludesPathsOrderAndroidClang() +{ + HeaderPathFilterTestHelper t; + t.headerPaths = { + t.builtIn("C:/Android/sdk/ndk-bundle/sysroot/usr/include/i686-linux-android"), + t.builtIn("C:/Android/sdk/ndk-bundle/sources/cxx-stl/llvm-libc++/include"), + t.builtIn("C:/Android/sdk/ndk-bundle/sources/android/support/include"), + t.builtIn("C:/Android/sdk/ndk-bundle/sources/cxx-stl/llvm-libc++abi/include"), + t.builtIn("C:/Android/sdk/ndk-bundle/sysroot/usr/include") + }; + t.targetTriple = "i686-linux-android"; + HeaderPathFilter filter(t.finalize(), UseTweakedHeaderPaths::Yes, "6.0", "clang_dir"); + filter.process(); + + QCOMPARE(filter.builtInHeaderPaths, (HeaderPaths{ + t.builtIn("C:/Android/sdk/ndk-bundle/sources/cxx-stl/llvm-libc++/include"), + t.builtIn("C:/Android/sdk/ndk-bundle/sources/cxx-stl/llvm-libc++abi/include"), + t.builtIn("clang_dir"), + t.builtIn("C:/Android/sdk/ndk-bundle/sysroot/usr/include/i686-linux-android"), + t.builtIn("C:/Android/sdk/ndk-bundle/sources/android/support/include"), + t.builtIn("C:/Android/sdk/ndk-bundle/sysroot/usr/include")})); +} + +void ProjectFileCategorizerTest::testC() +{ + const ProjectFileCategorizer categorizer({}, {"foo.c", "foo.h"}); + const ProjectFiles expected { + ProjectFile("foo.c", ProjectFile::CSource), + ProjectFile("foo.h", ProjectFile::CHeader), + }; + + QCOMPARE(categorizer.cSources(), expected); + QVERIFY(categorizer.cxxSources().isEmpty()); + QVERIFY(categorizer.objcSources().isEmpty()); + QVERIFY(categorizer.objcxxSources().isEmpty()); +} + +void ProjectFileCategorizerTest::testCxxWithUnambiguousHeaderSuffix() +{ + const ProjectFileCategorizer categorizer({}, {"foo.cpp", "foo.hpp"}); + const ProjectFiles expected { + ProjectFile("foo.cpp", ProjectFile::CXXSource), + ProjectFile("foo.hpp", ProjectFile::CXXHeader), + }; + + QCOMPARE(categorizer.cxxSources(), expected); + QVERIFY(categorizer.cSources().isEmpty()); + QVERIFY(categorizer.objcSources().isEmpty()); + QVERIFY(categorizer.objcxxSources().isEmpty()); +} + +void ProjectFileCategorizerTest::testCxxWithAmbiguousHeaderSuffix() +{ + const ProjectFiles expected { + ProjectFile("foo.cpp", ProjectFile::CXXSource), + ProjectFile("foo.h", ProjectFile::CXXHeader), + }; + + const ProjectFileCategorizer categorizer({}, {"foo.cpp", "foo.h"}); + + QCOMPARE(categorizer.cxxSources(), expected); + QVERIFY(categorizer.cSources().isEmpty()); + QVERIFY(categorizer.objcSources().isEmpty()); + QVERIFY(categorizer.objcxxSources().isEmpty()); +} + +void ProjectFileCategorizerTest::testObjectiveC() +{ + const ProjectFiles expected { + ProjectFile("foo.m", ProjectFile::ObjCSource), + ProjectFile("foo.h", ProjectFile::ObjCHeader), + }; + + const ProjectFileCategorizer categorizer({}, {"foo.m", "foo.h"}); + + QCOMPARE(categorizer.objcSources(), expected); + QVERIFY(categorizer.cxxSources().isEmpty()); + QVERIFY(categorizer.cSources().isEmpty()); + QVERIFY(categorizer.objcxxSources().isEmpty()); +} + +void ProjectFileCategorizerTest::testObjectiveCxx() +{ + const ProjectFiles expected { + ProjectFile("foo.mm", ProjectFile::ObjCXXSource), + ProjectFile("foo.h", ProjectFile::ObjCXXHeader), + }; + + const ProjectFileCategorizer categorizer({}, {"foo.mm", "foo.h"}); + + QCOMPARE(categorizer.objcxxSources(), expected); + QVERIFY(categorizer.objcSources().isEmpty()); + QVERIFY(categorizer.cSources().isEmpty()); + QVERIFY(categorizer.cxxSources().isEmpty()); +} + +void ProjectFileCategorizerTest::testMixedCAndCxx() +{ + const ProjectFiles expectedCxxSources { + ProjectFile("foo.cpp", ProjectFile::CXXSource), + ProjectFile("foo.h", ProjectFile::CXXHeader), + ProjectFile("bar.h", ProjectFile::CXXHeader), + }; + const ProjectFiles expectedCSources { + ProjectFile("bar.c", ProjectFile::CSource), + ProjectFile("foo.h", ProjectFile::CHeader), + ProjectFile("bar.h", ProjectFile::CHeader), + }; + + const ProjectFileCategorizer categorizer({}, {"foo.cpp", "foo.h", "bar.c", "bar.h"}); + + QCOMPARE(categorizer.cxxSources(), expectedCxxSources); + QCOMPARE(categorizer.cSources(), expectedCSources); + QVERIFY(categorizer.objcSources().isEmpty()); + QVERIFY(categorizer.objcxxSources().isEmpty()); +} + +void ProjectFileCategorizerTest::testAmbiguousHeaderOnly() +{ + const ProjectFileCategorizer categorizer({}, {"foo.h"}); + + QCOMPARE(categorizer.cSources(), {ProjectFile("foo.h", ProjectFile::CHeader)}); + QCOMPARE(categorizer.cxxSources(), {ProjectFile("foo.h", ProjectFile::CXXHeader)}); + QCOMPARE(categorizer.objcSources(), {ProjectFile("foo.h", ProjectFile::ObjCHeader)}); + QCOMPARE(categorizer.objcxxSources(), {ProjectFile("foo.h", ProjectFile::ObjCXXHeader)}); +} + +} // namespace Internal +} // namespace CppTools diff --git a/src/plugins/cpptools/projectinfo_test.h b/src/plugins/cpptools/projectinfo_test.h new file mode 100644 index 0000000000..cc32d513a8 --- /dev/null +++ b/src/plugins/cpptools/projectinfo_test.h @@ -0,0 +1,109 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#pragma once + +#include <QObject> + +namespace CppTools::Internal { + +class ProjectPartChooserTest : public QObject +{ + Q_OBJECT + +private slots: + void testChooseManuallySet(); + void testIndicateManuallySet(); + void testIndicateManuallySetForFallbackToProjectPartFromDependencies(); + void testDoNotIndicateNotManuallySet(); + void testForMultipleChooseFromActiveProject(); + void testForMultiplePreferSelectedForBuilding(); + void testForMultipleFromDependenciesChooseFromActiveProject(); + void testForMultipleCheckIfActiveProjectChanged(); + void testForMultipleAndAmbigiousHeaderPreferCProjectPart(); + void testForMultipleAndAmbigiousHeaderPreferCxxProjectPart(); + void testIndicateMultiple(); + void testIndicateMultipleForFallbackToProjectPartFromDependencies(); + void testForMultipleChooseNewIfPreviousIsGone(); + void testFallbackToProjectPartFromDependencies(); + void testFallbackToProjectPartFromModelManager(); + void testContinueUsingFallbackFromModelManagerIfProjectDoesNotChange(); + void testStopUsingFallbackFromModelManagerIfProjectChanges1(); + void testStopUsingFallbackFromModelManagerIfProjectChanges2(); + void testIndicateFallbacktoProjectPartFromModelManager(); + void testIndicateFromDependencies(); + void testDoNotIndicateFromDependencies(); +}; + +class ProjectInfoGeneratorTest : public QObject +{ + Q_OBJECT + +private slots: + void testCreateNoProjectPartsForEmptyFileList(); + void testCreateSingleProjectPart(); + void testCreateMultipleProjectParts(); + void testProjectPartIndicatesObjectiveCExtensionsByDefault(); + void testProjectPartHasLatestLanguageVersionByDefault(); + void testUseMacroInspectionReportForLanguageVersion(); + void testUseCompilerFlagsForLanguageExtensions(); + void testProjectFileKindsMatchProjectPartVersion(); +}; + +class HeaderPathFilterTest : public QObject +{ + Q_OBJECT + +private slots: + void testBuiltin(); + void testSystem(); + void testUser(); + void testNoProjectPathSet(); + void testDontAddInvalidPath(); + void testClangHeadersPath(); + void testClangHeadersPathWitoutClangVersion(); + void testClangHeadersAndCppIncludesPathsOrderMacOs(); + void testClangHeadersAndCppIncludesPathsOrderLinux(); + void testRemoveGccInternalPaths(); + void testRemoveGccInternalPathsExceptForStandardPaths(); + void testClangHeadersAndCppIncludesPathsOrderNoVersion(); + void testClangHeadersAndCppIncludesPathsOrderAndroidClang(); +}; + +class ProjectFileCategorizerTest : public QObject +{ + Q_OBJECT + +private slots: + void testC(); + void testCxxWithUnambiguousHeaderSuffix(); + void testCxxWithAmbiguousHeaderSuffix(); + void testObjectiveC(); + void testObjectiveCxx(); + void testMixedCAndCxx(); + void testAmbiguousHeaderOnly(); +}; + +} // namespace CppTools::Internal diff --git a/src/plugins/cpptools/projectpart.cpp b/src/plugins/cpptools/projectpart.cpp index d3d4668a81..8286412da5 100644 --- a/src/plugins/cpptools/projectpart.cpp +++ b/src/plugins/cpptools/projectpart.cpp @@ -25,6 +25,8 @@ #include "projectpart.h" +#include <projectexplorer/project.h> + #include <utils/algorithm.h> #include <QFile> @@ -35,66 +37,6 @@ using namespace ProjectExplorer; namespace CppTools { -void ProjectPart::updateLanguageFeatures() -{ - const bool hasCxx = languageVersion >= Utils::LanguageVersion::CXX98; - const bool hasQt = hasCxx && qtVersion != Utils::QtVersion::None; - languageFeatures.cxx11Enabled = languageVersion >= Utils::LanguageVersion::CXX11; - languageFeatures.cxx14Enabled = languageVersion >= Utils::LanguageVersion::CXX14; - languageFeatures.cxxEnabled = hasCxx; - languageFeatures.c99Enabled = languageVersion >= Utils::LanguageVersion::C99; - languageFeatures.objCEnabled = languageExtensions.testFlag(Utils::LanguageExtension::ObjectiveC); - languageFeatures.qtEnabled = hasQt; - languageFeatures.qtMocRunEnabled = hasQt; - if (!hasQt) { - languageFeatures.qtKeywordsEnabled = false; - } else { - languageFeatures.qtKeywordsEnabled = !Utils::contains( - projectMacros, - [] (const ProjectExplorer::Macro ¯o) { return macro.key == "QT_NO_KEYWORDS"; }); - } -} - -void ProjectPart::setupToolchainProperties(const ToolChainInfo &tcInfo, const QStringList &flags) -{ - toolchainType = tcInfo.type; - isMsvc2015Toolchain = tcInfo.isMsvc2015ToolChain; - toolChainWordWidth = tcInfo.wordWidth == 64 ? ProjectPart::WordWidth64Bit - : ProjectPart::WordWidth32Bit; - toolChainInstallDir = tcInfo.installDir; - toolChainTargetTriple = tcInfo.targetTriple; - extraCodeModelFlags = tcInfo.extraCodeModelFlags; - compilerFlags = flags; - - // Toolchain macros and language version - if (tcInfo.macroInspectionRunner) { - const auto macroInspectionReport = tcInfo.macroInspectionRunner(compilerFlags); - toolChainMacros = macroInspectionReport.macros; - languageVersion = macroInspectionReport.languageVersion; - // No compiler set in kit. - } else if (language == Utils::Language::C) { - languageVersion = Utils::LanguageVersion::LatestC; - } else { - languageVersion = Utils::LanguageVersion::LatestCxx; - } - - // Header paths - if (tcInfo.headerPathsRunner) { - const HeaderPaths builtInHeaderPaths - = tcInfo.headerPathsRunner(compilerFlags, tcInfo.sysRootPath, tcInfo.targetTriple); - for (const HeaderPath &header : builtInHeaderPaths) { - const HeaderPath headerPath{header.path, header.type}; - if (!headerPaths.contains(headerPath)) - headerPaths.push_back(headerPath); - } - } -} - -ProjectPart::Ptr ProjectPart::copy() const -{ - return Ptr(new ProjectPart(*this)); -} - QString ProjectPart::id() const { QString projectPartId = projectFileLocation(); @@ -113,11 +55,16 @@ QString ProjectPart::projectFileLocation() const return location; } -QByteArray ProjectPart::readProjectConfigFile(const Ptr &projectPart) +bool ProjectPart::belongsToProject(const ProjectExplorer::Project *project) const +{ + return project && topLevelProject == project->projectFilePath(); +} + +QByteArray ProjectPart::readProjectConfigFile(const QString &projectConfigFile) { QByteArray result; - QFile f(projectPart->projectConfigFile); + QFile f(projectConfigFile); if (f.open(QIODevice::ReadOnly)) { QTextStream is(&f); result = is.readAll().toUtf8(); @@ -127,4 +74,116 @@ QByteArray ProjectPart::readProjectConfigFile(const Ptr &projectPart) return result; } +// TODO: Why do we keep the file *and* the resulting macros? Why do we read the file +// in several places? +static Macros getProjectMacros(const RawProjectPart &rpp) +{ + Macros macros = rpp.projectMacros; + if (!rpp.projectConfigFile.isEmpty()) + macros += Macro::toMacros(ProjectPart::readProjectConfigFile(rpp.projectConfigFile)); + return macros; +} + +static HeaderPaths getHeaderPaths(const RawProjectPart &rpp, + const RawProjectPartFlags &flags, + const ProjectExplorer::ToolChainInfo &tcInfo) +{ + HeaderPaths headerPaths; + + // Prevent duplicate include paths. + // TODO: Do this once when finalizing the raw project part? + std::set<QString> seenPaths; + for (const HeaderPath &p : qAsConst(rpp.headerPaths)) { + const QString cleanPath = QDir::cleanPath(p.path); + if (seenPaths.insert(cleanPath).second) + headerPaths << HeaderPath(cleanPath, p.type); + } + + if (tcInfo.headerPathsRunner) { + const HeaderPaths builtInHeaderPaths = tcInfo.headerPathsRunner( + flags.commandLineFlags, tcInfo.sysRootPath, tcInfo.targetTriple); + for (const HeaderPath &header : builtInHeaderPaths) { + if (seenPaths.insert(header.path).second) + headerPaths.push_back(HeaderPath{header.path, header.type}); + } + } + return headerPaths; +} + +static ToolChain::MacroInspectionReport getToolchainMacros( + const RawProjectPartFlags &flags, const ToolChainInfo &tcInfo, Utils::Language language) +{ + ToolChain::MacroInspectionReport report; + if (tcInfo.macroInspectionRunner) { + report = tcInfo.macroInspectionRunner(flags.commandLineFlags); + } else if (language == Utils::Language::C) { // No compiler set in kit. + report.languageVersion = Utils::LanguageVersion::LatestC; + } else { + report.languageVersion = Utils::LanguageVersion::LatestCxx; + } + return report; +} + +ProjectPart::ProjectPart(const Utils::FilePath &topLevelProject, + const RawProjectPart &rpp, + const QString &displayName, + const ProjectFiles &files, + Utils::Language language, + Utils::LanguageExtensions languageExtensions, + const RawProjectPartFlags &flags, + const ToolChainInfo &tcInfo) + : topLevelProject(topLevelProject), + displayName(displayName), + projectFile(rpp.projectFile), + projectConfigFile(rpp.projectConfigFile), + projectFileLine(rpp.projectFileLine), + projectFileColumn(rpp.projectFileColumn), + callGroupId(rpp.callGroupId), + language(language), + languageExtensions(languageExtensions | flags.languageExtensions), + qtVersion(rpp.qtVersion), + files(files), + includedFiles(rpp.includedFiles), + precompiledHeaders(rpp.precompiledHeaders), + headerPaths(getHeaderPaths(rpp, flags, tcInfo)), + projectMacros(getProjectMacros(rpp)), + buildSystemTarget(rpp.buildSystemTarget), + buildTargetType(rpp.buildTargetType), + selectedForBuilding(rpp.selectedForBuilding), + toolchainType(tcInfo.type), + isMsvc2015Toolchain(tcInfo.isMsvc2015ToolChain), + toolChainTargetTriple(tcInfo.targetTriple), + toolChainWordWidth(tcInfo.wordWidth == 64 ? ProjectPart::WordWidth64Bit + : ProjectPart::WordWidth32Bit), + toolChainInstallDir(tcInfo.installDir), + compilerFilePath(tcInfo.compilerFilePath), + warningFlags(flags.warningFlags), + extraCodeModelFlags(tcInfo.extraCodeModelFlags), + compilerFlags(flags.commandLineFlags), + m_macroReport(getToolchainMacros(flags, tcInfo, language)), + languageFeatures(deriveLanguageFeatures()) +{ +} + +CPlusPlus::LanguageFeatures ProjectPart::deriveLanguageFeatures() const +{ + const bool hasCxx = languageVersion >= Utils::LanguageVersion::CXX98; + const bool hasQt = hasCxx && qtVersion != Utils::QtVersion::None; + CPlusPlus::LanguageFeatures features; + features.cxx11Enabled = languageVersion >= Utils::LanguageVersion::CXX11; + features.cxx14Enabled = languageVersion >= Utils::LanguageVersion::CXX14; + features.cxxEnabled = hasCxx; + features.c99Enabled = languageVersion >= Utils::LanguageVersion::C99; + features.objCEnabled = languageExtensions.testFlag(Utils::LanguageExtension::ObjectiveC); + features.qtEnabled = hasQt; + features.qtMocRunEnabled = hasQt; + if (!hasQt) { + features.qtKeywordsEnabled = false; + } else { + features.qtKeywordsEnabled = !Utils::contains(projectMacros, + [] (const ProjectExplorer::Macro ¯o) { return macro.key == "QT_NO_KEYWORDS"; }); + } + return features; +} + } // namespace CppTools diff --git a/src/plugins/cpptools/projectpart.h b/src/plugins/cpptools/projectpart.h index 010a21a312..4f1cee28aa 100644 --- a/src/plugins/cpptools/projectpart.h +++ b/src/plugins/cpptools/projectpart.h @@ -37,15 +37,13 @@ #include <cplusplus/Token.h> #include <utils/cpplanguage_details.h> -#include <utils/fileutils.h> +#include <utils/filepath.h> #include <utils/id.h> #include <QString> #include <QSharedPointer> -namespace ProjectExplorer { -class Project; -} +namespace ProjectExplorer { class Project; } namespace CppTools { @@ -60,60 +58,88 @@ public: using Ptr = QSharedPointer<ProjectPart>; public: + static Ptr create(const Utils::FilePath &topLevelProject, + const ProjectExplorer::RawProjectPart &rpp = {}, + const QString &displayName = {}, + const ProjectFiles &files = {}, + Utils::Language language = Utils::Language::Cxx, + Utils::LanguageExtensions languageExtensions = {}, + const ProjectExplorer::RawProjectPartFlags &flags = {}, + const ProjectExplorer::ToolChainInfo &tcInfo = {}) + { + return Ptr(new ProjectPart(topLevelProject, rpp, displayName, files, language, + languageExtensions, flags, tcInfo)); + } + QString id() const; QString projectFileLocation() const; + bool hasProject() const { return !topLevelProject.isEmpty(); } + bool belongsToProject(const ProjectExplorer::Project *project) const; - Ptr copy() const; - void updateLanguageFeatures(); - void setupToolchainProperties(const ProjectExplorer::ToolChainInfo &tcInfo, - const QStringList &flags); - - static QByteArray readProjectConfigFile(const Ptr &projectPart); + static QByteArray readProjectConfigFile(const QString &projectConfigFile); public: - ProjectExplorer::Project *project = nullptr; + const Utils::FilePath topLevelProject; + const QString displayName; + const QString projectFile; + const QString projectConfigFile; // Generic Project Manager only - QString displayName; - - QString projectFile; - int projectFileLine = -1; - int projectFileColumn = -1; - QString callGroupId; + const int projectFileLine = -1; + const int projectFileColumn = -1; + const QString callGroupId; // Versions, features and extensions - ::Utils::Language language = ::Utils::Language::Cxx; - ::Utils::LanguageVersion languageVersion = ::Utils::LanguageVersion::LatestCxx; - ::Utils::LanguageExtensions languageExtensions = ::Utils::LanguageExtension::None; - CPlusPlus::LanguageFeatures languageFeatures; - ::Utils::QtVersion qtVersion = ::Utils::QtVersion::Unknown; + const Utils::Language language = Utils::Language::Cxx; + const Utils::LanguageVersion &languageVersion = m_macroReport.languageVersion; + const Utils::LanguageExtensions languageExtensions = Utils::LanguageExtension::None; + const Utils::QtVersion qtVersion = Utils::QtVersion::Unknown; // Files - ProjectFiles files; - QStringList includedFiles; - QStringList precompiledHeaders; - ProjectExplorer::HeaderPaths headerPaths; - QString projectConfigFile; // Generic Project Manager only + const ProjectFiles files; + const QStringList includedFiles; + const QStringList precompiledHeaders; + const ProjectExplorer::HeaderPaths headerPaths; // Macros - ProjectExplorer::Macros projectMacros; - ProjectExplorer::Macros toolChainMacros; + const ProjectExplorer::Macros projectMacros; + const ProjectExplorer::Macros &toolChainMacros = m_macroReport.macros; // Build system - QString buildSystemTarget; - ProjectExplorer::BuildTargetType buildTargetType = ProjectExplorer::BuildTargetType::Unknown; - bool selectedForBuilding = true; + const QString buildSystemTarget; + const ProjectExplorer::BuildTargetType buildTargetType + = ProjectExplorer::BuildTargetType::Unknown; + const bool selectedForBuilding = true; // ToolChain - Utils::Id toolchainType; - bool isMsvc2015Toolchain = false; - QString toolChainTargetTriple; - ToolChainWordWidth toolChainWordWidth = WordWidth32Bit; - ::Utils::FilePath toolChainInstallDir; - ::Utils::WarningFlags warningFlags = ::Utils::WarningFlags::Default; + const Utils::Id toolchainType; + const bool isMsvc2015Toolchain = false; + const QString toolChainTargetTriple; + const ToolChainWordWidth toolChainWordWidth = WordWidth32Bit; + const Utils::FilePath toolChainInstallDir; + const Utils::FilePath compilerFilePath; + const Utils::WarningFlags warningFlags = Utils::WarningFlags::Default; // Misc - QStringList extraCodeModelFlags; - QStringList compilerFlags; + const QStringList extraCodeModelFlags; + const QStringList compilerFlags; + +private: + ProjectPart(const Utils::FilePath &topLevelProject, + const ProjectExplorer::RawProjectPart &rpp, + const QString &displayName, + const ProjectFiles &files, + Utils::Language language, + Utils::LanguageExtensions languageExtensions, + const ProjectExplorer::RawProjectPartFlags &flags, + const ProjectExplorer::ToolChainInfo &tcInfo); + + CPlusPlus::LanguageFeatures deriveLanguageFeatures() const; + + const ProjectExplorer::ToolChain::MacroInspectionReport m_macroReport; + +public: + // Must come last due to initialization order. + const CPlusPlus::LanguageFeatures languageFeatures; }; } // namespace CppTools diff --git a/src/plugins/cpptools/senddocumenttracker.cpp b/src/plugins/cpptools/senddocumenttracker.cpp index 619b3eeaea..d838424c52 100644 --- a/src/plugins/cpptools/senddocumenttracker.cpp +++ b/src/plugins/cpptools/senddocumenttracker.cpp @@ -27,6 +27,11 @@ #include <algorithm> +#ifdef WITH_TESTS +#include "cpptoolsplugin.h" +#include <QtTest> +#endif + namespace CppTools { void SendDocumentTracker::setLastSentRevision(int revision) @@ -81,4 +86,174 @@ bool SendDocumentTracker::changedBeforeCompletionPosition(int newCompletionPosit return m_contentChangeStartPosition < newCompletionPosition; } +#ifdef WITH_TESTS +namespace Internal { + +void DocumentTrackerTest::testDefaultLastSentRevision() +{ + SendDocumentTracker tracker; + + QCOMPARE(tracker.lastSentRevision(), -1); + QCOMPARE(tracker.lastCompletionPosition(), -1); +} + +void DocumentTrackerTest::testSetRevision() +{ + SendDocumentTracker tracker; + tracker.setLastSentRevision(46); + + QCOMPARE(tracker.lastSentRevision(), 46); + QCOMPARE(tracker.lastCompletionPosition(), -1); +} + +void DocumentTrackerTest::testSetLastCompletionPosition() +{ + SendDocumentTracker tracker; + tracker.setLastCompletionPosition(33); + + QCOMPARE(tracker.lastSentRevision(), -1); + QCOMPARE(tracker.lastCompletionPosition(), 33); +} + +void DocumentTrackerTest::testApplyContentChange() +{ + SendDocumentTracker tracker; + tracker.setLastSentRevision(46); + tracker.setLastCompletionPosition(33); + tracker.applyContentChange(10); + + QCOMPARE(tracker.lastSentRevision(), 46); + QCOMPARE(tracker.lastCompletionPosition(), -1); +} + +void DocumentTrackerTest::testDontSendCompletionIfPositionIsEqual() +{ + SendDocumentTracker tracker; + tracker.setLastCompletionPosition(33); + + QVERIFY(!tracker.shouldSendCompletion(33)); +} + +void DocumentTrackerTest::testSendCompletionIfPositionIsDifferent() +{ + SendDocumentTracker tracker; + tracker.setLastSentRevision(46); + tracker.setLastCompletionPosition(33); + + QVERIFY(tracker.shouldSendCompletion(22)); +} + +void DocumentTrackerTest::testSendCompletionIfChangeIsBeforeCompletionPositionAndPositionIsEqual() +{ + SendDocumentTracker tracker; + tracker.setLastSentRevision(46); + tracker.setLastCompletionPosition(33); + tracker.applyContentChange(10); + + QVERIFY(tracker.shouldSendCompletion(33)); +} + +void DocumentTrackerTest::testDontSendCompletionIfChangeIsAfterCompletionPositionAndPositionIsEqual() +{ + SendDocumentTracker tracker; + tracker.setLastSentRevision(46); + tracker.setLastCompletionPosition(33); + tracker.applyContentChange(40); + + QVERIFY(!tracker.shouldSendCompletion(33)); +} + +void DocumentTrackerTest::testDontSendRevisionIfRevisionIsEqual() +{ + SendDocumentTracker tracker; + tracker.setLastSentRevision(46); + + QVERIFY(!tracker.shouldSendRevision(46)); +} + +void DocumentTrackerTest::testSendRevisionIfRevisionIsDifferent() +{ + SendDocumentTracker tracker; + tracker.setLastSentRevision(46); + + QVERIFY(tracker.shouldSendRevision(21)); +} + +void DocumentTrackerTest::testDontSendRevisionWithDefaults() +{ + SendDocumentTracker tracker; + QVERIFY(!tracker.shouldSendRevisionWithCompletionPosition(21, 33)); +} + +void DocumentTrackerTest::testDontSendIfRevisionIsDifferentAndCompletionPositionIsEqualAndNoContentChange() +{ + SendDocumentTracker tracker; + tracker.setLastSentRevision(46); + tracker.setLastCompletionPosition(33); + + QVERIFY(!tracker.shouldSendRevisionWithCompletionPosition(21, 33)); +} + +void DocumentTrackerTest::testDontSendIfRevisionIsDifferentAndCompletionPositionIsDifferentAndNoContentChange() +{ + SendDocumentTracker tracker; + tracker.setLastSentRevision(46); + tracker.setLastCompletionPosition(33); + + QVERIFY(!tracker.shouldSendRevisionWithCompletionPosition(21, 44)); +} + +void DocumentTrackerTest::testDontSendIfRevisionIsEqualAndCompletionPositionIsDifferentAndNoContentChange() +{ + SendDocumentTracker tracker; + tracker.setLastSentRevision(46); + tracker.setLastCompletionPosition(33); + + QVERIFY(!tracker.shouldSendRevisionWithCompletionPosition(46,44)); +} + +void DocumentTrackerTest::testSendIfChangeIsBeforeCompletionAndPositionIsEqualAndRevisionIsDifferent() +{ + SendDocumentTracker tracker; + tracker.setLastSentRevision(46); + tracker.setLastCompletionPosition(33); + tracker.applyContentChange(10); + + QVERIFY(tracker.shouldSendRevisionWithCompletionPosition(45, 33)); +} + +void DocumentTrackerTest::testDontSendIfChangeIsAfterCompletionPositionAndRevisionIsDifferent() +{ + SendDocumentTracker tracker; + tracker.setLastSentRevision(46); + tracker.setLastCompletionPosition(50); + tracker.applyContentChange(40); + + QVERIFY(!tracker.shouldSendRevisionWithCompletionPosition(45, 36)); +} + +void DocumentTrackerTest::testSendIfChangeIsBeforeCompletionPositionAndRevisionIsDifferent() +{ + SendDocumentTracker tracker; + tracker.setLastSentRevision(46); + tracker.setLastCompletionPosition(50); + tracker.applyContentChange(30); + + QVERIFY(tracker.shouldSendRevisionWithCompletionPosition(45, 36)); +} + +void DocumentTrackerTest::testResetChangedContentStartPositionIfLastRevisionIsSet() +{ + SendDocumentTracker tracker; + tracker.setLastSentRevision(46); + tracker.setLastCompletionPosition(50); + tracker.applyContentChange(30); + tracker.setLastSentRevision(47); + + QVERIFY(!tracker.shouldSendRevisionWithCompletionPosition(45, 36)); +} + +} // namespace Internal +#endif + } // namespace CppTools diff --git a/src/plugins/cpptools/senddocumenttracker.h b/src/plugins/cpptools/senddocumenttracker.h index be84a8b16a..100b07e62f 100644 --- a/src/plugins/cpptools/senddocumenttracker.h +++ b/src/plugins/cpptools/senddocumenttracker.h @@ -27,6 +27,8 @@ #include "cpptools_global.h" +#include <QObject> + #include <limits> namespace CppTools { @@ -55,4 +57,33 @@ private: int m_contentChangeStartPosition = std::numeric_limits<int>::max(); }; +#ifdef WITH_TESTS +namespace Internal { +class DocumentTrackerTest : public QObject +{ + Q_OBJECT + +private slots: + void testDefaultLastSentRevision(); + void testSetRevision(); + void testSetLastCompletionPosition(); + void testApplyContentChange(); + void testDontSendCompletionIfPositionIsEqual(); + void testSendCompletionIfPositionIsDifferent(); + void testSendCompletionIfChangeIsBeforeCompletionPositionAndPositionIsEqual(); + void testDontSendCompletionIfChangeIsAfterCompletionPositionAndPositionIsEqual(); + void testDontSendRevisionIfRevisionIsEqual(); + void testSendRevisionIfRevisionIsDifferent(); + void testDontSendRevisionWithDefaults(); + void testDontSendIfRevisionIsDifferentAndCompletionPositionIsEqualAndNoContentChange(); + void testDontSendIfRevisionIsDifferentAndCompletionPositionIsDifferentAndNoContentChange(); + void testDontSendIfRevisionIsEqualAndCompletionPositionIsDifferentAndNoContentChange(); + void testSendIfChangeIsBeforeCompletionAndPositionIsEqualAndRevisionIsDifferent(); + void testDontSendIfChangeIsAfterCompletionPositionAndRevisionIsDifferent(); + void testSendIfChangeIsBeforeCompletionPositionAndRevisionIsDifferent(); + void testResetChangedContentStartPositionIfLastRevisionIsSet(); +}; +} // namespace Internal +#endif // WITH_TESTS + } // namespace CppTools diff --git a/src/plugins/cpptools/symbolsearcher_test.cpp b/src/plugins/cpptools/symbolsearcher_test.cpp index 44e13d9984..e9822291c1 100644 --- a/src/plugins/cpptools/symbolsearcher_test.cpp +++ b/src/plugins/cpptools/symbolsearcher_test.cpp @@ -23,7 +23,7 @@ ** ****************************************************************************/ -#include "cpptoolsplugin.h" +#include "symbolsearcher_test.h" #include "builtinindexingsupport.h" #include "cppmodelmanager.h" @@ -86,19 +86,12 @@ public: class SymbolSearcherTestCase : public Tests::TestCase { public: - /// Takes no ownership of indexingSupportToUse SymbolSearcherTestCase(const QString &testFile, - CppIndexingSupport *indexingSupportToUse, const SymbolSearcher::Parameters &searchParameters, const ResultDataList &expectedResults) - : m_indexingSupportToUse(indexingSupportToUse) { QVERIFY(succeededSoFar()); - - QVERIFY(m_indexingSupportToUse); QVERIFY(parseFiles(testFile)); - m_indexingSupportToRestore = m_modelManager->indexingSupport(); - m_modelManager->setIndexingSupport(m_indexingSupportToUse); CppIndexingSupport *indexingSupport = m_modelManager->indexingSupport(); const QScopedPointer<SymbolSearcher> symbolSearcher( @@ -109,16 +102,6 @@ public: ResultDataList results = ResultData::fromSearchResultList(search.results()); QCOMPARE(results, expectedResults); } - - ~SymbolSearcherTestCase() - { - if (m_indexingSupportToRestore) - m_modelManager->setIndexingSupport(m_indexingSupportToRestore); - } - -private: - CppIndexingSupport *m_indexingSupportToRestore = nullptr; - CppIndexingSupport *m_indexingSupportToUse = nullptr; }; } // anonymous namespace @@ -137,20 +120,16 @@ template<> char *toString(const ResultData &data) } // namespace QTest QT_END_NAMESPACE -void CppToolsPlugin::test_builtinsymbolsearcher() +void SymbolSearcherTest::test() { QFETCH(QString, testFile); QFETCH(SymbolSearcher::Parameters, searchParameters); QFETCH(ResultDataList, expectedResults); - QScopedPointer<CppIndexingSupport> builtinIndexingSupport(new BuiltinIndexingSupport); - SymbolSearcherTestCase(testFile, - builtinIndexingSupport.data(), - searchParameters, - expectedResults); + SymbolSearcherTestCase(testFile, searchParameters, expectedResults); } -void CppToolsPlugin::test_builtinsymbolsearcher_data() +void SymbolSearcherTest::test_data() { QTest::addColumn<QString>("testFile"); QTest::addColumn<SymbolSearcher::Parameters>("searchParameters"); diff --git a/src/plugins/cpptools/symbolsearcher_test.h b/src/plugins/cpptools/symbolsearcher_test.h new file mode 100644 index 0000000000..d8a0a37054 --- /dev/null +++ b/src/plugins/cpptools/symbolsearcher_test.h @@ -0,0 +1,41 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#pragma once + +#include <QObject> + +namespace CppTools::Internal { + +class SymbolSearcherTest : public QObject +{ + Q_OBJECT + +private slots: + void test(); + void test_data(); +}; + +} // namespace CppTools::Internal diff --git a/src/plugins/cpptools/typehierarchybuilder_test.cpp b/src/plugins/cpptools/typehierarchybuilder_test.cpp index 70b172acf9..ef6bec36e9 100644 --- a/src/plugins/cpptools/typehierarchybuilder_test.cpp +++ b/src/plugins/cpptools/typehierarchybuilder_test.cpp @@ -23,7 +23,7 @@ ** ****************************************************************************/ -#include "cpptoolsplugin.h" +#include "typehierarchybuilder_test.h" #include "cppmodelmanager.h" #include "cpptoolstestcase.h" @@ -136,7 +136,7 @@ public: } // anonymous namespace -void CppToolsPlugin::test_typehierarchy_data() +void TypeHierarchyBuilderTest::test_data() { QTest::addColumn<QList<TestDocument> >("documents"); QTest::addColumn<QString>("expectedHierarchy"); @@ -181,7 +181,7 @@ void CppToolsPlugin::test_typehierarchy_data() ); } -void CppToolsPlugin::test_typehierarchy() +void TypeHierarchyBuilderTest::test() { QFETCH(QList<TestDocument>, documents); QFETCH(QString, expectedHierarchy); diff --git a/src/plugins/cpptools/typehierarchybuilder_test.h b/src/plugins/cpptools/typehierarchybuilder_test.h new file mode 100644 index 0000000000..7ebf13a6ec --- /dev/null +++ b/src/plugins/cpptools/typehierarchybuilder_test.h @@ -0,0 +1,41 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#pragma once + +#include <QObject> + +namespace CppTools::Internal { + +class TypeHierarchyBuilderTest : public QObject +{ + Q_OBJECT + +private slots: + void test_data(); + void test(); +}; + +} // namespace CppTools::Internal |