[red-knot] Move `name` field on parameter kind (#16830)

## Summary

Previously, the `name` field was on `Parameter` which required it to be
always optional regardless of the parameter kind because a
`typing.Callable` signature does not have name for the parameters. This
is the case for positional-only parameters. This wasn't enforced at the
type level which meant that downstream usages would have to unwrap on
`name` even though it's guaranteed to be present.

This commit moves the `name` field from `Parameter` to the
`ParameterKind` variants and makes it optional only for
`ParameterKind::PositionalOnly` variant while required for all other
variants.

One change that's now required is that a `Callable` form using a gradual
form for parameter types (`...`) would have a default `args` and
`kwargs` name used for variadic and keyword-variadic parameter kind
respectively. This is also the case for invalid `Callable` type forms. I
think this is fine as names are not relevant in this context but happy
to make it optional even in variadic variants.

## Test Plan

No new tests; make sure existing tests are passing.
This commit is contained in:
Dhruv Manilawala 2025-03-18 22:47:44 +05:30 committed by GitHub
parent ab3ec4de6a
commit c3d429ddd8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 274 additions and 169 deletions

View File

@ -2351,14 +2351,18 @@ impl<'db> Type<'db> {
Signature::new( Signature::new(
Parameters::new([ Parameters::new([
Parameter::new( Parameter::new(
Some(Name::new_static("instance")),
Some(Type::none(db)), Some(Type::none(db)),
ParameterKind::PositionalOnly { default_ty: None }, ParameterKind::PositionalOnly {
name: Some(Name::new_static("instance")),
default_ty: None,
},
), ),
Parameter::new( Parameter::new(
Some(Name::new_static("owner")),
Some(KnownClass::Type.to_instance(db)), Some(KnownClass::Type.to_instance(db)),
ParameterKind::PositionalOnly { default_ty: None }, ParameterKind::PositionalOnly {
name: Some(Name::new_static("owner")),
default_ty: None,
},
), ),
]), ]),
None, None,
@ -2366,17 +2370,19 @@ impl<'db> Type<'db> {
Signature::new( Signature::new(
Parameters::new([ Parameters::new([
Parameter::new( Parameter::new(
Some(Name::new_static("instance")),
Some(not_none), Some(not_none),
ParameterKind::PositionalOnly { default_ty: None }, ParameterKind::PositionalOnly {
name: Some(Name::new_static("instance")),
default_ty: None,
},
), ),
Parameter::new( Parameter::new(
Some(Name::new_static("owner")),
Some(UnionType::from_elements( Some(UnionType::from_elements(
db, db,
[KnownClass::Type.to_instance(db), Type::none(db)], [KnownClass::Type.to_instance(db), Type::none(db)],
)), )),
ParameterKind::PositionalOnly { ParameterKind::PositionalOnly {
name: Some(Name::new_static("owner")),
default_ty: Some(Type::none(db)), default_ty: Some(Type::none(db)),
}, },
), ),
@ -2402,19 +2408,25 @@ impl<'db> Type<'db> {
Signature::new( Signature::new(
Parameters::new([ Parameters::new([
Parameter::new( Parameter::new(
Some(Name::new_static("self")),
Some(KnownClass::FunctionType.to_instance(db)), Some(KnownClass::FunctionType.to_instance(db)),
ParameterKind::PositionalOnly { default_ty: None }, ParameterKind::PositionalOnly {
name: Some(Name::new_static("self")),
default_ty: None,
},
), ),
Parameter::new( Parameter::new(
Some(Name::new_static("instance")),
Some(Type::none(db)), Some(Type::none(db)),
ParameterKind::PositionalOnly { default_ty: None }, ParameterKind::PositionalOnly {
name: Some(Name::new_static("instance")),
default_ty: None,
},
), ),
Parameter::new( Parameter::new(
Some(Name::new_static("owner")),
Some(KnownClass::Type.to_instance(db)), Some(KnownClass::Type.to_instance(db)),
ParameterKind::PositionalOnly { default_ty: None }, ParameterKind::PositionalOnly {
name: Some(Name::new_static("owner")),
default_ty: None,
},
), ),
]), ]),
None, None,
@ -2422,22 +2434,26 @@ impl<'db> Type<'db> {
Signature::new( Signature::new(
Parameters::new([ Parameters::new([
Parameter::new( Parameter::new(
Some(Name::new_static("self")),
Some(KnownClass::FunctionType.to_instance(db)), Some(KnownClass::FunctionType.to_instance(db)),
ParameterKind::PositionalOnly { default_ty: None }, ParameterKind::PositionalOnly {
name: Some(Name::new_static("self")),
default_ty: None,
},
), ),
Parameter::new( Parameter::new(
Some(Name::new_static("instance")),
Some(not_none), Some(not_none),
ParameterKind::PositionalOnly { default_ty: None }, ParameterKind::PositionalOnly {
name: Some(Name::new_static("instance")),
default_ty: None,
},
), ),
Parameter::new( Parameter::new(
Some(Name::new_static("owner")),
Some(UnionType::from_elements( Some(UnionType::from_elements(
db, db,
[KnownClass::Type.to_instance(db), Type::none(db)], [KnownClass::Type.to_instance(db), Type::none(db)],
)), )),
ParameterKind::PositionalOnly { ParameterKind::PositionalOnly {
name: Some(Name::new_static("owner")),
default_ty: Some(Type::none(db)), default_ty: Some(Type::none(db)),
}, },
), ),
@ -2464,9 +2480,9 @@ impl<'db> Type<'db> {
self, self,
Signature::new( Signature::new(
Parameters::new([Parameter::new( Parameters::new([Parameter::new(
Some(Name::new_static("o")),
Some(Type::any()), Some(Type::any()),
ParameterKind::PositionalOnly { ParameterKind::PositionalOnly {
name: Some(Name::new_static("o")),
default_ty: Some(Type::BooleanLiteral(false)), default_ty: Some(Type::BooleanLiteral(false)),
}, },
)]), )]),
@ -2489,9 +2505,9 @@ impl<'db> Type<'db> {
[ [
Signature::new( Signature::new(
Parameters::new([Parameter::new( Parameters::new([Parameter::new(
Some(Name::new_static("o")),
Some(Type::any()), Some(Type::any()),
ParameterKind::PositionalOnly { ParameterKind::PositionalOnly {
name: Some(Name::new_static("o")),
default_ty: Some(Type::string_literal(db, "")), default_ty: Some(Type::string_literal(db, "")),
}, },
)]), )]),
@ -2500,19 +2516,25 @@ impl<'db> Type<'db> {
Signature::new( Signature::new(
Parameters::new([ Parameters::new([
Parameter::new( Parameter::new(
Some(Name::new_static("o")),
Some(Type::any()), // TODO: ReadableBuffer Some(Type::any()), // TODO: ReadableBuffer
ParameterKind::PositionalOnly { default_ty: None }, ParameterKind::PositionalOnly {
name: Some(Name::new_static("o")),
default_ty: None,
},
), ),
Parameter::new( Parameter::new(
Some(Name::new_static("encoding")),
Some(KnownClass::Str.to_instance(db)), Some(KnownClass::Str.to_instance(db)),
ParameterKind::PositionalOnly { default_ty: None }, ParameterKind::PositionalOnly {
name: Some(Name::new_static("encoding")),
default_ty: None,
},
), ),
Parameter::new( Parameter::new(
Some(Name::new_static("errors")),
Some(KnownClass::Str.to_instance(db)), Some(KnownClass::Str.to_instance(db)),
ParameterKind::PositionalOnly { default_ty: None }, ParameterKind::PositionalOnly {
name: Some(Name::new_static("errors")),
default_ty: None,
},
), ),
]), ]),
Some(KnownClass::Str.to_instance(db)), Some(KnownClass::Str.to_instance(db)),
@ -2535,28 +2557,36 @@ impl<'db> Type<'db> {
[ [
Signature::new( Signature::new(
Parameters::new([Parameter::new( Parameters::new([Parameter::new(
Some(Name::new_static("o")),
Some(Type::any()), Some(Type::any()),
ParameterKind::PositionalOnly { default_ty: None }, ParameterKind::PositionalOnly {
name: Some(Name::new_static("o")),
default_ty: None,
},
)]), )]),
Some(KnownClass::Type.to_instance(db)), Some(KnownClass::Type.to_instance(db)),
), ),
Signature::new( Signature::new(
Parameters::new([ Parameters::new([
Parameter::new( Parameter::new(
Some(Name::new_static("o")),
Some(Type::any()), Some(Type::any()),
ParameterKind::PositionalOnly { default_ty: None }, ParameterKind::PositionalOnly {
name: Some(Name::new_static("o")),
default_ty: None,
},
), ),
Parameter::new( Parameter::new(
Some(Name::new_static("bases")),
Some(Type::any()), Some(Type::any()),
ParameterKind::PositionalOnly { default_ty: None }, ParameterKind::PositionalOnly {
name: Some(Name::new_static("bases")),
default_ty: None,
},
), ),
Parameter::new( Parameter::new(
Some(Name::new_static("dict")),
Some(Type::any()), Some(Type::any()),
ParameterKind::PositionalOnly { default_ty: None }, ParameterKind::PositionalOnly {
name: Some(Name::new_static("dict")),
default_ty: None,
},
), ),
]), ]),
Some(KnownClass::Type.to_instance(db)), Some(KnownClass::Type.to_instance(db)),

View File

@ -575,13 +575,15 @@ mod tests {
display_signature( display_signature(
&db, &db,
[Parameter::new( [Parameter::new(
None,
Some(Type::none(&db)), Some(Type::none(&db)),
ParameterKind::PositionalOrKeyword { default_ty: None } ParameterKind::PositionalOnly {
name: None,
default_ty: None
}
)], )],
Some(Type::none(&db)) Some(Type::none(&db))
), ),
"(None) -> None" "(None, /) -> None"
); );
// Two parameters where one has annotation and the other doesn't. // Two parameters where one has annotation and the other doesn't.
@ -590,16 +592,16 @@ mod tests {
&db, &db,
[ [
Parameter::new( Parameter::new(
Some(Name::new_static("x")),
None, None,
ParameterKind::PositionalOrKeyword { ParameterKind::PositionalOrKeyword {
name: Name::new_static("x"),
default_ty: Some(KnownClass::Int.to_instance(&db)) default_ty: Some(KnownClass::Int.to_instance(&db))
} }
), ),
Parameter::new( Parameter::new(
Some(Name::new_static("y")),
Some(KnownClass::Str.to_instance(&db)), Some(KnownClass::Str.to_instance(&db)),
ParameterKind::PositionalOrKeyword { ParameterKind::PositionalOrKeyword {
name: Name::new_static("y"),
default_ty: Some(KnownClass::Str.to_instance(&db)) default_ty: Some(KnownClass::Str.to_instance(&db))
} }
) )
@ -615,14 +617,18 @@ mod tests {
&db, &db,
[ [
Parameter::new( Parameter::new(
Some(Name::new_static("x")),
None, None,
ParameterKind::PositionalOnly { default_ty: None } ParameterKind::PositionalOnly {
name: Some(Name::new_static("x")),
default_ty: None
}
), ),
Parameter::new( Parameter::new(
Some(Name::new_static("y")),
None, None,
ParameterKind::PositionalOnly { default_ty: None } ParameterKind::PositionalOnly {
name: Some(Name::new_static("y")),
default_ty: None
}
) )
], ],
Some(Type::none(&db)) Some(Type::none(&db))
@ -636,14 +642,18 @@ mod tests {
&db, &db,
[ [
Parameter::new( Parameter::new(
Some(Name::new_static("x")),
None, None,
ParameterKind::PositionalOnly { default_ty: None } ParameterKind::PositionalOnly {
name: Some(Name::new_static("x")),
default_ty: None
}
), ),
Parameter::new( Parameter::new(
Some(Name::new_static("y")),
None, None,
ParameterKind::PositionalOrKeyword { default_ty: None } ParameterKind::PositionalOrKeyword {
name: Name::new_static("y"),
default_ty: None
}
) )
], ],
Some(Type::none(&db)) Some(Type::none(&db))
@ -657,14 +667,18 @@ mod tests {
&db, &db,
[ [
Parameter::new( Parameter::new(
Some(Name::new_static("x")),
None, None,
ParameterKind::KeywordOnly { default_ty: None } ParameterKind::KeywordOnly {
name: Name::new_static("x"),
default_ty: None
}
), ),
Parameter::new( Parameter::new(
Some(Name::new_static("y")),
None, None,
ParameterKind::KeywordOnly { default_ty: None } ParameterKind::KeywordOnly {
name: Name::new_static("y"),
default_ty: None
}
) )
], ],
Some(Type::none(&db)) Some(Type::none(&db))
@ -678,14 +692,18 @@ mod tests {
&db, &db,
[ [
Parameter::new( Parameter::new(
Some(Name::new_static("x")),
None, None,
ParameterKind::PositionalOrKeyword { default_ty: None } ParameterKind::PositionalOrKeyword {
name: Name::new_static("x"),
default_ty: None
}
), ),
Parameter::new( Parameter::new(
Some(Name::new_static("y")),
None, None,
ParameterKind::KeywordOnly { default_ty: None } ParameterKind::KeywordOnly {
name: Name::new_static("y"),
default_ty: None
}
) )
], ],
Some(Type::none(&db)) Some(Type::none(&db))
@ -699,66 +717,72 @@ mod tests {
&db, &db,
[ [
Parameter::new( Parameter::new(
Some(Name::new_static("a")),
None,
ParameterKind::PositionalOnly { default_ty: None },
),
Parameter::new(
Some(Name::new_static("b")),
Some(KnownClass::Int.to_instance(&db)),
ParameterKind::PositionalOnly { default_ty: None },
),
Parameter::new(
Some(Name::new_static("c")),
None, None,
ParameterKind::PositionalOnly { ParameterKind::PositionalOnly {
name: Some(Name::new_static("a")),
default_ty: None
},
),
Parameter::new(
Some(KnownClass::Int.to_instance(&db)),
ParameterKind::PositionalOnly {
name: Some(Name::new_static("b")),
default_ty: None
},
),
Parameter::new(
None,
ParameterKind::PositionalOnly {
name: Some(Name::new_static("c")),
default_ty: Some(Type::IntLiteral(1)), default_ty: Some(Type::IntLiteral(1)),
}, },
), ),
Parameter::new( Parameter::new(
Some(Name::new_static("d")),
Some(KnownClass::Int.to_instance(&db)), Some(KnownClass::Int.to_instance(&db)),
ParameterKind::PositionalOnly { ParameterKind::PositionalOnly {
name: Some(Name::new_static("d")),
default_ty: Some(Type::IntLiteral(2)), default_ty: Some(Type::IntLiteral(2)),
}, },
), ),
Parameter::new( Parameter::new(
Some(Name::new_static("e")),
None, None,
ParameterKind::PositionalOrKeyword { ParameterKind::PositionalOrKeyword {
name: Name::new_static("e"),
default_ty: Some(Type::IntLiteral(3)), default_ty: Some(Type::IntLiteral(3)),
}, },
), ),
Parameter::new( Parameter::new(
Some(Name::new_static("f")),
Some(KnownClass::Int.to_instance(&db)), Some(KnownClass::Int.to_instance(&db)),
ParameterKind::PositionalOrKeyword { ParameterKind::PositionalOrKeyword {
name: Name::new_static("f"),
default_ty: Some(Type::IntLiteral(4)), default_ty: Some(Type::IntLiteral(4)),
}, },
), ),
Parameter::new( Parameter::new(
Some(Name::new_static("args")),
Some(Type::object(&db)), Some(Type::object(&db)),
ParameterKind::Variadic, ParameterKind::Variadic {
name: Name::new_static("args")
},
), ),
Parameter::new( Parameter::new(
Some(Name::new_static("g")),
None, None,
ParameterKind::KeywordOnly { ParameterKind::KeywordOnly {
name: Name::new_static("g"),
default_ty: Some(Type::IntLiteral(5)), default_ty: Some(Type::IntLiteral(5)),
}, },
), ),
Parameter::new( Parameter::new(
Some(Name::new_static("h")),
Some(KnownClass::Int.to_instance(&db)), Some(KnownClass::Int.to_instance(&db)),
ParameterKind::KeywordOnly { ParameterKind::KeywordOnly {
name: Name::new_static("h"),
default_ty: Some(Type::IntLiteral(6)), default_ty: Some(Type::IntLiteral(6)),
}, },
), ),
Parameter::new( Parameter::new(
Some(Name::new_static("kwargs")),
Some(KnownClass::Str.to_instance(&db)), Some(KnownClass::Str.to_instance(&db)),
ParameterKind::KeywordVariadic, ParameterKind::KeywordVariadic {
name: Name::new_static("kwargs")
},
), ),
], ],
Some(KnownClass::Bytes.to_instance(&db)) Some(KnownClass::Bytes.to_instance(&db))

View File

@ -3771,9 +3771,9 @@ impl<'db> TypeInferenceBuilder<'db> {
.iter() .iter()
.map(|parameter| { .map(|parameter| {
Parameter::new( Parameter::new(
Some(parameter.name().id.clone()),
None, None,
ParameterKind::PositionalOnly { ParameterKind::PositionalOnly {
name: Some(parameter.name().id.clone()),
default_ty: parameter default_ty: parameter
.default() .default()
.map(|default| self.infer_expression(default)), .map(|default| self.infer_expression(default)),
@ -3786,9 +3786,9 @@ impl<'db> TypeInferenceBuilder<'db> {
.iter() .iter()
.map(|parameter| { .map(|parameter| {
Parameter::new( Parameter::new(
Some(parameter.name().id.clone()),
None, None,
ParameterKind::PositionalOrKeyword { ParameterKind::PositionalOrKeyword {
name: parameter.name().id.clone(),
default_ty: parameter default_ty: parameter
.default() .default()
.map(|default| self.infer_expression(default)), .map(|default| self.infer_expression(default)),
@ -3798,9 +3798,10 @@ impl<'db> TypeInferenceBuilder<'db> {
.collect::<Vec<_>>(); .collect::<Vec<_>>();
let variadic = parameters.vararg.as_ref().map(|parameter| { let variadic = parameters.vararg.as_ref().map(|parameter| {
Parameter::new( Parameter::new(
Some(parameter.name.id.clone()),
None, None,
ParameterKind::Variadic, ParameterKind::Variadic {
name: parameter.name.id.clone(),
},
) )
}); });
let keyword_only = parameters let keyword_only = parameters
@ -3808,9 +3809,9 @@ impl<'db> TypeInferenceBuilder<'db> {
.iter() .iter()
.map(|parameter| { .map(|parameter| {
Parameter::new( Parameter::new(
Some(parameter.name().id.clone()),
None, None,
ParameterKind::KeywordOnly { ParameterKind::KeywordOnly {
name: parameter.name().id.clone(),
default_ty: parameter default_ty: parameter
.default() .default()
.map(|default| self.infer_expression(default)), .map(|default| self.infer_expression(default)),
@ -3820,9 +3821,10 @@ impl<'db> TypeInferenceBuilder<'db> {
.collect::<Vec<_>>(); .collect::<Vec<_>>();
let keyword_variadic = parameters.kwarg.as_ref().map(|parameter| { let keyword_variadic = parameters.kwarg.as_ref().map(|parameter| {
Parameter::new( Parameter::new(
Some(parameter.name.id.clone()),
None, None,
ParameterKind::KeywordVariadic, ParameterKind::KeywordVariadic {
name: parameter.name.id.clone(),
},
) )
}); });
@ -6973,9 +6975,11 @@ impl<'db> TypeInferenceBuilder<'db> {
} else { } else {
Parameters::new(parameter_types.iter().map(|param_type| { Parameters::new(parameter_types.iter().map(|param_type| {
Parameter::new( Parameter::new(
None,
Some(*param_type), Some(*param_type),
ParameterKind::PositionalOnly { default_ty: None }, ParameterKind::PositionalOnly {
name: None,
default_ty: None,
},
) )
})) }))
} }

View File

@ -307,14 +307,16 @@ impl<'db> Parameters<'db> {
Self { Self {
value: vec![ value: vec![
Parameter { Parameter {
name: Some(Name::new_static("args")),
annotated_ty: Some(todo_type!("todo signature *args")), annotated_ty: Some(todo_type!("todo signature *args")),
kind: ParameterKind::Variadic, kind: ParameterKind::Variadic {
name: Name::new_static("args"),
},
}, },
Parameter { Parameter {
name: Some(Name::new_static("kwargs")),
annotated_ty: Some(todo_type!("todo signature **kwargs")), annotated_ty: Some(todo_type!("todo signature **kwargs")),
kind: ParameterKind::KeywordVariadic, kind: ParameterKind::KeywordVariadic {
name: Name::new_static("kwargs"),
},
}, },
], ],
is_gradual: false, is_gradual: false,
@ -330,14 +332,16 @@ impl<'db> Parameters<'db> {
Self { Self {
value: vec![ value: vec![
Parameter { Parameter {
name: None,
annotated_ty: Some(Type::Dynamic(DynamicType::Any)), annotated_ty: Some(Type::Dynamic(DynamicType::Any)),
kind: ParameterKind::Variadic, kind: ParameterKind::Variadic {
name: Name::new_static("args"),
},
}, },
Parameter { Parameter {
name: None,
annotated_ty: Some(Type::Dynamic(DynamicType::Any)), annotated_ty: Some(Type::Dynamic(DynamicType::Any)),
kind: ParameterKind::KeywordVariadic, kind: ParameterKind::KeywordVariadic {
name: Name::new_static("kwargs"),
},
}, },
], ],
is_gradual: true, is_gradual: true,
@ -354,14 +358,16 @@ impl<'db> Parameters<'db> {
Self { Self {
value: vec![ value: vec![
Parameter { Parameter {
name: None,
annotated_ty: Some(Type::Dynamic(DynamicType::Unknown)), annotated_ty: Some(Type::Dynamic(DynamicType::Unknown)),
kind: ParameterKind::Variadic, kind: ParameterKind::Variadic {
name: Name::new_static("args"),
},
}, },
Parameter { Parameter {
name: None,
annotated_ty: Some(Type::Dynamic(DynamicType::Unknown)), annotated_ty: Some(Type::Dynamic(DynamicType::Unknown)),
kind: ParameterKind::KeywordVariadic, kind: ParameterKind::KeywordVariadic {
name: Name::new_static("kwargs"),
},
}, },
], ],
is_gradual: true, is_gradual: true,
@ -392,6 +398,7 @@ impl<'db> Parameters<'db> {
definition, definition,
&arg.parameter, &arg.parameter,
ParameterKind::PositionalOnly { ParameterKind::PositionalOnly {
name: Some(arg.parameter.name.id.clone()),
default_ty: default_ty(arg), default_ty: default_ty(arg),
}, },
) )
@ -402,25 +409,41 @@ impl<'db> Parameters<'db> {
definition, definition,
&arg.parameter, &arg.parameter,
ParameterKind::PositionalOrKeyword { ParameterKind::PositionalOrKeyword {
name: arg.parameter.name.id.clone(),
default_ty: default_ty(arg), default_ty: default_ty(arg),
}, },
) )
}); });
let variadic = vararg let variadic = vararg.as_ref().map(|arg| {
.as_ref() Parameter::from_node_and_kind(
.map(|arg| Parameter::from_node_and_kind(db, definition, arg, ParameterKind::Variadic)); db,
definition,
arg,
ParameterKind::Variadic {
name: arg.name.id.clone(),
},
)
});
let keyword_only = kwonlyargs.iter().map(|arg| { let keyword_only = kwonlyargs.iter().map(|arg| {
Parameter::from_node_and_kind( Parameter::from_node_and_kind(
db, db,
definition, definition,
&arg.parameter, &arg.parameter,
ParameterKind::KeywordOnly { ParameterKind::KeywordOnly {
name: arg.parameter.name.id.clone(),
default_ty: default_ty(arg), default_ty: default_ty(arg),
}, },
) )
}); });
let keywords = kwarg.as_ref().map(|arg| { let keywords = kwarg.as_ref().map(|arg| {
Parameter::from_node_and_kind(db, definition, arg, ParameterKind::KeywordVariadic) Parameter::from_node_and_kind(
db,
definition,
arg,
ParameterKind::KeywordVariadic {
name: arg.name.id.clone(),
},
)
}); });
Self::new( Self::new(
positional_only positional_only
@ -507,12 +530,6 @@ impl<'db> std::ops::Index<usize> for Parameters<'db> {
#[derive(Clone, Debug, PartialEq, Eq, Hash, salsa::Update)] #[derive(Clone, Debug, PartialEq, Eq, Hash, salsa::Update)]
pub(crate) struct Parameter<'db> { pub(crate) struct Parameter<'db> {
/// Parameter name.
///
/// It is possible for signatures to be defined in ways that leave positional-only parameters
/// nameless (e.g. via `Callable` annotations).
name: Option<Name>,
/// Annotated type of the parameter. /// Annotated type of the parameter.
annotated_ty: Option<Type<'db>>, annotated_ty: Option<Type<'db>>,
@ -520,16 +537,8 @@ pub(crate) struct Parameter<'db> {
} }
impl<'db> Parameter<'db> { impl<'db> Parameter<'db> {
pub(crate) fn new( pub(crate) fn new(annotated_ty: Option<Type<'db>>, kind: ParameterKind<'db>) -> Self {
name: Option<Name>, Self { annotated_ty, kind }
annotated_ty: Option<Type<'db>>,
kind: ParameterKind<'db>,
) -> Self {
Self {
name,
annotated_ty,
kind,
}
} }
fn from_node_and_kind( fn from_node_and_kind(
@ -539,7 +548,6 @@ impl<'db> Parameter<'db> {
kind: ParameterKind<'db>, kind: ParameterKind<'db>,
) -> Self { ) -> Self {
Self { Self {
name: Some(parameter.name.id.clone()),
annotated_ty: parameter annotated_ty: parameter
.annotation() .annotation()
.map(|annotation| definition_expression_type(db, definition, annotation)), .map(|annotation| definition_expression_type(db, definition, annotation)),
@ -547,22 +555,28 @@ impl<'db> Parameter<'db> {
} }
} }
/// Returns `true` if this is a keyword-only parameter.
pub(crate) fn is_keyword_only(&self) -> bool { pub(crate) fn is_keyword_only(&self) -> bool {
matches!(self.kind, ParameterKind::KeywordOnly { .. }) matches!(self.kind, ParameterKind::KeywordOnly { .. })
} }
/// Returns `true` if this is a positional-only parameter.
pub(crate) fn is_positional_only(&self) -> bool { pub(crate) fn is_positional_only(&self) -> bool {
matches!(self.kind, ParameterKind::PositionalOnly { .. }) matches!(self.kind, ParameterKind::PositionalOnly { .. })
} }
/// Returns `true` if this is a variadic parameter.
pub(crate) fn is_variadic(&self) -> bool { pub(crate) fn is_variadic(&self) -> bool {
matches!(self.kind, ParameterKind::Variadic) matches!(self.kind, ParameterKind::Variadic { .. })
} }
/// Returns `true` if this is a keyword-variadic parameter.
pub(crate) fn is_keyword_variadic(&self) -> bool { pub(crate) fn is_keyword_variadic(&self) -> bool {
matches!(self.kind, ParameterKind::KeywordVariadic) matches!(self.kind, ParameterKind::KeywordVariadic { .. })
} }
/// Returns `true` if this is either a positional-only or standard (positional or keyword)
/// parameter.
pub(crate) fn is_positional(&self) -> bool { pub(crate) fn is_positional(&self) -> bool {
matches!( matches!(
self.kind, self.kind,
@ -571,11 +585,13 @@ impl<'db> Parameter<'db> {
} }
pub(crate) fn callable_by_name(&self, name: &str) -> bool { pub(crate) fn callable_by_name(&self, name: &str) -> bool {
match self.kind { match &self.kind {
ParameterKind::PositionalOrKeyword { .. } | ParameterKind::KeywordOnly { .. } => self ParameterKind::PositionalOrKeyword {
.name name: param_name, ..
.as_ref() }
.is_some_and(|param_name| param_name == name), | ParameterKind::KeywordOnly {
name: param_name, ..
} => param_name == name,
_ => false, _ => false,
} }
} }
@ -587,14 +603,20 @@ impl<'db> Parameter<'db> {
/// Name of the parameter (if it has one). /// Name of the parameter (if it has one).
pub(crate) fn name(&self) -> Option<&ast::name::Name> { pub(crate) fn name(&self) -> Option<&ast::name::Name> {
self.name.as_ref() match &self.kind {
ParameterKind::PositionalOnly { name, .. } => name.as_ref(),
ParameterKind::PositionalOrKeyword { name, .. } => Some(name),
ParameterKind::Variadic { name } => Some(name),
ParameterKind::KeywordOnly { name, .. } => Some(name),
ParameterKind::KeywordVariadic { name } => Some(name),
}
} }
/// Display name of the parameter, if it has one. /// Display name of the parameter, if it has one.
pub(crate) fn display_name(&self) -> Option<ast::name::Name> { pub(crate) fn display_name(&self) -> Option<ast::name::Name> {
self.name().map(|name| match self.kind { self.name().map(|name| match self.kind {
ParameterKind::Variadic => ast::name::Name::new(format!("*{name}")), ParameterKind::Variadic { .. } => ast::name::Name::new(format!("*{name}")),
ParameterKind::KeywordVariadic => ast::name::Name::new(format!("**{name}")), ParameterKind::KeywordVariadic { .. } => ast::name::Name::new(format!("**{name}")),
_ => name.clone(), _ => name.clone(),
}) })
} }
@ -602,11 +624,11 @@ impl<'db> Parameter<'db> {
/// Default-value type of the parameter, if any. /// Default-value type of the parameter, if any.
pub(crate) fn default_type(&self) -> Option<Type<'db>> { pub(crate) fn default_type(&self) -> Option<Type<'db>> {
match self.kind { match self.kind {
ParameterKind::PositionalOnly { default_ty } => default_ty, ParameterKind::PositionalOnly { default_ty, .. } => default_ty,
ParameterKind::PositionalOrKeyword { default_ty } => default_ty, ParameterKind::PositionalOrKeyword { default_ty, .. } => default_ty,
ParameterKind::Variadic => None, ParameterKind::Variadic { .. } => None,
ParameterKind::KeywordOnly { default_ty } => default_ty, ParameterKind::KeywordOnly { default_ty, .. } => default_ty,
ParameterKind::KeywordVariadic => None, ParameterKind::KeywordVariadic { .. } => None,
} }
} }
} }
@ -614,15 +636,40 @@ impl<'db> Parameter<'db> {
#[derive(Clone, Debug, PartialEq, Eq, Hash, salsa::Update)] #[derive(Clone, Debug, PartialEq, Eq, Hash, salsa::Update)]
pub(crate) enum ParameterKind<'db> { pub(crate) enum ParameterKind<'db> {
/// Positional-only parameter, e.g. `def f(x, /): ...` /// Positional-only parameter, e.g. `def f(x, /): ...`
PositionalOnly { default_ty: Option<Type<'db>> }, PositionalOnly {
/// Parameter name.
///
/// It is possible for signatures to be defined in ways that leave positional-only parameters
/// nameless (e.g. via `Callable` annotations).
name: Option<Name>,
default_ty: Option<Type<'db>>,
},
/// Positional-or-keyword parameter, e.g. `def f(x): ...` /// Positional-or-keyword parameter, e.g. `def f(x): ...`
PositionalOrKeyword { default_ty: Option<Type<'db>> }, PositionalOrKeyword {
/// Parameter name.
name: Name,
default_ty: Option<Type<'db>>,
},
/// Variadic parameter, e.g. `def f(*args): ...` /// Variadic parameter, e.g. `def f(*args): ...`
Variadic, Variadic {
/// Parameter name.
name: Name,
},
/// Keyword-only parameter, e.g. `def f(*, x): ...` /// Keyword-only parameter, e.g. `def f(*, x): ...`
KeywordOnly { default_ty: Option<Type<'db>> }, KeywordOnly {
/// Parameter name.
name: Name,
default_ty: Option<Type<'db>>,
},
/// Variadic keywords parameter, e.g. `def f(**kwargs): ...` /// Variadic keywords parameter, e.g. `def f(**kwargs): ...`
KeywordVariadic, KeywordVariadic {
/// Parameter name.
name: Name,
},
} }
#[cfg(test)] #[cfg(test)]
@ -683,66 +730,72 @@ mod tests {
&sig, &sig,
&[ &[
Parameter { Parameter {
name: Some(Name::new_static("a")),
annotated_ty: None,
kind: ParameterKind::PositionalOnly { default_ty: None },
},
Parameter {
name: Some(Name::new_static("b")),
annotated_ty: Some(KnownClass::Int.to_instance(&db)),
kind: ParameterKind::PositionalOnly { default_ty: None },
},
Parameter {
name: Some(Name::new_static("c")),
annotated_ty: None, annotated_ty: None,
kind: ParameterKind::PositionalOnly { kind: ParameterKind::PositionalOnly {
name: Some(Name::new_static("a")),
default_ty: None,
},
},
Parameter {
annotated_ty: Some(KnownClass::Int.to_instance(&db)),
kind: ParameterKind::PositionalOnly {
name: Some(Name::new_static("b")),
default_ty: None,
},
},
Parameter {
annotated_ty: None,
kind: ParameterKind::PositionalOnly {
name: Some(Name::new_static("c")),
default_ty: Some(Type::IntLiteral(1)), default_ty: Some(Type::IntLiteral(1)),
}, },
}, },
Parameter { Parameter {
name: Some(Name::new_static("d")),
annotated_ty: Some(KnownClass::Int.to_instance(&db)), annotated_ty: Some(KnownClass::Int.to_instance(&db)),
kind: ParameterKind::PositionalOnly { kind: ParameterKind::PositionalOnly {
name: Some(Name::new_static("d")),
default_ty: Some(Type::IntLiteral(2)), default_ty: Some(Type::IntLiteral(2)),
}, },
}, },
Parameter { Parameter {
name: Some(Name::new_static("e")),
annotated_ty: None, annotated_ty: None,
kind: ParameterKind::PositionalOrKeyword { kind: ParameterKind::PositionalOrKeyword {
name: Name::new_static("e"),
default_ty: Some(Type::IntLiteral(3)), default_ty: Some(Type::IntLiteral(3)),
}, },
}, },
Parameter { Parameter {
name: Some(Name::new_static("f")),
annotated_ty: Some(Type::IntLiteral(4)), annotated_ty: Some(Type::IntLiteral(4)),
kind: ParameterKind::PositionalOrKeyword { kind: ParameterKind::PositionalOrKeyword {
name: Name::new_static("f"),
default_ty: Some(Type::IntLiteral(4)), default_ty: Some(Type::IntLiteral(4)),
}, },
}, },
Parameter { Parameter {
name: Some(Name::new_static("args")),
annotated_ty: Some(Type::object(&db)), annotated_ty: Some(Type::object(&db)),
kind: ParameterKind::Variadic, kind: ParameterKind::Variadic {
name: Name::new_static("args"),
},
}, },
Parameter { Parameter {
name: Some(Name::new_static("g")),
annotated_ty: None, annotated_ty: None,
kind: ParameterKind::KeywordOnly { kind: ParameterKind::KeywordOnly {
name: Name::new_static("g"),
default_ty: Some(Type::IntLiteral(5)), default_ty: Some(Type::IntLiteral(5)),
}, },
}, },
Parameter { Parameter {
name: Some(Name::new_static("h")),
annotated_ty: Some(Type::IntLiteral(6)), annotated_ty: Some(Type::IntLiteral(6)),
kind: ParameterKind::KeywordOnly { kind: ParameterKind::KeywordOnly {
name: Name::new_static("h"),
default_ty: Some(Type::IntLiteral(6)), default_ty: Some(Type::IntLiteral(6)),
}, },
}, },
Parameter { Parameter {
name: Some(Name::new_static("kwargs")),
annotated_ty: Some(KnownClass::Str.to_instance(&db)), annotated_ty: Some(KnownClass::Str.to_instance(&db)),
kind: ParameterKind::KeywordVariadic, kind: ParameterKind::KeywordVariadic {
name: Name::new_static("kwargs"),
},
}, },
], ],
); );
@ -770,9 +823,8 @@ mod tests {
let sig = func.internal_signature(&db); let sig = func.internal_signature(&db);
let [Parameter { let [Parameter {
name: Some(name),
annotated_ty, annotated_ty,
kind: ParameterKind::PositionalOrKeyword { .. }, kind: ParameterKind::PositionalOrKeyword { name, .. },
}] = &sig.parameters.value[..] }] = &sig.parameters.value[..]
else { else {
panic!("expected one positional-or-keyword parameter"); panic!("expected one positional-or-keyword parameter");
@ -804,9 +856,8 @@ mod tests {
let sig = func.internal_signature(&db); let sig = func.internal_signature(&db);
let [Parameter { let [Parameter {
name: Some(name),
annotated_ty, annotated_ty,
kind: ParameterKind::PositionalOrKeyword { .. }, kind: ParameterKind::PositionalOrKeyword { name, .. },
}] = &sig.parameters.value[..] }] = &sig.parameters.value[..]
else { else {
panic!("expected one positional-or-keyword parameter"); panic!("expected one positional-or-keyword parameter");
@ -838,13 +889,11 @@ mod tests {
let sig = func.internal_signature(&db); let sig = func.internal_signature(&db);
let [Parameter { let [Parameter {
name: Some(a_name),
annotated_ty: a_annotated_ty, annotated_ty: a_annotated_ty,
kind: ParameterKind::PositionalOrKeyword { .. }, kind: ParameterKind::PositionalOrKeyword { name: a_name, .. },
}, Parameter { }, Parameter {
name: Some(b_name),
annotated_ty: b_annotated_ty, annotated_ty: b_annotated_ty,
kind: ParameterKind::PositionalOrKeyword { .. }, kind: ParameterKind::PositionalOrKeyword { name: b_name, .. },
}] = &sig.parameters.value[..] }] = &sig.parameters.value[..]
else { else {
panic!("expected two positional-or-keyword parameters"); panic!("expected two positional-or-keyword parameters");
@ -881,13 +930,11 @@ mod tests {
let sig = func.internal_signature(&db); let sig = func.internal_signature(&db);
let [Parameter { let [Parameter {
name: Some(a_name),
annotated_ty: a_annotated_ty, annotated_ty: a_annotated_ty,
kind: ParameterKind::PositionalOrKeyword { .. }, kind: ParameterKind::PositionalOrKeyword { name: a_name, .. },
}, Parameter { }, Parameter {
name: Some(b_name),
annotated_ty: b_annotated_ty, annotated_ty: b_annotated_ty,
kind: ParameterKind::PositionalOrKeyword { .. }, kind: ParameterKind::PositionalOrKeyword { name: b_name, .. },
}] = &sig.parameters.value[..] }] = &sig.parameters.value[..]
else { else {
panic!("expected two positional-or-keyword parameters"); panic!("expected two positional-or-keyword parameters");