Clang: Prepare for multi-threading

* Make the necessary data implicitly shared since it might get
  accessed/modified from two different threads with follow-up changes.
  This applies for UnsavedFiles/UnsavedFile and ProjectPart::arguments().

* Avoid returning references.

Change-Id: I98842c1cb90ae0d344a15c63b72cbc89568722d3
Reviewed-by: Tim Jenssen <tim.jenssen@qt.io>
This commit is contained in:
Nikolai Kosjar
2016-05-30 10:25:52 +02:00
parent 53415cece1
commit 606d41187c
23 changed files with 323 additions and 291 deletions

View File

@@ -35,7 +35,8 @@ HEADERS += $$PWD/clangcodemodelserver.h \
$$PWD/highlightingmarks.h \
$$PWD/highlightingmarksiterator.h \
$$PWD/utf8positionfromlinecolumn.h \
$$PWD/clangfilepath.h
$$PWD/clangfilepath.h \
$$PWD/clangunsavedfilesshallowarguments.h
SOURCES += $$PWD/clangcodemodelserver.cpp \
$$PWD/codecompleter.cpp \
@@ -70,4 +71,5 @@ SOURCES += $$PWD/clangcodemodelserver.cpp \
$$PWD/highlightingmark.cpp \
$$PWD/highlightingmarks.cpp \
$$PWD/utf8positionfromlinecolumn.cpp \
$$PWD/clangfilepath.cpp
$$PWD/clangfilepath.cpp \
$$PWD/clangunsavedfilesshallowarguments.cpp

View File

