diff --git a/plugins/clang/duchain/parsesession.cpp b/plugins/clang/duchain/parsesession.cpp index 1fbd0c9ea9..2310f9454d 100644 --- a/plugins/clang/duchain/parsesession.cpp +++ b/plugins/clang/duchain/parsesession.cpp @@ -1,653 +1,632 @@ /* This file is part of KDevelop Copyright 2013 Olivier de Gaalon Copyright 2013 Milian Wolff Copyright 2013 Kevin Funk This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "parsesession.h" #include #include "clangdiagnosticevaluator.h" #include "todoextractor.h" #include "clanghelpers.h" #include "clangindex.h" #include "clangparsingenvironment.h" #include "util/clangdebug.h" #include "util/clangtypes.h" #include "util/clangutils.h" #include "headerguardassistant.h" #include #include #include #include #include #include #include #include #include using namespace KDevelop; namespace { QVector extraArgs() { const auto extraArgsString = QString::fromLatin1(qgetenv("KDEV_CLANG_EXTRA_ARGUMENTS")); const auto extraArgs = KShell::splitArgs(extraArgsString); // transform to list of QByteArrays QVector result; result.reserve(extraArgs.size()); for (const QString& arg : extraArgs) { result << arg.toLatin1(); } clangDebug() << "Passing extra arguments to clang:" << result; return result; } void sanitizeArguments(QVector& arguments) { // We remove the -Werror flag, and replace -Werror=foo by -Wfoo. // Warning as error may cause problem to the clang parser. const auto asError = QByteArrayLiteral("-Werror="); const auto documentation = QByteArrayLiteral("-Wdocumentation"); for (auto& argument : arguments) { if (argument == "-Werror") { argument.clear(); } else if (argument.startsWith(asError)) { // replace -Werror=foo by -Wfoo argument.remove(2, asError.length() - 2); } #if CINDEX_VERSION_MINOR < 100 // FIXME https://bugs.llvm.org/show_bug.cgi?id=35333 if (argument == documentation) { argument.clear(); } #endif } } QVector argsForSession(const QString& path, ParseSessionData::Options options, const ParserSettings& parserSettings) { QMimeDatabase db; if (db.mimeTypeForFile(path).name() == QLatin1String("text/x-objcsrc")) { return {QByteArrayLiteral("-xobjective-c++")}; } // TODO: No proper mime type detection possible yet // cf. https://bugs.freedesktop.org/show_bug.cgi?id=26913 if (path.endsWith(QLatin1String(".cl"), Qt::CaseInsensitive)) { return {QByteArrayLiteral("-xcl")}; } // TODO: No proper mime type detection possible yet // cf. https://bugs.freedesktop.org/show_bug.cgi?id=23700 if (path.endsWith(QLatin1String(".cu"), Qt::CaseInsensitive) || path.endsWith(QLatin1String(".cuh"), Qt::CaseInsensitive)) { auto result = parserSettings.toClangAPI(); result.append(QByteArrayLiteral("-xcuda")); return result; } if (parserSettings.parserOptions.isEmpty()) { // The parserOptions can be empty for some unit tests that use ParseSession directly auto defaultArguments = ClangSettingsManager::self()->parserSettings(path).toClangAPI(); defaultArguments.append(QByteArrayLiteral("-nostdinc")); defaultArguments.append(QByteArrayLiteral("-nostdinc++")); defaultArguments.append(QByteArrayLiteral("-xc++")); sanitizeArguments(defaultArguments); return defaultArguments; } auto result = parserSettings.toClangAPI(); result.append(QByteArrayLiteral("-nostdinc")); if (parserSettings.isCpp()) { result.append(QByteArrayLiteral("-nostdinc++")); } if (options & ParseSessionData::PrecompiledHeader) { result.append(parserSettings.isCpp() ? QByteArrayLiteral("-xc++-header") : QByteArrayLiteral("-xc-header")); sanitizeArguments(result); return result; } result.append(parserSettings.isCpp() ? QByteArrayLiteral("-xc++") : QByteArrayLiteral("-xc")); sanitizeArguments(result); return result; } void addIncludes(QVector* args, QVector* otherArgs, const Path::List& includes, const char* cliSwitch) { for (const Path& url : includes) { if (url.isEmpty()) { continue; } QFileInfo info(url.toLocalFile()); QByteArray path = url.toLocalFile().toUtf8(); if (info.isFile()) { path.prepend("-include"); } else { path.prepend(cliSwitch); } otherArgs->append(path); args->append(path.constData()); } } void addFrameworkDirectories(QVector* args, QVector* otherArgs, const Path::List& frameworkDirectories, const char* cliSwitch) { for (const Path& url : frameworkDirectories) { if (url.isEmpty()) { continue; } QFileInfo info(url.toLocalFile()); if (!info.isDir()) { qCWarning(KDEV_CLANG) << "supposed framework directory is not a directory:" << url.pathOrUrl(); continue; } QByteArray path = url.toLocalFile().toUtf8(); otherArgs->append(cliSwitch); otherArgs->append(path); args->append(cliSwitch); args->append(path.constData()); } } QVector toClangApi(const QVector& unsavedFiles) { QVector unsaved; unsaved.reserve(unsavedFiles.size()); std::transform(unsavedFiles.begin(), unsavedFiles.end(), std::back_inserter(unsaved), [] (const UnsavedFile& file) { return file.toClangApi(); }); return unsaved; } bool hasQtIncludes(const Path::List& includePaths) { return std::find_if(includePaths.begin(), includePaths.end(), [] (const Path& path) { return path.lastPathSegment() == QLatin1String("QtCore"); }) != includePaths.end(); } } ParseSessionData::ParseSessionData(const QVector& unsavedFiles, ClangIndex* index, const ClangParsingEnvironment& environment, Options options) : m_file(nullptr) , m_unit(nullptr) { unsigned int flags = 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 if (environment.quality() == ClangParsingEnvironment::Unknown) { flags |= CXTranslationUnit_Incomplete; } if (options.testFlag(OpenedInEditor)) { flags |= CXTranslationUnit_CacheCompletionResults #if CINDEX_VERSION_MINOR >= 32 | CXTranslationUnit_CreatePreambleOnFirstParse #endif | CXTranslationUnit_PrecompiledPreamble; } const auto tuUrl = environment.translationUnitUrl(); Q_ASSERT(!tuUrl.isEmpty()); const auto arguments = argsForSession(tuUrl.str(), options, environment.parserSettings()); QVector clangArguments; const auto& includes = environment.includes(); const auto& pchInclude = environment.pchInclude(); // uses QByteArray as smart-pointer for const char* ownership QVector 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 (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 QByteArray 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"); // libclang cannot find it's builtin dir automatically, we have to specify it manually smartArgs << ClangHelpers::clangBuiltinIncludePath().toUtf8(); clangArguments << "-isystem" << smartArgs.last().constData(); if (!environment.defines().isEmpty()) { smartArgs << writeDefinesFile(environment.defines()); clangArguments << "-imacros" << smartArgs.last().constData(); } if (!environment.workingDirectory().isEmpty()) { QByteArray workingDirectory = environment.workingDirectory().toLocalFile().toUtf8(); workingDirectory.prepend("-working-directory"); smartArgs << workingDirectory; clangArguments << workingDirectory.constData(); } // append extra args from environment variable static const auto extraArgs = ::extraArgs(); for (const QByteArray& arg : extraArgs) { clangArguments << arg.constData(); } QVector 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"; for (const auto& arg : qAsConst(clangArguments)) { out << " " << arg; } out << " " << tuUrl.byteArray().constData() << "\n"; } const CXErrorCode code = clang_parseTranslationUnit2( index->index(), tuUrl.byteArray().constData(), clangArguments.constData(), clangArguments.size(), unsaved.data(), unsaved.size(), flags, &m_unit ); if (code != CXError_Success) { qCWarning(KDEV_CLANG) << "clang_parseTranslationUnit2 return with error code" << code; if (!qEnvironmentVariableIsSet("KDEV_CLANG_DISPLAY_DIAGS")) { qCWarning(KDEV_CLANG) << " (start KDevelop with `KDEV_CLANG_DISPLAY_DIAGS=1 kdevelop` to see more diagnostics)"; } } if (m_unit) { setUnit(m_unit); m_environment = environment; if (options.testFlag(PrecompiledHeader)) { clang_saveTranslationUnit(m_unit, QByteArray(tuUrl.byteArray() + ".pch").constData(), CXSaveTranslationUnit_None); } } else { qCWarning(KDEV_CLANG) << "Failed to parse translation unit:" << tuUrl; } } ParseSessionData::~ParseSessionData() { clang_disposeTranslationUnit(m_unit); } QByteArray ParseSessionData::writeDefinesFile(const QMap& defines) { m_definesFile.open(); Q_ASSERT(m_definesFile.isWritable()); { QTextStream definesStream(&m_definesFile); // don't show warnings about redefined macros definesStream << "#pragma clang system_header\n"; for (auto it = defines.begin(); it != defines.end(); ++it) { if (it.key().startsWith(QLatin1String("__has_include(")) || it.key().startsWith(QLatin1String("__has_include_next("))) { continue; } definesStream << QLatin1String("#define ") << it.key() << ' ' << it.value() << '\n'; } } m_definesFile.close(); if (qEnvironmentVariableIsSet("KDEV_CLANG_DISPLAY_DEFINES")) { QFile f(m_definesFile.fileName()); f.open(QIODevice::ReadOnly); Q_ASSERT(f.isReadable()); QTextStream out(stdout); out << "Defines file: " << f.fileName() << "\n" << f.readAll() << f.size() << "\n VS defines:" << defines.size() << "\n"; } return m_definesFile.fileName().toUtf8(); } void ParseSessionData::setUnit(CXTranslationUnit unit) { m_unit = unit; m_diagnosticsCache.clear(); if (m_unit) { const ClangString unitFile(clang_getTranslationUnitSpelling(unit)); m_file = clang_getFile(m_unit, unitFile.c_str()); } else { m_file = nullptr; } } ClangParsingEnvironment ParseSessionData::environment() const { return m_environment; } -void ParseSessionData::lock() -{ - ENSURE_CHAIN_NOT_LOCKED -#if CINDEX_VERSION_MINOR >= 41 - m_waitingSessions.ref(); -#endif - m_mutex.lock(); -} - -void ParseSessionData::unlock() -{ - m_mutex.unlock(); -#if CINDEX_VERSION_MINOR >= 41 - if (!m_waitingSessions.deref() && m_unit) { - // noone is waiting anymore, suspend - qCDebug(KDEV_CLANG) << "suspending translation unit" << ClangString(clang_getFileName(m_file)); - if (!clang_suspendTranslationUnit(m_unit)) { - qCWarning(KDEV_CLANG) << "could not suspend translation unit" << ClangString(clang_getFileName(m_file)); - } - } -#endif -} - ParseSession::ParseSession(const ParseSessionData::Ptr& data) : d(data) { if (d) { - d->lock(); + ENSURE_CHAIN_NOT_LOCKED + d->m_mutex.lock(); } } ParseSession::~ParseSession() { if (d) { - d->unlock(); + d->m_mutex.unlock(); } } void ParseSession::setData(const ParseSessionData::Ptr& data) { if (data == d) { return; } if (d) { - d->unlock(); + d->m_mutex.unlock(); } d = data; if (d) { - d->lock(); + ENSURE_CHAIN_NOT_LOCKED + d->m_mutex.lock(); } } ParseSessionData::Ptr ParseSession::data() const { return d; } IndexedString ParseSession::languageString() { static const IndexedString lang("Clang"); return lang; } ClangProblem::Ptr ParseSession::getOrCreateProblem(int indexInTU, CXDiagnostic diagnostic) const { if (!d) { return {}; } auto& problem = d->m_diagnosticsCache[indexInTU]; if (!problem) { problem = ClangDiagnosticEvaluator::createProblem(diagnostic, d->m_unit); } return problem; } ClangProblem::Ptr ParseSession::createExternalProblem(int indexInTU, CXDiagnostic diagnostic, const KLocalizedString& descriptionTemplate, int childProblemFinalLocationIndex) const { // Make a copy of the original (cached) problem since it is modified later auto problem = ClangProblem::Ptr(new ClangProblem(*getOrCreateProblem(indexInTU, diagnostic))); // Insert a copy of the parent problem (without child problems) as the first // child problem to preserve its location. auto* problemCopy = new ClangProblem(); problemCopy->setSource(problem->source()); problemCopy->setFinalLocation(problem->finalLocation()); problemCopy->setFinalLocationMode(problem->finalLocationMode()); problemCopy->setDescription(problem->description()); problemCopy->setExplanation(problem->explanation()); problemCopy->setSeverity(problem->severity()); auto childProblems = problem->diagnostics(); childProblems.prepend(IProblem::Ptr(problemCopy)); problem->setDiagnostics(childProblems); // Override the problem's finalLocation with that of the child problem in this document. // This is required to make the problem show up in the problem reporter for this // file, since it filters by finalLocation. It will also lead the user to the correct // location when clicking the problem and cause proper error highlighting. int index = (childProblemFinalLocationIndex >= 0) ? (1 + childProblemFinalLocationIndex) : (childProblems.size() - 1); problem->setFinalLocation(childProblems[index]->finalLocation()); problem->setDescription(descriptionTemplate.subs(problem->description()).toString()); return problem; } QList ParseSession::createRequestedHereProblems(int indexInTU, CXDiagnostic diagnostic, CXFile file) const { QList results; auto childDiagnostics = clang_getChildDiagnostics(diagnostic); auto numChildDiagnostics = clang_getNumDiagnosticsInSet(childDiagnostics); for (uint j = 0; j < numChildDiagnostics; ++j) { auto childDiagnostic = clang_getDiagnosticInSet(childDiagnostics, j); CXSourceLocation childLocation = clang_getDiagnosticLocation(childDiagnostic); CXFile childDiagnosticFile; clang_getFileLocation(childLocation, &childDiagnosticFile, nullptr, nullptr, nullptr); if (childDiagnosticFile == file) { QString description = ClangString(clang_getDiagnosticSpelling(childDiagnostic)).toString(); if (description.endsWith(QLatin1String("requested here"))) { // Note: Using the index j here assumes a 1:1 mapping from clang child diagnostics to KDevelop // problem diagnostics (i.e., child problems). If we wanted to avoid making this assumption, we'd have // to use ClangDiagnosticEvaluator::createProblem() first and then search within its // child problems to find the correct index. results << createExternalProblem(indexInTU, diagnostic, ki18n("Requested here: %1"), j); } } } return results; } QList ParseSession::problemsForFile(CXFile file) const { if (!d) { return {}; } QList problems; // extra clang diagnostics const uint numDiagnostics = clang_getNumDiagnostics(d->m_unit); problems.reserve(numDiagnostics); d->m_diagnosticsCache.resize(numDiagnostics); for (uint i = 0; i < numDiagnostics; ++i) { auto diagnostic = clang_getDiagnostic(d->m_unit, i); CXSourceLocation location = clang_getDiagnosticLocation(diagnostic); CXFile diagnosticFile; clang_getFileLocation(location, &diagnosticFile, nullptr, nullptr, nullptr); const auto requestedHereProblems = createRequestedHereProblems(i, diagnostic, file); for (const auto& ptr : requestedHereProblems) { problems.append(static_cast(ptr)); } // missing-include problems are so severe in clang that we always propagate // them to this document, to ensure that the user will see the error. if (diagnosticFile != file && ClangDiagnosticEvaluator::diagnosticType(diagnostic) != ClangDiagnosticEvaluator::IncludeFileNotFoundProblem) { continue; } problems << ((diagnosticFile == file) ? getOrCreateProblem(i, diagnostic) : createExternalProblem(i, diagnostic, ki18n("In included file: %1"))); clang_disposeDiagnostic(diagnostic); } // other problem sources TodoExtractor extractor(unit(), file); problems << extractor.problems(); #if CINDEX_VERSION_MINOR > 30 // note that the below warning is triggered on every reparse when there is a precompiled preamble // see also TestDUChain::testReparseIncludeGuard const QString path = QDir(ClangString(clang_getFileName(file)).toString()).canonicalPath(); const IndexedString indexedPath(path); const auto location = clang_getLocationForOffset(d->m_unit, file, 0); if (ClangHelpers::isHeader(path) && !clang_isFileMultipleIncludeGuarded(unit(), file) && !clang_Location_isInSystemHeader(location) // clang_isFileMultipleIncludeGuarded always returns 0 in case our only file is the header && !clang_Location_isFromMainFile(location)) { QExplicitlySharedDataPointer problem(new StaticAssistantProblem); problem->setSeverity(IProblem::Warning); problem->setDescription(i18n("Header is not guarded against multiple inclusions")); problem->setExplanation(i18n("The given header is not guarded against multiple inclusions, " "either with the conventional #ifndef/#define/#endif macro guards or with #pragma once.")); const KTextEditor::Range problemRange(0, 0, KDevelop::createCodeRepresentation(indexedPath)->lines(), 0); problem->setFinalLocation(DocumentRange{indexedPath, problemRange}); problem->setSource(IProblem::Preprocessor); problem->setSolutionAssistant(KDevelop::IAssistant::Ptr(new HeaderGuardAssistant(d->m_unit, file))); problems << problem; } #endif return problems; } CXTranslationUnit ParseSession::unit() const { return d ? d->m_unit : nullptr; } CXFile ParseSession::file(const QByteArray& path) const { return clang_getFile(unit(), path.constData()); } CXFile ParseSession::mainFile() const { return d ? d->m_file : nullptr; } bool ParseSession::reparse(const QVector& unsavedFiles, const ClangParsingEnvironment& environment) { if (!d || environment != d->m_environment) { return false; } auto unsaved = toClangApi(unsavedFiles); const auto code = clang_reparseTranslationUnit(d->m_unit, unsaved.size(), unsaved.data(), clang_defaultReparseOptions(d->m_unit)); if (code != CXError_Success) { qCWarning(KDEV_CLANG) << "clang_reparseTranslationUnit return with error code" << code; // if error code != 0 => clang_reparseTranslationUnit invalidates the old translation unit => clean up clang_disposeTranslationUnit(d->m_unit); d->setUnit(nullptr); return false; } // update state d->setUnit(d->m_unit); return true; } ClangParsingEnvironment ParseSession::environment() const { if (!d) { return {}; } return d->m_environment; } diff --git a/plugins/clang/duchain/parsesession.h b/plugins/clang/duchain/parsesession.h index ef24719ef9..a1db5916fe 100644 --- a/plugins/clang/duchain/parsesession.h +++ b/plugins/clang/duchain/parsesession.h @@ -1,156 +1,150 @@ /* This file is part of KDevelop Copyright 2013 Olivier de Gaalon Copyright 2013 Milian Wolff This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef PARSESESSION_H #define PARSESESSION_H #include #include #include #include #include #include #include #include "clangprivateexport.h" #include "clangproblem.h" #include "clangparsingenvironment.h" #include "unsavedfile.h" class ClangIndex; class KDEVCLANGPRIVATE_EXPORT ParseSessionData : public KDevelop::IAstContainer { public: using Ptr = QExplicitlySharedDataPointer; enum Option { NoOption = 0, ///< No special options SkipFunctionBodies = 1 << 0, ///< Pass CXTranslationUnit_SkipFunctionBodies (likely unwanted) PrecompiledHeader = 1 << 1, ///< Pass CXTranslationUnit_PrecompiledPreamble and others to cache precompiled headers OpenedInEditor = 1 << 2, ///< File is currently opened in the editor }; Q_DECLARE_FLAGS(Options, Option) /** * Parse the given @p contents. * * @param unsavedFiles Optional unsaved document contents from the editor. */ ParseSessionData(const QVector& unsavedFiles, ClangIndex* index, const ClangParsingEnvironment& environment, Options options = Options()); ~ParseSessionData() override; ClangParsingEnvironment environment() const; private: - void lock(); - void unlock(); - friend class ParseSession; void setUnit(CXTranslationUnit unit); QByteArray writeDefinesFile(const QMap& defines); - // ref count of ParseSessions that are waiting to get access to this data - // only when this drops to zero after unlocking should we suspend a TU - QAtomicInt m_waitingSessions; QMutex m_mutex; CXFile m_file = nullptr; CXTranslationUnit m_unit = nullptr; ClangParsingEnvironment m_environment; /// TODO: share this file for all TUs that use the same defines (probably most in a project) /// best would be a PCH, if possible QTemporaryFile m_definesFile; // cached ProblemPointer representation for diagnostics QVector m_diagnosticsCache; }; /** * Thread-safe utility class around a CXTranslationUnit. * * It will lock the mutex of the currently set ParseSessionData and thereby ensure * only one ParseSession can operate on a given CXTranslationUnit stored therein. */ class KDEVCLANGPRIVATE_EXPORT ParseSession { public: /** * @return a unique identifier for Clang documents. */ static KDevelop::IndexedString languageString(); /** * Initialize a parse session with the given data and, if that data is valid, lock its mutex. */ explicit ParseSession(const ParseSessionData::Ptr& data); /** * Unlocks the mutex of the currently set ParseSessionData. */ ~ParseSession(); /** * Unlocks the mutex of the currently set ParseSessionData, and instead acquire the lock in @p data. */ void setData(const ParseSessionData::Ptr& data); ParseSessionData::Ptr data() const; /** * @return find the CXFile for the given path. */ CXFile file(const QByteArray& path) const; /** * @return the CXFile for the first file in this translation unit. */ CXFile mainFile() const; QList problemsForFile(CXFile file) const; CXTranslationUnit unit() const; bool reparse(const QVector& unsavedFiles, const ClangParsingEnvironment& environment); ClangParsingEnvironment environment() const; private: Q_DISABLE_COPY(ParseSession) ClangProblem::Ptr getOrCreateProblem(int indexInTU, CXDiagnostic diagnostic) const; ClangProblem::Ptr createExternalProblem(int indexInTU, CXDiagnostic diagnostic, const KLocalizedString& descriptionTemplate, int childProblemFinalLocationIndex = -1) const; QList createRequestedHereProblems(int indexInTU, CXDiagnostic diagnostic, CXFile file) const; ParseSessionData::Ptr d; }; #endif // PARSESESSION_H