PY-31442 Handle f-strings terminated by other string literals in fragments

When the terminating quote is either at the beginning of a string literal or
inside it, either in the given f-string itself or inside a nested one and
either in normal fragment or in a fragment located inside a format part of
another fragment. Normal string literals and nested f-strings are also checked
separately.
This commit is contained in:
Mikhail Golubev
2018-10-01 12:46:42 +03:00
parent 50383b4768
commit 68f72f3dd9
22 changed files with 1303 additions and 662 deletions
File diff suppressed because it is too large Load Diff
@@ -3,7 +3,7 @@ package com.jetbrains.python.lexer;
import com.intellij.lexer.FlexLexer;
import com.intellij.psi.tree.IElementType;
import com.jetbrains.python.PyTokenTypes;
import com.intellij.util.containers.ContainerUtil;import com.jetbrains.python.PyTokenTypes;
import com.intellij.util.containers.Stack;
import com.intellij.openapi.util.text.StringUtil;
import com.jetbrains.python.psi.PyStringLiteralUtil;
@@ -136,10 +136,7 @@ boolean hasMatchingFStringStart(String endQuotes){
if (endQuotes.startsWith(state.quotes)){
final int nestedNum = fStringStates.size() - i;
for(int j = 0; j < nestedNum; j++) {
final FStringState removedFString = fStringStates.pop();
while (!fragmentStates.isEmpty() && fragmentStates.peek().containingFString == removedFString) {
fragmentStates.pop();
}
popFString();
}
yybegin(state.oldState);
final int unmatchedQuotes = endQuotes.length() - state.quotes.length();
@@ -150,6 +147,47 @@ boolean hasMatchingFStringStart(String endQuotes){
return false;
}
private void popFString() {
final FStringState removedFString = fStringStates.pop();
while (!fragmentStates.isEmpty() && fragmentStates.peek().containingFString == removedFString) {
fragmentStates.pop();
}
}
IElementType findFStringEndInStringLiteral(String stringLiteral, IElementType stringType){
int i = 0;
while(i < stringLiteral.length()) {
final char c = stringLiteral.charAt(i);
if (c == '\\') {
i += 2;
continue;
}
final int prefixLength = PyStringLiteralUtil.getPrefixLength(stringLiteral);
final String nextThree = stringLiteral.substring(i, Math.min(stringLiteral.length(), i + 3));
for(int j = fStringStates.size() - 1; j >= 0; j--) {
final FStringState state = fStringStates.get(j);
if (nextThree.startsWith(state.quotes)){
if (i == 0) {
final int nestedNum = fStringStates.size() - j;
for(int k = 0; k < nestedNum; k++) {
popFString();
}
yybegin(state.oldState);
final int unmatched = stringLiteral.length() - state.quotes.length();
yypushback(unmatched);
return PyTokenTypes.FSTRING_END;
}
else {
yypushback(stringLiteral.length() - i);
return i == prefixLength ? PyTokenTypes.IDENTIFIER : stringType;
}
}
}
i++;
}
return stringType;
}
private final Stack<FragmentState> fragmentStates = new Stack<>();
private final Stack<FStringState> fStringStates = new Stack<>();
@@ -194,6 +232,9 @@ return yylength()-s.length();
":" { if (braceBalance == 0) { yybegin(FSTRING_FRAGMENT_FORMAT); return PyTokenTypes.FSTRING_FRAGMENT_FORMAT_START; }
else { return PyTokenTypes.COLON; } }
{SINGLE_QUOTED_STRING} { return findFStringEndInStringLiteral(yytext().toString(), PyTokenTypes.SINGLE_QUOTED_STRING); }
{TRIPLE_QUOTED_LITERAL} { return findFStringEndInStringLiteral(yytext().toString(), PyTokenTypes.TRIPLE_QUOTED_STRING); }
// Should be impossible inside expression fragments: any quotes should be matched as a string literal there
{FSTRING_QUOTES} { return hasMatchingFStringStart(yytext().toString()) ? PyTokenTypes.FSTRING_END : PyTokenTypes.FSTRING_TEXT; }
}
@@ -324,7 +365,12 @@ return PyTokenTypes.DOCSTRING; }
"=" { return PyTokenTypes.EQ; }
";" { return PyTokenTypes.SEMICOLON; }
{FSTRING_START} { pushFString(); return PyTokenTypes.FSTRING_START; }
{FSTRING_START} { IElementType type = findFStringEndInStringLiteral(yytext().toString(), PyTokenTypes.FSTRING_START);
if (type == PyTokenTypes.FSTRING_START) {
pushFString();
}
return type;
}
[^] { return PyTokenTypes.BAD_CHARACTER; }
}
@@ -0,0 +1,22 @@
PyFile:FStringTerminatedByQuoteInsideFStringLiteral.py
PyAssignmentStatement
PyTargetExpression: s
PsiElement(Py:IDENTIFIER)('s')
PsiWhiteSpace(' ')
PsiElement(Py:EQ)('=')
PsiWhiteSpace(' ')
PyStringLiteralExpression: }baz'
PyFormattedStringNode
PsiElement(Py:FSTRING_START)('f'')
PsiElement(Py:FSTRING_TEXT)('foo')
PyFStringFragment
PsiElement(Py:FSTRING_FRAGMENT_START)('{')
PyStringLiteralExpression:
PyFormattedStringNode
PsiElement(Py:FSTRING_START)('f"')
PsiErrorElement:Expected "
<empty list>
PsiErrorElement:type conversion, : or } expected
<empty list>
PsiElement(Py:FSTRING_END)(''')
PsiElement(Py:SINGLE_QUOTED_STRING)('"}baz'')
@@ -0,0 +1,27 @@
PyFile:FStringTerminatedByQuoteInsideFStringLiteralInFormatPart.py
PyAssignmentStatement
PyTargetExpression: s
PsiElement(Py:IDENTIFIER)('s')
PsiWhiteSpace(' ')
PsiElement(Py:EQ)('=')
PsiWhiteSpace(' ')
PyStringLiteralExpression: }}'
PyFormattedStringNode
PsiElement(Py:FSTRING_START)('f'')
PyFStringFragment
PsiElement(Py:FSTRING_FRAGMENT_START)('{')
PyNumericLiteralExpression
PsiElement(Py:INTEGER_LITERAL)('42')
PyFStringFragmentFormatPart
PsiElement(Py:FSTRING_FRAGMENT_FORMAT_START)(':')
PyFStringFragment
PsiElement(Py:FSTRING_FRAGMENT_START)('{')
PyStringLiteralExpression:
PyFormattedStringNode
PsiElement(Py:FSTRING_START)('f"')
PsiErrorElement:Expected "
<empty list>
PsiErrorElement:type conversion, : or } expected
<empty list>
PsiElement(Py:FSTRING_END)(''')
PsiElement(Py:SINGLE_QUOTED_STRING)('"}}'')
@@ -0,0 +1,37 @@
PyFile:FStringTerminatedByQuoteInsideNestedFStringLiteral.py
PyAssignmentStatement
PyTargetExpression: s
PsiElement(Py:IDENTIFIER)('s')
PsiWhiteSpace(' ')
PsiElement(Py:EQ)('=')
PsiWhiteSpace(' ')
PyStringLiteralExpression: }
PyFormattedStringNode
PsiElement(Py:FSTRING_START)('f'')
PyFStringFragment
PsiElement(Py:FSTRING_FRAGMENT_START)('{')
PyStringLiteralExpression:
PyFormattedStringNode
PsiElement(Py:FSTRING_START)('f"""')
PyFStringFragment
PsiElement(Py:FSTRING_FRAGMENT_START)('{')
PyStringLiteralExpression:
PyFormattedStringNode
PsiElement(Py:FSTRING_START)('f"')
PsiErrorElement:Expected "
<empty list>
PsiErrorElement:type conversion, : or } expected
<empty list>
PsiErrorElement:Expected """
<empty list>
PsiElement(Py:FSTRING_END)(''')
PsiElement(Py:SINGLE_QUOTED_STRING)('"}"')
PsiElement(Py:SINGLE_QUOTED_STRING)('""')
PsiErrorElement:End of statement expected
<empty list>
PsiElement(Py:RBRACE)('}')
PsiErrorElement:Statement expected, found Py:RBRACE
<empty list>
PyExpressionStatement
PyStringLiteralExpression:
PsiElement(Py:SINGLE_QUOTED_STRING)(''')
@@ -1 +1 @@
s = f'{f"""{42:f"'"}"""}'
s = f'{f"""{42:{f"'"}}"""}'
@@ -0,0 +1,43 @@
PyFile:FStringTerminatedByQuoteInsideNestedFStringLiteralInFormatPart.py
PyAssignmentStatement
PyTargetExpression: s
PsiElement(Py:IDENTIFIER)('s')
PsiWhiteSpace(' ')
PsiElement(Py:EQ)('=')
PsiWhiteSpace(' ')
PyStringLiteralExpression: }}
PyFormattedStringNode
PsiElement(Py:FSTRING_START)('f'')
PyFStringFragment
PsiElement(Py:FSTRING_FRAGMENT_START)('{')
PyStringLiteralExpression:
PyFormattedStringNode
PsiElement(Py:FSTRING_START)('f"""')
PyFStringFragment
PsiElement(Py:FSTRING_FRAGMENT_START)('{')
PyNumericLiteralExpression
PsiElement(Py:INTEGER_LITERAL)('42')
PyFStringFragmentFormatPart
PsiElement(Py:FSTRING_FRAGMENT_FORMAT_START)(':')
PyFStringFragment
PsiElement(Py:FSTRING_FRAGMENT_START)('{')
PyStringLiteralExpression:
PyFormattedStringNode
PsiElement(Py:FSTRING_START)('f"')
PsiErrorElement:Expected "
<empty list>
PsiErrorElement:type conversion, : or } expected
<empty list>
PsiErrorElement:Expected """
<empty list>
PsiElement(Py:FSTRING_END)(''')
PsiElement(Py:SINGLE_QUOTED_STRING)('"}}"')
PsiElement(Py:SINGLE_QUOTED_STRING)('""')
PsiErrorElement:End of statement expected
<empty list>
PsiElement(Py:RBRACE)('}')
PsiErrorElement:Statement expected, found Py:RBRACE
<empty list>
PyExpressionStatement
PyStringLiteralExpression:
PsiElement(Py:SINGLE_QUOTED_STRING)(''')
@@ -0,0 +1,34 @@
PyFile:FStringTerminatedByQuoteInsideNestedStringLiteral.py
PyAssignmentStatement
PyTargetExpression: s
PsiElement(Py:IDENTIFIER)('s')
PsiWhiteSpace(' ')
PsiElement(Py:EQ)('=')
PsiWhiteSpace(' ')
PyStringLiteralExpression: }
PyFormattedStringNode
PsiElement(Py:FSTRING_START)('f'')
PyFStringFragment
PsiElement(Py:FSTRING_FRAGMENT_START)('{')
PyStringLiteralExpression:
PyFormattedStringNode
PsiElement(Py:FSTRING_START)('f"""')
PyFStringFragment
PsiElement(Py:FSTRING_FRAGMENT_START)('{')
PyStringLiteralExpression:
PsiElement(Py:SINGLE_QUOTED_STRING)('"')
PsiErrorElement:type conversion, : or } expected
<empty list>
PsiErrorElement:Expected """
<empty list>
PsiElement(Py:FSTRING_END)(''')
PsiElement(Py:SINGLE_QUOTED_STRING)('"}"')
PsiElement(Py:SINGLE_QUOTED_STRING)('""')
PsiErrorElement:End of statement expected
<empty list>
PsiElement(Py:RBRACE)('}')
PsiErrorElement:Statement expected, found Py:RBRACE
<empty list>
PyExpressionStatement
PyStringLiteralExpression:
PsiElement(Py:SINGLE_QUOTED_STRING)(''')
@@ -1 +1 @@
s = f'{f"""{42:"'"}"""}'
s = f'{f"""{42:{"'"}}"""}'
@@ -0,0 +1,40 @@
PyFile:FStringTerminatedByQuoteInsideNestedStringLiteralInFormatPart.py
PyAssignmentStatement
PyTargetExpression: s
PsiElement(Py:IDENTIFIER)('s')
PsiWhiteSpace(' ')
PsiElement(Py:EQ)('=')
PsiWhiteSpace(' ')
PyStringLiteralExpression: }}
PyFormattedStringNode
PsiElement(Py:FSTRING_START)('f'')
PyFStringFragment
PsiElement(Py:FSTRING_FRAGMENT_START)('{')
PyStringLiteralExpression:
PyFormattedStringNode
PsiElement(Py:FSTRING_START)('f"""')
PyFStringFragment
PsiElement(Py:FSTRING_FRAGMENT_START)('{')
PyNumericLiteralExpression
PsiElement(Py:INTEGER_LITERAL)('42')
PyFStringFragmentFormatPart
PsiElement(Py:FSTRING_FRAGMENT_FORMAT_START)(':')
PyFStringFragment
PsiElement(Py:FSTRING_FRAGMENT_START)('{')
PyStringLiteralExpression:
PsiElement(Py:SINGLE_QUOTED_STRING)('"')
PsiErrorElement:type conversion, : or } expected
<empty list>
PsiErrorElement:Expected """
<empty list>
PsiElement(Py:FSTRING_END)(''')
PsiElement(Py:SINGLE_QUOTED_STRING)('"}}"')
PsiElement(Py:SINGLE_QUOTED_STRING)('""')
PsiErrorElement:End of statement expected
<empty list>
PsiElement(Py:RBRACE)('}')
PsiErrorElement:Statement expected, found Py:RBRACE
<empty list>
PyExpressionStatement
PyStringLiteralExpression:
PsiElement(Py:SINGLE_QUOTED_STRING)(''')
@@ -0,0 +1,19 @@
PyFile:FStringTerminatedByQuoteInsideStringLiteral.py
PyAssignmentStatement
PyTargetExpression: s
PsiElement(Py:IDENTIFIER)('s')
PsiWhiteSpace(' ')
PsiElement(Py:EQ)('=')
PsiWhiteSpace(' ')
PyStringLiteralExpression: }baz'
PyFormattedStringNode
PsiElement(Py:FSTRING_START)('f'')
PsiElement(Py:FSTRING_TEXT)('foo')
PyFStringFragment
PsiElement(Py:FSTRING_FRAGMENT_START)('{')
PyStringLiteralExpression:
PsiElement(Py:SINGLE_QUOTED_STRING)('"')
PsiErrorElement:type conversion, : or } expected
<empty list>
PsiElement(Py:FSTRING_END)(''')
PsiElement(Py:SINGLE_QUOTED_STRING)('"}baz'')
@@ -0,0 +1,24 @@
PyFile:FStringTerminatedByQuoteInsideStringLiteralInFormatPart.py
PyAssignmentStatement
PyTargetExpression: s
PsiElement(Py:IDENTIFIER)('s')
PsiWhiteSpace(' ')
PsiElement(Py:EQ)('=')
PsiWhiteSpace(' ')
PyStringLiteralExpression: }}'
PyFormattedStringNode
PsiElement(Py:FSTRING_START)('f'')
PyFStringFragment
PsiElement(Py:FSTRING_FRAGMENT_START)('{')
PyNumericLiteralExpression
PsiElement(Py:INTEGER_LITERAL)('42')
PyFStringFragmentFormatPart
PsiElement(Py:FSTRING_FRAGMENT_FORMAT_START)(':')
PyFStringFragment
PsiElement(Py:FSTRING_FRAGMENT_START)('{')
PyStringLiteralExpression:
PsiElement(Py:SINGLE_QUOTED_STRING)('"')
PsiErrorElement:type conversion, : or } expected
<empty list>
PsiElement(Py:FSTRING_END)(''')
PsiElement(Py:SINGLE_QUOTED_STRING)('"}}'')
@@ -0,0 +1,28 @@
PyFile:FStringTerminatedByQuoteOfFStringLiteral.py
PyAssignmentStatement
PyTargetExpression: s
PsiElement(Py:IDENTIFIER)('s')
PsiWhiteSpace(' ')
PsiElement(Py:EQ)('=')
PsiWhiteSpace(' ')
PyStringLiteralExpression:
PyFormattedStringNode
PsiElement(Py:FSTRING_START)('f'')
PsiElement(Py:FSTRING_TEXT)('foo')
PyFStringFragment
PsiElement(Py:FSTRING_FRAGMENT_START)('{')
PyReferenceExpression: f
PsiElement(Py:IDENTIFIER)('f')
PsiErrorElement:type conversion, : or } expected
<empty list>
PsiElement(Py:FSTRING_END)(''')
PsiErrorElement:End of statement expected
<empty list>
PyExpressionStatement
PyReferenceExpression: bar
PsiElement(Py:IDENTIFIER)('bar')
PsiErrorElement:End of statement expected
<empty list>
PyExpressionStatement
PyStringLiteralExpression: }baz
PsiElement(Py:SINGLE_QUOTED_STRING)(''}baz'')
@@ -0,0 +1,33 @@
PyFile:FStringTerminatedByQuoteOfFStringLiteralInFormatPart.py
PyAssignmentStatement
PyTargetExpression: s
PsiElement(Py:IDENTIFIER)('s')
PsiWhiteSpace(' ')
PsiElement(Py:EQ)('=')
PsiWhiteSpace(' ')
PyStringLiteralExpression:
PyFormattedStringNode
PsiElement(Py:FSTRING_START)('f'')
PyFStringFragment
PsiElement(Py:FSTRING_FRAGMENT_START)('{')
PyNumericLiteralExpression
PsiElement(Py:INTEGER_LITERAL)('42')
PyFStringFragmentFormatPart
PsiElement(Py:FSTRING_FRAGMENT_FORMAT_START)(':')
PyFStringFragment
PsiElement(Py:FSTRING_FRAGMENT_START)('{')
PyReferenceExpression: f
PsiElement(Py:IDENTIFIER)('f')
PsiErrorElement:type conversion, : or } expected
<empty list>
PsiElement(Py:FSTRING_END)(''')
PsiErrorElement:End of statement expected
<empty list>
PyExpressionStatement
PyReferenceExpression: foo
PsiElement(Py:IDENTIFIER)('foo')
PsiErrorElement:End of statement expected
<empty list>
PyExpressionStatement
PyStringLiteralExpression: }}
PsiElement(Py:SINGLE_QUOTED_STRING)(''}}'')
@@ -0,0 +1,34 @@
PyFile:FStringTerminatedByQuoteOfNestedFStringLiteral.py
PyAssignmentStatement
PyTargetExpression: s
PsiElement(Py:IDENTIFIER)('s')
PsiWhiteSpace(' ')
PsiElement(Py:EQ)('=')
PsiWhiteSpace(' ')
PyStringLiteralExpression:
PyFormattedStringNode
PsiElement(Py:FSTRING_START)('f'')
PyFStringFragment
PsiElement(Py:FSTRING_FRAGMENT_START)('{')
PyStringLiteralExpression:
PyFormattedStringNode
PsiElement(Py:FSTRING_START)('f"')
PyFStringFragment
PsiElement(Py:FSTRING_FRAGMENT_START)('{')
PyReferenceExpression: f
PsiElement(Py:IDENTIFIER)('f')
PsiErrorElement:type conversion, : or } expected
<empty list>
PsiErrorElement:Expected "
<empty list>
PsiElement(Py:FSTRING_END)(''')
PsiErrorElement:End of statement expected
<empty list>
PyExpressionStatement
PyReferenceExpression: foo
PsiElement(Py:IDENTIFIER)('foo')
PsiErrorElement:End of statement expected
<empty list>
PyExpressionStatement
PyStringLiteralExpression: }"}
PsiElement(Py:SINGLE_QUOTED_STRING)(''}"}'')
@@ -1 +1 @@
s = f'{f"{42:f'foo'}"}'
s = f'{f"{42:{f'foo'}}"}'
@@ -0,0 +1,40 @@
PyFile:FStringTerminatedByQuoteOfNestedFStringLiteralInFormatPart.py
PyAssignmentStatement
PyTargetExpression: s
PsiElement(Py:IDENTIFIER)('s')
PsiWhiteSpace(' ')
PsiElement(Py:EQ)('=')
PsiWhiteSpace(' ')
PyStringLiteralExpression:
PyFormattedStringNode
PsiElement(Py:FSTRING_START)('f'')
PyFStringFragment
PsiElement(Py:FSTRING_FRAGMENT_START)('{')
PyStringLiteralExpression:
PyFormattedStringNode
PsiElement(Py:FSTRING_START)('f"')
PyFStringFragment
PsiElement(Py:FSTRING_FRAGMENT_START)('{')
PyNumericLiteralExpression
PsiElement(Py:INTEGER_LITERAL)('42')
PyFStringFragmentFormatPart
PsiElement(Py:FSTRING_FRAGMENT_FORMAT_START)(':')
PyFStringFragment
PsiElement(Py:FSTRING_FRAGMENT_START)('{')
PyReferenceExpression: f
PsiElement(Py:IDENTIFIER)('f')
PsiErrorElement:type conversion, : or } expected
<empty list>
PsiErrorElement:Expected "
<empty list>
PsiElement(Py:FSTRING_END)(''')
PsiErrorElement:End of statement expected
<empty list>
PyExpressionStatement
PyReferenceExpression: foo
PsiElement(Py:IDENTIFIER)('foo')
PsiErrorElement:End of statement expected
<empty list>
PyExpressionStatement
PyStringLiteralExpression: }}"}
PsiElement(Py:SINGLE_QUOTED_STRING)(''}}"}'')
@@ -0,0 +1,32 @@
PyFile:FStringTerminatedByQuoteOfNestedStringLiteral.py
PyAssignmentStatement
PyTargetExpression: s
PsiElement(Py:IDENTIFIER)('s')
PsiWhiteSpace(' ')
PsiElement(Py:EQ)('=')
PsiWhiteSpace(' ')
PyStringLiteralExpression:
PyFormattedStringNode
PsiElement(Py:FSTRING_START)('f'')
PyFStringFragment
PsiElement(Py:FSTRING_FRAGMENT_START)('{')
PyStringLiteralExpression:
PyFormattedStringNode
PsiElement(Py:FSTRING_START)('f"')
PyFStringFragment
PsiElement(Py:FSTRING_FRAGMENT_START)('{')
PsiErrorElement:expression expected
<empty list>
PsiErrorElement:Expected "
<empty list>
PsiElement(Py:FSTRING_END)(''')
PsiErrorElement:End of statement expected
<empty list>
PyExpressionStatement
PyReferenceExpression: foo
PsiElement(Py:IDENTIFIER)('foo')
PsiErrorElement:End of statement expected
<empty list>
PyExpressionStatement
PyStringLiteralExpression: }"}
PsiElement(Py:SINGLE_QUOTED_STRING)(''}"}'')
@@ -1 +1 @@
s = f'{f"{42:'foo'}"}'
s = f'{f"{42:{'foo'}}"}'
@@ -0,0 +1,38 @@
PyFile:FStringTerminatedByQuoteOfNestedStringLiteralInFormatPart.py
PyAssignmentStatement
PyTargetExpression: s
PsiElement(Py:IDENTIFIER)('s')
PsiWhiteSpace(' ')
PsiElement(Py:EQ)('=')
PsiWhiteSpace(' ')
PyStringLiteralExpression:
PyFormattedStringNode
PsiElement(Py:FSTRING_START)('f'')
PyFStringFragment
PsiElement(Py:FSTRING_FRAGMENT_START)('{')
PyStringLiteralExpression:
PyFormattedStringNode
PsiElement(Py:FSTRING_START)('f"')
PyFStringFragment
PsiElement(Py:FSTRING_FRAGMENT_START)('{')
PyNumericLiteralExpression
PsiElement(Py:INTEGER_LITERAL)('42')
PyFStringFragmentFormatPart
PsiElement(Py:FSTRING_FRAGMENT_FORMAT_START)(':')
PyFStringFragment
PsiElement(Py:FSTRING_FRAGMENT_START)('{')
PsiErrorElement:expression expected
<empty list>
PsiErrorElement:Expected "
<empty list>
PsiElement(Py:FSTRING_END)(''')
PsiErrorElement:End of statement expected
<empty list>
PyExpressionStatement
PyReferenceExpression: foo
PsiElement(Py:IDENTIFIER)('foo')
PsiErrorElement:End of statement expected
<empty list>
PyExpressionStatement
PyStringLiteralExpression: }}"}
PsiElement(Py:SINGLE_QUOTED_STRING)(''}}"}'')
@@ -0,0 +1,26 @@
PyFile:FStringTerminatedByQuoteOfStringLiteral.py
PyAssignmentStatement
PyTargetExpression: s
PsiElement(Py:IDENTIFIER)('s')
PsiWhiteSpace(' ')
PsiElement(Py:EQ)('=')
PsiWhiteSpace(' ')
PyStringLiteralExpression:
PyFormattedStringNode
PsiElement(Py:FSTRING_START)('f'')
PsiElement(Py:FSTRING_TEXT)('foo')
PyFStringFragment
PsiElement(Py:FSTRING_FRAGMENT_START)('{')
PsiErrorElement:expression expected
<empty list>
PsiElement(Py:FSTRING_END)(''')
PsiErrorElement:End of statement expected
<empty list>
PyExpressionStatement
PyReferenceExpression: bar
PsiElement(Py:IDENTIFIER)('bar')
PsiErrorElement:End of statement expected
<empty list>
PyExpressionStatement
PyStringLiteralExpression: }baz
PsiElement(Py:SINGLE_QUOTED_STRING)(''}baz'')
@@ -0,0 +1,31 @@
PyFile:FStringTerminatedByQuoteOfStringLiteralInFormatPart.py
PyAssignmentStatement
PyTargetExpression: s
PsiElement(Py:IDENTIFIER)('s')
PsiWhiteSpace(' ')
PsiElement(Py:EQ)('=')
PsiWhiteSpace(' ')
PyStringLiteralExpression:
PyFormattedStringNode
PsiElement(Py:FSTRING_START)('f'')
PyFStringFragment
PsiElement(Py:FSTRING_FRAGMENT_START)('{')
PyNumericLiteralExpression
PsiElement(Py:INTEGER_LITERAL)('42')
PyFStringFragmentFormatPart
PsiElement(Py:FSTRING_FRAGMENT_FORMAT_START)(':')
PyFStringFragment
PsiElement(Py:FSTRING_FRAGMENT_START)('{')
PsiErrorElement:expression expected
<empty list>
PsiElement(Py:FSTRING_END)(''')
PsiErrorElement:End of statement expected
<empty list>
PyExpressionStatement
PyReferenceExpression: foo
PsiElement(Py:IDENTIFIER)('foo')
PsiErrorElement:End of statement expected
<empty list>
PyExpressionStatement
PyStringLiteralExpression: }}
PsiElement(Py:SINGLE_QUOTED_STRING)(''}}'')