diff --git a/clangparsejob.cpp b/clangparsejob.cpp index 8c64c03280..98a13a6720 100644 --- a/clangparsejob.cpp +++ b/clangparsejob.cpp @@ -1,353 +1,363 @@ /* 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. */ #include "clangparsejob.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "duchain/clanghelpers.h" #include "duchain/clangpch.h" #include "duchain/tuduchain.h" #include "duchain/parsesession.h" #include "duchain/clangindex.h" #include "duchain/clangparsingenvironmentfile.h" #include "util/clangdebug.h" #include "util/clangtypes.h" #include "clangsupport.h" #include "documentfinderhelpers.h" #include #include #include #include #include #include using namespace KDevelop; namespace { QString findConfigFile(const QString& forFile, const QString& configFileName) { QDir dir = QFileInfo(forFile).dir(); while (dir.exists()) { const QFileInfo customIncludePaths(dir, configFileName); if (customIncludePaths.exists()) { return customIncludePaths.absoluteFilePath(); } if (!dir.cdUp()) { break; } } return {}; } Path::List readPathListFile(const QString& filepath) { if (filepath.isEmpty()) { return {}; } QFile f(filepath); if (!f.open(QIODevice::ReadOnly | QIODevice::Text)) { return {}; } const QString text = QString::fromLocal8Bit(f.readAll()); const QStringList lines = text.split('\n', QString::SkipEmptyParts); Path::List paths(lines.length()); std::transform(lines.begin(), lines.end(), paths.begin(), [] (const QString& line) { return Path(line); }); return paths; } /** * File should contain the header to precompile and use while parsing * @returns the first path in the file */ Path userDefinedPchIncludeForFile(const QString& sourcefile) { static const QString pchIncludeFilename = QStringLiteral(".kdev_pch_include"); const auto paths = readPathListFile(findConfigFile(sourcefile, pchIncludeFilename)); return paths.isEmpty() ? Path() : paths.first(); } ProjectFileItem* findProjectFileItem(const IndexedString& url, bool* hasBuildSystemInfo) { ProjectFileItem* file = nullptr; *hasBuildSystemInfo = false; for (auto project: ICore::self()->projectController()->projects()) { auto files = project->filesForPath(url); if (files.isEmpty()) { continue; } file = files.last(); // A file might be defined in different targets. // Prefer file items defined inside a target with non-empty includes. for (auto f: files) { if (!dynamic_cast(f->parent())) { continue; } file = f; if (!IDefinesAndIncludesManager::manager()->includes(f, IDefinesAndIncludesManager::ProjectSpecific).isEmpty()) { break; } } } if (file && file->project()) { if (auto bsm = file->project()->buildSystemManager()) { *hasBuildSystemInfo = bsm->hasIncludesOrDefines(file); } } return file; } +QString languageStandard(KSharedConfigPtr cfg) +{ + auto grp = cfg->group("Defines And Includes").group("Compiler"); + auto standard = grp.readEntry("Standard", QStringLiteral("c++11")); + // QStringLiteral("c++11") shouldn't be here, but surprisingly grp.readEntry can return empty string even if the default value is set. + return standard.isEmpty() ? QStringLiteral("c++11") : standard; +} + ClangParsingEnvironmentFile* parsingEnvironmentFile(const TopDUContext* context) { return dynamic_cast(context->parsingEnvironmentFile().data()); } bool hasTracker(const IndexedString& url) { return ICore::self()->languageController()->backgroundParser()->trackerForUrl(url); } ParseSessionData::Ptr findParseSession(const IndexedString &file) { DUChainReadLocker lock; const auto& context = DUChainUtils::standardContextForUrl(file.toUrl()); if (context) { return ParseSessionData::Ptr(dynamic_cast(context->ast().data())); } return {}; } } ClangParseJob::ClangParseJob(const IndexedString& url, ILanguageSupport* languageSupport) : ParseJob(url, languageSupport) { const auto tuUrl = clang()->index()->translationUnitForUrl(url); bool hasBuildSystemInfo; if (auto file = findProjectFileItem(tuUrl, &hasBuildSystemInfo)) { m_environment.addIncludes(IDefinesAndIncludesManager::manager()->includes(file)); m_environment.addDefines(IDefinesAndIncludesManager::manager()->defines(file)); + m_environment.setLanguageStandard(languageStandard(file->project()->projectConfiguration())); } else { m_environment.addIncludes(IDefinesAndIncludesManager::manager()->includes(tuUrl.str())); m_environment.addDefines(IDefinesAndIncludesManager::manager()->defines(tuUrl.str())); + m_environment.setLanguageStandard(QStringLiteral("c++11")); } const bool isSource = ClangHelpers::isSource(tuUrl.str()); m_environment.setQuality( isSource ? (hasBuildSystemInfo ? ClangParsingEnvironment::BuildSystem : ClangParsingEnvironment::Source) : ClangParsingEnvironment::Unknown ); m_environment.setTranslationUnitUrl(tuUrl); Path::List projectPaths; const auto& projects = ICore::self()->projectController()->projects(); projectPaths.reserve(projects.size()); foreach (auto project, projects) { projectPaths.append(project->path()); } m_environment.setProjectPaths(projectPaths); foreach(auto document, ICore::self()->documentController()->openDocuments()) { auto textDocument = document->textDocument(); if (!textDocument || !textDocument->isModified() || !textDocument->url().isLocalFile() || !DocumentFinderHelpers::mimeTypesList().contains(textDocument->mimeType())) { continue; } m_unsavedFiles << UnsavedFile(textDocument->url().toLocalFile(), textDocument->textLines(textDocument->documentRange())); const IndexedString indexedUrl(textDocument->url()); m_unsavedRevisions.insert(indexedUrl, ModificationRevision::revisionForFile(indexedUrl)); } } ClangSupport* ClangParseJob::clang() const { return static_cast(languageSupport()); } void ClangParseJob::run(ThreadWeaver::JobPointer /*self*/, ThreadWeaver::Thread */*thread*/) { QReadLocker parseLock(languageSupport()->parseLock()); if (abortRequested()) { return; } { const auto tuUrlStr = m_environment.translationUnitUrl().str(); m_environment.addIncludes(IDefinesAndIncludesManager::manager()->includesInBackground(tuUrlStr)); m_environment.addDefines(IDefinesAndIncludesManager::manager()->definesInBackground(tuUrlStr)); m_environment.setPchInclude(userDefinedPchIncludeForFile(tuUrlStr)); } if (abortRequested()) { return; } { UrlParseLock urlLock(document()); if (abortRequested() || !isUpdateRequired(ParseSession::languageString())) { return; } } ParseSession session(findParseSession(document())); if (abortRequested()) { return; } if (!session.data() && document() != m_environment.translationUnitUrl()) { // no cached data found for the current file, but maybe // we are lucky and can grab it from the TU context // this happens e.g. when originally a .cpp file is open and then one // of its included files is opened in the editor. session.setData(findParseSession(m_environment.translationUnitUrl())); if (abortRequested()) { return; } } if (!session.data() || !session.reparse(m_unsavedFiles, m_environment)) { session.setData(createSessionData()); } if (!session.unit()) { // failed to parse file, unpin and don't try again clang()->index()->unpinTranslationUnitForUrl(document()); return; } if (!clang_getFile(session.unit(), document().byteArray())) { // this parse job's document does not exist in the pinned translation unit // so we need to unpin and re-add this document // Ideally we'd reset m_environment and session, but this is much simpler // and shouldn't be a common case clang()->index()->unpinTranslationUnitForUrl(document()); ICore::self()->languageController()->backgroundParser()->addDocument(document(), minimumFeatures(), priority()); return; } Imports imports = ClangHelpers::tuImports(session.unit()); IncludeFileContexts includedFiles; if (auto pch = clang()->index()->pch(m_environment)) { auto pchFile = pch->mapFile(session.unit()); includedFiles = pch->mapIncludes(session.unit()); includedFiles.insert(pchFile, pch->context()); auto tuFile = clang_getFile(session.unit(), m_environment.translationUnitUrl().byteArray()); imports.insert(tuFile, { pchFile, CursorInRevision(0, 0) } ); } if (abortRequested()) { return; } auto context = ClangHelpers::buildDUChain(session.mainFile(), imports, session, minimumFeatures(), includedFiles, clang()->index()); setDuChain(context); if (abortRequested()) { return; } { if (minimumFeatures() & TopDUContext::AST) { DUChainWriteLocker lock; context->setAst(IAstContainer::Ptr(session.data())); } #ifdef QT_DEBUG DUChainReadLocker lock; auto file = parsingEnvironmentFile(context); Q_ASSERT(file); // verify that features and environment where properly set in ClangHelpers::buildDUChain Q_ASSERT(file->featuresSatisfied(TopDUContext::Features(minimumFeatures() & ~TopDUContext::ForceUpdateRecursive))); #endif } foreach(const auto& context, includedFiles) { if (!context) { continue; } { // prefer the editor modification revision, instead of the on-disk revision auto it = m_unsavedRevisions.find(context->url()); if (it != m_unsavedRevisions.end()) { DUChainWriteLocker lock; auto file = parsingEnvironmentFile(context); Q_ASSERT(file); file->setModificationRevision(it.value()); } } if (::hasTracker(context->url())) { if (clang()->index()->translationUnitForUrl(context->url()) == m_environment.translationUnitUrl()) { // cache the parse session and the contained translation unit for this chain // this then allows us to quickly reparse the document if it is changed by // the user // otherwise no editor component is open for this document and we can dispose // the TU to save memory // share the session data with all contexts that are pinned to this TU DUChainWriteLocker lock; context->setAst(IAstContainer::Ptr(session.data())); } languageSupport()->codeHighlighting()->highlightDUChain(context); } } } ParseSessionData::Ptr ClangParseJob::createSessionData() const { const bool skipFunctionBodies = (minimumFeatures() <= TopDUContext::VisibleDeclarationsAndContexts); return ParseSessionData::Ptr(new ParseSessionData(m_unsavedFiles, clang()->index(), m_environment, (skipFunctionBodies ? ParseSessionData::SkipFunctionBodies : ParseSessionData::NoOption))); } const ParsingEnvironment* ClangParseJob::environment() const { return &m_environment; } diff --git a/duchain/clangparsingenvironment.cpp b/duchain/clangparsingenvironment.cpp index ebf4aea194..20a2dbfcb5 100644 --- a/duchain/clangparsingenvironment.cpp +++ b/duchain/clangparsingenvironment.cpp @@ -1,135 +1,147 @@ /* * This file is part of KDevelop * * Copyright 2014 Milian Wolff * * This program 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 program 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 General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "clangparsingenvironment.h" using namespace KDevelop; int ClangParsingEnvironment::type() const { return CppParsingEnvironment; } void ClangParsingEnvironment::setProjectPaths(const Path::List& projectPaths) { m_projectPaths = projectPaths; } Path::List ClangParsingEnvironment::projectPaths() const { return m_projectPaths; } void ClangParsingEnvironment::addIncludes(const Path::List& includes) { m_includes += includes; } ClangParsingEnvironment::IncludePaths ClangParsingEnvironment::includes() const { IncludePaths ret; ret.project.reserve(m_includes.size()); ret.system.reserve(m_includes.size()); foreach (const auto& path, m_includes) { bool inProject = false; foreach (const auto& project, m_projectPaths) { if (project.isParentOf(path) || project == path) { inProject = true; break; } } if (inProject) { ret.project.append(path); } else { ret.system.append(path); } } return ret; } void ClangParsingEnvironment::addDefines(const QHash& defines) { for (auto it = defines.constBegin(); it != defines.constEnd(); ++it) { m_defines[it.key()] = it.value(); } } QMap ClangParsingEnvironment::defines() const { return m_defines; } void ClangParsingEnvironment::setPchInclude(const Path& path) { m_pchInclude = path; } Path ClangParsingEnvironment::pchInclude() const { return m_pchInclude; } void ClangParsingEnvironment::setTranslationUnitUrl(const IndexedString& url) { m_tuUrl = url; } IndexedString ClangParsingEnvironment::translationUnitUrl() const { return m_tuUrl; } void ClangParsingEnvironment::setQuality(Quality quality) { m_quality = quality; } ClangParsingEnvironment::Quality ClangParsingEnvironment::quality() const { return m_quality; } uint ClangParsingEnvironment::hash() const { KDevHash hash; hash << m_defines.size(); for (auto it = m_defines.constBegin(); it != m_defines.constEnd(); ++it) { hash << qHash(it.key()) << qHash(it.value()); } hash << m_includes.size(); for (const auto& include : m_includes) { hash << qHash(include); } hash << qHash(m_pchInclude); + hash << qHash(m_languageStandard); return hash; } bool ClangParsingEnvironment::operator==(const ClangParsingEnvironment& other) const { return m_defines == other.m_defines && m_includes == other.m_includes && m_pchInclude == other.m_pchInclude && m_quality == other.m_quality - && m_tuUrl == other.m_tuUrl; + && m_tuUrl == other.m_tuUrl + && m_languageStandard == other.m_languageStandard; +} + +void ClangParsingEnvironment::setLanguageStandard(const QString& standard) +{ + m_languageStandard = standard; +} + +QString ClangParsingEnvironment::languageStandard() const +{ + return m_languageStandard; } diff --git a/duchain/clangparsingenvironment.h b/duchain/clangparsingenvironment.h index c9be2d3be0..2e4ea8b39c 100644 --- a/duchain/clangparsingenvironment.h +++ b/duchain/clangparsingenvironment.h @@ -1,105 +1,110 @@ /* * This file is part of KDevelop * * Copyright 2014 Milian Wolff * * This program 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 program 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 General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef CLANGPARSINGENVIRONMENT_H #define CLANGPARSINGENVIRONMENT_H #include #include #include class KDEVCLANGDUCHAIN_EXPORT ClangParsingEnvironment : public KDevelop::ParsingEnvironment { public: virtual ~ClangParsingEnvironment() = default; virtual int type() const override; /** * Sets the list of project paths. * * Any include path outside these project paths is considered * to be a system include. */ void setProjectPaths(const KDevelop::Path::List& projectPaths); KDevelop::Path::List projectPaths() const; /** * Add the given list of @p include paths to this environment. */ void addIncludes(const KDevelop::Path::List& includes); struct IncludePaths { /// This list contains all include paths outside the known projects paths. KDevelop::Path::List system; /// This list contains all include paths inside the known projects paths. KDevelop::Path::List project; }; /** * Returns the list of includes, split into a list of system includes and project includes. */ IncludePaths includes() const; void addDefines(const QHash& defines); QMap defines() const; void setPchInclude(const KDevelop::Path& path); KDevelop::Path pchInclude() const; void setTranslationUnitUrl(const KDevelop::IndexedString& url); KDevelop::IndexedString translationUnitUrl() const; enum Quality { Unknown, Source, BuildSystem }; void setQuality(Quality quality); Quality quality() const; + void setLanguageStandard(const QString& standard); + + QString languageStandard() const; + /** * Hash all contents of this environment and return the result. * * This is useful for a quick comparison, and enough to store on-disk * to figure out if the environment changed or not. */ uint hash() const; bool operator==(const ClangParsingEnvironment& other) const; bool operator!=(const ClangParsingEnvironment& other) const { return !(*this == other); } private: KDevelop::Path::List m_projectPaths; KDevelop::Path::List m_includes; // NOTE: As elements in QHash stored in an unordered sequence, we're using QMap instead QMap m_defines; KDevelop::Path m_pchInclude; KDevelop::IndexedString m_tuUrl; Quality m_quality = Unknown; + QString m_languageStandard; }; #endif // CLANGPARSINGENVIRONMENT_H diff --git a/duchain/parsesession.cpp b/duchain/parsesession.cpp index 5b0552ce8a..77fd1129f1 100644 --- a/duchain/parsesession.cpp +++ b/duchain/parsesession.cpp @@ -1,355 +1,348 @@ /* 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 "clangproblem.h" #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 #include #include #include #include #include #include using namespace KDevelop; namespace { -QVector argsForSession(const QString& path, ParseSessionData::Options options) +QVector argsForSession(const QString& path, ParseSessionData::Options options, QByteArray& languageStandard) { - if (options & ParseSessionData::PrecompiledHeader) { - static const QVector pchArgs {"-std=c++11", "-xc++-header", "-Wall", "-nostdinc", "-nostdinc++"}; - return pchArgs; + QMimeDatabase db; + if(db.mimeTypeForFile(path).name() == QStringLiteral("text/x-objcsrc")) { + return {"-xobjective-c++"}; } - static const std::map> mimeToArgs = { - { - QStringLiteral("text/x-csrc"), { "-std=c11", "-Wall", "-nostdinc", "-nostdinc++" } - }, - { - QStringLiteral("text/x-c++src"), { "-std=c++11", "-xc++", "-Wall", "-nostdinc", "-nostdinc++" } - }, - { - QStringLiteral("text/x-objcsrc"), {"-xobjective-c++"} - } - }; + Q_ASSERT(!languageStandard.isEmpty()); + languageStandard = "-std=" + languageStandard; - QMimeDatabase db; - QString mimeType = db.mimeTypeForFile(path).name(); - auto res = mimeToArgs.find(mimeType); + if (options & ParseSessionData::PrecompiledHeader) { + return {languageStandard.data(), languageStandard.contains("++") ? "-xc++-header" : "-xc-header", "-Wall", "-nostdinc", "-nostdinc++"}; + } - if (res != mimeToArgs.end()) { - return res->second; + if(!languageStandard.contains("++")) { + return { languageStandard.data(), "-Wall", "-nostdinc", "-nostdinc++" }; } - return mimeToArgs.find(QStringLiteral("text/x-c++src"))->second; + + return { languageStandard.data(), "-xc++", "-Wall", "-nostdinc", "-nostdinc++" }; } void addIncludes(QVector* args, QVector* otherArgs, const Path::List& includes, const char* cliSwitch) { foreach (const Path& url, includes) { 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()); } } 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; } } ParseSessionData::ParseSessionData(const QVector& unsavedFiles, ClangIndex* index, const ClangParsingEnvironment& environment, Options options) : m_file(nullptr) , m_unit(nullptr) { unsigned int flags = CXTranslationUnit_CXXChainedPCH | CXTranslationUnit_DetailedPreprocessingRecord; if (options.testFlag(SkipFunctionBodies)) { flags |= CXTranslationUnit_SkipFunctionBodies; } if (options.testFlag(PrecompiledHeader)) { flags |= CXTranslationUnit_ForSerialization; } else { flags |= CXTranslationUnit_CacheCompletionResults | CXTranslationUnit_PrecompiledPreamble | CXTranslationUnit_Incomplete; } const auto tuUrl = environment.translationUnitUrl(); Q_ASSERT(!tuUrl.isEmpty()); - QVector args = argsForSession(tuUrl.str(), options); + auto languageStandard = environment.languageStandard().toLocal8Bit(); + QVector args = argsForSession(tuUrl.str(), options, languageStandard); if (!options.testFlag(DisableSpellChecking)) { // TODO: Check whether this slows down parsing noticably // also see http://lists.cs.uiuc.edu/pipermail/cfe-commits/Week-of-Mon-20100705/032025.html args << "-fspell-checking"; // note: disabled by default in CIndex } if (!options.testFlag(DisableDocumentationWarnings)) { // TODO: Check whether this slows down parsing noticably // according to llvm.org/devmtg/2012-11/Gribenko_CommentParsing.pdf this is about 5% with lots (> 10000) of documentation comments args << "-Wdocumentation"; } const auto& includes = environment.includes(); const auto& pchInclude = environment.pchInclude(); // uses QByteArray as smart-pointer for const char* ownership QVector otherArgs; otherArgs.reserve(includes.system.size() + includes.project.size() + pchInclude.isValid() + 1); args.reserve(args.size() + otherArgs.size()); // NOTE: the PCH include must come before all other includes! if (pchInclude.isValid()) { args << "-include"; QByteArray pchFile = pchInclude.toLocalFile().toUtf8(); otherArgs << pchFile; args << pchFile.constData(); } addIncludes(&args, &otherArgs, includes.system, "-isystem"); addIncludes(&args, &otherArgs, includes.project, "-I"); m_definesFile.open(); QTextStream definesStream(&m_definesFile); Q_ASSERT(m_definesFile.isWritable()); const auto& defines = environment.defines(); for (auto it = defines.begin(); it != defines.end(); ++it) { definesStream << QStringLiteral("#define ") << it.key() << ' ' << it.value() << '\n'; } definesStream.flush(); otherArgs << m_definesFile.fileName().toUtf8(); args << "-imacros" << otherArgs.last().constData(); QVector unsaved; //For PrecompiledHeader, we don't want unsaved contents (and contents.isEmpty()) if (!options.testFlag(PrecompiledHeader)) { unsaved = toClangApi(unsavedFiles); } #if CINDEX_VERSION_MINOR >= 23 const CXErrorCode code = clang_parseTranslationUnit2( index->index(), tuUrl.byteArray(), args.constData(), args.size(), unsaved.data(), unsaved.size(), flags, &m_unit ); if (code != CXError_Success) { clangDebug() << "clang_parseTranslationUnit2 return with error code" << code; } #else m_unit = clang_parseTranslationUnit( index->index(), tuUrl.byteArray(), args.constData(), args.size(), unsaved.data(), unsaved.size(), flags ); #endif if (m_unit) { setUnit(m_unit); m_environment = environment; if (options.testFlag(PrecompiledHeader)) { clang_saveTranslationUnit(m_unit, tuUrl.byteArray() + ".pch", CXSaveTranslationUnit_None); } } else { clangDebug() << "Failed to parse translation unit:" << tuUrl; } } ParseSessionData::~ParseSessionData() { clang_disposeTranslationUnit(m_unit); } void ParseSessionData::setUnit(CXTranslationUnit unit) { Q_ASSERT(!m_unit || unit == m_unit); m_unit = unit; const ClangString unitFile(clang_getTranslationUnitSpelling(unit)); m_file = clang_getFile(m_unit, unitFile.c_str()); } ParseSession::ParseSession(const ParseSessionData::Ptr& data) : d(data) { if (d) { ENSURE_CHAIN_NOT_LOCKED d->m_mutex.lock(); } } ParseSession::~ParseSession() { if (d) { d->m_mutex.unlock(); } } void ParseSession::setData(const ParseSessionData::Ptr& data) { if (data == d) { return; } if (d) { d->m_mutex.unlock(); } d = data; if (d) { 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; } QList ParseSession::problemsForFile(CXFile file) const { if (!d) { return {}; } QList problems; // extra clang diagnostics static const ClangDiagnosticEvaluator evaluator; const uint numDiagnostics = clang_getNumDiagnostics(d->m_unit); problems.reserve(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); if (diagnosticFile != file) { continue; } ProblemPointer problem(evaluator.createProblem(diagnostic)); problems << problem; clang_disposeDiagnostic(diagnostic); } const QString path = QDir::cleanPath(ClangString(clang_getFileName(file)).toString()); const IndexedString indexedPath(path); // extract to-do problems if (ClangUtils::isFileEqual(file, d->m_file)) { // TODO: also extract problems from included files and put them into the correct places // currently, this is not possible as we don't know ho TodoExtractor extractor(unit(), indexedPath); problems << extractor.problems(); } // other problem sources if (ClangHelpers::isHeader(path) && !clang_isFileMultipleIncludeGuarded(unit(), file) && !clang_Location_isInSystemHeader(clang_getLocationForOffset(d->m_unit, file, 0))) { ProblemPointer problem(new Problem); problem->setSeverity(ProblemData::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.")); problem->setFinalLocation({indexedPath, KTextEditor::Range()}); problem->setSource(ProblemData::Preprocessor); problems << problem; // TODO: Easy to add an assistant here that adds the guards -- any takers? } 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); if (clang_reparseTranslationUnit(d->m_unit, unsaved.size(), unsaved.data(), clang_defaultReparseOptions(d->m_unit)) == 0) { d->setUnit(d->m_unit); return true; } else { return false; } } ClangParsingEnvironment ParseSession::environment() const { return d->m_environment; }