aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNikolai Kosjar <[email protected]>2016-01-11 15:20:04 +0100
committerNikolai Kosjar <[email protected]>2016-01-19 13:20:35 +0000
commit8d6549fa74bcc5e05f43497aef73e814a268139e (patch)
tree97abf08ec9c8cb02c102ce6a8a911d567b8472c7
parent43800a6aa8111550ce6d0c4992c09bb657ef234e (diff)
Clang: Correct member access operator if possible
1 struct Foo { int member; }; 2 void f(Foo *foo) 3 { 4 foo.<REQUEST COMPLETION> // correct '.' to '->' and provide results 5 } The preferred approach would be to check if "foo" in line 4 is of pointer type, but there is no suitable cursor (only CompoundStmt) at that position since the code is usually not yet parsed and thus invalid. Thus, just run the completion as is. If there are not any results for a dot completion, re-run the completion with "." exchanged by "->". This approach is inherently slower than the preferred approach implemented in the built-in code model. The following rare cases are not handled: 1) Requesting completion after white space: Works: foo.<COMPLETE HERE> Fails: foo. <COMPLETE HERE> 2) Opening a file and requesting completion (ctrl+space) without prior editing. No editing before triggering completion means that no unsaved file is generated on the backend side, which is a requirement for the correction. Task-number: QTCREATORBUG-11581 Change-Id: I6bc8e8594778774ab342755fdb01a8a3e5c52ba0 Reviewed-by: Marco Bubke <[email protected]>
-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"));