@@ -240,7 +240,8 @@ void ClangCodeModelServer::completeCode(const ClangBackEnd::CompleteCodeMessage
TIME_SCOPE_DURATION("ClangCodeModelServer::completeCode");
try {
CodeCompleter codeCompleter(translationUnits.translationUnit(message.filePath(), message.projectPartId()));
CodeCompleter codeCompleter(translationUnits.translationUnit(message.filePath(), message.projectPartId()),
unsavedFiles);
const auto codeCompletions = codeCompleter.complete(message.line(), message.column());

View File

@@ -28,6 +28,7 @@
#include "cursor.h"
#include "clangfilepath.h"
#include "clangstring.h"
#include "clangunsavedfilesshallowarguments.h"
#include "codecompleter.h"
#include "commandlinearguments.h"
#include "diagnosticcontainer.h"
@@ -43,6 +44,7 @@
#include "translationunitreparseerrorexception.h"
#include "translationunits.h"
#include "unsavedfiles.h"
#include "unsavedfile.h"
#include <utf8string.h>
@@ -199,19 +201,26 @@ CXTranslationUnit TranslationUnit::cxTranslationUnitWithoutReparsing() const
return d->translationUnit;
}
UnsavedFile &TranslationUnit::unsavedFile() const
UnsavedFile TranslationUnit::unsavedFile() const
{
return unsavedFiles().unsavedFile(filePath());
}
const Utf8String &TranslationUnit::filePath() const
Utf8String TranslationUnit::filePath() const
{
checkIfNull();
return d->filePath;
}
const Utf8String &TranslationUnit::projectPartId() const
Utf8StringVector TranslationUnit::fileArguments() const
{
checkIfNull();
return d->fileArguments;
}
Utf8String TranslationUnit::projectPartId() const
{
checkIfNull();
@@ -410,12 +419,14 @@ void TranslationUnit::createTranslationUnitIfNeeded() const
if (isVerboseModeEnabled())
args.print();
UnsavedFilesShallowArguments unsaved = unsavedFiles().shallowArguments();
d->parseErrorCode = clang_parseTranslationUnit2(index(),
NULL,
args.data(),
args.count(),
unsavedFiles().cxUnsavedFiles(),
unsavedFiles().count(),
unsaved.data(),
unsaved.count(),
defaultOptions(),
&d->translationUnit);
@@ -447,10 +458,12 @@ void TranslationUnit::checkReparseErrorCode() const
void TranslationUnit::reparseTranslationUnit() const
{
UnsavedFilesShallowArguments unsaved = unsavedFiles().shallowArguments();
d->reparseErrorCode = clang_reparseTranslationUnit(
d->translationUnit,
unsavedFiles().count(),
unsavedFiles().cxUnsavedFiles(),
unsaved.count(),
unsaved.data(),
clang_defaultReparseOptions(d->translationUnit));
checkReparseErrorCode();
@@ -480,7 +493,7 @@ void TranslationUnit::includeCallback(CXFile included_file,
translationUnit->d->dependedFilePaths.insert(normalizedFilePath);
}
UnsavedFiles &TranslationUnit::unsavedFiles() const
UnsavedFiles TranslationUnit::unsavedFiles() const
{
return d->translationUnits.unsavedFiles();
}
@@ -539,11 +552,6 @@ uint TranslationUnit::unsavedFilesCount() const
return unsavedFiles().count();
}
CXUnsavedFile *TranslationUnit::cxUnsavedFiles() const
{
return unsavedFiles().cxUnsavedFiles();
}
TranslationUnit::~TranslationUnit() = default;
TranslationUnit::TranslationUnit(const TranslationUnit &) = default;

View File

@@ -96,13 +96,14 @@ public:
CXTranslationUnit cxTranslationUnit() const;
CXTranslationUnit cxTranslationUnitWithoutReparsing() const;
UnsavedFile &unsavedFile() const;
CXUnsavedFile * cxUnsavedFiles() const;
UnsavedFile unsavedFile() const;
UnsavedFiles unsavedFiles() const;
uint unsavedFilesCount() const;
const Utf8String &filePath() const;
const Utf8String &projectPartId() const;
Utf8String filePath() const;
Utf8StringVector fileArguments() const;
Utf8String projectPartId() const;
FileContainer fileContainer() const;
const ProjectPart &projectPart() const;
@@ -163,7 +164,6 @@ private:
CXSourceLocation * /*inclusion_stack*/,
unsigned /*include_len*/,
CXClientData clientData);
UnsavedFiles &unsavedFiles() const;
private:
mutable std::shared_ptr<TranslationUnitData> d;

View File

@@ -0,0 +1,57 @@
/****************************************************************************
**
** 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 "clangunsavedfilesshallowarguments.h"
#include "clangfilepath.h"
#include "unsavedfile.h"
#include "unsavedfiles.h"
namespace ClangBackEnd {
UnsavedFilesShallowArguments::UnsavedFilesShallowArguments(const UnsavedFiles &unsavedFiles)
{
const int unsavedFilesCount = int(unsavedFiles.count());
m_cxUnsavedFiles.resize(unsavedFilesCount);
for (int i = 0, total = unsavedFilesCount; i < total; ++i) {
const UnsavedFile &unsavedFile = unsavedFiles.at(i);
m_cxUnsavedFiles[i].Filename = unsavedFile.nativeFilePath().constData();
m_cxUnsavedFiles[i].Contents = unsavedFile.fileContent().constData();
m_cxUnsavedFiles[i].Length = uint(unsavedFile.fileContent().byteSize());
}
}
uint UnsavedFilesShallowArguments::count() const
{
return uint(m_cxUnsavedFiles.count());
}
CXUnsavedFile *UnsavedFilesShallowArguments::data()
{
return m_cxUnsavedFiles.data();
}
} // namespace ClangBackEnd

View File

@@ -0,0 +1,48 @@
/****************************************************************************
**
** 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 <QVector>
#include <clang-c/Index.h>
namespace ClangBackEnd {
class UnsavedFile;
class UnsavedFiles;
class UnsavedFilesShallowArguments {
public:
UnsavedFilesShallowArguments(const UnsavedFiles &unsavedFiles);
uint count() const;
CXUnsavedFile *data();
private:
QVector<CXUnsavedFile> m_cxUnsavedFiles;
};
} // namespace ClangBackEnd

View File

@@ -33,8 +33,10 @@
#include "codecompletionsextractor.h"
#include "sourcelocation.h"
#include "unsavedfile.h"
#include "unsavedfiles.h"
#include "clangtranslationunit.h"
#include "sourcerange.h"
#include "clangunsavedfilesshallowarguments.h"
#include <clang-c/Index.h>
@@ -55,8 +57,10 @@ CodeCompletions toCodeCompletions(const ClangCodeCompleteResults &results)
} // anonymous namespace
CodeCompleter::CodeCompleter(TranslationUnit translationUnit)
CodeCompleter::CodeCompleter(TranslationUnit translationUnit,
const UnsavedFiles &unsavedFiles)
: translationUnit(std::move(translationUnit))
, unsavedFiles(unsavedFiles)
{
}
@@ -64,10 +68,7 @@ CodeCompletions CodeCompleter::complete(uint line, uint column)
{
neededCorrection_ = CompletionCorrection::NoCorrection;
ClangCodeCompleteResults results = complete(line,
column,
translationUnit.cxUnsavedFiles(),
translationUnit.unsavedFilesCount());
ClangCodeCompleteResults results = completeHelper(line, column);
tryDotArrowCorrectionIfNoResults(results, line, column);
@@ -79,19 +80,17 @@ CompletionCorrection CodeCompleter::neededCorrection() const
return neededCorrection_;
}
ClangCodeCompleteResults CodeCompleter::complete(uint line,
uint column,
CXUnsavedFile *unsavedFiles,
unsigned unsavedFileCount)
ClangCodeCompleteResults CodeCompleter::completeHelper(uint line, uint column)
{
const Utf8String nativeFilePath = FilePath::toNativeSeparators(translationUnit.filePath());
UnsavedFilesShallowArguments unsaved = unsavedFiles.shallowArguments();
return clang_codeCompleteAt(translationUnit.cxTranslationUnitWithoutReparsing(),
nativeFilePath.constData(),
line,
column,
unsavedFiles,
unsavedFileCount,
unsaved.data(),
unsaved.count(),
defaultOptions());
}
@@ -106,6 +105,11 @@ uint CodeCompleter::defaultOptions() const
return options;
}
UnsavedFile &CodeCompleter::unsavedFile()
{
return unsavedFiles.unsavedFile(translationUnit.filePath());
}
void CodeCompleter::tryDotArrowCorrectionIfNoResults(ClangCodeCompleteResults &results,
uint line,
uint column)
@@ -124,16 +128,12 @@ ClangCodeCompleteResults CodeCompleter::completeWithArrowInsteadOfDot(uint line,
uint dotPosition)
{
ClangCodeCompleteResults results;
const bool replaced = translationUnit.unsavedFile().replaceAt(dotPosition,
const bool replaced = unsavedFile().replaceAt(dotPosition,
1,
Utf8StringLiteral("->"));
if (replaced) {
results = complete(line,
column + 1,
translationUnit.cxUnsavedFiles(),
translationUnit.unsavedFilesCount());
results = completeHelper(line, column + 1);
if (results.hasResults())
neededCorrection_ = CompletionCorrection::DotToArrowCorrection;
}

View File

@@ -26,6 +26,7 @@
#pragma once
#include "clangtranslationunit.h"
#include "unsavedfiles.h"
#include <codecompletion.h>
@@ -39,7 +40,8 @@ class CodeCompleter
{
public:
CodeCompleter() = default;
CodeCompleter(TranslationUnit translationUnit);
CodeCompleter(TranslationUnit translationUnit,
const UnsavedFiles &unsavedFiles);
CodeCompletions complete(uint line, uint column);
@@ -47,15 +49,13 @@ public:
private:
uint defaultOptions() const;
UnsavedFile &unsavedFile();
void tryDotArrowCorrectionIfNoResults(ClangCodeCompleteResults &results,
uint line,
uint column);
ClangCodeCompleteResults complete(uint line,
uint column,
CXUnsavedFile *unsavedFiles,
unsigned unsavedFileCount);
ClangCodeCompleteResults completeHelper(uint line, uint column);
ClangCodeCompleteResults completeWithArrowInsteadOfDot(uint line,
uint column,
uint dotPosition);
@@ -65,6 +65,7 @@ private:
private:
TranslationUnit translationUnit;
UnsavedFiles unsavedFiles;
CompletionCorrection neededCorrection_ = CompletionCorrection::NoCorrection;
};

View File

@@ -37,7 +37,7 @@
namespace ClangBackEnd {
CommandLineArguments::CommandLineArguments(const char *filePath,
const std::vector<const char *> &projectPartArguments,
const Utf8StringVector &projectPartArguments,
const Utf8StringVector &fileArguments,
bool addVerboseOption)
{
@@ -46,7 +46,8 @@ CommandLineArguments::CommandLineArguments(const char *filePath,
+ (addVerboseOption ? 1 : 0);
m_arguments.reserve(elementsToReserve);
m_arguments = projectPartArguments;
for (const auto &argument : projectPartArguments)
m_arguments.push_back(argument.constData());
for (const auto &argument : fileArguments)
m_arguments.push_back(argument.constData());
if (addVerboseOption)

View File

@@ -35,7 +35,7 @@ class CommandLineArguments
{
public:
CommandLineArguments(const char *filePath,
const std::vector<const char *> &projectPartArguments,
const Utf8StringVector &projectPartArguments,
const Utf8StringVector &fileArguments,
bool addVerboseOption);

View File

@@ -38,23 +38,12 @@ public:
ProjectPartData(const Utf8String &projectPartId);
~ProjectPartData();
public:
void clearArguments();
public:
time_point lastChangeTimePoint;
std::vector<const char*> arguments;
Utf8StringVector arguments;
Utf8String projectPartId;
};
void ProjectPartData::clearArguments()
{
for (auto argument : arguments)
delete [] argument;
arguments.clear();
}
ProjectPartData::ProjectPartData(const Utf8String &projectPartId)
: lastChangeTimePoint(std::chrono::steady_clock::now()),
projectPartId(projectPartId)
@@ -63,7 +52,6 @@ ProjectPartData::ProjectPartData(const Utf8String &projectPartId)
ProjectPartData::~ProjectPartData()
{
clearArguments();
}
ProjectPart::ProjectPart(const Utf8String &projectPartId)
@@ -103,46 +91,26 @@ ProjectPart &ProjectPart::operator=(ProjectPart &&other)
void ProjectPart::clear()
{
d->projectPartId.clear();
d->clearArguments();
d->arguments.clear();
updateLastChangeTimePoint();
}
const Utf8String &ProjectPart::projectPartId() const
Utf8String ProjectPart::projectPartId() const
{
return d->projectPartId;
}
static const char *strdup(const Utf8String &utf8String)
{
char *cxArgument = new char[utf8String.byteSize() + 1];
std::memcpy(cxArgument, utf8String.constData(), utf8String.byteSize() + 1);
return cxArgument;
}
void ProjectPart::setArguments(const Utf8StringVector &arguments)
{
d->clearArguments();
d->arguments.resize(arguments.size());
std::transform(arguments.cbegin(), arguments.cend(), d->arguments.begin(), strdup);
d->arguments = arguments;
updateLastChangeTimePoint();
}
const std::vector<const char*> &ProjectPart::arguments() const
const Utf8StringVector ProjectPart::arguments() const
{
return d->arguments;
}
int ProjectPart::argumentCount() const
{
return d->arguments.size();
}
const char * const *ProjectPart::cxArguments() const
{
return arguments().data();
}
const time_point &ProjectPart::lastChangeTimePoint() const
{
return d->lastChangeTimePoint;

View File

@@ -29,7 +29,6 @@
#include <chrono>
#include <memory>
#include <vector>
class Utf8StringVector;
@@ -57,14 +56,10 @@ public:
void clear();
const Utf8String &projectPartId() const;
Utf8String projectPartId() const;
void setArguments(const Utf8StringVector &arguments_);
const std::vector<const char*> &arguments() const;
int argumentCount() const;
const char *const *cxArguments() const;
const Utf8StringVector arguments() const;
const time_point &lastChangeTimePoint() const;

View File

@@ -33,6 +33,7 @@
#include <skippedsourceranges.h>
#include <translationunitalreadyexistsexception.h>
#include <translationunitdoesnotexistexception.h>
#include <unsavedfiles.h>
#include <QDebug>
@@ -142,7 +143,7 @@ const std::vector<TranslationUnit> &TranslationUnits::translationUnits() const
return translationUnits_;
}
UnsavedFiles &TranslationUnits::unsavedFiles() const
UnsavedFiles TranslationUnits::unsavedFiles() const
{
return unsavedFiles_;
}

View File

@@ -69,7 +69,7 @@ public:
const std::vector<TranslationUnit> &translationUnits() const;
UnsavedFiles &unsavedFiles() const;
UnsavedFiles unsavedFiles() const;
void addWatchedFiles(QSet<Utf8String> &filePaths);

View File

@@ -26,64 +26,42 @@
#include "unsavedfile.h"
#include "clangfilepath.h"
#include "utf8string.h"
#include "utf8positionfromlinecolumn.h"
#include <cstring>
#include <ostream>
namespace ClangBackEnd {
UnsavedFile::UnsavedFile()
: cxUnsavedFile(CXUnsavedFile{nullptr, nullptr, 0UL})
{
}
UnsavedFile::UnsavedFile(const Utf8String &filePath, const Utf8String &fileContent)
UnsavedFile::UnsavedFile(const Utf8String &filePath,
const Utf8String &fileContent)
: m_filePath(filePath)
, m_nativeFilePath(FilePath::toNativeSeparators(filePath))
, m_fileContent(fileContent)
{
const Utf8String nativeFilePath = FilePath::toNativeSeparators(filePath);
char *cxUnsavedFilePath = new char[nativeFilePath.byteSize() + 1];
char *cxUnsavedFileContent = new char[fileContent.byteSize() + 1];
std::memcpy(cxUnsavedFilePath, nativeFilePath.constData(), nativeFilePath.byteSize() + 1);
std::memcpy(cxUnsavedFileContent, fileContent.constData(), fileContent.byteSize() + 1);
cxUnsavedFile = CXUnsavedFile{cxUnsavedFilePath,
cxUnsavedFileContent,
ulong(fileContent.byteSize())};
}
UnsavedFile::UnsavedFile(UnsavedFile &&other) Q_DECL_NOEXCEPT
: cxUnsavedFile(other.cxUnsavedFile)
Utf8String UnsavedFile::nativeFilePath() const
{
other.cxUnsavedFile = { nullptr, nullptr, 0UL };
}
UnsavedFile &UnsavedFile::operator=(UnsavedFile &&other) Q_DECL_NOEXCEPT
{
using std::swap;
swap(this->cxUnsavedFile, other.cxUnsavedFile);
return *this;
return m_nativeFilePath;
}
Utf8String UnsavedFile::filePath() const
{
const Utf8String nativeFilePathAsUtf8String = Utf8String::fromUtf8(nativeFilePath());
return FilePath::fromNativeSeparators(nativeFilePathAsUtf8String);
return m_filePath;
}
const char *UnsavedFile::nativeFilePath() const
Utf8String UnsavedFile::fileContent() const
{
return cxUnsavedFile.Filename;
return m_fileContent;
}
uint UnsavedFile::toUtf8Position(uint line, uint column, bool *ok) const
{
Utf8PositionFromLineColumn converter(cxUnsavedFile.Contents);
Utf8PositionFromLineColumn converter(m_fileContent.constData());
if (converter.find(line, column)) {
*ok = true;
return converter.position();
@@ -103,51 +81,28 @@ bool UnsavedFile::hasCharacterAt(uint line, uint column, char character) const
bool UnsavedFile::hasCharacterAt(uint position, char character) const
{
if (position < cxUnsavedFile.Length)
return cxUnsavedFile.Contents[position] == character;
if (position < uint(m_fileContent.byteSize()))
return m_fileContent.constData()[position] == character;
return false;
}
bool UnsavedFile::replaceAt(uint position, uint length, const Utf8String &replacement)
{
if (position < cxUnsavedFile.Length) {
Utf8String modifiedContent(cxUnsavedFile.Contents, cxUnsavedFile.Length);
modifiedContent.replace(int(position), int(length), replacement);
*this = UnsavedFile(Utf8String::fromUtf8(nativeFilePath()), modifiedContent);
if (position < uint(m_fileContent.byteSize())) {
m_fileContent.replace(int(position), int(length), replacement);
return true;
}
return false;
}
CXUnsavedFile *UnsavedFile::data()
{
return &cxUnsavedFile;
}
UnsavedFile::~UnsavedFile()
{
delete [] cxUnsavedFile.Contents;
delete [] cxUnsavedFile.Filename;
cxUnsavedFile.Contents = nullptr;
cxUnsavedFile.Filename = nullptr;
cxUnsavedFile.Length = 0;
}
static const char *printCString(const char *str)
{
return str ? str : "nullptr";
}
void PrintTo(const UnsavedFile &unsavedFile, std::ostream *os)
{
*os << "UnsavedFile("
<< printCString(unsavedFile.cxUnsavedFile.Filename) << ", "
<< printCString(unsavedFile.cxUnsavedFile.Contents) << ", "
<< unsavedFile.cxUnsavedFile.Length << ")";
<< unsavedFile.m_filePath.constData() << ", "
<< unsavedFile.m_fileContent.constData() << ", "
<< unsavedFile.m_fileContent.byteSize() << ")";
}
} // namespace ClangBackEnd

View File

@@ -25,13 +25,7 @@
#pragma once
#include <QtGlobal>
#include <clang-c/Index.h>
#include <iosfwd>
class Utf8String;
#include "utf8string.h"
namespace ClangBackEnd {
@@ -44,27 +38,23 @@ public:
UnsavedFile();
UnsavedFile(const Utf8String &filePath, const Utf8String &fileContent);
~UnsavedFile();
UnsavedFile(const UnsavedFile &other) = delete;
bool operator=(const UnsavedFile &other) = delete;
UnsavedFile(UnsavedFile &&other) Q_DECL_NOEXCEPT;
UnsavedFile &operator=(UnsavedFile &&other) Q_DECL_NOEXCEPT;
Utf8String filePath() const;
const char *nativeFilePath() const;
Utf8String nativeFilePath() const;
Utf8String fileContent() const;
// 1-based line and column
uint toUtf8Position(uint line, uint column, bool *ok) const;
bool hasCharacterAt(uint line, uint column, char character) const;
// 0-based position
bool hasCharacterAt(uint position, char character) const;
bool replaceAt(uint position, uint length, const Utf8String &replacement);
CXUnsavedFile *data();
public: // for tests
CXUnsavedFile cxUnsavedFile;
private:
Utf8String m_filePath;
Utf8String m_nativeFilePath;
Utf8String m_fileContent;
};
} // namespace ClangBackEnd

View File

@@ -25,20 +25,23 @@
#include "unsavedfiles.h"
#include "clangunsavedfilesshallowarguments.h"
#include "unsavedfile.h"
#include <QSharedData>
#include <algorithm>
namespace ClangBackEnd {
class UnsavedFilesData
class UnsavedFilesData : public QSharedData
{
public:
UnsavedFilesData();
public:
time_point lastChangeTimePoint;
std::vector<UnsavedFile> unsavedFiles;
QVector<UnsavedFile> unsavedFiles;
};
UnsavedFilesData::UnsavedFilesData()
@@ -47,24 +50,22 @@ UnsavedFilesData::UnsavedFilesData()
}
UnsavedFiles::UnsavedFiles()
: d(std::make_shared<UnsavedFilesData>())
: d(new UnsavedFilesData)
{
}
UnsavedFiles::~UnsavedFiles() = default;
UnsavedFiles::UnsavedFiles(const UnsavedFiles &) = default;
UnsavedFiles &UnsavedFiles::operator=(const UnsavedFiles &) = default;
UnsavedFiles::UnsavedFiles(UnsavedFiles &&other)
: d(std::move(other.d))
UnsavedFiles::~UnsavedFiles()
{
}
UnsavedFiles &UnsavedFiles::operator=(UnsavedFiles &&other)
UnsavedFiles::UnsavedFiles(const UnsavedFiles &other)
: d(other.d)
{
d = std::move(other.d);
}
UnsavedFiles &UnsavedFiles::operator=(const UnsavedFiles &other)
{
d = other.d;
return *this;
}
@@ -105,12 +106,17 @@ uint UnsavedFiles::count() const
return uint(d->unsavedFiles.size());
}
CXUnsavedFile *UnsavedFiles::cxUnsavedFiles() const
const UnsavedFile &UnsavedFiles::at(int index) const
{
return d->unsavedFiles.data()->data();
return d->unsavedFiles.at(index);
}
const time_point &UnsavedFiles::lastChangeTimePoint() const
UnsavedFilesShallowArguments UnsavedFiles::shallowArguments() const
{
return UnsavedFilesShallowArguments(*this);
}
const time_point UnsavedFiles::lastChangeTimePoint() const
{
return d->lastChangeTimePoint;
}
@@ -141,7 +147,7 @@ void UnsavedFiles::addOrUpdateUnsavedFile(const FileContainer &fileContainer)
auto unsavedFileIterator = std::find_if(d->unsavedFiles.begin(), d->unsavedFiles.end(), isSameFile);
if (unsavedFileIterator == d->unsavedFiles.end())
d->unsavedFiles.emplace_back(filePath, fileContent);
d->unsavedFiles.append(UnsavedFile(filePath, fileContent));
else
*unsavedFileIterator = UnsavedFile(filePath, fileContent);
}

View File

@@ -27,12 +27,12 @@
#include <filecontainer.h>
#include <QSharedPointer>
#include <QVector>
#include <clang-c/Index.h>
#include <chrono>
#include <memory>
namespace ClangBackEnd {
@@ -40,29 +40,28 @@ using time_point = std::chrono::steady_clock::time_point;
class UnsavedFile;
class UnsavedFilesData;
class UnsavedFilesShallowArguments;
class UnsavedFiles
{
friend class UnsavedFilesData;
public:
UnsavedFiles();
~UnsavedFiles();
UnsavedFiles(const UnsavedFiles &unsavedFiles);
UnsavedFiles &operator=(const UnsavedFiles &unsavedFiles);
UnsavedFiles(UnsavedFiles &&unsavedFiles);
UnsavedFiles &operator=(UnsavedFiles &&unsavedFiles);
UnsavedFiles(const UnsavedFiles &other);
UnsavedFiles &operator=(const UnsavedFiles &other);
void createOrUpdate(const QVector<FileContainer> &fileContainers);
void remove(const QVector<FileContainer> &fileContainers);
uint count() const;
const UnsavedFile &at(int index) const;
UnsavedFile &unsavedFile(const Utf8String &filePath);
uint count() const;
CXUnsavedFile *cxUnsavedFiles() const;
UnsavedFilesShallowArguments shallowArguments() const;
const time_point &lastChangeTimePoint() const;
const time_point lastChangeTimePoint() const;
private:
void updateUnsavedFileWithFileContainer(const FileContainer &fileContainer);
@@ -71,7 +70,7 @@ private:
void updateLastChangeTimePoint();
private:
mutable std::shared_ptr<UnsavedFilesData> d;
QSharedDataPointer<UnsavedFilesData> d;
};
} // namespace ClangBackEnd

View File

@@ -30,6 +30,7 @@
#include <projectpart.h>
#include <projects.h>
#include <clangtranslationunit.h>
#include <clangunsavedfilesshallowarguments.h>
#include <translationunits.h>
#include <unsavedfiles.h>
#include <utf8stringvector.h>
@@ -49,6 +50,7 @@ using ClangBackEnd::FilePath;
using ClangBackEnd::TranslationUnit;
using ClangBackEnd::CodeCompletion;
using ClangBackEnd::UnsavedFiles;
using ClangBackEnd::UnsavedFilesShallowArguments;
using ClangBackEnd::CodeCompletionChunk;
using ClangBackEnd::CodeCompletionChunks;
@@ -139,21 +141,13 @@ const ClangBackEnd::FileContainer unsavedDataFileContainer(const char *filePath,
true);
}
ClangCodeCompleteResults getResults(const TranslationUnit &translationUnit, uint line, uint column = 1)
{
Utf8String nativeFilePath = FilePath::toNativeSeparators(translationUnit.filePath());
return ClangCodeCompleteResults(clang_codeCompleteAt(translationUnit.cxTranslationUnit(),
nativeFilePath.constData(),
line,
column,
translationUnit.cxUnsavedFiles(),
translationUnit.unsavedFilesCount(),
CXCodeComplete_IncludeMacros | CXCodeComplete_IncludeCodePatterns));
}
class CodeCompletionsExtractor : public ::testing::Test
{
protected:
ClangCodeCompleteResults getResults(const TranslationUnit &translationUnit,
uint line,
uint column = 1);
protected:
ClangBackEnd::ProjectPart project{Utf8StringLiteral("/path/to/projectfile")};
ClangBackEnd::ProjectParts projects;
@@ -686,5 +680,18 @@ TEST_F(CodeCompletionsExtractor, BriefComment)
ASSERT_THAT(extractor, HasBriefComment(Utf8StringLiteral("BriefComment"), Utf8StringLiteral("A brief comment")));
}
ClangCodeCompleteResults CodeCompletionsExtractor::getResults(const TranslationUnit &translationUnit, uint line, uint column)
{
const Utf8String nativeFilePath = FilePath::toNativeSeparators(translationUnit.filePath());
UnsavedFilesShallowArguments unsaved = unsavedFiles.shallowArguments();
return ClangCodeCompleteResults(clang_codeCompleteAt(translationUnit.cxTranslationUnit(),
nativeFilePath.constData(),
line,
column,
unsaved.data(),
unsaved.count(),
CXCodeComplete_IncludeMacros | CXCodeComplete_IncludeCodePatterns));
}
}

View File

@@ -213,7 +213,7 @@ void CodeCompleter::SetUp()
projects.createOrUpdate({projectPart});
translationUnits.create({mainFileContainer});
translationUnit = translationUnits.translationUnit(mainFileContainer);
completer = ClangBackEnd::CodeCompleter(translationUnit);
completer = ClangBackEnd::CodeCompleter(translationUnit, unsavedFiles);
copyTargetHeaderToTemporaryIncludeDirecory();
@@ -224,6 +224,7 @@ TEST_F(CodeCompleter, FunctionInUnsavedFile)
{
unsavedFiles.createOrUpdate({unsavedMainFileContainer});
translationUnits.update({unsavedMainFileContainer});
completer = ClangBackEnd::CodeCompleter(translationUnit, unsavedFiles);
ASSERT_THAT(completer.complete(27, 1),
AllOf(Contains(IsCodeCompletion(Utf8StringLiteral("FunctionWithArguments"),
@@ -242,6 +243,7 @@ TEST_F(CodeCompleter, VariableInUnsavedFile)
{
unsavedFiles.createOrUpdate({unsavedMainFileContainer});
translationUnits.update({unsavedMainFileContainer});
completer = ClangBackEnd::CodeCompleter(translationUnit, unsavedFiles);
ASSERT_THAT(completer.complete(27, 1),
Contains(IsCodeCompletion(Utf8StringLiteral("VariableInUnsavedFile"),
@@ -252,6 +254,7 @@ TEST_F(CodeCompleter, GlobalVariableInUnsavedFile)
{
unsavedFiles.createOrUpdate({unsavedMainFileContainer});
translationUnits.update({unsavedMainFileContainer});
completer = ClangBackEnd::CodeCompleter(translationUnit, unsavedFiles);
ASSERT_THAT(completer.complete(27, 1),
Contains(IsCodeCompletion(Utf8StringLiteral("GlobalVariableInUnsavedFile"),
@@ -262,6 +265,7 @@ TEST_F(CodeCompleter, Macro)
{
unsavedFiles.createOrUpdate({unsavedMainFileContainer});
translationUnits.update({unsavedMainFileContainer});
completer = ClangBackEnd::CodeCompleter(translationUnit, unsavedFiles);
ASSERT_THAT(completer.complete(27, 1),
Contains(IsCodeCompletion(Utf8StringLiteral("Macro"),
@@ -286,6 +290,7 @@ TEST_F(CodeCompleter, FunctionInUnsavedIncludedHeader)
{
unsavedFiles.createOrUpdate({unsavedTargetHeaderFileContainer});
translationUnits.create({unsavedTargetHeaderFileContainer});
completer = ClangBackEnd::CodeCompleter(translationUnit, unsavedFiles);
ASSERT_THAT(completer.complete(27, 1),
Contains(IsCodeCompletion(Utf8StringLiteral("FunctionInIncludedHeaderUnsaved"),
@@ -305,6 +310,7 @@ TEST_F(CodeCompleter, DISABLED_FunctionInChangedIncludedHeaderWithUnsavedContent
{
unsavedFiles.createOrUpdate({unsavedMainFileContainer});
translationUnits.update({unsavedMainFileContainer});
completer = ClangBackEnd::CodeCompleter(translationUnit, unsavedFiles);
copyChangedTargetHeaderToTemporaryIncludeDirecory();
@@ -342,11 +348,14 @@ TEST_F(CodeCompleter, DotToArrowCompletionForPointer)
TEST_F(CodeCompleter, DotToArrowCompletionForPointerInOutdatedTranslationUnit)
{
auto fileContainerBeforeTyping = dotArrowCorrectionForPointerFileContainerBeforeTyping;
auto myCompleter = setupCompleter(fileContainerBeforeTyping);
translationUnits.create({fileContainerBeforeTyping});
unsavedFiles.createOrUpdate({fileContainerBeforeTyping});
auto translationUnit = translationUnits.translationUnit(fileContainerBeforeTyping.filePath(),
fileContainerBeforeTyping.projectPartId());
translationUnit.cxTranslationUnit(); // Parse
unsavedFiles.createOrUpdate({dotArrowCorrectionForPointerFileContainerAfterTyping});
ClangBackEnd::CodeCompleter myCompleter(translationUnits.translationUnit(dotArrowCorrectionForPointerFileContainerAfterTyping),
unsavedFiles);
const ClangBackEnd::CodeCompletions completions = myCompleter.complete(5, 9);
@@ -437,7 +446,8 @@ ClangBackEnd::CodeCompleter CodeCompleter::setupCompleter(
translationUnits.create({fileContainer});
unsavedFiles.createOrUpdate({fileContainer});
return ClangBackEnd::CodeCompleter(translationUnits.translationUnit(fileContainer));
return ClangBackEnd::CodeCompleter(translationUnits.translationUnit(fileContainer),
unsavedFiles);
}
}

View File

@@ -37,7 +37,6 @@
#include <thread>
using testing::ElementsAre;
using testing::StrEq;
using testing::Pointwise;
using testing::Contains;
using testing::Gt;
@@ -61,7 +60,7 @@ TEST(ProjectPart, CreateProjectPartWithProjectPartContainer)
ClangBackEnd::ProjectPart project(projectContainer);
ASSERT_THAT(project.projectPartId(), Utf8StringLiteral("pathToProjectPart.pro"));
ASSERT_THAT(project.arguments(), Contains(StrEq("-O")));
ASSERT_THAT(project.arguments(), Contains(Utf8StringLiteral("-O")));
}
TEST(ProjectPart, SetArguments)
@@ -70,7 +69,7 @@ TEST(ProjectPart, SetArguments)
project.setArguments(Utf8StringVector({Utf8StringLiteral("-O"), Utf8StringLiteral("-fast")}));
ASSERT_THAT(project.arguments(), ElementsAre(StrEq("-O"), StrEq("-fast")));
ASSERT_THAT(project.arguments(), ElementsAre(Utf8StringLiteral("-O"), Utf8StringLiteral("-fast")));
}
TEST(ProjectPart, ArgumentCount)
@@ -79,7 +78,7 @@ TEST(ProjectPart, ArgumentCount)
project.setArguments(Utf8StringVector({Utf8StringLiteral("-O"), Utf8StringLiteral("-fast")}));
ASSERT_THAT(project.argumentCount(), 2);
ASSERT_THAT(project.arguments().count(), 2);
}
TEST(ProjectPart, TimeStampIsUpdatedAsArgumentChanged)
@@ -109,7 +108,7 @@ TEST(ProjectPart, AddProjectParts)
projects.createOrUpdate({projectContainer});
ASSERT_THAT(projects.project(projectContainer.projectPartId()), ClangBackEnd::ProjectPart(projectContainer));
ASSERT_THAT(projects.project(projectContainer.projectPartId()).arguments(), ElementsAre(StrEq("-O")));
ASSERT_THAT(projects.project(projectContainer.projectPartId()).arguments(), ElementsAre(Utf8StringLiteral("-O")));
}
TEST(ProjectPart, UpdateProjectParts)
@@ -122,7 +121,7 @@ TEST(ProjectPart, UpdateProjectParts)
projects.createOrUpdate({projectContainerWithNewArguments});
ASSERT_THAT(projects.project(projectContainer.projectPartId()), ClangBackEnd::ProjectPart(projectContainer));
ASSERT_THAT(projects.project(projectContainer.projectPartId()).arguments(), ElementsAre(StrEq("-fast")));
ASSERT_THAT(projects.project(projectContainer.projectPartId()).arguments(), ElementsAre(Utf8StringLiteral("-fast")));
}
TEST(ProjectPart, ThrowExceptionForAccesingRemovedProjectParts)
@@ -172,7 +171,7 @@ TEST(ProjectPart, ProjectPartIsClearedAfterRemove)
projects.remove({projectContainer.projectPartId()});
ASSERT_THAT(project.projectPartId(), Utf8String());
ASSERT_THAT(project.argumentCount(), 0);
ASSERT_THAT(project.arguments().count(), 0);
ASSERT_THAT(project.lastChangeTimePoint(), Gt(lastChangeTimePoint));
}

View File

@@ -38,24 +38,27 @@ using ClangBackEnd::UnsavedFile;
using ClangBackEnd::UnsavedFiles;
using ClangBackEnd::FileContainer;
using ::testing::Eq;
using ::testing::Gt;
using ::testing::IsNull;
using ::testing::Ne;
using ::testing::NotNull;
using ::testing::PrintToString;
namespace {
bool operator==(const ClangBackEnd::FileContainer &fileContainer, const CXUnsavedFile &cxUnsavedFile)
bool operator==(const ClangBackEnd::FileContainer &fileContainer, const UnsavedFile &unsavedFile)
{
return fileContainer.filePath() == Utf8String::fromUtf8(cxUnsavedFile.Filename)
&& fileContainer.unsavedFileContent() == Utf8String(cxUnsavedFile.Contents, cxUnsavedFile.Length);
return fileContainer.filePath() == unsavedFile.filePath()
&& fileContainer.unsavedFileContent() == unsavedFile.fileContent();
}
bool fileContainersContainsItemMatchingToCxUnsavedFile(const QVector<FileContainer> &fileContainers, const CXUnsavedFile &cxUnsavedFile)
bool fileContainersContainsItemMatchingToCxUnsavedFile(const QVector<FileContainer> &fileContainers, const UnsavedFile &unsavedFile)
{
for (const FileContainer &fileContainer : fileContainers)
if (fileContainer == cxUnsavedFile)
for (const FileContainer &fileContainer : fileContainers) {
if (fileContainer == unsavedFile)
return true;
}
return false;
}
@@ -69,8 +72,8 @@ MATCHER_P(HasUnsavedFiles, fileContainers, "")
}
for (uint i = 0, to = unsavedFiles.count(); i < to; ++i) {
CXUnsavedFile *cxUnsavedFile = unsavedFiles.cxUnsavedFiles() + i;
if (!fileContainersContainsItemMatchingToCxUnsavedFile(fileContainers, *cxUnsavedFile))
UnsavedFile unsavedFile = unsavedFiles.at(i);
if (!fileContainersContainsItemMatchingToCxUnsavedFile(fileContainers, unsavedFile))
return false;
}
@@ -83,11 +86,9 @@ MATCHER_P3(IsUnsavedFile, fileName, contents, contentsLength,
+ ", contents " + PrintToString(contents)
+ ", contents length " + PrintToString(contentsLength))
{
CXUnsavedFile unsavedFile = arg.cxUnsavedFile;
return fileName == unsavedFile.Filename
&& contents == unsavedFile.Contents
&& size_t(contentsLength) == unsavedFile.Length;
return fileName == arg.filePath()
&& contents == arg.fileContent()
&& int(contentsLength) == arg.fileContent().byteSize();
}
class UnsavedFiles : public ::testing::Test
@@ -101,6 +102,19 @@ protected:
Utf8String unsavedContent2{Utf8StringLiteral("bar")};
};
TEST_F(UnsavedFiles, ModifiedCopyIsDifferent)
{
QVector<FileContainer> fileContainers({FileContainer(filePath, projectPartId, unsavedContent1, true)});
unsavedFiles.createOrUpdate(fileContainers);
::UnsavedFiles copy = unsavedFiles;
QVector<FileContainer> updatedFileContainers({FileContainer(filePath, projectPartId, unsavedContent2, true)});
copy.createOrUpdate(updatedFileContainers);
ASSERT_THAT(copy.at(0).fileContent(), Ne(unsavedFiles.at(0).fileContent()));
ASSERT_THAT(copy.lastChangeTimePoint(), Gt(unsavedFiles.lastChangeTimePoint()));
}
TEST_F(UnsavedFiles, DoNothingForUpdateIfFileHasNoUnsavedContent)
{
QVector<FileContainer> fileContainers({FileContainer(filePath, projectPartId)});

View File

@@ -47,11 +47,9 @@ MATCHER_P3(IsUnsavedFile, fileName, contents, contentsLength,
+ ", contents " + PrintToString(contents)
+ ", contents length " + PrintToString(contentsLength))
{
CXUnsavedFile unsavedFile = arg.cxUnsavedFile;
return fileName == unsavedFile.Filename
&& contents == unsavedFile.Contents
&& size_t(contentsLength) == unsavedFile.Length;
return fileName == arg.filePath()
&& contents == arg.fileContent()
&& int(contentsLength) == arg.fileContent().byteSize();
}
class UnsavedFile : public ::testing::Test
@@ -78,58 +76,17 @@ TEST_F(UnsavedFile, Create)
fileContent.byteSize()));
}
TEST_F(UnsavedFile, Destruct)
TEST_F(UnsavedFile, CopyConstruct)
{
auto *unsavedFile = new ::UnsavedFile(filePath, fileContent);
unsavedFile->~UnsavedFile();
::UnsavedFile copyFrom(filePath, fileContent);
ASSERT_THAT(*unsavedFile, IsUnsavedFile(nullptr, nullptr, 0UL));
}
::UnsavedFile copyTo = copyFrom;
TEST_F(UnsavedFile, ConstructMoveToIsValid)
{
::UnsavedFile movedFrom(filePath, fileContent);
::UnsavedFile movedTo = std::move(movedFrom);
ASSERT_THAT(movedTo, IsUnsavedFile(filePath,
ASSERT_THAT(copyTo, IsUnsavedFile(filePath,
fileContent,
fileContent.byteSize()));
}
TEST_F(UnsavedFile, ConstructMoveFromIsNull)
{
::UnsavedFile movedFrom(filePath, fileContent);
::UnsavedFile movedTo = std::move(movedFrom);
ASSERT_THAT(movedFrom, IsUnsavedFile(nullptr, nullptr, 0UL));
}
TEST_F(UnsavedFile, AssignMoveToIsValid)
{
::UnsavedFile movedFrom(filePath, fileContent);
::UnsavedFile movedTo(otherFilePath, otherFileContent);
movedTo = std::move(movedFrom);
ASSERT_THAT(movedTo, IsUnsavedFile(filePath,
fileContent,
fileContent.byteSize()));
}
TEST_F(UnsavedFile, AssignMoveFromIsSwapped)
{
::UnsavedFile movedFrom(filePath, fileContent);
::UnsavedFile movedTo(otherFilePath, otherFileContent);
movedTo = std::move(movedFrom);
ASSERT_THAT(movedFrom, IsUnsavedFile(otherFilePath,
otherFileContent,
otherFileContent.byteSize()));
}
TEST_F(UnsavedFile, FilePath)
{
::UnsavedFile unsavedFile(absoluteFilePath, QStringLiteral(""));
@@ -208,6 +165,19 @@ TEST_F(UnsavedFile, HasNoCharacterForDefaultConstructedUnsavedFile)
ASSERT_FALSE(unsavedFile.hasCharacterAt(0, 'x'));
}
TEST_F(UnsavedFile, ReplacingInCopyDoesNotModifyOriginal)
{
const Utf8String originalContent = Utf8StringLiteral("foo");
::UnsavedFile original(filePath, originalContent);
::UnsavedFile copy = original;
const bool hasReplaced = copy.replaceAt(0, 3, aReplacement);
ASSERT_TRUE(hasReplaced);
ASSERT_THAT(original, IsUnsavedFile(filePath, originalContent, originalContent.byteSize()));
ASSERT_THAT(copy, IsUnsavedFile(filePath, aReplacement, aReplacement.byteSize()));
}
TEST_F(UnsavedFile, HasNoCharacterForTooBigOffset)
{
::UnsavedFile unsavedFile(filePath, fileContent);