aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/libs/clangbackendipc/clangbackendipc_global.h6
-rw-r--r--src/libs/clangbackendipc/cmbcodecompletedmessage.cpp37
-rw-r--r--src/libs/clangbackendipc/cmbcodecompletedmessage.h9
-rw-r--r--src/libs/sqlite/utf8string.cpp5
-rw-r--r--src/libs/sqlite/utf8string.h1
-rw-r--r--src/plugins/clangcodemodel/clangassistproposal.cpp5
-rw-r--r--src/plugins/clangcodemodel/clangassistproposalmodel.cpp11
-rw-r--r--src/plugins/clangcodemodel/clangassistproposalmodel.h9
-rw-r--r--src/plugins/clangcodemodel/clangbackendipcintegration.cpp4
-rw-r--r--src/plugins/clangcodemodel/clangcompletionassistprocessor.cpp16
-rw-r--r--src/plugins/clangcodemodel/clangcompletionassistprocessor.h10
-rw-r--r--src/plugins/clangcodemodel/test/clangcodecompletion_test.cpp22
-rw-r--r--src/plugins/clangcodemodel/test/clangcodecompletion_test.h2
-rw-r--r--src/plugins/clangcodemodel/test/data/clangtestdata.qrc1
-rw-r--r--src/plugins/clangcodemodel/test/data/dotToArrowCorrection.cpp5
-rw-r--r--src/tools/clangbackend/ipcsource/clangbackendclangipc-source.pri6
-rw-r--r--src/tools/clangbackend/ipcsource/clangcodecompleteresults.cpp22
-rw-r--r--src/tools/clangbackend/ipcsource/clangcodecompleteresults.h8
-rw-r--r--src/tools/clangbackend/ipcsource/clangipcserver.cpp4
-rw-r--r--src/tools/clangbackend/ipcsource/clangtranslationunit.cpp10
-rw-r--r--src/tools/clangbackend/ipcsource/clangtranslationunit.h3
-rw-r--r--src/tools/clangbackend/ipcsource/codecompleter.cpp86
-rw-r--r--src/tools/clangbackend/ipcsource/codecompleter.h12
-rw-r--r--src/tools/clangbackend/ipcsource/sourcelocation.cpp1
-rw-r--r--src/tools/clangbackend/ipcsource/temporarymodifiedunsavedfiles.cpp87
-rw-r--r--src/tools/clangbackend/ipcsource/temporarymodifiedunsavedfiles.h66
-rw-r--r--tests/unit/unittest/clientserverinprocesstest.cpp4
-rw-r--r--tests/unit/unittest/codecompletiontest.cpp151
-rw-r--r--tests/unit/unittest/data/complete_arrow.cpp6
-rw-r--r--tests/unit/unittest/data/complete_withDotArrowCorrectionForPointer.cpp6
-rw-r--r--tests/unit/unittest/data/complete_withNoDotArrowCorrectionForArrowDot.cpp7
-rw-r--r--tests/unit/unittest/data/complete_withNoDotArrowCorrectionForDotDot.cpp7
-rw-r--r--tests/unit/unittest/data/complete_withNoDotArrowCorrectionForFloat.cpp4
-rw-r--r--tests/unit/unittest/data/complete_withNoDotArrowCorrectionForObject.cpp6
-rw-r--r--tests/unit/unittest/data/complete_withNoDotArrowCorrectionForObjectWithArrowOperator.cpp9
-rw-r--r--tests/unit/unittest/data/complete_withNoDotArrowCorrectionForOnlyDot.cpp7
-rw-r--r--tests/unit/unittest/readandwritemessageblocktest.cpp11
-rw-r--r--tests/unit/unittest/temporarymodifiedunsavedfilestest.cpp106
-rw-r--r--tests/unit/unittest/unittest.pro3
-rw-r--r--tests/unit/unittest/utf8test.cpp9
40 files changed, 747 insertions, 37 deletions
diff --git a/src/libs/clangbackendipc/clangbackendipc_global.h b/src/libs/clangbackendipc/clangbackendipc_global.h
index 3efaf48c3c3..f3e97e5f32e 100644
--- a/src/libs/clangbackendipc/clangbackendipc_global.h
+++ b/src/libs/clangbackendipc/clangbackendipc_global.h
@@ -78,5 +78,11 @@ enum class HighlightingType
OutputArgument
};
+enum class CompletionCorrection
+{
+ NoCorrection,
+ DotToArrowCorrection
+};
+
}
#endif // CLANGBACKENDIPC_GLOBAL_H
diff --git a/src/libs/clangbackendipc/cmbcodecompletedmessage.cpp b/src/libs/clangbackendipc/cmbcodecompletedmessage.cpp
index ca584306886..6b2d5b52ddc 100644
--- a/src/libs/clangbackendipc/cmbcodecompletedmessage.cpp
+++ b/src/libs/clangbackendipc/cmbcodecompletedmessage.cpp
@@ -37,9 +37,12 @@
namespace ClangBackEnd {
-CodeCompletedMessage::CodeCompletedMessage(const CodeCompletions &codeCompletions, quint64 ticketNumber)
+CodeCompletedMessage::CodeCompletedMessage(const CodeCompletions &codeCompletions,
+ CompletionCorrection neededCorrection,
+ quint64 ticketNumber)
: codeCompletions_(codeCompletions),
- ticketNumber_(ticketNumber)
+ ticketNumber_(ticketNumber),
+ neededCorrection_(neededCorrection)
{
}
@@ -48,14 +51,25 @@ const CodeCompletions &CodeCompletedMessage::codeCompletions() const
return codeCompletions_;
}
+CompletionCorrection CodeCompletedMessage::neededCorrection() const
+{
+ return neededCorrection_;
+}
+
quint64 CodeCompletedMessage::ticketNumber() const
{
return ticketNumber_;
}
+quint32 &CodeCompletedMessage::neededCorrectionAsInt()
+{
+ return reinterpret_cast<quint32&>(neededCorrection_);
+}
+
QDataStream &operator<<(QDataStream &out, const CodeCompletedMessage &message)
{
out << message.codeCompletions_;
+ out << quint32(message.neededCorrection_);
out << message.ticketNumber_;
return out;
@@ -64,6 +78,7 @@ QDataStream &operator<<(QDataStream &out, const CodeCompletedMessage &message)
QDataStream &operator>>(QDataStream &in, CodeCompletedMessage &message)
{
in >> message.codeCompletions_;
+ in >> message.neededCorrectionAsInt();
in >> message.ticketNumber_;
return in;
@@ -72,7 +87,8 @@ QDataStream &operator>>(QDataStream &in, CodeCompletedMessage &message)
bool operator==(const CodeCompletedMessage &first, const CodeCompletedMessage &second)
{
return first.ticketNumber_ == second.ticketNumber_
- && first.codeCompletions_ == second.codeCompletions_;
+ && first.neededCorrection_ == second.neededCorrection_
+ && first.codeCompletions_ == second.codeCompletions_;
}
bool operator<(const CodeCompletedMessage &first, const CodeCompletedMessage &second)
@@ -80,11 +96,24 @@ bool operator<(const CodeCompletedMessage &first, const CodeCompletedMessage &se
return first.ticketNumber_ < second.ticketNumber_;
}
+#define RETURN_TEXT_FOR_CASE(enumValue) case CompletionCorrection::enumValue: return #enumValue
+static const char *completionCorrectionToText(CompletionCorrection correction)
+{
+ switch (correction) {
+ RETURN_TEXT_FOR_CASE(NoCorrection);
+ RETURN_TEXT_FOR_CASE(DotToArrowCorrection);
+ default: return "UnhandledCompletionCorrection";
+ }
+}
+#undef RETURN_TEXT_FOR_CASE
+
QDebug operator<<(QDebug debug, const CodeCompletedMessage &message)
{
debug.nospace() << "CodeCompletedMessage(";
- debug.nospace() << message.codeCompletions_ << ", " << message.ticketNumber_;
+ debug.nospace() << message.codeCompletions_ << ", "
+ << completionCorrectionToText(message.neededCorrection()) << ", "
+ << message.ticketNumber_;
debug.nospace() << ")";
diff --git a/src/libs/clangbackendipc/cmbcodecompletedmessage.h b/src/libs/clangbackendipc/cmbcodecompletedmessage.h
index 69d4e11bc1d..a215bc01cd2 100644
--- a/src/libs/clangbackendipc/cmbcodecompletedmessage.h
+++ b/src/libs/clangbackendipc/cmbcodecompletedmessage.h
@@ -48,15 +48,22 @@ class CMBIPC_EXPORT CodeCompletedMessage
friend void PrintTo(const CodeCompletedMessage &message, ::std::ostream* os);
public:
CodeCompletedMessage() = default;
- CodeCompletedMessage(const CodeCompletions &codeCompletions, quint64 ticketNumber);
+ CodeCompletedMessage(const CodeCompletions &codeCompletions,
+ CompletionCorrection neededCorrection,
+ quint64 ticketNumber);
const CodeCompletions &codeCompletions() const;
+ CompletionCorrection neededCorrection() const;
quint64 ticketNumber() const;
private:
+ quint32 &neededCorrectionAsInt();
+
+private:
CodeCompletions codeCompletions_;
quint64 ticketNumber_ = 0;
+ CompletionCorrection neededCorrection_ = CompletionCorrection::NoCorrection;
};
CMBIPC_EXPORT QDataStream &operator<<(QDataStream &out, const CodeCompletedMessage &message);
diff --git a/src/libs/sqlite/utf8string.cpp b/src/libs/sqlite/utf8string.cpp
index 9fb92b5d39d..d032b775ea9 100644
--- a/src/libs/sqlite/utf8string.cpp
+++ b/src/libs/sqlite/utf8string.cpp
@@ -103,6 +103,11 @@ void Utf8String::replace(const Utf8String &before, const Utf8String &after)
byteArray.replace(before.byteArray, after.byteArray);
}
+void Utf8String::replace(int position, int length, const Utf8String &after)
+{
+ byteArray.replace(position, length, after.byteArray);
+}
+
Utf8StringVector Utf8String::split(char separator) const
{
Utf8StringVector utf8Vector;
diff --git a/src/libs/sqlite/utf8string.h b/src/libs/sqlite/utf8string.h
index 6158f8b690d..2d7ff32a772 100644
--- a/src/libs/sqlite/utf8string.h
+++ b/src/libs/sqlite/utf8string.h
@@ -76,6 +76,7 @@ public:
Utf8String mid(int position, int length = -1) const;
void replace(const Utf8String &before, const Utf8String &after);
+ void replace(int position, int length, const Utf8String &after);
Utf8StringVector split(char separator) const;
void clear();
diff --git a/src/plugins/clangcodemodel/clangassistproposal.cpp b/src/plugins/clangcodemodel/clangassistproposal.cpp
index 6904f9abfcb..812dbd8313f 100644
--- a/src/plugins/clangcodemodel/clangassistproposal.cpp
+++ b/src/plugins/clangcodemodel/clangassistproposal.cpp
@@ -42,7 +42,10 @@ ClangAssistProposal::ClangAssistProposal(int cursorPos, TextEditor::GenericPropo
bool ClangAssistProposal::isCorrective() const
{
- return false;
+ auto clangAssistProposalModel = static_cast<ClangAssistProposalModel*>(model());
+
+ return clangAssistProposalModel->neededCorrection()
+ == ClangBackEnd::CompletionCorrection::DotToArrowCorrection;
}
void ClangAssistProposal::makeCorrection(TextEditor::TextEditorWidget *editorWidget)
diff --git a/src/plugins/clangcodemodel/clangassistproposalmodel.cpp b/src/plugins/clangcodemodel/clangassistproposalmodel.cpp
index d4085b39298..1092f062b8d 100644
--- a/src/plugins/clangcodemodel/clangassistproposalmodel.cpp
+++ b/src/plugins/clangcodemodel/clangassistproposalmodel.cpp
@@ -39,6 +39,12 @@
namespace ClangCodeModel {
namespace Internal {
+ClangAssistProposalModel::ClangAssistProposalModel(
+ ClangBackEnd::CompletionCorrection neededCorrection)
+ : m_neededCorrection(neededCorrection)
+{
+}
+
bool ClangAssistProposalModel::isSortable(const QString &/*prefix*/) const
{
return true;
@@ -57,6 +63,11 @@ void ClangAssistProposalModel::sort(const QString &/*prefix*/)
std::sort(m_currentItems.begin(), m_currentItems.end(), currentItemsCompare);
}
+ClangBackEnd::CompletionCorrection ClangAssistProposalModel::neededCorrection() const
+{
+ return m_neededCorrection;
+}
+
} // namespace Internal
} // namespace ClangCodeModel
diff --git a/src/plugins/clangcodemodel/clangassistproposalmodel.h b/src/plugins/clangcodemodel/clangassistproposalmodel.h
index 5f7b1f502a0..2e2db193a1f 100644
--- a/src/plugins/clangcodemodel/clangassistproposalmodel.h
+++ b/src/plugins/clangcodemodel/clangassistproposalmodel.h
@@ -35,14 +35,23 @@
#include <texteditor/codeassist/genericproposalmodel.h>
+#include <clangbackendipc/clangbackendipc_global.h>
+
namespace ClangCodeModel {
namespace Internal {
class ClangAssistProposalModel : public TextEditor::GenericProposalModel
{
public:
+ ClangAssistProposalModel(ClangBackEnd::CompletionCorrection neededCorrection);
+
bool isSortable(const QString &prefix) const override;
void sort(const QString &prefix) override;
+
+ ClangBackEnd::CompletionCorrection neededCorrection() const;
+
+private:
+ ClangBackEnd::CompletionCorrection m_neededCorrection;
};
} // namespace Internal
diff --git a/src/plugins/clangcodemodel/clangbackendipcintegration.cpp b/src/plugins/clangcodemodel/clangbackendipcintegration.cpp
index cf4a5e1bb70..cc0935551ec 100644
--- a/src/plugins/clangcodemodel/clangbackendipcintegration.cpp
+++ b/src/plugins/clangcodemodel/clangbackendipcintegration.cpp
@@ -163,7 +163,9 @@ void IpcReceiver::codeCompleted(const CodeCompletedMessage &message)
const quint64 ticket = message.ticketNumber();
QScopedPointer<ClangCompletionAssistProcessor> processor(m_assistProcessorsTable.take(ticket));
if (processor) {
- const bool finished = processor->handleAvailableAsyncCompletions(message.codeCompletions());
+ const bool finished = processor->handleAvailableAsyncCompletions(
+ message.codeCompletions(),
+ message.neededCorrection());
if (!finished)
processor.take();
}
diff --git a/src/plugins/clangcodemodel/clangcompletionassistprocessor.cpp b/src/plugins/clangcodemodel/clangcompletionassistprocessor.cpp
index 284e9fe3d7e..ed01da7239f 100644
--- a/src/plugins/clangcodemodel/clangcompletionassistprocessor.cpp
+++ b/src/plugins/clangcodemodel/clangcompletionassistprocessor.cpp
@@ -240,13 +240,14 @@ IAssistProposal *ClangCompletionAssistProcessor::perform(const AssistInterface *
}
bool ClangCompletionAssistProcessor::handleAvailableAsyncCompletions(
- const CodeCompletions &completions)
+ const CodeCompletions &completions,
+ CompletionCorrection neededCorrection)
{
bool handled = true;
switch (m_sentRequestType) {
case CompletionRequestType::NormalCompletion:
- handleAvailableCompletions(completions);
+ handleAvailableCompletions(completions, neededCorrection);
break;
case CompletionRequestType::FunctionHintCompletion:
handled = handleAvailableFunctionHintCompletions(completions);
@@ -775,14 +776,17 @@ bool ClangCompletionAssistProcessor::sendCompletionRequest(int position,
return false;
}
-TextEditor::IAssistProposal *ClangCompletionAssistProcessor::createProposal() const
+TextEditor::IAssistProposal *ClangCompletionAssistProcessor::createProposal(
+ CompletionCorrection neededCorrection) const
{
- ClangAssistProposalModel *model = new ClangAssistProposalModel;
+ ClangAssistProposalModel *model = new ClangAssistProposalModel(neededCorrection);
model->loadContent(m_completions);
return new ClangAssistProposal(m_positionForProposal, model);
}
-void ClangCompletionAssistProcessor::handleAvailableCompletions(const CodeCompletions &completions)
+void ClangCompletionAssistProcessor::handleAvailableCompletions(
+ const CodeCompletions &completions,
+ CompletionCorrection neededCorrection)
{
QTC_CHECK(m_completions.isEmpty());
@@ -790,7 +794,7 @@ void ClangCompletionAssistProcessor::handleAvailableCompletions(const CodeComple
if (m_addSnippets)
addSnippets();
- setAsyncProposalAvailable(createProposal());
+ setAsyncProposalAvailable(createProposal(neededCorrection));
}
bool ClangCompletionAssistProcessor::handleAvailableFunctionHintCompletions(
diff --git a/src/plugins/clangcodemodel/clangcompletionassistprocessor.h b/src/plugins/clangcodemodel/clangcompletionassistprocessor.h
index f4aee3618d0..bd4fd36d381 100644
--- a/src/plugins/clangcodemodel/clangcompletionassistprocessor.h
+++ b/src/plugins/clangcodemodel/clangcompletionassistprocessor.h
@@ -43,6 +43,7 @@ namespace ClangCodeModel {
namespace Internal {
using ClangBackEnd::CodeCompletions;
+using ClangBackEnd::CompletionCorrection;
class ClangCompletionAssistProcessor : public CppTools::CppCompletionAssistProcessor
{
@@ -54,7 +55,8 @@ public:
TextEditor::IAssistProposal *perform(const TextEditor::AssistInterface *interface) override;
- bool handleAvailableAsyncCompletions(const CodeCompletions &completions);
+ bool handleAvailableAsyncCompletions(const CodeCompletions &completions,
+ CompletionCorrection neededCorrection);
const TextEditor::TextEditorWidget *textEditorWidget() const;
@@ -64,7 +66,8 @@ private:
int findStartOfName(int pos = -1) const;
bool accepts() const;
- TextEditor::IAssistProposal *createProposal() const;
+ TextEditor::IAssistProposal *createProposal(
+ CompletionCorrection neededCorrection = CompletionCorrection::NoCorrection) const;
bool completeInclude(const QTextCursor &cursor);
bool completeInclude(int position);
@@ -85,7 +88,8 @@ private:
void sendFileContent(const QByteArray &customFileContent);
bool sendCompletionRequest(int position, const QByteArray &customFileContent);
- void handleAvailableCompletions(const CodeCompletions &completions);
+ void handleAvailableCompletions(const CodeCompletions &completions,
+ CompletionCorrection neededCorrection);
bool handleAvailableFunctionHintCompletions(const CodeCompletions &completions);
private:
diff --git a/src/plugins/clangcodemodel/test/clangcodecompletion_test.cpp b/src/plugins/clangcodemodel/test/clangcodecompletion_test.cpp
index e1dd36ccbb3..27f5be09c58 100644
--- a/src/plugins/clangcodemodel/test/clangcodecompletion_test.cpp
+++ b/src/plugins/clangcodemodel/test/clangcodecompletion_test.cpp
@@ -689,6 +689,7 @@ class ProjectLessCompletionTest
{
public:
ProjectLessCompletionTest(const QByteArray &testFileName,
+ const QString &textToInsert = QString(),
const QStringList &includePaths = QStringList())
{
CppTools::Tests::TestCase garbageCollectionGlobalSnapshot;
@@ -697,8 +698,11 @@ public:
const TestDocument testDocument(testFileName, globalTemporaryDir());
QVERIFY(testDocument.isCreatedAndHasValidCursorPosition());
OpenEditorAtCursorPosition openEditor(testDocument);
-
QVERIFY(openEditor.succeeded());
+
+ if (!textToInsert.isEmpty())
+ openEditor.editor()->insert(textToInsert);
+
proposal = completionResults(openEditor.editor(), includePaths);
}
@@ -880,7 +884,9 @@ void ClangCodeCompletionTest::testCompletePreprocessorKeywords()
void ClangCodeCompletionTest::testCompleteIncludeDirective()
{
CppTools::Tests::TemporaryCopiedDir testDir(qrcPath("exampleIncludeDir"));
- ProjectLessCompletionTest t("includeDirectiveCompletion.cpp", QStringList(testDir.path()));
+ ProjectLessCompletionTest t("includeDirectiveCompletion.cpp",
+ QString(),
+ QStringList(testDir.path()));
QVERIFY(hasItem(t.proposal, "file.h"));
QVERIFY(hasItem(t.proposal, "otherFile.h"));
@@ -931,6 +937,18 @@ void ClangCodeCompletionTest::testCompleteConstructorAndFallbackToGlobalCompleti
QVERIFY(!hasSnippet(t.proposal, "class"));
}
+void ClangCodeCompletionTest::testCompleteWithDotToArrowCorrection()
+{
+ // Inserting the dot for this test is important since it will send the editor
+ // content to the backend and thus generate an unsaved file on the backend
+ // side. The unsaved file enables us to do the dot to arrow correction.
+
+ ProjectLessCompletionTest t("dotToArrowCorrection.cpp",
+ QStringLiteral("."));
+
+ QVERIFY(hasItem(t.proposal, "member"));
+}
+
void ClangCodeCompletionTest::testCompleteProjectDependingCode()
{
const TestDocument testDocument("completionWithProject.cpp");
diff --git a/src/plugins/clangcodemodel/test/clangcodecompletion_test.h b/src/plugins/clangcodemodel/test/clangcodecompletion_test.h
index 3334475505d..3449fd32ff4 100644
--- a/src/plugins/clangcodemodel/test/clangcodecompletion_test.h
+++ b/src/plugins/clangcodemodel/test/clangcodecompletion_test.h
@@ -53,6 +53,8 @@ private slots:
void testCompleteFunctions();
void testCompleteConstructorAndFallbackToGlobalCompletion();
+ void testCompleteWithDotToArrowCorrection();
+
void testCompleteProjectDependingCode();
void testCompleteProjectDependingCodeAfterChangingProject();
void testCompleteProjectDependingCodeInGeneratedUiFile();
diff --git a/src/plugins/clangcodemodel/test/data/clangtestdata.qrc b/src/plugins/clangcodemodel/test/data/clangtestdata.qrc
index 429b59eae8d..c013ccd7f40 100644
--- a/src/plugins/clangcodemodel/test/data/clangtestdata.qrc
+++ b/src/plugins/clangcodemodel/test/data/clangtestdata.qrc
@@ -21,5 +21,6 @@
<file>objc_messages_2.mm</file>
<file>objc_messages_3.mm</file>
<file>preprocessorKeywordsCompletion.cpp</file>
+ <file>dotToArrowCorrection.cpp</file>
</qresource>
</RCC>
diff --git a/src/plugins/clangcodemodel/test/data/dotToArrowCorrection.cpp b/src/plugins/clangcodemodel/test/data/dotToArrowCorrection.cpp
new file mode 100644
index 00000000000..27993974381
--- /dev/null
+++ b/src/plugins/clangcodemodel/test/data/dotToArrowCorrection.cpp
@@ -0,0 +1,5 @@
+struct Bar { int member; };
+void f(Bar *bar)
+{
+ bar /* COMPLETE HERE */
+}
diff --git a/src/tools/clangbackend/ipcsource/clangbackendclangipc-source.pri b/src/tools/clangbackend/ipcsource/clangbackendclangipc-source.pri
index 1724c5799c2..632d687ccbd 100644
--- a/src/tools/clangbackend/ipcsource/clangbackendclangipc-source.pri
+++ b/src/tools/clangbackend/ipcsource/clangbackendclangipc-source.pri
@@ -31,7 +31,8 @@ HEADERS += $$PWD/clangipcserver.h \
$$PWD/highlightinginformationsiterator.h \
$$PWD/skippedsourceranges.h \
$$PWD/clangtranslationunit.h \
- $$PWD/clangtype.h
+ $$PWD/clangtype.h \
+ $$PWD/temporarymodifiedunsavedfiles.h
SOURCES += $$PWD/clangipcserver.cpp \
$$PWD/codecompleter.cpp \
@@ -62,4 +63,5 @@ SOURCES += $$PWD/clangipcserver.cpp \
$$PWD/highlightinginformation.cpp \
$$PWD/skippedsourceranges.cpp \
$$PWD/clangtranslationunit.cpp \
- $$PWD/clangtype.cpp
+ $$PWD/clangtype.cpp \
+ $$PWD/temporarymodifiedunsavedfiles.cpp
diff --git a/src/tools/clangbackend/ipcsource/clangcodecompleteresults.cpp b/src/tools/clangbackend/ipcsource/clangcodecompleteresults.cpp
index 210a6a71992..98c5f8f1a3f 100644
--- a/src/tools/clangbackend/ipcsource/clangcodecompleteresults.cpp
+++ b/src/tools/clangbackend/ipcsource/clangcodecompleteresults.cpp
@@ -51,6 +51,28 @@ bool ClangCodeCompleteResults::isNull() const
return cxCodeCompleteResults == nullptr;
}
+bool ClangCodeCompleteResults::isEmpty() const
+{
+ return cxCodeCompleteResults->NumResults == 0;
+}
+
+bool ClangCodeCompleteResults::hasResults() const
+{
+ return !isNull() && !isEmpty();
+}
+
+bool ClangCodeCompleteResults::hasNoResultsForDotCompletion() const
+{
+ return !hasResults() && isDotCompletion();
+}
+
+bool ClangCodeCompleteResults::isDotCompletion() const
+{
+ const unsigned long long contexts = clang_codeCompleteGetContexts(cxCodeCompleteResults);
+
+ return contexts & CXCompletionContext_DotMemberAccess;
+}
+
CXCodeCompleteResults *ClangCodeCompleteResults::data() const
{
return cxCodeCompleteResults;
diff --git a/src/tools/clangbackend/ipcsource/clangcodecompleteresults.h b/src/tools/clangbackend/ipcsource/clangcodecompleteresults.h
index 12b38d84bfe..5afdc7af149 100644
--- a/src/tools/clangbackend/ipcsource/clangcodecompleteresults.h
+++ b/src/tools/clangbackend/ipcsource/clangcodecompleteresults.h
@@ -40,6 +40,8 @@ namespace ClangBackEnd {
class ClangCodeCompleteResults
{
public:
+ ClangCodeCompleteResults() = default;
+
ClangCodeCompleteResults(CXCodeCompleteResults *cxCodeCompleteResults);
~ClangCodeCompleteResults();
@@ -50,6 +52,12 @@ public:
ClangCodeCompleteResults &operator=(ClangCodeCompleteResults &&ClangCodeCompleteResults);
bool isNull() const;
+ bool isEmpty() const;
+
+ bool hasResults() const;
+ bool hasNoResultsForDotCompletion() const;
+
+ bool isDotCompletion() const;
CXCodeCompleteResults *data() const;
diff --git a/src/tools/clangbackend/ipcsource/clangipcserver.cpp b/src/tools/clangbackend/ipcsource/clangipcserver.cpp
index ac04f932030..476471b1cb8 100644
--- a/src/tools/clangbackend/ipcsource/clangipcserver.cpp
+++ b/src/tools/clangbackend/ipcsource/clangipcserver.cpp
@@ -253,7 +253,9 @@ void ClangIpcServer::completeCode(const ClangBackEnd::CompleteCodeMessage &messa
const auto codeCompletions = codeCompleter.complete(message.line(), message.column());
- client()->codeCompleted(CodeCompletedMessage(codeCompletions, message.ticketNumber()));
+ client()->codeCompleted(CodeCompletedMessage(codeCompletions,
+ codeCompleter.neededCorrection(),
+ message.ticketNumber()));
} catch (const TranslationUnitDoesNotExistException &exception) {
client()->translationUnitDoesNotExist(TranslationUnitDoesNotExistMessage(exception.fileContainer()));
} catch (const ProjectPartDoNotExistException &exception) {
diff --git a/src/tools/clangbackend/ipcsource/clangtranslationunit.cpp b/src/tools/clangbackend/ipcsource/clangtranslationunit.cpp
index 4954cd79048..4534ce81e08 100644
--- a/src/tools/clangbackend/ipcsource/clangtranslationunit.cpp
+++ b/src/tools/clangbackend/ipcsource/clangtranslationunit.cpp
@@ -480,6 +480,11 @@ CommandLineArguments TranslationUnit::commandLineArguments() const
isVerboseModeEnabled());
}
+SourceLocation TranslationUnit::sourceLocationAtWithoutReparsing(uint line, uint column) const
+{
+ return SourceLocation(cxTranslationUnitWithoutReparsing(), filePath(), line, column);
+}
+
uint TranslationUnit::defaultOptions()
{
return CXTranslationUnit_CacheCompletionResults
@@ -498,6 +503,11 @@ CXUnsavedFile *TranslationUnit::cxUnsavedFiles() const
return unsavedFiles().cxUnsavedFiles();
}
+const std::vector<CXUnsavedFile> &TranslationUnit::cxUnsavedFilesVector() const
+{
+ return unsavedFiles().cxUnsavedFileVector();
+}
+
TranslationUnit::~TranslationUnit() = default;
TranslationUnit::TranslationUnit(const TranslationUnit &) = default;
diff --git a/src/tools/clangbackend/ipcsource/clangtranslationunit.h b/src/tools/clangbackend/ipcsource/clangtranslationunit.h
index 016067dc369..e10f2bd39e6 100644
--- a/src/tools/clangbackend/ipcsource/clangtranslationunit.h
+++ b/src/tools/clangbackend/ipcsource/clangtranslationunit.h
@@ -99,6 +99,8 @@ public:
CXTranslationUnit cxTranslationUnit() const;
CXTranslationUnit cxTranslationUnitWithoutReparsing() const;
CXUnsavedFile * cxUnsavedFiles() const;
+ const std::vector<CXUnsavedFile> &cxUnsavedFilesVector() const;
+
uint unsavedFilesCount() const;
const Utf8String &filePath() const;
@@ -125,6 +127,7 @@ public:
CommandLineArguments commandLineArguments() const;
+ SourceLocation sourceLocationAtWithoutReparsing(uint line, uint column) const;
SourceLocation sourceLocationAt(uint line, uint column) const;
SourceLocation sourceLocationAt(const Utf8String &filePath, uint line, uint column) const;
diff --git a/src/tools/clangbackend/ipcsource/codecompleter.cpp b/src/tools/clangbackend/ipcsource/codecompleter.cpp
index b628c51eb8f..45325fb5046 100644
--- a/src/tools/clangbackend/ipcsource/codecompleter.cpp
+++ b/src/tools/clangbackend/ipcsource/codecompleter.cpp
@@ -32,16 +32,33 @@
#include "clangcodecompleteresults.h"
#include "clangstring.h"
+#include "cursor.h"
#include "codecompletefailedexception.h"
#include "codecompletionsextractor.h"
+#include "sourcelocation.h"
+#include "temporarymodifiedunsavedfiles.h"
#include "clangtranslationunit.h"
+#include "sourcerange.h"
#include <clang-c/Index.h>
-#include <QDebug>
-
namespace ClangBackEnd {
+namespace {
+
+CodeCompletions toCodeCompletions(const ClangCodeCompleteResults &results)
+{
+ if (results.isNull())
+ return CodeCompletions();
+
+ CodeCompletionsExtractor extractor(results.data());
+ CodeCompletions codeCompletions = extractor.extractAll();
+
+ return codeCompletions;
+}
+
+} // anonymous namespace
+
CodeCompleter::CodeCompleter(TranslationUnit translationUnit)
: translationUnit(std::move(translationUnit))
{
@@ -49,17 +66,64 @@ CodeCompleter::CodeCompleter(TranslationUnit translationUnit)
CodeCompletions CodeCompleter::complete(uint line, uint column)
{
- ClangCodeCompleteResults completeResults(clang_codeCompleteAt(translationUnit.cxTranslationUnitWithoutReparsing(),
- translationUnit.filePath().constData(),
- line,
- column,
- translationUnit.cxUnsavedFiles(),
- translationUnit.unsavedFilesCount(),
- CXCodeComplete_IncludeMacros | CXCodeComplete_IncludeCodePatterns));
+ neededCorrection_ = CompletionCorrection::NoCorrection;
+
+ ClangCodeCompleteResults results = complete(line,
+ column,
+ translationUnit.cxUnsavedFiles(),
+ translationUnit.unsavedFilesCount());
+
+ if (results.hasNoResultsForDotCompletion())
+ results = completeWithArrowInsteadOfDot(line, column);
+
+ return toCodeCompletions(results);
+}
+
+CompletionCorrection CodeCompleter::neededCorrection() const
+{
+ return neededCorrection_;
+}
+
+ClangCodeCompleteResults CodeCompleter::complete(uint line,
+ uint column,
+ CXUnsavedFile *unsavedFiles,
+ unsigned unsavedFileCount)
+{
+ const auto options = CXCodeComplete_IncludeMacros | CXCodeComplete_IncludeCodePatterns;
+
+ return clang_codeCompleteAt(translationUnit.cxTranslationUnitWithoutReparsing(),
+ translationUnit.filePath().constData(),
+ line,
+ column,
+ unsavedFiles,
+ unsavedFileCount,
+ options);
+}
+
+ClangCodeCompleteResults CodeCompleter::completeWithArrowInsteadOfDot(uint line, uint column)
+{
+ TemporaryModifiedUnsavedFiles modifiedUnsavedFiles(translationUnit.cxUnsavedFilesVector());
+ const SourceLocation location = translationUnit.sourceLocationAtWithoutReparsing(line,
+ column - 1);
+
+ const bool replaced = modifiedUnsavedFiles.replaceInFile(filePath(),
+ location.offset(),
+ 1,
+ Utf8StringLiteral("->"));
+
+ ClangCodeCompleteResults results;
+
+ if (replaced) {
+ results = complete(line,
+ column + 1,
+ modifiedUnsavedFiles.cxUnsavedFiles(),
+ modifiedUnsavedFiles.count());
- CodeCompletionsExtractor extractor(completeResults.data());
+ if (results.hasResults())
+ neededCorrection_ = CompletionCorrection::DotToArrowCorrection;
+ }
- return extractor.extractAll();
+ return results;
}
Utf8String CodeCompleter::filePath() const
diff --git a/src/tools/clangbackend/ipcsource/codecompleter.h b/src/tools/clangbackend/ipcsource/codecompleter.h
index 971f209c361..20cfa301cb0 100644
--- a/src/tools/clangbackend/ipcsource/codecompleter.h
+++ b/src/tools/clangbackend/ipcsource/codecompleter.h
@@ -39,7 +39,7 @@
namespace ClangBackEnd {
-class TranslationUnit;
+class ClangCodeCompleteResults;
class CodeCompleter
{
@@ -49,12 +49,22 @@ public:
CodeCompletions complete(uint line, uint column);
+ CompletionCorrection neededCorrection() const;
+
private:
+ ClangCodeCompleteResults complete(uint line,
+ uint column,
+ CXUnsavedFile *unsavedFiles,
+ unsigned unsavedFileCount);
+
+ ClangCodeCompleteResults completeWithArrowInsteadOfDot(uint line, uint column);
+
Utf8String filePath() const;
static void checkCodeCompleteResult(CXCodeCompleteResults *completeResults);
private:
TranslationUnit translationUnit;
+ CompletionCorrection neededCorrection_ = CompletionCorrection::NoCorrection;
};
} // namespace ClangBackEnd
diff --git a/src/tools/clangbackend/ipcsource/sourcelocation.cpp b/src/tools/clangbackend/ipcsource/sourcelocation.cpp
index 19794130b68..e274e25ee33 100644
--- a/src/tools/clangbackend/ipcsource/sourcelocation.cpp
+++ b/src/tools/clangbackend/ipcsource/sourcelocation.cpp
@@ -97,6 +97,7 @@ SourceLocation::SourceLocation(CXTranslationUnit cxTranslationUnit,
line_(line),
column_(column)
{
+ clang_getFileLocation(cxSourceLocation, 0, 0, 0, &offset_);
}
bool operator==(const SourceLocation &first, const SourceLocation &second)
diff --git a/src/tools/clangbackend/ipcsource/temporarymodifiedunsavedfiles.cpp b/src/tools/clangbackend/ipcsource/temporarymodifiedunsavedfiles.cpp
new file mode 100644
index 00000000000..a22a04e57c1
--- /dev/null
+++ b/src/tools/clangbackend/ipcsource/temporarymodifiedunsavedfiles.cpp
@@ -0,0 +1,87 @@
+/****************************************************************************
+**
+** 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 "temporarymodifiedunsavedfiles.h"
+
+#include <cstring>
+
+namespace ClangBackEnd {
+
+TemporaryModifiedUnsavedFiles::TemporaryModifiedUnsavedFiles(
+ const std::vector<CXUnsavedFile> &unsavedFilesVector)
+ : m_unsavedFileVector(unsavedFilesVector)
+{
+}
+
+bool TemporaryModifiedUnsavedFiles::replaceInFile(const Utf8String &filePath,
+ uint offset,
+ uint length,
+ const Utf8String &replacement)
+{
+ const auto isMatchingFile = [filePath] (const CXUnsavedFile &unsavedFile) {
+ return std::strcmp(unsavedFile.Filename, filePath.constData()) == 0;
+ };
+ const auto unsavedFileIterator = std::find_if(m_unsavedFileVector.begin(),
+ m_unsavedFileVector.end(),
+ isMatchingFile);
+
+ if (unsavedFileIterator == m_unsavedFileVector.end())
+ return false;
+
+ return replaceInFile_internal(*unsavedFileIterator, offset, length, replacement);
+}
+
+CXUnsavedFile TemporaryModifiedUnsavedFiles::cxUnsavedFileAt(uint index)
+{
+ return m_unsavedFileVector[index];
+}
+
+CXUnsavedFile *TemporaryModifiedUnsavedFiles::cxUnsavedFiles()
+{
+ return m_unsavedFileVector.data();
+}
+
+uint TemporaryModifiedUnsavedFiles::count()
+{
+ return uint(m_unsavedFileVector.size());
+}
+
+bool TemporaryModifiedUnsavedFiles::replaceInFile_internal(CXUnsavedFile &unsavedFile,
+ uint offset,
+ uint length,
+ const Utf8String &replacement)
+{
+ auto modifiedContent = Utf8String::fromUtf8(unsavedFile.Contents);
+ modifiedContent.replace(int(offset), int(length), replacement);
+
+ unsavedFile.Contents = modifiedContent.constData();
+ unsavedFile.Length = uint(modifiedContent.byteSize() + 1);
+
+ m_modifiedContents.push_back(modifiedContent); // Keep the modified copy.
+
+ return true;
+}
+
+} // namespace ClangBackEnd
diff --git a/src/tools/clangbackend/ipcsource/temporarymodifiedunsavedfiles.h b/src/tools/clangbackend/ipcsource/temporarymodifiedunsavedfiles.h
new file mode 100644
index 00000000000..959b9431505
--- /dev/null
+++ b/src/tools/clangbackend/ipcsource/temporarymodifiedunsavedfiles.h
@@ -0,0 +1,66 @@
+/****************************************************************************
+**
+** 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.
+**
+****************************************************************************/
+
+#ifndef CLANGBACKEND_TEMPORARYMODIFIEDUNSAVEDFILES_H
+#define CLANGBACKEND_TEMPORARYMODIFIEDUNSAVEDFILES_H
+
+#include <clang-c/Index.h>
+
+#include <utf8string.h>
+
+#include <vector>
+
+namespace ClangBackEnd {
+
+class TemporaryModifiedUnsavedFiles
+{
+public:
+ TemporaryModifiedUnsavedFiles(const std::vector<CXUnsavedFile> &unsavedFilesVector);
+
+ TemporaryModifiedUnsavedFiles(const TemporaryModifiedUnsavedFiles &) = delete;
+
+ bool replaceInFile(const Utf8String &filePath,
+ uint offset,
+ uint length,
+ const Utf8String &replacement);
+
+ CXUnsavedFile cxUnsavedFileAt(uint index);
+ CXUnsavedFile *cxUnsavedFiles();
+ uint count();
+
+private:
+ bool replaceInFile_internal(CXUnsavedFile &unsavedFile,
+ uint offset,
+ uint length,
+ const Utf8String &replacement);
+
+private:
+ std::vector<CXUnsavedFile> m_unsavedFileVector;
+ std::vector<Utf8String> m_modifiedContents;
+};
+
+} // namespace ClangBackEnd
+
+#endif // CLANGBACKEND_TEMPORARYMODIFIEDUNSAVEDFILES_H
diff --git a/tests/unit/unittest/clientserverinprocesstest.cpp b/tests/unit/unittest/clientserverinprocesstest.cpp
index c8f74551d43..7d31199aee6 100644
--- a/tests/unit/unittest/clientserverinprocesstest.cpp
+++ b/tests/unit/unittest/clientserverinprocesstest.cpp
@@ -217,7 +217,9 @@ TEST_F(ClientServerInProcess, SendRequestHighlightingMessage)
TEST_F(ClientServerInProcess, SendCodeCompletedMessage)
{
ClangBackEnd::CodeCompletions codeCompletions({Utf8StringLiteral("newFunction()")});
- ClangBackEnd::CodeCompletedMessage message(codeCompletions, 1);
+ ClangBackEnd::CodeCompletedMessage message(codeCompletions,
+ ClangBackEnd::CompletionCorrection::NoCorrection,
+ 1);
EXPECT_CALL(mockIpcClient, codeCompleted(message))
.Times(1);
diff --git a/tests/unit/unittest/codecompletiontest.cpp b/tests/unit/unittest/codecompletiontest.cpp
index efbf7ea5901..972a86c28ca 100644
--- a/tests/unit/unittest/codecompletiontest.cpp
+++ b/tests/unit/unittest/codecompletiontest.cpp
@@ -81,6 +81,7 @@ protected:
void SetUp();
void copyTargetHeaderToTemporaryIncludeDirecory();
void copyChangedTargetHeaderToTemporaryIncludeDirecory();
+ ClangBackEnd::CodeCompleter setupCompleter(const ClangBackEnd::FileContainer &fileContainer);
static Utf8String readFileContent(const QString &fileName);
protected:
@@ -103,6 +104,55 @@ protected:
projectPart.projectPartId(),
readFileContent(QStringLiteral("/complete_target_header_unsaved.h")),
true};
+
+ ClangBackEnd::FileContainer arrowFileContainer{
+ Utf8StringLiteral(TESTDATA_DIR"/complete_arrow.cpp"),
+ projectPart.projectPartId(),
+ readFileContent(QStringLiteral("/complete_arrow.cpp")),
+ true
+ };
+ ClangBackEnd::FileContainer dotArrowCorrectionForPointerFileContainer{
+ Utf8StringLiteral(TESTDATA_DIR"/complete_withDotArrowCorrectionForPointer.cpp"),
+ projectPart.projectPartId(),
+ readFileContent(QStringLiteral("/complete_withDotArrowCorrectionForPointer.cpp")),
+ true
+ };
+ ClangBackEnd::FileContainer noDotArrowCorrectionForObjectFileContainer{
+ Utf8StringLiteral(TESTDATA_DIR"/complete_withNoDotArrowCorrectionForObject.cpp"),
+ projectPart.projectPartId(),
+ readFileContent(QStringLiteral("/complete_withNoDotArrowCorrectionForObject.cpp")),
+ true
+ };
+ ClangBackEnd::FileContainer noDotArrowCorrectionForFloatFileContainer{
+ Utf8StringLiteral(TESTDATA_DIR"/complete_withNoDotArrowCorrectionForFloat.cpp"),
+ projectPart.projectPartId(),
+ readFileContent(QStringLiteral("/complete_withNoDotArrowCorrectionForFloat.cpp")),
+ true
+ };
+ ClangBackEnd::FileContainer noDotArrowCorrectionForObjectWithArrowOperatortFileContainer{
+ Utf8StringLiteral(TESTDATA_DIR"/complete_withNoDotArrowCorrectionForObjectWithArrowOperator.cpp"),
+ projectPart.projectPartId(),
+ readFileContent(QStringLiteral("/complete_withNoDotArrowCorrectionForObjectWithArrowOperator.cpp")),
+ true
+ };
+ ClangBackEnd::FileContainer noDotArrowCorrectionForDotDotFileContainer{
+ Utf8StringLiteral(TESTDATA_DIR"/complete_withNoDotArrowCorrectionForDotDot.cpp"),
+ projectPart.projectPartId(),
+ readFileContent(QStringLiteral("/complete_withNoDotArrowCorrectionForDotDot.cpp")),
+ true
+ };
+ ClangBackEnd::FileContainer noDotArrowCorrectionForArrowDotFileContainer{
+ Utf8StringLiteral(TESTDATA_DIR"/complete_withNoDotArrowCorrectionForArrowDot.cpp"),
+ projectPart.projectPartId(),
+ readFileContent(QStringLiteral("/complete_withNoDotArrowCorrectionForArrowDot.cpp")),
+ true
+ };
+ ClangBackEnd::FileContainer noDotArrowCorrectionForOnlyDotContainer{
+ Utf8StringLiteral(TESTDATA_DIR"/complete_withNoDotArrowCorrectionForOnlyDot.cpp"),
+ projectPart.projectPartId(),
+ readFileContent(QStringLiteral("/complete_withNoDotArrowCorrectionForOnlyDot.cpp")),
+ true
+ };
};
Utf8String CodeCompleter::readFileContent(const QString &fileName)
@@ -237,4 +287,105 @@ TEST_F(CodeCompleter, DISABLED_FunctionInChangedIncludedHeaderWithUnsavedContent
CodeCompletion::FunctionCompletionKind)));
}
+TEST_F(CodeCompleter, ArrowCompletion)
+{
+ auto myCompleter = setupCompleter(arrowFileContainer);
+
+ const ClangBackEnd::CodeCompletions completions = myCompleter.complete(5, 10);
+
+ ASSERT_THAT(completions,
+ Contains(IsCodeCompletion(Utf8StringLiteral("member"),
+ CodeCompletion::VariableCompletionKind)));
+ ASSERT_THAT(myCompleter.neededCorrection(),
+ ClangBackEnd::CompletionCorrection::NoCorrection);
+}
+
+TEST_F(CodeCompleter, DotToArrowCompletionForPointer)
+{
+ auto myCompleter = setupCompleter(dotArrowCorrectionForPointerFileContainer);
+
+ const ClangBackEnd::CodeCompletions completions = myCompleter.complete(5, 9);
+
+ ASSERT_THAT(completions,
+ Contains(IsCodeCompletion(Utf8StringLiteral("member"),
+ CodeCompletion::VariableCompletionKind)));
+ ASSERT_THAT(myCompleter.neededCorrection(),
+ ClangBackEnd::CompletionCorrection::DotToArrowCorrection);
+}
+
+TEST_F(CodeCompleter, NoDotToArrowCompletionForObject)
+{
+ auto myCompleter = setupCompleter(noDotArrowCorrectionForObjectFileContainer);
+
+ const ClangBackEnd::CodeCompletions completions = myCompleter.complete(5, 9);
+
+ ASSERT_THAT(completions,
+ Contains(IsCodeCompletion(Utf8StringLiteral("member"),
+ CodeCompletion::VariableCompletionKind)));
+ ASSERT_THAT(myCompleter.neededCorrection(), ClangBackEnd::CompletionCorrection::NoCorrection);
+}
+
+TEST_F(CodeCompleter, NoDotToArrowCompletionForFloat)
+{
+ auto myCompleter = setupCompleter(noDotArrowCorrectionForFloatFileContainer);
+
+ const ClangBackEnd::CodeCompletions completions = myCompleter.complete(3, 18);
+
+ ASSERT_TRUE(completions.isEmpty());
+ ASSERT_THAT(myCompleter.neededCorrection(), ClangBackEnd::CompletionCorrection::NoCorrection);
+}
+
+TEST_F(CodeCompleter, NoDotArrowCorrectionForObjectWithArrowOperator)
+{
+ auto myCompleter = setupCompleter(noDotArrowCorrectionForObjectWithArrowOperatortFileContainer);
+
+ const ClangBackEnd::CodeCompletions completions = myCompleter.complete(8, 9);
+
+ ASSERT_THAT(completions,
+ Contains(IsCodeCompletion(Utf8StringLiteral("member"),
+ CodeCompletion::VariableCompletionKind)));
+ ASSERT_THAT(myCompleter.neededCorrection(), ClangBackEnd::CompletionCorrection::NoCorrection);
+}
+
+TEST_F(CodeCompleter, NoDotArrowCorrectionForDotDot)
+{
+ auto myCompleter = setupCompleter(noDotArrowCorrectionForDotDotFileContainer);
+
+ const ClangBackEnd::CodeCompletions completions = myCompleter.complete(5, 10);
+
+ ASSERT_TRUE(completions.isEmpty());
+ ASSERT_THAT(myCompleter.neededCorrection(), ClangBackEnd::CompletionCorrection::NoCorrection);
+}
+
+TEST_F(CodeCompleter, NoDotArrowCorrectionForArrowDot)
+{
+ auto myCompleter = setupCompleter(noDotArrowCorrectionForArrowDotFileContainer);
+
+ const ClangBackEnd::CodeCompletions completions = myCompleter.complete(5, 11);
+
+ ASSERT_TRUE(completions.isEmpty());
+ ASSERT_THAT(myCompleter.neededCorrection(), ClangBackEnd::CompletionCorrection::NoCorrection);
+}
+
+TEST_F(CodeCompleter, NoDotArrowCorrectionForOnlyDot)
+{
+ auto myCompleter = setupCompleter(noDotArrowCorrectionForOnlyDotContainer);
+
+ const ClangBackEnd::CodeCompletions completions = myCompleter.complete(5, 6);
+
+ ASSERT_THAT(completions,
+ Contains(IsCodeCompletion(Utf8StringLiteral("Foo"),
+ CodeCompletion::ClassCompletionKind)));
+ ASSERT_THAT(myCompleter.neededCorrection(), ClangBackEnd::CompletionCorrection::NoCorrection);
+}
+
+ClangBackEnd::CodeCompleter CodeCompleter::setupCompleter(
+ const ClangBackEnd::FileContainer &fileContainer)
+{
+ translationUnits.create({fileContainer});
+ unsavedFiles.createOrUpdate({fileContainer});
+
+ return ClangBackEnd::CodeCompleter(translationUnits.translationUnit(fileContainer));
+}
+
}
diff --git a/tests/unit/unittest/data/complete_arrow.cpp b/tests/unit/unittest/data/complete_arrow.cpp
new file mode 100644
index 00000000000..2d969e2bfbd
--- /dev/null
+++ b/tests/unit/unittest/data/complete_arrow.cpp
@@ -0,0 +1,6 @@
+struct Foo { int member; };
+
+void g(Foo *foo)
+{
+ foo->
+}
diff --git a/tests/unit/unittest/data/complete_withDotArrowCorrectionForPointer.cpp b/tests/unit/unittest/data/complete_withDotArrowCorrectionForPointer.cpp
new file mode 100644
index 00000000000..9f8d3645b2e
--- /dev/null
+++ b/tests/unit/unittest/data/complete_withDotArrowCorrectionForPointer.cpp
@@ -0,0 +1,6 @@
+struct Foo { int member; };
+
+void g(Foo *foo)
+{
+ foo.
+}
diff --git a/tests/unit/unittest/data/complete_withNoDotArrowCorrectionForArrowDot.cpp b/tests/unit/unittest/data/complete_withNoDotArrowCorrectionForArrowDot.cpp
new file mode 100644
index 00000000000..304c17c05b6
--- /dev/null
+++ b/tests/unit/unittest/data/complete_withNoDotArrowCorrectionForArrowDot.cpp
@@ -0,0 +1,7 @@
+struct Foo { int member; };
+
+void g(Foo *foo)
+{
+ foo->.
+}
+
diff --git a/tests/unit/unittest/data/complete_withNoDotArrowCorrectionForDotDot.cpp b/tests/unit/unittest/data/complete_withNoDotArrowCorrectionForDotDot.cpp
new file mode 100644
index 00000000000..8f77989da67
--- /dev/null
+++ b/tests/unit/unittest/data/complete_withNoDotArrowCorrectionForDotDot.cpp
@@ -0,0 +1,7 @@
+struct Foo { int member; };
+
+void g(Foo *foo)
+{
+ foo..
+}
+
diff --git a/tests/unit/unittest/data/complete_withNoDotArrowCorrectionForFloat.cpp b/tests/unit/unittest/data/complete_withNoDotArrowCorrectionForFloat.cpp
new file mode 100644
index 00000000000..c27a0ee8f08
--- /dev/null
+++ b/tests/unit/unittest/data/complete_withNoDotArrowCorrectionForFloat.cpp
@@ -0,0 +1,4 @@
+void f()
+{
+ float pi = 3.
+}
diff --git a/tests/unit/unittest/data/complete_withNoDotArrowCorrectionForObject.cpp b/tests/unit/unittest/data/complete_withNoDotArrowCorrectionForObject.cpp
new file mode 100644
index 00000000000..d2e3b88da7c
--- /dev/null
+++ b/tests/unit/unittest/data/complete_withNoDotArrowCorrectionForObject.cpp
@@ -0,0 +1,6 @@
+struct Foo { int member; };
+
+void g(Foo foo)
+{
+ foo.
+}
diff --git a/tests/unit/unittest/data/complete_withNoDotArrowCorrectionForObjectWithArrowOperator.cpp b/tests/unit/unittest/data/complete_withNoDotArrowCorrectionForObjectWithArrowOperator.cpp
new file mode 100644
index 00000000000..6a741603a18
--- /dev/null
+++ b/tests/unit/unittest/data/complete_withNoDotArrowCorrectionForObjectWithArrowOperator.cpp
@@ -0,0 +1,9 @@
+struct Foo {
+ Foo *operator->();
+ int member;
+};
+
+void g(Foo foo)
+{
+ foo.
+}
diff --git a/tests/unit/unittest/data/complete_withNoDotArrowCorrectionForOnlyDot.cpp b/tests/unit/unittest/data/complete_withNoDotArrowCorrectionForOnlyDot.cpp
new file mode 100644
index 00000000000..4a5d7894c4a
--- /dev/null
+++ b/tests/unit/unittest/data/complete_withNoDotArrowCorrectionForOnlyDot.cpp
@@ -0,0 +1,7 @@
+struct Foo { int member; };
+
+void g(Foo *foo)
+{
+ .
+}
+
diff --git a/tests/unit/unittest/readandwritemessageblocktest.cpp b/tests/unit/unittest/readandwritemessageblocktest.cpp
index ef958253dd2..f5f97946690 100644
--- a/tests/unit/unittest/readandwritemessageblocktest.cpp
+++ b/tests/unit/unittest/readandwritemessageblocktest.cpp
@@ -175,7 +175,11 @@ TEST_F(ReadAndWriteMessageBlock, CompareCodeCompletedMessage)
{
ClangBackEnd::CodeCompletions codeCompletions({Utf8StringLiteral("newFunction()")});
- CompareMessage(ClangBackEnd::CodeCompletedMessage(codeCompletions, 1));
+ CompareMessage(
+ ClangBackEnd::CodeCompletedMessage(codeCompletions,
+ ClangBackEnd::CompletionCorrection::NoCorrection,
+ 1)
+ );
}
TEST_F(ReadAndWriteMessageBlock, CompareDiagnosticsChangedMessage)
@@ -244,7 +248,10 @@ TEST_F(ReadAndWriteMessageBlock, ReadMessageAfterInterruption)
QVariant ReadAndWriteMessageBlock::writeCodeCompletedMessage()
{
- ClangBackEnd::CodeCompletedMessage message(ClangBackEnd::CodeCompletions({Utf8StringLiteral("newFunction()")}), 1);
+ ClangBackEnd::CodeCompletedMessage message(
+ ClangBackEnd::CodeCompletions({Utf8StringLiteral("newFunction()")}),
+ ClangBackEnd::CompletionCorrection::NoCorrection,
+ 1);
const QVariant writeMessage = QVariant::fromValue(message);
writeMessageBlock.write(writeMessage);
diff --git a/tests/unit/unittest/temporarymodifiedunsavedfilestest.cpp b/tests/unit/unittest/temporarymodifiedunsavedfilestest.cpp
new file mode 100644
index 00000000000..7fdd977e50d
--- /dev/null
+++ b/tests/unit/unittest/temporarymodifiedunsavedfilestest.cpp
@@ -0,0 +1,106 @@
+/****************************************************************************
+**
+** 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 <filecontainer.h>
+#include <temporarymodifiedunsavedfiles.h>
+#include <unsavedfiles.h>
+
+#include <gmock/gmock.h>
+#include <gmock/gmock-matchers.h>
+#include <gtest/gtest.h>
+#include "gtest-qt-printing.h"
+
+using ClangBackEnd::FileContainer;
+using ClangBackEnd::TemporaryModifiedUnsavedFiles;
+using ClangBackEnd::UnsavedFiles;
+
+using testing::Eq;
+
+namespace {
+
+class TemporaryModifiedUnsavedFiles : public ::testing::Test
+{
+protected:
+ ClangBackEnd::UnsavedFiles unsavedFiles;
+
+ FileContainer fileContainer{Utf8StringLiteral("file1.h"),
+ Utf8StringLiteral("projectPartId"),
+ Utf8StringLiteral("void f() { foo. }"),
+ true};
+
+ Utf8String someNotExistingFilePath{Utf8StringLiteral("nonExistingFile.cpp")};
+};
+
+TEST_F(TemporaryModifiedUnsavedFiles, HasZeroFilesOnCreation)
+{
+ ::TemporaryModifiedUnsavedFiles files(unsavedFiles.cxUnsavedFileVector());
+
+ ASSERT_THAT(files.count(), Eq(0L));
+}
+
+TEST_F(TemporaryModifiedUnsavedFiles, HasOneFileAfterCreatingOne)
+{
+ unsavedFiles.createOrUpdate({fileContainer});
+
+ ::TemporaryModifiedUnsavedFiles files(unsavedFiles.cxUnsavedFileVector());
+
+ ASSERT_THAT(files.count(), Eq(1L));
+}
+
+TEST_F(TemporaryModifiedUnsavedFiles, ReplaceIndicatesFailureOnNonExistingUnsavedFile)
+{
+ ::TemporaryModifiedUnsavedFiles files(unsavedFiles.cxUnsavedFileVector());
+ const uint someOffset = 0;
+ const uint someLength = 1;
+ const auto someReplacement = Utf8StringLiteral("->");
+
+ const bool replaced = files.replaceInFile(someNotExistingFilePath,
+ someOffset,
+ someLength,
+ someReplacement);
+
+ ASSERT_FALSE(replaced);
+}
+
+TEST_F(TemporaryModifiedUnsavedFiles, ReplaceDotWithArrow)
+{
+ unsavedFiles.createOrUpdate({fileContainer});
+ ::TemporaryModifiedUnsavedFiles files(unsavedFiles.cxUnsavedFileVector());
+ const uint offset = 14;
+ const uint length = 1;
+ const auto replacement = Utf8StringLiteral("->");
+
+ const bool replacedSuccessfully = files.replaceInFile(fileContainer.filePath(),
+ offset,
+ length,
+ replacement);
+
+ CXUnsavedFile cxUnsavedFile = files.cxUnsavedFileAt(0);
+ ASSERT_TRUE(replacedSuccessfully);
+ ASSERT_THAT(Utf8String::fromUtf8(cxUnsavedFile.Contents),
+ Eq(Utf8StringLiteral("void f() { foo-> }")));
+}
+
+} // anonymous
diff --git a/tests/unit/unittest/unittest.pro b/tests/unit/unittest/unittest.pro
index d3cefda9da0..69d0e8c8aab 100644
--- a/tests/unit/unittest/unittest.pro
+++ b/tests/unit/unittest/unittest.pro
@@ -59,7 +59,8 @@ SOURCES += \
highlightinginformationstest.cpp \
skippedsourcerangestest.cpp \
highlightingmarksreportertest.cpp \
- chunksreportedmonitor.cpp
+ chunksreportedmonitor.cpp \
+ temporarymodifiedunsavedfilestest.cpp
HEADERS += \
gtest-qt-printing.h \
diff --git a/tests/unit/unittest/utf8test.cpp b/tests/unit/unittest/utf8test.cpp
index f3e4f94c6af..6053f56c0f1 100644
--- a/tests/unit/unittest/utf8test.cpp
+++ b/tests/unit/unittest/utf8test.cpp
@@ -164,6 +164,15 @@ TEST(Utf8, Replace)
ASSERT_THAT(text, Utf8StringLiteral("any text"));
}
+TEST(Utf8, ReplaceNBytesFromIndexPosition)
+{
+ Utf8String text(Utf8StringLiteral("min"));
+
+ text.replace(1, 1, Utf8StringLiteral("aa"));
+
+ ASSERT_THAT(text, Utf8StringLiteral("maan"));
+}
+
TEST(Utf8, StartsWith)
{
Utf8String text(Utf8StringLiteral("$column"));