From 8d0391a4f9b91fb15ee3d20db725c7a37c4646aa Mon Sep 17 00:00:00 2001 From: Ivan Donchevskii Date: Mon, 20 Aug 2018 13:15:13 +0200 Subject: [PATCH] Clang: Treat brace initialization as constructor completion Try to complete constructor after left brace with fallback to normal completion. Task-number: QTCREATORBUG-20957 Change-Id: I6c33790a3ee1e623a3d8abe9a44cfd821b6f3106 Reviewed-by: Marco Bubke --- src/libs/cplusplus/ExpressionUnderCursor.cpp | 2 +- ...langactivationsequencecontextprocessor.cpp | 6 ++-- .../clangactivationsequencecontextprocessor.h | 2 +- .../clangactivationsequenceprocessor.cpp | 9 ++++++ .../clangactivationsequenceprocessor.h | 1 + .../clangcompletionassistprocessor.cpp | 28 +++++++++++-------- .../clangcompletioncontextanalyzer.cpp | 2 +- .../clangcompletioncontextanalyzer-test.cpp | 21 ++++++++++++++ 8 files changed, 54 insertions(+), 17 deletions(-) diff --git a/src/libs/cplusplus/ExpressionUnderCursor.cpp b/src/libs/cplusplus/ExpressionUnderCursor.cpp index 0b4405412bc..e176cbde355 100644 --- a/src/libs/cplusplus/ExpressionUnderCursor.cpp +++ b/src/libs/cplusplus/ExpressionUnderCursor.cpp @@ -262,7 +262,7 @@ int ExpressionUnderCursor::startOfFunctionCall(const QTextCursor &cursor) const if (tk.is(T_EOF_SYMBOL)) { break; - } else if (tk.is(T_LPAREN)) { + } else if (tk.is(T_LPAREN) || tk.is(T_LBRACE)) { return scanner.startPosition() + tk.utf16charsBegin(); } else if (tk.is(T_RPAREN)) { int matchingBrace = scanner.startOfMatchingBrace(index); diff --git a/src/plugins/clangcodemodel/clangactivationsequencecontextprocessor.cpp b/src/plugins/clangcodemodel/clangactivationsequencecontextprocessor.cpp index 2b364a1d8c5..57f34581cc3 100644 --- a/src/plugins/clangcodemodel/clangactivationsequencecontextprocessor.cpp +++ b/src/plugins/clangcodemodel/clangactivationsequencecontextprocessor.cpp @@ -83,7 +83,7 @@ void ActivationSequenceContextProcessor::process() processComment(); processInclude(); processSlashOutsideOfAString(); - processLeftParen(); + processLeftParenOrBrace(); processPreprocessorInclude(); } @@ -163,9 +163,9 @@ void ActivationSequenceContextProcessor::processSlashOutsideOfAString() m_completionKind = CPlusPlus::T_EOF_SYMBOL; } -void ActivationSequenceContextProcessor::processLeftParen() +void ActivationSequenceContextProcessor::processLeftParenOrBrace() { - if (m_completionKind == CPlusPlus::T_LPAREN) { + if (m_completionKind == CPlusPlus::T_LPAREN || m_completionKind == CPlusPlus::T_LBRACE) { if (m_tokenIndex > 0) { // look at the token at the left of T_LPAREN const CPlusPlus::Token &previousToken = m_tokens.at(m_tokenIndex - 1); diff --git a/src/plugins/clangcodemodel/clangactivationsequencecontextprocessor.h b/src/plugins/clangcodemodel/clangactivationsequencecontextprocessor.h index bffd8d0d262..ca84f1663b9 100644 --- a/src/plugins/clangcodemodel/clangactivationsequencecontextprocessor.h +++ b/src/plugins/clangcodemodel/clangactivationsequencecontextprocessor.h @@ -67,7 +67,7 @@ protected: void processComment(); void processInclude(); void processSlashOutsideOfAString(); - void processLeftParen(); + void processLeftParenOrBrace(); void processPreprocessorInclude(); void resetPositionsForEOFCompletionKind(); diff --git a/src/plugins/clangcodemodel/clangactivationsequenceprocessor.cpp b/src/plugins/clangcodemodel/clangactivationsequenceprocessor.cpp index 27ddb77a0e5..975c08eff9e 100644 --- a/src/plugins/clangcodemodel/clangactivationsequenceprocessor.cpp +++ b/src/plugins/clangcodemodel/clangactivationsequenceprocessor.cpp @@ -90,6 +90,7 @@ void ActivationSequenceProcessor::process() processDot(); processComma(); processLeftParen(); + processLeftBrace(); processColonColon(); processArrow(); processDotStar(); @@ -125,6 +126,14 @@ void ActivationSequenceProcessor::processLeftParen() } } +void ActivationSequenceProcessor::processLeftBrace() +{ + if (m_char3 == QLatin1Char('{') && m_wantFunctionCall) { + m_completionKind = CPlusPlus::T_LBRACE; + m_offset = 1; + } +} + void ActivationSequenceProcessor::processColonColon() { if (m_char2 == QLatin1Char(':') && m_char3 == QLatin1Char(':')) { diff --git a/src/plugins/clangcodemodel/clangactivationsequenceprocessor.h b/src/plugins/clangcodemodel/clangactivationsequenceprocessor.h index d8d4d77ed2e..9f70b2633e7 100644 --- a/src/plugins/clangcodemodel/clangactivationsequenceprocessor.h +++ b/src/plugins/clangcodemodel/clangactivationsequenceprocessor.h @@ -49,6 +49,7 @@ private: void processDot(); void processComma(); void processLeftParen(); + void processLeftBrace(); void processColonColon(); void processArrow(); void processDotStar(); diff --git a/src/plugins/clangcodemodel/clangcompletionassistprocessor.cpp b/src/plugins/clangcodemodel/clangcompletionassistprocessor.cpp index 2afa03a3b4e..dba3e380f3c 100644 --- a/src/plugins/clangcodemodel/clangcompletionassistprocessor.cpp +++ b/src/plugins/clangcodemodel/clangcompletionassistprocessor.cpp @@ -188,7 +188,8 @@ IAssistProposal *ClangCompletionAssistProcessor::perform(const AssistInterface * static CodeCompletions filterFunctionSignatures(const CodeCompletions &completions) { return ::Utils::filtered(completions, [](const CodeCompletion &completion) { - return completion.completionKind == CodeCompletion::FunctionOverloadCompletionKind; + return completion.completionKind == CodeCompletion::FunctionOverloadCompletionKind + || completion.completionKind == CodeCompletion::ConstructorCompletionKind; }); } @@ -197,19 +198,24 @@ void ClangCompletionAssistProcessor::handleAvailableCompletions( { QTC_CHECK(m_completions.isEmpty()); - if (m_sentRequestType == NormalCompletion) { - m_completions = toAssistProposalItems(completions, m_interface.data()); - - if (m_addSnippets && !m_completions.isEmpty()) - addSnippets(); - - setAsyncProposalAvailable(createProposal()); - } else { + if (m_sentRequestType == FunctionHintCompletion){ const CodeCompletions functionSignatures = filterFunctionSignatures(completions); - if (!functionSignatures.isEmpty()) + if (!functionSignatures.isEmpty()) { setAsyncProposalAvailable(createFunctionHintProposal(functionSignatures)); - // else: Not a function call, but e.g. a function declaration like "void f(" + return; + } + // else: Proceed with a normal completion in case: + // 1) it was not a function call, but e.g. a function declaration like "void f(" + // 2) '{' meant not a constructor call. } + + //m_sentRequestType == NormalCompletion or function signatures were empty + m_completions = toAssistProposalItems(completions, m_interface.data()); + + if (m_addSnippets && !m_completions.isEmpty()) + addSnippets(); + + setAsyncProposalAvailable(createProposal()); } const TextEditorWidget *ClangCompletionAssistProcessor::textEditorWidget() const diff --git a/src/plugins/clangcodemodel/clangcompletioncontextanalyzer.cpp b/src/plugins/clangcodemodel/clangcompletioncontextanalyzer.cpp index 1f3f5bfebb1..7cc97217121 100644 --- a/src/plugins/clangcodemodel/clangcompletioncontextanalyzer.cpp +++ b/src/plugins/clangcodemodel/clangcompletioncontextanalyzer.cpp @@ -145,7 +145,7 @@ void ClangCompletionContextAnalyzer::handleCommaInFunctionCall() void ClangCompletionContextAnalyzer::handleFunctionCall(int afterOperatorPosition) { - if (m_completionOperator == T_LPAREN) { + if (m_completionOperator == T_LPAREN || m_completionOperator == T_LBRACE) { ExpressionUnderCursor expressionUnderCursor(m_languageFeatures); QTextCursor textCursor(m_interface->textDocument()); textCursor.setPosition(m_positionEndOfExpression); diff --git a/tests/unit/unittest/clangcompletioncontextanalyzer-test.cpp b/tests/unit/unittest/clangcompletioncontextanalyzer-test.cpp index 9047b091656..2f43ed76af6 100644 --- a/tests/unit/unittest/clangcompletioncontextanalyzer-test.cpp +++ b/tests/unit/unittest/clangcompletioncontextanalyzer-test.cpp @@ -269,6 +269,27 @@ TEST_F(ClangCompletionContextAnalyzer, WhitespaceAfterFunctionName) ASSERT_THAT(analyzer, HasResult(CCA::PassThroughToLibClangAfterLeftParen, 0, 0, positionInText)); } +TEST_F(ClangCompletionContextAnalyzer, ConstructorCallWithBraceInitializer) +{ + auto analyzer = runAnalyzer("f{@"); + + ASSERT_THAT(analyzer, HasResult(CCA::PassThroughToLibClangAfterLeftParen, 0, 0, positionInText)); +} + +TEST_F(ClangCompletionContextAnalyzer, ArgumentTwoWithSpaceAtConstructorCallWithBraceInitializer) +{ + auto analyzer = runAnalyzer("f{1, @"); + + ASSERT_THAT(analyzer, HasResult(CCA::PassThroughToLibClangAfterLeftParen, -3, -3, positionInText)); +} + +TEST_F(ClangCompletionContextAnalyzer, WhitespaceBeforeConstructorCallWithBraceInitializer) +{ + auto analyzer = runAnalyzer("foo {@"); + + ASSERT_THAT(analyzer, HasResult(CCA::PassThroughToLibClangAfterLeftParen, 0, 0, positionInText)); +} + TEST_F(ClangCompletionContextAnalyzer, AfterOpeningParenthesis) { auto analyzer = runAnalyzer("(@");