Clang: fix completion after forward-declared class

Do not complete -> and . with global completions if
foo is a ptr/ref to forward-declared class.

Change-Id: I41e6745ffb07be1d973fe6a8132824f1b3bf7fb1
Reviewed-by: Nikolai Kosjar <nikolai.kosjar@qt.io>
This commit is contained in:
Ivan Donchevskii
2017-06-02 15:23:18 +02:00
parent 821a2ad665
commit 9a0eca15c5
7 changed files with 87 additions and 3 deletions

View File

@@ -61,6 +61,12 @@ bool ClangCodeCompleteResults::hasNoResultsForDotCompletion() const
return !hasResults() && isDotCompletion(); return !hasResults() && isDotCompletion();
} }
bool ClangCodeCompleteResults::hasUnknownContext() const
{
const unsigned long long contexts = clang_codeCompleteGetContexts(cxCodeCompleteResults);
return contexts == CXCompletionContext_Unknown;
}
bool ClangCodeCompleteResults::isDotCompletion() const bool ClangCodeCompleteResults::isDotCompletion() const
{ {
const unsigned long long contexts = clang_codeCompleteGetContexts(cxCodeCompleteResults); const unsigned long long contexts = clang_codeCompleteGetContexts(cxCodeCompleteResults);

View File

@@ -51,6 +51,7 @@ public:
bool hasResults() const; bool hasResults() const;
bool hasNoResultsForDotCompletion() const; bool hasNoResultsForDotCompletion() const;
bool hasUnknownContext() const;
bool isDotCompletion() const; bool isDotCompletion() const;
CXCodeCompleteResults *data() const; CXCodeCompleteResults *data() const;

View File

@@ -56,6 +56,22 @@ CodeCompletions toCodeCompletions(const ClangCodeCompleteResults &results)
return codeCompletions; return codeCompletions;
} }
void filterUnknownContextResults(ClangCodeCompleteResults &results,
const UnsavedFile &theUnsavedFile,
uint line,
uint column)
{
if (results.hasUnknownContext()) {
bool positionIsOk = false;
const uint position = theUnsavedFile.toUtf8Position(line, column - 1, &positionIsOk);
if (positionIsOk && (theUnsavedFile.hasCharacterAt(position, '.')
|| (theUnsavedFile.hasCharacterAt(position - 1, '-')
&& theUnsavedFile.hasCharacterAt(position, '>')))) {
results = {};
}
}
}
} // anonymous namespace } // anonymous namespace
CodeCompleter::CodeCompleter(const TranslationUnit &translationUnit, CodeCompleter::CodeCompleter(const TranslationUnit &translationUnit,
@@ -70,7 +86,7 @@ CodeCompletions CodeCompleter::complete(uint line, uint column)
neededCorrection_ = CompletionCorrection::NoCorrection; neededCorrection_ = CompletionCorrection::NoCorrection;
ClangCodeCompleteResults results = completeHelper(line, column); ClangCodeCompleteResults results = completeHelper(line, column);
filterUnknownContextResults(results, unsavedFile(), line, column);
tryDotArrowCorrectionIfNoResults(results, line, column); tryDotArrowCorrectionIfNoResults(results, line, column);
return toCodeCompletions(results); return toCodeCompletions(results);
@@ -139,6 +155,7 @@ ClangCodeCompleteResults CodeCompleter::completeWithArrowInsteadOfDot(uint line,
results = completeHelper(line, column + 1); results = completeHelper(line, column + 1);
if (results.hasResults()) if (results.hasResults())
neededCorrection_ = CompletionCorrection::DotToArrowCorrection; neededCorrection_ = CompletionCorrection::DotToArrowCorrection;
filterUnknownContextResults(results, unsavedFile(), line, column+1);
} }
return results; return results;

View File

@@ -176,6 +176,18 @@ protected:
readFileContent(QStringLiteral("/complete_withNoDotArrowCorrectionForColonColon.cpp")), readFileContent(QStringLiteral("/complete_withNoDotArrowCorrectionForColonColon.cpp")),
true true
}; };
ClangBackEnd::FileContainer dotArrowCorrectionForForwardDeclaredClassPointer{
Utf8StringLiteral(TESTDATA_DIR"/complete_withDotArrowCorrectionForForwardDeclaredClassPointer.cpp"),
projectPart.projectPartId(),
readFileContent(QStringLiteral("/complete_withDotArrowCorrectionForForwardDeclaredClassPointer.cpp")),
true
};
ClangBackEnd::FileContainer globalCompletionAfterForwardDeclaredClassPointer{
Utf8StringLiteral(TESTDATA_DIR"/complete_withGlobalCompletionAfterForwardDeclaredClassPointer.cpp"),
projectPart.projectPartId(),
readFileContent(QStringLiteral("/complete_withGlobalCompletionAfterForwardDeclaredClassPointer.cpp")),
true
};
}; };
using CodeCompleterSlowTest = CodeCompleter; using CodeCompleterSlowTest = CodeCompleter;
@@ -425,10 +437,18 @@ TEST_F(CodeCompleterSlowTest, NoDotArrowCorrectionForOnlyDot)
const ClangBackEnd::CodeCompletions completions = myCompleter.complete(5, 6); const ClangBackEnd::CodeCompletions completions = myCompleter.complete(5, 6);
ASSERT_TRUE(completions.isEmpty());
ASSERT_THAT(myCompleter.neededCorrection(), ClangBackEnd::CompletionCorrection::NoCorrection);
}
TEST_F(CodeCompleterSlowTest, GlobalCompletionForSpaceAfterOnlyDot)
{
auto myCompleter = setupCompleter(noDotArrowCorrectionForOnlyDotFileContainer);
const ClangBackEnd::CodeCompletions completions = myCompleter.complete(5, 7);
ASSERT_THAT(completions, ASSERT_THAT(completions,
Contains(IsCodeCompletion(Utf8StringLiteral("Foo"), Contains(IsCodeCompletion(Utf8StringLiteral("Foo"),
CodeCompletion::ClassCompletionKind))); CodeCompletion::ClassCompletionKind)));
ASSERT_THAT(myCompleter.neededCorrection(), ClangBackEnd::CompletionCorrection::NoCorrection);
} }
TEST_F(CodeCompleterSlowTest, NoDotArrowCorrectionForColonColon) TEST_F(CodeCompleterSlowTest, NoDotArrowCorrectionForColonColon)
@@ -439,6 +459,33 @@ TEST_F(CodeCompleterSlowTest, NoDotArrowCorrectionForColonColon)
ASSERT_THAT(myCompleter.neededCorrection(), ClangBackEnd::CompletionCorrection::NoCorrection); ASSERT_THAT(myCompleter.neededCorrection(), ClangBackEnd::CompletionCorrection::NoCorrection);
} }
TEST_F(CodeCompleterSlowTest, DotArrowCorrectionForForwardDeclaredClassPointer)
{
auto myCompleter = setupCompleter(dotArrowCorrectionForForwardDeclaredClassPointer);
const ClangBackEnd::CodeCompletions completions = myCompleter.complete(5, 9);
ASSERT_TRUE(completions.isEmpty());
ASSERT_THAT(myCompleter.neededCorrection(), ClangBackEnd::CompletionCorrection::DotToArrowCorrection);
}
TEST_F(CodeCompleterSlowTest, NoGlobalCompletionAfterForwardDeclaredClassPointer)
{
auto myCompleter = setupCompleter(globalCompletionAfterForwardDeclaredClassPointer);
const ClangBackEnd::CodeCompletions completions = myCompleter.complete(5, 10);
ASSERT_TRUE(completions.isEmpty());
ASSERT_THAT(myCompleter.neededCorrection(), ClangBackEnd::CompletionCorrection::NoCorrection);
}
TEST_F(CodeCompleterSlowTest, GlobalCompletionAfterForwardDeclaredClassPointer)
{
auto myCompleter = setupCompleter(globalCompletionAfterForwardDeclaredClassPointer);
const ClangBackEnd::CodeCompletions completions = myCompleter.complete(6, 4);
ASSERT_TRUE(!completions.isEmpty());
ASSERT_THAT(myCompleter.neededCorrection(), ClangBackEnd::CompletionCorrection::NoCorrection);
}
ClangBackEnd::CodeCompleter CodeCompleter::setupCompleter( ClangBackEnd::CodeCompleter CodeCompleter::setupCompleter(
const ClangBackEnd::FileContainer &fileContainer) const ClangBackEnd::FileContainer &fileContainer)
{ {

View File

@@ -0,0 +1,6 @@
struct Bar;
void g(Bar *bar)
{
bar.
}

View File

@@ -0,0 +1,7 @@
struct Bar;
void g(Bar *bar)
{
bar->
// white space preserver
}

View File

@@ -2,6 +2,6 @@ struct Foo { int member; };
void g(Foo *foo) void g(Foo *foo)
{ {
. . // white space preserver
} }