From 3318e962a1757a1ec87083e7e493883c399e6902 Mon Sep 17 00:00:00 2001 From: decorator-factory <42166884+decorator-factory@users.noreply.github.com> Date: Thu, 16 Oct 2025 03:50:08 +0000 Subject: [PATCH 01/10] Initial support for handling `type_check_only` in completions For now we only support classes --- crates/ty_ide/src/completion.rs | 61 +++++++++++++++++-- .../ty_python_semantic/src/semantic_model.rs | 6 ++ crates/ty_python_semantic/src/types.rs | 12 ++++ .../ty_python_semantic/src/types/call/bind.rs | 1 + crates/ty_python_semantic/src/types/class.rs | 2 + .../ty_python_semantic/src/types/function.rs | 11 +++- .../src/types/infer/builder.rs | 10 +++ 7 files changed, 98 insertions(+), 5 deletions(-) diff --git a/crates/ty_ide/src/completion.rs b/crates/ty_ide/src/completion.rs index 2c571522488ad..c7c7a19edd208 100644 --- a/crates/ty_ide/src/completion.rs +++ b/crates/ty_ide/src/completion.rs @@ -65,6 +65,9 @@ pub struct Completion<'db> { /// use it mainly in tests so that we can write less /// noisy tests. pub builtin: bool, + /// Whether this item only exists for type checking purposes and + /// will be missing at runtime + pub is_type_check_only: bool, /// The documentation associated with this item, if /// available. pub documentation: Option, @@ -79,6 +82,7 @@ impl<'db> Completion<'db> { .ty .and_then(|ty| DefinitionsOrTargets::from_ty(db, ty)); let documentation = definition.and_then(|def| def.docstring(db)); + let is_type_check_only = semantic.is_type_check_only(db); Completion { name: semantic.name, insert: None, @@ -87,6 +91,7 @@ impl<'db> Completion<'db> { module_name: None, import: None, builtin: semantic.builtin, + is_type_check_only, documentation, } } @@ -294,6 +299,7 @@ fn add_keyword_value_completions<'db>( kind: None, module_name: None, import: None, + is_type_check_only: false, builtin: true, documentation: None, }); @@ -339,6 +345,7 @@ fn add_unimported_completions<'db>( module_name: Some(symbol.module.name(db)), import: import_action.import().cloned(), builtin: false, + is_type_check_only: false, documentation: None, }); } @@ -839,16 +846,22 @@ fn is_in_string(parsed: &ParsedModuleRef, offset: TextSize) -> bool { }) } -/// Order completions lexicographically, with these exceptions: +/// Order completions according to the following rules: /// -/// 1) A `_[^_]` prefix sorts last and -/// 2) A `__` prefix sorts last except before (1) +/// 1) Names with no underscore prefix +/// 2) Names starting with `_` but not dunders +/// 3) `__dunder__` names +/// +/// Among each category, type-check-only items are sorted last. /// /// This has the effect of putting all dunder attributes after "normal" /// attributes, and all single-underscore attributes after dunder attributes. fn compare_suggestions(c1: &Completion, c2: &Completion) -> Ordering { let (kind1, kind2) = (NameKind::classify(&c1.name), NameKind::classify(&c2.name)); - kind1.cmp(&kind2).then_with(|| c1.name.cmp(&c2.name)) + + (kind1, c1.is_type_check_only) + .cmp(&(kind2, c2.is_type_check_only)) + .then_with(|| c1.name.cmp(&c2.name)) } #[cfg(test)] @@ -3365,6 +3378,46 @@ from os. ); } + #[test] + fn import_type_check_only_lowers_ranking() { + let test = CursorTest::builder() + .source( + "main.py", + r#" + import foo + foo.A + "#, + ) + .source( + "foo/__init__.py", + r#" + from typing import type_check_only + + @type_check_only + class Apple: pass + + class Banana: pass + class Cat: pass + class Azorubine: pass + "#, + ) + .build(); + + let settings = CompletionSettings::default(); + let completions = completion(&test.db, &settings, test.cursor.file, test.cursor.offset); + + let [apple_pos, banana_pos, cat_pos, azo_pos, ann_pos] = + ["Apple", "Banana", "Cat", "Azorubine", "__annotations__"].map(|name| { + completions + .iter() + .position(|comp| comp.name == name) + .unwrap() + }); + + assert!(apple_pos > banana_pos.max(cat_pos).max(azo_pos)); + assert!(ann_pos > apple_pos); + } + #[test] fn regression_test_issue_642() { // Regression test for https://github.com/astral-sh/ty/issues/642 diff --git a/crates/ty_python_semantic/src/semantic_model.rs b/crates/ty_python_semantic/src/semantic_model.rs index eb50b503667e0..65c167fb08b5c 100644 --- a/crates/ty_python_semantic/src/semantic_model.rs +++ b/crates/ty_python_semantic/src/semantic_model.rs @@ -342,6 +342,12 @@ pub struct Completion<'db> { pub builtin: bool, } +impl<'db> Completion<'db> { + pub fn is_type_check_only(&self, db: &'db dyn Db) -> bool { + self.ty.is_some_and(|ty| ty.is_type_check_only(db)) + } +} + pub trait HasType { /// Returns the inferred type of `self`. /// diff --git a/crates/ty_python_semantic/src/types.rs b/crates/ty_python_semantic/src/types.rs index 3cb6d43e5c8e3..4346be3a59dc9 100644 --- a/crates/ty_python_semantic/src/types.rs +++ b/crates/ty_python_semantic/src/types.rs @@ -874,6 +874,18 @@ impl<'db> Type<'db> { matches!(self, Type::Dynamic(_)) } + /// Is a value of this type only usable in typing contexts? + pub(crate) fn is_type_check_only(&self, db: &'db dyn Db) -> bool { + match self { + Type::ClassLiteral(class_literal) => class_literal.type_check_only(db), + Type::FunctionLiteral(f) => { + // TODO(PR): implement `@type_check_only` for functions + false + } + _ => false, + } + } + // If the type is a specialized instance of the given `KnownClass`, returns the specialization. pub(crate) fn known_specialization( &self, diff --git a/crates/ty_python_semantic/src/types/call/bind.rs b/crates/ty_python_semantic/src/types/call/bind.rs index 128d61e1d5976..c122d5758cf0e 100644 --- a/crates/ty_python_semantic/src/types/call/bind.rs +++ b/crates/ty_python_semantic/src/types/call/bind.rs @@ -922,6 +922,7 @@ impl<'db> Bindings<'db> { class_literal.body_scope(db), class_literal.known(db), class_literal.deprecated(db), + class_literal.type_check_only(db), Some(params), class_literal.dataclass_transformer_params(db), ))); diff --git a/crates/ty_python_semantic/src/types/class.rs b/crates/ty_python_semantic/src/types/class.rs index 64ba611a46265..ce6cfa7e2afdf 100644 --- a/crates/ty_python_semantic/src/types/class.rs +++ b/crates/ty_python_semantic/src/types/class.rs @@ -1387,6 +1387,8 @@ pub struct ClassLiteral<'db> { /// If this class is deprecated, this holds the deprecation message. pub(crate) deprecated: Option>, + pub(crate) type_check_only: bool, + pub(crate) dataclass_params: Option, pub(crate) dataclass_transformer_params: Option, } diff --git a/crates/ty_python_semantic/src/types/function.rs b/crates/ty_python_semantic/src/types/function.rs index 1456f12526eec..26251cc695a35 100644 --- a/crates/ty_python_semantic/src/types/function.rs +++ b/crates/ty_python_semantic/src/types/function.rs @@ -121,6 +121,8 @@ bitflags! { const STATICMETHOD = 1 << 5; /// `@typing.override` const OVERRIDE = 1 << 6; + /// `@typing.type_check_only` + const TYPE_CHECK_ONLY = 1 << 7; } } @@ -135,6 +137,7 @@ impl FunctionDecorators { Some(KnownFunction::AbstractMethod) => FunctionDecorators::ABSTRACT_METHOD, Some(KnownFunction::Final) => FunctionDecorators::FINAL, Some(KnownFunction::Override) => FunctionDecorators::OVERRIDE, + Some(KnownFunction::TypeCheckOnly) => FunctionDecorators::TYPE_CHECK_ONLY, _ => FunctionDecorators::empty(), }, Type::ClassLiteral(class) => match class.known(db) { @@ -1284,6 +1287,8 @@ pub enum KnownFunction { DisjointBase, /// [`typing(_extensions).no_type_check`](https://typing.python.org/en/latest/spec/directives.html#no-type-check) NoTypeCheck, + /// `typing(_extensions).type_check_only` + TypeCheckOnly, /// `typing(_extensions).assert_type` AssertType, @@ -1368,7 +1373,7 @@ impl KnownFunction { .then_some(candidate) } - /// Return `true` if `self` is defined in `module` at runtime. + /// Return `true` if `self` is defined in `module` const fn check_module(self, module: KnownModule) -> bool { match self { Self::IsInstance @@ -1422,6 +1427,8 @@ impl KnownFunction { | Self::NegatedRangeConstraint | Self::AllMembers => module.is_ty_extensions(), Self::ImportModule => module.is_importlib(), + + Self::TypeCheckOnly => matches!(module, KnownModule::Typing), } } @@ -1835,6 +1842,8 @@ pub(crate) mod tests { | KnownFunction::DisjointBase | KnownFunction::NoTypeCheck => KnownModule::TypingExtensions, + KnownFunction::TypeCheckOnly => KnownModule::Typing, + KnownFunction::IsSingleton | KnownFunction::IsSubtypeOf | KnownFunction::GenericContext diff --git a/crates/ty_python_semantic/src/types/infer/builder.rs b/crates/ty_python_semantic/src/types/infer/builder.rs index be92acc9abf51..1df75b54e5218 100644 --- a/crates/ty_python_semantic/src/types/infer/builder.rs +++ b/crates/ty_python_semantic/src/types/infer/builder.rs @@ -2565,6 +2565,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> { } = class_node; let mut deprecated = None; + let mut type_check_only = false; let mut dataclass_params = None; let mut dataclass_transformer_params = None; for decorator in decorator_list { @@ -2589,6 +2590,14 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> { continue; } + if decorator_ty + .as_function_literal() + .is_some_and(|function| function.is_known(self.db(), KnownFunction::TypeCheckOnly)) + { + type_check_only = true; + continue; + } + if let Type::FunctionLiteral(f) = decorator_ty { // We do not yet detect or flag `@dataclass_transform` applied to more than one // overload, or an overload and the implementation both. Nevertheless, this is not @@ -2634,6 +2643,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> { body_scope, maybe_known_class, deprecated, + type_check_only, dataclass_params, dataclass_transformer_params, )), From 12865e48b75a13ce7f42b7f3f76c0a6ba783657b Mon Sep 17 00:00:00 2001 From: decorator-factory <42166884+decorator-factory@users.noreply.github.com> Date: Fri, 17 Oct 2025 00:28:18 +0000 Subject: [PATCH 02/10] Support `type_check_only` functions (for the sake of completions) --- crates/ty_ide/src/completion.rs | 26 ++++++++++++++++++++++---- crates/ty_python_semantic/src/types.rs | 10 +++++++--- 2 files changed, 29 insertions(+), 7 deletions(-) diff --git a/crates/ty_ide/src/completion.rs b/crates/ty_ide/src/completion.rs index c7c7a19edd208..b0e8dc69e9cd7 100644 --- a/crates/ty_ide/src/completion.rs +++ b/crates/ty_ide/src/completion.rs @@ -852,16 +852,15 @@ fn is_in_string(parsed: &ParsedModuleRef, offset: TextSize) -> bool { /// 2) Names starting with `_` but not dunders /// 3) `__dunder__` names /// -/// Among each category, type-check-only items are sorted last. +/// Among each category, type-check-only items are sorted last, +/// and otherwise completions are sorted lexicographically. /// /// This has the effect of putting all dunder attributes after "normal" /// attributes, and all single-underscore attributes after dunder attributes. fn compare_suggestions(c1: &Completion, c2: &Completion) -> Ordering { let (kind1, kind2) = (NameKind::classify(&c1.name), NameKind::classify(&c2.name)); - (kind1, c1.is_type_check_only) - .cmp(&(kind2, c2.is_type_check_only)) - .then_with(|| c1.name.cmp(&c2.name)) + (kind1, c1.is_type_check_only, &c1.name).cmp(&(kind2, c2.is_type_check_only, &c2.name)) } #[cfg(test)] @@ -3414,10 +3413,29 @@ from os. .unwrap() }); + assert!(completions[apple_pos].is_type_check_only); assert!(apple_pos > banana_pos.max(cat_pos).max(azo_pos)); assert!(ann_pos > apple_pos); } + #[test] + fn type_check_only_is_type_check_only() { + // `@typing.type_check_only` is a function that's unavailable at runtime + // and so should be the last "non-underscore" completion in `typing` + let test = cursor_test("from typing import t"); + + let settings = CompletionSettings::default(); + let completions = completion(&test.db, &settings, test.cursor.file, test.cursor.offset); + let last_nonunderscore = completions + .into_iter() + .filter(|c| !c.name.starts_with("_")) + .last() + .unwrap(); + + assert_eq!(&last_nonunderscore.name, "type_check_only"); + assert!(last_nonunderscore.is_type_check_only); + } + #[test] fn regression_test_issue_642() { // Regression test for https://github.com/astral-sh/ty/issues/642 diff --git a/crates/ty_python_semantic/src/types.rs b/crates/ty_python_semantic/src/types.rs index 4346be3a59dc9..0cdce5abfd28a 100644 --- a/crates/ty_python_semantic/src/types.rs +++ b/crates/ty_python_semantic/src/types.rs @@ -50,7 +50,7 @@ pub use crate::types::display::DisplaySettings; use crate::types::display::TupleSpecialization; use crate::types::enums::{enum_metadata, is_single_member_enum}; use crate::types::function::{ - DataclassTransformerParams, FunctionSpans, FunctionType, KnownFunction, + DataclassTransformerParams, FunctionDecorators, FunctionSpans, FunctionType, KnownFunction, }; use crate::types::generics::{ GenericContext, InferableTypeVars, PartialSpecialization, Specialization, bind_typevar, @@ -879,8 +879,12 @@ impl<'db> Type<'db> { match self { Type::ClassLiteral(class_literal) => class_literal.type_check_only(db), Type::FunctionLiteral(f) => { - // TODO(PR): implement `@type_check_only` for functions - false + if f.is_known(db, KnownFunction::TypeCheckOnly) { + // `@typing.type_check_only` is itself unavailable at runtime + true + } else { + f.has_known_decorator(db, FunctionDecorators::TYPE_CHECK_ONLY) + } } _ => false, } From 45c09d6368a3523f0ff03a01955fb1ddbc106674 Mon Sep 17 00:00:00 2001 From: decorator-factory <42166884+decorator-factory@users.noreply.github.com> Date: Fri, 17 Oct 2025 00:38:00 +0000 Subject: [PATCH 03/10] cargo fmt --- crates/ty_python_semantic/src/types.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/ty_python_semantic/src/types.rs b/crates/ty_python_semantic/src/types.rs index 90fbaae976c61..867cc28bfb552 100644 --- a/crates/ty_python_semantic/src/types.rs +++ b/crates/ty_python_semantic/src/types.rs @@ -52,8 +52,8 @@ pub use crate::types::display::DisplaySettings; use crate::types::display::TupleSpecialization; use crate::types::enums::{enum_metadata, is_single_member_enum}; use crate::types::function::{ - DataclassTransformerFlags, DataclassTransformerParams, FunctionDecorators, FunctionSpans, FunctionType, - KnownFunction, + DataclassTransformerFlags, DataclassTransformerParams, FunctionDecorators, FunctionSpans, + FunctionType, KnownFunction, }; use crate::types::generics::{ GenericContext, InferableTypeVars, PartialSpecialization, Specialization, bind_typevar, From 7b85ec605775f37f297c49a33f7ba82edb6adf51 Mon Sep 17 00:00:00 2001 From: decorator-factory <42166884+decorator-factory@users.noreply.github.com> Date: Fri, 17 Oct 2025 11:43:47 +0000 Subject: [PATCH 04/10] make clippy happy --- crates/ty_ide/src/completion.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/ty_ide/src/completion.rs b/crates/ty_ide/src/completion.rs index 4ecb9f6fdd9d2..b42b1df96865a 100644 --- a/crates/ty_ide/src/completion.rs +++ b/crates/ty_ide/src/completion.rs @@ -3428,8 +3428,8 @@ from os. let completions = completion(&test.db, &settings, test.cursor.file, test.cursor.offset); let last_nonunderscore = completions .into_iter() - .filter(|c| !c.name.starts_with("_")) - .last() + .filter(|c| !c.name.starts_with('_')) + .next_back() .unwrap(); assert_eq!(&last_nonunderscore.name, "type_check_only"); From 4928fbe5864cc000364281a111a8bb8ef9c0fb7e Mon Sep 17 00:00:00 2001 From: decorator-factory <42166884+decorator-factory@users.noreply.github.com> Date: Fri, 17 Oct 2025 12:42:48 +0000 Subject: [PATCH 05/10] Add integration test for type_check_only ranking --- .../completion-evaluation-tasks.csv | 1 + .../completion.toml | 2 ++ .../main.py | 12 +++++++++++ .../module.py | 20 +++++++++++++++++++ .../pyproject.toml | 5 +++++ .../uv.lock | 8 ++++++++ 6 files changed, 48 insertions(+) create mode 100644 crates/ty_completion_eval/truth/import-deprioritizes-type_check_only/completion.toml create mode 100644 crates/ty_completion_eval/truth/import-deprioritizes-type_check_only/main.py create mode 100644 crates/ty_completion_eval/truth/import-deprioritizes-type_check_only/module.py create mode 100644 crates/ty_completion_eval/truth/import-deprioritizes-type_check_only/pyproject.toml create mode 100644 crates/ty_completion_eval/truth/import-deprioritizes-type_check_only/uv.lock diff --git a/crates/ty_completion_eval/completion-evaluation-tasks.csv b/crates/ty_completion_eval/completion-evaluation-tasks.csv index 00b612e21750e..e41e895c3813d 100644 --- a/crates/ty_completion_eval/completion-evaluation-tasks.csv +++ b/crates/ty_completion_eval/completion-evaluation-tasks.csv @@ -4,6 +4,7 @@ higher-level-symbols-preferred,main.py,0, higher-level-symbols-preferred,main.py,1,1 import-deprioritizes-dunder,main.py,0,1 import-deprioritizes-sunder,main.py,0,1 +import-deprioritizes-type_check-only,main.py,0,1 internal-typeshed-hidden,main.py,0,4 none-completion,main.py,0,11 numpy-array,main.py,0, diff --git a/crates/ty_completion_eval/truth/import-deprioritizes-type_check_only/completion.toml b/crates/ty_completion_eval/truth/import-deprioritizes-type_check_only/completion.toml new file mode 100644 index 0000000000000..cbd5805f07331 --- /dev/null +++ b/crates/ty_completion_eval/truth/import-deprioritizes-type_check_only/completion.toml @@ -0,0 +1,2 @@ +[settings] +auto-import = true diff --git a/crates/ty_completion_eval/truth/import-deprioritizes-type_check_only/main.py b/crates/ty_completion_eval/truth/import-deprioritizes-type_check_only/main.py new file mode 100644 index 0000000000000..46bad21e41110 --- /dev/null +++ b/crates/ty_completion_eval/truth/import-deprioritizes-type_check_only/main.py @@ -0,0 +1,12 @@ +from module import UniquePrefixA +from module import unique_prefix_ + +from module import Class + +Class.meth_ + +# TODO: bound methods don't preserve type-check-only-ness, this is a bug +# Class().meth_ + +# TODO: auto-imports don't take type-check-only-ness into account, this is a bug +# UniquePrefixA \ No newline at end of file diff --git a/crates/ty_completion_eval/truth/import-deprioritizes-type_check_only/module.py b/crates/ty_completion_eval/truth/import-deprioritizes-type_check_only/module.py new file mode 100644 index 0000000000000..9fd59987689ed --- /dev/null +++ b/crates/ty_completion_eval/truth/import-deprioritizes-type_check_only/module.py @@ -0,0 +1,20 @@ +from typing import type_check_only + + +@type_check_only +class UniquePrefixApple: pass + +class UniquePrefixAzurous: pass + + +@type_check_only +def unique_prefix_apple() -> None: pass + +def unique_prefix_azurous() -> None: pass + + +class Class: + @type_check_only + def meth_apple(self) -> None: pass + + def meth_azurous(self) -> None: pass diff --git a/crates/ty_completion_eval/truth/import-deprioritizes-type_check_only/pyproject.toml b/crates/ty_completion_eval/truth/import-deprioritizes-type_check_only/pyproject.toml new file mode 100644 index 0000000000000..cd277d8097f3d --- /dev/null +++ b/crates/ty_completion_eval/truth/import-deprioritizes-type_check_only/pyproject.toml @@ -0,0 +1,5 @@ +[project] +name = "test" +version = "0.1.0" +requires-python = ">=3.13" +dependencies = [] diff --git a/crates/ty_completion_eval/truth/import-deprioritizes-type_check_only/uv.lock b/crates/ty_completion_eval/truth/import-deprioritizes-type_check_only/uv.lock new file mode 100644 index 0000000000000..a4937d10d3cd8 --- /dev/null +++ b/crates/ty_completion_eval/truth/import-deprioritizes-type_check_only/uv.lock @@ -0,0 +1,8 @@ +version = 1 +revision = 3 +requires-python = ">=3.13" + +[[package]] +name = "test" +version = "0.1.0" +source = { virtual = "." } From a3cfa2da3038c0c7206115289f5add173a477fd4 Mon Sep 17 00:00:00 2001 From: decorator-factory <42166884+decorator-factory@users.noreply.github.com> Date: Fri, 17 Oct 2025 12:54:17 +0000 Subject: [PATCH 06/10] Assign type-check-only-ness for `@type_check_only` itself --- crates/ty_python_semantic/src/types.rs | 7 +------ crates/ty_python_semantic/src/types/infer/builder.rs | 5 +++++ 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/crates/ty_python_semantic/src/types.rs b/crates/ty_python_semantic/src/types.rs index 867cc28bfb552..21d319759c1cb 100644 --- a/crates/ty_python_semantic/src/types.rs +++ b/crates/ty_python_semantic/src/types.rs @@ -904,12 +904,7 @@ impl<'db> Type<'db> { match self { Type::ClassLiteral(class_literal) => class_literal.type_check_only(db), Type::FunctionLiteral(f) => { - if f.is_known(db, KnownFunction::TypeCheckOnly) { - // `@typing.type_check_only` is itself unavailable at runtime - true - } else { - f.has_known_decorator(db, FunctionDecorators::TYPE_CHECK_ONLY) - } + f.has_known_decorator(db, FunctionDecorators::TYPE_CHECK_ONLY) } _ => false, } diff --git a/crates/ty_python_semantic/src/types/infer/builder.rs b/crates/ty_python_semantic/src/types/infer/builder.rs index 820da99a89c25..19d78133b3136 100644 --- a/crates/ty_python_semantic/src/types/infer/builder.rs +++ b/crates/ty_python_semantic/src/types/infer/builder.rs @@ -2205,6 +2205,11 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> { let known_function = KnownFunction::try_from_definition_and_name(self.db(), definition, name); + // `type_check_only` is itself not available at runtime + if known_function == Some(KnownFunction::TypeCheckOnly) { + function_decorators |= FunctionDecorators::TYPE_CHECK_ONLY; + } + let body_scope = self .index .node_scope(NodeWithScopeRef::Function(function)) From 463a815777a15078922e2657df498d86b2e6332d Mon Sep 17 00:00:00 2001 From: decorator-factory <42166884+decorator-factory@users.noreply.github.com> Date: Fri, 17 Oct 2025 13:12:48 +0000 Subject: [PATCH 07/10] fix completion integration tests --- crates/ty_completion_eval/completion-evaluation-tasks.csv | 2 +- .../truth/import-deprioritizes-type_check_only/main.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/ty_completion_eval/completion-evaluation-tasks.csv b/crates/ty_completion_eval/completion-evaluation-tasks.csv index e41e895c3813d..c94d0d5b089cd 100644 --- a/crates/ty_completion_eval/completion-evaluation-tasks.csv +++ b/crates/ty_completion_eval/completion-evaluation-tasks.csv @@ -4,7 +4,7 @@ higher-level-symbols-preferred,main.py,0, higher-level-symbols-preferred,main.py,1,1 import-deprioritizes-dunder,main.py,0,1 import-deprioritizes-sunder,main.py,0,1 -import-deprioritizes-type_check-only,main.py,0,1 +import-deprioritizes-type_check_only,main.py,0,1 internal-typeshed-hidden,main.py,0,4 none-completion,main.py,0,11 numpy-array,main.py,0, diff --git a/crates/ty_completion_eval/truth/import-deprioritizes-type_check_only/main.py b/crates/ty_completion_eval/truth/import-deprioritizes-type_check_only/main.py index 46bad21e41110..8ec0b0d1c44e9 100644 --- a/crates/ty_completion_eval/truth/import-deprioritizes-type_check_only/main.py +++ b/crates/ty_completion_eval/truth/import-deprioritizes-type_check_only/main.py @@ -6,7 +6,7 @@ Class.meth_ # TODO: bound methods don't preserve type-check-only-ness, this is a bug -# Class().meth_ +# Class().meth_[CURSOR:meth_azurous] # TODO: auto-imports don't take type-check-only-ness into account, this is a bug -# UniquePrefixA \ No newline at end of file +# UniquePrefixA[CURSOR:module.UniquePrefixAzurous] From 0a0a3ab996f9965fd6ccf61476797e35c0b01864 Mon Sep 17 00:00:00 2001 From: decorator-factory <42166884+decorator-factory@users.noreply.github.com> Date: Fri, 17 Oct 2025 13:23:57 +0000 Subject: [PATCH 08/10] fix completion integration tests v2 --- crates/ty_completion_eval/completion-evaluation-tasks.csv | 2 ++ 1 file changed, 2 insertions(+) diff --git a/crates/ty_completion_eval/completion-evaluation-tasks.csv b/crates/ty_completion_eval/completion-evaluation-tasks.csv index c94d0d5b089cd..73b58ab5493e8 100644 --- a/crates/ty_completion_eval/completion-evaluation-tasks.csv +++ b/crates/ty_completion_eval/completion-evaluation-tasks.csv @@ -5,6 +5,8 @@ higher-level-symbols-preferred,main.py,1,1 import-deprioritizes-dunder,main.py,0,1 import-deprioritizes-sunder,main.py,0,1 import-deprioritizes-type_check_only,main.py,0,1 +import-deprioritizes-type_check_only,main.py,1,1 +import-deprioritizes-type_check_only,main.py,2,1 internal-typeshed-hidden,main.py,0,4 none-completion,main.py,0,11 numpy-array,main.py,0, From 49da6cdaf1635a6798de4e6fb7bd3eb304ea9621 Mon Sep 17 00:00:00 2001 From: decorator-factory <42166884+decorator-factory@users.noreply.github.com> Date: Wed, 22 Oct 2025 16:51:04 +0000 Subject: [PATCH 09/10] Add TODO comment --- crates/ty_ide/src/completion.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/ty_ide/src/completion.rs b/crates/ty_ide/src/completion.rs index 34a31e166c73b..ad29fb34c05b3 100644 --- a/crates/ty_ide/src/completion.rs +++ b/crates/ty_ide/src/completion.rs @@ -345,6 +345,7 @@ fn add_unimported_completions<'db>( module_name: Some(symbol.module.name(db)), import: import_action.import().cloned(), builtin: false, + // TODO: `is_type_check_only` requires inferring the type of the symbol is_type_check_only: false, documentation: None, }); From 3646b1b722578f41295b357464b4995881b77ce8 Mon Sep 17 00:00:00 2001 From: decorator-factory <42166884+decorator-factory@users.noreply.github.com> Date: Thu, 23 Oct 2025 13:56:58 +0000 Subject: [PATCH 10/10] Uncomment completion integration tests --- crates/ty_completion_eval/completion-evaluation-tasks.csv | 2 ++ .../truth/import-deprioritizes-type_check_only/main.py | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/crates/ty_completion_eval/completion-evaluation-tasks.csv b/crates/ty_completion_eval/completion-evaluation-tasks.csv index 73b58ab5493e8..cf73a817e1a93 100644 --- a/crates/ty_completion_eval/completion-evaluation-tasks.csv +++ b/crates/ty_completion_eval/completion-evaluation-tasks.csv @@ -7,6 +7,8 @@ import-deprioritizes-sunder,main.py,0,1 import-deprioritizes-type_check_only,main.py,0,1 import-deprioritizes-type_check_only,main.py,1,1 import-deprioritizes-type_check_only,main.py,2,1 +import-deprioritizes-type_check_only,main.py,3,2 +import-deprioritizes-type_check_only,main.py,4,3 internal-typeshed-hidden,main.py,0,4 none-completion,main.py,0,11 numpy-array,main.py,0, diff --git a/crates/ty_completion_eval/truth/import-deprioritizes-type_check_only/main.py b/crates/ty_completion_eval/truth/import-deprioritizes-type_check_only/main.py index 8ec0b0d1c44e9..52dd9ee9f8a89 100644 --- a/crates/ty_completion_eval/truth/import-deprioritizes-type_check_only/main.py +++ b/crates/ty_completion_eval/truth/import-deprioritizes-type_check_only/main.py @@ -6,7 +6,7 @@ Class.meth_ # TODO: bound methods don't preserve type-check-only-ness, this is a bug -# Class().meth_[CURSOR:meth_azurous] +Class().meth_ # TODO: auto-imports don't take type-check-only-ness into account, this is a bug -# UniquePrefixA[CURSOR:module.UniquePrefixAzurous] +UniquePrefixA