mirror of https://github.com/astral-sh/ruff
[ty] Exclude `typing_extensions` from completions unless it's really available
This works by adding a third module resolution mode that lets the caller opt into _some_ shadowing of modules that is otherwise not allowed (for `typing` and `typing_extensions`). Fixes astral-sh/ty#1658
This commit is contained in:
parent
0e651b50b7
commit
a561e6659d
|
|
@ -25,4 +25,4 @@ scope-simple-long-identifier,main.py,0,1
|
||||||
tstring-completions,main.py,0,1
|
tstring-completions,main.py,0,1
|
||||||
ty-extensions-lower-stdlib,main.py,0,8
|
ty-extensions-lower-stdlib,main.py,0,8
|
||||||
type-var-typing-over-ast,main.py,0,3
|
type-var-typing-over-ast,main.py,0,3
|
||||||
type-var-typing-over-ast,main.py,1,278
|
type-var-typing-over-ast,main.py,1,275
|
||||||
|
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use ruff_db::files::File;
|
use ruff_db::files::File;
|
||||||
use ty_project::Db;
|
use ty_project::Db;
|
||||||
use ty_python_semantic::{Module, all_modules};
|
use ty_python_semantic::{Module, ModuleName, all_modules, resolve_real_shadowable_module};
|
||||||
|
|
||||||
use crate::symbols::{QueryPattern, SymbolInfo, symbols_for_file_global_only};
|
use crate::symbols::{QueryPattern, SymbolInfo, symbols_for_file_global_only};
|
||||||
|
|
||||||
|
|
@ -8,12 +8,20 @@ use crate::symbols::{QueryPattern, SymbolInfo, symbols_for_file_global_only};
|
||||||
///
|
///
|
||||||
/// Returns symbols from all files in the workspace and dependencies, filtered
|
/// Returns symbols from all files in the workspace and dependencies, filtered
|
||||||
/// by the query.
|
/// by the query.
|
||||||
pub fn all_symbols<'db>(db: &'db dyn Db, query: &QueryPattern) -> Vec<AllSymbolInfo<'db>> {
|
pub fn all_symbols<'db>(
|
||||||
|
db: &'db dyn Db,
|
||||||
|
importing_from: File,
|
||||||
|
query: &QueryPattern,
|
||||||
|
) -> Vec<AllSymbolInfo<'db>> {
|
||||||
// If the query is empty, return immediately to avoid expensive file scanning
|
// If the query is empty, return immediately to avoid expensive file scanning
|
||||||
if query.will_match_everything() {
|
if query.will_match_everything() {
|
||||||
return Vec::new();
|
return Vec::new();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let typing_extensions = ModuleName::new("typing_extensions").unwrap();
|
||||||
|
let is_typing_extensions_available = importing_from.is_stub(db)
|
||||||
|
|| resolve_real_shadowable_module(db, &typing_extensions).is_some();
|
||||||
|
|
||||||
let results = std::sync::Mutex::new(Vec::new());
|
let results = std::sync::Mutex::new(Vec::new());
|
||||||
{
|
{
|
||||||
let modules = all_modules(db);
|
let modules = all_modules(db);
|
||||||
|
|
@ -28,6 +36,11 @@ pub fn all_symbols<'db>(db: &'db dyn Db, query: &QueryPattern) -> Vec<AllSymbolI
|
||||||
let Some(file) = module.file(&*db) else {
|
let Some(file) = module.file(&*db) else {
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
|
// TODO: also make it available in `TYPE_CHECKING` blocks
|
||||||
|
// (we'd need https://github.com/astral-sh/ty/issues/1553 to do this well)
|
||||||
|
if !is_typing_extensions_available && module.name(&*db) == &typing_extensions {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
s.spawn(move |_| {
|
s.spawn(move |_| {
|
||||||
for (_, symbol) in symbols_for_file_global_only(&*db, file).search(query) {
|
for (_, symbol) in symbols_for_file_global_only(&*db, file).search(query) {
|
||||||
// It seems like we could do better here than
|
// It seems like we could do better here than
|
||||||
|
|
@ -143,7 +156,7 @@ ABCDEFGHIJKLMNOP = 'https://api.example.com'
|
||||||
|
|
||||||
impl CursorTest {
|
impl CursorTest {
|
||||||
fn all_symbols(&self, query: &str) -> String {
|
fn all_symbols(&self, query: &str) -> String {
|
||||||
let symbols = all_symbols(&self.db, &QueryPattern::fuzzy(query));
|
let symbols = all_symbols(&self.db, self.cursor.file, &QueryPattern::fuzzy(query));
|
||||||
|
|
||||||
if symbols.is_empty() {
|
if symbols.is_empty() {
|
||||||
return "No symbols found".to_string();
|
return "No symbols found".to_string();
|
||||||
|
|
|
||||||
|
|
@ -517,7 +517,7 @@ fn add_unimported_completions<'db>(
|
||||||
let importer = Importer::new(db, &stylist, file, source.as_str(), parsed);
|
let importer = Importer::new(db, &stylist, file, source.as_str(), parsed);
|
||||||
let members = importer.members_in_scope_at(scoped.node, scoped.node.start());
|
let members = importer.members_in_scope_at(scoped.node, scoped.node.start());
|
||||||
|
|
||||||
for symbol in all_symbols(db, &completions.query) {
|
for symbol in all_symbols(db, file, &completions.query) {
|
||||||
if symbol.module.file(db) == Some(file) || symbol.module.is_known(db, KnownModule::Builtins)
|
if symbol.module.file(db) == Some(file) || symbol.module.is_known(db, KnownModule::Builtins)
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
|
|
@ -5566,10 +5566,7 @@ def foo(param: s<CURSOR>)
|
||||||
#[test]
|
#[test]
|
||||||
fn from_import_no_space_not_suggests_import() {
|
fn from_import_no_space_not_suggests_import() {
|
||||||
let builder = completion_test_builder("from typing<CURSOR>");
|
let builder = completion_test_builder("from typing<CURSOR>");
|
||||||
assert_snapshot!(builder.build().snapshot(), @r"
|
assert_snapshot!(builder.build().snapshot(), @"typing");
|
||||||
typing
|
|
||||||
typing_extensions
|
|
||||||
");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
@ -5785,6 +5782,86 @@ from .imp<CURSOR>
|
||||||
");
|
");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn typing_extensions_excluded_from_import() {
|
||||||
|
let builder = completion_test_builder("from typing<CURSOR>").module_names();
|
||||||
|
assert_snapshot!(builder.build().snapshot(), @"typing :: Current module");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn typing_extensions_excluded_from_auto_import() {
|
||||||
|
let builder = completion_test_builder("deprecated<CURSOR>")
|
||||||
|
.auto_import()
|
||||||
|
.module_names();
|
||||||
|
assert_snapshot!(builder.build().snapshot(), @r"
|
||||||
|
Deprecated :: importlib.metadata
|
||||||
|
DeprecatedList :: importlib.metadata
|
||||||
|
DeprecatedNonAbstract :: importlib.metadata
|
||||||
|
DeprecatedTuple :: importlib.metadata
|
||||||
|
deprecated :: warnings
|
||||||
|
");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn typing_extensions_included_from_import() {
|
||||||
|
let builder = CursorTest::builder()
|
||||||
|
.source("typing_extensions.py", "deprecated = 1")
|
||||||
|
.source("foo.py", "from typing<CURSOR>")
|
||||||
|
.completion_test_builder()
|
||||||
|
.module_names();
|
||||||
|
assert_snapshot!(builder.build().snapshot(), @r"
|
||||||
|
typing :: Current module
|
||||||
|
typing_extensions :: Current module
|
||||||
|
");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn typing_extensions_included_from_auto_import() {
|
||||||
|
let builder = CursorTest::builder()
|
||||||
|
.source("typing_extensions.py", "deprecated = 1")
|
||||||
|
.source("foo.py", "deprecated<CURSOR>")
|
||||||
|
.completion_test_builder()
|
||||||
|
.auto_import()
|
||||||
|
.module_names();
|
||||||
|
assert_snapshot!(builder.build().snapshot(), @r"
|
||||||
|
Deprecated :: importlib.metadata
|
||||||
|
DeprecatedList :: importlib.metadata
|
||||||
|
DeprecatedNonAbstract :: importlib.metadata
|
||||||
|
DeprecatedTuple :: importlib.metadata
|
||||||
|
deprecated :: typing_extensions
|
||||||
|
deprecated :: warnings
|
||||||
|
");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn typing_extensions_included_from_import_in_stub() {
|
||||||
|
let builder = CursorTest::builder()
|
||||||
|
.source("foo.pyi", "from typing<CURSOR>")
|
||||||
|
.completion_test_builder()
|
||||||
|
.module_names();
|
||||||
|
assert_snapshot!(builder.build().snapshot(), @r"
|
||||||
|
typing :: Current module
|
||||||
|
typing_extensions :: Current module
|
||||||
|
");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn typing_extensions_included_from_auto_import_in_stub() {
|
||||||
|
let builder = CursorTest::builder()
|
||||||
|
.source("foo.pyi", "deprecated<CURSOR>")
|
||||||
|
.completion_test_builder()
|
||||||
|
.auto_import()
|
||||||
|
.module_names();
|
||||||
|
assert_snapshot!(builder.build().snapshot(), @r"
|
||||||
|
Deprecated :: importlib.metadata
|
||||||
|
DeprecatedList :: importlib.metadata
|
||||||
|
DeprecatedNonAbstract :: importlib.metadata
|
||||||
|
DeprecatedTuple :: importlib.metadata
|
||||||
|
deprecated :: typing_extensions
|
||||||
|
deprecated :: warnings
|
||||||
|
");
|
||||||
|
}
|
||||||
|
|
||||||
/// A way to create a simple single-file (named `main.py`) completion test
|
/// A way to create a simple single-file (named `main.py`) completion test
|
||||||
/// builder.
|
/// builder.
|
||||||
///
|
///
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,8 @@ pub use diagnostic::add_inferred_python_version_hint_to_diagnostic;
|
||||||
pub use module_name::{ModuleName, ModuleNameResolutionError};
|
pub use module_name::{ModuleName, ModuleNameResolutionError};
|
||||||
pub use module_resolver::{
|
pub use module_resolver::{
|
||||||
KnownModule, Module, SearchPath, SearchPathValidationError, SearchPaths, all_modules,
|
KnownModule, Module, SearchPath, SearchPathValidationError, SearchPaths, all_modules,
|
||||||
list_modules, resolve_module, resolve_real_module, system_module_search_paths,
|
list_modules, resolve_module, resolve_real_module, resolve_real_shadowable_module,
|
||||||
|
system_module_search_paths,
|
||||||
};
|
};
|
||||||
pub use program::{
|
pub use program::{
|
||||||
Program, ProgramSettings, PythonVersionFileSource, PythonVersionSource,
|
Program, ProgramSettings, PythonVersionFileSource, PythonVersionSource,
|
||||||
|
|
|
||||||
|
|
@ -8,9 +8,7 @@ use crate::program::Program;
|
||||||
|
|
||||||
use super::module::{Module, ModuleKind};
|
use super::module::{Module, ModuleKind};
|
||||||
use super::path::{ModulePath, SearchPath, SystemOrVendoredPathRef};
|
use super::path::{ModulePath, SearchPath, SystemOrVendoredPathRef};
|
||||||
use super::resolver::{
|
use super::resolver::{ModuleResolveMode, ResolverContext, resolve_file_module, search_paths};
|
||||||
ModuleResolveMode, ResolverContext, is_non_shadowable, resolve_file_module, search_paths,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// List all available modules, including all sub-modules, sorted in lexicographic order.
|
/// List all available modules, including all sub-modules, sorted in lexicographic order.
|
||||||
pub fn all_modules(db: &dyn Db) -> Vec<Module<'_>> {
|
pub fn all_modules(db: &dyn Db) -> Vec<Module<'_>> {
|
||||||
|
|
@ -309,7 +307,8 @@ impl<'db> Lister<'db> {
|
||||||
|
|
||||||
/// Returns true if the given module name cannot be shadowable.
|
/// Returns true if the given module name cannot be shadowable.
|
||||||
fn is_non_shadowable(&self, name: &ModuleName) -> bool {
|
fn is_non_shadowable(&self, name: &ModuleName) -> bool {
|
||||||
is_non_shadowable(self.python_version().minor, name.as_str())
|
ModuleResolveMode::StubsAllowed
|
||||||
|
.is_non_shadowable(self.python_version().minor, name.as_str())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the Python version we want to perform module resolution
|
/// Returns the Python version we want to perform module resolution
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ pub use module::Module;
|
||||||
pub use path::{SearchPath, SearchPathValidationError};
|
pub use path::{SearchPath, SearchPathValidationError};
|
||||||
pub use resolver::SearchPaths;
|
pub use resolver::SearchPaths;
|
||||||
pub(crate) use resolver::file_to_module;
|
pub(crate) use resolver::file_to_module;
|
||||||
pub use resolver::{resolve_module, resolve_real_module};
|
pub use resolver::{resolve_module, resolve_real_module, resolve_real_shadowable_module};
|
||||||
use ruff_db::system::SystemPath;
|
use ruff_db::system::SystemPath;
|
||||||
|
|
||||||
use crate::Db;
|
use crate::Db;
|
||||||
|
|
|
||||||
|
|
@ -47,8 +47,33 @@ pub fn resolve_real_module<'db>(db: &'db dyn Db, module_name: &ModuleName) -> Op
|
||||||
resolve_module_query(db, interned_name)
|
resolve_module_query(db, interned_name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Resolves a module name to a module (stubs not allowed, some shadowing is
|
||||||
|
/// allowed).
|
||||||
|
///
|
||||||
|
/// In particular, this allows `typing_extensions` to be shadowed by a
|
||||||
|
/// non-standard library module. This is useful in the context of the LSP
|
||||||
|
/// where we don't want to pretend as if these modules are always available at
|
||||||
|
/// runtime.
|
||||||
|
///
|
||||||
|
/// This should generally only be used within the context of the LSP. Using it
|
||||||
|
/// within ty proper risks being unable to resolve builtin modules since they
|
||||||
|
/// are involved in an import cycle with `builtins`.
|
||||||
|
pub fn resolve_real_shadowable_module<'db>(
|
||||||
|
db: &'db dyn Db,
|
||||||
|
module_name: &ModuleName,
|
||||||
|
) -> Option<Module<'db>> {
|
||||||
|
let interned_name = ModuleNameIngredient::new(
|
||||||
|
db,
|
||||||
|
module_name,
|
||||||
|
ModuleResolveMode::StubsNotAllowedSomeShadowingAllowed,
|
||||||
|
);
|
||||||
|
|
||||||
|
resolve_module_query(db, interned_name)
|
||||||
|
}
|
||||||
|
|
||||||
/// Which files should be visible when doing a module query
|
/// Which files should be visible when doing a module query
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, get_size2::GetSize)]
|
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, get_size2::GetSize)]
|
||||||
|
#[allow(clippy::enum_variant_names)]
|
||||||
pub(crate) enum ModuleResolveMode {
|
pub(crate) enum ModuleResolveMode {
|
||||||
/// Stubs are allowed to appear.
|
/// Stubs are allowed to appear.
|
||||||
///
|
///
|
||||||
|
|
@ -61,6 +86,13 @@ pub(crate) enum ModuleResolveMode {
|
||||||
/// implementations. When querying searchpaths this also notably replaces typeshed with
|
/// implementations. When querying searchpaths this also notably replaces typeshed with
|
||||||
/// the "real" stdlib.
|
/// the "real" stdlib.
|
||||||
StubsNotAllowed,
|
StubsNotAllowed,
|
||||||
|
/// Like `StubsNotAllowed`, but permits some modules to be shadowed.
|
||||||
|
///
|
||||||
|
/// In particular, this allows `typing_extensions` to be shadowed by a
|
||||||
|
/// non-standard library module. This is useful in the context of the LSP
|
||||||
|
/// where we don't want to pretend as if these modules are always available
|
||||||
|
/// at runtime.
|
||||||
|
StubsNotAllowedSomeShadowingAllowed,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[salsa::interned(heap_size=ruff_memory_usage::heap_size)]
|
#[salsa::interned(heap_size=ruff_memory_usage::heap_size)]
|
||||||
|
|
@ -73,6 +105,39 @@ impl ModuleResolveMode {
|
||||||
fn stubs_allowed(self) -> bool {
|
fn stubs_allowed(self) -> bool {
|
||||||
matches!(self, Self::StubsAllowed)
|
matches!(self, Self::StubsAllowed)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns `true` if the module name refers to a standard library module
|
||||||
|
/// which can't be shadowed by a first-party module.
|
||||||
|
///
|
||||||
|
/// This includes "builtin" modules, which can never be shadowed at runtime
|
||||||
|
/// either. Additionally, certain other modules that are involved in an
|
||||||
|
/// import cycle with `builtins` (`types`, `typing_extensions`, etc.) are
|
||||||
|
/// also considered non-shadowable, unless the module resolution mode
|
||||||
|
/// specifically opts into allowing some of them to be shadowed. This
|
||||||
|
/// latter set of modules cannot be allowed to be shadowed by first-party
|
||||||
|
/// or "extra-path" modules in ty proper, or we risk panics in unexpected
|
||||||
|
/// places due to being unable to resolve builtin symbols. This is similar
|
||||||
|
/// behaviour to other type checkers such as mypy:
|
||||||
|
/// <https://github.com/python/mypy/blob/3807423e9d98e678bf16b13ec8b4f909fe181908/mypy/build.py#L104-L117>
|
||||||
|
pub(super) fn is_non_shadowable(self, minor_version: u8, module_name: &str) -> bool {
|
||||||
|
// Builtin modules are never shadowable, no matter what.
|
||||||
|
if ruff_python_stdlib::sys::is_builtin_module(minor_version, module_name) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// Similarly for `types`, which is always available at runtime.
|
||||||
|
if module_name == "types" {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise, some modules should only be conditionally allowed
|
||||||
|
// to be shadowed, depending on the module resolution mode.
|
||||||
|
match self {
|
||||||
|
ModuleResolveMode::StubsAllowed | ModuleResolveMode::StubsNotAllowed => {
|
||||||
|
module_name == "typing_extensions"
|
||||||
|
}
|
||||||
|
ModuleResolveMode::StubsNotAllowedSomeShadowingAllowed => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Salsa query that resolves an interned [`ModuleNameIngredient`] to a module.
|
/// Salsa query that resolves an interned [`ModuleNameIngredient`] to a module.
|
||||||
|
|
@ -386,7 +451,10 @@ impl SearchPaths {
|
||||||
pub(crate) fn stdlib(&self, mode: ModuleResolveMode) -> Option<&SearchPath> {
|
pub(crate) fn stdlib(&self, mode: ModuleResolveMode) -> Option<&SearchPath> {
|
||||||
match mode {
|
match mode {
|
||||||
ModuleResolveMode::StubsAllowed => self.stdlib_path.as_ref(),
|
ModuleResolveMode::StubsAllowed => self.stdlib_path.as_ref(),
|
||||||
ModuleResolveMode::StubsNotAllowed => self.real_stdlib_path.as_ref(),
|
ModuleResolveMode::StubsNotAllowed
|
||||||
|
| ModuleResolveMode::StubsNotAllowedSomeShadowingAllowed => {
|
||||||
|
self.real_stdlib_path.as_ref()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -439,7 +507,8 @@ pub(crate) fn dynamic_resolution_paths<'db>(
|
||||||
// Use the `ModuleResolveMode` to determine which stdlib (if any) to mark as existing
|
// Use the `ModuleResolveMode` to determine which stdlib (if any) to mark as existing
|
||||||
let stdlib = match mode.mode(db) {
|
let stdlib = match mode.mode(db) {
|
||||||
ModuleResolveMode::StubsAllowed => stdlib_path,
|
ModuleResolveMode::StubsAllowed => stdlib_path,
|
||||||
ModuleResolveMode::StubsNotAllowed => real_stdlib_path,
|
ModuleResolveMode::StubsNotAllowed
|
||||||
|
| ModuleResolveMode::StubsNotAllowedSomeShadowingAllowed => real_stdlib_path,
|
||||||
};
|
};
|
||||||
if let Some(path) = stdlib.as_ref().and_then(SearchPath::as_system_path) {
|
if let Some(path) = stdlib.as_ref().and_then(SearchPath::as_system_path) {
|
||||||
existing_paths.insert(Cow::Borrowed(path));
|
existing_paths.insert(Cow::Borrowed(path));
|
||||||
|
|
@ -684,27 +753,13 @@ struct ModuleNameIngredient<'db> {
|
||||||
pub(super) mode: ModuleResolveMode,
|
pub(super) mode: ModuleResolveMode,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns `true` if the module name refers to a standard library module which can't be shadowed
|
|
||||||
/// by a first-party module.
|
|
||||||
///
|
|
||||||
/// This includes "builtin" modules, which can never be shadowed at runtime either, as well as
|
|
||||||
/// certain other modules that are involved in an import cycle with `builtins` (`types`,
|
|
||||||
/// `typing_extensions`, etc.). This latter set of modules cannot be allowed to be shadowed by
|
|
||||||
/// first-party or "extra-path" modules, or we risk panics in unexpected places due to being
|
|
||||||
/// unable to resolve builtin symbols. This is similar behaviour to other type checkers such
|
|
||||||
/// as mypy: <https://github.com/python/mypy/blob/3807423e9d98e678bf16b13ec8b4f909fe181908/mypy/build.py#L104-L117>
|
|
||||||
pub(super) fn is_non_shadowable(minor_version: u8, module_name: &str) -> bool {
|
|
||||||
matches!(module_name, "types" | "typing_extensions")
|
|
||||||
|| ruff_python_stdlib::sys::is_builtin_module(minor_version, module_name)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Given a module name and a list of search paths in which to lookup modules,
|
/// Given a module name and a list of search paths in which to lookup modules,
|
||||||
/// attempt to resolve the module name
|
/// attempt to resolve the module name
|
||||||
fn resolve_name(db: &dyn Db, name: &ModuleName, mode: ModuleResolveMode) -> Option<ResolvedName> {
|
fn resolve_name(db: &dyn Db, name: &ModuleName, mode: ModuleResolveMode) -> Option<ResolvedName> {
|
||||||
let program = Program::get(db);
|
let program = Program::get(db);
|
||||||
let python_version = program.python_version(db);
|
let python_version = program.python_version(db);
|
||||||
let resolver_state = ResolverContext::new(db, python_version, mode);
|
let resolver_state = ResolverContext::new(db, python_version, mode);
|
||||||
let is_non_shadowable = is_non_shadowable(python_version.minor, name.as_str());
|
let is_non_shadowable = mode.is_non_shadowable(python_version.minor, name.as_str());
|
||||||
|
|
||||||
let name = RelaxedModuleName::new(name);
|
let name = RelaxedModuleName::new(name);
|
||||||
let stub_name = name.to_stub_package();
|
let stub_name = name.to_stub_package();
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,6 @@ use ruff_python_parser::Parsed;
|
||||||
use ruff_source_file::LineIndex;
|
use ruff_source_file::LineIndex;
|
||||||
use rustc_hash::FxHashMap;
|
use rustc_hash::FxHashMap;
|
||||||
|
|
||||||
use crate::Db;
|
|
||||||
use crate::module_name::ModuleName;
|
use crate::module_name::ModuleName;
|
||||||
use crate::module_resolver::{KnownModule, Module, list_modules, resolve_module};
|
use crate::module_resolver::{KnownModule, Module, list_modules, resolve_module};
|
||||||
use crate::semantic_index::definition::Definition;
|
use crate::semantic_index::definition::Definition;
|
||||||
|
|
@ -14,6 +13,7 @@ use crate::semantic_index::scope::FileScopeId;
|
||||||
use crate::semantic_index::semantic_index;
|
use crate::semantic_index::semantic_index;
|
||||||
use crate::types::ide_support::{Member, all_declarations_and_bindings, all_members};
|
use crate::types::ide_support::{Member, all_declarations_and_bindings, all_members};
|
||||||
use crate::types::{Type, binding_type, infer_scope_types};
|
use crate::types::{Type, binding_type, infer_scope_types};
|
||||||
|
use crate::{Db, resolve_real_shadowable_module};
|
||||||
|
|
||||||
/// The primary interface the LSP should use for querying semantic information about a [`File`].
|
/// The primary interface the LSP should use for querying semantic information about a [`File`].
|
||||||
///
|
///
|
||||||
|
|
@ -105,8 +105,14 @@ impl<'db> SemanticModel<'db> {
|
||||||
|
|
||||||
/// Returns completions for symbols available in a `import <CURSOR>` context.
|
/// Returns completions for symbols available in a `import <CURSOR>` context.
|
||||||
pub fn import_completions(&self) -> Vec<Completion<'db>> {
|
pub fn import_completions(&self) -> Vec<Completion<'db>> {
|
||||||
|
let typing_extensions = ModuleName::new("typing_extensions").unwrap();
|
||||||
|
let is_typing_extensions_available = self.file.is_stub(self.db)
|
||||||
|
|| resolve_real_shadowable_module(self.db, &typing_extensions).is_some();
|
||||||
list_modules(self.db)
|
list_modules(self.db)
|
||||||
.into_iter()
|
.into_iter()
|
||||||
|
.filter(|module| {
|
||||||
|
is_typing_extensions_available || module.name(self.db) != &typing_extensions
|
||||||
|
})
|
||||||
.map(|module| {
|
.map(|module| {
|
||||||
let builtin = module.is_known(self.db, KnownModule::Builtins);
|
let builtin = module.is_known(self.db, KnownModule::Builtins);
|
||||||
let ty = Type::module_literal(self.db, self.file, module);
|
let ty = Type::module_literal(self.db, self.file, module);
|
||||||
|
|
|
||||||
|
|
@ -3,52 +3,6 @@ source: crates/ty_server/tests/e2e/code_actions.rs
|
||||||
expression: code_actions
|
expression: code_actions
|
||||||
---
|
---
|
||||||
[
|
[
|
||||||
{
|
|
||||||
"title": "import typing_extensions.deprecated",
|
|
||||||
"kind": "quickfix",
|
|
||||||
"diagnostics": [
|
|
||||||
{
|
|
||||||
"range": {
|
|
||||||
"start": {
|
|
||||||
"line": 1,
|
|
||||||
"character": 1
|
|
||||||
},
|
|
||||||
"end": {
|
|
||||||
"line": 1,
|
|
||||||
"character": 11
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"severity": 1,
|
|
||||||
"code": "unresolved-reference",
|
|
||||||
"codeDescription": {
|
|
||||||
"href": "https://ty.dev/rules#unresolved-reference"
|
|
||||||
},
|
|
||||||
"source": "ty",
|
|
||||||
"message": "Name `deprecated` used when not defined",
|
|
||||||
"relatedInformation": []
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"edit": {
|
|
||||||
"changes": {
|
|
||||||
"file://<temp_dir>/src/foo.py": [
|
|
||||||
{
|
|
||||||
"range": {
|
|
||||||
"start": {
|
|
||||||
"line": 0,
|
|
||||||
"character": 0
|
|
||||||
},
|
|
||||||
"end": {
|
|
||||||
"line": 0,
|
|
||||||
"character": 0
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"newText": "from typing_extensions import deprecated\n"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"isPreferred": true
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"title": "import warnings.deprecated",
|
"title": "import warnings.deprecated",
|
||||||
"kind": "quickfix",
|
"kind": "quickfix",
|
||||||
|
|
|
||||||
|
|
@ -49,52 +49,6 @@ expression: code_actions
|
||||||
},
|
},
|
||||||
"isPreferred": true
|
"isPreferred": true
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"title": "import typing_extensions.Literal",
|
|
||||||
"kind": "quickfix",
|
|
||||||
"diagnostics": [
|
|
||||||
{
|
|
||||||
"range": {
|
|
||||||
"start": {
|
|
||||||
"line": 0,
|
|
||||||
"character": 3
|
|
||||||
},
|
|
||||||
"end": {
|
|
||||||
"line": 0,
|
|
||||||
"character": 10
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"severity": 1,
|
|
||||||
"code": "unresolved-reference",
|
|
||||||
"codeDescription": {
|
|
||||||
"href": "https://ty.dev/rules#unresolved-reference"
|
|
||||||
},
|
|
||||||
"source": "ty",
|
|
||||||
"message": "Name `Literal` used when not defined",
|
|
||||||
"relatedInformation": []
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"edit": {
|
|
||||||
"changes": {
|
|
||||||
"file://<temp_dir>/src/foo.py": [
|
|
||||||
{
|
|
||||||
"range": {
|
|
||||||
"start": {
|
|
||||||
"line": 0,
|
|
||||||
"character": 0
|
|
||||||
},
|
|
||||||
"end": {
|
|
||||||
"line": 0,
|
|
||||||
"character": 0
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"newText": "from typing_extensions import Literal\n"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"isPreferred": true
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"title": "Ignore 'unresolved-reference' for this line",
|
"title": "Ignore 'unresolved-reference' for this line",
|
||||||
"kind": "quickfix",
|
"kind": "quickfix",
|
||||||
|
|
|
||||||
|
|
@ -24,31 +24,10 @@ expression: completions
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"label": "Literal (import typing_extensions)",
|
|
||||||
"kind": 6,
|
|
||||||
"sortText": " 51",
|
|
||||||
"insertText": "Literal",
|
|
||||||
"additionalTextEdits": [
|
|
||||||
{
|
|
||||||
"range": {
|
|
||||||
"start": {
|
|
||||||
"line": 1,
|
|
||||||
"character": 0
|
|
||||||
},
|
|
||||||
"end": {
|
|
||||||
"line": 1,
|
|
||||||
"character": 0
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"newText": "from typing_extensions import Literal\n"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"label": "LiteralString (import typing)",
|
"label": "LiteralString (import typing)",
|
||||||
"kind": 6,
|
"kind": 6,
|
||||||
"sortText": " 52",
|
"sortText": " 51",
|
||||||
"insertText": "LiteralString",
|
"insertText": "LiteralString",
|
||||||
"additionalTextEdits": [
|
"additionalTextEdits": [
|
||||||
{
|
{
|
||||||
|
|
@ -65,26 +44,5 @@ expression: completions
|
||||||
"newText": "from typing import LiteralString\n"
|
"newText": "from typing import LiteralString\n"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
|
||||||
{
|
|
||||||
"label": "LiteralString (import typing_extensions)",
|
|
||||||
"kind": 6,
|
|
||||||
"sortText": " 53",
|
|
||||||
"insertText": "LiteralString",
|
|
||||||
"additionalTextEdits": [
|
|
||||||
{
|
|
||||||
"range": {
|
|
||||||
"start": {
|
|
||||||
"line": 1,
|
|
||||||
"character": 0
|
|
||||||
},
|
|
||||||
"end": {
|
|
||||||
"line": 1,
|
|
||||||
"character": 0
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"newText": "from typing_extensions import LiteralString\n"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -24,31 +24,10 @@ expression: completions
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"label": "Literal (import typing_extensions)",
|
|
||||||
"kind": 6,
|
|
||||||
"sortText": " 51",
|
|
||||||
"insertText": "Literal",
|
|
||||||
"additionalTextEdits": [
|
|
||||||
{
|
|
||||||
"range": {
|
|
||||||
"start": {
|
|
||||||
"line": 1,
|
|
||||||
"character": 0
|
|
||||||
},
|
|
||||||
"end": {
|
|
||||||
"line": 1,
|
|
||||||
"character": 0
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"newText": "from typing_extensions import Literal\n"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"label": "LiteralString (import typing)",
|
"label": "LiteralString (import typing)",
|
||||||
"kind": 6,
|
"kind": 6,
|
||||||
"sortText": " 52",
|
"sortText": " 51",
|
||||||
"insertText": "LiteralString",
|
"insertText": "LiteralString",
|
||||||
"additionalTextEdits": [
|
"additionalTextEdits": [
|
||||||
{
|
{
|
||||||
|
|
@ -65,26 +44,5 @@ expression: completions
|
||||||
"newText": "from typing import LiteralString\n"
|
"newText": "from typing import LiteralString\n"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
|
||||||
{
|
|
||||||
"label": "LiteralString (import typing_extensions)",
|
|
||||||
"kind": 6,
|
|
||||||
"sortText": " 53",
|
|
||||||
"insertText": "LiteralString",
|
|
||||||
"additionalTextEdits": [
|
|
||||||
{
|
|
||||||
"range": {
|
|
||||||
"start": {
|
|
||||||
"line": 1,
|
|
||||||
"character": 0
|
|
||||||
},
|
|
||||||
"end": {
|
|
||||||
"line": 1,
|
|
||||||
"character": 0
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"newText": "from typing_extensions import LiteralString\n"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -24,31 +24,10 @@ expression: completions
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"label": "Literal (import typing_extensions)",
|
|
||||||
"kind": 6,
|
|
||||||
"sortText": " 51",
|
|
||||||
"insertText": "Literal",
|
|
||||||
"additionalTextEdits": [
|
|
||||||
{
|
|
||||||
"range": {
|
|
||||||
"start": {
|
|
||||||
"line": 1,
|
|
||||||
"character": 0
|
|
||||||
},
|
|
||||||
"end": {
|
|
||||||
"line": 1,
|
|
||||||
"character": 0
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"newText": "from typing_extensions import Literal\n"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"label": "LiteralString (import typing)",
|
"label": "LiteralString (import typing)",
|
||||||
"kind": 6,
|
"kind": 6,
|
||||||
"sortText": " 52",
|
"sortText": " 51",
|
||||||
"insertText": "LiteralString",
|
"insertText": "LiteralString",
|
||||||
"additionalTextEdits": [
|
"additionalTextEdits": [
|
||||||
{
|
{
|
||||||
|
|
@ -65,26 +44,5 @@ expression: completions
|
||||||
"newText": "from typing import LiteralString\n"
|
"newText": "from typing import LiteralString\n"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
|
||||||
{
|
|
||||||
"label": "LiteralString (import typing_extensions)",
|
|
||||||
"kind": 6,
|
|
||||||
"sortText": " 53",
|
|
||||||
"insertText": "LiteralString",
|
|
||||||
"additionalTextEdits": [
|
|
||||||
{
|
|
||||||
"range": {
|
|
||||||
"start": {
|
|
||||||
"line": 1,
|
|
||||||
"character": 0
|
|
||||||
},
|
|
||||||
"end": {
|
|
||||||
"line": 1,
|
|
||||||
"character": 0
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"newText": "from typing_extensions import LiteralString\n"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -24,31 +24,10 @@ expression: completions
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"label": "Literal (import typing_extensions)",
|
|
||||||
"kind": 6,
|
|
||||||
"sortText": " 51",
|
|
||||||
"insertText": "Literal",
|
|
||||||
"additionalTextEdits": [
|
|
||||||
{
|
|
||||||
"range": {
|
|
||||||
"start": {
|
|
||||||
"line": 0,
|
|
||||||
"character": 0
|
|
||||||
},
|
|
||||||
"end": {
|
|
||||||
"line": 0,
|
|
||||||
"character": 0
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"newText": "from typing_extensions import Literal\n"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"label": "LiteralString (import typing)",
|
"label": "LiteralString (import typing)",
|
||||||
"kind": 6,
|
"kind": 6,
|
||||||
"sortText": " 52",
|
"sortText": " 51",
|
||||||
"insertText": "LiteralString",
|
"insertText": "LiteralString",
|
||||||
"additionalTextEdits": [
|
"additionalTextEdits": [
|
||||||
{
|
{
|
||||||
|
|
@ -65,26 +44,5 @@ expression: completions
|
||||||
"newText": ", LiteralString"
|
"newText": ", LiteralString"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
|
||||||
{
|
|
||||||
"label": "LiteralString (import typing_extensions)",
|
|
||||||
"kind": 6,
|
|
||||||
"sortText": " 53",
|
|
||||||
"insertText": "LiteralString",
|
|
||||||
"additionalTextEdits": [
|
|
||||||
{
|
|
||||||
"range": {
|
|
||||||
"start": {
|
|
||||||
"line": 0,
|
|
||||||
"character": 0
|
|
||||||
},
|
|
||||||
"end": {
|
|
||||||
"line": 0,
|
|
||||||
"character": 0
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"newText": "from typing_extensions import LiteralString\n"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue