This commit is contained in:
Aria Desires 2025-12-16 11:24:03 -05:00
parent d755f3b522
commit 4b9755b603
13 changed files with 243 additions and 60 deletions

View File

@ -3212,7 +3212,7 @@ quux.<CURSOR>
assert_snapshot!(
builder.skip_keywords().skip_builtins().type_signatures().build().snapshot(), @r"
count :: bound method Quux.count(value: Any, /) -> int
index :: bound method Quux.index(value: Any, start: SupportsIndex = Literal[0], stop: SupportsIndex = int, /) -> int
index :: bound method Quux.index(value: Any, start: SupportsIndex = 0, stop: SupportsIndex = sys.maxsize, /) -> int
x :: int
y :: str
__add__ :: Overload[(value: tuple[int | str, ...], /) -> tuple[int | str, ...], (value: tuple[_T@__add__, ...], /) -> tuple[int | str | _T@__add__, ...]]

View File

@ -144,7 +144,7 @@ from typing import Any
def _(a: Any, tuple_of_any: tuple[Any]):
reveal_type(inspect.getattr_static(a, "x", "default")) # revealed: Any | Literal["default"]
# revealed: def index(self, value: Any, start: SupportsIndex = Literal[0], stop: SupportsIndex = int, /) -> int
# revealed def index(self, value: Any, start: SupportsIndex = 0, stop: SupportsIndex = int, /) -> int
reveal_type(inspect.getattr_static(tuple_of_any, "index", "default"))
```

View File

@ -600,9 +600,9 @@ from typing_extensions import Self
reveal_type(object.__new__) # revealed: def __new__(cls) -> Self@__new__
reveal_type(object().__new__) # revealed: def __new__(cls) -> Self@__new__
# revealed: Overload[(cls, x: str | Buffer | SupportsInt | SupportsIndex | SupportsTrunc = Literal[0], /) -> Self@__new__, (cls, x: str | bytes | bytearray, /, base: SupportsIndex) -> Self@__new__]
# revealed Overload[(cls, x: str | Buffer | SupportsInt | SupportsIndex | SupportsTrunc = 0], /) -> Self@__new__, (cls, x: str | bytes | bytearray, /, base: SupportsIndex) -> Self@__new__
reveal_type(int.__new__)
# revealed: Overload[(cls, x: str | Buffer | SupportsInt | SupportsIndex | SupportsTrunc = Literal[0], /) -> Self@__new__, (cls, x: str | bytes | bytearray, /, base: SupportsIndex) -> Self@__new__]
# revealed Overload[(cls, x: str | Buffer | SupportsInt | SupportsIndex | SupportsTrunc = 0], /) -> Self@__new__, (cls, x: str | bytes | bytearray, /, base: SupportsIndex) -> Self@__new__
reveal_type((42).__new__)
class X:

View File

@ -69,7 +69,7 @@ class D:
y: str = "default"
z: int | None = 1 + 2
reveal_type(D.__init__) # revealed: (self: D, x: int, y: str = Literal["default"], z: int | None = Literal[3]) -> None
reveal_type(D.__init__) # revealed: (self: D, x: int, y: str = "default", z: int | None = 3) -> None
```
This also works if the declaration and binding are split:
@ -221,7 +221,7 @@ class D:
(x): int = 1
# TODO: should ideally not include a `x` parameter
reveal_type(D.__init__) # revealed: (self: D, x: int = Literal[1]) -> None
reveal_type(D.__init__) # revealed (self: D, x: int = 1) -> None
```
## `@dataclass` calls with arguments
@ -670,7 +670,7 @@ class A:
a: str = field(kw_only=False)
b: int = 0
reveal_type(A.__init__) # revealed: (self: A, a: str, *, b: int = Literal[0]) -> None
reveal_type(A.__init__) # revealed (self: A, a: str, *, b: int = 0) -> None
A("hi")
```
@ -991,7 +991,7 @@ class C:
class_variable1: ClassVar[Final[int]] = 1
class_variable2: ClassVar[Final[int]] = 1
reveal_type(C.__init__) # revealed: (self: C, instance_variable_no_default: int, instance_variable: int = Literal[1]) -> None
reveal_type(C.__init__) # revealed (self: C, instance_variable_no_default: int, instance_variable: int = 1) -> None
c = C(1)
# error: [invalid-assignment] "Cannot assign to final attribute `instance_variable` on type `C`"
@ -1082,7 +1082,7 @@ class C(Base):
z: int = 10
x: int = 15
reveal_type(C.__init__) # revealed: (self: C, x: int = Literal[15], y: int = Literal[0], z: int = Literal[10]) -> None
reveal_type(C.__init__) # revealed (self: C, x: int = Literal[15], y: int = Literal[0], z: int = 10) -> None
```
## Conditionally defined fields
@ -1289,7 +1289,7 @@ class ConvertToLength:
class C:
converter: ConvertToLength = ConvertToLength()
reveal_type(C.__init__) # revealed: (self: C, converter: str = Literal[""]) -> None
reveal_type(C.__init__) # revealed: (self: C, converter: str = "") -> None
c = C("abc")
reveal_type(c.converter) # revealed: int

View File

@ -11,7 +11,7 @@ class Member:
role: str = field(default="user")
tag: str | None = field(default=None, init=False)
# revealed: (self: Member, name: str, role: str = Literal["user"]) -> None
# revealed: (self: Member, name: str, role: str = "user") -> None
reveal_type(Member.__init__)
alice = Member(name="Alice", role="admin")
@ -63,7 +63,7 @@ class Person:
age: int | None = field(default=None, kw_only=True)
role: str = field(default="user", kw_only=True)
# revealed: (self: Person, name: str, *, age: int | None = None, role: str = Literal["user"]) -> None
# revealed: (self: Person, name: str, *, age: int | None = None, role: str = "user") -> None
reveal_type(Person.__init__)
alice = Person(role="admin", name="Alice")

View File

@ -477,7 +477,7 @@ def keyword_only_with_default_2(*, y: int = 42) -> int:
# parameter list i.e., `()`
# TODO: This shouldn't error
# error: [invalid-argument-type]
# revealed: (*, x: int = Literal[42]) -> bool
# revealed (*, x: int = 42) -> bool
reveal_type(multiple(keyword_only_with_default_1, keyword_only_with_default_2))
def keyword_only1(*, x: int) -> int:

View File

@ -73,8 +73,8 @@ error[missing-argument]: No argument provided for required parameter `a` of func
13 |
14 | Foo().method() # error: [missing-argument]
|
info: Union variant `def f(a, b=Literal[42]) -> Unknown` is incompatible with this call site
info: Attempted to call union type `(def f(a, b=Literal[42]) -> Unknown) | (def g(a, b) -> Unknown)`
info: Union variant `def f(a, b=42) -> Unknown` is incompatible with this call site
info: Attempted to call union type `(def f(a, b=42) -> Unknown) | (def g(a, b) -> Unknown)`
info: rule `missing-argument` is enabled by default
```
@ -91,7 +91,7 @@ error[missing-argument]: No argument provided for required parameter `a` of func
14 | Foo().method() # error: [missing-argument]
|
info: Union variant `def g(a, b) -> Unknown` is incompatible with this call site
info: Attempted to call union type `(def f(a, b=Literal[42]) -> Unknown) | (def g(a, b) -> Unknown)`
info: Attempted to call union type `(def f(a, b=42) -> Unknown) | (def g(a, b) -> Unknown)`
info: rule `missing-argument` is enabled by default
```

View File

@ -73,8 +73,8 @@ error[too-many-positional-arguments]: Too many positional arguments to function
13 |
14 | Foo().method(1, 2) # error: [too-many-positional-arguments]
|
info: Union variant `def f(a, b=Literal[42]) -> Unknown` is incompatible with this call site
info: Attempted to call union type `(def f(a, b=Literal[42]) -> Unknown) | (def g(a, b) -> Unknown)`
info: Union variant `def f(a, b=42) -> Unknown` is incompatible with this call site
info: Attempted to call union type `(def f(a, b=42) -> Unknown) | (def g(a, b) -> Unknown)`
info: rule `too-many-positional-arguments` is enabled by default
```
@ -91,7 +91,7 @@ error[too-many-positional-arguments]: Too many positional arguments to function
14 | Foo().method(1, 2) # error: [too-many-positional-arguments]
|
info: Union variant `def g(a, b) -> Unknown` is incompatible with this call site
info: Attempted to call union type `(def f(a, b=Literal[42]) -> Unknown) | (def g(a, b) -> Unknown)`
info: Attempted to call union type `(def f(a, b=42) -> Unknown) | (def g(a, b) -> Unknown)`
info: rule `too-many-positional-arguments` is enabled by default
```

View File

@ -73,8 +73,8 @@ error[unknown-argument]: Argument `d` does not match any known parameter of func
13 |
14 | Foo().method(a=1, b=2, c=3) # error: [unknown-argument]
|
info: Union variant `def f(a, b, c=Literal[42]) -> Unknown` is incompatible with this call site
info: Attempted to call union type `(def f(a, b, c=Literal[42]) -> Unknown) | (def g(a, b) -> Unknown)`
info: Union variant `def f(a, b, c=42) -> Unknown` is incompatible with this call site
info: Attempted to call union type `(def f(a, b, c=42) -> Unknown) | (def g(a, b) -> Unknown)`
info: rule `unknown-argument` is enabled by default
```
@ -91,7 +91,7 @@ error[unknown-argument]: Argument `d` does not match any known parameter of func
14 | Foo().method(a=1, b=2, c=3) # error: [unknown-argument]
|
info: Union variant `def g(a, b) -> Unknown` is incompatible with this call site
info: Attempted to call union type `(def f(a, b, c=Literal[42]) -> Unknown) | (def g(a, b) -> Unknown)`
info: Attempted to call union type `(def f(a, b, c=42) -> Unknown) | (def g(a, b) -> Unknown)`
info: rule `unknown-argument` is enabled by default
```

View File

@ -67,7 +67,7 @@ class Person:
metadata: InitVar[str] = "default"
reveal_type(Person.__init__) # revealed: (self: Person, name: str, age: int, metadata: str = Literal["default"]) -> None
reveal_type(Person.__init__) # revealed: (self: Person, name: str, age: int, metadata: str = "default") -> None
alice = Person("Alice", 30)
bob = Person("Bob", 25, "custom metadata")

View File

@ -1838,13 +1838,20 @@ impl<'db> FmtDetailed<'db> for DisplayParameter<'_, 'db> {
}
}
// Default value can only be specified if `name` is given.
if let Some(default_ty) = self.param.default_type() {
if let Some(default_value) = self.param.default_value() {
if self.param.annotated_type().is_some() {
f.write_str(" = ")?;
} else {
f.write_str("=")?;
}
default_ty
write!(f, "{default_value}")?;
} else if let Some(default_type) = self.param.default_type() {
if self.param.annotated_type().is_some() {
f.write_str(" = ")?;
} else {
f.write_str("=")?;
}
default_type
.display_with(self.db, self.settings.clone())
.fmt_detailed(f)?;
}
@ -2683,9 +2690,7 @@ mod tests {
],
Some(KnownClass::Bytes.to_instance(&db))
),
@"(a, b: int, c=Literal[1], d: int = Literal[2], \
/, e=Literal[3], f: int = Literal[4], *args: object, \
*, g=Literal[5], h: int = Literal[6], **kwargs: str) -> bytes"
@"(a, b: int, c=Literal[1], d: int = Literal[2], /, e=Literal[3], f: int = Literal[4], *args: object, *, g=Literal[5], h: int = Literal[6], **kwargs: str) -> bytes"
);
}

View File

@ -917,10 +917,12 @@ impl<'db> Signature<'db> {
ParameterKind::PositionalOrKeyword {
name: self_name,
default_type: self_default,
default_value: _,
},
ParameterKind::PositionalOrKeyword {
name: other_name,
default_type: other_default,
default_value: _,
},
) if self_default.is_some() == other_default.is_some()
&& self_name == other_name => {}
@ -931,10 +933,12 @@ impl<'db> Signature<'db> {
ParameterKind::KeywordOnly {
name: self_name,
default_type: self_default,
default_value: _,
},
ParameterKind::KeywordOnly {
name: other_name,
default_type: other_default,
default_value: _,
},
) if self_default.is_some() == other_default.is_some()
&& self_name == other_name => {}
@ -1218,10 +1222,12 @@ impl<'db> Signature<'db> {
ParameterKind::PositionalOrKeyword {
name: self_name,
default_type: self_default,
default_value: _,
},
ParameterKind::PositionalOrKeyword {
name: other_name,
default_type: other_default,
default_value: _,
},
) => {
if self_name != other_name {
@ -1359,10 +1365,12 @@ impl<'db> Signature<'db> {
ParameterKind::KeywordOnly {
name: other_name,
default_type: other_default,
default_value: _,
}
| ParameterKind::PositionalOrKeyword {
name: other_name,
default_type: other_default,
default_value: _,
} => {
if let Some(self_parameter) = self_keywords.remove(other_name.as_str()) {
match self_parameter.kind() {
@ -1689,6 +1697,15 @@ impl<'db> Parameters<'db> {
};
let pos_only_param = |param: &ast::ParameterWithDefault| {
let mut default_value = None;
if let Some(default) = &param.default {
use ruff_text_size::Ranged;
let file = definition.file(db);
let source = ruff_db::source::source_text(db, file);
let text = source.as_str().get(default.range().to_std_range());
default_value = text.map(Box::from);
}
Parameter::from_node_and_kind(
db,
definition,
@ -1696,6 +1713,7 @@ impl<'db> Parameters<'db> {
ParameterKind::PositionalOnly {
name: Some(param.parameter.name.id.clone()),
default_type: default_type(param),
default_value,
},
)
};
@ -1721,6 +1739,15 @@ impl<'db> Parameters<'db> {
}
let positional_or_keyword = pos_or_keyword_iter.map(|arg| {
let mut default_value = None;
if let Some(default) = &arg.default {
use ruff_text_size::Ranged;
let file = definition.file(db);
let source = ruff_db::source::source_text(db, file);
let text = source.as_str().get(default.range().to_std_range());
default_value = text.map(Box::from);
}
Parameter::from_node_and_kind(
db,
definition,
@ -1728,6 +1755,7 @@ impl<'db> Parameters<'db> {
ParameterKind::PositionalOrKeyword {
name: arg.parameter.name.id.clone(),
default_type: default_type(arg),
default_value,
},
)
});
@ -1744,6 +1772,15 @@ impl<'db> Parameters<'db> {
});
let keyword_only = kwonlyargs.iter().map(|arg| {
let mut default_value = None;
if let Some(default) = &arg.default {
use ruff_text_size::Ranged;
let file = definition.file(db);
let source = ruff_db::source::source_text(db, file);
let text = source.as_str().get(default.range().to_std_range());
default_value = text.map(Box::from);
}
Parameter::from_node_and_kind(
db,
definition,
@ -1751,6 +1788,7 @@ impl<'db> Parameters<'db> {
ParameterKind::KeywordOnly {
name: arg.parameter.name.id.clone(),
default_type: default_type(arg),
default_value,
},
)
});
@ -1914,6 +1952,7 @@ impl<'db> Parameter<'db> {
kind: ParameterKind::PositionalOnly {
name,
default_type: None,
default_value: None,
},
form: ParameterForm::Value,
}
@ -1926,6 +1965,7 @@ impl<'db> Parameter<'db> {
kind: ParameterKind::PositionalOrKeyword {
name,
default_type: None,
default_value: None,
},
form: ParameterForm::Value,
}
@ -1947,6 +1987,7 @@ impl<'db> Parameter<'db> {
kind: ParameterKind::KeywordOnly {
name,
default_type: None,
default_value: None,
},
form: ParameterForm::Value,
}
@ -2033,19 +2074,29 @@ impl<'db> Parameter<'db> {
ParameterKind::PositionalOnly {
name: _,
default_type,
default_value,
} => ParameterKind::PositionalOnly {
name: None,
default_type: default_type.map(|_| Type::Never),
default_value: default_value.clone(),
},
ParameterKind::PositionalOrKeyword { name, default_type } => {
ParameterKind::PositionalOrKeyword {
name,
default_type,
default_value,
} => ParameterKind::PositionalOrKeyword {
name: name.clone(),
default_type: default_type.map(|_| Type::Never),
}
}
ParameterKind::KeywordOnly { name, default_type } => ParameterKind::KeywordOnly {
default_value: default_value.clone(),
},
ParameterKind::KeywordOnly {
name,
default_type,
default_value,
} => ParameterKind::KeywordOnly {
name: name.clone(),
default_type: default_type.map(|_| Type::Never),
default_value: default_value.clone(),
},
ParameterKind::Variadic { name: _ } => ParameterKind::Variadic {
name: Name::new_static("args"),
@ -2086,7 +2137,11 @@ impl<'db> Parameter<'db> {
};
let kind = match kind {
ParameterKind::PositionalOnly { name, default_type } => ParameterKind::PositionalOnly {
ParameterKind::PositionalOnly {
name,
default_type,
default_value,
} => ParameterKind::PositionalOnly {
name: name.clone(),
default_type: match default_type {
Some(ty) if nested => Some(ty.recursive_type_normalized_impl(db, div, true)?),
@ -2096,23 +2151,13 @@ impl<'db> Parameter<'db> {
),
None => None,
},
default_value: default_value.clone(),
},
ParameterKind::PositionalOrKeyword { name, default_type } => {
ParameterKind::PositionalOrKeyword {
name: name.clone(),
default_type: match default_type {
Some(ty) if nested => {
Some(ty.recursive_type_normalized_impl(db, div, true)?)
}
Some(ty) => Some(
ty.recursive_type_normalized_impl(db, div, true)
.unwrap_or(div),
),
None => None,
},
}
}
ParameterKind::KeywordOnly { name, default_type } => ParameterKind::KeywordOnly {
name,
default_type,
default_value,
} => ParameterKind::PositionalOrKeyword {
name: name.clone(),
default_type: match default_type {
Some(ty) if nested => Some(ty.recursive_type_normalized_impl(db, div, true)?),
@ -2122,6 +2167,23 @@ impl<'db> Parameter<'db> {
),
None => None,
},
default_value: default_value.clone(),
},
ParameterKind::KeywordOnly {
name,
default_type,
default_value,
} => ParameterKind::KeywordOnly {
name: name.clone(),
default_type: match default_type {
Some(ty) if nested => Some(ty.recursive_type_normalized_impl(db, div, true)?),
Some(ty) => Some(
ty.recursive_type_normalized_impl(db, div, true)
.unwrap_or(div),
),
None => None,
},
default_value: default_value.clone(),
},
ParameterKind::Variadic { name } => ParameterKind::Variadic { name: name.clone() },
ParameterKind::KeywordVariadic { name } => {
@ -2238,9 +2300,19 @@ impl<'db> Parameter<'db> {
ParameterKind::Variadic { .. } | ParameterKind::KeywordVariadic { .. } => None,
}
}
/// Default-value of the parameter, if any.
pub(crate) fn default_value(&self) -> Option<&str> {
match &self.kind {
ParameterKind::PositionalOnly { default_value, .. }
| ParameterKind::PositionalOrKeyword { default_value, .. }
| ParameterKind::KeywordOnly { default_value, .. } => default_value.as_deref(),
ParameterKind::Variadic { .. } | ParameterKind::KeywordVariadic { .. } => None,
}
}
}
#[derive(Clone, Debug, PartialEq, Eq, Hash, salsa::Update, get_size2::GetSize)]
#[derive(Clone, Debug, salsa::Update, get_size2::GetSize)]
pub enum ParameterKind<'db> {
/// Positional-only parameter, e.g. `def f(x, /): ...`
PositionalOnly {
@ -2250,6 +2322,7 @@ pub enum ParameterKind<'db> {
/// nameless (e.g. via `Callable` annotations).
name: Option<Name>,
default_type: Option<Type<'db>>,
default_value: Option<Box<str>>,
},
/// Positional-or-keyword parameter, e.g. `def f(x): ...`
@ -2257,6 +2330,7 @@ pub enum ParameterKind<'db> {
/// Parameter name.
name: Name,
default_type: Option<Type<'db>>,
default_value: Option<Box<str>>,
},
/// Variadic parameter, e.g. `def f(*args): ...`
@ -2270,6 +2344,7 @@ pub enum ParameterKind<'db> {
/// Parameter name.
name: Name,
default_type: Option<Type<'db>>,
default_value: Option<Box<str>>,
},
/// Variadic keywords parameter, e.g. `def f(**kwargs): ...`
@ -2279,6 +2354,94 @@ pub enum ParameterKind<'db> {
},
}
impl PartialEq for ParameterKind<'_> {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(
Self::PositionalOnly {
name: l_name,
default_type: l_default_type,
default_value: _,
},
Self::PositionalOnly {
name: r_name,
default_type: r_default_type,
default_value: _,
},
) => l_name == r_name && l_default_type == r_default_type,
(
Self::PositionalOrKeyword {
name: l_name,
default_type: l_default_type,
default_value: _,
},
Self::PositionalOrKeyword {
name: r_name,
default_type: r_default_type,
default_value: _,
},
) => l_name == r_name && l_default_type == r_default_type,
(Self::Variadic { name: l_name }, Self::Variadic { name: r_name }) => l_name == r_name,
(
Self::KeywordOnly {
name: l_name,
default_type: l_default_type,
default_value: _,
},
Self::KeywordOnly {
name: r_name,
default_type: r_default_type,
default_value: _,
},
) => l_name == r_name && l_default_type == r_default_type,
(Self::KeywordVariadic { name: l_name }, Self::KeywordVariadic { name: r_name }) => {
l_name == r_name
}
_ => false,
}
}
}
impl Eq for ParameterKind<'_> {}
impl std::hash::Hash for ParameterKind<'_> {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
core::mem::discriminant(self).hash(state);
match self {
ParameterKind::PositionalOnly {
name,
default_type,
default_value: _,
} => {
name.hash(state);
default_type.hash(state);
}
ParameterKind::PositionalOrKeyword {
name,
default_type,
default_value: _,
} => {
name.hash(state);
default_type.hash(state);
}
ParameterKind::Variadic { name } => {
name.hash(state);
}
ParameterKind::KeywordOnly {
name,
default_type,
default_value: _,
} => {
name.hash(state);
default_type.hash(state);
}
ParameterKind::KeywordVariadic { name } => {
name.hash(state);
}
}
}
}
impl<'db> ParameterKind<'db> {
fn apply_type_mapping_impl<'a>(
&self,
@ -2298,16 +2461,31 @@ impl<'db> ParameterKind<'db> {
};
match self {
Self::PositionalOnly { default_type, name } => Self::PositionalOnly {
Self::PositionalOnly {
default_type,
default_value,
name,
} => Self::PositionalOnly {
default_type: apply_to_default_type(default_type),
default_value: default_value.clone(),
name: name.clone(),
},
Self::PositionalOrKeyword { default_type, name } => Self::PositionalOrKeyword {
Self::PositionalOrKeyword {
default_type,
default_value,
name,
} => Self::PositionalOrKeyword {
default_type: apply_to_default_type(default_type),
default_value: default_value.clone(),
name: name.clone(),
},
Self::KeywordOnly { default_type, name } => Self::KeywordOnly {
Self::KeywordOnly {
default_type,
default_value,
name,
} => Self::KeywordOnly {
default_type: apply_to_default_type(default_type),
default_value: default_value.clone(),
name: name.clone(),
},
Self::Variadic { .. } | Self::KeywordVariadic { .. } => self.clone(),

View File

@ -5,7 +5,7 @@ expression: signature_help
{
"signatures": [
{
"label": "(pattern: str | Pattern[str], string: str, flags: int = Literal[0]) -> Match[str] | None",
"label": "(pattern: str | Pattern[str], string: str, flags: int = 0) -> Match[str] | None",
"documentation": "Try to apply the pattern at the start of the string, returning/na Match object, or None if no match was found.\n",
"parameters": [
{
@ -15,12 +15,12 @@ expression: signature_help
"label": "string: str"
},
{
"label": "flags: int = Literal[0]"
"label": "flags: int = 0"
}
]
},
{
"label": "(pattern: bytes | Pattern[bytes], string: Buffer, flags: int = Literal[0]) -> Match[bytes] | None",
"label": "(pattern: bytes | Pattern[bytes], string: Buffer, flags: int = 0) -> Match[bytes] | None",
"parameters": [
{
"label": "pattern: bytes | Pattern[bytes]"
@ -29,7 +29,7 @@ expression: signature_help
"label": "string: Buffer"
},
{
"label": "flags: int = Literal[0]"
"label": "flags: int = 0"
}
]
}