Esempio n. 1
0
ParseSessionData::ParseSessionData(const QVector<UnsavedFile>& unsavedFiles, ClangIndex* index,
                                   const ClangParsingEnvironment& environment, Options options)
    : m_file(nullptr)
    , m_unit(nullptr)
{
    unsigned int flags = CXTranslationUnit_CXXChainedPCH
        | CXTranslationUnit_DetailedPreprocessingRecord
#if CINDEX_VERSION_MINOR >= 34
        | CXTranslationUnit_KeepGoing
#endif
    ;
    if (options.testFlag(SkipFunctionBodies)) {
        flags |= CXTranslationUnit_SkipFunctionBodies;
    }
    if (options.testFlag(PrecompiledHeader)) {
        flags |= CXTranslationUnit_ForSerialization;
    } else {
        flags |= CXTranslationUnit_CacheCompletionResults
              |  CXTranslationUnit_PrecompiledPreamble;
        if (environment.quality() == ClangParsingEnvironment::Unknown) {
            flags |= CXTranslationUnit_Incomplete;
        }
    }

    const auto tuUrl = environment.translationUnitUrl();
    Q_ASSERT(!tuUrl.isEmpty());

    const auto arguments = argsForSession(tuUrl.str(), options, environment.parserSettings());
    QVector<const char*> clangArguments;

    const auto& includes = environment.includes();
    const auto& pchInclude = environment.pchInclude();

    // uses QByteArray as smart-pointer for const char* ownership
    QVector<QByteArray> smartArgs;
    smartArgs.reserve(includes.system.size() + includes.project.size()
                      + pchInclude.isValid() + arguments.size() + 1);
    clangArguments.reserve(smartArgs.size());

    std::transform(arguments.constBegin(), arguments.constEnd(),
                   std::back_inserter(clangArguments),
                   [] (const QByteArray &argument) { return argument.constData(); });

    // NOTE: the PCH include must come before all other includes!
    if (pchInclude.isValid()) {
        clangArguments << "-include";
        QByteArray pchFile = pchInclude.toLocalFile().toUtf8();
        smartArgs << pchFile;
        clangArguments << pchFile.constData();
    }

    if (needGccCompatibility(environment)) {
        const auto compatFile = QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("kdevclangsupport/gcc_compat.h")).toUtf8();
        if (!compatFile.isEmpty()) {
            smartArgs << compatFile;
            clangArguments << "-include" << compatFile.constData();
        }
    }

    if (hasQtIncludes(includes.system)) {
        const auto wrappedQtHeaders = QStandardPaths::locate(QStandardPaths::GenericDataLocation,
                                                             QStringLiteral("kdevclangsupport/wrappedQtHeaders"),
                                                             QStandardPaths::LocateDirectory).toUtf8();
        if (!wrappedQtHeaders.isEmpty()) {
            smartArgs << wrappedQtHeaders;
            clangArguments << "-isystem" << wrappedQtHeaders.constData();
            const auto qtCore = wrappedQtHeaders + "/QtCore";
            smartArgs << qtCore;
            clangArguments << "-isystem" << qtCore.constData();
        }
    }

    addIncludes(&clangArguments, &smartArgs, includes.system, "-isystem");
    addIncludes(&clangArguments, &smartArgs, includes.project, "-I");

    const auto& frameworkDirectories = environment.frameworkDirectories();
    addFrameworkDirectories(&clangArguments, &smartArgs, frameworkDirectories.system, "-iframework");
    addFrameworkDirectories(&clangArguments, &smartArgs, frameworkDirectories.project, "-F");

    smartArgs << writeDefinesFile(environment.defines());
    clangArguments << "-imacros" << smartArgs.last().constData();

    // append extra args from environment variable
    static const auto extraArgs = ::extraArgs();
    foreach (const QByteArray& arg, extraArgs) {
        clangArguments << arg.constData();
    }

    QVector<CXUnsavedFile> unsaved;
    //For PrecompiledHeader, we don't want unsaved contents (and contents.isEmpty())
    if (!options.testFlag(PrecompiledHeader)) {
        unsaved = toClangApi(unsavedFiles);
    }

    // debugging: print hypothetical clang invocation including args (for easy c&p for local testing)
    if (qEnvironmentVariableIsSet("KDEV_CLANG_DISPLAY_ARGS")) {
        QTextStream out(stdout);
        out << "Invocation: clang";
        foreach (const auto& arg, clangArguments) {
            out << " " << arg;
        }
        out << " " << tuUrl.byteArray().constData() << "\n";
    }
