PY-59594 PEP 701: Allow quote-reuse and line breaks inside f-strings. Keep reporting these problems for Python <3.12.

PEP 498 required f-strings to be recognizable by existing tooling, such as syntax highlighters,
by prohibiting re-using quotes of the same kind and having line breaks inside expression fragments.
We used to detect these problems already at the lexer level, correctly replacing violating quotes
with FSTRING_END token, and appending STATEMENT_BREAK tokens to illegal line breaks inside expressions,
depending on the lexer's state. Now, thanks to a general f-string grammar in PEP 701, most of this
bookkeeping could be moved from the lexer to the CompatibilityVisitor (to still be reported
for previous versions of the language and by the compatibility inspection).

Previously forbidden backslashes and line comments are now also detected by the CompatibilityVisitor
instead of the version-agnostic FStringAnnotator.

One side effect of the new grammar is that parser recovery in pre-3.12 version of Python became
slightly worse. For instance, something like `f'{foo'` used to be recognized as an f-string
with an incomplete fragment lacking its closing brace. Now, it's parsed as an incomplete
f-string, lacking its own closing quote, containing an incomplete string literal inside
an incomplete fragment. What's more, parsing of this fragment's expression doesn't terminate
until the end of a file, because STATEMENT_BREAK is never produced by PythonIndentingProcessor
while it's inside an f-string fragment, and every quote is considered a new string literal.

Examples of parsing tests affected by this are:
PythonParsingTest.testFStringFragmentIncompleteTypeConversionBeforeClosingQuote
PythonParsingTest.testFStringIncompleteFragmentWithTypeConversion
PythonParsingTest.testFStringIncompleteFragment

I also had to simplify some scenarios from PythonHighlightingTest, removing snippets
with incomplete fragments or moving such examples to the very end of a file.

It's not clear how to handle these situations not overcomplicating the lexer.

(cherry picked from commit 03ba6d7fba1b45a84aa92221e6a452645a765205)

IJ-MR-115763

