From 8b6a16c95b6c49e0737519df20c0826d542d8c17 Mon Sep 17 00:00:00 2001 From: Ivan Donchevskii Date: Tue, 28 Aug 2018 12:17:33 +0200 Subject: [PATCH] Clang: Fix completion after '{' Follow-up fix for 8d0391a4f9. Do not complete after '{' coming not after an identifier. Take constructor completions only for '{' and function completions only for '('. Filter constructor completions by class/struct type. Task-number: QTCREATORBUG-21004 Change-Id: I7ae2d6bee23cf907648c42b93eb12742942833f6 Reviewed-by: Marco Bubke --- src/libs/sqlite/utf8string.h | 10 ++++++ .../clangcompletionassistprocessor.cpp | 31 +++++++++++++++++-- .../cpptools/cppcompletionassistprocessor.cpp | 21 +++++++++++++ .../clangcompletioncontextanalyzer-test.cpp | 7 +++++ 4 files changed, 66 insertions(+), 3 deletions(-) diff --git a/src/libs/sqlite/utf8string.h b/src/libs/sqlite/utf8string.h index f23237d2bea..4d843c2f5fe 100644 --- a/src/libs/sqlite/utf8string.h +++ b/src/libs/sqlite/utf8string.h @@ -131,6 +131,16 @@ public: return byteArray.indexOf(text); } + int lastIndexOf(const Utf8String &text) const + { + return byteArray.lastIndexOf(text.byteArray); + } + + int lastIndexOf(const char *text) const + { + return byteArray.lastIndexOf(text); + } + int indexOf(char character) const { return byteArray.indexOf(character); diff --git a/src/plugins/clangcodemodel/clangcompletionassistprocessor.cpp b/src/plugins/clangcodemodel/clangcompletionassistprocessor.cpp index 1dd1a98671b..4c1f846aa84 100644 --- a/src/plugins/clangcodemodel/clangcompletionassistprocessor.cpp +++ b/src/plugins/clangcodemodel/clangcompletionassistprocessor.cpp @@ -190,8 +190,22 @@ IAssistProposal *ClangCompletionAssistProcessor::perform(const AssistInterface * static CodeCompletions filterFunctionSignatures(const CodeCompletions &completions) { return ::Utils::filtered(completions, [](const CodeCompletion &completion) { - return completion.completionKind == CodeCompletion::FunctionOverloadCompletionKind - || completion.completionKind == CodeCompletion::ConstructorCompletionKind; + return completion.completionKind == CodeCompletion::FunctionOverloadCompletionKind; + }); +} + +static CodeCompletions filterConstructorSignatures(Utf8String textBefore, + const CodeCompletions &completions) +{ + const int prevStatementEnd = textBefore.lastIndexOf(";"); + if (prevStatementEnd != -1) + textBefore = textBefore.mid(prevStatementEnd + 1); + + return ::Utils::filtered(completions, [&textBefore](const CodeCompletion &completion) { + if (completion.completionKind != CodeCompletion::ConstructorCompletionKind) + return false; + const Utf8String type = completion.chunks.at(0).text; + return textBefore.indexOf(type) != -1; }); } @@ -201,7 +215,18 @@ void ClangCompletionAssistProcessor::handleAvailableCompletions( QTC_CHECK(m_completions.isEmpty()); if (m_sentRequestType == FunctionHintCompletion){ - const CodeCompletions functionSignatures = filterFunctionSignatures(completions); + CodeCompletions functionSignatures; + if (m_completionOperator == T_LPAREN) { + functionSignatures = filterFunctionSignatures(completions); + } else { + const QTextBlock block = m_interface->textDocument()->findBlock( + m_interface->position()); + const QString textBefore = block.text().left( + m_interface->position() - block.position()); + + functionSignatures = filterConstructorSignatures(textBefore, completions); + } + if (!functionSignatures.isEmpty()) { setAsyncProposalAvailable(createFunctionHintProposal(functionSignatures)); return; diff --git a/src/plugins/cpptools/cppcompletionassistprocessor.cpp b/src/plugins/cpptools/cppcompletionassistprocessor.cpp index 17b75eb066b..07ba1acc488 100644 --- a/src/plugins/cpptools/cppcompletionassistprocessor.cpp +++ b/src/plugins/cpptools/cppcompletionassistprocessor.cpp @@ -68,6 +68,22 @@ static bool isDoxygenTagCompletionCharacter(const QChar &character) || character == QLatin1Char('@') ; } +static bool twoIndentifiersBeforeLBrace(const Tokens &tokens, int tokenIdx) +{ + const Token &previousToken = tokens.at(tokenIdx - 1); + if (previousToken.kind() != T_IDENTIFIER) + return false; + for (int index = tokenIdx - 2; index >= 0; index -= 2) { + const Token &token = tokens.at(index); + if (token.kind() == T_IDENTIFIER) + return true; + + if (token.kind() != T_COLON_COLON) + return false; + } + return false; +} + void CppCompletionAssistProcessor::startOfOperator(QTextDocument *textDocument, int positionInDocument, unsigned *kind, @@ -146,6 +162,11 @@ void CppCompletionAssistProcessor::startOfOperator(QTextDocument *textDocument, start = positionInDocument; } } + } else if (*kind == T_LBRACE) { + if (tokenIdx > 0 && !twoIndentifiersBeforeLBrace(tokens, tokenIdx)) { + *kind = T_EOF_SYMBOL; + start = positionInDocument; + } } // Check for include preprocessor directive else if (*kind == T_STRING_LITERAL || *kind == T_ANGLE_STRING_LITERAL || *kind == T_SLASH diff --git a/tests/unit/unittest/clangcompletioncontextanalyzer-test.cpp b/tests/unit/unittest/clangcompletioncontextanalyzer-test.cpp index 2f43ed76af6..90c4ae268f3 100644 --- a/tests/unit/unittest/clangcompletioncontextanalyzer-test.cpp +++ b/tests/unit/unittest/clangcompletioncontextanalyzer-test.cpp @@ -290,6 +290,13 @@ TEST_F(ClangCompletionContextAnalyzer, WhitespaceBeforeConstructorCallWithBraceI ASSERT_THAT(analyzer, HasResult(CCA::PassThroughToLibClangAfterLeftParen, 0, 0, positionInText)); } +TEST_F(ClangCompletionContextAnalyzer, OpenFunctionScopeNotAConstructor) +{ + auto analyzer = runAnalyzer("foo() {@"); + + ASSERT_THAT(analyzer, HasResult(CCA::PassThroughToLibClang, 0, 0, positionInText)); +} + TEST_F(ClangCompletionContextAnalyzer, AfterOpeningParenthesis) { auto analyzer = runAnalyzer("(@");