diff --git a/crates/ty_python_semantic/resources/mdtest/call/builtins.md b/crates/ty_python_semantic/resources/mdtest/call/builtins.md index f32c007c1c..0a7fdf2407 100644 --- a/crates/ty_python_semantic/resources/mdtest/call/builtins.md +++ b/crates/ty_python_semantic/resources/mdtest/call/builtins.md @@ -162,3 +162,22 @@ def _(x: A | B, y: list[int]): reveal_type(x) # revealed: B & ~A reveal_type(isinstance(x, B)) # revealed: Literal[True] ``` + +## Calls to `open()` + +We do not fully understand typeshed's overloads for `open()` yet, due to missing support for PEP-613 +type aliases. However, we also do not emit false-positive diagnostics on common calls to `open()`: + +```py +import pickle + +reveal_type(open("")) # revealed: TextIOWrapper[_WrappedBuffer] +reveal_type(open("", "r")) # revealed: TextIOWrapper[_WrappedBuffer] +reveal_type(open("", "rb")) # revealed: @Todo(`builtins.open` return type) + +with open("foo.pickle", "rb") as f: + x = pickle.load(f) # fine + +def _(mode: str): + reveal_type(open("", mode)) # revealed: @Todo(`builtins.open` return type) +``` diff --git a/crates/ty_python_semantic/src/types/function.rs b/crates/ty_python_semantic/src/types/function.rs index a2a8547faa..4d9fdfb756 100644 --- a/crates/ty_python_semantic/src/types/function.rs +++ b/crates/ty_python_semantic/src/types/function.rs @@ -81,7 +81,7 @@ use crate::types::{ DeprecatedInstance, DynamicType, FindLegacyTypeVarsVisitor, HasRelationToVisitor, IsEquivalentVisitor, KnownClass, KnownInstanceType, NormalizedVisitor, SpecialFormType, TrackedConstraintSet, Truthiness, Type, TypeMapping, TypeRelation, UnionBuilder, all_members, - binding_type, walk_type_mapping, + binding_type, todo_type, walk_type_mapping, }; use crate::{Db, FxOrderSet, ModuleName, resolve_module}; @@ -1191,6 +1191,7 @@ pub enum KnownFunction { DunderImport, /// `importlib.import_module`, which returns the submodule. ImportModule, + Open, /// `typing(_extensions).final` Final, @@ -1292,6 +1293,7 @@ impl KnownFunction { | Self::HasAttr | Self::Len | Self::Repr + | Self::Open | Self::DunderImport => module.is_builtins(), Self::AssertType | Self::AssertNever @@ -1703,6 +1705,76 @@ impl KnownFunction { ))); } + KnownFunction::Open => { + // Temporary special-casing for `builtins.open` to avoid an excessive number of false positives + // in lieu of proper support for PEP-614 type aliases. + if let [_, Some(mode), ..] = parameter_types { + // Infer `Todo` for any argument that doesn't match typeshed's + // `OpenTextMode` type alias (). + // Without this special-casing, we'd just always select the first overload in our current state, + // which leads to lots of false positives. + if mode.into_string_literal().is_none_or(|mode| { + !matches!( + mode.value(db), + "r+" | "+r" + | "rt+" + | "r+t" + | "+rt" + | "tr+" + | "t+r" + | "+tr" + | "w+" + | "+w" + | "wt+" + | "w+t" + | "+wt" + | "tw+" + | "t+w" + | "+tw" + | "a+" + | "+a" + | "at+" + | "a+t" + | "+at" + | "ta+" + | "t+a" + | "+ta" + | "x+" + | "+x" + | "xt+" + | "x+t" + | "+xt" + | "tx+" + | "t+x" + | "+tx" + | "w" + | "wt" + | "tw" + | "a" + | "at" + | "ta" + | "x" + | "xt" + | "tx" + | "r" + | "rt" + | "tr" + | "U" + | "rU" + | "Ur" + | "rtU" + | "rUt" + | "Urt" + | "trU" + | "tUr" + | "Utr" + ) + }) { + overload.set_return_type(todo_type!("`builtins.open` return type")); + } + } + } + _ => {} } } @@ -1729,6 +1801,7 @@ pub(crate) mod tests { | KnownFunction::IsInstance | KnownFunction::HasAttr | KnownFunction::IsSubclass + | KnownFunction::Open | KnownFunction::DunderImport => KnownModule::Builtins, KnownFunction::AbstractMethod => KnownModule::Abc,