[ty] Fix auto import for files with from __future__ import (#20987)
Some checks are pending
CI / Determine changes (push) Waiting to run
CI / cargo fmt (push) Waiting to run
CI / cargo clippy (push) Blocked by required conditions
CI / cargo test (linux) (push) Blocked by required conditions
CI / cargo test (linux, release) (push) Blocked by required conditions
CI / cargo test (${{ github.repository == 'astral-sh/ruff' && 'depot-windows-2022-16' || 'windows-latest' }}) (push) Blocked by required conditions
CI / cargo test (macos-latest) (push) Blocked by required conditions
CI / cargo build (msrv) (push) Blocked by required conditions
CI / fuzz parser (push) Blocked by required conditions
CI / cargo test (wasm) (push) Blocked by required conditions
CI / pre-commit (push) Waiting to run
CI / cargo fuzz build (push) Blocked by required conditions
CI / test scripts (push) Blocked by required conditions
CI / ecosystem (push) Blocked by required conditions
CI / Fuzz for new ty panics (push) Blocked by required conditions
CI / cargo shear (push) Blocked by required conditions
CI / ty completion evaluation (push) Blocked by required conditions
CI / python package (push) Waiting to run
CI / mkdocs (push) Waiting to run
CI / formatter instabilities and black similarity (push) Blocked by required conditions
CI / test ruff-lsp (push) Blocked by required conditions
CI / check playground (push) Blocked by required conditions
CI / benchmarks instrumented (ruff) (push) Blocked by required conditions
CI / benchmarks instrumented (ty) (push) Blocked by required conditions
CI / benchmarks walltime (medium|multithreaded) (push) Blocked by required conditions
CI / benchmarks walltime (small|large) (push) Blocked by required conditions
[ty Playground] Release / publish (push) Waiting to run

This commit is contained in:
Micha Reiser 2025-10-21 08:14:39 +02:00 committed by GitHub
parent a802d7a0ea
commit 24d0f65d62
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -145,8 +145,12 @@ impl<'a> Importer<'a> {
let request = request.avoid_conflicts(self.db, self.file, members); let request = request.avoid_conflicts(self.db, self.file, members);
let mut symbol_text: Box<str> = request.member.into(); let mut symbol_text: Box<str> = request.member.into();
let Some(response) = self.find(&request, members.at) else { let Some(response) = self.find(&request, members.at) else {
let import = Insertion::start_of_file(self.parsed.suite(), self.source, self.stylist) let insertion = if let Some(future) = self.find_last_future_import() {
.into_edit(&request.to_string()); Insertion::end_of_statement(future.stmt, self.source, self.stylist)
} else {
Insertion::start_of_file(self.parsed.suite(), self.source, self.stylist)
};
let import = insertion.into_edit(&request.to_string());
if matches!(request.style, ImportStyle::Import) { if matches!(request.style, ImportStyle::Import) {
symbol_text = format!("{}.{}", request.module, request.member).into(); symbol_text = format!("{}.{}", request.module, request.member).into();
} }
@ -241,6 +245,19 @@ impl<'a> Importer<'a> {
} }
choice choice
} }
/// Find the last `from __future__` import statement in the AST.
fn find_last_future_import(&self) -> Option<&'a AstImport> {
self.imports
.iter()
.take_while(|import| {
import
.stmt
.as_import_from_stmt()
.is_some_and(|import_from| import_from.module.as_deref() == Some("__future__"))
})
.last()
}
} }
/// A map of symbols in scope at a particular location in a module. /// A map of symbols in scope at a particular location in a module.
@ -1293,6 +1310,44 @@ def foo():
"); ");
} }
#[test]
fn existing_future_import() {
let test = cursor_test(
"\
from __future__ import annotations
<CURSOR>
",
);
assert_snapshot!(
test.import("typing", "TypeVar"), @r"
from __future__ import annotations
import typing
typing.TypeVar
");
}
#[test]
fn existing_future_import_after_docstring() {
let test = cursor_test(
r#"
"This is a module level docstring"
from __future__ import annotations
<CURSOR>
"#,
);
assert_snapshot!(
test.import("typing", "TypeVar"), @r#"
"This is a module level docstring"
from __future__ import annotations
import typing
typing.TypeVar
"#);
}
#[test] #[test]
fn qualify_symbol_to_avoid_overwriting_other_symbol_in_scope() { fn qualify_symbol_to_avoid_overwriting_other_symbol_in_scope() {
let test = cursor_test( let test = cursor_test(