mirror of
https://github.com/astral-sh/ruff.git
synced 2025-09-30 13:51:16 +00:00
[ty] Expand API of all_members
to return a struct
This commit doesn't change any behavior, but makes it so `all_members` returns a `Vec<Member>` instead of `Vec<Name>`, where a `Member` contains a `Name`. This gives us an expansion point to include other data (such as the type of the `Name`).
This commit is contained in:
parent
f7234cb474
commit
79fe538458
4 changed files with 54 additions and 25 deletions
|
@ -10,7 +10,7 @@ use ty_python_semantic::{Completion, NameKind, SemanticModel};
|
||||||
use crate::Db;
|
use crate::Db;
|
||||||
use crate::find_node::covering_node;
|
use crate::find_node::covering_node;
|
||||||
|
|
||||||
pub fn completion(db: &dyn Db, file: File, offset: TextSize) -> Vec<Completion> {
|
pub fn completion(db: &dyn Db, file: File, offset: TextSize) -> Vec<Completion<'_>> {
|
||||||
let parsed = parsed_module(db, file).load(db);
|
let parsed = parsed_module(db, file).load(db);
|
||||||
|
|
||||||
let Some(target_token) = CompletionTargetTokens::find(&parsed, offset) else {
|
let Some(target_token) = CompletionTargetTokens::find(&parsed, offset) else {
|
||||||
|
|
|
@ -45,7 +45,7 @@ impl<'db> SemanticModel<'db> {
|
||||||
&self,
|
&self,
|
||||||
import: &ast::StmtImportFrom,
|
import: &ast::StmtImportFrom,
|
||||||
_name: Option<usize>,
|
_name: Option<usize>,
|
||||||
) -> Vec<Completion> {
|
) -> Vec<Completion<'db>> {
|
||||||
let module_name = match ModuleName::from_import_statement(self.db, self.file, import) {
|
let module_name = match ModuleName::from_import_statement(self.db, self.file, import) {
|
||||||
Ok(module_name) => module_name,
|
Ok(module_name) => module_name,
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
|
@ -62,7 +62,7 @@ impl<'db> SemanticModel<'db> {
|
||||||
|
|
||||||
/// Returns completions for symbols available in the given module as if
|
/// Returns completions for symbols available in the given module as if
|
||||||
/// it were imported by this model's `File`.
|
/// it were imported by this model's `File`.
|
||||||
fn module_completions(&self, module_name: &ModuleName) -> Vec<Completion> {
|
fn module_completions(&self, module_name: &ModuleName) -> Vec<Completion<'db>> {
|
||||||
let Some(module) = resolve_module(self.db, module_name) else {
|
let Some(module) = resolve_module(self.db, module_name) else {
|
||||||
tracing::debug!("Could not resolve module from `{module_name:?}`");
|
tracing::debug!("Could not resolve module from `{module_name:?}`");
|
||||||
return vec![];
|
return vec![];
|
||||||
|
@ -71,17 +71,22 @@ impl<'db> SemanticModel<'db> {
|
||||||
let builtin = module.is_known(KnownModule::Builtins);
|
let builtin = module.is_known(KnownModule::Builtins);
|
||||||
crate::types::all_members(self.db, ty)
|
crate::types::all_members(self.db, ty)
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|name| Completion { name, builtin })
|
.map(|member| Completion {
|
||||||
|
name: member.name,
|
||||||
|
ty: None,
|
||||||
|
builtin,
|
||||||
|
})
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns completions for symbols available in a `object.<CURSOR>` context.
|
/// Returns completions for symbols available in a `object.<CURSOR>` context.
|
||||||
pub fn attribute_completions(&self, node: &ast::ExprAttribute) -> Vec<Completion> {
|
pub fn attribute_completions(&self, node: &ast::ExprAttribute) -> Vec<Completion<'db>> {
|
||||||
let ty = node.value.inferred_type(self);
|
let ty = node.value.inferred_type(self);
|
||||||
crate::types::all_members(self.db, ty)
|
crate::types::all_members(self.db, ty)
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|name| Completion {
|
.map(|member| Completion {
|
||||||
name,
|
name: member.name,
|
||||||
|
ty: None,
|
||||||
builtin: false,
|
builtin: false,
|
||||||
})
|
})
|
||||||
.collect()
|
.collect()
|
||||||
|
@ -92,7 +97,7 @@ impl<'db> SemanticModel<'db> {
|
||||||
///
|
///
|
||||||
/// If a scope could not be determined, then completions for the global
|
/// If a scope could not be determined, then completions for the global
|
||||||
/// scope of this model's `File` are returned.
|
/// scope of this model's `File` are returned.
|
||||||
pub fn scoped_completions(&self, node: ast::AnyNodeRef<'_>) -> Vec<Completion> {
|
pub fn scoped_completions(&self, node: ast::AnyNodeRef<'_>) -> Vec<Completion<'db>> {
|
||||||
let index = semantic_index(self.db, self.file);
|
let index = semantic_index(self.db, self.file);
|
||||||
|
|
||||||
// TODO: We currently use `try_expression_scope_id` here as a hotfix for [1].
|
// TODO: We currently use `try_expression_scope_id` here as a hotfix for [1].
|
||||||
|
@ -115,8 +120,9 @@ impl<'db> SemanticModel<'db> {
|
||||||
for (file_scope, _) in index.ancestor_scopes(file_scope) {
|
for (file_scope, _) in index.ancestor_scopes(file_scope) {
|
||||||
completions.extend(
|
completions.extend(
|
||||||
all_declarations_and_bindings(self.db, file_scope.to_scope_id(self.db, self.file))
|
all_declarations_and_bindings(self.db, file_scope.to_scope_id(self.db, self.file))
|
||||||
.map(|name| Completion {
|
.map(|member| Completion {
|
||||||
name,
|
name: member.name,
|
||||||
|
ty: None,
|
||||||
builtin: false,
|
builtin: false,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
@ -163,9 +169,11 @@ impl NameKind {
|
||||||
|
|
||||||
/// A suggestion for code completion.
|
/// A suggestion for code completion.
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct Completion {
|
pub struct Completion<'db> {
|
||||||
/// The label shown to the user for this suggestion.
|
/// The label shown to the user for this suggestion.
|
||||||
pub name: Name,
|
pub name: Name,
|
||||||
|
/// The type of this completion, if available.
|
||||||
|
pub ty: Option<Type<'db>>,
|
||||||
/// Whether this suggestion came from builtins or not.
|
/// Whether this suggestion came from builtins or not.
|
||||||
///
|
///
|
||||||
/// At time of writing (2025-06-26), this information
|
/// At time of writing (2025-06-26), this information
|
||||||
|
|
|
@ -668,7 +668,7 @@ impl<'db> Bindings<'db> {
|
||||||
ide_support::all_members(db, *ty)
|
ide_support::all_members(db, *ty)
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.sorted()
|
.sorted()
|
||||||
.map(|member| Type::string_literal(db, &member)),
|
.map(|member| Type::string_literal(db, &member.name)),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,7 @@ use rustc_hash::FxHashSet;
|
||||||
pub(crate) fn all_declarations_and_bindings<'db>(
|
pub(crate) fn all_declarations_and_bindings<'db>(
|
||||||
db: &'db dyn Db,
|
db: &'db dyn Db,
|
||||||
scope_id: ScopeId<'db>,
|
scope_id: ScopeId<'db>,
|
||||||
) -> impl Iterator<Item = Name> + 'db {
|
) -> impl Iterator<Item = Member> + 'db {
|
||||||
let use_def_map = use_def_map(db, scope_id);
|
let use_def_map = use_def_map(db, scope_id);
|
||||||
let table = place_table(db, scope_id);
|
let table = place_table(db, scope_id);
|
||||||
|
|
||||||
|
@ -24,10 +24,13 @@ pub(crate) fn all_declarations_and_bindings<'db>(
|
||||||
place_from_declarations(db, declarations)
|
place_from_declarations(db, declarations)
|
||||||
.ok()
|
.ok()
|
||||||
.and_then(|result| {
|
.and_then(|result| {
|
||||||
result
|
result.place.ignore_possibly_unbound().and_then(|_| {
|
||||||
.place
|
table
|
||||||
.ignore_possibly_unbound()
|
.place_expr(symbol_id)
|
||||||
.and_then(|_| table.place_expr(symbol_id).as_name().cloned())
|
.as_name()
|
||||||
|
.cloned()
|
||||||
|
.map(|name| Member { name })
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.chain(
|
.chain(
|
||||||
|
@ -36,13 +39,19 @@ pub(crate) fn all_declarations_and_bindings<'db>(
|
||||||
.filter_map(move |(symbol_id, bindings)| {
|
.filter_map(move |(symbol_id, bindings)| {
|
||||||
place_from_bindings(db, bindings)
|
place_from_bindings(db, bindings)
|
||||||
.ignore_possibly_unbound()
|
.ignore_possibly_unbound()
|
||||||
.and_then(|_| table.place_expr(symbol_id).as_name().cloned())
|
.and_then(|_| {
|
||||||
|
table
|
||||||
|
.place_expr(symbol_id)
|
||||||
|
.as_name()
|
||||||
|
.cloned()
|
||||||
|
.map(|name| Member { name })
|
||||||
|
})
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
struct AllMembers {
|
struct AllMembers {
|
||||||
members: FxHashSet<Name>,
|
members: FxHashSet<Member>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AllMembers {
|
impl AllMembers {
|
||||||
|
@ -180,8 +189,9 @@ impl AllMembers {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.members
|
self.members.insert(Member {
|
||||||
.insert(place_table.place_expr(symbol_id).expect_name().clone());
|
name: place_table.place_expr(symbol_id).expect_name().clone(),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
let module_name = module.name();
|
let module_name = module.name();
|
||||||
|
@ -190,7 +200,9 @@ impl AllMembers {
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|submodule_name| submodule_name.relative_to(module_name))
|
.filter_map(|submodule_name| submodule_name.relative_to(module_name))
|
||||||
.filter_map(|relative_submodule_name| {
|
.filter_map(|relative_submodule_name| {
|
||||||
Some(Name::from(relative_submodule_name.components().next()?))
|
Some(Member {
|
||||||
|
name: Name::from(relative_submodule_name.components().next()?),
|
||||||
|
})
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -232,16 +244,25 @@ impl AllMembers {
|
||||||
let index = semantic_index(db, file);
|
let index = semantic_index(db, file);
|
||||||
for function_scope_id in attribute_scopes(db, class_body_scope) {
|
for function_scope_id in attribute_scopes(db, class_body_scope) {
|
||||||
let place_table = index.place_table(function_scope_id);
|
let place_table = index.place_table(function_scope_id);
|
||||||
self.members
|
self.members.extend(
|
||||||
.extend(place_table.instance_attributes().cloned());
|
place_table
|
||||||
|
.instance_attributes()
|
||||||
|
.cloned()
|
||||||
|
.map(|name| Member { name }),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
|
||||||
|
pub struct Member {
|
||||||
|
pub name: Name,
|
||||||
|
}
|
||||||
|
|
||||||
/// List all members of a given type: anything that would be valid when accessed
|
/// List all members of a given type: anything that would be valid when accessed
|
||||||
/// as an attribute on an object of the given type.
|
/// as an attribute on an object of the given type.
|
||||||
pub fn all_members<'db>(db: &'db dyn Db, ty: Type<'db>) -> FxHashSet<Name> {
|
pub fn all_members<'db>(db: &'db dyn Db, ty: Type<'db>) -> FxHashSet<Member> {
|
||||||
AllMembers::of(db, ty).members
|
AllMembers::of(db, ty).members
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue