PY-59594 PEP 701: Stop recovery of incomplete f-string fragments at statement-only keywords

(cherry picked from commit 7aa912a43d63f6c9867d772e120c5b45adb10be3)

IJ-MR-115763

GitOrigin-RevId: ca6909a8e7a59e06eae69b631160e4d48541ce58
This commit is contained in:
Mikhail Golubev
2023-09-29 09:33:45 +00:00
committed by intellij-monorepo-bot
parent 37d25ee815
commit b3351b941e
8 changed files with 140 additions and 1 deletions
@@ -259,6 +259,7 @@ public class PythonIndentingProcessor extends MergingLexerAdapter {
}
private void adjustBraceLevel() {
boolean insideFStringFragment = !myFStringStack.isEmpty() && !myFStringStack.peek().fragments.isEmpty();
final IElementType tokenType = getTokenType();
if (PyTokenTypes.OPEN_BRACES.contains(tokenType)) {
myBraceLevel++;
@@ -266,8 +267,11 @@ public class PythonIndentingProcessor extends MergingLexerAdapter {
else if (PyTokenTypes.CLOSE_BRACES.contains(tokenType)) {
myBraceLevel--;
}
else if (myBraceLevel != 0 && RECOVERY_TOKENS.contains(tokenType)) {
else if ((myBraceLevel != 0 || insideFStringFragment) && RECOVERY_TOKENS.contains(tokenType)) {
myBraceLevel = 0;
if (insideFStringFragment) {
myFStringStack.clear();
}
final int pos = getTokenStart();
pushToken(PyTokenTypes.STATEMENT_BREAK, pos, pos);
final int indents = myIndentStack.size();
@@ -0,0 +1,3 @@
s = f'{foo:{bar'
print(42)
raise Exception
@@ -0,0 +1,39 @@
PyFile:FormatPartFStringFragmentRecoveryStoppedAtStatementOnlyKeyword.py
PyAssignmentStatement
PyTargetExpression: s
PsiElement(Py:IDENTIFIER)('s')
PsiWhiteSpace(' ')
PsiElement(Py:EQ)('=')
PsiWhiteSpace(' ')
PyStringLiteralExpression: {foo:{bar'
print(42)
PyFormattedStringElement
PsiElement(Py:FSTRING_START)('f'')
PyFStringFragment
PsiElement(Py:FSTRING_FRAGMENT_START)('{')
PyReferenceExpression: foo
PsiElement(Py:IDENTIFIER)('foo')
PyFStringFragmentFormatPart
PsiElement(Py:FSTRING_FRAGMENT_FORMAT_START)(':')
PyFStringFragment
PsiElement(Py:FSTRING_FRAGMENT_START)('{')
PyReferenceExpression: bar
PsiElement(Py:IDENTIFIER)('bar')
PsiErrorElement:Unexpected expression part
PsiElement(Py:SINGLE_QUOTED_STRING)(''')
PsiWhiteSpace('\n')
PsiElement(Py:IDENTIFIER)('print')
PsiElement(Py:LPAR)('(')
PsiElement(Py:INTEGER_LITERAL)('42')
PsiElement(Py:RPAR)(')')
PsiWhiteSpace('\n')
PsiErrorElement:Type conversion, ':' or '}' expected
<empty list>
PsiErrorElement:' expected
<empty list>
PyRaiseStatement
PsiElement(Py:RAISE_KEYWORD)('raise')
PsiWhiteSpace(' ')
PyReferenceExpression: Exception
PsiElement(Py:IDENTIFIER)('Exception')
@@ -0,0 +1,3 @@
s = f'{foo'
print(42)
raise Exception
@@ -0,0 +1,33 @@
PyFile:IncompleteFStringFragmentRecoveryStoppedAtStatementOnlyKeyword.py
PyAssignmentStatement
PyTargetExpression: s
PsiElement(Py:IDENTIFIER)('s')
PsiWhiteSpace(' ')
PsiElement(Py:EQ)('=')
PsiWhiteSpace(' ')
PyStringLiteralExpression: {foo'
print(42)
PyFormattedStringElement
PsiElement(Py:FSTRING_START)('f'')
PyFStringFragment
PsiElement(Py:FSTRING_FRAGMENT_START)('{')
PyReferenceExpression: foo
PsiElement(Py:IDENTIFIER)('foo')
PsiErrorElement:Unexpected expression part
PsiElement(Py:SINGLE_QUOTED_STRING)(''')
PsiWhiteSpace('\n')
PsiElement(Py:IDENTIFIER)('print')
PsiElement(Py:LPAR)('(')
PsiElement(Py:INTEGER_LITERAL)('42')
PsiElement(Py:RPAR)(')')
PsiWhiteSpace('\n')
PsiErrorElement:Type conversion, ':' or '}' expected
<empty list>
PsiErrorElement:' expected
<empty list>
PyRaiseStatement
PsiElement(Py:RAISE_KEYWORD)('raise')
PsiWhiteSpace(' ')
PyReferenceExpression: Exception
PsiElement(Py:IDENTIFIER)('Exception')
@@ -0,0 +1,3 @@
s = f'{f"{foo"'
print(42)
raise Exception
@@ -0,0 +1,42 @@
PyFile:NestedIncompleteFStringFragmentRecoveryStoppedAtStatementOnlyKeyword.py
PyAssignmentStatement
PyTargetExpression: s
PsiElement(Py:IDENTIFIER)('s')
PsiWhiteSpace(' ')
PsiElement(Py:EQ)('=')
PsiWhiteSpace(' ')
PyStringLiteralExpression: {f"{foo"'
print(42)
PyFormattedStringElement
PsiElement(Py:FSTRING_START)('f'')
PyFStringFragment
PsiElement(Py:FSTRING_FRAGMENT_START)('{')
PyStringLiteralExpression: {foo"'
print(42)
PyFormattedStringElement
PsiElement(Py:FSTRING_START)('f"')
PyFStringFragment
PsiElement(Py:FSTRING_FRAGMENT_START)('{')
PyReferenceExpression: foo
PsiElement(Py:IDENTIFIER)('foo')
PsiErrorElement:Unexpected expression part
PsiElement(Py:SINGLE_QUOTED_STRING)('"'')
PsiWhiteSpace('\n')
PsiElement(Py:IDENTIFIER)('print')
PsiElement(Py:LPAR)('(')
PsiElement(Py:INTEGER_LITERAL)('42')
PsiElement(Py:RPAR)(')')
PsiWhiteSpace('\n')
PsiErrorElement:Type conversion, ':' or '}' expected
<empty list>
PsiErrorElement:" expected
<empty list>
PsiErrorElement:' expected
<empty list>
PyRaiseStatement
PsiElement(Py:RAISE_KEYWORD)('raise')
PsiWhiteSpace(' ')
PyReferenceExpression: Exception
PsiElement(Py:IDENTIFIER)('Exception')
@@ -861,6 +861,18 @@ public class PythonParsingTest extends ParsingTestCase {
doTest(LanguageLevel.PYTHON36);
}
public void testIncompleteFStringFragmentRecoveryStoppedAtStatementOnlyKeyword() {
doTest(LanguageLevel.PYTHON36);
}
public void testNestedIncompleteFStringFragmentRecoveryStoppedAtStatementOnlyKeyword() {
doTest(LanguageLevel.PYTHON36);
}
public void testFormatPartFStringFragmentRecoveryStoppedAtStatementOnlyKeyword() {
doTest(LanguageLevel.PYTHON36);
}
// PY-19036
public void testAwaitInNonAsyncNestedFunction() {
doTest(LanguageLevel.PYTHON35);