diff --git a/src/tools/clangbackend/ipcsource/codecompleter.cpp b/src/tools/clangbackend/ipcsource/codecompleter.cpp index a139e02edf7..691686d0479 100644 --- a/src/tools/clangbackend/ipcsource/codecompleter.cpp +++ b/src/tools/clangbackend/ipcsource/codecompleter.cpp @@ -68,7 +68,7 @@ CodeCompletions CodeCompleter::complete(uint line, uint column) translationUnit.cxUnsavedFiles(), translationUnit.unsavedFilesCount()); - if (results.hasNoResultsForDotCompletion()) + if (results.hasNoResultsForDotCompletion() && hasDotAt(line, column - 1)) results = completeWithArrowInsteadOfDot(line, column); return toCodeCompletions(results); @@ -95,6 +95,14 @@ ClangCodeCompleteResults CodeCompleter::complete(uint line, options); } +bool CodeCompleter::hasDotAt(uint line, uint column) const +{ + const UnsavedFile &unsavedFile = translationUnit.unsavedFile(); + const SourceLocation location = translationUnit.sourceLocationAtWithoutReparsing(line, column); + + return unsavedFile.hasCharacterAt(location.offset(), '.'); +} + ClangCodeCompleteResults CodeCompleter::completeWithArrowInsteadOfDot(uint line, uint column) { ClangCodeCompleteResults results; diff --git a/src/tools/clangbackend/ipcsource/codecompleter.h b/src/tools/clangbackend/ipcsource/codecompleter.h index ceb594df630..99749414f4b 100644 --- a/src/tools/clangbackend/ipcsource/codecompleter.h +++ b/src/tools/clangbackend/ipcsource/codecompleter.h @@ -46,6 +46,9 @@ public: CompletionCorrection neededCorrection() const; +public: // for tests + bool hasDotAt(uint line, uint column) const; + private: ClangCodeCompleteResults complete(uint line, uint column, diff --git a/src/tools/clangbackend/ipcsource/unsavedfile.cpp b/src/tools/clangbackend/ipcsource/unsavedfile.cpp index 1db76d22cfa..b1c87e6025d 100644 --- a/src/tools/clangbackend/ipcsource/unsavedfile.cpp +++ b/src/tools/clangbackend/ipcsource/unsavedfile.cpp @@ -65,6 +65,14 @@ const char *UnsavedFile::filePath() const return cxUnsavedFile.Filename; } +bool UnsavedFile::hasCharacterAt(uint position, char character) const +{ + if (position < cxUnsavedFile.Length) + return cxUnsavedFile.Contents[position] == character; + + return false; +} + bool UnsavedFile::replaceAt(uint position, uint length, const Utf8String &replacement) { if (position < cxUnsavedFile.Length) { diff --git a/src/tools/clangbackend/ipcsource/unsavedfile.h b/src/tools/clangbackend/ipcsource/unsavedfile.h index fe738bb262e..17e09667dcd 100644 --- a/src/tools/clangbackend/ipcsource/unsavedfile.h +++ b/src/tools/clangbackend/ipcsource/unsavedfile.h @@ -51,6 +51,7 @@ public: const char *filePath() const; + bool hasCharacterAt(uint position, char character) const; bool replaceAt(uint position, uint length, const Utf8String &replacement); CXUnsavedFile *data(); diff --git a/tests/unit/unittest/codecompletiontest.cpp b/tests/unit/unittest/codecompletiontest.cpp index d7a6cba6234..3b2eff7d82d 100644 --- a/tests/unit/unittest/codecompletiontest.cpp +++ b/tests/unit/unittest/codecompletiontest.cpp @@ -148,6 +148,12 @@ protected: readFileContent(QStringLiteral("/complete_withNoDotArrowCorrectionForOnlyDot.cpp")), true }; + ClangBackEnd::FileContainer noDotArrowCorrectionForColonColonFileContainer{ + Utf8StringLiteral(TESTDATA_DIR"/complete_withNoDotArrowCorrectionForColonColon.cpp"), + projectPart.projectPartId(), + readFileContent(QStringLiteral("/complete_withNoDotArrowCorrectionForColonColon.cpp")), + true + }; }; Utf8String CodeCompleter::readFileContent(const QString &fileName) @@ -295,6 +301,22 @@ TEST_F(CodeCompleter, ArrowCompletion) ClangBackEnd::CompletionCorrection::NoCorrection); } +TEST_F(CodeCompleter, HasDotAt) +{ + auto myCompleter = setupCompleter(dotArrowCorrectionForPointerFileContainer); + + ASSERT_TRUE(myCompleter.hasDotAt(5, 8)); +} + +TEST_F(CodeCompleter, HasNoDotAtDueToMissingUnsavedFile) +{ + const ClangBackEnd::FileContainer fileContainer = dotArrowCorrectionForPointerFileContainer; + translationUnits.create({fileContainer}); + ClangBackEnd::CodeCompleter myCompleter(translationUnits.translationUnit(fileContainer)); + + ASSERT_FALSE(myCompleter.hasDotAt(5, 8)); +} + TEST_F(CodeCompleter, DotToArrowCompletionForPointer) { auto myCompleter = setupCompleter(dotArrowCorrectionForPointerFileContainer); @@ -374,6 +396,14 @@ TEST_F(CodeCompleter, NoDotArrowCorrectionForOnlyDot) ASSERT_THAT(myCompleter.neededCorrection(), ClangBackEnd::CompletionCorrection::NoCorrection); } +TEST_F(CodeCompleter, NoDotArrowCorrectionForColonColon) +{ + auto myCompleter = setupCompleter(noDotArrowCorrectionForColonColonFileContainer); + const ClangBackEnd::CodeCompletions completions = myCompleter.complete(1, 7); + + ASSERT_THAT(myCompleter.neededCorrection(), ClangBackEnd::CompletionCorrection::NoCorrection); +} + ClangBackEnd::CodeCompleter CodeCompleter::setupCompleter( const ClangBackEnd::FileContainer &fileContainer) { diff --git a/tests/unit/unittest/data/complete_withNoDotArrowCorrectionForColonColon.cpp b/tests/unit/unittest/data/complete_withNoDotArrowCorrectionForColonColon.cpp new file mode 100644 index 00000000000..62af4343c10 --- /dev/null +++ b/tests/unit/unittest/data/complete_withNoDotArrowCorrectionForColonColon.cpp @@ -0,0 +1 @@ +Blah:: diff --git a/tests/unit/unittest/unsavedfiletest.cpp b/tests/unit/unittest/unsavedfiletest.cpp index aa54550f2d1..80f22ded354 100644 --- a/tests/unit/unittest/unsavedfiletest.cpp +++ b/tests/unit/unittest/unsavedfiletest.cpp @@ -150,4 +150,25 @@ TEST_F(UnsavedFile, Replace) ASSERT_THAT(unsavedFile, IsUnsavedFile(filePath, expectedContent, expectedContent.byteSize())); } +TEST_F(UnsavedFile, HasNoCharacterForTooBigOffset) +{ + ::UnsavedFile unsavedFile(filePath, fileContent); + + ASSERT_FALSE(unsavedFile.hasCharacterAt(100, 'x')); +} + +TEST_F(UnsavedFile, HasNoCharacterForWrongOffset) +{ + ::UnsavedFile unsavedFile(filePath, fileContent); + + ASSERT_FALSE(unsavedFile.hasCharacterAt(0, 'x')); +} + +TEST_F(UnsavedFile, HasCharacterForCorrectOffset) +{ + ::UnsavedFile unsavedFile(filePath, fileContent); + + ASSERT_TRUE(unsavedFile.hasCharacterAt(0, 'c')); +} + } // anonymous namespace