GitOrigin-RevId: cd36470d9cae353fe3caeb2d3b628d8743b46cbb
This commit is contained in:
Mikhail Golubev
2023-09-29 09:33:42 +00:00
committed by intellij-monorepo-bot
parent 4671fe0c7c
commit 37d25ee815
124 changed files with 896 additions and 841 deletions
@@ -142,12 +142,9 @@ ANN.variable.annotation.cannot.be.combined.with.tuple.unpacking=A variable annot
ANN.variable.annotation.cannot.be.used.in.assignment.with.multiple.targets=A variable annotation cannot be used in assignment with multiple targets
ANN.generator.expression.must.be.parenthesized.if.not.sole.argument=Generator expression must be parenthesized if not sole argument
ANN.fstrings.expression.fragment.inside.fstring.nested.too.deeply=Expression fragment inside an f-string is nested too deeply
ANN.fstrings.missing.conversion.character=A conversion character is expected: should be one of 's', 'r', 'a'
ANN.fstrings.illegal.conversion.character=An illegal conversion character ''{0}'': should be one of ''s'', ''r'', ''a''
ANN.fstrings.expression.fragments.cannot.include.backslashes=Expression fragments inside f-strings cannot include backslashes
ANN.fstrings.single.right.brace.not.allowed.inside.fstrings=A single '}' is not allowed inside f-strings
ANN.fstrings.expression.fragments.cannot.include.line.comments=Expression fragments inside f-strings cannot include line comments
ANN.patterns.single.star.pattern.cannot.be.used.outside.sequence.patterns=Single star pattern cannot be used outside sequence patterns
ANN.patterns.double.star.pattern.cannot.be.used.outside.mapping.patterns=Double star pattern cannot be used outside mapping patterns
@@ -813,6 +810,11 @@ INSP.compatibility.feature.support.raise.with.no.arguments.outside.except.block=
INSP.compatibility.feature.support.backquotes=support backquotes, use repr() instead
INSP.compatibility.feature.support.print.statement=support this syntax. The print statement has been replaced with a print() function
INSP.compatibility.feature.support.super.without.arguments=support this syntax. super() should have arguments in Python 2
INSP.compatibility.feature.allow.quote.reuse.in.f-strings=allow nesting of string literals with the same quote type inside f-strings
INSP.compatibility.feature.allow.new.lines.in.f-strings=allow new lines in expression parts of non-triple-quoted f-strings
INSP.compatibility.feature.allow.deep.expression.nesting.in.f-strings=allow nesting expressions in format specifiers this deep
INSP.compatibility.feature.allow.backslashes.in.f-strings=allow backslashes inside expression parts of f-strings
INSP.compatibility.feature.line.comments.in.f-strings=allow comments inside expression parts of f-strings
INSP.compatibility.py35.does.not.support.yield.inside.async.functions=Python version 3.5 does not support 'yield' inside async functions
INSP.compatibility.feature.support.yield.from=support this syntax. Delegating to a subgenerator is available since Python 3.3; use explicit iteration over subgenerator instead.
INSP.compatibility.pre35.versions.do.not.allow.return.with.argument.inside.generator=Python versions < 3.3 do not allow 'return' with argument inside generator.
@@ -14,11 +14,7 @@ class PyLexerFStringHelper(private val myLexer: FlexLexerEx) {
fun handleFStringStartInFragment(): IElementType {
val prefixAndQuotes = myLexer.yytext().toString()
val (_, offset) = findFStringTerminator(prefixAndQuotes)
if (offset == prefixAndQuotes.length) {
return pushFString(prefixAndQuotes)
}
return PyTokenTypes.IDENTIFIER
return pushFString(prefixAndQuotes)
}
fun handleFStringStart(): IElementType {
@@ -90,9 +86,6 @@ 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
}
@@ -106,15 +99,7 @@ class PyLexerFStringHelper(private val myLexer: FlexLexerEx) {
}
fun handleStringLiteral(stringLiteralType: IElementType): IElementType {
val stringText = myLexer.yytext().toString()
val prefixLength = PyStringLiteralUtil.getPrefixLength(stringText)
val (type, offset) = findFStringTerminator(stringText)
return when (offset) {
0 -> type!!
prefixLength -> PyTokenTypes.IDENTIFIER
else -> stringLiteralType
}
return stringLiteralType
}
private fun findFStringTerminator(text: String): Pair<IElementType?, Int> {
@@ -126,8 +111,8 @@ class PyLexerFStringHelper(private val myLexer: FlexLexerEx) {
continue
}
if (c == '\n') {
val insideSingleQuoted = myFStringStates.any { it.openingQuotes.length == 1 }
if (insideSingleQuoted) {
val topmostFStringIsSingleQuoted = myFStringStates.peek().openingQuotes.length == 1
if (topmostFStringIsSingleQuoted) {
if (i == 0) {
// Terminate all f-strings and insert STATEMENT_BREAK at this point
dropFStringStateWithAllNested(0)
@@ -138,13 +123,13 @@ class PyLexerFStringHelper(private val myLexer: FlexLexerEx) {
}
else {
val nextThree = text.substring(i, min(text.length, i + 3))
val lastWithMatchingQuotesIndex = myFStringStates.indexOfLast { nextThree.startsWith(it.openingQuotes) }
if (lastWithMatchingQuotesIndex >= 0) {
val state = myFStringStates[lastWithMatchingQuotesIndex]
val topmostFStringState = myFStringStates.peek()
val closesTopmostFStringQuotes = nextThree.startsWith(topmostFStringState.openingQuotes)
if (closesTopmostFStringQuotes) {
if (i == 0) {
dropFStringStateWithAllNested(lastWithMatchingQuotesIndex)
dropFStringStateWithAllNested(myFStringStates.size - 1)
}
pushBackToOrConsumeMatch(i, state.openingQuotes.length)
pushBackToOrConsumeMatch(i, topmostFStringState.openingQuotes.length)
return Pair(PyTokenTypes.FSTRING_END, i)
}
}
@@ -20,7 +20,6 @@ import com.intellij.lexer.FlexLexer;
import com.intellij.lexer.MergingLexerAdapter;
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.tree.TokenSet;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.Stack;
import com.jetbrains.python.PyTokenTypes;
import com.jetbrains.python.PythonDialectsTokenSetProvider;
@@ -44,7 +43,7 @@ public class PythonIndentingProcessor extends MergingLexerAdapter {
private int myLineBreakBeforeFirstCommentIndex = -1;
protected boolean myProcessSpecialTokensPending = false;
private final Stack<String> myFStringStack = new Stack<>();
private final Stack<FString> myFStringStack = new Stack<>();
private static final boolean DUMP_TOKENS = false;
private final TokenSet RECOVERY_TOKENS = PythonDialectsTokenSetProvider.getInstance().getUnbalancedBracesRecoveryTokens();
@@ -200,16 +199,33 @@ public class PythonIndentingProcessor extends MergingLexerAdapter {
final int prefixLength = PyStringLiteralUtil.getPrefixLength(tokenText);
final String openingQuotes = tokenText.substring(prefixLength);
assert !openingQuotes.isEmpty();
myFStringStack.push(openingQuotes);
myFStringStack.push(new FString(openingQuotes, new Stack<>()));
}
else if (isBaseAt(PyTokenTypes.FSTRING_END)) {
while (!myFStringStack.isEmpty()) {
final String lastOpeningQuotes = myFStringStack.pop();
if (lastOpeningQuotes.equals(tokenText)) {
final FString lastFString = myFStringStack.pop();
if (lastFString.quotes.equals(tokenText)) {
break;
}
}
}
else if (isBaseAt(PyTokenTypes.FSTRING_FRAGMENT_START)) {
assert !myFStringStack.isEmpty();
myFStringStack.peek().fragments.push(FStringFragmentPart.EXPRESSION);
}
else if (isBaseAt(PyTokenTypes.FSTRING_FRAGMENT_END)) {
assert !myFStringStack.isEmpty();
FString topmostFString = myFStringStack.peek();
assert !topmostFString.fragments.isEmpty();
topmostFString.fragments.pop();
}
else if (isBaseAt(PyTokenTypes.FSTRING_FRAGMENT_FORMAT_START) || isBaseAt(PyTokenTypes.FSTRING_FRAGMENT_TYPE_CONVERSION)) {
assert !myFStringStack.isEmpty();
FString topmostFString = myFStringStack.peek();
assert !topmostFString.fragments.isEmpty();
topmostFString.fragments.pop();
topmostFString.fragments.push(FStringFragmentPart.TYPE_CONVERSION_OR_FORMAT);
}
}
protected void pushToken(IElementType type, int start, int end) {
@@ -343,14 +359,9 @@ public class PythonIndentingProcessor extends MergingLexerAdapter {
}
protected void processLineBreak(int startPos) {
// See https://www.python.org/dev/peps/pep-0498/#expression-evaluation
final boolean allFStringsAreTripleQuoted = ContainerUtil.and(myFStringStack, quotes -> quotes.length() == 3);
final boolean insideImplicitFragmentParentheses = !myFStringStack.isEmpty() && allFStringsAreTripleQuoted;
final boolean shouldTerminateFStrings = !myFStringStack.isEmpty() && !allFStringsAreTripleQuoted;
if ((myBraceLevel == 0 && !insideImplicitFragmentParentheses) || shouldTerminateFStrings) {
if (myLineHasSignificantTokens || shouldTerminateFStrings) {
if (myBraceLevel == 0 && isOutsideFStringOrInsideItsLineBreakSensitiveTextPart()) {
if (myLineHasSignificantTokens) {
pushToken(PyTokenTypes.STATEMENT_BREAK, startPos, startPos);
myFStringStack.clear();
}
myLineHasSignificantTokens = false;
advanceBase();
@@ -361,6 +372,14 @@ public class PythonIndentingProcessor extends MergingLexerAdapter {
}
}
private boolean isOutsideFStringOrInsideItsLineBreakSensitiveTextPart() {
if (myFStringStack.isEmpty()) return true;
FString topmostFString = myFStringStack.peek();
// In triple-quoted f-strings one can put line breaks in any plain-text part
if (topmostFString.quotes.length() != 1) return false;
return topmostFString.fragments.isEmpty() || topmostFString.fragments.peek() == FStringFragmentPart.TYPE_CONVERSION_OR_FORMAT;
}
protected void processInsignificantLineBreak(int startPos,
boolean breakStatementOnLineBreak) {
// merge whitespace following the line break character into the
@@ -485,4 +504,13 @@ public class PythonIndentingProcessor extends MergingLexerAdapter {
protected IElementType getCommentTokenType() {
return PyTokenTypes.END_OF_LINE_COMMENT;
}
private record FString(@NotNull String quotes, @NotNull Stack<FStringFragmentPart> fragments) {
}
private enum FStringFragmentPart {
EXPRESSION,
TYPE_CONVERSION_OR_FORMAT,
}
}
@@ -12,10 +12,7 @@ import com.intellij.openapi.util.NlsSafe;
import com.intellij.openapi.util.Ref;
import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiErrorElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiWhiteSpace;
import com.intellij.psi.*;
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.tree.TokenSet;
import com.intellij.psi.util.PsiTreeUtil;
@@ -709,7 +706,87 @@ public abstract class CompatibilityVisitor extends PyAnnotator {
final ASTNode equalitySignInFStringFragment = node.getNode().findChildByType(PyTokenTypes.EQ);
if (equalitySignInFStringFragment != null) {
registerForAllMatchingVersions(level -> level.isOlderThan(LanguageLevel.PYTHON38) && registerForLanguageLevel(level),
PyPsiBundle.message("INSP.compatibility.support.equality.signs.in.fstrings"), equalitySignInFStringFragment.getPsi());
PyPsiBundle.message("INSP.compatibility.support.equality.signs.in.fstrings"),
equalitySignInFStringFragment.getPsi());
}
List<PyFStringFragment> containingFragmentsOfSameFString =
PsiTreeUtil.collectParents(node, PyFStringFragment.class, false, o -> o instanceof PyStringLiteralExpression);
if (containingFragmentsOfSameFString.size() > 1) {
// At the moment, there is a limit of 2 for CPython 3.12, but it's implementation-dependent.
// See https://peps.python.org/pep-0701/#specification
registerForAllMatchingVersions(level -> level.isOlderThan(LanguageLevel.PYTHON312) && registerForLanguageLevel(level),
PyPsiBundle.message("INSP.compatibility.feature.allow.deep.expression.nesting.in.f-strings"), node);
}
boolean isTopmostFragment = PsiTreeUtil.getParentOfType(node, PyFStringFragment.class, true) == null;
if (isTopmostFragment) {
List<PyFStringFragment> fragments = new ArrayList<>();
fragments.add(node);
PyFStringFragmentFormatPart formatPart = node.getFormatPart();
if (formatPart != null) {
fragments.addAll(formatPart.getFragments());
}
for (PyFStringFragment fragment : fragments) {
String wholeNodeText = fragment.getText();
TextRange range = fragment.getExpressionContentRange();
for (int i = range.getStartOffset(); i < range.getEndOffset(); i++) {
if (wholeNodeText.charAt(i) == '\\') {
TextRange backslashRange = TextRange.from(i, 1).shiftRight(fragment.getTextRange().getStartOffset());
registerForAllMatchingVersions(
level -> level.isOlderThan(LanguageLevel.PYTHON312) && registerForLanguageLevel(level),
PyPsiBundle.message("INSP.compatibility.feature.allow.backslashes.in.f-strings"),
node, backslashRange, true
);
}
}
}
}
List<PyFormattedStringElement> containingFStrings =
PsiTreeUtil.collectParents(node, PyFormattedStringElement.class, false, e -> e instanceof PyStatement);
assert !containingFStrings.isEmpty();
PyFormattedStringElement parentFString = containingFStrings.get(0);
List<PyFormattedStringElement> remainingEnclosingFStrings = ContainerUtil.subList(containingFStrings, 1);
// Report only on fragments of the topmost single-quoted f-string to avoid duplicates
// in cases like: f'{f'{1<BR>
// + 1}'}'
boolean isFragmentOfTopmostSingleQuotedFString = !parentFString.isTripleQuoted() &&
ContainerUtil.all(remainingEnclosingFStrings, PyStringElement::isTripleQuoted);
if (isFragmentOfTopmostSingleQuotedFString && node.textContains('\n')) {
int lineBreakOffset = node.getText().indexOf('\n');
if (node.getExpressionContentRange().contains(lineBreakOffset)) {
PsiElement multiLineLeaf = node.findElementAt(lineBreakOffset);
assert multiLineLeaf != null;
registerForAllMatchingVersions(level -> level.isOlderThan(LanguageLevel.PYTHON312) && registerForLanguageLevel(level),
PyPsiBundle.message("INSP.compatibility.feature.allow.new.lines.in.f-strings"), multiLineLeaf);
}
}
String parentFStringQuote = parentFString.getQuote();
// Report only on fragments of the topmost f-string with this quote type to avoid duplicates in cases like: f'{f'{f"'"}'}'
boolean isFragmentOfTopmostFStringWithSuchQuotes = ContainerUtil.all(remainingEnclosingFStrings,
fString -> !fString.getQuote().equals(parentFStringQuote));
if (isFragmentOfTopmostFStringWithSuchQuotes) {
int illegalQuoteOffset = node.getText().indexOf(parentFStringQuote);
if (node.getExpressionContentRange().contains(illegalQuoteOffset)) {
TextRange illegalQuoteRange = TextRange.from(illegalQuoteOffset, parentFStringQuote.length());
registerForAllMatchingVersions(level -> level.isOlderThan(LanguageLevel.PYTHON312) && registerForLanguageLevel(level),
PyPsiBundle.message("INSP.compatibility.feature.allow.quote.reuse.in.f-strings"),
node, illegalQuoteRange.shiftRight(node.getTextRange().getStartOffset()), true);
}
}
}
@Override
public void visitComment(@NotNull PsiComment node) {
boolean insideFStringFragment = PsiTreeUtil.getParentOfType(node, PyFStringFragment.class) != null;
if (insideFStringFragment) {
registerForAllMatchingVersions(level -> level.isOlderThan(LanguageLevel.PYTHON312) && registerForLanguageLevel(level),
PyPsiBundle.message("INSP.compatibility.feature.line.comments.in.f-strings"), node);
}
}
@@ -39,11 +39,6 @@ public class FStringsAnnotator extends PyAnnotator {
@Override
public void visitPyFStringFragment(@NotNull PyFStringFragment node) {
final List<PyFStringFragment> enclosingFragments = PsiTreeUtil.collectParents(node, PyFStringFragment.class, false,
PyStringLiteralExpression.class::isInstance);
if (enclosingFragments.size() > 1) {
report(node, PyPsiBundle.message("ANN.fstrings.expression.fragment.inside.fstring.nested.too.deeply"));
}
final PsiElement typeConversion = node.getTypeConversion();
if (typeConversion != null) {
final String conversionChar = typeConversion.getText().substring(1);
@@ -54,24 +49,6 @@ public class FStringsAnnotator extends PyAnnotator {
report(typeConversion, PyPsiBundle.message("ANN.fstrings.illegal.conversion.character", conversionChar));
}
}
final boolean topLevel = PsiTreeUtil.getParentOfType(node, PyFStringFragment.class, true) == null;
if (topLevel) {
final List<PyFStringFragment> fragments = Lists.newArrayList(node);
final PyFStringFragmentFormatPart formatPart = node.getFormatPart();
if (formatPart != null) {
fragments.addAll(formatPart.getFragments());
}
for (PyFStringFragment fragment : fragments) {
final String wholeNodeText = fragment.getText();
final TextRange range = fragment.getExpressionContentRange();
for (int i = range.getStartOffset(); i < range.getEndOffset(); i++) {
if (wholeNodeText.charAt(i) == '\\') {
reportCharacter(fragment, i, PyPsiBundle.message("ANN.fstrings.expression.fragments.cannot.include.backslashes"));
}
}
}
}
}
@Override
@@ -106,14 +83,6 @@ public class FStringsAnnotator extends PyAnnotator {
return offset;
}
@Override
public void visitComment(@NotNull PsiComment comment) {
final boolean insideFragment = PsiTreeUtil.getParentOfType(comment, PyFStringFragment.class) != null;
if (insideFragment) {
report(comment, PyPsiBundle.message("ANN.fstrings.expression.fragments.cannot.include.line.comments"));
}
}
public void reportCharacter(@NotNull PsiElement element, int offset, @NotNull @InspectionMessage String message) {
final int nodeStartOffset = element.getTextRange().getStartOffset();
getHolder().newAnnotation(HighlightSeverity.ERROR, message).range(TextRange.from(offset, 1).shiftRight(nodeStartOffset)).create();
@@ -1,6 +0,0 @@
f'{<error descr="Expression expected"><error descr="Expression fragments inside f-strings cannot include backslashes">\</error>t</error>}'
f'{<error descr="Expression expected"><error descr="Expression fragments inside f-strings cannot include backslashes">\</error>t</error><error descr="'}' expected">'</error>
f'{<error descr="Expression expected"><error descr="Expression fragments inside f-strings cannot include backslashes">\</error>N{GREEK SMALL LETTER ALPHA}</error>}'
f'{Formatable():\n\t}'
f'{42:{<error descr="Expression expected"><error descr="Expression fragments inside f-strings cannot include backslashes">\</error>t</error>}}'
f'{f"""{"<error descr="Expression fragments inside f-strings cannot include backslashes">\</error>n"}"""}'
@@ -0,0 +1,7 @@
f'{<error descr="Expression expected"><error descr="Python version 3.6 does not allow backslashes inside expression parts of f-strings">\</error>t</error>}'
f'{<error descr="Expression expected"><error descr="Python version 3.6 does not allow backslashes inside expression parts of f-strings">\</error>N{GREEK SMALL LETTER ALPHA}</error>}'
f'{Formatable():\n\t}'
f'{42:{<error descr="Expression expected"><error descr="Python version 3.6 does not allow backslashes inside expression parts of f-strings">\</error>t</error>}}'
f'{f"""{"<error descr="Python version 3.6 does not allow backslashes inside expression parts of f-strings">\</error>n"}"""}'
f'{<error descr="Expression expected"><error descr="Python version 3.6 does not allow backslashes inside expression parts of f-strings">\</error>t<error descr="Python version 3.6 does not allow nesting of string literals with the same quote type inside f-strings">'</error><error descr="Python version 3.6 does not allow new lines in expression parts of non-triple-quoted f-strings">
</error></error><EOLError descr="Type conversion, ':' or '}' expected"></EOLError><EOLError descr="' expected"></EOLError>
@@ -0,0 +1,3 @@
f'''{[
42 <error descr="Python version 3.6 does not allow comments inside expression parts of f-strings"># foo</error>
]}'''
@@ -1,10 +1,7 @@
f'{<error descr="Expression expected">}</error>'
f'{<error descr="'}' expected"><error descr="Expression expected">'</error></error>
f'{<EOLError descr="Type conversion, ':' or '}' expected"></EOLError><EOLError descr="Expression expected"></EOLError><EOLError descr="' expected"></EOLError>
f'{<error descr="Expression expected">!</error>r}'
f'{<error descr="Expression expected">:</error>2.3}'
f'{42:2.{<error descr="Expression expected">}</error>}'
f'{<error descr="Expression expected"> </error>}'
f'{42:{<error descr="Expression expected"> </error>}}'
f'{<error descr="Expression expected"> </error>:{<error descr="Expression expected"> </error><error descr="'}' expected">'</error>
f'{<error descr="Expression expected"> </error>!r:{<error descr="Expression expected"> </error>:42}}'
@@ -0,0 +1,19 @@
import math
s1 = f'{math.pi = :.2f}'
s2 = f'{f"{3.1415=:.1f}":*^20}'
s3 = f'{0 == 1}'
x = 'A string'
s4 = f'{x=!s}'
x = 2.71828
s5 = f'{x=:.2f}'
s6 = f'{x=:}'
s7 = f'{x=!r:^20}'
s8 = f'{x=!s:^20}'
s9 = f'{x= !a:^20}'
s10 = f'{3 * x + 15=}'
pi = 'π'
s11 = f'alpha α {pi = } ω omega'
s12 = f'''{
3
=}'''
s13 = f'{"="}'
@@ -1,10 +0,0 @@
f'{<error descr="Expression expected"><error descr="Expression fragments inside f-strings cannot include line comments">#'</error></error><EOLError descr="Type conversion, ':' or '}' expected"></EOLError><EOLError descr="' expected"></EOLError>
f'{<error descr="Expression expected"><error descr="Expression fragments inside f-strings cannot include line comments">#</error></error><EOLError descr="Type conversion, ':' or '}' expected"></EOLError><EOLError descr="' expected"></EOLError>
f'{<error descr="Expression expected"><error descr="Expression fragments inside f-strings cannot include line comments">#foo#}'</error></error><EOLError descr="Type conversion, ':' or '}' expected"></EOLError><EOLError descr="' expected"></EOLError>
f'{42:#}'
f'{42:{<error descr="Expression expected"><error descr="Expression fragments inside f-strings cannot include line comments">#}}'</error></error><EOLError descr="Type conversion, ':' or '}' expected"></EOLError><EOLError descr="' expected"></EOLError>
f'{x<error descr="Type conversion, ':' or '}' expected"> </error><error descr="Expression fragments inside f-strings cannot include line comments">### foo}'</error><EOLError descr="' expected"></EOLError>
f'{"###"}'
f'''{[
42 <error descr="Expression fragments inside f-strings cannot include line comments"># foo</error>
]}'''
@@ -5,5 +5,3 @@ f'{42<error descr="An illegal conversion character 'z': should be one of 's', 'r
f'{42<error descr="An illegal conversion character 'foo': should be one of 's', 'r', 'a'">!foo</error>}'
f'{42<error descr="A conversion character is expected: should be one of 's', 'r', 'a'">!</error>}'
f'{42<error descr="A conversion character is expected: should be one of 's', 'r', 'a'">!</error>:2}'
f'{42<error descr="A conversion character is expected: should be one of 's', 'r', 'a'">!</error><error descr="'}' expected">'</error>
f'{42<error descr="A conversion character is expected: should be one of 's', 'r', 'a'">!</error><EOLError descr=": or '}' expected"></EOLError><EOLError descr="' expected"></EOLError>
@@ -1,10 +1 @@
f'{42}'
f'{42!r}'
f'{42!r:03}'
f'{42:03}'
f'{42!r:{y}.{z}}'
f'{<error descr="'}' expected"><error descr="Expression expected">'</error></error>
f'{42:{<error descr="'}' expected"><error descr="Expression expected">'</error></error>
f'{42!r:{<error descr="'}' expected"><error descr="Expression expected">'</error></error>
f'{{'
f'{{{<error descr="'}' expected"><error descr="Expression expected">'</error></error>
f'{<error descr="Missing closing quote [']"><error descr="Python version 3.6 does not allow nesting of string literals with the same quote type inside f-strings">'</error></error><EOLError descr="Type conversion, ':' or '}' expected"></EOLError><EOLError descr="' expected"></EOLError>
@@ -0,0 +1,2 @@
s = f'{1 +<error descr="Python version 3.11 does not allow new lines in expression parts of non-triple-quoted f-strings">
</error>2}'
@@ -0,0 +1,2 @@
s = f'{42:{1 +<error descr="Python version 3.11 does not allow new lines in expression parts of non-triple-quoted f-strings">
</error>2}}'
@@ -0,0 +1,2 @@
s = f"{f'{1 +<error descr="Python version 3.11 does not allow new lines in expression parts of non-triple-quoted f-strings">
</error>2}'}"
@@ -0,0 +1,2 @@
s = f"{f'{42:{1 +<error descr="Python version 3.11 does not allow new lines in expression parts of non-triple-quoted f-strings">
</error>2}}'}"
@@ -0,0 +1,2 @@
s = f"{f'foo{42:bar
baz}'}"
@@ -0,0 +1,2 @@
s = f'{<error descr="Python version 3.11 does not allow new lines in expression parts of non-triple-quoted f-strings">"""
"""</error>}'
@@ -0,0 +1,2 @@
s = f'{foo:{<error descr="Python version 3.11 does not allow new lines in expression parts of non-triple-quoted f-strings">"""
"""</error>}}'
@@ -0,0 +1 @@
s = f'{f"{42:<error descr="Python version 3.11 does not allow nesting of string literals with the same quote type inside f-strings">'</error>}"}'
@@ -0,0 +1 @@
s = f'foo{f"baz<error descr="Python version 3.11 does not allow nesting of string literals with the same quote type inside f-strings">'</error>quux"}bar'
@@ -0,0 +1 @@
s = f'foo{f"<error descr="Python version 3.11 does not allow nesting of string literals with the same quote type inside f-strings">'</error>"}baz'
@@ -0,0 +1 @@
s = f'{42:{f"<error descr="Python version 3.11 does not allow nesting of string literals with the same quote type inside f-strings">'</error>"}}'
@@ -0,0 +1 @@
s = f'{f"""{f"<error descr="Python version 3.11 does not allow nesting of string literals with the same quote type inside f-strings">'</error>"}"""}'
@@ -0,0 +1 @@
s = f'{f"""{42:{f"<error descr="Python version 3.11 does not allow nesting of string literals with the same quote type inside f-strings">'</error>"}}"""}'
@@ -0,0 +1 @@
s = f'{f"""{"<error descr="Python version 3.11 does not allow nesting of string literals with the same quote type inside f-strings">'</error>"}"""}'
@@ -0,0 +1 @@
s = f'{f"""{42:{"<error descr="Python version 3.11 does not allow nesting of string literals with the same quote type inside f-strings">'</error>"}}"""}'
@@ -0,0 +1 @@
s = f'foo{"<error descr="Python version 3.11 does not allow nesting of string literals with the same quote type inside f-strings">'</error>"}baz'
@@ -0,0 +1 @@
s = f'{42:{"<error descr="Python version 3.11 does not allow nesting of string literals with the same quote type inside f-strings">'</error>"}}'
@@ -0,0 +1 @@
s = f'foo{f<error descr="Python version 3.11 does not allow nesting of string literals with the same quote type inside f-strings">'</error>bar'}baz'
@@ -0,0 +1 @@
s = f'{42:{f<error descr="Python version 3.11 does not allow nesting of string literals with the same quote type inside f-strings">'</error>foo'}}'
@@ -0,0 +1 @@
s = f'{f"{f<error descr="Python version 3.11 does not allow nesting of string literals with the same quote type inside f-strings">'</error>foo'}"}'
@@ -0,0 +1 @@
s = f'{f"{42:{f<error descr="Python version 3.11 does not allow nesting of string literals with the same quote type inside f-strings">'</error>foo'}}"}'
@@ -0,0 +1 @@
s = f'{f"{<error descr="Python version 3.11 does not allow nesting of string literals with the same quote type inside f-strings">'</error>foo'}"}'
@@ -0,0 +1 @@
s = f'{f"{42:{<error descr="Python version 3.11 does not allow nesting of string literals with the same quote type inside f-strings">'</error>foo'}}"}'
@@ -0,0 +1 @@
s = f'foo{<error descr="Python version 3.11 does not allow nesting of string literals with the same quote type inside f-strings">'</error>bar'}baz'
@@ -0,0 +1 @@
s = f'{42:{<error descr="Python version 3.11 does not allow nesting of string literals with the same quote type inside f-strings" textAttributesKey="ERRORS_ATTRIBUTES">'</error>foo'}}'
@@ -1,7 +0,0 @@
f'{x:{y:<error descr="Expression fragment inside an f-string is nested too deeply">{<error descr="Expression expected">}</error></error>}}'
f'{x:{y:<error descr="Expression fragment inside an f-string is nested too deeply">{<error descr="Expression expected"><error descr="Expression fragments inside f-strings cannot include line comments"># foo}}}'</error></error></error><EOLError descr="Type conversion, ':' or '}' expected"></EOLError><EOLError descr="' expected"></EOLError>
f'{x:{y:<error descr="Expression fragment inside an f-string is nested too deeply">{z<error descr="An illegal conversion character 'z': should be one of 's', 'r', 'a'">!z</error>}</error>}}'
f'{x:{y:<error descr="Expression fragment inside an f-string is nested too deeply">{z:<error descr="Expression fragment inside an f-string is nested too deeply">{42}</error>}</error>}}'
f'{<error descr="Expression expected">:</error>{<error descr="Expression expected">:</error><error descr="Expression fragment inside an f-string is nested too deeply">{<error descr="Expression expected">:</error><error descr="Expression fragment inside an f-string is nested too deeply">{<error descr="Expression expected">}</error></error>}</error>}}'
f'{x:{y:<error descr="Expression fragment inside an f-string is nested too deeply">{z</error><error descr="'}' expected">'</error>
f'{x:{y:<error descr="Expression fragment inside an f-string is nested too deeply">{z</error><EOLError descr="Type conversion, ':' or '}' expected"></EOLError><EOLError descr="' expected"></EOLError>
@@ -0,0 +1,5 @@
f'{x:{y:<error descr="Python version 3.6 does not allow nesting expressions in format specifiers this deep">{<error descr="Expression expected">}</error></error>}}'
f'{x:{y:<error descr="Python version 3.6 does not allow nesting expressions in format specifiers this deep">{z<error descr="An illegal conversion character 'z': should be one of 's', 'r', 'a'">!z</error>}</error>}}'
f'{x:{y:<error descr="Python version 3.6 does not allow nesting expressions in format specifiers this deep">{z:<error descr="Python version 3.6 does not allow nesting expressions in format specifiers this deep">{42}</error>}</error>}}'
f'{<error descr="Expression expected">:</error>{<error descr="Expression expected">:</error><error descr="Python version 3.6 does not allow nesting expressions in format specifiers this deep">{<error descr="Expression expected">:</error><error descr="Python version 3.6 does not allow nesting expressions in format specifiers this deep">{<error descr="Expression expected">}</error></error>}</error>}}'
f'{x:{y:<error descr="Python version 3.6 does not allow nesting expressions in format specifiers this deep">{z</error><EOLError descr="Type conversion, ':' or '}' expected"></EOLError><EOLError descr="' expected"></EOLError>
@@ -0,0 +1,2 @@
s = f'''{1 +
2}'''
@@ -0,0 +1 @@
s = f'{ (lambda x: <error descr="Python version 3.11 does not allow nesting of string literals with the same quote type inside f-strings">'</error>foo') }'
@@ -0,0 +1,3 @@
s = f'''{
<error descr="Python version 3.11 does not allow nesting of string literals with the same quote type inside f-strings">'''</error>'''
}'''
@@ -0,0 +1,5 @@
s = f"""{f'''
{"bar"
}
'''
}"""
@@ -0,0 +1,2 @@
s = f"""{f'{1 +<error descr="Python version 3.11 does not allow new lines in expression parts of non-triple-quoted f-strings">
</error>2}'}"""
@@ -0,0 +1,2 @@
s = f"""{f'{(1 +<error descr="Python version 3.11 does not allow new lines in expression parts of non-triple-quoted f-strings">
</error>2)}'}"""
@@ -13,6 +13,6 @@ PyFile:FStringFragmentIncompleteTypeConversionBeforeClosingQuote.py
PyNumericLiteralExpression
PsiElement(Py:INTEGER_LITERAL)('42')
PsiElement(Py:FSTRING_FRAGMENT_TYPE_CONVERSION)('!')
PsiErrorElement:'}' expected
PsiErrorElement:: or '}' expected
<empty list>
PsiElement(Py:FSTRING_END)(''')
PsiElement(Py:SINGLE_QUOTED_STRING)(''')
@@ -5,13 +5,16 @@ PyFile:FStringIncompleteFragment.py
PsiWhiteSpace(' ')
PsiElement(Py:EQ)('=')
PsiWhiteSpace(' ')
PyStringLiteralExpression: {42
PyStringLiteralExpression: {42'
PyFormattedStringElement
PsiElement(Py:FSTRING_START)('f'')
PyFStringFragment
PsiElement(Py:FSTRING_FRAGMENT_START)('{')
PyNumericLiteralExpression
PsiElement(Py:INTEGER_LITERAL)('42')
PsiErrorElement:'}' expected
PsiErrorElement:Unexpected expression part
PsiElement(Py:SINGLE_QUOTED_STRING)(''')
PsiErrorElement:Type conversion, ':' or '}' expected
<empty list>
PsiElement(Py:FSTRING_END)(''')
PsiErrorElement:' expected
<empty list>
@@ -13,6 +13,6 @@ PyFile:FStringIncompleteFragmentWithTypeConversion.py
PyNumericLiteralExpression
PsiElement(Py:INTEGER_LITERAL)('42')
PsiElement(Py:FSTRING_FRAGMENT_TYPE_CONVERSION)('!r')
PsiErrorElement:'}' expected
PsiErrorElement:: or '}' expected
<empty list>
PsiElement(Py:FSTRING_END)(''')
PsiElement(Py:SINGLE_QUOTED_STRING)(''')
@@ -0,0 +1,23 @@
PyFile:FStringNotTerminatedByLineBreakInExpression.py
PyAssignmentStatement
PyTargetExpression: s
PsiElement(Py:IDENTIFIER)('s')
PsiWhiteSpace(' ')
PsiElement(Py:EQ)('=')
PsiWhiteSpace(' ')
PyStringLiteralExpression: {1 +
2}
PyFormattedStringElement
PsiElement(Py:FSTRING_START)('f'')
PyFStringFragment
PsiElement(Py:FSTRING_FRAGMENT_START)('{')
PyBinaryExpression
PyNumericLiteralExpression
PsiElement(Py:INTEGER_LITERAL)('1')
PsiWhiteSpace(' ')
PsiElement(Py:PLUS)('+')
PsiWhiteSpace('\n')
PyNumericLiteralExpression
PsiElement(Py:INTEGER_LITERAL)('2')
PsiElement(Py:FSTRING_FRAGMENT_END)('}')
PsiElement(Py:FSTRING_END)(''')
@@ -1,4 +1,4 @@
PyFile:FStringTerminatedByLineBreakInExpressionInFormatPart.py
PyFile:FStringNotTerminatedByLineBreakInExpressionInFormatPart.py
PyAssignmentStatement
PyTargetExpression: s
PsiElement(Py:IDENTIFIER)('s')
@@ -6,6 +6,7 @@ PyFile:FStringTerminatedByLineBreakInExpressionInFormatPart.py
PsiElement(Py:EQ)('=')
PsiWhiteSpace(' ')
PyStringLiteralExpression: {42:{1 +
2}}
PyFormattedStringElement
PsiElement(Py:FSTRING_START)('f'')
PyFStringFragment
@@ -21,22 +22,9 @@ PyFile:FStringTerminatedByLineBreakInExpressionInFormatPart.py
PsiElement(Py:INTEGER_LITERAL)('1')
PsiWhiteSpace(' ')
PsiElement(Py:PLUS)('+')
PsiErrorElement:Expression expected
<empty list>
PsiErrorElement:' expected
<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)(''')
PsiWhiteSpace('\n')
PyNumericLiteralExpression
PsiElement(Py:INTEGER_LITERAL)('2')
PsiElement(Py:FSTRING_FRAGMENT_END)('}')
PsiElement(Py:FSTRING_FRAGMENT_END)('}')
PsiElement(Py:FSTRING_END)(''')
@@ -1,4 +1,4 @@
PyFile:FStringTerminatedByLineBreakInNestedExpression.py
PyFile:FStringNotTerminatedByLineBreakInNestedExpression.py
PyAssignmentStatement
PyTargetExpression: s
PsiElement(Py:IDENTIFIER)('s')
@@ -6,11 +6,13 @@ PyFile:FStringTerminatedByLineBreakInNestedExpression.py
PsiElement(Py:EQ)('=')
PsiWhiteSpace(' ')
PyStringLiteralExpression: {f'{1 +
2}'}
PyFormattedStringElement
PsiElement(Py:FSTRING_START)('f"')
PyFStringFragment
PsiElement(Py:FSTRING_FRAGMENT_START)('{')
PyStringLiteralExpression: {1 +
2}
PyFormattedStringElement
PsiElement(Py:FSTRING_START)('f'')
PyFStringFragment
@@ -20,21 +22,10 @@ PyFile:FStringTerminatedByLineBreakInNestedExpression.py
PsiElement(Py:INTEGER_LITERAL)('1')
PsiWhiteSpace(' ')
PsiElement(Py:PLUS)('+')
PsiErrorElement:Expression expected
<empty list>
PsiErrorElement:' expected
<empty list>
PsiErrorElement:" expected
<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)(''}"')
PsiWhiteSpace('\n')
PyNumericLiteralExpression
PsiElement(Py:INTEGER_LITERAL)('2')
PsiElement(Py:FSTRING_FRAGMENT_END)('}')
PsiElement(Py:FSTRING_END)(''')
PsiElement(Py:FSTRING_FRAGMENT_END)('}')
PsiElement(Py:FSTRING_END)('"')
@@ -1,4 +1,4 @@
PyFile:FStringTerminatedByLineBreakInNestedExpressionInFormatPart.py
PyFile:FStringNotTerminatedByLineBreakInNestedExpressionInFormatPart.py
PyAssignmentStatement
PyTargetExpression: s
PsiElement(Py:IDENTIFIER)('s')
@@ -6,11 +6,13 @@ PyFile:FStringTerminatedByLineBreakInNestedExpressionInFormatPart.py
PsiElement(Py:EQ)('=')
PsiWhiteSpace(' ')
PyStringLiteralExpression: {f'{42:{1 +
2}}'}
PyFormattedStringElement
PsiElement(Py:FSTRING_START)('f"')
PyFStringFragment
PsiElement(Py:FSTRING_FRAGMENT_START)('{')
PyStringLiteralExpression: {42:{1 +
2}}
PyFormattedStringElement
PsiElement(Py:FSTRING_START)('f'')
PyFStringFragment
@@ -26,24 +28,11 @@ PyFile:FStringTerminatedByLineBreakInNestedExpressionInFormatPart.py
PsiElement(Py:INTEGER_LITERAL)('1')
PsiWhiteSpace(' ')
PsiElement(Py:PLUS)('+')
PsiErrorElement:Expression expected
<empty list>
PsiErrorElement:' expected
<empty list>
PsiErrorElement:" expected
<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)(''}"')
PsiWhiteSpace('\n')
PyNumericLiteralExpression
PsiElement(Py:INTEGER_LITERAL)('2')
PsiElement(Py:FSTRING_FRAGMENT_END)('}')
PsiElement(Py:FSTRING_FRAGMENT_END)('}')
PsiElement(Py:FSTRING_END)(''')
PsiElement(Py:FSTRING_FRAGMENT_END)('}')
PsiElement(Py:FSTRING_END)('"')
@@ -1,4 +1,4 @@
PyFile:FStringTerminatedByLineBreakInStringLiteral.py
PyFile:FStringNotTerminatedByLineBreakInStringLiteral.py
PyAssignmentStatement
PyTargetExpression: s
PsiElement(Py:IDENTIFIER)('s')
@@ -6,17 +6,13 @@ PyFile:FStringTerminatedByLineBreakInStringLiteral.py
PsiElement(Py:EQ)('=')
PsiWhiteSpace(' ')
PyStringLiteralExpression: {"""
"""}
PyFormattedStringElement
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:' expected
<empty list>
PsiWhiteSpace('\n')
PyExpressionStatement
PyStringLiteralExpression: }'
PsiElement(Py:TRIPLE_QUOTED_STRING)('"""}'')
PsiElement(Py:TRIPLE_QUOTED_STRING)('"""\n"""')
PsiElement(Py:FSTRING_FRAGMENT_END)('}')
PsiElement(Py:FSTRING_END)(''')
@@ -1,4 +1,4 @@
PyFile:FStringTerminatedByLineBreakInStringLiteralInFormatPart.py
PyFile:FStringNotTerminatedByLineBreakInStringLiteralInFormatPart.py
PyAssignmentStatement
PyTargetExpression: s
PsiElement(Py:IDENTIFIER)('s')
@@ -6,6 +6,7 @@ PyFile:FStringTerminatedByLineBreakInStringLiteralInFormatPart.py
PsiElement(Py:EQ)('=')
PsiWhiteSpace(' ')
PyStringLiteralExpression: {foo:{"""
"""}}
PyFormattedStringElement
PsiElement(Py:FSTRING_START)('f'')
PyFStringFragment
@@ -17,12 +18,8 @@ PyFile:FStringTerminatedByLineBreakInStringLiteralInFormatPart.py
PyFStringFragment
PsiElement(Py:FSTRING_FRAGMENT_START)('{')
PyStringLiteralExpression:
PsiElement(Py:TRIPLE_QUOTED_STRING)('"""')
PsiErrorElement:Type conversion, ':' or '}' expected
<empty list>
PsiErrorElement:' expected
<empty list>
PsiWhiteSpace('\n')
PyExpressionStatement
PyStringLiteralExpression: }}'
PsiElement(Py:TRIPLE_QUOTED_STRING)('"""}}'')
PsiElement(Py:TRIPLE_QUOTED_STRING)('"""\n"""')
PsiElement(Py:FSTRING_FRAGMENT_END)('}')
PsiElement(Py:FSTRING_FRAGMENT_END)('}')
PsiElement(Py:FSTRING_END)(''')
@@ -1,16 +1,16 @@
PyFile:FStringTerminatedByQuoteInNestedFormatPart.py
PyFile:FStringNotTerminatedByQuoteInNestedFormatPart.py
PyAssignmentStatement
PyTargetExpression: s
PsiElement(Py:IDENTIFIER)('s')
PsiWhiteSpace(' ')
PsiElement(Py:EQ)('=')
PsiWhiteSpace(' ')
PyStringLiteralExpression: {f"{42:
PyStringLiteralExpression: {f"{42:'}"}
PyFormattedStringElement
PsiElement(Py:FSTRING_START)('f'')
PyFStringFragment
PsiElement(Py:FSTRING_FRAGMENT_START)('{')
PyStringLiteralExpression: {42:
PyStringLiteralExpression: {42:'}
PyFormattedStringElement
PsiElement(Py:FSTRING_START)('f"')
PyFStringFragment
@@ -19,16 +19,8 @@ PyFile:FStringTerminatedByQuoteInNestedFormatPart.py
PsiElement(Py:INTEGER_LITERAL)('42')
PyFStringFragmentFormatPart
PsiElement(Py:FSTRING_FRAGMENT_FORMAT_START)(':')
PsiErrorElement:'}' expected
<empty list>
PsiErrorElement:" expected
<empty list>
PsiElement(Py:FSTRING_END)(''')
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)('"}'')
PsiElement(Py:FSTRING_TEXT)(''')
PsiElement(Py:FSTRING_FRAGMENT_END)('}')
PsiElement(Py:FSTRING_END)('"')
PsiElement(Py:FSTRING_FRAGMENT_END)('}')
PsiElement(Py:FSTRING_END)(''')
@@ -0,0 +1,21 @@
PyFile:FStringNotTerminatedByQuoteInNestedLiteralPart.py
PyAssignmentStatement
PyTargetExpression: s
PsiElement(Py:IDENTIFIER)('s')
PsiWhiteSpace(' ')
PsiElement(Py:EQ)('=')
PsiWhiteSpace(' ')
PyStringLiteralExpression: foo{f"baz'quux"}bar
PyFormattedStringElement
PsiElement(Py:FSTRING_START)('f'')
PsiElement(Py:FSTRING_TEXT)('foo')
PyFStringFragment
PsiElement(Py:FSTRING_FRAGMENT_START)('{')
PyStringLiteralExpression: baz'quux
PyFormattedStringElement
PsiElement(Py:FSTRING_START)('f"')
PsiElement(Py:FSTRING_TEXT)('baz'quux')
PsiElement(Py:FSTRING_END)('"')
PsiElement(Py:FSTRING_FRAGMENT_END)('}')
PsiElement(Py:FSTRING_TEXT)('bar')
PsiElement(Py:FSTRING_END)(''')
@@ -1,22 +1,21 @@
PyFile:FStringTerminatedByQuoteInsideFStringLiteral.py
PyFile:FStringNotTerminatedByQuoteInsideFStringLiteral.py
PyAssignmentStatement
PyTargetExpression: s
PsiElement(Py:IDENTIFIER)('s')
PsiWhiteSpace(' ')
PsiElement(Py:EQ)('=')
PsiWhiteSpace(' ')
PyStringLiteralExpression: foo{f"}baz'
PyStringLiteralExpression: foo{f"'"}baz
PyFormattedStringElement
PsiElement(Py:FSTRING_START)('f'')
PsiElement(Py:FSTRING_TEXT)('foo')
PyFStringFragment
PsiElement(Py:FSTRING_FRAGMENT_START)('{')
PyStringLiteralExpression:
PyStringLiteralExpression: '
PyFormattedStringElement
PsiElement(Py:FSTRING_START)('f"')
PsiErrorElement:" expected
<empty list>
PsiErrorElement:'}' expected
<empty list>
PsiElement(Py:FSTRING_END)(''')
PsiElement(Py:SINGLE_QUOTED_STRING)('"}baz'')
PsiElement(Py:FSTRING_TEXT)(''')
PsiElement(Py:FSTRING_END)('"')
PsiElement(Py:FSTRING_FRAGMENT_END)('}')
PsiElement(Py:FSTRING_TEXT)('baz')
PsiElement(Py:FSTRING_END)(''')
@@ -1,11 +1,11 @@
PyFile:FStringTerminatedByQuoteInsideFStringLiteralInFormatPart.py
PyFile:FStringNotTerminatedByQuoteInsideFStringLiteralInFormatPart.py
PyAssignmentStatement
PyTargetExpression: s
PsiElement(Py:IDENTIFIER)('s')
PsiWhiteSpace(' ')
PsiElement(Py:EQ)('=')
PsiWhiteSpace(' ')
PyStringLiteralExpression: {42:{f"}}'
PyStringLiteralExpression: {42:{f"'"}}
PyFormattedStringElement
PsiElement(Py:FSTRING_START)('f'')
PyFStringFragment
@@ -16,12 +16,11 @@ PyFile:FStringTerminatedByQuoteInsideFStringLiteralInFormatPart.py
PsiElement(Py:FSTRING_FRAGMENT_FORMAT_START)(':')
PyFStringFragment
PsiElement(Py:FSTRING_FRAGMENT_START)('{')
PyStringLiteralExpression:
PyStringLiteralExpression: '
PyFormattedStringElement
PsiElement(Py:FSTRING_START)('f"')
PsiErrorElement:" expected
<empty list>
PsiErrorElement:'}' expected
<empty list>
PsiElement(Py:FSTRING_END)(''')
PsiElement(Py:SINGLE_QUOTED_STRING)('"}}'')
PsiElement(Py:FSTRING_TEXT)(''')
PsiElement(Py:FSTRING_END)('"')
PsiElement(Py:FSTRING_FRAGMENT_END)('}')
PsiElement(Py:FSTRING_FRAGMENT_END)('}')
PsiElement(Py:FSTRING_END)(''')
@@ -0,0 +1,26 @@
PyFile:FStringNotTerminatedByQuoteInsideNestedFStringLiteral.py
PyAssignmentStatement
PyTargetExpression: s
PsiElement(Py:IDENTIFIER)('s')
PsiWhiteSpace(' ')
PsiElement(Py:EQ)('=')
PsiWhiteSpace(' ')
PyStringLiteralExpression: {f"""{f"'"}"""}
PyFormattedStringElement
PsiElement(Py:FSTRING_START)('f'')
PyFStringFragment
PsiElement(Py:FSTRING_FRAGMENT_START)('{')
PyStringLiteralExpression: {f"'"}
PyFormattedStringElement
PsiElement(Py:FSTRING_START)('f"""')
PyFStringFragment
PsiElement(Py:FSTRING_FRAGMENT_START)('{')
PyStringLiteralExpression: '
PyFormattedStringElement
PsiElement(Py:FSTRING_START)('f"')
PsiElement(Py:FSTRING_TEXT)(''')
PsiElement(Py:FSTRING_END)('"')
PsiElement(Py:FSTRING_FRAGMENT_END)('}')
PsiElement(Py:FSTRING_END)('"""')
PsiElement(Py:FSTRING_FRAGMENT_END)('}')
PsiElement(Py:FSTRING_END)(''')
@@ -1,16 +1,16 @@
PyFile:FStringTerminatedByQuoteInsideNestedFStringLiteralInFormatPart.py
PyFile:FStringNotTerminatedByQuoteInsideNestedFStringLiteralInFormatPart.py
PyAssignmentStatement
PyTargetExpression: s
PsiElement(Py:IDENTIFIER)('s')
PsiWhiteSpace(' ')
PsiElement(Py:EQ)('=')
PsiWhiteSpace(' ')
PyStringLiteralExpression: {f"""{42:{f"}}
PyStringLiteralExpression: {f"""{42:{f"'"}}"""}
PyFormattedStringElement
PsiElement(Py:FSTRING_START)('f'')
PyFStringFragment
PsiElement(Py:FSTRING_FRAGMENT_START)('{')
PyStringLiteralExpression: {42:{f"
PyStringLiteralExpression: {42:{f"'"}}
PyFormattedStringElement
PsiElement(Py:FSTRING_START)('f"""')
PyFStringFragment
@@ -21,23 +21,13 @@ PyFile:FStringTerminatedByQuoteInsideNestedFStringLiteralInFormatPart.py
PsiElement(Py:FSTRING_FRAGMENT_FORMAT_START)(':')
PyFStringFragment
PsiElement(Py:FSTRING_FRAGMENT_START)('{')
PyStringLiteralExpression:
PyStringLiteralExpression: '
PyFormattedStringElement
PsiElement(Py:FSTRING_START)('f"')
PsiErrorElement:" expected
<empty list>
PsiErrorElement:'}' 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)(''')
PsiElement(Py:FSTRING_TEXT)(''')
PsiElement(Py:FSTRING_END)('"')
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,23 @@
PyFile:FStringNotTerminatedByQuoteInsideNestedStringLiteral.py
PyAssignmentStatement
PyTargetExpression: s
PsiElement(Py:IDENTIFIER)('s')
PsiWhiteSpace(' ')
PsiElement(Py:EQ)('=')
PsiWhiteSpace(' ')
PyStringLiteralExpression: {f"""{"'"}"""}
PyFormattedStringElement
PsiElement(Py:FSTRING_START)('f'')
PyFStringFragment
PsiElement(Py:FSTRING_FRAGMENT_START)('{')
PyStringLiteralExpression: {"'"}
PyFormattedStringElement
PsiElement(Py:FSTRING_START)('f"""')
PyFStringFragment
PsiElement(Py:FSTRING_FRAGMENT_START)('{')
PyStringLiteralExpression: '
PsiElement(Py:SINGLE_QUOTED_STRING)('"'"')
PsiElement(Py:FSTRING_FRAGMENT_END)('}')
PsiElement(Py:FSTRING_END)('"""')
PsiElement(Py:FSTRING_FRAGMENT_END)('}')
PsiElement(Py:FSTRING_END)(''')
@@ -0,0 +1,30 @@
PyFile:FStringNotTerminatedByQuoteInsideNestedStringLiteralInFormatPart.py
PyAssignmentStatement
PyTargetExpression: s
PsiElement(Py:IDENTIFIER)('s')
PsiWhiteSpace(' ')
PsiElement(Py:EQ)('=')
PsiWhiteSpace(' ')
PyStringLiteralExpression: {f"""{42:{"'"}}"""}
PyFormattedStringElement
PsiElement(Py:FSTRING_START)('f'')
PyFStringFragment
PsiElement(Py:FSTRING_FRAGMENT_START)('{')
PyStringLiteralExpression: {42:{"'"}}
PyFormattedStringElement
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)('"'"')
PsiElement(Py:FSTRING_FRAGMENT_END)('}')
PsiElement(Py:FSTRING_FRAGMENT_END)('}')
PsiElement(Py:FSTRING_END)('"""')
PsiElement(Py:FSTRING_FRAGMENT_END)('}')
PsiElement(Py:FSTRING_END)(''')
@@ -1,19 +1,18 @@
PyFile:FStringTerminatedByQuoteInsideStringLiteral.py
PyFile:FStringNotTerminatedByQuoteInsideStringLiteral.py
PyAssignmentStatement
PyTargetExpression: s
PsiElement(Py:IDENTIFIER)('s')
PsiWhiteSpace(' ')
PsiElement(Py:EQ)('=')
PsiWhiteSpace(' ')
PyStringLiteralExpression: foo{"}baz'
PyStringLiteralExpression: foo{"'"}baz
PyFormattedStringElement
PsiElement(Py:FSTRING_START)('f'')
PsiElement(Py:FSTRING_TEXT)('foo')
PyFStringFragment
PsiElement(Py:FSTRING_FRAGMENT_START)('{')
PyStringLiteralExpression:
PsiElement(Py:SINGLE_QUOTED_STRING)('"')
PsiErrorElement:'}' expected
<empty list>
PsiElement(Py:FSTRING_END)(''')
PsiElement(Py:SINGLE_QUOTED_STRING)('"}baz'')
PyStringLiteralExpression: '
PsiElement(Py:SINGLE_QUOTED_STRING)('"'"')
PsiElement(Py:FSTRING_FRAGMENT_END)('}')
PsiElement(Py:FSTRING_TEXT)('baz')
PsiElement(Py:FSTRING_END)(''')
@@ -1,11 +1,11 @@
PyFile:FStringTerminatedByQuoteInsideStringLiteralInFormatPart.py
PyFile:FStringNotTerminatedByQuoteInsideStringLiteralInFormatPart.py
PyAssignmentStatement
PyTargetExpression: s
PsiElement(Py:IDENTIFIER)('s')
PsiWhiteSpace(' ')
PsiElement(Py:EQ)('=')
PsiWhiteSpace(' ')
PyStringLiteralExpression: {42:{"}}'
PyStringLiteralExpression: {42:{"'"}}
PyFormattedStringElement
PsiElement(Py:FSTRING_START)('f'')
PyFStringFragment
@@ -16,9 +16,8 @@ PyFile:FStringTerminatedByQuoteInsideStringLiteralInFormatPart.py
PsiElement(Py:FSTRING_FRAGMENT_FORMAT_START)(':')
PyFStringFragment
PsiElement(Py:FSTRING_FRAGMENT_START)('{')
PyStringLiteralExpression:
PsiElement(Py:SINGLE_QUOTED_STRING)('"')
PsiErrorElement:'}' expected
<empty list>
PsiElement(Py:FSTRING_END)(''')
PsiElement(Py:SINGLE_QUOTED_STRING)('"}}'')
PyStringLiteralExpression: '
PsiElement(Py:SINGLE_QUOTED_STRING)('"'"')
PsiElement(Py:FSTRING_FRAGMENT_END)('}')
PsiElement(Py:FSTRING_FRAGMENT_END)('}')
PsiElement(Py:FSTRING_END)(''')
@@ -0,0 +1,21 @@
PyFile:FStringNotTerminatedByQuoteOfFStringLiteral.py
PyAssignmentStatement
PyTargetExpression: s
PsiElement(Py:IDENTIFIER)('s')
PsiWhiteSpace(' ')
PsiElement(Py:EQ)('=')
PsiWhiteSpace(' ')
PyStringLiteralExpression: foo{f'bar'}baz
PyFormattedStringElement
PsiElement(Py:FSTRING_START)('f'')
PsiElement(Py:FSTRING_TEXT)('foo')
PyFStringFragment
PsiElement(Py:FSTRING_FRAGMENT_START)('{')
PyStringLiteralExpression: bar
PyFormattedStringElement
PsiElement(Py:FSTRING_START)('f'')
PsiElement(Py:FSTRING_TEXT)('bar')
PsiElement(Py:FSTRING_END)(''')
PsiElement(Py:FSTRING_FRAGMENT_END)('}')
PsiElement(Py:FSTRING_TEXT)('baz')
PsiElement(Py:FSTRING_END)(''')
@@ -0,0 +1,26 @@
PyFile:FStringNotTerminatedByQuoteOfFStringLiteralInFormatPart.py
PyAssignmentStatement
PyTargetExpression: s
PsiElement(Py:IDENTIFIER)('s')
PsiWhiteSpace(' ')
PsiElement(Py:EQ)('=')
PsiWhiteSpace(' ')
PyStringLiteralExpression: {42:{f'foo'}}
PyFormattedStringElement
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: foo
PyFormattedStringElement
PsiElement(Py:FSTRING_START)('f'')
PsiElement(Py:FSTRING_TEXT)('foo')
PsiElement(Py:FSTRING_END)(''')
PsiElement(Py:FSTRING_FRAGMENT_END)('}')
PsiElement(Py:FSTRING_FRAGMENT_END)('}')
PsiElement(Py:FSTRING_END)(''')
@@ -0,0 +1,26 @@
PyFile:FStringNotTerminatedByQuoteOfNestedFStringLiteral.py
PyAssignmentStatement
PyTargetExpression: s
PsiElement(Py:IDENTIFIER)('s')
PsiWhiteSpace(' ')
PsiElement(Py:EQ)('=')
PsiWhiteSpace(' ')
PyStringLiteralExpression: {f"{f'foo'}"}
PyFormattedStringElement
PsiElement(Py:FSTRING_START)('f'')
PyFStringFragment
PsiElement(Py:FSTRING_FRAGMENT_START)('{')
PyStringLiteralExpression: {f'foo'}
PyFormattedStringElement
PsiElement(Py:FSTRING_START)('f"')
PyFStringFragment
PsiElement(Py:FSTRING_FRAGMENT_START)('{')
PyStringLiteralExpression: foo
PyFormattedStringElement
PsiElement(Py:FSTRING_START)('f'')
PsiElement(Py:FSTRING_TEXT)('foo')
PsiElement(Py:FSTRING_END)(''')
PsiElement(Py:FSTRING_FRAGMENT_END)('}')
PsiElement(Py:FSTRING_END)('"')
PsiElement(Py:FSTRING_FRAGMENT_END)('}')
PsiElement(Py:FSTRING_END)(''')
@@ -1,16 +1,16 @@
PyFile:FStringTerminatedByQuoteOfNestedStringLiteralInFormatPart.py
PyFile:FStringNotTerminatedByQuoteOfNestedFStringLiteralInFormatPart.py
PyAssignmentStatement
PyTargetExpression: s
PsiElement(Py:IDENTIFIER)('s')
PsiWhiteSpace(' ')
PsiElement(Py:EQ)('=')
PsiWhiteSpace(' ')
PyStringLiteralExpression: {f"{42:{
PyStringLiteralExpression: {f"{42:{f'foo'}}"}
PyFormattedStringElement
PsiElement(Py:FSTRING_START)('f'')
PyFStringFragment
PsiElement(Py:FSTRING_FRAGMENT_START)('{')
PyStringLiteralExpression: {42:{
PyStringLiteralExpression: {42:{f'foo'}}
PyFormattedStringElement
PsiElement(Py:FSTRING_START)('f"')
PyFStringFragment
@@ -21,20 +21,13 @@ PyFile:FStringTerminatedByQuoteOfNestedStringLiteralInFormatPart.py
PsiElement(Py:FSTRING_FRAGMENT_FORMAT_START)(':')
PyFStringFragment
PsiElement(Py:FSTRING_FRAGMENT_START)('{')
PsiErrorElement:Expression expected
<empty list>
PsiErrorElement:'}' 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)(''}}"}'')
PyStringLiteralExpression: foo
PyFormattedStringElement
PsiElement(Py:FSTRING_START)('f'')
PsiElement(Py:FSTRING_TEXT)('foo')
PsiElement(Py:FSTRING_END)(''')
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,23 @@
PyFile:FStringNotTerminatedByQuoteOfNestedStringLiteral.py
PyAssignmentStatement
PyTargetExpression: s
PsiElement(Py:IDENTIFIER)('s')
PsiWhiteSpace(' ')
PsiElement(Py:EQ)('=')
PsiWhiteSpace(' ')
PyStringLiteralExpression: {f"{'foo'}"}
PyFormattedStringElement
PsiElement(Py:FSTRING_START)('f'')
PyFStringFragment
PsiElement(Py:FSTRING_FRAGMENT_START)('{')
PyStringLiteralExpression: {'foo'}
PyFormattedStringElement
PsiElement(Py:FSTRING_START)('f"')
PyFStringFragment
PsiElement(Py:FSTRING_FRAGMENT_START)('{')
PyStringLiteralExpression: foo
PsiElement(Py:SINGLE_QUOTED_STRING)(''foo'')
PsiElement(Py:FSTRING_FRAGMENT_END)('}')
PsiElement(Py:FSTRING_END)('"')
PsiElement(Py:FSTRING_FRAGMENT_END)('}')
PsiElement(Py:FSTRING_END)(''')
@@ -1,16 +1,16 @@
PyFile:FStringTerminatedByQuoteOfNestedFStringLiteralInFormatPart.py
PyFile:FStringNotTerminatedByQuoteOfNestedStringLiteralInFormatPart.py
PyAssignmentStatement
PyTargetExpression: s
PsiElement(Py:IDENTIFIER)('s')
PsiWhiteSpace(' ')
PsiElement(Py:EQ)('=')
PsiWhiteSpace(' ')
PyStringLiteralExpression: {f"{42:{f
PyStringLiteralExpression: {f"{42:{'foo'}}"}
PyFormattedStringElement
PsiElement(Py:FSTRING_START)('f'')
PyFStringFragment
PsiElement(Py:FSTRING_FRAGMENT_START)('{')
PyStringLiteralExpression: {42:{f
PyStringLiteralExpression: {42:{'foo'}}
PyFormattedStringElement
PsiElement(Py:FSTRING_START)('f"')
PyFStringFragment
@@ -21,20 +21,10 @@ PyFile:FStringTerminatedByQuoteOfNestedFStringLiteralInFormatPart.py
PsiElement(Py:FSTRING_FRAGMENT_FORMAT_START)(':')
PyFStringFragment
PsiElement(Py:FSTRING_FRAGMENT_START)('{')
PyReferenceExpression: f
PsiElement(Py:IDENTIFIER)('f')
PsiErrorElement:'}' 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)(''}}"}'')
PyStringLiteralExpression: foo
PsiElement(Py:SINGLE_QUOTED_STRING)(''foo'')
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,18 @@
PyFile:FStringNotTerminatedByQuoteOfStringLiteral.py
PyAssignmentStatement
PyTargetExpression: s
PsiElement(Py:IDENTIFIER)('s')
PsiWhiteSpace(' ')
PsiElement(Py:EQ)('=')
PsiWhiteSpace(' ')
PyStringLiteralExpression: foo{'bar'}baz
PyFormattedStringElement
PsiElement(Py:FSTRING_START)('f'')
PsiElement(Py:FSTRING_TEXT)('foo')
PyFStringFragment
PsiElement(Py:FSTRING_FRAGMENT_START)('{')
PyStringLiteralExpression: bar
PsiElement(Py:SINGLE_QUOTED_STRING)(''bar'')
PsiElement(Py:FSTRING_FRAGMENT_END)('}')
PsiElement(Py:FSTRING_TEXT)('baz')
PsiElement(Py:FSTRING_END)(''')
@@ -0,0 +1,23 @@
PyFile:FStringNotTerminatedByQuoteOfStringLiteralInFormatPart.py
PyAssignmentStatement
PyTargetExpression: s
PsiElement(Py:IDENTIFIER)('s')
PsiWhiteSpace(' ')
PsiElement(Py:EQ)('=')
PsiWhiteSpace(' ')
PyStringLiteralExpression: {42:{'foo'}}
PyFormattedStringElement
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: foo
PsiElement(Py:SINGLE_QUOTED_STRING)(''foo'')
PsiElement(Py:FSTRING_FRAGMENT_END)('}')
PsiElement(Py:FSTRING_FRAGMENT_END)('}')
PsiElement(Py:FSTRING_END)(''')
@@ -1,11 +1,11 @@
PyFile:MultilineFStringTerminatedByQuotesInsideParenthesizedExpression.py
PyFile:FStringNotTerminatedByQuotesInsideParenthesizedExpression.py
PyAssignmentStatement
PyTargetExpression: s
PsiElement(Py:IDENTIFIER)('s')
PsiWhiteSpace(' ')
PsiElement(Py:EQ)('=')
PsiWhiteSpace(' ')
PyStringLiteralExpression: { (lambda x:
PyStringLiteralExpression: { (lambda x: 'foo') }
PyFormattedStringElement
PsiElement(Py:FSTRING_START)('f'')
PyFStringFragment
@@ -20,17 +20,10 @@ PyFile:MultilineFStringTerminatedByQuotesInsideParenthesizedExpression.py
PyNamedParameter('x')
PsiElement(Py:IDENTIFIER)('x')
PsiElement(Py:COLON)(':')
PsiErrorElement:Expression expected
<empty list>
PsiWhiteSpace(' ')
PyStringLiteralExpression: foo
PsiElement(Py:SINGLE_QUOTED_STRING)(''foo'')
PsiElement(Py:RPAR)(')')
PsiWhiteSpace(' ')
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)('') }'')
PsiElement(Py:FSTRING_FRAGMENT_END)('}')
PsiElement(Py:FSTRING_END)(''')

Some files were not shown because too many files have changed in this diff Show More