void () { final SyntaxTreeBuilder builder = myContext.getBuilder(); if (atToken(PyTokenTypes.FSTRING_FRAGMENT_START)) { final SyntaxTreeBuilder.Marker marker = builder.mark(); nextToken(); SyntaxTreeBuilder.Marker recoveryMarker = builder.mark(); final boolean parsedExpression = myContext.getExpressionParser().parseExpressionOptional(); if (parsedExpression) { recoveryMarker.drop(); recoveryMarker = builder.mark(); } boolean recovery = !parsedExpression; while (!builder.eof() && !atAnyOfTokens(PyTokenTypes.FSTRING_FRAGMENT_TYPE_CONVERSION, PyTokenTypes.FSTRING_FRAGMENT_FORMAT_START, PyTokenTypes.FSTRING_FRAGMENT_END, PyTokenTypes.FSTRING_END, PyTokenTypes.STATEMENT_BREAK, PyTokenTypes.EQ)) { nextToken(); recovery = true; } if (recovery) { recoveryMarker.error(parsedExpression ? message("unexpected.expression.part") : message("PARSE.expected.expression")); recoveryMarker.setCustomEdgeTokenBinders(null, CONSUME_COMMENTS_AND_SPACES_TO_LEFT); } else { recoveryMarker.drop(); } matchToken(PyTokenTypes.EQ); final boolean hasTypeConversion = matchToken(PyTokenTypes.FSTRING_FRAGMENT_TYPE_CONVERSION); final boolean hasFormatPart = atToken(PyTokenTypes.FSTRING_FRAGMENT_FORMAT_START); if (hasFormatPart) { parseFStringFragmentFormatPart(); } @ParsingError String errorMessage = message("PARSE.expected.fstring.rbrace"); if (!hasFormatPart && !atToken(PyTokenTypes.FSTRING_END)) { errorMessage = message("PARSE.expected.fstring.colon.or.rbrace"); if (!hasTypeConversion) { errorMessage = message("PARSE.expected.fstring.type.conversion.or.colon.or.rbrace"); } } checkMatches(PyTokenTypes.FSTRING_FRAGMENT_END, errorMessage); marker.setCustomEdgeTokenBinders(null, CONSUME_COMMENTS_AND_SPACES_TO_LEFT); marker.done(PyElementTypes.FSTRING_FRAGMENT); } }