[syntax-errors] Assignment expressions before Python 3.8 (#16383)

## Summary
This PR is the first in a series derived from
https://github.com/astral-sh/ruff/pull/16308, each of which add support
for detecting one version-related syntax error from
https://github.com/astral-sh/ruff/issues/6591. This one should be
the largest because it also includes the addition of the 
`Parser::add_unsupported_syntax_error` method

Otherwise I think the general structure will be the same for each syntax
error:
* Detecting the error in the parser
* Inline parser tests for the new error
* New ruff CLI tests for the new error

## Test Plan
As noted above, there are new inline parser tests, as well as new ruff
CLI
tests. Once https://github.com/astral-sh/ruff/pull/16379 is resolved,
there should also be new mdtests for red-knot,
but this PR does not currently include those.
This commit is contained in:
Brent Westbrook 2025-02-28 17:13:46 -05:00 committed by GitHub
parent ba44e9de13
commit 4431978262
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 182 additions and 28 deletions

View file

@ -7,7 +7,7 @@ use rustc_hash::{FxBuildHasher, FxHashSet};
use ruff_python_ast::name::Name;
use ruff_python_ast::{
self as ast, BoolOp, CmpOp, ConversionFlag, Expr, ExprContext, FStringElement, FStringElements,
IpyEscapeKind, Number, Operator, StringFlags, UnaryOp,
IpyEscapeKind, Number, Operator, PythonVersion, StringFlags, UnaryOp,
};
use ruff_text_size::{Ranged, TextLen, TextRange, TextSize};
@ -16,7 +16,7 @@ use crate::parser::{helpers, FunctionKind, Parser};
use crate::string::{parse_fstring_literal_element, parse_string_literal, StringType};
use crate::token::{TokenKind, TokenValue};
use crate::token_set::TokenSet;
use crate::{FStringErrorType, Mode, ParseErrorType};
use crate::{FStringErrorType, Mode, ParseErrorType, UnsupportedSyntaxErrorKind};
use super::{FStringElementsKind, Parenthesized, RecoveryContextKind};
@ -2161,10 +2161,24 @@ impl<'src> Parser<'src> {
let value = self.parse_conditional_expression_or_higher();
let range = self.node_range(start);
// test_err walrus_py37
// # parse_options: { "target-version": "3.7" }
// (x := 1)
// test_ok walrus_py38
// # parse_options: { "target-version": "3.8" }
// (x := 1)
if self.options.target_version < PythonVersion::PY38 {
self.add_unsupported_syntax_error(UnsupportedSyntaxErrorKind::Walrus, range);
}
ast::ExprNamed {
target: Box::new(target),
value: Box::new(value.expr),
range: self.node_range(start),
range,
}
}

View file

@ -11,7 +11,7 @@ use crate::parser::progress::{ParserProgress, TokenId};
use crate::token::TokenValue;
use crate::token_set::TokenSet;
use crate::token_source::{TokenSource, TokenSourceCheckpoint};
use crate::{Mode, ParseError, ParseErrorType, TokenKind};
use crate::{Mode, ParseError, ParseErrorType, TokenKind, UnsupportedSyntaxErrorKind};
use crate::{Parsed, Tokens};
pub use crate::parser::options::ParseOptions;
@ -438,6 +438,16 @@ impl<'src> Parser<'src> {
inner(&mut self.errors, error, ranged.range());
}
/// Add an [`UnsupportedSyntaxError`] with the given [`UnsupportedSyntaxErrorKind`] and
/// [`TextRange`].
fn add_unsupported_syntax_error(&mut self, kind: UnsupportedSyntaxErrorKind, range: TextRange) {
self.unsupported_syntax_errors.push(UnsupportedSyntaxError {
kind,
range,
target_version: self.options.target_version,
});
}
/// Returns `true` if the current token is of the given kind.
fn at(&self, kind: TokenKind) -> bool {
self.current_token_kind() == kind

View file

@ -17,7 +17,7 @@ use crate::parser::{
};
use crate::token::{TokenKind, TokenValue};
use crate::token_set::TokenSet;
use crate::{Mode, ParseErrorType, UnsupportedSyntaxError, UnsupportedSyntaxErrorKind};
use crate::{Mode, ParseErrorType, UnsupportedSyntaxErrorKind};
use super::expression::ExpressionContext;
use super::Parenthesized;
@ -2278,11 +2278,7 @@ impl<'src> Parser<'src> {
// pass
if self.options.target_version < PythonVersion::PY310 {
self.unsupported_syntax_errors.push(UnsupportedSyntaxError {
kind: UnsupportedSyntaxErrorKind::MatchBeforePy310,
range: match_range,
target_version: self.options.target_version,
});
self.add_unsupported_syntax_error(UnsupportedSyntaxErrorKind::Match, match_range);
}
ast::StmtMatch {