From 37d25ee8150baa5fd93bdb4ba894508f5477eb2d Mon Sep 17 00:00:00 2001 From: Mikhail Golubev Date: Mon, 18 Sep 2023 22:50:49 +0300 Subject: [PATCH] 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 --- .../resources/messages/PyPsiBundle.properties | 8 +- .../python/lexer/PyLexerFStringHelper.kt | 33 +--- .../lexer/PythonIndentingProcessor.java | 52 ++++-- .../validation/CompatibilityVisitor.java | 87 +++++++++- .../python/validation/FStringsAnnotator.java | 31 ---- .../highlighting/fStringBackslashes.py | 6 - .../fStringBackslashesBefore312.py | 7 + .../highlighting/fStringCommentsBefore312.py | 3 + .../highlighting/fStringEmptyExpressions.py | 3 - .../fStringEqualitySignBefore312.py | 19 ++ .../testData/highlighting/fStringHashSigns.py | 10 -- .../fStringIllegalConversionCharacter.py | 2 - .../highlighting/fStringMissingRightBrace.py | 11 +- ...minatedByLineBreakInExpressionBefore312.py | 2 + ...eBreakInExpressionInFormatPartBefore312.py | 2 + ...dByLineBreakInNestedExpressionBefore312.py | 2 + ...InNestedExpressionInFormatPartBefore312.py | 2 + ...dByLineBreakInNestedFormatPartBefore312.py | 2 + ...atedByLineBreakInStringLiteralBefore312.py | 2 + ...eakInStringLiteralInFormatPartBefore312.py | 2 + ...natedByQuoteInNestedFormatPartBefore312.py | 1 + ...atedByQuoteInNestedLiteralPartBefore312.py | 1 + ...tedByQuoteInsideFStringLiteralBefore312.py | 1 + ...sideFStringLiteralInFormatPartBefore312.py | 1 + ...uoteInsideNestedFStringLiteralBefore312.py | 1 + ...stedFStringLiteralInFormatPartBefore312.py | 1 + ...QuoteInsideNestedStringLiteralBefore312.py | 1 + ...estedStringLiteralInFormatPartBefore312.py | 1 + ...atedByQuoteInsideStringLiteralBefore312.py | 1 + ...nsideStringLiteralInFormatPartBefore312.py | 1 + ...minatedByQuoteOfFStringLiteralBefore312.py | 1 + ...teOfFStringLiteralInFormatPartBefore312.py | 1 + ...dByQuoteOfNestedFStringLiteralBefore312.py | 1 + ...stedFStringLiteralInFormatPartBefore312.py | 1 + ...edByQuoteOfNestedStringLiteralBefore312.py | 1 + ...estedStringLiteralInFormatPartBefore312.py | 1 + ...rminatedByQuoteOfStringLiteralBefore312.py | 1 + ...oteOfStringLiteralInFormatPartBefore312.py | 1 + ...tringTooDeeplyNestedExpressionFragments.py | 7 - ...eeplyNestedExpressionFragmentsBefore312.py | 5 + ...eExpressionAfterStatementBreakBefore312.py | 3 + ...gContainingMultilineExpressionBefore312.py | 2 + ...sInsideParenthesizedExpressionBefore312.py | 1 + ...minatedByQuotesOfStringLiteralBefore312.py | 3 + ...tringsWithMultilineExpressionsBefore312.py | 5 + ...minatedByLineBreakInExpressionBefore312.py | 2 + ...BreakInExpressionInParenthesesBefore312.py | 2 + ...mpleteTypeConversionBeforeClosingQuote.txt | 4 +- .../psi/FStringIncompleteFragment.txt | 9 +- ...ngIncompleteFragmentWithTypeConversion.txt | 4 +- ...ngNotTerminatedByLineBreakInExpression.py} | 0 ...ngNotTerminatedByLineBreakInExpression.txt | 23 +++ ...tedByLineBreakInExpressionInFormatPart.py} | 0 ...edByLineBreakInExpressionInFormatPart.txt} | 28 +-- ...erminatedByLineBreakInNestedExpression.py} | 0 ...rminatedByLineBreakInNestedExpression.txt} | 29 ++-- ...ineBreakInNestedExpressionInFormatPart.py} | 0 ...neBreakInNestedExpressionInFormatPart.txt} | 33 ++-- ...otTerminatedByLineBreakInStringLiteral.py} | 0 ...tTerminatedByLineBreakInStringLiteral.txt} | 16 +- ...ByLineBreakInStringLiteralInFormatPart.py} | 0 ...yLineBreakInStringLiteralInFormatPart.txt} | 17 +- ...NotTerminatedByQuoteInNestedFormatPart.py} | 0 ...otTerminatedByQuoteInNestedFormatPart.txt} | 24 +-- ...otTerminatedByQuoteInNestedLiteralPart.py} | 0 ...otTerminatedByQuoteInNestedLiteralPart.txt | 21 +++ ...tTerminatedByQuoteInsideFStringLiteral.py} | 0 ...TerminatedByQuoteInsideFStringLiteral.txt} | 17 +- ...yQuoteInsideFStringLiteralInFormatPart.py} | 0 ...QuoteInsideFStringLiteralInFormatPart.txt} | 17 +- ...natedByQuoteInsideNestedFStringLiteral.py} | 0 ...natedByQuoteInsideNestedFStringLiteral.txt | 26 +++ ...InsideNestedFStringLiteralInFormatPart.py} | 0 ...nsideNestedFStringLiteralInFormatPart.txt} | 32 ++-- ...inatedByQuoteInsideNestedStringLiteral.py} | 0 ...inatedByQuoteInsideNestedStringLiteral.txt | 23 +++ ...eInsideNestedStringLiteralInFormatPart.py} | 0 ...eInsideNestedStringLiteralInFormatPart.txt | 30 ++++ ...otTerminatedByQuoteInsideStringLiteral.py} | 0 ...tTerminatedByQuoteInsideStringLiteral.txt} | 15 +- ...ByQuoteInsideStringLiteralInFormatPart.py} | 0 ...yQuoteInsideStringLiteralInFormatPart.txt} | 15 +- ...ngNotTerminatedByQuoteOfFStringLiteral.py} | 0 ...ngNotTerminatedByQuoteOfFStringLiteral.txt | 21 +++ ...tedByQuoteOfFStringLiteralInFormatPart.py} | 0 ...tedByQuoteOfFStringLiteralInFormatPart.txt | 26 +++ ...erminatedByQuoteOfNestedFStringLiteral.py} | 0 ...erminatedByQuoteOfNestedFStringLiteral.txt | 26 +++ ...uoteOfNestedFStringLiteralInFormatPart.py} | 0 ...oteOfNestedFStringLiteralInFormatPart.txt} | 33 ++-- ...TerminatedByQuoteOfNestedStringLiteral.py} | 0 ...TerminatedByQuoteOfNestedStringLiteral.txt | 23 +++ ...QuoteOfNestedStringLiteralInFormatPart.py} | 0 ...uoteOfNestedStringLiteralInFormatPart.txt} | 30 ++-- ...ingNotTerminatedByQuoteOfStringLiteral.py} | 0 ...ingNotTerminatedByQuoteOfStringLiteral.txt | 18 ++ ...atedByQuoteOfStringLiteralInFormatPart.py} | 0 ...atedByQuoteOfStringLiteralInFormatPart.txt | 23 +++ ...dByQuotesInsideParenthesizedExpression.py} | 0 ...ByQuotesInsideParenthesizedExpression.txt} | 23 +-- ...tringTerminatedByLineBreakInExpression.txt | 33 ---- ...ngTerminatedByQuoteInNestedLiteralPart.txt | 32 ---- ...natedByQuoteInsideNestedFStringLiteral.txt | 37 ---- ...inatedByQuoteInsideNestedStringLiteral.txt | 34 ---- ...eInsideNestedStringLiteralInFormatPart.txt | 40 ----- ...tringTerminatedByQuoteOfFStringLiteral.txt | 28 --- ...tedByQuoteOfFStringLiteralInFormatPart.txt | 33 ---- ...erminatedByQuoteOfNestedFStringLiteral.txt | 34 ---- ...TerminatedByQuoteOfNestedStringLiteral.txt | 34 ---- ...StringTerminatedByQuoteOfStringLiteral.txt | 28 --- ...atedByQuoteOfStringLiteralInFormatPart.txt | 33 ---- ...espaceInIncompleteFragmentInFormatPart.txt | 12 +- ...ngNotTerminatedByQuotesOfStringLiteral.py} | 0 ...gNotTerminatedByQuotesOfStringLiteral.txt} | 16 +- ...ngNotTerminatedByLineBreakInExpression.py} | 0 ...ngNotTerminatedByLineBreakInExpression.txt | 31 ++++ ...edByLineBreakInExpressionInParentheses.py} | 0 ...edByLineBreakInExpressionInParentheses.txt | 34 ++++ ...tringTerminatedByLineBreakInExpression.txt | 40 ----- ...edByLineBreakInExpressionInParentheses.txt | 45 ----- .../com/jetbrains/python/PyEditingTest.java | 2 +- .../python/PythonHighlightingTest.java | 162 +++++++++++++++++- .../com/jetbrains/python/PythonLexerTest.java | 27 ++- .../python/parsing/PythonParsingTest.java | 62 +++---- 124 files changed, 896 insertions(+), 841 deletions(-) delete mode 100644 python/testData/highlighting/fStringBackslashes.py create mode 100644 python/testData/highlighting/fStringBackslashesBefore312.py create mode 100644 python/testData/highlighting/fStringCommentsBefore312.py create mode 100644 python/testData/highlighting/fStringEqualitySignBefore312.py delete mode 100644 python/testData/highlighting/fStringHashSigns.py create mode 100644 python/testData/highlighting/fStringTerminatedByLineBreakInExpressionBefore312.py create mode 100644 python/testData/highlighting/fStringTerminatedByLineBreakInExpressionInFormatPartBefore312.py create mode 100644 python/testData/highlighting/fStringTerminatedByLineBreakInNestedExpressionBefore312.py create mode 100644 python/testData/highlighting/fStringTerminatedByLineBreakInNestedExpressionInFormatPartBefore312.py create mode 100644 python/testData/highlighting/fStringTerminatedByLineBreakInNestedFormatPartBefore312.py create mode 100644 python/testData/highlighting/fStringTerminatedByLineBreakInStringLiteralBefore312.py create mode 100644 python/testData/highlighting/fStringTerminatedByLineBreakInStringLiteralInFormatPartBefore312.py create mode 100644 python/testData/highlighting/fStringTerminatedByQuoteInNestedFormatPartBefore312.py create mode 100644 python/testData/highlighting/fStringTerminatedByQuoteInNestedLiteralPartBefore312.py create mode 100644 python/testData/highlighting/fStringTerminatedByQuoteInsideFStringLiteralBefore312.py create mode 100644 python/testData/highlighting/fStringTerminatedByQuoteInsideFStringLiteralInFormatPartBefore312.py create mode 100644 python/testData/highlighting/fStringTerminatedByQuoteInsideNestedFStringLiteralBefore312.py create mode 100644 python/testData/highlighting/fStringTerminatedByQuoteInsideNestedFStringLiteralInFormatPartBefore312.py create mode 100644 python/testData/highlighting/fStringTerminatedByQuoteInsideNestedStringLiteralBefore312.py create mode 100644 python/testData/highlighting/fStringTerminatedByQuoteInsideNestedStringLiteralInFormatPartBefore312.py create mode 100644 python/testData/highlighting/fStringTerminatedByQuoteInsideStringLiteralBefore312.py create mode 100644 python/testData/highlighting/fStringTerminatedByQuoteInsideStringLiteralInFormatPartBefore312.py create mode 100644 python/testData/highlighting/fStringTerminatedByQuoteOfFStringLiteralBefore312.py create mode 100644 python/testData/highlighting/fStringTerminatedByQuoteOfFStringLiteralInFormatPartBefore312.py create mode 100644 python/testData/highlighting/fStringTerminatedByQuoteOfNestedFStringLiteralBefore312.py create mode 100644 python/testData/highlighting/fStringTerminatedByQuoteOfNestedFStringLiteralInFormatPartBefore312.py create mode 100644 python/testData/highlighting/fStringTerminatedByQuoteOfNestedStringLiteralBefore312.py create mode 100644 python/testData/highlighting/fStringTerminatedByQuoteOfNestedStringLiteralInFormatPartBefore312.py create mode 100644 python/testData/highlighting/fStringTerminatedByQuoteOfStringLiteralBefore312.py create mode 100644 python/testData/highlighting/fStringTerminatedByQuoteOfStringLiteralInFormatPartBefore312.py delete mode 100644 python/testData/highlighting/fStringTooDeeplyNestedExpressionFragments.py create mode 100644 python/testData/highlighting/fStringTooDeeplyNestedExpressionFragmentsBefore312.py create mode 100644 python/testData/highlighting/multilineFStringContainingMultilineExpressionAfterStatementBreakBefore312.py create mode 100644 python/testData/highlighting/multilineFStringContainingMultilineExpressionBefore312.py create mode 100644 python/testData/highlighting/multilineFStringTerminatedByQuotesInsideParenthesizedExpressionBefore312.py create mode 100644 python/testData/highlighting/multilineFStringTerminatedByQuotesOfStringLiteralBefore312.py create mode 100644 python/testData/highlighting/nestedMultilineFStringsWithMultilineExpressionsBefore312.py create mode 100644 python/testData/highlighting/singleQuotedFStringInsideMultilineFStringTerminatedByLineBreakInExpressionBefore312.py create mode 100644 python/testData/highlighting/singleQuotedFStringInsideMultilineFStringTerminatedByLineBreakInExpressionInParenthesesBefore312.py rename python/testData/psi/{FStringTerminatedByLineBreakInExpression.py => FStringNotTerminatedByLineBreakInExpression.py} (100%) create mode 100644 python/testData/psi/FStringNotTerminatedByLineBreakInExpression.txt rename python/testData/psi/{FStringTerminatedByLineBreakInExpressionInFormatPart.py => FStringNotTerminatedByLineBreakInExpressionInFormatPart.py} (100%) rename python/testData/psi/{FStringTerminatedByLineBreakInExpressionInFormatPart.txt => FStringNotTerminatedByLineBreakInExpressionInFormatPart.txt} (54%) rename python/testData/psi/{FStringTerminatedByLineBreakInNestedExpression.py => FStringNotTerminatedByLineBreakInNestedExpression.py} (100%) rename python/testData/psi/{FStringTerminatedByLineBreakInNestedExpression.txt => FStringNotTerminatedByLineBreakInNestedExpression.txt} (54%) rename python/testData/psi/{FStringTerminatedByLineBreakInNestedExpressionInFormatPart.py => FStringNotTerminatedByLineBreakInNestedExpressionInFormatPart.py} (100%) rename python/testData/psi/{FStringTerminatedByLineBreakInNestedExpressionInFormatPart.txt => FStringNotTerminatedByLineBreakInNestedExpressionInFormatPart.txt} (58%) rename python/testData/psi/{FStringTerminatedByLineBreakInStringLiteral.py => FStringNotTerminatedByLineBreakInStringLiteral.py} (100%) rename python/testData/psi/{FStringTerminatedByLineBreakInStringLiteral.txt => FStringNotTerminatedByLineBreakInStringLiteral.txt} (50%) rename python/testData/psi/{FStringTerminatedByLineBreakInStringLiteralInFormatPart.py => FStringNotTerminatedByLineBreakInStringLiteralInFormatPart.py} (100%) rename python/testData/psi/{FStringTerminatedByLineBreakInStringLiteralInFormatPart.txt => FStringNotTerminatedByLineBreakInStringLiteralInFormatPart.txt} (61%) rename python/testData/psi/{FStringTerminatedByQuoteInNestedFormatPart.py => FStringNotTerminatedByQuoteInNestedFormatPart.py} (100%) rename python/testData/psi/{FStringTerminatedByQuoteInNestedFormatPart.txt => FStringNotTerminatedByQuoteInNestedFormatPart.txt} (54%) rename python/testData/psi/{FStringTerminatedByQuoteInNestedLiteralPart.py => FStringNotTerminatedByQuoteInNestedLiteralPart.py} (100%) create mode 100644 python/testData/psi/FStringNotTerminatedByQuoteInNestedLiteralPart.txt rename python/testData/psi/{FStringTerminatedByQuoteInsideFStringLiteral.py => FStringNotTerminatedByQuoteInsideFStringLiteral.py} (100%) rename python/testData/psi/{FStringTerminatedByQuoteInsideFStringLiteral.txt => FStringNotTerminatedByQuoteInsideFStringLiteral.txt} (54%) rename python/testData/psi/{FStringTerminatedByQuoteInsideFStringLiteralInFormatPart.py => FStringNotTerminatedByQuoteInsideFStringLiteralInFormatPart.py} (100%) rename python/testData/psi/{FStringTerminatedByQuoteInsideFStringLiteralInFormatPart.txt => FStringNotTerminatedByQuoteInsideFStringLiteralInFormatPart.txt} (62%) rename python/testData/psi/{FStringTerminatedByQuoteInsideNestedFStringLiteral.py => FStringNotTerminatedByQuoteInsideNestedFStringLiteral.py} (100%) create mode 100644 python/testData/psi/FStringNotTerminatedByQuoteInsideNestedFStringLiteral.txt rename python/testData/psi/{FStringTerminatedByQuoteInsideNestedFStringLiteralInFormatPart.py => FStringNotTerminatedByQuoteInsideNestedFStringLiteralInFormatPart.py} (100%) rename python/testData/psi/{FStringTerminatedByQuoteInsideNestedFStringLiteralInFormatPart.txt => FStringNotTerminatedByQuoteInsideNestedFStringLiteralInFormatPart.txt} (51%) rename python/testData/psi/{FStringTerminatedByQuoteInsideNestedStringLiteral.py => FStringNotTerminatedByQuoteInsideNestedStringLiteral.py} (100%) create mode 100644 python/testData/psi/FStringNotTerminatedByQuoteInsideNestedStringLiteral.txt rename python/testData/psi/{FStringTerminatedByQuoteInsideNestedStringLiteralInFormatPart.py => FStringNotTerminatedByQuoteInsideNestedStringLiteralInFormatPart.py} (100%) create mode 100644 python/testData/psi/FStringNotTerminatedByQuoteInsideNestedStringLiteralInFormatPart.txt rename python/testData/psi/{FStringTerminatedByQuoteInsideStringLiteral.py => FStringNotTerminatedByQuoteInsideStringLiteral.py} (100%) rename python/testData/psi/{FStringTerminatedByQuoteInsideStringLiteral.txt => FStringNotTerminatedByQuoteInsideStringLiteral.txt} (51%) rename python/testData/psi/{FStringTerminatedByQuoteInsideStringLiteralInFormatPart.py => FStringNotTerminatedByQuoteInsideStringLiteralInFormatPart.py} (100%) rename python/testData/psi/{FStringTerminatedByQuoteInsideStringLiteralInFormatPart.txt => FStringNotTerminatedByQuoteInsideStringLiteralInFormatPart.txt} (61%) rename python/testData/psi/{FStringTerminatedByQuoteOfFStringLiteral.py => FStringNotTerminatedByQuoteOfFStringLiteral.py} (100%) create mode 100644 python/testData/psi/FStringNotTerminatedByQuoteOfFStringLiteral.txt rename python/testData/psi/{FStringTerminatedByQuoteOfFStringLiteralInFormatPart.py => FStringNotTerminatedByQuoteOfFStringLiteralInFormatPart.py} (100%) create mode 100644 python/testData/psi/FStringNotTerminatedByQuoteOfFStringLiteralInFormatPart.txt rename python/testData/psi/{FStringTerminatedByQuoteOfNestedFStringLiteral.py => FStringNotTerminatedByQuoteOfNestedFStringLiteral.py} (100%) create mode 100644 python/testData/psi/FStringNotTerminatedByQuoteOfNestedFStringLiteral.txt rename python/testData/psi/{FStringTerminatedByQuoteOfNestedFStringLiteralInFormatPart.py => FStringNotTerminatedByQuoteOfNestedFStringLiteralInFormatPart.py} (100%) rename python/testData/psi/{FStringTerminatedByQuoteOfNestedStringLiteralInFormatPart.txt => FStringNotTerminatedByQuoteOfNestedFStringLiteralInFormatPart.txt} (50%) rename python/testData/psi/{FStringTerminatedByQuoteOfNestedStringLiteral.py => FStringNotTerminatedByQuoteOfNestedStringLiteral.py} (100%) create mode 100644 python/testData/psi/FStringNotTerminatedByQuoteOfNestedStringLiteral.txt rename python/testData/psi/{FStringTerminatedByQuoteOfNestedStringLiteralInFormatPart.py => FStringNotTerminatedByQuoteOfNestedStringLiteralInFormatPart.py} (100%) rename python/testData/psi/{FStringTerminatedByQuoteOfNestedFStringLiteralInFormatPart.txt => FStringNotTerminatedByQuoteOfNestedStringLiteralInFormatPart.txt} (50%) rename python/testData/psi/{FStringTerminatedByQuoteOfStringLiteral.py => FStringNotTerminatedByQuoteOfStringLiteral.py} (100%) create mode 100644 python/testData/psi/FStringNotTerminatedByQuoteOfStringLiteral.txt rename python/testData/psi/{FStringTerminatedByQuoteOfStringLiteralInFormatPart.py => FStringNotTerminatedByQuoteOfStringLiteralInFormatPart.py} (100%) create mode 100644 python/testData/psi/FStringNotTerminatedByQuoteOfStringLiteralInFormatPart.txt rename python/testData/psi/{MultilineFStringTerminatedByQuotesInsideParenthesizedExpression.py => FStringNotTerminatedByQuotesInsideParenthesizedExpression.py} (100%) rename python/testData/psi/{MultilineFStringTerminatedByQuotesInsideParenthesizedExpression.txt => FStringNotTerminatedByQuotesInsideParenthesizedExpression.txt} (55%) delete mode 100644 python/testData/psi/FStringTerminatedByLineBreakInExpression.txt delete mode 100644 python/testData/psi/FStringTerminatedByQuoteInNestedLiteralPart.txt delete mode 100644 python/testData/psi/FStringTerminatedByQuoteInsideNestedFStringLiteral.txt delete mode 100644 python/testData/psi/FStringTerminatedByQuoteInsideNestedStringLiteral.txt delete mode 100644 python/testData/psi/FStringTerminatedByQuoteInsideNestedStringLiteralInFormatPart.txt delete mode 100644 python/testData/psi/FStringTerminatedByQuoteOfFStringLiteral.txt delete mode 100644 python/testData/psi/FStringTerminatedByQuoteOfFStringLiteralInFormatPart.txt delete mode 100644 python/testData/psi/FStringTerminatedByQuoteOfNestedFStringLiteral.txt delete mode 100644 python/testData/psi/FStringTerminatedByQuoteOfNestedStringLiteral.txt delete mode 100644 python/testData/psi/FStringTerminatedByQuoteOfStringLiteral.txt delete mode 100644 python/testData/psi/FStringTerminatedByQuoteOfStringLiteralInFormatPart.txt rename python/testData/psi/{MultilineFStringTerminatedByQuotesOfStringLiteral.py => MultilineFStringNotTerminatedByQuotesOfStringLiteral.py} (100%) rename python/testData/psi/{MultilineFStringTerminatedByQuotesOfStringLiteral.txt => MultilineFStringNotTerminatedByQuotesOfStringLiteral.txt} (51%) rename python/testData/psi/{SingleQuotedFStringInsideMultilineFStringTerminatedByLineBreakInExpression.py => SingleQuotedFStringInsideMultilineFStringNotTerminatedByLineBreakInExpression.py} (100%) create mode 100644 python/testData/psi/SingleQuotedFStringInsideMultilineFStringNotTerminatedByLineBreakInExpression.txt rename python/testData/psi/{SingleQuotedFStringInsideMultilineFStringTerminatedByLineBreakInExpressionInParentheses.py => SingleQuotedFStringInsideMultilineFStringNotTerminatedByLineBreakInExpressionInParentheses.py} (100%) create mode 100644 python/testData/psi/SingleQuotedFStringInsideMultilineFStringNotTerminatedByLineBreakInExpressionInParentheses.txt delete mode 100644 python/testData/psi/SingleQuotedFStringInsideMultilineFStringTerminatedByLineBreakInExpression.txt delete mode 100644 python/testData/psi/SingleQuotedFStringInsideMultilineFStringTerminatedByLineBreakInExpressionInParentheses.txt diff --git a/python/python-psi-impl/resources/messages/PyPsiBundle.properties b/python/python-psi-impl/resources/messages/PyPsiBundle.properties index 5d083938730f..e7b3b30930b9 100644 --- a/python/python-psi-impl/resources/messages/PyPsiBundle.properties +++ b/python/python-psi-impl/resources/messages/PyPsiBundle.properties @@ -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. diff --git a/python/python-psi-impl/src/com/jetbrains/python/lexer/PyLexerFStringHelper.kt b/python/python-psi-impl/src/com/jetbrains/python/lexer/PyLexerFStringHelper.kt index 6efcaa250620..0a0c48cf1ce2 100644 --- a/python/python-psi-impl/src/com/jetbrains/python/lexer/PyLexerFStringHelper.kt +++ b/python/python-psi-impl/src/com/jetbrains/python/lexer/PyLexerFStringHelper.kt @@ -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 { @@ -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) } } diff --git a/python/python-psi-impl/src/com/jetbrains/python/lexer/PythonIndentingProcessor.java b/python/python-psi-impl/src/com/jetbrains/python/lexer/PythonIndentingProcessor.java index 9f43aa3871ae..a8d1c8a10e59 100644 --- a/python/python-psi-impl/src/com/jetbrains/python/lexer/PythonIndentingProcessor.java +++ b/python/python-psi-impl/src/com/jetbrains/python/lexer/PythonIndentingProcessor.java @@ -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 myFStringStack = new Stack<>(); + private final Stack 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 fragments) { + } + + private enum FStringFragmentPart { + EXPRESSION, + TYPE_CONVERSION_OR_FORMAT, + } + } diff --git a/python/python-psi-impl/src/com/jetbrains/python/validation/CompatibilityVisitor.java b/python/python-psi-impl/src/com/jetbrains/python/validation/CompatibilityVisitor.java index d747ee6fd7c3..bc4383c30144 100644 --- a/python/python-psi-impl/src/com/jetbrains/python/validation/CompatibilityVisitor.java +++ b/python/python-psi-impl/src/com/jetbrains/python/validation/CompatibilityVisitor.java @@ -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 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 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 containingFStrings = + PsiTreeUtil.collectParents(node, PyFormattedStringElement.class, false, e -> e instanceof PyStatement); + assert !containingFStrings.isEmpty(); + PyFormattedStringElement parentFString = containingFStrings.get(0); + List 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
+ // + 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); } } diff --git a/python/python-psi-impl/src/com/jetbrains/python/validation/FStringsAnnotator.java b/python/python-psi-impl/src/com/jetbrains/python/validation/FStringsAnnotator.java index 4ed847248fc3..b56e00617223 100644 --- a/python/python-psi-impl/src/com/jetbrains/python/validation/FStringsAnnotator.java +++ b/python/python-psi-impl/src/com/jetbrains/python/validation/FStringsAnnotator.java @@ -39,11 +39,6 @@ public class FStringsAnnotator extends PyAnnotator { @Override public void visitPyFStringFragment(@NotNull PyFStringFragment node) { - final List 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 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(); diff --git a/python/testData/highlighting/fStringBackslashes.py b/python/testData/highlighting/fStringBackslashes.py deleted file mode 100644 index 075062450b90..000000000000 --- a/python/testData/highlighting/fStringBackslashes.py +++ /dev/null @@ -1,6 +0,0 @@ -f'{\t}' -f'{\t' -f'{\N{GREEK SMALL LETTER ALPHA}}' -f'{Formatable():\n\t}' -f'{42:{\t}}' -f'{f"""{"\n"}"""}' \ No newline at end of file diff --git a/python/testData/highlighting/fStringBackslashesBefore312.py b/python/testData/highlighting/fStringBackslashesBefore312.py new file mode 100644 index 000000000000..34a7e0398be6 --- /dev/null +++ b/python/testData/highlighting/fStringBackslashesBefore312.py @@ -0,0 +1,7 @@ +f'{\t}' +f'{\N{GREEK SMALL LETTER ALPHA}}' +f'{Formatable():\n\t}' +f'{42:{\t}}' +f'{f"""{"\n"}"""}' +f'{\t' + \ No newline at end of file diff --git a/python/testData/highlighting/fStringCommentsBefore312.py b/python/testData/highlighting/fStringCommentsBefore312.py new file mode 100644 index 000000000000..1c89ecac6ed7 --- /dev/null +++ b/python/testData/highlighting/fStringCommentsBefore312.py @@ -0,0 +1,3 @@ +f'''{[ + 42 # foo +]}''' diff --git a/python/testData/highlighting/fStringEmptyExpressions.py b/python/testData/highlighting/fStringEmptyExpressions.py index 43d1449b15d0..97ba02b4d34c 100644 --- a/python/testData/highlighting/fStringEmptyExpressions.py +++ b/python/testData/highlighting/fStringEmptyExpressions.py @@ -1,10 +1,7 @@ f'{}' -f'{' -f'{ f'{!r}' f'{:2.3}' f'{42:2.{}}' f'{ }' f'{42:{ }}' -f'{ :{ ' f'{ !r:{ :42}}' \ No newline at end of file diff --git a/python/testData/highlighting/fStringEqualitySignBefore312.py b/python/testData/highlighting/fStringEqualitySignBefore312.py new file mode 100644 index 000000000000..34ec4dcabdb3 --- /dev/null +++ b/python/testData/highlighting/fStringEqualitySignBefore312.py @@ -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'{"="}' \ No newline at end of file diff --git a/python/testData/highlighting/fStringHashSigns.py b/python/testData/highlighting/fStringHashSigns.py deleted file mode 100644 index 9d3fed7304e9..000000000000 --- a/python/testData/highlighting/fStringHashSigns.py +++ /dev/null @@ -1,10 +0,0 @@ -f'{#' -f'{# -f'{#foo#}' -f'{42:#}' -f'{42:{#}}' -f'{x ### foo}' -f'{"###"}' -f'''{[ - 42 # foo -]}''' diff --git a/python/testData/highlighting/fStringIllegalConversionCharacter.py b/python/testData/highlighting/fStringIllegalConversionCharacter.py index 010464c76fd4..3592be910031 100644 --- a/python/testData/highlighting/fStringIllegalConversionCharacter.py +++ b/python/testData/highlighting/fStringIllegalConversionCharacter.py @@ -5,5 +5,3 @@ f'{42!foo}' f'{42!}' f'{42!:2}' -f'{42!' -f'{42! diff --git a/python/testData/highlighting/fStringMissingRightBrace.py b/python/testData/highlighting/fStringMissingRightBrace.py index 6bd10dc83598..2c752e433307 100644 --- a/python/testData/highlighting/fStringMissingRightBrace.py +++ b/python/testData/highlighting/fStringMissingRightBrace.py @@ -1,10 +1 @@ -f'{42}' -f'{42!r}' -f'{42!r:03}' -f'{42:03}' -f'{42!r:{y}.{z}}' -f'{' -f'{42:{' -f'{42!r:{' -f'{{' -f'{{{' \ No newline at end of file +f'{' \ No newline at end of file diff --git a/python/testData/highlighting/fStringTerminatedByLineBreakInExpressionBefore312.py b/python/testData/highlighting/fStringTerminatedByLineBreakInExpressionBefore312.py new file mode 100644 index 000000000000..50b4b7d36821 --- /dev/null +++ b/python/testData/highlighting/fStringTerminatedByLineBreakInExpressionBefore312.py @@ -0,0 +1,2 @@ +s = f'{1 + +2}' \ No newline at end of file diff --git a/python/testData/highlighting/fStringTerminatedByLineBreakInExpressionInFormatPartBefore312.py b/python/testData/highlighting/fStringTerminatedByLineBreakInExpressionInFormatPartBefore312.py new file mode 100644 index 000000000000..ccf36c32ee43 --- /dev/null +++ b/python/testData/highlighting/fStringTerminatedByLineBreakInExpressionInFormatPartBefore312.py @@ -0,0 +1,2 @@ +s = f'{42:{1 + +2}}' \ No newline at end of file diff --git a/python/testData/highlighting/fStringTerminatedByLineBreakInNestedExpressionBefore312.py b/python/testData/highlighting/fStringTerminatedByLineBreakInNestedExpressionBefore312.py new file mode 100644 index 000000000000..92dce76bba84 --- /dev/null +++ b/python/testData/highlighting/fStringTerminatedByLineBreakInNestedExpressionBefore312.py @@ -0,0 +1,2 @@ +s = f"{f'{1 + +2}'}" \ No newline at end of file diff --git a/python/testData/highlighting/fStringTerminatedByLineBreakInNestedExpressionInFormatPartBefore312.py b/python/testData/highlighting/fStringTerminatedByLineBreakInNestedExpressionInFormatPartBefore312.py new file mode 100644 index 000000000000..531762b86213 --- /dev/null +++ b/python/testData/highlighting/fStringTerminatedByLineBreakInNestedExpressionInFormatPartBefore312.py @@ -0,0 +1,2 @@ +s = f"{f'{42:{1 + +2}}'}" \ No newline at end of file diff --git a/python/testData/highlighting/fStringTerminatedByLineBreakInNestedFormatPartBefore312.py b/python/testData/highlighting/fStringTerminatedByLineBreakInNestedFormatPartBefore312.py new file mode 100644 index 000000000000..0750c869be2e --- /dev/null +++ b/python/testData/highlighting/fStringTerminatedByLineBreakInNestedFormatPartBefore312.py @@ -0,0 +1,2 @@ +s = f"{f'foo{42:bar +baz}'}" \ No newline at end of file diff --git a/python/testData/highlighting/fStringTerminatedByLineBreakInStringLiteralBefore312.py b/python/testData/highlighting/fStringTerminatedByLineBreakInStringLiteralBefore312.py new file mode 100644 index 000000000000..b88ea7373e21 --- /dev/null +++ b/python/testData/highlighting/fStringTerminatedByLineBreakInStringLiteralBefore312.py @@ -0,0 +1,2 @@ +s = f'{""" +"""}' \ No newline at end of file diff --git a/python/testData/highlighting/fStringTerminatedByLineBreakInStringLiteralInFormatPartBefore312.py b/python/testData/highlighting/fStringTerminatedByLineBreakInStringLiteralInFormatPartBefore312.py new file mode 100644 index 000000000000..eae29796e309 --- /dev/null +++ b/python/testData/highlighting/fStringTerminatedByLineBreakInStringLiteralInFormatPartBefore312.py @@ -0,0 +1,2 @@ +s = f'{foo:{""" +"""}}' \ No newline at end of file diff --git a/python/testData/highlighting/fStringTerminatedByQuoteInNestedFormatPartBefore312.py b/python/testData/highlighting/fStringTerminatedByQuoteInNestedFormatPartBefore312.py new file mode 100644 index 000000000000..330c0ce0466b --- /dev/null +++ b/python/testData/highlighting/fStringTerminatedByQuoteInNestedFormatPartBefore312.py @@ -0,0 +1 @@ +s = f'{f"{42:'}"}' diff --git a/python/testData/highlighting/fStringTerminatedByQuoteInNestedLiteralPartBefore312.py b/python/testData/highlighting/fStringTerminatedByQuoteInNestedLiteralPartBefore312.py new file mode 100644 index 000000000000..c14022b43bf4 --- /dev/null +++ b/python/testData/highlighting/fStringTerminatedByQuoteInNestedLiteralPartBefore312.py @@ -0,0 +1 @@ +s = f'foo{f"baz'quux"}bar' \ No newline at end of file diff --git a/python/testData/highlighting/fStringTerminatedByQuoteInsideFStringLiteralBefore312.py b/python/testData/highlighting/fStringTerminatedByQuoteInsideFStringLiteralBefore312.py new file mode 100644 index 000000000000..f94ed0891855 --- /dev/null +++ b/python/testData/highlighting/fStringTerminatedByQuoteInsideFStringLiteralBefore312.py @@ -0,0 +1 @@ +s = f'foo{f"'"}baz' \ No newline at end of file diff --git a/python/testData/highlighting/fStringTerminatedByQuoteInsideFStringLiteralInFormatPartBefore312.py b/python/testData/highlighting/fStringTerminatedByQuoteInsideFStringLiteralInFormatPartBefore312.py new file mode 100644 index 000000000000..ecdeb317521e --- /dev/null +++ b/python/testData/highlighting/fStringTerminatedByQuoteInsideFStringLiteralInFormatPartBefore312.py @@ -0,0 +1 @@ +s = f'{42:{f"'"}}' \ No newline at end of file diff --git a/python/testData/highlighting/fStringTerminatedByQuoteInsideNestedFStringLiteralBefore312.py b/python/testData/highlighting/fStringTerminatedByQuoteInsideNestedFStringLiteralBefore312.py new file mode 100644 index 000000000000..44e919d6bdd7 --- /dev/null +++ b/python/testData/highlighting/fStringTerminatedByQuoteInsideNestedFStringLiteralBefore312.py @@ -0,0 +1 @@ +s = f'{f"""{f"'"}"""}' \ No newline at end of file diff --git a/python/testData/highlighting/fStringTerminatedByQuoteInsideNestedFStringLiteralInFormatPartBefore312.py b/python/testData/highlighting/fStringTerminatedByQuoteInsideNestedFStringLiteralInFormatPartBefore312.py new file mode 100644 index 000000000000..c5383519d156 --- /dev/null +++ b/python/testData/highlighting/fStringTerminatedByQuoteInsideNestedFStringLiteralInFormatPartBefore312.py @@ -0,0 +1 @@ +s = f'{f"""{42:{f"'"}}"""}' \ No newline at end of file diff --git a/python/testData/highlighting/fStringTerminatedByQuoteInsideNestedStringLiteralBefore312.py b/python/testData/highlighting/fStringTerminatedByQuoteInsideNestedStringLiteralBefore312.py new file mode 100644 index 000000000000..c319403be6f2 --- /dev/null +++ b/python/testData/highlighting/fStringTerminatedByQuoteInsideNestedStringLiteralBefore312.py @@ -0,0 +1 @@ +s = f'{f"""{"'"}"""}' \ No newline at end of file diff --git a/python/testData/highlighting/fStringTerminatedByQuoteInsideNestedStringLiteralInFormatPartBefore312.py b/python/testData/highlighting/fStringTerminatedByQuoteInsideNestedStringLiteralInFormatPartBefore312.py new file mode 100644 index 000000000000..996db55a5cfd --- /dev/null +++ b/python/testData/highlighting/fStringTerminatedByQuoteInsideNestedStringLiteralInFormatPartBefore312.py @@ -0,0 +1 @@ +s = f'{f"""{42:{"'"}}"""}' \ No newline at end of file diff --git a/python/testData/highlighting/fStringTerminatedByQuoteInsideStringLiteralBefore312.py b/python/testData/highlighting/fStringTerminatedByQuoteInsideStringLiteralBefore312.py new file mode 100644 index 000000000000..5afa8d286e0d --- /dev/null +++ b/python/testData/highlighting/fStringTerminatedByQuoteInsideStringLiteralBefore312.py @@ -0,0 +1 @@ +s = f'foo{"'"}baz' \ No newline at end of file diff --git a/python/testData/highlighting/fStringTerminatedByQuoteInsideStringLiteralInFormatPartBefore312.py b/python/testData/highlighting/fStringTerminatedByQuoteInsideStringLiteralInFormatPartBefore312.py new file mode 100644 index 000000000000..2673886e486f --- /dev/null +++ b/python/testData/highlighting/fStringTerminatedByQuoteInsideStringLiteralInFormatPartBefore312.py @@ -0,0 +1 @@ +s = f'{42:{"'"}}' \ No newline at end of file diff --git a/python/testData/highlighting/fStringTerminatedByQuoteOfFStringLiteralBefore312.py b/python/testData/highlighting/fStringTerminatedByQuoteOfFStringLiteralBefore312.py new file mode 100644 index 000000000000..654f45cdc28b --- /dev/null +++ b/python/testData/highlighting/fStringTerminatedByQuoteOfFStringLiteralBefore312.py @@ -0,0 +1 @@ +s = f'foo{f'bar'}baz' diff --git a/python/testData/highlighting/fStringTerminatedByQuoteOfFStringLiteralInFormatPartBefore312.py b/python/testData/highlighting/fStringTerminatedByQuoteOfFStringLiteralInFormatPartBefore312.py new file mode 100644 index 000000000000..3602f6d2a8fc --- /dev/null +++ b/python/testData/highlighting/fStringTerminatedByQuoteOfFStringLiteralInFormatPartBefore312.py @@ -0,0 +1 @@ +s = f'{42:{f'foo'}}' \ No newline at end of file diff --git a/python/testData/highlighting/fStringTerminatedByQuoteOfNestedFStringLiteralBefore312.py b/python/testData/highlighting/fStringTerminatedByQuoteOfNestedFStringLiteralBefore312.py new file mode 100644 index 000000000000..9527a465bc38 --- /dev/null +++ b/python/testData/highlighting/fStringTerminatedByQuoteOfNestedFStringLiteralBefore312.py @@ -0,0 +1 @@ +s = f'{f"{f'foo'}"}' \ No newline at end of file diff --git a/python/testData/highlighting/fStringTerminatedByQuoteOfNestedFStringLiteralInFormatPartBefore312.py b/python/testData/highlighting/fStringTerminatedByQuoteOfNestedFStringLiteralInFormatPartBefore312.py new file mode 100644 index 000000000000..de5ef3dc8974 --- /dev/null +++ b/python/testData/highlighting/fStringTerminatedByQuoteOfNestedFStringLiteralInFormatPartBefore312.py @@ -0,0 +1 @@ +s = f'{f"{42:{f'foo'}}"}' \ No newline at end of file diff --git a/python/testData/highlighting/fStringTerminatedByQuoteOfNestedStringLiteralBefore312.py b/python/testData/highlighting/fStringTerminatedByQuoteOfNestedStringLiteralBefore312.py new file mode 100644 index 000000000000..8fa989fa7c9d --- /dev/null +++ b/python/testData/highlighting/fStringTerminatedByQuoteOfNestedStringLiteralBefore312.py @@ -0,0 +1 @@ +s = f'{f"{'foo'}"}' \ No newline at end of file diff --git a/python/testData/highlighting/fStringTerminatedByQuoteOfNestedStringLiteralInFormatPartBefore312.py b/python/testData/highlighting/fStringTerminatedByQuoteOfNestedStringLiteralInFormatPartBefore312.py new file mode 100644 index 000000000000..bcc891f854f2 --- /dev/null +++ b/python/testData/highlighting/fStringTerminatedByQuoteOfNestedStringLiteralInFormatPartBefore312.py @@ -0,0 +1 @@ +s = f'{f"{42:{'foo'}}"}' \ No newline at end of file diff --git a/python/testData/highlighting/fStringTerminatedByQuoteOfStringLiteralBefore312.py b/python/testData/highlighting/fStringTerminatedByQuoteOfStringLiteralBefore312.py new file mode 100644 index 000000000000..682d93c5b916 --- /dev/null +++ b/python/testData/highlighting/fStringTerminatedByQuoteOfStringLiteralBefore312.py @@ -0,0 +1 @@ +s = f'foo{'bar'}baz' \ No newline at end of file diff --git a/python/testData/highlighting/fStringTerminatedByQuoteOfStringLiteralInFormatPartBefore312.py b/python/testData/highlighting/fStringTerminatedByQuoteOfStringLiteralInFormatPartBefore312.py new file mode 100644 index 000000000000..74ab20e6a2ea --- /dev/null +++ b/python/testData/highlighting/fStringTerminatedByQuoteOfStringLiteralInFormatPartBefore312.py @@ -0,0 +1 @@ +s = f'{42:{'foo'}}' \ No newline at end of file diff --git a/python/testData/highlighting/fStringTooDeeplyNestedExpressionFragments.py b/python/testData/highlighting/fStringTooDeeplyNestedExpressionFragments.py deleted file mode 100644 index b72a4a4af4a9..000000000000 --- a/python/testData/highlighting/fStringTooDeeplyNestedExpressionFragments.py +++ /dev/null @@ -1,7 +0,0 @@ -f'{x:{y:{}}}' -f'{x:{y:{# foo}}}' -f'{x:{y:{z!z}}}' -f'{x:{y:{z:{42}}}}' -f'{:{:{:{}}}}' -f'{x:{y:{z' -f'{x:{y:{z \ No newline at end of file diff --git a/python/testData/highlighting/fStringTooDeeplyNestedExpressionFragmentsBefore312.py b/python/testData/highlighting/fStringTooDeeplyNestedExpressionFragmentsBefore312.py new file mode 100644 index 000000000000..8c7f5f470ac0 --- /dev/null +++ b/python/testData/highlighting/fStringTooDeeplyNestedExpressionFragmentsBefore312.py @@ -0,0 +1,5 @@ +f'{x:{y:{}}}' +f'{x:{y:{z!z}}}' +f'{x:{y:{z:{42}}}}' +f'{:{:{:{}}}}' +f'{x:{y:{z \ No newline at end of file diff --git a/python/testData/highlighting/multilineFStringContainingMultilineExpressionAfterStatementBreakBefore312.py b/python/testData/highlighting/multilineFStringContainingMultilineExpressionAfterStatementBreakBefore312.py new file mode 100644 index 000000000000..730263e7d443 --- /dev/null +++ b/python/testData/highlighting/multilineFStringContainingMultilineExpressionAfterStatementBreakBefore312.py @@ -0,0 +1,3 @@ +pass +f'''{1 + +2}''' diff --git a/python/testData/highlighting/multilineFStringContainingMultilineExpressionBefore312.py b/python/testData/highlighting/multilineFStringContainingMultilineExpressionBefore312.py new file mode 100644 index 000000000000..920f3f79d180 --- /dev/null +++ b/python/testData/highlighting/multilineFStringContainingMultilineExpressionBefore312.py @@ -0,0 +1,2 @@ +s = f'''{1 + +2}''' \ No newline at end of file diff --git a/python/testData/highlighting/multilineFStringTerminatedByQuotesInsideParenthesizedExpressionBefore312.py b/python/testData/highlighting/multilineFStringTerminatedByQuotesInsideParenthesizedExpressionBefore312.py new file mode 100644 index 000000000000..36fc87f41e68 --- /dev/null +++ b/python/testData/highlighting/multilineFStringTerminatedByQuotesInsideParenthesizedExpressionBefore312.py @@ -0,0 +1 @@ +s = f'{ (lambda x: 'foo') }' \ No newline at end of file diff --git a/python/testData/highlighting/multilineFStringTerminatedByQuotesOfStringLiteralBefore312.py b/python/testData/highlighting/multilineFStringTerminatedByQuotesOfStringLiteralBefore312.py new file mode 100644 index 000000000000..915239855a17 --- /dev/null +++ b/python/testData/highlighting/multilineFStringTerminatedByQuotesOfStringLiteralBefore312.py @@ -0,0 +1,3 @@ +s = f'''{ + '''''' +}''' \ No newline at end of file diff --git a/python/testData/highlighting/nestedMultilineFStringsWithMultilineExpressionsBefore312.py b/python/testData/highlighting/nestedMultilineFStringsWithMultilineExpressionsBefore312.py new file mode 100644 index 000000000000..dabb5d897ee0 --- /dev/null +++ b/python/testData/highlighting/nestedMultilineFStringsWithMultilineExpressionsBefore312.py @@ -0,0 +1,5 @@ +s = f"""{f''' +{"bar" +} +''' +}""" \ No newline at end of file diff --git a/python/testData/highlighting/singleQuotedFStringInsideMultilineFStringTerminatedByLineBreakInExpressionBefore312.py b/python/testData/highlighting/singleQuotedFStringInsideMultilineFStringTerminatedByLineBreakInExpressionBefore312.py new file mode 100644 index 000000000000..223d563dbfa4 --- /dev/null +++ b/python/testData/highlighting/singleQuotedFStringInsideMultilineFStringTerminatedByLineBreakInExpressionBefore312.py @@ -0,0 +1,2 @@ +s = f"""{f'{1 + +2}'}""" \ No newline at end of file diff --git a/python/testData/highlighting/singleQuotedFStringInsideMultilineFStringTerminatedByLineBreakInExpressionInParenthesesBefore312.py b/python/testData/highlighting/singleQuotedFStringInsideMultilineFStringTerminatedByLineBreakInExpressionInParenthesesBefore312.py new file mode 100644 index 000000000000..fecfdc1116e2 --- /dev/null +++ b/python/testData/highlighting/singleQuotedFStringInsideMultilineFStringTerminatedByLineBreakInExpressionInParenthesesBefore312.py @@ -0,0 +1,2 @@ +s = f"""{f'{(1 + +2)}'}""" \ No newline at end of file diff --git a/python/testData/psi/FStringFragmentIncompleteTypeConversionBeforeClosingQuote.txt b/python/testData/psi/FStringFragmentIncompleteTypeConversionBeforeClosingQuote.txt index 4bbf05b51c76..ff9a0e0e79a2 100644 --- a/python/testData/psi/FStringFragmentIncompleteTypeConversionBeforeClosingQuote.txt +++ b/python/testData/psi/FStringFragmentIncompleteTypeConversionBeforeClosingQuote.txt @@ -13,6 +13,6 @@ PyFile:FStringFragmentIncompleteTypeConversionBeforeClosingQuote.py PyNumericLiteralExpression PsiElement(Py:INTEGER_LITERAL)('42') PsiElement(Py:FSTRING_FRAGMENT_TYPE_CONVERSION)('!') - PsiErrorElement:'}' expected + PsiErrorElement:: or '}' expected - PsiElement(Py:FSTRING_END)(''') \ No newline at end of file + PsiElement(Py:SINGLE_QUOTED_STRING)(''') \ No newline at end of file diff --git a/python/testData/psi/FStringIncompleteFragment.txt b/python/testData/psi/FStringIncompleteFragment.txt index c9b0774bb8af..8f0aa144473b 100644 --- a/python/testData/psi/FStringIncompleteFragment.txt +++ b/python/testData/psi/FStringIncompleteFragment.txt @@ -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 - PsiElement(Py:FSTRING_END)(''') \ No newline at end of file + PsiErrorElement:' expected + \ No newline at end of file diff --git a/python/testData/psi/FStringIncompleteFragmentWithTypeConversion.txt b/python/testData/psi/FStringIncompleteFragmentWithTypeConversion.txt index 020507dd927a..8b510ef3abd2 100644 --- a/python/testData/psi/FStringIncompleteFragmentWithTypeConversion.txt +++ b/python/testData/psi/FStringIncompleteFragmentWithTypeConversion.txt @@ -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 - PsiElement(Py:FSTRING_END)(''') \ No newline at end of file + PsiElement(Py:SINGLE_QUOTED_STRING)(''') \ No newline at end of file diff --git a/python/testData/psi/FStringTerminatedByLineBreakInExpression.py b/python/testData/psi/FStringNotTerminatedByLineBreakInExpression.py similarity index 100% rename from python/testData/psi/FStringTerminatedByLineBreakInExpression.py rename to python/testData/psi/FStringNotTerminatedByLineBreakInExpression.py diff --git a/python/testData/psi/FStringNotTerminatedByLineBreakInExpression.txt b/python/testData/psi/FStringNotTerminatedByLineBreakInExpression.txt new file mode 100644 index 000000000000..b446b6878bb9 --- /dev/null +++ b/python/testData/psi/FStringNotTerminatedByLineBreakInExpression.txt @@ -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)(''') \ No newline at end of file diff --git a/python/testData/psi/FStringTerminatedByLineBreakInExpressionInFormatPart.py b/python/testData/psi/FStringNotTerminatedByLineBreakInExpressionInFormatPart.py similarity index 100% rename from python/testData/psi/FStringTerminatedByLineBreakInExpressionInFormatPart.py rename to python/testData/psi/FStringNotTerminatedByLineBreakInExpressionInFormatPart.py diff --git a/python/testData/psi/FStringTerminatedByLineBreakInExpressionInFormatPart.txt b/python/testData/psi/FStringNotTerminatedByLineBreakInExpressionInFormatPart.txt similarity index 54% rename from python/testData/psi/FStringTerminatedByLineBreakInExpressionInFormatPart.txt rename to python/testData/psi/FStringNotTerminatedByLineBreakInExpressionInFormatPart.txt index b0e4d946c007..f5bb1406cbb4 100644 --- a/python/testData/psi/FStringTerminatedByLineBreakInExpressionInFormatPart.txt +++ b/python/testData/psi/FStringNotTerminatedByLineBreakInExpressionInFormatPart.txt @@ -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 - - PsiErrorElement:' expected - - PsiWhiteSpace('\n') - PyExpressionStatement - PyNumericLiteralExpression - PsiElement(Py:INTEGER_LITERAL)('2') - PsiErrorElement:End of statement expected - - PsiElement(Py:RBRACE)('}') - PsiErrorElement:Statement expected, found Py:RBRACE - - PsiElement(Py:RBRACE)('}') - PsiErrorElement:Statement expected, found Py:RBRACE - - PyExpressionStatement - PyStringLiteralExpression: - PsiElement(Py:SINGLE_QUOTED_STRING)(''') \ No newline at end of file + PsiWhiteSpace('\n') + PyNumericLiteralExpression + PsiElement(Py:INTEGER_LITERAL)('2') + PsiElement(Py:FSTRING_FRAGMENT_END)('}') + PsiElement(Py:FSTRING_FRAGMENT_END)('}') + PsiElement(Py:FSTRING_END)(''') \ No newline at end of file diff --git a/python/testData/psi/FStringTerminatedByLineBreakInNestedExpression.py b/python/testData/psi/FStringNotTerminatedByLineBreakInNestedExpression.py similarity index 100% rename from python/testData/psi/FStringTerminatedByLineBreakInNestedExpression.py rename to python/testData/psi/FStringNotTerminatedByLineBreakInNestedExpression.py diff --git a/python/testData/psi/FStringTerminatedByLineBreakInNestedExpression.txt b/python/testData/psi/FStringNotTerminatedByLineBreakInNestedExpression.txt similarity index 54% rename from python/testData/psi/FStringTerminatedByLineBreakInNestedExpression.txt rename to python/testData/psi/FStringNotTerminatedByLineBreakInNestedExpression.txt index 6c2495393c4b..00ed0074c543 100644 --- a/python/testData/psi/FStringTerminatedByLineBreakInNestedExpression.txt +++ b/python/testData/psi/FStringNotTerminatedByLineBreakInNestedExpression.txt @@ -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 - - PsiErrorElement:' expected - - PsiErrorElement:" expected - - PsiWhiteSpace('\n') - PyExpressionStatement - PyNumericLiteralExpression - PsiElement(Py:INTEGER_LITERAL)('2') - PsiErrorElement:End of statement expected - - PsiElement(Py:RBRACE)('}') - PsiErrorElement:Statement expected, found Py:RBRACE - - PyExpressionStatement - PyStringLiteralExpression: }" - PsiElement(Py:SINGLE_QUOTED_STRING)(''}"') \ No newline at end of file + 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)('"') \ No newline at end of file diff --git a/python/testData/psi/FStringTerminatedByLineBreakInNestedExpressionInFormatPart.py b/python/testData/psi/FStringNotTerminatedByLineBreakInNestedExpressionInFormatPart.py similarity index 100% rename from python/testData/psi/FStringTerminatedByLineBreakInNestedExpressionInFormatPart.py rename to python/testData/psi/FStringNotTerminatedByLineBreakInNestedExpressionInFormatPart.py diff --git a/python/testData/psi/FStringTerminatedByLineBreakInNestedExpressionInFormatPart.txt b/python/testData/psi/FStringNotTerminatedByLineBreakInNestedExpressionInFormatPart.txt similarity index 58% rename from python/testData/psi/FStringTerminatedByLineBreakInNestedExpressionInFormatPart.txt rename to python/testData/psi/FStringNotTerminatedByLineBreakInNestedExpressionInFormatPart.txt index 122774b49752..6fcabad617ea 100644 --- a/python/testData/psi/FStringTerminatedByLineBreakInNestedExpressionInFormatPart.txt +++ b/python/testData/psi/FStringNotTerminatedByLineBreakInNestedExpressionInFormatPart.txt @@ -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 - - PsiErrorElement:' expected - - PsiErrorElement:" expected - - PsiWhiteSpace('\n') - PyExpressionStatement - PyNumericLiteralExpression - PsiElement(Py:INTEGER_LITERAL)('2') - PsiErrorElement:End of statement expected - - PsiElement(Py:RBRACE)('}') - PsiErrorElement:Statement expected, found Py:RBRACE - - PsiElement(Py:RBRACE)('}') - PsiErrorElement:Statement expected, found Py:RBRACE - - PyExpressionStatement - PyStringLiteralExpression: }" - PsiElement(Py:SINGLE_QUOTED_STRING)(''}"') \ No newline at end of file + 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)('"') \ No newline at end of file diff --git a/python/testData/psi/FStringTerminatedByLineBreakInStringLiteral.py b/python/testData/psi/FStringNotTerminatedByLineBreakInStringLiteral.py similarity index 100% rename from python/testData/psi/FStringTerminatedByLineBreakInStringLiteral.py rename to python/testData/psi/FStringNotTerminatedByLineBreakInStringLiteral.py diff --git a/python/testData/psi/FStringTerminatedByLineBreakInStringLiteral.txt b/python/testData/psi/FStringNotTerminatedByLineBreakInStringLiteral.txt similarity index 50% rename from python/testData/psi/FStringTerminatedByLineBreakInStringLiteral.txt rename to python/testData/psi/FStringNotTerminatedByLineBreakInStringLiteral.txt index fd30b907d736..b5884552e6e5 100644 --- a/python/testData/psi/FStringTerminatedByLineBreakInStringLiteral.txt +++ b/python/testData/psi/FStringNotTerminatedByLineBreakInStringLiteral.txt @@ -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 - - PsiErrorElement:' expected - - PsiWhiteSpace('\n') - PyExpressionStatement - PyStringLiteralExpression: }' - PsiElement(Py:TRIPLE_QUOTED_STRING)('"""}'') \ No newline at end of file + + PsiElement(Py:TRIPLE_QUOTED_STRING)('"""\n"""') + PsiElement(Py:FSTRING_FRAGMENT_END)('}') + PsiElement(Py:FSTRING_END)(''') \ No newline at end of file diff --git a/python/testData/psi/FStringTerminatedByLineBreakInStringLiteralInFormatPart.py b/python/testData/psi/FStringNotTerminatedByLineBreakInStringLiteralInFormatPart.py similarity index 100% rename from python/testData/psi/FStringTerminatedByLineBreakInStringLiteralInFormatPart.py rename to python/testData/psi/FStringNotTerminatedByLineBreakInStringLiteralInFormatPart.py diff --git a/python/testData/psi/FStringTerminatedByLineBreakInStringLiteralInFormatPart.txt b/python/testData/psi/FStringNotTerminatedByLineBreakInStringLiteralInFormatPart.txt similarity index 61% rename from python/testData/psi/FStringTerminatedByLineBreakInStringLiteralInFormatPart.txt rename to python/testData/psi/FStringNotTerminatedByLineBreakInStringLiteralInFormatPart.txt index 838e035a6376..31244eb088a4 100644 --- a/python/testData/psi/FStringTerminatedByLineBreakInStringLiteralInFormatPart.txt +++ b/python/testData/psi/FStringNotTerminatedByLineBreakInStringLiteralInFormatPart.txt @@ -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 - - PsiErrorElement:' expected - - PsiWhiteSpace('\n') - PyExpressionStatement - PyStringLiteralExpression: }}' - PsiElement(Py:TRIPLE_QUOTED_STRING)('"""}}'') \ No newline at end of file + + PsiElement(Py:TRIPLE_QUOTED_STRING)('"""\n"""') + PsiElement(Py:FSTRING_FRAGMENT_END)('}') + PsiElement(Py:FSTRING_FRAGMENT_END)('}') + PsiElement(Py:FSTRING_END)(''') \ No newline at end of file diff --git a/python/testData/psi/FStringTerminatedByQuoteInNestedFormatPart.py b/python/testData/psi/FStringNotTerminatedByQuoteInNestedFormatPart.py similarity index 100% rename from python/testData/psi/FStringTerminatedByQuoteInNestedFormatPart.py rename to python/testData/psi/FStringNotTerminatedByQuoteInNestedFormatPart.py diff --git a/python/testData/psi/FStringTerminatedByQuoteInNestedFormatPart.txt b/python/testData/psi/FStringNotTerminatedByQuoteInNestedFormatPart.txt similarity index 54% rename from python/testData/psi/FStringTerminatedByQuoteInNestedFormatPart.txt rename to python/testData/psi/FStringNotTerminatedByQuoteInNestedFormatPart.txt index 651cede03a11..bf546793ccac 100644 --- a/python/testData/psi/FStringTerminatedByQuoteInNestedFormatPart.txt +++ b/python/testData/psi/FStringNotTerminatedByQuoteInNestedFormatPart.txt @@ -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 - - PsiErrorElement:" expected - - PsiElement(Py:FSTRING_END)(''') - PsiErrorElement:End of statement expected - - PsiElement(Py:RBRACE)('}') - PsiErrorElement:Statement expected, found Py:RBRACE - - PyExpressionStatement - PyStringLiteralExpression: }' - PsiElement(Py:SINGLE_QUOTED_STRING)('"}'') \ No newline at end of file + PsiElement(Py:FSTRING_TEXT)(''') + PsiElement(Py:FSTRING_FRAGMENT_END)('}') + PsiElement(Py:FSTRING_END)('"') + PsiElement(Py:FSTRING_FRAGMENT_END)('}') + PsiElement(Py:FSTRING_END)(''') \ No newline at end of file diff --git a/python/testData/psi/FStringTerminatedByQuoteInNestedLiteralPart.py b/python/testData/psi/FStringNotTerminatedByQuoteInNestedLiteralPart.py similarity index 100% rename from python/testData/psi/FStringTerminatedByQuoteInNestedLiteralPart.py rename to python/testData/psi/FStringNotTerminatedByQuoteInNestedLiteralPart.py diff --git a/python/testData/psi/FStringNotTerminatedByQuoteInNestedLiteralPart.txt b/python/testData/psi/FStringNotTerminatedByQuoteInNestedLiteralPart.txt new file mode 100644 index 000000000000..d007a8e32aff --- /dev/null +++ b/python/testData/psi/FStringNotTerminatedByQuoteInNestedLiteralPart.txt @@ -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)(''') \ No newline at end of file diff --git a/python/testData/psi/FStringTerminatedByQuoteInsideFStringLiteral.py b/python/testData/psi/FStringNotTerminatedByQuoteInsideFStringLiteral.py similarity index 100% rename from python/testData/psi/FStringTerminatedByQuoteInsideFStringLiteral.py rename to python/testData/psi/FStringNotTerminatedByQuoteInsideFStringLiteral.py diff --git a/python/testData/psi/FStringTerminatedByQuoteInsideFStringLiteral.txt b/python/testData/psi/FStringNotTerminatedByQuoteInsideFStringLiteral.txt similarity index 54% rename from python/testData/psi/FStringTerminatedByQuoteInsideFStringLiteral.txt rename to python/testData/psi/FStringNotTerminatedByQuoteInsideFStringLiteral.txt index 683276087197..d2a92cc69b1c 100644 --- a/python/testData/psi/FStringTerminatedByQuoteInsideFStringLiteral.txt +++ b/python/testData/psi/FStringNotTerminatedByQuoteInsideFStringLiteral.txt @@ -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 - - PsiErrorElement:'}' expected - - PsiElement(Py:FSTRING_END)(''') - PsiElement(Py:SINGLE_QUOTED_STRING)('"}baz'') \ No newline at end of file + PsiElement(Py:FSTRING_TEXT)(''') + PsiElement(Py:FSTRING_END)('"') + PsiElement(Py:FSTRING_FRAGMENT_END)('}') + PsiElement(Py:FSTRING_TEXT)('baz') + PsiElement(Py:FSTRING_END)(''') \ No newline at end of file diff --git a/python/testData/psi/FStringTerminatedByQuoteInsideFStringLiteralInFormatPart.py b/python/testData/psi/FStringNotTerminatedByQuoteInsideFStringLiteralInFormatPart.py similarity index 100% rename from python/testData/psi/FStringTerminatedByQuoteInsideFStringLiteralInFormatPart.py rename to python/testData/psi/FStringNotTerminatedByQuoteInsideFStringLiteralInFormatPart.py diff --git a/python/testData/psi/FStringTerminatedByQuoteInsideFStringLiteralInFormatPart.txt b/python/testData/psi/FStringNotTerminatedByQuoteInsideFStringLiteralInFormatPart.txt similarity index 62% rename from python/testData/psi/FStringTerminatedByQuoteInsideFStringLiteralInFormatPart.txt rename to python/testData/psi/FStringNotTerminatedByQuoteInsideFStringLiteralInFormatPart.txt index 204b6df8985b..dac7a811b281 100644 --- a/python/testData/psi/FStringTerminatedByQuoteInsideFStringLiteralInFormatPart.txt +++ b/python/testData/psi/FStringNotTerminatedByQuoteInsideFStringLiteralInFormatPart.txt @@ -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 - - PsiErrorElement:'}' expected - - PsiElement(Py:FSTRING_END)(''') - PsiElement(Py:SINGLE_QUOTED_STRING)('"}}'') \ No newline at end of file + PsiElement(Py:FSTRING_TEXT)(''') + PsiElement(Py:FSTRING_END)('"') + PsiElement(Py:FSTRING_FRAGMENT_END)('}') + PsiElement(Py:FSTRING_FRAGMENT_END)('}') + PsiElement(Py:FSTRING_END)(''') \ No newline at end of file diff --git a/python/testData/psi/FStringTerminatedByQuoteInsideNestedFStringLiteral.py b/python/testData/psi/FStringNotTerminatedByQuoteInsideNestedFStringLiteral.py similarity index 100% rename from python/testData/psi/FStringTerminatedByQuoteInsideNestedFStringLiteral.py rename to python/testData/psi/FStringNotTerminatedByQuoteInsideNestedFStringLiteral.py diff --git a/python/testData/psi/FStringNotTerminatedByQuoteInsideNestedFStringLiteral.txt b/python/testData/psi/FStringNotTerminatedByQuoteInsideNestedFStringLiteral.txt new file mode 100644 index 000000000000..1c092c290df8 --- /dev/null +++ b/python/testData/psi/FStringNotTerminatedByQuoteInsideNestedFStringLiteral.txt @@ -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)(''') \ No newline at end of file diff --git a/python/testData/psi/FStringTerminatedByQuoteInsideNestedFStringLiteralInFormatPart.py b/python/testData/psi/FStringNotTerminatedByQuoteInsideNestedFStringLiteralInFormatPart.py similarity index 100% rename from python/testData/psi/FStringTerminatedByQuoteInsideNestedFStringLiteralInFormatPart.py rename to python/testData/psi/FStringNotTerminatedByQuoteInsideNestedFStringLiteralInFormatPart.py diff --git a/python/testData/psi/FStringTerminatedByQuoteInsideNestedFStringLiteralInFormatPart.txt b/python/testData/psi/FStringNotTerminatedByQuoteInsideNestedFStringLiteralInFormatPart.txt similarity index 51% rename from python/testData/psi/FStringTerminatedByQuoteInsideNestedFStringLiteralInFormatPart.txt rename to python/testData/psi/FStringNotTerminatedByQuoteInsideNestedFStringLiteralInFormatPart.txt index de0aab8678f5..951388cf1465 100644 --- a/python/testData/psi/FStringTerminatedByQuoteInsideNestedFStringLiteralInFormatPart.txt +++ b/python/testData/psi/FStringNotTerminatedByQuoteInsideNestedFStringLiteralInFormatPart.txt @@ -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 - - PsiErrorElement:'}' expected - - PsiErrorElement:""" expected - - PsiElement(Py:FSTRING_END)(''') - PsiElement(Py:SINGLE_QUOTED_STRING)('"}}"') - PsiElement(Py:SINGLE_QUOTED_STRING)('""') - PsiErrorElement:End of statement expected - - PsiElement(Py:RBRACE)('}') - PsiErrorElement:Statement expected, found Py:RBRACE - - PyExpressionStatement - PyStringLiteralExpression: - PsiElement(Py:SINGLE_QUOTED_STRING)(''') \ No newline at end of file + 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)(''') \ No newline at end of file diff --git a/python/testData/psi/FStringTerminatedByQuoteInsideNestedStringLiteral.py b/python/testData/psi/FStringNotTerminatedByQuoteInsideNestedStringLiteral.py similarity index 100% rename from python/testData/psi/FStringTerminatedByQuoteInsideNestedStringLiteral.py rename to python/testData/psi/FStringNotTerminatedByQuoteInsideNestedStringLiteral.py diff --git a/python/testData/psi/FStringNotTerminatedByQuoteInsideNestedStringLiteral.txt b/python/testData/psi/FStringNotTerminatedByQuoteInsideNestedStringLiteral.txt new file mode 100644 index 000000000000..4db1bbc8d423 --- /dev/null +++ b/python/testData/psi/FStringNotTerminatedByQuoteInsideNestedStringLiteral.txt @@ -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)(''') \ No newline at end of file diff --git a/python/testData/psi/FStringTerminatedByQuoteInsideNestedStringLiteralInFormatPart.py b/python/testData/psi/FStringNotTerminatedByQuoteInsideNestedStringLiteralInFormatPart.py similarity index 100% rename from python/testData/psi/FStringTerminatedByQuoteInsideNestedStringLiteralInFormatPart.py rename to python/testData/psi/FStringNotTerminatedByQuoteInsideNestedStringLiteralInFormatPart.py diff --git a/python/testData/psi/FStringNotTerminatedByQuoteInsideNestedStringLiteralInFormatPart.txt b/python/testData/psi/FStringNotTerminatedByQuoteInsideNestedStringLiteralInFormatPart.txt new file mode 100644 index 000000000000..6ef923cdd75c --- /dev/null +++ b/python/testData/psi/FStringNotTerminatedByQuoteInsideNestedStringLiteralInFormatPart.txt @@ -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)(''') \ No newline at end of file diff --git a/python/testData/psi/FStringTerminatedByQuoteInsideStringLiteral.py b/python/testData/psi/FStringNotTerminatedByQuoteInsideStringLiteral.py similarity index 100% rename from python/testData/psi/FStringTerminatedByQuoteInsideStringLiteral.py rename to python/testData/psi/FStringNotTerminatedByQuoteInsideStringLiteral.py diff --git a/python/testData/psi/FStringTerminatedByQuoteInsideStringLiteral.txt b/python/testData/psi/FStringNotTerminatedByQuoteInsideStringLiteral.txt similarity index 51% rename from python/testData/psi/FStringTerminatedByQuoteInsideStringLiteral.txt rename to python/testData/psi/FStringNotTerminatedByQuoteInsideStringLiteral.txt index 1d3ef5b161af..3888dbd1a2a9 100644 --- a/python/testData/psi/FStringTerminatedByQuoteInsideStringLiteral.txt +++ b/python/testData/psi/FStringNotTerminatedByQuoteInsideStringLiteral.txt @@ -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 - - PsiElement(Py:FSTRING_END)(''') - PsiElement(Py:SINGLE_QUOTED_STRING)('"}baz'') \ No newline at end of file + PyStringLiteralExpression: ' + PsiElement(Py:SINGLE_QUOTED_STRING)('"'"') + PsiElement(Py:FSTRING_FRAGMENT_END)('}') + PsiElement(Py:FSTRING_TEXT)('baz') + PsiElement(Py:FSTRING_END)(''') \ No newline at end of file diff --git a/python/testData/psi/FStringTerminatedByQuoteInsideStringLiteralInFormatPart.py b/python/testData/psi/FStringNotTerminatedByQuoteInsideStringLiteralInFormatPart.py similarity index 100% rename from python/testData/psi/FStringTerminatedByQuoteInsideStringLiteralInFormatPart.py rename to python/testData/psi/FStringNotTerminatedByQuoteInsideStringLiteralInFormatPart.py diff --git a/python/testData/psi/FStringTerminatedByQuoteInsideStringLiteralInFormatPart.txt b/python/testData/psi/FStringNotTerminatedByQuoteInsideStringLiteralInFormatPart.txt similarity index 61% rename from python/testData/psi/FStringTerminatedByQuoteInsideStringLiteralInFormatPart.txt rename to python/testData/psi/FStringNotTerminatedByQuoteInsideStringLiteralInFormatPart.txt index 11f89b8948e4..2f3a41f68b0c 100644 --- a/python/testData/psi/FStringTerminatedByQuoteInsideStringLiteralInFormatPart.txt +++ b/python/testData/psi/FStringNotTerminatedByQuoteInsideStringLiteralInFormatPart.txt @@ -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 - - PsiElement(Py:FSTRING_END)(''') - PsiElement(Py:SINGLE_QUOTED_STRING)('"}}'') \ No newline at end of file + PyStringLiteralExpression: ' + PsiElement(Py:SINGLE_QUOTED_STRING)('"'"') + PsiElement(Py:FSTRING_FRAGMENT_END)('}') + PsiElement(Py:FSTRING_FRAGMENT_END)('}') + PsiElement(Py:FSTRING_END)(''') \ No newline at end of file diff --git a/python/testData/psi/FStringTerminatedByQuoteOfFStringLiteral.py b/python/testData/psi/FStringNotTerminatedByQuoteOfFStringLiteral.py similarity index 100% rename from python/testData/psi/FStringTerminatedByQuoteOfFStringLiteral.py rename to python/testData/psi/FStringNotTerminatedByQuoteOfFStringLiteral.py diff --git a/python/testData/psi/FStringNotTerminatedByQuoteOfFStringLiteral.txt b/python/testData/psi/FStringNotTerminatedByQuoteOfFStringLiteral.txt new file mode 100644 index 000000000000..cc0b7dc3c6a5 --- /dev/null +++ b/python/testData/psi/FStringNotTerminatedByQuoteOfFStringLiteral.txt @@ -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)(''') \ No newline at end of file diff --git a/python/testData/psi/FStringTerminatedByQuoteOfFStringLiteralInFormatPart.py b/python/testData/psi/FStringNotTerminatedByQuoteOfFStringLiteralInFormatPart.py similarity index 100% rename from python/testData/psi/FStringTerminatedByQuoteOfFStringLiteralInFormatPart.py rename to python/testData/psi/FStringNotTerminatedByQuoteOfFStringLiteralInFormatPart.py diff --git a/python/testData/psi/FStringNotTerminatedByQuoteOfFStringLiteralInFormatPart.txt b/python/testData/psi/FStringNotTerminatedByQuoteOfFStringLiteralInFormatPart.txt new file mode 100644 index 000000000000..1711d936d621 --- /dev/null +++ b/python/testData/psi/FStringNotTerminatedByQuoteOfFStringLiteralInFormatPart.txt @@ -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)(''') \ No newline at end of file diff --git a/python/testData/psi/FStringTerminatedByQuoteOfNestedFStringLiteral.py b/python/testData/psi/FStringNotTerminatedByQuoteOfNestedFStringLiteral.py similarity index 100% rename from python/testData/psi/FStringTerminatedByQuoteOfNestedFStringLiteral.py rename to python/testData/psi/FStringNotTerminatedByQuoteOfNestedFStringLiteral.py diff --git a/python/testData/psi/FStringNotTerminatedByQuoteOfNestedFStringLiteral.txt b/python/testData/psi/FStringNotTerminatedByQuoteOfNestedFStringLiteral.txt new file mode 100644 index 000000000000..e70dffe3f425 --- /dev/null +++ b/python/testData/psi/FStringNotTerminatedByQuoteOfNestedFStringLiteral.txt @@ -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)(''') \ No newline at end of file diff --git a/python/testData/psi/FStringTerminatedByQuoteOfNestedFStringLiteralInFormatPart.py b/python/testData/psi/FStringNotTerminatedByQuoteOfNestedFStringLiteralInFormatPart.py similarity index 100% rename from python/testData/psi/FStringTerminatedByQuoteOfNestedFStringLiteralInFormatPart.py rename to python/testData/psi/FStringNotTerminatedByQuoteOfNestedFStringLiteralInFormatPart.py diff --git a/python/testData/psi/FStringTerminatedByQuoteOfNestedStringLiteralInFormatPart.txt b/python/testData/psi/FStringNotTerminatedByQuoteOfNestedFStringLiteralInFormatPart.txt similarity index 50% rename from python/testData/psi/FStringTerminatedByQuoteOfNestedStringLiteralInFormatPart.txt rename to python/testData/psi/FStringNotTerminatedByQuoteOfNestedFStringLiteralInFormatPart.txt index 2fe870bdae2d..dcb6e6266a46 100644 --- a/python/testData/psi/FStringTerminatedByQuoteOfNestedStringLiteralInFormatPart.txt +++ b/python/testData/psi/FStringNotTerminatedByQuoteOfNestedFStringLiteralInFormatPart.txt @@ -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 - - PsiErrorElement:'}' expected - - PsiErrorElement:" expected - - PsiElement(Py:FSTRING_END)(''') - PsiErrorElement:End of statement expected - - PyExpressionStatement - PyReferenceExpression: foo - PsiElement(Py:IDENTIFIER)('foo') - PsiErrorElement:End of statement expected - - PyExpressionStatement - PyStringLiteralExpression: }}"} - PsiElement(Py:SINGLE_QUOTED_STRING)(''}}"}'') \ No newline at end of file + 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)(''') \ No newline at end of file diff --git a/python/testData/psi/FStringTerminatedByQuoteOfNestedStringLiteral.py b/python/testData/psi/FStringNotTerminatedByQuoteOfNestedStringLiteral.py similarity index 100% rename from python/testData/psi/FStringTerminatedByQuoteOfNestedStringLiteral.py rename to python/testData/psi/FStringNotTerminatedByQuoteOfNestedStringLiteral.py diff --git a/python/testData/psi/FStringNotTerminatedByQuoteOfNestedStringLiteral.txt b/python/testData/psi/FStringNotTerminatedByQuoteOfNestedStringLiteral.txt new file mode 100644 index 000000000000..32f7e649c5cb --- /dev/null +++ b/python/testData/psi/FStringNotTerminatedByQuoteOfNestedStringLiteral.txt @@ -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)(''') \ No newline at end of file diff --git a/python/testData/psi/FStringTerminatedByQuoteOfNestedStringLiteralInFormatPart.py b/python/testData/psi/FStringNotTerminatedByQuoteOfNestedStringLiteralInFormatPart.py similarity index 100% rename from python/testData/psi/FStringTerminatedByQuoteOfNestedStringLiteralInFormatPart.py rename to python/testData/psi/FStringNotTerminatedByQuoteOfNestedStringLiteralInFormatPart.py diff --git a/python/testData/psi/FStringTerminatedByQuoteOfNestedFStringLiteralInFormatPart.txt b/python/testData/psi/FStringNotTerminatedByQuoteOfNestedStringLiteralInFormatPart.txt similarity index 50% rename from python/testData/psi/FStringTerminatedByQuoteOfNestedFStringLiteralInFormatPart.txt rename to python/testData/psi/FStringNotTerminatedByQuoteOfNestedStringLiteralInFormatPart.txt index 127c7b375298..1d2697de97d5 100644 --- a/python/testData/psi/FStringTerminatedByQuoteOfNestedFStringLiteralInFormatPart.txt +++ b/python/testData/psi/FStringNotTerminatedByQuoteOfNestedStringLiteralInFormatPart.txt @@ -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 - - PsiErrorElement:" expected - - PsiElement(Py:FSTRING_END)(''') - PsiErrorElement:End of statement expected - - PyExpressionStatement - PyReferenceExpression: foo - PsiElement(Py:IDENTIFIER)('foo') - PsiErrorElement:End of statement expected - - PyExpressionStatement - PyStringLiteralExpression: }}"} - PsiElement(Py:SINGLE_QUOTED_STRING)(''}}"}'') \ No newline at end of file + 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)(''') \ No newline at end of file diff --git a/python/testData/psi/FStringTerminatedByQuoteOfStringLiteral.py b/python/testData/psi/FStringNotTerminatedByQuoteOfStringLiteral.py similarity index 100% rename from python/testData/psi/FStringTerminatedByQuoteOfStringLiteral.py rename to python/testData/psi/FStringNotTerminatedByQuoteOfStringLiteral.py diff --git a/python/testData/psi/FStringNotTerminatedByQuoteOfStringLiteral.txt b/python/testData/psi/FStringNotTerminatedByQuoteOfStringLiteral.txt new file mode 100644 index 000000000000..dcac914fa388 --- /dev/null +++ b/python/testData/psi/FStringNotTerminatedByQuoteOfStringLiteral.txt @@ -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)(''') \ No newline at end of file diff --git a/python/testData/psi/FStringTerminatedByQuoteOfStringLiteralInFormatPart.py b/python/testData/psi/FStringNotTerminatedByQuoteOfStringLiteralInFormatPart.py similarity index 100% rename from python/testData/psi/FStringTerminatedByQuoteOfStringLiteralInFormatPart.py rename to python/testData/psi/FStringNotTerminatedByQuoteOfStringLiteralInFormatPart.py diff --git a/python/testData/psi/FStringNotTerminatedByQuoteOfStringLiteralInFormatPart.txt b/python/testData/psi/FStringNotTerminatedByQuoteOfStringLiteralInFormatPart.txt new file mode 100644 index 000000000000..5389c8555095 --- /dev/null +++ b/python/testData/psi/FStringNotTerminatedByQuoteOfStringLiteralInFormatPart.txt @@ -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)(''') \ No newline at end of file diff --git a/python/testData/psi/MultilineFStringTerminatedByQuotesInsideParenthesizedExpression.py b/python/testData/psi/FStringNotTerminatedByQuotesInsideParenthesizedExpression.py similarity index 100% rename from python/testData/psi/MultilineFStringTerminatedByQuotesInsideParenthesizedExpression.py rename to python/testData/psi/FStringNotTerminatedByQuotesInsideParenthesizedExpression.py diff --git a/python/testData/psi/MultilineFStringTerminatedByQuotesInsideParenthesizedExpression.txt b/python/testData/psi/FStringNotTerminatedByQuotesInsideParenthesizedExpression.txt similarity index 55% rename from python/testData/psi/MultilineFStringTerminatedByQuotesInsideParenthesizedExpression.txt rename to python/testData/psi/FStringNotTerminatedByQuotesInsideParenthesizedExpression.txt index 3881a8e41391..ee210f917be3 100644 --- a/python/testData/psi/MultilineFStringTerminatedByQuotesInsideParenthesizedExpression.txt +++ b/python/testData/psi/FStringNotTerminatedByQuotesInsideParenthesizedExpression.txt @@ -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 - + PsiWhiteSpace(' ') + PyStringLiteralExpression: foo + PsiElement(Py:SINGLE_QUOTED_STRING)(''foo'') + PsiElement(Py:RPAR)(')') PsiWhiteSpace(' ') - PsiElement(Py:FSTRING_END)(''') - PsiErrorElement:End of statement expected - - PyExpressionStatement - PyReferenceExpression: foo - PsiElement(Py:IDENTIFIER)('foo') - PsiErrorElement:End of statement expected - - PyExpressionStatement - PyStringLiteralExpression: ) } - PsiElement(Py:SINGLE_QUOTED_STRING)('') }'') \ No newline at end of file + PsiElement(Py:FSTRING_FRAGMENT_END)('}') + PsiElement(Py:FSTRING_END)(''') \ No newline at end of file diff --git a/python/testData/psi/FStringTerminatedByLineBreakInExpression.txt b/python/testData/psi/FStringTerminatedByLineBreakInExpression.txt deleted file mode 100644 index a123c79a32ee..000000000000 --- a/python/testData/psi/FStringTerminatedByLineBreakInExpression.txt +++ /dev/null @@ -1,33 +0,0 @@ -PyFile:FStringTerminatedByLineBreakInExpression.py - PyAssignmentStatement - PyTargetExpression: s - PsiElement(Py:IDENTIFIER)('s') - PsiWhiteSpace(' ') - PsiElement(Py:EQ)('=') - PsiWhiteSpace(' ') - PyStringLiteralExpression: {1 + - PyFormattedStringElement - 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 - - PsiErrorElement:' expected - - PsiWhiteSpace('\n') - PyExpressionStatement - PyNumericLiteralExpression - PsiElement(Py:INTEGER_LITERAL)('2') - PsiErrorElement:End of statement expected - - PsiElement(Py:RBRACE)('}') - PsiErrorElement:Statement expected, found Py:RBRACE - - PyExpressionStatement - PyStringLiteralExpression: - PsiElement(Py:SINGLE_QUOTED_STRING)(''') \ No newline at end of file diff --git a/python/testData/psi/FStringTerminatedByQuoteInNestedLiteralPart.txt b/python/testData/psi/FStringTerminatedByQuoteInNestedLiteralPart.txt deleted file mode 100644 index 382cb53b306f..000000000000 --- a/python/testData/psi/FStringTerminatedByQuoteInNestedLiteralPart.txt +++ /dev/null @@ -1,32 +0,0 @@ -PyFile:FStringTerminatedByQuoteInNestedLiteralPart.py - PyAssignmentStatement - PyTargetExpression: s - PsiElement(Py:IDENTIFIER)('s') - PsiWhiteSpace(' ') - PsiElement(Py:EQ)('=') - PsiWhiteSpace(' ') - PyStringLiteralExpression: foo{f"baz - PyFormattedStringElement - PsiElement(Py:FSTRING_START)('f'') - PsiElement(Py:FSTRING_TEXT)('foo') - PyFStringFragment - PsiElement(Py:FSTRING_FRAGMENT_START)('{') - PyStringLiteralExpression: baz - PyFormattedStringElement - PsiElement(Py:FSTRING_START)('f"') - PsiElement(Py:FSTRING_TEXT)('baz') - PsiErrorElement:" expected - - PsiErrorElement:'}' expected - - PsiElement(Py:FSTRING_END)(''') - PsiErrorElement:End of statement expected - - PyExpressionStatement - PyReferenceExpression: quux - PsiElement(Py:IDENTIFIER)('quux') - PsiErrorElement:End of statement expected - - PyExpressionStatement - PyStringLiteralExpression: }bar' - PsiElement(Py:SINGLE_QUOTED_STRING)('"}bar'') \ No newline at end of file diff --git a/python/testData/psi/FStringTerminatedByQuoteInsideNestedFStringLiteral.txt b/python/testData/psi/FStringTerminatedByQuoteInsideNestedFStringLiteral.txt deleted file mode 100644 index fb4f93af1345..000000000000 --- a/python/testData/psi/FStringTerminatedByQuoteInsideNestedFStringLiteral.txt +++ /dev/null @@ -1,37 +0,0 @@ -PyFile:FStringTerminatedByQuoteInsideNestedFStringLiteral.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"') - PsiErrorElement:" expected - - PsiErrorElement:'}' expected - - PsiErrorElement:""" expected - - PsiElement(Py:FSTRING_END)(''') - PsiElement(Py:SINGLE_QUOTED_STRING)('"}"') - PsiElement(Py:SINGLE_QUOTED_STRING)('""') - PsiErrorElement:End of statement expected - - PsiElement(Py:RBRACE)('}') - PsiErrorElement:Statement expected, found Py:RBRACE - - PyExpressionStatement - PyStringLiteralExpression: - PsiElement(Py:SINGLE_QUOTED_STRING)(''') \ No newline at end of file diff --git a/python/testData/psi/FStringTerminatedByQuoteInsideNestedStringLiteral.txt b/python/testData/psi/FStringTerminatedByQuoteInsideNestedStringLiteral.txt deleted file mode 100644 index e548547b67da..000000000000 --- a/python/testData/psi/FStringTerminatedByQuoteInsideNestedStringLiteral.txt +++ /dev/null @@ -1,34 +0,0 @@ -PyFile:FStringTerminatedByQuoteInsideNestedStringLiteral.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)('"') - PsiErrorElement:'}' expected - - PsiErrorElement:""" expected - - PsiElement(Py:FSTRING_END)(''') - PsiElement(Py:SINGLE_QUOTED_STRING)('"}"') - PsiElement(Py:SINGLE_QUOTED_STRING)('""') - PsiErrorElement:End of statement expected - - PsiElement(Py:RBRACE)('}') - PsiErrorElement:Statement expected, found Py:RBRACE - - PyExpressionStatement - PyStringLiteralExpression: - PsiElement(Py:SINGLE_QUOTED_STRING)(''') \ No newline at end of file diff --git a/python/testData/psi/FStringTerminatedByQuoteInsideNestedStringLiteralInFormatPart.txt b/python/testData/psi/FStringTerminatedByQuoteInsideNestedStringLiteralInFormatPart.txt deleted file mode 100644 index b5dfca23a383..000000000000 --- a/python/testData/psi/FStringTerminatedByQuoteInsideNestedStringLiteralInFormatPart.txt +++ /dev/null @@ -1,40 +0,0 @@ -PyFile:FStringTerminatedByQuoteInsideNestedStringLiteralInFormatPart.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)('"') - PsiErrorElement:'}' expected - - PsiErrorElement:""" expected - - PsiElement(Py:FSTRING_END)(''') - PsiElement(Py:SINGLE_QUOTED_STRING)('"}}"') - PsiElement(Py:SINGLE_QUOTED_STRING)('""') - PsiErrorElement:End of statement expected - - PsiElement(Py:RBRACE)('}') - PsiErrorElement:Statement expected, found Py:RBRACE - - PyExpressionStatement - PyStringLiteralExpression: - PsiElement(Py:SINGLE_QUOTED_STRING)(''') \ No newline at end of file diff --git a/python/testData/psi/FStringTerminatedByQuoteOfFStringLiteral.txt b/python/testData/psi/FStringTerminatedByQuoteOfFStringLiteral.txt deleted file mode 100644 index f70e1a90d208..000000000000 --- a/python/testData/psi/FStringTerminatedByQuoteOfFStringLiteral.txt +++ /dev/null @@ -1,28 +0,0 @@ -PyFile:FStringTerminatedByQuoteOfFStringLiteral.py - PyAssignmentStatement - PyTargetExpression: s - PsiElement(Py:IDENTIFIER)('s') - PsiWhiteSpace(' ') - PsiElement(Py:EQ)('=') - PsiWhiteSpace(' ') - PyStringLiteralExpression: foo{f - PyFormattedStringElement - PsiElement(Py:FSTRING_START)('f'') - PsiElement(Py:FSTRING_TEXT)('foo') - PyFStringFragment - PsiElement(Py:FSTRING_FRAGMENT_START)('{') - PyReferenceExpression: f - PsiElement(Py:IDENTIFIER)('f') - PsiErrorElement:'}' expected - - PsiElement(Py:FSTRING_END)(''') - PsiErrorElement:End of statement expected - - PyExpressionStatement - PyReferenceExpression: bar - PsiElement(Py:IDENTIFIER)('bar') - PsiErrorElement:End of statement expected - - PyExpressionStatement - PyStringLiteralExpression: }baz - PsiElement(Py:SINGLE_QUOTED_STRING)(''}baz'') \ No newline at end of file diff --git a/python/testData/psi/FStringTerminatedByQuoteOfFStringLiteralInFormatPart.txt b/python/testData/psi/FStringTerminatedByQuoteOfFStringLiteralInFormatPart.txt deleted file mode 100644 index ae88f4d8edbf..000000000000 --- a/python/testData/psi/FStringTerminatedByQuoteOfFStringLiteralInFormatPart.txt +++ /dev/null @@ -1,33 +0,0 @@ -PyFile:FStringTerminatedByQuoteOfFStringLiteralInFormatPart.py - PyAssignmentStatement - PyTargetExpression: s - PsiElement(Py:IDENTIFIER)('s') - PsiWhiteSpace(' ') - PsiElement(Py:EQ)('=') - PsiWhiteSpace(' ') - PyStringLiteralExpression: {42:{f - 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)('{') - PyReferenceExpression: f - PsiElement(Py:IDENTIFIER)('f') - PsiErrorElement:'}' expected - - PsiElement(Py:FSTRING_END)(''') - PsiErrorElement:End of statement expected - - PyExpressionStatement - PyReferenceExpression: foo - PsiElement(Py:IDENTIFIER)('foo') - PsiErrorElement:End of statement expected - - PyExpressionStatement - PyStringLiteralExpression: }} - PsiElement(Py:SINGLE_QUOTED_STRING)(''}}'') \ No newline at end of file diff --git a/python/testData/psi/FStringTerminatedByQuoteOfNestedFStringLiteral.txt b/python/testData/psi/FStringTerminatedByQuoteOfNestedFStringLiteral.txt deleted file mode 100644 index 19233ed7c379..000000000000 --- a/python/testData/psi/FStringTerminatedByQuoteOfNestedFStringLiteral.txt +++ /dev/null @@ -1,34 +0,0 @@ -PyFile:FStringTerminatedByQuoteOfNestedFStringLiteral.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)('{') - PyReferenceExpression: f - PsiElement(Py:IDENTIFIER)('f') - PsiErrorElement:'}' expected - - PsiErrorElement:" expected - - PsiElement(Py:FSTRING_END)(''') - PsiErrorElement:End of statement expected - - PyExpressionStatement - PyReferenceExpression: foo - PsiElement(Py:IDENTIFIER)('foo') - PsiErrorElement:End of statement expected - - PyExpressionStatement - PyStringLiteralExpression: }"} - PsiElement(Py:SINGLE_QUOTED_STRING)(''}"}'') \ No newline at end of file diff --git a/python/testData/psi/FStringTerminatedByQuoteOfNestedStringLiteral.txt b/python/testData/psi/FStringTerminatedByQuoteOfNestedStringLiteral.txt deleted file mode 100644 index 2a74814881de..000000000000 --- a/python/testData/psi/FStringTerminatedByQuoteOfNestedStringLiteral.txt +++ /dev/null @@ -1,34 +0,0 @@ -PyFile:FStringTerminatedByQuoteOfNestedStringLiteral.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)('{') - PsiErrorElement:Expression expected - - PsiErrorElement:'}' expected - - PsiErrorElement:" expected - - PsiElement(Py:FSTRING_END)(''') - PsiErrorElement:End of statement expected - - PyExpressionStatement - PyReferenceExpression: foo - PsiElement(Py:IDENTIFIER)('foo') - PsiErrorElement:End of statement expected - - PyExpressionStatement - PyStringLiteralExpression: }"} - PsiElement(Py:SINGLE_QUOTED_STRING)(''}"}'') \ No newline at end of file diff --git a/python/testData/psi/FStringTerminatedByQuoteOfStringLiteral.txt b/python/testData/psi/FStringTerminatedByQuoteOfStringLiteral.txt deleted file mode 100644 index 5e1fd681be94..000000000000 --- a/python/testData/psi/FStringTerminatedByQuoteOfStringLiteral.txt +++ /dev/null @@ -1,28 +0,0 @@ -PyFile:FStringTerminatedByQuoteOfStringLiteral.py - PyAssignmentStatement - PyTargetExpression: s - PsiElement(Py:IDENTIFIER)('s') - PsiWhiteSpace(' ') - PsiElement(Py:EQ)('=') - PsiWhiteSpace(' ') - PyStringLiteralExpression: foo{ - PyFormattedStringElement - PsiElement(Py:FSTRING_START)('f'') - PsiElement(Py:FSTRING_TEXT)('foo') - PyFStringFragment - PsiElement(Py:FSTRING_FRAGMENT_START)('{') - PsiErrorElement:Expression expected - - PsiErrorElement:'}' expected - - PsiElement(Py:FSTRING_END)(''') - PsiErrorElement:End of statement expected - - PyExpressionStatement - PyReferenceExpression: bar - PsiElement(Py:IDENTIFIER)('bar') - PsiErrorElement:End of statement expected - - PyExpressionStatement - PyStringLiteralExpression: }baz - PsiElement(Py:SINGLE_QUOTED_STRING)(''}baz'') \ No newline at end of file diff --git a/python/testData/psi/FStringTerminatedByQuoteOfStringLiteralInFormatPart.txt b/python/testData/psi/FStringTerminatedByQuoteOfStringLiteralInFormatPart.txt deleted file mode 100644 index 272c6a4fa425..000000000000 --- a/python/testData/psi/FStringTerminatedByQuoteOfStringLiteralInFormatPart.txt +++ /dev/null @@ -1,33 +0,0 @@ -PyFile:FStringTerminatedByQuoteOfStringLiteralInFormatPart.py - PyAssignmentStatement - PyTargetExpression: s - PsiElement(Py:IDENTIFIER)('s') - PsiWhiteSpace(' ') - PsiElement(Py:EQ)('=') - PsiWhiteSpace(' ') - 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)('{') - PsiErrorElement:Expression expected - - PsiErrorElement:'}' expected - - PsiElement(Py:FSTRING_END)(''') - PsiErrorElement:End of statement expected - - PyExpressionStatement - PyReferenceExpression: foo - PsiElement(Py:IDENTIFIER)('foo') - PsiErrorElement:End of statement expected - - PyExpressionStatement - PyStringLiteralExpression: }} - PsiElement(Py:SINGLE_QUOTED_STRING)(''}}'') \ No newline at end of file diff --git a/python/testData/psi/FStringTrailingWhitespaceInIncompleteFragmentInFormatPart.txt b/python/testData/psi/FStringTrailingWhitespaceInIncompleteFragmentInFormatPart.txt index 9087400c35df..2110a4621daa 100644 --- a/python/testData/psi/FStringTrailingWhitespaceInIncompleteFragmentInFormatPart.txt +++ b/python/testData/psi/FStringTrailingWhitespaceInIncompleteFragmentInFormatPart.txt @@ -5,7 +5,7 @@ PyFile:FStringTrailingWhitespaceInIncompleteFragmentInFormatPart.py PsiWhiteSpace(' ') PsiElement(Py:EQ)('=') PsiWhiteSpace(' ') - PyStringLiteralExpression: {42:{ + PyStringLiteralExpression: {42:{ ' PyFormattedStringElement PsiElement(Py:FSTRING_START)('f'') PyFStringFragment @@ -16,8 +16,10 @@ PyFile:FStringTrailingWhitespaceInIncompleteFragmentInFormatPart.py PsiElement(Py:FSTRING_FRAGMENT_FORMAT_START)(':') PyFStringFragment PsiElement(Py:FSTRING_FRAGMENT_START)('{') - PsiErrorElement:Expression expected - PsiWhiteSpace(' ') - PsiErrorElement:'}' expected + PsiWhiteSpace(' ') + PyStringLiteralExpression: + PsiElement(Py:SINGLE_QUOTED_STRING)(''') + PsiErrorElement:Type conversion, ':' or '}' expected - PsiElement(Py:FSTRING_END)(''') \ No newline at end of file + PsiErrorElement:' expected + \ No newline at end of file diff --git a/python/testData/psi/MultilineFStringTerminatedByQuotesOfStringLiteral.py b/python/testData/psi/MultilineFStringNotTerminatedByQuotesOfStringLiteral.py similarity index 100% rename from python/testData/psi/MultilineFStringTerminatedByQuotesOfStringLiteral.py rename to python/testData/psi/MultilineFStringNotTerminatedByQuotesOfStringLiteral.py diff --git a/python/testData/psi/MultilineFStringTerminatedByQuotesOfStringLiteral.txt b/python/testData/psi/MultilineFStringNotTerminatedByQuotesOfStringLiteral.txt similarity index 51% rename from python/testData/psi/MultilineFStringTerminatedByQuotesOfStringLiteral.txt rename to python/testData/psi/MultilineFStringNotTerminatedByQuotesOfStringLiteral.txt index 57ab02dbac00..87fa34c573e4 100644 --- a/python/testData/psi/MultilineFStringTerminatedByQuotesOfStringLiteral.txt +++ b/python/testData/psi/MultilineFStringNotTerminatedByQuotesOfStringLiteral.txt @@ -1,4 +1,4 @@ -PyFile:MultilineFStringTerminatedByQuotesOfStringLiteral.py +PyFile:MultilineFStringNotTerminatedByQuotesOfStringLiteral.py PyAssignmentStatement PyTargetExpression: s PsiElement(Py:IDENTIFIER)('s') @@ -6,15 +6,15 @@ PyFile:MultilineFStringTerminatedByQuotesOfStringLiteral.py PsiElement(Py:EQ)('=') PsiWhiteSpace(' ') PyStringLiteralExpression: { - + '''''' } PyFormattedStringElement PsiElement(Py:FSTRING_START)('f'''') PyFStringFragment PsiElement(Py:FSTRING_FRAGMENT_START)('{') - PsiErrorElement:Expression expected - PsiWhiteSpace('\n ') - PsiErrorElement:'}' expected - - PsiElement(Py:FSTRING_END)(''''') - PsiElement(Py:TRIPLE_QUOTED_STRING)(''''\n}'''') \ No newline at end of file + PsiWhiteSpace('\n ') + PyStringLiteralExpression: + PsiElement(Py:TRIPLE_QUOTED_STRING)('''''''') + PsiWhiteSpace('\n') + PsiElement(Py:FSTRING_FRAGMENT_END)('}') + PsiElement(Py:FSTRING_END)(''''') \ No newline at end of file diff --git a/python/testData/psi/SingleQuotedFStringInsideMultilineFStringTerminatedByLineBreakInExpression.py b/python/testData/psi/SingleQuotedFStringInsideMultilineFStringNotTerminatedByLineBreakInExpression.py similarity index 100% rename from python/testData/psi/SingleQuotedFStringInsideMultilineFStringTerminatedByLineBreakInExpression.py rename to python/testData/psi/SingleQuotedFStringInsideMultilineFStringNotTerminatedByLineBreakInExpression.py diff --git a/python/testData/psi/SingleQuotedFStringInsideMultilineFStringNotTerminatedByLineBreakInExpression.txt b/python/testData/psi/SingleQuotedFStringInsideMultilineFStringNotTerminatedByLineBreakInExpression.txt new file mode 100644 index 000000000000..04bf29613e5f --- /dev/null +++ b/python/testData/psi/SingleQuotedFStringInsideMultilineFStringNotTerminatedByLineBreakInExpression.txt @@ -0,0 +1,31 @@ +PyFile:SingleQuotedFStringInsideMultilineFStringNotTerminatedByLineBreakInExpression.py + PyAssignmentStatement + PyTargetExpression: s + PsiElement(Py:IDENTIFIER)('s') + PsiWhiteSpace(' ') + 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 + 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)(''') + PsiElement(Py:FSTRING_FRAGMENT_END)('}') + PsiElement(Py:FSTRING_END)('"""') \ No newline at end of file diff --git a/python/testData/psi/SingleQuotedFStringInsideMultilineFStringTerminatedByLineBreakInExpressionInParentheses.py b/python/testData/psi/SingleQuotedFStringInsideMultilineFStringNotTerminatedByLineBreakInExpressionInParentheses.py similarity index 100% rename from python/testData/psi/SingleQuotedFStringInsideMultilineFStringTerminatedByLineBreakInExpressionInParentheses.py rename to python/testData/psi/SingleQuotedFStringInsideMultilineFStringNotTerminatedByLineBreakInExpressionInParentheses.py diff --git a/python/testData/psi/SingleQuotedFStringInsideMultilineFStringNotTerminatedByLineBreakInExpressionInParentheses.txt b/python/testData/psi/SingleQuotedFStringInsideMultilineFStringNotTerminatedByLineBreakInExpressionInParentheses.txt new file mode 100644 index 000000000000..5950c1de8c35 --- /dev/null +++ b/python/testData/psi/SingleQuotedFStringInsideMultilineFStringNotTerminatedByLineBreakInExpressionInParentheses.txt @@ -0,0 +1,34 @@ +PyFile:SingleQuotedFStringInsideMultilineFStringNotTerminatedByLineBreakInExpressionInParentheses.py + PyAssignmentStatement + PyTargetExpression: s + PsiElement(Py:IDENTIFIER)('s') + PsiWhiteSpace(' ') + 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 + PsiElement(Py:FSTRING_FRAGMENT_START)('{') + PyParenthesizedExpression + PsiElement(Py:LPAR)('(') + PyBinaryExpression + PyNumericLiteralExpression + PsiElement(Py:INTEGER_LITERAL)('1') + PsiWhiteSpace(' ') + PsiElement(Py:PLUS)('+') + PsiWhiteSpace(' \n') + PyNumericLiteralExpression + PsiElement(Py:INTEGER_LITERAL)('2') + PsiElement(Py:RPAR)(')') + PsiElement(Py:FSTRING_FRAGMENT_END)('}') + PsiElement(Py:FSTRING_END)(''') + PsiElement(Py:FSTRING_FRAGMENT_END)('}') + PsiElement(Py:FSTRING_END)('"""') \ No newline at end of file diff --git a/python/testData/psi/SingleQuotedFStringInsideMultilineFStringTerminatedByLineBreakInExpression.txt b/python/testData/psi/SingleQuotedFStringInsideMultilineFStringTerminatedByLineBreakInExpression.txt deleted file mode 100644 index dc0858fb1831..000000000000 --- a/python/testData/psi/SingleQuotedFStringInsideMultilineFStringTerminatedByLineBreakInExpression.txt +++ /dev/null @@ -1,40 +0,0 @@ -PyFile:SingleQuotedFStringInsideMultilineFStringTerminatedByLineBreakInExpression.py - PyAssignmentStatement - PyTargetExpression: s - PsiElement(Py:IDENTIFIER)('s') - PsiWhiteSpace(' ') - PsiElement(Py:EQ)('=') - PsiWhiteSpace(' ') - PyStringLiteralExpression: {f'{1 + - PyFormattedStringElement - PsiElement(Py:FSTRING_START)('f"""') - PyFStringFragment - PsiElement(Py:FSTRING_FRAGMENT_START)('{') - PyStringLiteralExpression: {1 + - PyFormattedStringElement - 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 - - PsiErrorElement:' expected - - PsiErrorElement:""" expected - - PsiWhiteSpace(' \n') - PyExpressionStatement - PyNumericLiteralExpression - PsiElement(Py:INTEGER_LITERAL)('2') - PsiErrorElement:End of statement expected - - PsiElement(Py:RBRACE)('}') - PsiErrorElement:Statement expected, found Py:RBRACE - - PyExpressionStatement - PyStringLiteralExpression: }""" - PsiElement(Py:SINGLE_QUOTED_STRING)(''}"""') \ No newline at end of file diff --git a/python/testData/psi/SingleQuotedFStringInsideMultilineFStringTerminatedByLineBreakInExpressionInParentheses.txt b/python/testData/psi/SingleQuotedFStringInsideMultilineFStringTerminatedByLineBreakInExpressionInParentheses.txt deleted file mode 100644 index 4e81557a5db2..000000000000 --- a/python/testData/psi/SingleQuotedFStringInsideMultilineFStringTerminatedByLineBreakInExpressionInParentheses.txt +++ /dev/null @@ -1,45 +0,0 @@ -PyFile:SingleQuotedFStringInsideMultilineFStringTerminatedByLineBreakInExpressionInParentheses.py - PyAssignmentStatement - PyTargetExpression: s - PsiElement(Py:IDENTIFIER)('s') - PsiWhiteSpace(' ') - PsiElement(Py:EQ)('=') - PsiWhiteSpace(' ') - PyStringLiteralExpression: {f'{(1 + - PyFormattedStringElement - PsiElement(Py:FSTRING_START)('f"""') - PyFStringFragment - PsiElement(Py:FSTRING_FRAGMENT_START)('{') - PyStringLiteralExpression: {(1 + - PyFormattedStringElement - PsiElement(Py:FSTRING_START)('f'') - PyFStringFragment - PsiElement(Py:FSTRING_FRAGMENT_START)('{') - PyParenthesizedExpression - PsiElement(Py:LPAR)('(') - PyBinaryExpression - PyNumericLiteralExpression - PsiElement(Py:INTEGER_LITERAL)('1') - PsiWhiteSpace(' ') - PsiElement(Py:PLUS)('+') - PsiErrorElement:Expression expected - - PsiErrorElement:' expected - - PsiErrorElement:""" expected - - PsiWhiteSpace(' \n') - PyExpressionStatement - PyNumericLiteralExpression - PsiElement(Py:INTEGER_LITERAL)('2') - PsiErrorElement:End of statement expected - - PsiElement(Py:RPAR)(')') - PsiErrorElement:Statement expected, found Py:RPAR - - PsiElement(Py:RBRACE)('}') - PsiErrorElement:Statement expected, found Py:RBRACE - - PyExpressionStatement - PyStringLiteralExpression: }""" - PsiElement(Py:SINGLE_QUOTED_STRING)(''}"""') \ No newline at end of file diff --git a/python/testSrc/com/jetbrains/python/PyEditingTest.java b/python/testSrc/com/jetbrains/python/PyEditingTest.java index 010a6f0ba0ef..2963581edb8f 100644 --- a/python/testSrc/com/jetbrains/python/PyEditingTest.java +++ b/python/testSrc/com/jetbrains/python/PyEditingTest.java @@ -126,7 +126,7 @@ public class PyEditingTest extends PyTestCase { public void testEnterInSingleLineFStringFragment() { doTestEnter("f'foo{1 + 2}bar'", "f'foo{1 +\n" + - "2}bar'"); + " 2}bar'"); } public void testEnterInFStringTextPart() { diff --git a/python/testSrc/com/jetbrains/python/PythonHighlightingTest.java b/python/testSrc/com/jetbrains/python/PythonHighlightingTest.java index 46f48b5e333b..99c84a96e818 100644 --- a/python/testSrc/com/jetbrains/python/PythonHighlightingTest.java +++ b/python/testSrc/com/jetbrains/python/PythonHighlightingTest.java @@ -416,12 +416,12 @@ public class PythonHighlightingTest extends PyTestCase { } // PY-20773 - public void testFStringHashSigns() { + public void testFStringCommentsBefore312() { runWithLanguageLevel(LanguageLevel.PYTHON36, () -> doTest(true, false)); } // PY-20844 - public void testFStringBackslashes() { + public void testFStringBackslashesBefore312() { runWithLanguageLevel(LanguageLevel.PYTHON36, () -> doTest(true, false)); } @@ -431,10 +431,166 @@ public class PythonHighlightingTest extends PyTestCase { } // PY-20901 - public void testFStringTooDeeplyNestedExpressionFragments() { + public void testFStringTooDeeplyNestedExpressionFragmentsBefore312() { runWithLanguageLevel(LanguageLevel.PYTHON36, () -> doTest(true, false)); } + // PY-59594 + public void testFStringTerminatedByQuoteOfStringLiteralInFormatPartBefore312() { + runWithLanguageLevel(LanguageLevel.PYTHON311, () -> doTest(true, false)); + } + + // PY-59594 + public void testFStringTerminatedByQuoteOfNestedFStringLiteralInFormatPartBefore312() { + runWithLanguageLevel(LanguageLevel.PYTHON311, () -> doTest(true, false)); + } + + // PY-59594 + public void testMultilineFStringTerminatedByQuotesOfStringLiteralBefore312() { + runWithLanguageLevel(LanguageLevel.PYTHON311, () -> doTest(true, false)); + } + + // PY-59594 + public void testSingleQuotedFStringInsideMultilineFStringTerminatedByLineBreakInExpressionBefore312() { + runWithLanguageLevel(LanguageLevel.PYTHON311, () -> doTest(true, false)); + } + + // PY-59594 + public void testFStringTerminatedByQuoteOfStringLiteralBefore312() { + runWithLanguageLevel(LanguageLevel.PYTHON311, () -> doTest(true, false)); + } + + // PY-59594 + public void testFStringTerminatedByQuoteInsideNestedFStringLiteralInFormatPartBefore312() { + runWithLanguageLevel(LanguageLevel.PYTHON311, () -> doTest(true, false)); + } + + // PY-59594 + public void testFStringTerminatedByQuoteOfNestedFStringLiteralBefore312() { + runWithLanguageLevel(LanguageLevel.PYTHON311, () -> doTest(true, false)); + } + + // PY-59594 + public void testFStringTerminatedByQuoteInsideFStringLiteralBefore312() { + runWithLanguageLevel(LanguageLevel.PYTHON311, () -> doTest(true, false)); + } + + // PY-59594 + public void testNestedMultilineFStringsWithMultilineExpressionsBefore312() { + runWithLanguageLevel(LanguageLevel.PYTHON311, () -> doTest(true, false)); + } + + // PY-59594 + public void testFStringTerminatedByLineBreakInExpressionInFormatPartBefore312() { + runWithLanguageLevel(LanguageLevel.PYTHON311, () -> doTest(true, false)); + } + + // PY-59594 + public void testFStringTerminatedByQuoteOfNestedStringLiteralBefore312() { + runWithLanguageLevel(LanguageLevel.PYTHON311, () -> doTest(true, false)); + } + + // PY-59594 + public void testMultilineFStringTerminatedByQuotesInsideParenthesizedExpressionBefore312() { + runWithLanguageLevel(LanguageLevel.PYTHON311, () -> doTest(true, false)); + } + + // PY-59594 + public void testFStringTerminatedByLineBreakInNestedExpressionInFormatPartBefore312() { + runWithLanguageLevel(LanguageLevel.PYTHON311, () -> doTest(true, false)); + } + + // PY-59594 + public void testFStringTerminatedByLineBreakInExpressionBefore312() { + runWithLanguageLevel(LanguageLevel.PYTHON311, () -> doTest(true, false)); + } + + // PY-59594 + public void testFStringTerminatedByLineBreakInStringLiteralInFormatPartBefore312() { + runWithLanguageLevel(LanguageLevel.PYTHON311, () -> doTest(true, false)); + } + + // PY-59594 + public void testFStringTerminatedByQuoteInsideStringLiteralInFormatPartBefore312() { + runWithLanguageLevel(LanguageLevel.PYTHON311, () -> doTest(true, false)); + } + + // PY-59594 + public void testFStringTerminatedByQuoteInsideNestedStringLiteralBefore312() { + runWithLanguageLevel(LanguageLevel.PYTHON311, () -> doTest(true, false)); + } + + // PY-59594 + public void testMultilineFStringContainingMultilineExpressionAfterStatementBreakBefore312() { + runWithLanguageLevel(LanguageLevel.PYTHON311, () -> doTest(true, false)); + } + + // PY-59594 + public void testFStringTerminatedByQuoteInNestedFormatPartBefore312() { runWithLanguageLevel(LanguageLevel.PYTHON311, () -> doTest(true, false)); } + + // PY-59594 + public void testSingleQuotedFStringInsideMultilineFStringTerminatedByLineBreakInExpressionInParenthesesBefore312() { + runWithLanguageLevel(LanguageLevel.PYTHON311, () -> doTest(true, false)); + } + + // PY-59594 + public void testFStringTerminatedByQuoteInNestedLiteralPartBefore312() { + runWithLanguageLevel(LanguageLevel.PYTHON311, () -> doTest(true, false)); + } + + // PY-59594 + public void testFStringTerminatedByQuoteInsideStringLiteralBefore312() { + runWithLanguageLevel(LanguageLevel.PYTHON311, () -> doTest(true, false)); + } + + // PY-59594 + public void testFStringEqualitySignBefore312() { runWithLanguageLevel(LanguageLevel.PYTHON311, () -> doTest(true, false)); } + + // PY-59594 + public void testFStringTerminatedByLineBreakInNestedExpressionBefore312() { + runWithLanguageLevel(LanguageLevel.PYTHON311, () -> doTest(true, false)); + } + + // PY-59594 + public void testFStringTerminatedByQuoteOfNestedStringLiteralInFormatPartBefore312() { + runWithLanguageLevel(LanguageLevel.PYTHON311, () -> doTest(true, false)); + } + + // PY-59594 + public void testFStringTerminatedByLineBreakInStringLiteralBefore312() { + runWithLanguageLevel(LanguageLevel.PYTHON311, () -> doTest(true, false)); + } + + // PY-59594 + public void testMultilineFStringContainingMultilineExpressionBefore312() { + runWithLanguageLevel(LanguageLevel.PYTHON311, () -> doTest(true, false)); + } + + // PY-59594 + public void testFStringTerminatedByQuoteOfFStringLiteralInFormatPartBefore312() { + runWithLanguageLevel(LanguageLevel.PYTHON311, () -> doTest(true, false)); + } + + // PY-59594 + public void testFStringTerminatedByQuoteInsideNestedFStringLiteralBefore312() { + runWithLanguageLevel(LanguageLevel.PYTHON311, () -> doTest(true, false)); + } + + // PY-59594 + public void testFStringTerminatedByQuoteOfFStringLiteralBefore312() { + runWithLanguageLevel(LanguageLevel.PYTHON311, () -> doTest(true, false)); + } + + // PY-59594 + public void testFStringTerminatedByQuoteInsideFStringLiteralInFormatPartBefore312() { + runWithLanguageLevel(LanguageLevel.PYTHON311, () -> doTest(true, false)); + } + + // PY-59594 + public void testFStringTerminatedByQuoteInsideNestedStringLiteralInFormatPartBefore312() { + runWithLanguageLevel(LanguageLevel.PYTHON311, () -> doTest(true, false)); + } + // PY-12634 public void testSpaceBetweenAtAndDecorator() { doTest(true, true); diff --git a/python/testSrc/com/jetbrains/python/PythonLexerTest.java b/python/testSrc/com/jetbrains/python/PythonLexerTest.java index 014544765e1b..c9057e327856 100644 --- a/python/testSrc/com/jetbrains/python/PythonLexerTest.java +++ b/python/testSrc/com/jetbrains/python/PythonLexerTest.java @@ -389,24 +389,22 @@ public class PythonLexerTest extends PyLexerTestCase { } - public void testFStringMatchingQuoteRecoveryInsideContentOfNestedStringLiteral() { - doTest("s = f'{ur\"foo'bar\"}'", - "Py:IDENTIFIER", "Py:SPACE", "Py:EQ", "Py:SPACE", - "Py:FSTRING_START", "Py:FSTRING_FRAGMENT_START", "Py:SINGLE_QUOTED_STRING", "Py:FSTRING_END", - "Py:IDENTIFIER", "Py:SINGLE_QUOTED_STRING", "Py:STATEMENT_BREAK"); + public void testFStringMatchingQuoteHandlingInsideContentOfNestedStringLiteral() { + doTest("s = f'{ur\"foo'bar\"}'", + "Py:IDENTIFIER", "Py:SPACE", "Py:EQ", "Py:SPACE", "Py:FSTRING_START", "Py:FSTRING_FRAGMENT_START", "Py:SINGLE_QUOTED_STRING", + "Py:FSTRING_FRAGMENT_END", "Py:FSTRING_END", "Py:STATEMENT_BREAK"); } - public void testFStringMatchingQuoteRecoveryQuoteOfNestedStringLiteralWithPrefix() { + public void testFStringMatchingQuoteHandlingQuoteOfNestedStringLiteralWithPrefix() { doTest("s = f'{ur'foo'}'", - "Py:IDENTIFIER", "Py:SPACE", "Py:EQ", "Py:SPACE", - "Py:FSTRING_START", "Py:FSTRING_FRAGMENT_START", "Py:IDENTIFIER", "Py:FSTRING_END", - "Py:IDENTIFIER", "Py:SINGLE_QUOTED_STRING", "Py:STATEMENT_BREAK"); + "Py:IDENTIFIER", "Py:SPACE", "Py:EQ", "Py:SPACE", "Py:FSTRING_START", "Py:FSTRING_FRAGMENT_START", "Py:SINGLE_QUOTED_STRING", + "Py:FSTRING_FRAGMENT_END", "Py:FSTRING_END", "Py:STATEMENT_BREAK"); } - public void testFStringMatchingQuoteRecoveryQuoteOfNestedStringLiteralWithoutPrefix() { + public void testFStringMatchingQuoteHandlingQuoteOfNestedStringLiteralWithoutPrefix() { doTest("s = f'{'foo'}'", - "Py:IDENTIFIER", "Py:SPACE", "Py:EQ", "Py:SPACE", "Py:FSTRING_START", "Py:FSTRING_FRAGMENT_START", "Py:FSTRING_END", - "Py:IDENTIFIER", "Py:SINGLE_QUOTED_STRING", "Py:STATEMENT_BREAK"); + "Py:IDENTIFIER", "Py:SPACE", "Py:EQ", "Py:SPACE", "Py:FSTRING_START", "Py:FSTRING_FRAGMENT_START", "Py:SINGLE_QUOTED_STRING", + "Py:FSTRING_FRAGMENT_END", "Py:FSTRING_END", "Py:STATEMENT_BREAK"); } public void testNoStatementBreakInsideFragmentOfMultilineFString() { @@ -420,9 +418,8 @@ public class PythonLexerTest extends PyLexerTestCase { public void testStatementBreakInsideFragmentOfSingleLineFString() { doTest("s = f'{1 +\n" + " 2}'", - "Py:IDENTIFIER", "Py:SPACE", "Py:EQ", "Py:SPACE", - "Py:FSTRING_START", "Py:FSTRING_FRAGMENT_START", "Py:INTEGER_LITERAL", "Py:SPACE", "Py:PLUS", "Py:STATEMENT_BREAK", - "Py:LINE_BREAK", "Py:INDENT", "Py:INTEGER_LITERAL", "Py:RBRACE", "Py:SINGLE_QUOTED_STRING", "Py:STATEMENT_BREAK"); + "Py:IDENTIFIER", "Py:SPACE", "Py:EQ", "Py:SPACE", "Py:FSTRING_START", "Py:FSTRING_FRAGMENT_START", "Py:INTEGER_LITERAL", + "Py:SPACE", "Py:PLUS", "Py:LINE_BREAK", "Py:INTEGER_LITERAL", "Py:FSTRING_FRAGMENT_END", "Py:FSTRING_END", "Py:STATEMENT_BREAK"); } public void testFStringUnmatchedQuotesAsTextParts() { diff --git a/python/testSrc/com/jetbrains/python/parsing/PythonParsingTest.java b/python/testSrc/com/jetbrains/python/parsing/PythonParsingTest.java index d92667d979e0..c9481c327ffd 100644 --- a/python/testSrc/com/jetbrains/python/parsing/PythonParsingTest.java +++ b/python/testSrc/com/jetbrains/python/parsing/PythonParsingTest.java @@ -575,67 +575,67 @@ public class PythonParsingTest extends ParsingTestCase { doTest(LanguageLevel.PYTHON36); } - public void testFStringTerminatedByQuoteOfStringLiteral() { + public void testFStringNotTerminatedByQuoteOfStringLiteral() { doTest(LanguageLevel.PYTHON36); } - public void testFStringTerminatedByQuoteInsideStringLiteral() { + public void testFStringNotTerminatedByQuoteInsideStringLiteral() { doTest(LanguageLevel.PYTHON36); } - public void testFStringTerminatedByQuoteOfNestedStringLiteral() { + public void testFStringNotTerminatedByQuoteOfNestedStringLiteral() { doTest(LanguageLevel.PYTHON36); } - public void testFStringTerminatedByQuoteInsideNestedStringLiteral() { + public void testFStringNotTerminatedByQuoteInsideNestedStringLiteral() { doTest(LanguageLevel.PYTHON36); } - public void testFStringTerminatedByQuoteOfFStringLiteral() { + public void testFStringNotTerminatedByQuoteOfFStringLiteral() { doTest(LanguageLevel.PYTHON36); } - public void testFStringTerminatedByQuoteInsideFStringLiteral() { + public void testFStringNotTerminatedByQuoteInsideFStringLiteral() { doTest(LanguageLevel.PYTHON36); } - public void testFStringTerminatedByQuoteOfNestedFStringLiteral() { + public void testFStringNotTerminatedByQuoteOfNestedFStringLiteral() { doTest(LanguageLevel.PYTHON36); } - public void testFStringTerminatedByQuoteInsideNestedFStringLiteral() { + public void testFStringNotTerminatedByQuoteInsideNestedFStringLiteral() { doTest(LanguageLevel.PYTHON36); } - public void testFStringTerminatedByQuoteOfStringLiteralInFormatPart() { + public void testFStringNotTerminatedByQuoteOfStringLiteralInFormatPart() { + doTest(LanguageLevel.getLatest()); + } + + public void testFStringNotTerminatedByQuoteInsideStringLiteralInFormatPart() { doTest(LanguageLevel.PYTHON36); } - public void testFStringTerminatedByQuoteInsideStringLiteralInFormatPart() { + public void testFStringNotTerminatedByQuoteOfNestedStringLiteralInFormatPart() { doTest(LanguageLevel.PYTHON36); } - public void testFStringTerminatedByQuoteOfNestedStringLiteralInFormatPart() { + public void testFStringNotTerminatedByQuoteInsideNestedStringLiteralInFormatPart() { doTest(LanguageLevel.PYTHON36); } - public void testFStringTerminatedByQuoteInsideNestedStringLiteralInFormatPart() { + public void testFStringNotTerminatedByQuoteOfFStringLiteralInFormatPart() { doTest(LanguageLevel.PYTHON36); } - public void testFStringTerminatedByQuoteOfFStringLiteralInFormatPart() { + public void testFStringNotTerminatedByQuoteInsideFStringLiteralInFormatPart() { doTest(LanguageLevel.PYTHON36); } - public void testFStringTerminatedByQuoteInsideFStringLiteralInFormatPart() { + public void testFStringNotTerminatedByQuoteOfNestedFStringLiteralInFormatPart() { doTest(LanguageLevel.PYTHON36); } - public void testFStringTerminatedByQuoteOfNestedFStringLiteralInFormatPart() { - doTest(LanguageLevel.PYTHON36); - } - - public void testFStringTerminatedByQuoteInsideNestedFStringLiteralInFormatPart() { + public void testFStringNotTerminatedByQuoteInsideNestedFStringLiteralInFormatPart() { doTest(LanguageLevel.PYTHON36); } @@ -643,7 +643,7 @@ public class PythonParsingTest extends ParsingTestCase { doTest(LanguageLevel.PYTHON36); } - public void testFStringTerminatedByQuoteInNestedLiteralPart() { + public void testFStringNotTerminatedByQuoteInNestedLiteralPart() { doTest(LanguageLevel.PYTHON36); } @@ -651,7 +651,7 @@ public class PythonParsingTest extends ParsingTestCase { doTest(LanguageLevel.PYTHON36); } - public void testFStringTerminatedByQuoteInNestedFormatPart() { + public void testFStringNotTerminatedByQuoteInNestedFormatPart() { doTest(LanguageLevel.PYTHON36); } @@ -671,23 +671,23 @@ public class PythonParsingTest extends ParsingTestCase { doTest(LanguageLevel.PYTHON36); } - public void testFStringTerminatedByLineBreakInExpression() { + public void testFStringNotTerminatedByLineBreakInExpression() { doTest(LanguageLevel.PYTHON36); } - public void testFStringTerminatedByLineBreakInNestedExpression() { + public void testFStringNotTerminatedByLineBreakInNestedExpression() { doTest(LanguageLevel.PYTHON36); } - public void testFStringTerminatedByLineBreakInExpressionInFormatPart() { + public void testFStringNotTerminatedByLineBreakInExpressionInFormatPart() { doTest(LanguageLevel.PYTHON36); } - public void testFStringTerminatedByLineBreakInNestedExpressionInFormatPart() { + public void testFStringNotTerminatedByLineBreakInNestedExpressionInFormatPart() { doTest(LanguageLevel.PYTHON36); } - public void testFStringTerminatedByLineBreakInStringLiteral() { + public void testFStringNotTerminatedByLineBreakInStringLiteral() { doTest(LanguageLevel.PYTHON36); } @@ -697,7 +697,7 @@ public class PythonParsingTest extends ParsingTestCase { doTest(LanguageLevel.PYTHON36); } - public void testFStringTerminatedByLineBreakInStringLiteralInFormatPart() { + public void testFStringNotTerminatedByLineBreakInStringLiteralInFormatPart() { doTest(LanguageLevel.PYTHON36); } @@ -705,11 +705,11 @@ public class PythonParsingTest extends ParsingTestCase { doTest(LanguageLevel.PYTHON36); } - public void testMultilineFStringTerminatedByQuotesOfStringLiteral() { + public void testMultilineFStringNotTerminatedByQuotesOfStringLiteral() { doTest(LanguageLevel.PYTHON36); } - public void testMultilineFStringTerminatedByQuotesInsideParenthesizedExpression() { + public void testFStringNotTerminatedByQuotesInsideParenthesizedExpression() { doTest(LanguageLevel.PYTHON36); } @@ -729,11 +729,11 @@ public class PythonParsingTest extends ParsingTestCase { doTest(LanguageLevel.PYTHON36); } - public void testSingleQuotedFStringInsideMultilineFStringTerminatedByLineBreakInExpression() { + public void testSingleQuotedFStringInsideMultilineFStringNotTerminatedByLineBreakInExpression() { doTest(LanguageLevel.PYTHON36); } - public void testSingleQuotedFStringInsideMultilineFStringTerminatedByLineBreakInExpressionInParentheses() { + public void testSingleQuotedFStringInsideMultilineFStringNotTerminatedByLineBreakInExpressionInParentheses() { doTest(LanguageLevel.PYTHON36); }