mirror of
https://gitflic.ru/project/openide/openide.git
synced 2025-12-15 02:59:33 +07:00
PY-31442 Lexer handles line breaks in single quoted f-strings
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -66,6 +66,22 @@ class PyLexerFStringHelper(private val myLexer: FlexLexerEx) {
|
||||
}
|
||||
}
|
||||
|
||||
fun handleLineBreakInFragment(): IElementType {
|
||||
val text = myLexer.yytext().toString()
|
||||
// We will return a line break anyway, but we need to transit from FSTRING state of the lexer
|
||||
findFStringTerminator(text)
|
||||
return PyTokenTypes.LINE_BREAK
|
||||
}
|
||||
|
||||
fun handleLineBreakInLiteralText(): IElementType {
|
||||
val text = myLexer.yytext().toString()
|
||||
val (_, offset) = findFStringTerminator(text)
|
||||
if (offset == text.length) {
|
||||
return PyTokenTypes.FSTRING_TEXT
|
||||
}
|
||||
return PyTokenTypes.LINE_BREAK
|
||||
}
|
||||
|
||||
fun handleStringLiteral(stringLiteralType: IElementType): IElementType {
|
||||
val stringText = myLexer.yytext().toString()
|
||||
val prefixLength = PyStringLiteralUtil.getPrefixLength(stringText)
|
||||
@@ -86,13 +102,28 @@ class PyLexerFStringHelper(private val myLexer: FlexLexerEx) {
|
||||
i += 2
|
||||
continue
|
||||
}
|
||||
if (c == '\n') {
|
||||
val firstSingleQuotedIndex = myFStringStates.indexOfFirst { it.openingQuotes.length == 1 }
|
||||
if (firstSingleQuotedIndex >= 0) {
|
||||
if (i == 0) {
|
||||
myLexer.yybegin(myFStringStates[firstSingleQuotedIndex].oldState)
|
||||
myFStringStates.subList(firstSingleQuotedIndex, myFStringStates.size).clear()
|
||||
myLexer.yypushback(text.length - 1)
|
||||
}
|
||||
else {
|
||||
myLexer.yypushback(text.length - i)
|
||||
}
|
||||
return Pair(PyTokenTypes.LINE_BREAK, i)
|
||||
}
|
||||
}
|
||||
else {
|
||||
val nextThree = text.substring(i, Math.min(text.length, i + 3))
|
||||
for (j in myFStringStates.size - 1 downTo 0) {
|
||||
val state = myFStringStates[j]
|
||||
if (nextThree.startsWith(state.openingQuotes)) {
|
||||
if (i == 0) {
|
||||
myFStringStates.subList(i, myFStringStates.size).clear()
|
||||
myLexer.yybegin(state.oldState)
|
||||
myFStringStates.subList(j, myFStringStates.size).clear()
|
||||
val unmatched = text.length - state.openingQuotes.length
|
||||
myLexer.yypushback(unmatched)
|
||||
}
|
||||
@@ -102,6 +133,7 @@ class PyLexerFStringHelper(private val myLexer: FlexLexerEx) {
|
||||
return Pair(PyTokenTypes.FSTRING_END, i)
|
||||
}
|
||||
}
|
||||
}
|
||||
i++
|
||||
}
|
||||
return Pair(null, text.length)
|
||||
|
||||
@@ -2,10 +2,8 @@
|
||||
package com.jetbrains.python.lexer;
|
||||
|
||||
import com.intellij.psi.tree.IElementType;
|
||||
import com.intellij.util.containers.ContainerUtil;import com.jetbrains.python.PyTokenTypes;
|
||||
import com.intellij.util.containers.Stack;
|
||||
import com.jetbrains.python.PyTokenTypes;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.jetbrains.python.psi.PyStringLiteralUtil;
|
||||
|
||||
%%
|
||||
|
||||
@@ -104,6 +102,7 @@ return yylength()-s.length();
|
||||
|
||||
<FSTRING> {
|
||||
{FSTRING_TEXT_NO_QUOTES} { return PyTokenTypes.FSTRING_TEXT; }
|
||||
[\n] { return fStringHelper.handleLineBreakInLiteralText(); }
|
||||
{FSTRING_QUOTES} { return fStringHelper.handleFStringEnd(); }
|
||||
"{" { return fStringHelper.handleFragmentStart(); }
|
||||
}
|
||||
@@ -125,16 +124,19 @@ return yylength()-s.length();
|
||||
{SINGLE_QUOTED_STRING} { return fStringHelper.handleStringLiteral(PyTokenTypes.SINGLE_QUOTED_STRING); }
|
||||
{TRIPLE_QUOTED_LITERAL} { return fStringHelper.handleStringLiteral(PyTokenTypes.TRIPLE_QUOTED_STRING); }
|
||||
|
||||
[\n] { return fStringHelper.handleLineBreakInFragment(); }
|
||||
|
||||
// Should be impossible inside expression fragments: any openingQuotes should be matched as a string literal there
|
||||
// {FSTRING_QUOTES} { return hasMatchingFStringStart(yytext().toString()) ? PyTokenTypes.FSTRING_END : PyTokenTypes.FSTRING_TEXT; }
|
||||
}
|
||||
|
||||
<FSTRING_FRAGMENT_FORMAT> {
|
||||
{FSTRING_FORMAT_TEXT_NO_QUOTES} { return PyTokenTypes.FSTRING_TEXT; }
|
||||
[\n] { return fStringHelper.handleLineBreakInLiteralText(); }
|
||||
"{" { return fStringHelper.handleFragmentStart(); }
|
||||
"}" { return fStringHelper.handleFragmentEnd(); }
|
||||
|
||||
// format part of a fragment can contain openingQuotes
|
||||
// format part of a fragment can contain opening quotes
|
||||
{FSTRING_QUOTES} { return fStringHelper.handleFStringEnd(); }
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
s = f"{f'''{'\
|
||||
'}'''}"
|
||||
@@ -0,0 +1,26 @@
|
||||
PyFile:FStringTerminatedByEscapedLineBreakInNestedStringLiteral.py
|
||||
PyAssignmentStatement
|
||||
PyTargetExpression: s
|
||||
PsiElement(Py:IDENTIFIER)('s')
|
||||
PsiWhiteSpace(' ')
|
||||
PsiElement(Py:EQ)('=')
|
||||
PsiWhiteSpace(' ')
|
||||
PyStringLiteralExpression: {f'''{'\
|
||||
'}'''}
|
||||
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)(''\\n'')
|
||||
PsiElement(Py:FSTRING_FRAGMENT_END)('}')
|
||||
PsiElement(Py:FSTRING_END)(''''')
|
||||
PsiElement(Py:FSTRING_FRAGMENT_END)('}')
|
||||
PsiElement(Py:FSTRING_END)('"')
|
||||
@@ -0,0 +1,33 @@
|
||||
PyFile:FStringTerminatedByLineBreakInExpression.py
|
||||
PyAssignmentStatement
|
||||
PyTargetExpression: s
|
||||
PsiElement(Py:IDENTIFIER)('s')
|
||||
PsiWhiteSpace(' ')
|
||||
PsiElement(Py:EQ)('=')
|
||||
PsiWhiteSpace(' ')
|
||||
PyStringLiteralExpression: {1 +
|
||||
PyFormattedStringNode
|
||||
PsiElement(Py:FSTRING_START)('f'')
|
||||
PyFStringFragment
|
||||
PsiElement(Py:FSTRING_FRAGMENT_START)('{')
|
||||
PyBinaryExpression
|
||||
PyNumericLiteralExpression
|
||||
PsiElement(Py:INTEGER_LITERAL)('1')
|
||||
PsiWhiteSpace(' ')
|
||||
PsiElement(Py:PLUS)('+')
|
||||
PsiErrorElement:expression expected
|
||||
<empty list>
|
||||
PsiErrorElement:Unexpected f-string token
|
||||
<empty list>
|
||||
PsiWhiteSpace('\n')
|
||||
PyExpressionStatement
|
||||
PyNumericLiteralExpression
|
||||
PsiElement(Py:INTEGER_LITERAL)('2')
|
||||
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,42 @@
|
||||
PyFile:FStringTerminatedByLineBreakInExpressionInFormatPart.py
|
||||
PyAssignmentStatement
|
||||
PyTargetExpression: s
|
||||
PsiElement(Py:IDENTIFIER)('s')
|
||||
PsiWhiteSpace(' ')
|
||||
PsiElement(Py:EQ)('=')
|
||||
PsiWhiteSpace(' ')
|
||||
PyStringLiteralExpression: {42:{1 +
|
||||
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)('{')
|
||||
PyBinaryExpression
|
||||
PyNumericLiteralExpression
|
||||
PsiElement(Py:INTEGER_LITERAL)('1')
|
||||
PsiWhiteSpace(' ')
|
||||
PsiElement(Py:PLUS)('+')
|
||||
PsiErrorElement:expression expected
|
||||
<empty list>
|
||||
PsiErrorElement:Unexpected f-string token
|
||||
<empty list>
|
||||
PsiWhiteSpace('\n')
|
||||
PyExpressionStatement
|
||||
PyNumericLiteralExpression
|
||||
PsiElement(Py:INTEGER_LITERAL)('2')
|
||||
PsiErrorElement:End of statement expected
|
||||
<empty list>
|
||||
PsiElement(Py:RBRACE)('}')
|
||||
PsiErrorElement:Statement expected, found Py:RBRACE
|
||||
<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:FStringTerminatedByLineBreakInFormatPart.py
|
||||
PyAssignmentStatement
|
||||
PyTargetExpression: s
|
||||
PsiElement(Py:IDENTIFIER)('s')
|
||||
PsiWhiteSpace(' ')
|
||||
PsiElement(Py:EQ)('=')
|
||||
PsiWhiteSpace(' ')
|
||||
PyStringLiteralExpression: foo{42:bar
|
||||
PyFormattedStringNode
|
||||
PsiElement(Py:FSTRING_START)('f'')
|
||||
PsiElement(Py:FSTRING_TEXT)('foo')
|
||||
PyFStringFragment
|
||||
PsiElement(Py:FSTRING_FRAGMENT_START)('{')
|
||||
PyNumericLiteralExpression
|
||||
PsiElement(Py:INTEGER_LITERAL)('42')
|
||||
PyFStringFragmentFormatPart
|
||||
PsiElement(Py:FSTRING_FRAGMENT_FORMAT_START)(':')
|
||||
PsiElement(Py:FSTRING_TEXT)('bar')
|
||||
PsiErrorElement:} expected
|
||||
<empty list>
|
||||
PsiErrorElement:Unexpected f-string token
|
||||
<empty list>
|
||||
PsiWhiteSpace('\n')
|
||||
PyExpressionStatement
|
||||
PyReferenceExpression: baz
|
||||
PsiElement(Py:IDENTIFIER)('baz')
|
||||
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,22 @@
|
||||
PyFile:FStringTerminatedByLineBreakInLiteralPart.py
|
||||
PyAssignmentStatement
|
||||
PyTargetExpression: s
|
||||
PsiElement(Py:IDENTIFIER)('s')
|
||||
PsiWhiteSpace(' ')
|
||||
PsiElement(Py:EQ)('=')
|
||||
PsiWhiteSpace(' ')
|
||||
PyStringLiteralExpression: foo
|
||||
PyFormattedStringNode
|
||||
PsiElement(Py:FSTRING_START)('f'')
|
||||
PsiElement(Py:FSTRING_TEXT)('foo')
|
||||
PsiErrorElement:Unexpected f-string token
|
||||
<empty list>
|
||||
PsiWhiteSpace('\n')
|
||||
PyExpressionStatement
|
||||
PyReferenceExpression: bar
|
||||
PsiElement(Py:IDENTIFIER)('bar')
|
||||
PsiErrorElement:End of statement expected
|
||||
<empty list>
|
||||
PyExpressionStatement
|
||||
PyStringLiteralExpression:
|
||||
PsiElement(Py:SINGLE_QUOTED_STRING)(''')
|
||||
@@ -0,0 +1,40 @@
|
||||
PyFile:FStringTerminatedByLineBreakInNestedExpression.py
|
||||
PyAssignmentStatement
|
||||
PyTargetExpression: s
|
||||
PsiElement(Py:IDENTIFIER)('s')
|
||||
PsiWhiteSpace(' ')
|
||||
PsiElement(Py:EQ)('=')
|
||||
PsiWhiteSpace(' ')
|
||||
PyStringLiteralExpression: {f'{1 +
|
||||
PyFormattedStringNode
|
||||
PsiElement(Py:FSTRING_START)('f"')
|
||||
PyFStringFragment
|
||||
PsiElement(Py:FSTRING_FRAGMENT_START)('{')
|
||||
PyStringLiteralExpression: {1 +
|
||||
PyFormattedStringNode
|
||||
PsiElement(Py:FSTRING_START)('f'')
|
||||
PyFStringFragment
|
||||
PsiElement(Py:FSTRING_FRAGMENT_START)('{')
|
||||
PyBinaryExpression
|
||||
PyNumericLiteralExpression
|
||||
PsiElement(Py:INTEGER_LITERAL)('1')
|
||||
PsiWhiteSpace(' ')
|
||||
PsiElement(Py:PLUS)('+')
|
||||
PsiErrorElement:expression expected
|
||||
<empty list>
|
||||
PsiErrorElement:Unexpected f-string token
|
||||
<empty list>
|
||||
PsiErrorElement:Unexpected f-string token
|
||||
<empty list>
|
||||
PsiWhiteSpace('\n')
|
||||
PyExpressionStatement
|
||||
PyNumericLiteralExpression
|
||||
PsiElement(Py:INTEGER_LITERAL)('2')
|
||||
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,49 @@
|
||||
PyFile:FStringTerminatedByLineBreakInNestedExpressionInFormatPart.py
|
||||
PyAssignmentStatement
|
||||
PyTargetExpression: s
|
||||
PsiElement(Py:IDENTIFIER)('s')
|
||||
PsiWhiteSpace(' ')
|
||||
PsiElement(Py:EQ)('=')
|
||||
PsiWhiteSpace(' ')
|
||||
PyStringLiteralExpression: {f'{42:{1 +
|
||||
PyFormattedStringNode
|
||||
PsiElement(Py:FSTRING_START)('f"')
|
||||
PyFStringFragment
|
||||
PsiElement(Py:FSTRING_FRAGMENT_START)('{')
|
||||
PyStringLiteralExpression: {42:{1 +
|
||||
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)('{')
|
||||
PyBinaryExpression
|
||||
PyNumericLiteralExpression
|
||||
PsiElement(Py:INTEGER_LITERAL)('1')
|
||||
PsiWhiteSpace(' ')
|
||||
PsiElement(Py:PLUS)('+')
|
||||
PsiErrorElement:expression expected
|
||||
<empty list>
|
||||
PsiErrorElement:Unexpected f-string token
|
||||
<empty list>
|
||||
PsiErrorElement:Unexpected f-string token
|
||||
<empty list>
|
||||
PsiWhiteSpace('\n')
|
||||
PyExpressionStatement
|
||||
PyNumericLiteralExpression
|
||||
PsiElement(Py:INTEGER_LITERAL)('2')
|
||||
PsiErrorElement:End of statement expected
|
||||
<empty list>
|
||||
PsiElement(Py:RBRACE)('}')
|
||||
PsiErrorElement:Statement expected, found Py:RBRACE
|
||||
<empty list>
|
||||
PsiElement(Py:RBRACE)('}')
|
||||
PsiErrorElement:Statement expected, found Py:RBRACE
|
||||
<empty list>
|
||||
PyExpressionStatement
|
||||
PyStringLiteralExpression: }"
|
||||
PsiElement(Py:SINGLE_QUOTED_STRING)(''}"')
|
||||
@@ -0,0 +1,41 @@
|
||||
PyFile:FStringTerminatedByLineBreakInNestedFormatPart.py
|
||||
PyAssignmentStatement
|
||||
PyTargetExpression: s
|
||||
PsiElement(Py:IDENTIFIER)('s')
|
||||
PsiWhiteSpace(' ')
|
||||
PsiElement(Py:EQ)('=')
|
||||
PsiWhiteSpace(' ')
|
||||
PyStringLiteralExpression: {f'foo{42:bar
|
||||
PyFormattedStringNode
|
||||
PsiElement(Py:FSTRING_START)('f"')
|
||||
PyFStringFragment
|
||||
PsiElement(Py:FSTRING_FRAGMENT_START)('{')
|
||||
PyStringLiteralExpression: foo{42:bar
|
||||
PyFormattedStringNode
|
||||
PsiElement(Py:FSTRING_START)('f'')
|
||||
PsiElement(Py:FSTRING_TEXT)('foo')
|
||||
PyFStringFragment
|
||||
PsiElement(Py:FSTRING_FRAGMENT_START)('{')
|
||||
PyNumericLiteralExpression
|
||||
PsiElement(Py:INTEGER_LITERAL)('42')
|
||||
PyFStringFragmentFormatPart
|
||||
PsiElement(Py:FSTRING_FRAGMENT_FORMAT_START)(':')
|
||||
PsiElement(Py:FSTRING_TEXT)('bar')
|
||||
PsiErrorElement:} expected
|
||||
<empty list>
|
||||
PsiErrorElement:Unexpected f-string token
|
||||
<empty list>
|
||||
PsiErrorElement:Unexpected f-string token
|
||||
<empty list>
|
||||
PsiWhiteSpace('\n')
|
||||
PyExpressionStatement
|
||||
PyReferenceExpression: baz
|
||||
PsiElement(Py:IDENTIFIER)('baz')
|
||||
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,31 @@
|
||||
PyFile:FStringTerminatedByLineBreakInNestedLiteralPart.py
|
||||
PyAssignmentStatement
|
||||
PyTargetExpression: s
|
||||
PsiElement(Py:IDENTIFIER)('s')
|
||||
PsiWhiteSpace(' ')
|
||||
PsiElement(Py:EQ)('=')
|
||||
PsiWhiteSpace(' ')
|
||||
PyStringLiteralExpression: {f'foo
|
||||
PyFormattedStringNode
|
||||
PsiElement(Py:FSTRING_START)('f"')
|
||||
PyFStringFragment
|
||||
PsiElement(Py:FSTRING_FRAGMENT_START)('{')
|
||||
PyStringLiteralExpression: foo
|
||||
PyFormattedStringNode
|
||||
PsiElement(Py:FSTRING_START)('f'')
|
||||
PsiElement(Py:FSTRING_TEXT)('foo')
|
||||
PsiErrorElement:Unexpected f-string token
|
||||
<empty list>
|
||||
PsiErrorElement:type conversion, : or } expected
|
||||
<empty list>
|
||||
PsiErrorElement:Unexpected f-string token
|
||||
<empty list>
|
||||
PsiWhiteSpace('\n')
|
||||
PyExpressionStatement
|
||||
PyReferenceExpression: bar
|
||||
PsiElement(Py:IDENTIFIER)('bar')
|
||||
PsiErrorElement:End of statement expected
|
||||
<empty list>
|
||||
PyExpressionStatement
|
||||
PyStringLiteralExpression: }"
|
||||
PsiElement(Py:SINGLE_QUOTED_STRING)(''}"')
|
||||
@@ -1,2 +0,0 @@
|
||||
s = f"{f'{"""
|
||||
"""}'}"
|
||||
@@ -0,0 +1,2 @@
|
||||
s = f"{f'''{foo:{'\
|
||||
'}}'''}"
|
||||
@@ -0,0 +1,33 @@
|
||||
PyFile:FStringTerminatedByLineBreakInNestedStringLiteralInFormatPart.py
|
||||
PyAssignmentStatement
|
||||
PyTargetExpression: s
|
||||
PsiElement(Py:IDENTIFIER)('s')
|
||||
PsiWhiteSpace(' ')
|
||||
PsiElement(Py:EQ)('=')
|
||||
PsiWhiteSpace(' ')
|
||||
PyStringLiteralExpression: {f'''{foo:{'\
|
||||
'}}'''}
|
||||
PyFormattedStringNode
|
||||
PsiElement(Py:FSTRING_START)('f"')
|
||||
PyFStringFragment
|
||||
PsiElement(Py:FSTRING_FRAGMENT_START)('{')
|
||||
PyStringLiteralExpression: {foo:{'\
|
||||
'}}
|
||||
PyFormattedStringNode
|
||||
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)('{')
|
||||
PyStringLiteralExpression:
|
||||
|
||||
PsiElement(Py:SINGLE_QUOTED_STRING)(''\\n'')
|
||||
PsiElement(Py:FSTRING_FRAGMENT_END)('}')
|
||||
PsiElement(Py:FSTRING_FRAGMENT_END)('}')
|
||||
PsiElement(Py:FSTRING_END)(''''')
|
||||
PsiElement(Py:FSTRING_FRAGMENT_END)('}')
|
||||
PsiElement(Py:FSTRING_END)('"')
|
||||
@@ -0,0 +1,22 @@
|
||||
PyFile:FStringTerminatedByLineBreakInStringLiteral.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:
|
||||
PsiElement(Py:TRIPLE_QUOTED_STRING)('"""')
|
||||
PsiErrorElement:type conversion, : or } expected
|
||||
<empty list>
|
||||
PsiErrorElement:Unexpected f-string token
|
||||
<empty list>
|
||||
PsiWhiteSpace('\n')
|
||||
PyExpressionStatement
|
||||
PyStringLiteralExpression: }'
|
||||
PsiElement(Py:TRIPLE_QUOTED_STRING)('"""}'')
|
||||
@@ -0,0 +1,28 @@
|
||||
PyFile:FStringTerminatedByLineBreakInStringLiteralInFormatPart.py
|
||||
PyAssignmentStatement
|
||||
PyTargetExpression: s
|
||||
PsiElement(Py:IDENTIFIER)('s')
|
||||
PsiWhiteSpace(' ')
|
||||
PsiElement(Py:EQ)('=')
|
||||
PsiWhiteSpace(' ')
|
||||
PyStringLiteralExpression: {foo:{"""
|
||||
PyFormattedStringNode
|
||||
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)('{')
|
||||
PyStringLiteralExpression:
|
||||
PsiElement(Py:TRIPLE_QUOTED_STRING)('"""')
|
||||
PsiErrorElement:type conversion, : or } expected
|
||||
<empty list>
|
||||
PsiErrorElement:Unexpected f-string token
|
||||
<empty list>
|
||||
PsiWhiteSpace('\n')
|
||||
PyExpressionStatement
|
||||
PyStringLiteralExpression: }}'
|
||||
PsiElement(Py:TRIPLE_QUOTED_STRING)('"""}}'')
|
||||
@@ -1,2 +0,0 @@
|
||||
s = f"{f'{foo:{"""
|
||||
"""}}'}"
|
||||
@@ -713,7 +713,9 @@ public class PythonParsingTest extends ParsingTestCase {
|
||||
doTest(LanguageLevel.PYTHON36);
|
||||
}
|
||||
|
||||
public void testFStringTerminatedByLineBreakInNestedStringLiteral() {
|
||||
// not possible to come up with such case without escaping: triple-quoted string inside
|
||||
// two nested f-strings with different types of quotes
|
||||
public void testFStringTerminatedByEscapedLineBreakInNestedStringLiteral() {
|
||||
doTest(LanguageLevel.PYTHON36);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user