mirror of
https://github.com/astral-sh/ruff.git
synced 2025-09-29 05:14:52 +00:00
[ty] guard against recursion in determining completion kind (#20354)
## Summary Fixes https://github.com/astral-sh/ty/issues/1158 ## Test Plan Added test.
This commit is contained in:
parent
5bf6977ded
commit
c4cd5c00fd
2 changed files with 46 additions and 9 deletions
|
@ -551,6 +551,8 @@ mod tests {
|
|||
|
||||
use super::{CompletionSettings, token_suffix_by_kinds};
|
||||
|
||||
use ty_python_semantic::CompletionKind;
|
||||
|
||||
#[test]
|
||||
fn token_suffixes_match() {
|
||||
insta::assert_debug_snapshot!(
|
||||
|
@ -3093,6 +3095,27 @@ from os.<CURSOR>
|
|||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn completion_kind_recursive_type_alias() {
|
||||
let test = cursor_test(
|
||||
r#"
|
||||
type T = T | None
|
||||
def f(rec: T):
|
||||
re<CURSOR>
|
||||
"#,
|
||||
);
|
||||
|
||||
let completions = completion(
|
||||
&test.db,
|
||||
&CompletionSettings::default(),
|
||||
test.cursor.file,
|
||||
test.cursor.offset,
|
||||
);
|
||||
let completion = completions.iter().find(|c| c.name == "rec").unwrap();
|
||||
|
||||
assert_eq!(completion.kind(&test.db), Some(CompletionKind::Struct));
|
||||
}
|
||||
|
||||
// NOTE: The methods below are getting somewhat ridiculous.
|
||||
// We should refactor this by converting to using a builder
|
||||
// to set different modes. ---AG
|
||||
|
|
|
@ -11,7 +11,7 @@ use crate::semantic_index::definition::Definition;
|
|||
use crate::semantic_index::scope::FileScopeId;
|
||||
use crate::semantic_index::semantic_index;
|
||||
use crate::types::ide_support::all_declarations_and_bindings;
|
||||
use crate::types::{Type, binding_type, infer_scope_types};
|
||||
use crate::types::{CycleDetector, Type, binding_type, infer_scope_types};
|
||||
|
||||
pub struct SemanticModel<'db> {
|
||||
db: &'db dyn Db,
|
||||
|
@ -319,7 +319,11 @@ impl<'db> Completion<'db> {
|
|||
/// the client uses this information to help improve the UX (perhaps by
|
||||
/// assigning an icon of some kind to the completion).
|
||||
pub fn kind(&self, db: &'db dyn Db) -> Option<CompletionKind> {
|
||||
fn imp<'db>(db: &'db dyn Db, ty: Type<'db>) -> Option<CompletionKind> {
|
||||
fn imp<'db>(
|
||||
db: &'db dyn Db,
|
||||
ty: Type<'db>,
|
||||
visitor: &CompletionKindVisitor<'db>,
|
||||
) -> Option<CompletionKind> {
|
||||
Some(match ty {
|
||||
Type::FunctionLiteral(_)
|
||||
| Type::DataclassDecorator(_)
|
||||
|
@ -346,23 +350,33 @@ impl<'db> Completion<'db> {
|
|||
Type::EnumLiteral(_) => CompletionKind::Enum,
|
||||
Type::ProtocolInstance(_) => CompletionKind::Interface,
|
||||
Type::NonInferableTypeVar(_) | Type::TypeVar(_) => CompletionKind::TypeParameter,
|
||||
Type::Union(union) => union.elements(db).iter().find_map(|&ty| imp(db, ty))?,
|
||||
Type::Intersection(intersection) => {
|
||||
intersection.iter_positive(db).find_map(|ty| imp(db, ty))?
|
||||
}
|
||||
Type::Union(union) => union
|
||||
.elements(db)
|
||||
.iter()
|
||||
.find_map(|&ty| imp(db, ty, visitor))?,
|
||||
Type::Intersection(intersection) => intersection
|
||||
.iter_positive(db)
|
||||
.find_map(|ty| imp(db, ty, visitor))?,
|
||||
Type::Dynamic(_)
|
||||
| Type::Never
|
||||
| Type::SpecialForm(_)
|
||||
| Type::KnownInstance(_)
|
||||
| Type::AlwaysTruthy
|
||||
| Type::AlwaysFalsy => return None,
|
||||
Type::TypeAlias(alias) => imp(db, alias.value_type(db))?,
|
||||
Type::TypeAlias(alias) => {
|
||||
visitor.visit(ty, || imp(db, alias.value_type(db), visitor))?
|
||||
}
|
||||
})
|
||||
}
|
||||
self.kind.or_else(|| self.ty.and_then(|ty| imp(db, ty)))
|
||||
self.kind.or_else(|| {
|
||||
self.ty
|
||||
.and_then(|ty| imp(db, ty, &CompletionKindVisitor::default()))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
type CompletionKindVisitor<'db> = CycleDetector<CompletionKind, Type<'db>, Option<CompletionKind>>;
|
||||
|
||||
/// The "kind" of a completion.
|
||||
///
|
||||
/// This is taken directly from the LSP completion specification:
|
||||
|
@ -372,7 +386,7 @@ impl<'db> Completion<'db> {
|
|||
/// `Type` (and possibly other information), which might be interesting and
|
||||
/// contentious. Then the outer edges map this to the LSP types, which is
|
||||
/// expected to be mundane and boring.
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub enum CompletionKind {
|
||||
Text,
|
||||
Method,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue