diff options
Diffstat (limited to 'src/tools/clangbackend/ipcsource/clangtranslationunit.cpp')
-rw-r--r-- | src/tools/clangbackend/ipcsource/clangtranslationunit.cpp | 521 |
1 files changed, 521 insertions, 0 deletions
diff --git a/src/tools/clangbackend/ipcsource/clangtranslationunit.cpp b/src/tools/clangbackend/ipcsource/clangtranslationunit.cpp new file mode 100644 index 0000000000..5df086781f --- /dev/null +++ b/src/tools/clangbackend/ipcsource/clangtranslationunit.cpp @@ -0,0 +1,521 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://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 http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#include "clangtranslationunit.h" + +#include "cursor.h" +#include "clangstring.h" +#include "codecompleter.h" +#include "commandlinearguments.h" +#include "diagnosticcontainer.h" +#include "diagnosticset.h" +#include "projectpart.h" +#include "skippedsourceranges.h" +#include "sourcelocation.h" +#include "sourcerange.h" +#include "highlightinginformations.h" +#include "translationunitfilenotexitexception.h" +#include "translationunitisnullexception.h" +#include "translationunitparseerrorexception.h" +#include "translationunits.h" +#include "unsavedfiles.h" + +#include <utf8string.h> + +#include <QDebug> +#include <QFileInfo> +#include <QLoggingCategory> + +#include <ostream> + +static Q_LOGGING_CATEGORY(verboseLibLog, "qtc.clangbackend.verboselib"); + +static bool isVerboseModeEnabled() +{ + return verboseLibLog().isDebugEnabled(); +} + +namespace ClangBackEnd { + +class TranslationUnitData +{ +public: + TranslationUnitData(const Utf8String &filePath, + const ProjectPart &projectPart, + const Utf8StringVector &fileArguments, + TranslationUnits &translationUnits); + ~TranslationUnitData(); + +public: + TranslationUnits &translationUnits; + time_point lastProjectPartChangeTimePoint; + QSet<Utf8String> dependedFilePaths; + ProjectPart projectPart; + Utf8StringVector fileArguments; + Utf8String filePath; + CXTranslationUnit translationUnit = nullptr; + CXIndex index = nullptr; + uint documentRevision = 0; + bool needsToBeReparsed = false; + bool hasNewDiagnostics = true; + bool hasNewHighlightingInformations = true; + bool isUsedByCurrentEditor = false; + bool isVisibleInEditor = false; +}; + +TranslationUnitData::TranslationUnitData(const Utf8String &filePath, + const ProjectPart &projectPart, + const Utf8StringVector &fileArguments, + TranslationUnits &translationUnits) + : translationUnits(translationUnits), + lastProjectPartChangeTimePoint(std::chrono::steady_clock::now()), + projectPart(projectPart), + fileArguments(fileArguments), + filePath(filePath) +{ + dependedFilePaths.insert(filePath); +} + +TranslationUnitData::~TranslationUnitData() +{ + clang_disposeTranslationUnit(translationUnit); + clang_disposeIndex(index); +} + +TranslationUnit::TranslationUnit(const Utf8String &filePath, + const ProjectPart &projectPart, + const Utf8StringVector &fileArguments, + TranslationUnits &translationUnits, + FileExistsCheck fileExistsCheck) + : d(std::make_shared<TranslationUnitData>(filePath, + projectPart, + fileArguments, + translationUnits)) +{ + if (fileExistsCheck == CheckIfFileExists) + checkIfFileExists(); +} + +bool TranslationUnit::isNull() const +{ + return !d; +} + +void TranslationUnit::setIsUsedByCurrentEditor(bool isUsedByCurrentEditor) +{ + d->isUsedByCurrentEditor = isUsedByCurrentEditor; +} + +bool TranslationUnit::isUsedByCurrentEditor() const +{ + return d->isUsedByCurrentEditor; +} + +void TranslationUnit::setIsVisibleInEditor(bool isVisibleInEditor) +{ + d->isVisibleInEditor = isVisibleInEditor; +} + +bool TranslationUnit::isVisibleInEditor() const +{ + return d->isVisibleInEditor; +} + +void TranslationUnit::reset() +{ + d.reset(); +} + +void TranslationUnit::reparse() const +{ + cxTranslationUnit(); + + reparseTranslationUnit(); +} + +CXIndex TranslationUnit::index() const +{ + checkIfNull(); + + if (!d->index) { + const bool displayDiagnostics = isVerboseModeEnabled(); + d->index = clang_createIndex(1, displayDiagnostics); + } + + return d->index; +} + +CXTranslationUnit TranslationUnit::cxTranslationUnit() const +{ + cxTranslationUnitWithoutReparsing(); + reparseTranslationUnitIfFilesAreChanged(); + + return d->translationUnit; +} + +CXTranslationUnit TranslationUnit::cxTranslationUnitWithoutReparsing() const +{ + checkIfNull(); + checkIfFileExists(); + removeTranslationUnitIfProjectPartWasChanged(); + createTranslationUnitIfNeeded(); + + return d->translationUnit; +} + +const Utf8String &TranslationUnit::filePath() const +{ + checkIfNull(); + + return d->filePath; +} + +const Utf8String &TranslationUnit::projectPartId() const +{ + checkIfNull(); + + return d->projectPart.projectPartId(); +} + +FileContainer TranslationUnit::fileContainer() const +{ + checkIfNull(); + + return FileContainer(d->filePath, + d->projectPart.projectPartId(), + Utf8String(), + false, + d->documentRevision); +} + +const ProjectPart &TranslationUnit::projectPart() const +{ + checkIfNull(); + + return d->projectPart; +} + +void TranslationUnit::setDocumentRevision(uint revision) +{ + d->documentRevision = revision; +} + +uint TranslationUnit::documentRevision() const +{ + return d->documentRevision; +} + +const time_point &TranslationUnit::lastProjectPartChangeTimePoint() const +{ + return d->lastProjectPartChangeTimePoint; +} + +bool TranslationUnit::isNeedingReparse() const +{ + return d->needsToBeReparsed; +} + +bool TranslationUnit::hasNewDiagnostics() const +{ + return d->hasNewDiagnostics; +} + +bool TranslationUnit::hasNewHighlightingInformations() const +{ + return d->hasNewHighlightingInformations; +} + +DiagnosticSet TranslationUnit::diagnostics() const +{ + d->hasNewDiagnostics = false; + + return DiagnosticSet(clang_getDiagnosticSetFromTU(cxTranslationUnit())); +} + +QVector<ClangBackEnd::DiagnosticContainer> TranslationUnit::mainFileDiagnostics() const +{ + const auto mainFilePath = filePath(); + const auto isMainFileDiagnostic = [mainFilePath](const Diagnostic &diagnostic) { + return diagnostic.location().filePath() == mainFilePath; + }; + + return diagnostics().toDiagnosticContainers(isMainFileDiagnostic); +} + +const QSet<Utf8String> &TranslationUnit::dependedFilePaths() const +{ + createTranslationUnitIfNeeded(); + + return d->dependedFilePaths; +} + +void TranslationUnit::setDirtyIfDependencyIsMet(const Utf8String &filePath) +{ + if (d->dependedFilePaths.contains(filePath) && isMainFileAndExistsOrIsOtherFile(filePath)) { + d->needsToBeReparsed = true; + d->hasNewDiagnostics = true; + d->hasNewHighlightingInformations = true; + } +} + +SourceLocation TranslationUnit::sourceLocationAt(uint line, uint column) const +{ + return SourceLocation(cxTranslationUnit(), filePath(), line, column); +} + +SourceLocation TranslationUnit::sourceLocationAt(const Utf8String &filePath, uint line, uint column) const +{ + return SourceLocation(cxTranslationUnit(), filePath, line, column); +} + +SourceRange TranslationUnit::sourceRange(uint fromLine, uint fromColumn, uint toLine, uint toColumn) const +{ + return SourceRange(sourceLocationAt(fromLine, fromColumn), + sourceLocationAt(toLine, toColumn)); +} + +Cursor TranslationUnit::cursorAt(uint line, uint column) const +{ + return clang_getCursor(cxTranslationUnit(), sourceLocationAt(line, column)); +} + +Cursor TranslationUnit::cursorAt(const Utf8String &filePath, uint line, uint column) const +{ + return clang_getCursor(cxTranslationUnit(), sourceLocationAt(filePath, line, column)); +} + +Cursor TranslationUnit::cursor() const +{ + return clang_getTranslationUnitCursor(cxTranslationUnit()); +} + +HighlightingInformations TranslationUnit::highlightingInformations() const +{ + d->hasNewHighlightingInformations = false; + + return highlightingInformationsInRange(cursor().sourceRange()); +} + +HighlightingInformations TranslationUnit::highlightingInformationsInRange(const SourceRange &range) const +{ + CXToken *cxTokens = 0; + uint cxTokensCount = 0; + auto translationUnit = cxTranslationUnit(); + + clang_tokenize(translationUnit, range, &cxTokens, &cxTokensCount); + + return HighlightingInformations(translationUnit, cxTokens, cxTokensCount); +} + +SkippedSourceRanges TranslationUnit::skippedSourceRanges() const +{ + return SkippedSourceRanges(cxTranslationUnit(), d->filePath.constData()); +} + +void TranslationUnit::checkIfNull() const +{ + if (isNull()) + throw TranslationUnitIsNullException(); +} + +void TranslationUnit::checkIfFileExists() const +{ + if (!QFileInfo::exists(d->filePath.toString())) + throw TranslationUnitFileNotExitsException(d->filePath); +} + +void TranslationUnit::updateLastProjectPartChangeTimePoint() const +{ + d->lastProjectPartChangeTimePoint = std::chrono::steady_clock::now(); +} + +void TranslationUnit::removeTranslationUnitIfProjectPartWasChanged() const +{ + if (projectPartIsOutdated()) { + clang_disposeTranslationUnit(d->translationUnit); + d->translationUnit = nullptr; + } +} + +bool TranslationUnit::projectPartIsOutdated() const +{ + return d->projectPart.lastChangeTimePoint() >= d->lastProjectPartChangeTimePoint; +} + +bool TranslationUnit::isMainFileAndExistsOrIsOtherFile(const Utf8String &filePath) const +{ + if (filePath == d->filePath) + return QFileInfo::exists(d->filePath); + + return true; +} + +void TranslationUnit::createTranslationUnitIfNeeded() const +{ + if (!d->translationUnit) { + d->translationUnit = CXTranslationUnit(); + + const auto args = commandLineArguments(); + if (isVerboseModeEnabled()) + args.print(); + + CXErrorCode errorCode = clang_parseTranslationUnit2(index(), + NULL, + args.data(), + args.count(), + unsavedFiles().cxUnsavedFiles(), + unsavedFiles().count(), + defaultOptions(), + &d->translationUnit); + + checkTranslationUnitErrorCode(errorCode); + + updateIncludeFilePaths(); + + updateLastProjectPartChangeTimePoint(); + } +} + +void TranslationUnit::checkTranslationUnitErrorCode(CXErrorCode errorCode) const +{ + switch (errorCode) { + case CXError_Success: break; + default: throw TranslationUnitParseErrorException(d->filePath, d->projectPart.projectPartId()); + } +} + +void TranslationUnit::reparseTranslationUnit() const +{ + clang_reparseTranslationUnit(d->translationUnit, + unsavedFiles().count(), + unsavedFiles().cxUnsavedFiles(), + clang_defaultReparseOptions(d->translationUnit)); + + updateIncludeFilePaths(); + + d->needsToBeReparsed = false; +} + +void TranslationUnit::reparseTranslationUnitIfFilesAreChanged() const +{ + if (isNeedingReparse()) + reparseTranslationUnit(); +} + +void TranslationUnit::includeCallback(CXFile included_file, + CXSourceLocation * /*inclusion_stack*/, + unsigned /*include_len*/, + CXClientData clientData) +{ + + ClangString includeFilePath(clang_getFileName(included_file)); + + TranslationUnit *translationUnit = static_cast<TranslationUnit*>(clientData); + + translationUnit->d->dependedFilePaths.insert(includeFilePath); +} + +UnsavedFiles &TranslationUnit::unsavedFiles() const +{ + return d->translationUnits.unsavedFiles(); +} + +void TranslationUnit::updateIncludeFilePaths() const +{ + auto oldDependedFilePaths = d->dependedFilePaths; + + d->dependedFilePaths.clear(); + d->dependedFilePaths.insert(filePath()); + + clang_getInclusions(d->translationUnit, includeCallback, const_cast<TranslationUnit*>(this)); + + if (d->dependedFilePaths.size() == 1) + d->dependedFilePaths = oldDependedFilePaths; + + d->translationUnits.addWatchedFiles(d->dependedFilePaths); +} + +CommandLineArguments TranslationUnit::commandLineArguments() const +{ + return CommandLineArguments(d->filePath.constData(), + d->projectPart.arguments(), + d->fileArguments, + isVerboseModeEnabled()); +} + +uint TranslationUnit::defaultOptions() +{ + return CXTranslationUnit_CacheCompletionResults + | CXTranslationUnit_PrecompiledPreamble + | CXTranslationUnit_IncludeBriefCommentsInCodeCompletion + | CXTranslationUnit_DetailedPreprocessingRecord; +} + +uint TranslationUnit::unsavedFilesCount() const +{ + return unsavedFiles().count(); +} + +CXUnsavedFile *TranslationUnit::cxUnsavedFiles() const +{ + return unsavedFiles().cxUnsavedFiles(); +} + +TranslationUnit::~TranslationUnit() = default; + +TranslationUnit::TranslationUnit(const TranslationUnit &) = default; +TranslationUnit &TranslationUnit::operator=(const TranslationUnit &) = default; + +TranslationUnit::TranslationUnit(TranslationUnit &&other) + : d(std::move(other.d)) +{ +} + +TranslationUnit &TranslationUnit::operator=(TranslationUnit &&other) +{ + d = std::move(other.d); + + return *this; +} + +bool operator==(const TranslationUnit &first, const TranslationUnit &second) +{ + return first.filePath() == second.filePath() && first.projectPartId() == second.projectPartId(); +} + +void PrintTo(const TranslationUnit &translationUnit, ::std::ostream *os) +{ + *os << "TranslationUnit(" + << translationUnit.filePath().constData() << ", " + << translationUnit.projectPartId().constData() << ", " + << translationUnit.documentRevision() << ")"; +} + +} // namespace ClangBackEnd |