diff --git a/src/libs/cplusplus/MatchingText.cpp b/src/libs/cplusplus/MatchingText.cpp index c22d3fe89cf..59440813a70 100644 --- a/src/libs/cplusplus/MatchingText.cpp +++ b/src/libs/cplusplus/MatchingText.cpp @@ -358,6 +358,32 @@ static bool isAfterRecordLikeDefinition(const BackwardsScanner &tokens, int inde return false; } +static bool isControlFlowKeywordRequiringParentheses(const Token &token) +{ + return token.is(T_IF) + || token.is(T_WHILE) + || token.is(T_FOR) + || token.is(T_SWITCH) + || token.is(T_CATCH); +} + +static bool isAfterControlFlow(const BackwardsScanner &tokens, int index) +{ + const Token &token = tokens[index]; + if (token.is(T_DO) || token.is(T_ELSE) || token.is(T_TRY)) + return true; + + if (token.is(T_RPAREN)) { + const int startIndex = index + 1; + const int matchingBraceIndex = tokens.startOfMatchingBrace(startIndex); + if (matchingBraceIndex == startIndex) + return false; // No matching paren found. + return isControlFlowKeywordRequiringParentheses(tokens[matchingBraceIndex - 1]); + } + + return false; +} + static bool allowAutoClosingBrace(const QTextCursor &cursor, MatchingText::IsNextBlockDeeperIndented isNextIndented) { @@ -370,6 +396,9 @@ static bool allowAutoClosingBrace(const QTextCursor &cursor, if (tokens[index].isStringLiteral()) return false; + if (isAfterControlFlow(tokens, index)) + return false; + if (isAfterNamespaceDefinition(tokens, index)) return false; diff --git a/tests/unit/unittest/matchingtext-test.cpp b/tests/unit/unittest/matchingtext-test.cpp index 081e356764e..b49731d3e62 100644 --- a/tests/unit/unittest/matchingtext-test.cpp +++ b/tests/unit/unittest/matchingtext-test.cpp @@ -221,6 +221,41 @@ TEST_F(MatchingText, ContextAllowsAutoParentheses_CurlyBrace_NotInTheMiddle) ASSERT_FALSE(MT::contextAllowsAutoParentheses(document.cursor, "{")); } +TEST_F(MatchingText, ContextAllowsAutoParentheses_CurlyBrace_NotAfterControlFlow_WhileAndFriends) +{ + const Document document("while (true) @"); + + ASSERT_FALSE(MT::contextAllowsAutoParentheses(document.cursor, "{")); +} + +TEST_F(MatchingText, ContextAllowsAutoParentheses_CurlyBrace_NotAfterControlFlow_DoAndFriends) +{ + const Document document("do @"); + + ASSERT_FALSE(MT::contextAllowsAutoParentheses(document.cursor, "{")); +} + +TEST_F(MatchingText, ContextAllowsAutoParentheses_CurlyBrace_InvalidCode_UnbalancedParens) +{ + const Document document(") @"); + + ASSERT_TRUE(MT::contextAllowsAutoParentheses(document.cursor, "{")); +} + +TEST_F(MatchingText, ContextAllowsAutoParentheses_CurlyBrace_InvalidCode_UnbalancedParens2) +{ + const Document document("while true) @"); + + ASSERT_TRUE(MT::contextAllowsAutoParentheses(document.cursor, "{")); +} + +TEST_F(MatchingText, ContextAllowsAutoParentheses_CurlyBrace_InvalidCode_OnlyBalancedParens) +{ + const Document document("() @"); + + ASSERT_TRUE(MT::contextAllowsAutoParentheses(document.cursor, "{")); +} + TEST_F(MatchingText, ContextAllowsAutoParentheses_CurlyBrace_NotBeforeNamedNamespace) { const Document document("namespace X @");