[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) .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> { 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) symbol_ty_by_name(db, global_scope(db, file), name)
} }
/// Shorthand for `symbol_ty` that looks up a symbol in the builtins. /// 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> { pub(crate) fn builtins_symbol_ty_by_name<'db>(db: &'db dyn Db, name: &str) -> Type<'db> {
builtins_scope(db) builtins_scope(db)
.map(|builtins| symbol_ty_by_name(db, builtins, name)) .map(|builtins| symbol_ty_by_name(db, builtins, name))

View file

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