[red-knot] Detect version-related syntax errors (#16379)

## Summary
This PR extends version-related syntax error detection to red-knot. The
main changes here are:

1. Passing `ParseOptions` specifying a `PythonVersion` to parser calls
2. Adding a `python_version` method to the `Db` trait to make this
possible
3. Converting `UnsupportedSyntaxError`s to `Diagnostic`s
4. Updating existing mdtests  to avoid unrelated syntax errors

My initial draft of (1) and (2) in #16090 instead tried passing a
`PythonVersion` down to every parser call, but @MichaReiser suggested
the `Db` approach instead
[here](https://github.com/astral-sh/ruff/pull/16090#discussion_r1969198407),
and I think it turned out much nicer.

All of the new `python_version` methods look like this:

```rust
fn python_version(&self) -> ruff_python_ast::PythonVersion {
    Program::get(self).python_version(self)
}
```

with the exception of the `TestDb` in `ruff_db`, which hard-codes
`PythonVersion::latest()`.

## Test Plan

Existing mdtests, plus a new mdtest to see at least one of the new
diagnostics.
This commit is contained in:
Brent Westbrook 2025-04-17 14:00:30 -04:00 committed by GitHub
parent d2ebfd6ed7
commit 9c47b6dbb0
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
43 changed files with 353 additions and 14 deletions

View file

@ -489,13 +489,27 @@ pub(crate) enum ErrorAssertionParseError<'a> {
#[cfg(test)]
mod tests {
use super::*;
use red_knot_python_semantic::{Program, ProgramSettings, PythonPlatform, SearchPathSettings};
use ruff_db::files::system_path_to_file;
use ruff_db::system::DbWithWritableSystem as _;
use ruff_python_ast::PythonVersion;
use ruff_python_trivia::textwrap::dedent;
use ruff_source_file::OneIndexed;
fn get_assertions(source: &str) -> InlineFileAssertions {
let mut db = Db::setup();
let settings = ProgramSettings {
python_version: PythonVersion::default(),
python_platform: PythonPlatform::default(),
search_paths: SearchPathSettings::new(Vec::new()),
};
match Program::try_get(&db) {
Some(program) => program.update_from_settings(&mut db, settings),
None => Program::from_settings(&db, settings).map(|_| ()),
}
.expect("Failed to update Program settings in TestDb");
db.write_file("/src/test.py", source).unwrap();
let file = system_path_to_file(&db, "/src/test.py").unwrap();
InlineFileAssertions::from_file(&db, file)