Esempio n. 2
0
void TestDUChain::testParsingEnvironment()
{
    const TopDUContext::Features features = TopDUContext::AllDeclarationsContextsAndUses;

    IndexedTopDUContext indexed;
    ClangParsingEnvironment lastEnv;
    {
        TestFile file("int main() {}\n", "cpp");
        auto astFeatures = static_cast<TopDUContext::Features>(features | TopDUContext::AST);
        file.parse(astFeatures);
        file.setKeepDUChainData(true);
        QVERIFY(file.waitForParsed());

        DUChainWriteLocker lock;
        auto top = file.topContext();
        QVERIFY(top);
        auto sessionData = ParseSessionData::Ptr(dynamic_cast<ParseSessionData*>(top->ast().data()));
        lock.unlock();
        ParseSession session(sessionData);
        lock.lock();
        QVERIFY(session.data());
        QVERIFY(top);

        auto envFile = QExplicitlySharedDataPointer<ClangParsingEnvironmentFile>(
            dynamic_cast<ClangParsingEnvironmentFile*>(file.topContext()->parsingEnvironmentFile().data()));

        QCOMPARE(envFile->features(), astFeatures);
        QVERIFY(envFile->featuresSatisfied(astFeatures));
        QCOMPARE(envFile->environmentQuality(), ClangParsingEnvironment::Source);

        // if no environment is given, no update should be triggered
        QVERIFY(!envFile->needsUpdate());

        // same env should also not trigger a reparse
        ClangParsingEnvironment env = session.environment();
        QCOMPARE(env.quality(), ClangParsingEnvironment::Source);
        QVERIFY(!envFile->needsUpdate(&env));

        // but changing the environment should trigger an update
        env.addIncludes(Path::List() << Path("/foo/bar/baz"));
        QVERIFY(envFile->needsUpdate(&env));
        envFile->setEnvironment(env);
        QVERIFY(!envFile->needsUpdate(&env));

        // setting the environment quality higher should require an update
        env.setQuality(ClangParsingEnvironment::BuildSystem);
        QVERIFY(envFile->needsUpdate(&env));
        envFile->setEnvironment(env);
        QVERIFY(!envFile->needsUpdate(&env));

        // changing defines requires an update
        env.addDefines(QHash<QString, QString>{ { "foo", "bar" } });
        QVERIFY(envFile->needsUpdate(&env));

        // but only when changing the defines for the envFile's TU
        const auto barTU = IndexedString("bar.cpp");
        const auto oldTU = env.translationUnitUrl();
        env.setTranslationUnitUrl(barTU);
        QCOMPARE(env.translationUnitUrl(), barTU);
        QVERIFY(!envFile->needsUpdate(&env));
        env.setTranslationUnitUrl(oldTU);
        QVERIFY(envFile->needsUpdate(&env));

        // update it again
        envFile->setEnvironment(env);
        QVERIFY(!envFile->needsUpdate(&env));
        lastEnv = env;

        // now compare against a lower quality environment
        // in such a case, we do not want to trigger an update
        env.setQuality(ClangParsingEnvironment::Unknown);
        env.setTranslationUnitUrl(barTU);
        QVERIFY(!envFile->needsUpdate(&env));

        // even when the environment changes
        env.addIncludes(Path::List() << Path("/lalalala"));
        QVERIFY(!envFile->needsUpdate(&env));

        indexed = top->indexed();
    }

    DUChain::self()->storeToDisk();

    {
        DUChainWriteLocker lock;
        QVERIFY(!DUChain::self()->isInMemory(indexed.index()));
        QVERIFY(indexed.data());
        QVERIFY(DUChain::self()->environmentFileForDocument(indexed));
        auto envFile = QExplicitlySharedDataPointer<ClangParsingEnvironmentFile>(
            dynamic_cast<ClangParsingEnvironmentFile*>(DUChain::self()->environmentFileForDocument(indexed).data()));
        QVERIFY(envFile);

        QCOMPARE(envFile->features(), features);
        QVERIFY(envFile->featuresSatisfied(features));
        QVERIFY(!envFile->needsUpdate(&lastEnv));
        DUChain::self()->removeDocumentChain(indexed.data());
    }
}