[red-knot] resolve int/list/dict/set/tuple to builtin type (#12521)

Now that we have builtins available, resolve some simple cases to the
right builtin type.

We should also adjust the display for types to include their module
name; that's not done yet here.
This commit is contained in:
Carl Meyer 2024-07-26 08:21:31 -07:00 committed by GitHub
parent bf23d38a21
commit 4b69271809
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 102 additions and 23 deletions

View file

@ -43,14 +43,14 @@ pub(crate) fn symbol_ty_by_name<'db>(
.unwrap_or(Type::Unbound)
}
/// Shorthand for `symbol_ty` that looks up a module-global symbol in a file.
/// Shorthand for `symbol_ty` that looks up a module-global symbol by name in a file.
pub(crate) fn global_symbol_ty_by_name<'db>(db: &'db dyn Db, file: File, name: &str) -> Type<'db> {
symbol_ty_by_name(db, global_scope(db, file), name)
}
/// Shorthand for `symbol_ty` that looks up a symbol in the builtins.
///
/// Returns `None` if the builtins module isn't available for some reason.
/// Returns `Unbound` if the builtins module isn't available for some reason.
pub(crate) fn builtins_symbol_ty_by_name<'db>(db: &'db dyn Db, name: &str) -> Type<'db> {
builtins_scope(db)
.map(|builtins| symbol_ty_by_name(db, builtins, name))

View file

@ -553,7 +553,6 @@ impl<'db> TypeInferenceBuilder<'db> {
pattern,
guard,
} = case;
// TODO infer case patterns; they aren't normal expressions
self.infer_match_pattern(pattern);
self.infer_optional_expression(guard.as_deref());
self.infer_body(body);
@ -920,10 +919,10 @@ impl<'db> TypeInferenceBuilder<'db> {
let ast::ExprNumberLiteral { range: _, value } = literal;
match value {
ast::Number::Int(n) => {
// TODO support big int literals
n.as_i64().map(Type::IntLiteral).unwrap_or(Type::Unknown)
}
ast::Number::Int(n) => n
.as_i64()
.map(Type::IntLiteral)
.unwrap_or_else(|| builtins_symbol_ty_by_name(self.db, "int")),
// TODO builtins.float or builtins.complex
_ => Type::Unknown,
}
@ -1004,8 +1003,8 @@ impl<'db> TypeInferenceBuilder<'db> {
self.infer_expression(elt);
}
// TODO tuple type
Type::Unknown
// TODO generic
builtins_symbol_ty_by_name(self.db, "tuple")
}
fn infer_list_expression(&mut self, list: &ast::ExprList) -> Type<'db> {
@ -1019,8 +1018,8 @@ impl<'db> TypeInferenceBuilder<'db> {
self.infer_expression(elt);
}
// TODO list type
Type::Unknown
// TODO generic
builtins_symbol_ty_by_name(self.db, "list")
}
fn infer_set_expression(&mut self, set: &ast::ExprSet) -> Type<'db> {
@ -1030,8 +1029,8 @@ impl<'db> TypeInferenceBuilder<'db> {
self.infer_expression(elt);
}
// TODO set type
Type::Unknown
// TODO generic
builtins_symbol_ty_by_name(self.db, "set")
}
fn infer_dict_expression(&mut self, dict: &ast::ExprDict) -> Type<'db> {
@ -1042,8 +1041,8 @@ impl<'db> TypeInferenceBuilder<'db> {
self.infer_expression(&item.value);
}
// TODO dict type
Type::Unknown
// TODO generic
builtins_symbol_ty_by_name(self.db, "dict")
}
fn infer_generator_expression(&mut self, generator: &ast::ExprGenerator) -> Type<'db> {
@ -1346,23 +1345,19 @@ impl<'db> TypeInferenceBuilder<'db> {
ast::Operator::Add => n
.checked_add(m)
.map(Type::IntLiteral)
// TODO builtins.int
.unwrap_or(Type::Unknown),
.unwrap_or_else(|| builtins_symbol_ty_by_name(self.db, "int")),
ast::Operator::Sub => n
.checked_sub(m)
.map(Type::IntLiteral)
// TODO builtins.int
.unwrap_or(Type::Unknown),
.unwrap_or_else(|| builtins_symbol_ty_by_name(self.db, "int")),
ast::Operator::Mult => n
.checked_mul(m)
.map(Type::IntLiteral)
// TODO builtins.int
.unwrap_or(Type::Unknown),
.unwrap_or_else(|| builtins_symbol_ty_by_name(self.db, "int")),
ast::Operator::Div => n
.checked_div(m)
.map(Type::IntLiteral)
// TODO builtins.int
.unwrap_or(Type::Unknown),
.unwrap_or_else(|| builtins_symbol_ty_by_name(self.db, "int")),
ast::Operator::Mod => n
.checked_rem(m)
.map(Type::IntLiteral)
@ -2236,6 +2231,90 @@ mod tests {
Ok(())
}
#[test]
fn big_int() -> anyhow::Result<()> {
let mut db = setup_db();
db.write_dedented(
"/src/a.py",
"
x = 10_000_000_000_000_000_000
",
)?;
assert_public_ty(&db, "/src/a.py", "x", "Literal[int]");
Ok(())
}
#[test]
fn tuple_literal() -> anyhow::Result<()> {
let mut db = setup_db();
db.write_dedented(
"/src/a.py",
"
x = ()
",
)?;
// TODO should be a generic type
assert_public_ty(&db, "/src/a.py", "x", "Literal[tuple]");
Ok(())
}
#[test]
fn list_literal() -> anyhow::Result<()> {
let mut db = setup_db();
db.write_dedented(
"/src/a.py",
"
x = []
",
)?;
// TODO should be a generic type
assert_public_ty(&db, "/src/a.py", "x", "Literal[list]");
Ok(())
}
#[test]
fn set_literal() -> anyhow::Result<()> {
let mut db = setup_db();
db.write_dedented(
"/src/a.py",
"
x = {1, 2}
",
)?;
// TODO should be a generic type
assert_public_ty(&db, "/src/a.py", "x", "Literal[set]");
Ok(())
}
#[test]
fn dict_literal() -> anyhow::Result<()> {
let mut db = setup_db();
db.write_dedented(
"/src/a.py",
"
x = {}
",
)?;
// TODO should be a generic type
assert_public_ty(&db, "/src/a.py", "x", "Literal[dict]");
Ok(())
}
fn first_public_def<'db>(db: &'db TestDb, file: File, name: &str) -> Definition<'db> {
let scope = global_scope(db, file);
*use_def_map(db, scope)