Migrate some inference tests to mdtests (#14795)

As part of #13696, this PR ports a smallish number of inference tests
over to the mdtest framework.
This commit is contained in:
Douglas Creager 2024-12-06 05:19:22 -05:00 committed by GitHub
parent b01a651e69
commit 918358aaa6
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 139 additions and 248 deletions

View file

@ -18,43 +18,3 @@ Note: in this particular example, one could argue that the most likely error wou
of the `x`/`foo` definitions, and so it could be desirable to infer `Literal[1]` for the type of
`x`. On the other hand, there might be a variable `fob` a little higher up in this file, and the
actual error might have been just a typo. Inferring `Unknown` thus seems like the safest option.
## Unbound class variable
Name lookups within a class scope fall back to globals, but lookups of class attributes don't.
```py
def bool_instance() -> bool:
return True
flag = bool_instance()
x = 1
class C:
y = x
if flag:
x = 2
# error: [possibly-unbound-attribute] "Attribute `x` on type `Literal[C]` is possibly unbound"
reveal_type(C.x) # revealed: Literal[2]
reveal_type(C.y) # revealed: Literal[1]
```
## Possibly unbound in class and global scope
```py
def bool_instance() -> bool:
return True
if bool_instance():
x = "abc"
class C:
if bool_instance():
x = 1
# error: [possibly-unresolved-reference]
y = x
reveal_type(C.y) # revealed: Literal[1] | Literal["abc"]
```

View file

@ -55,3 +55,24 @@ from b import x
x = "foo" # error: [invalid-assignment] "Object of type `Literal["foo"]"
```
## Import cycle
```py path=a.py
class A: ...
reveal_type(A.__mro__) # revealed: tuple[Literal[A], Literal[object]]
import b
class C(b.B): ...
reveal_type(C.__mro__) # revealed: tuple[Literal[C], Literal[B], Literal[A], Literal[object]]
```
```py path=b.py
from a import A
class B(A): ...
reveal_type(B.__mro__) # revealed: tuple[Literal[B], Literal[A], Literal[object]]
```

View file

@ -0,0 +1,9 @@
# Invalid syntax
## Missing module name
```py
from import bar # error: [invalid-syntax]
reveal_type(bar) # revealed: Unknown
```

View file

@ -0,0 +1,7 @@
# Ellipsis literals
## Simple
```py
reveal_type(...) # revealed: EllipsisType | ellipsis
```

View file

@ -0,0 +1,45 @@
# Nonlocal references
## One level up
```py
def f():
x = 1
def g():
reveal_type(x) # revealed: Literal[1]
```
## Two levels up
```py
def f():
x = 1
def g():
def h():
reveal_type(x) # revealed: Literal[1]
```
## Skips class scope
```py
def f():
x = 1
class C:
x = 2
def g():
reveal_type(x) # revealed: Literal[1]
```
## Skips annotation-only assignment
```py
def f():
x = 1
def g():
# it's pretty weird to have an annotated assignment in a function where the
# name is otherwise not defined; maybe should be an error?
x: int
def h():
reveal_type(x) # revealed: Literal[1]
```

View file

@ -0,0 +1,57 @@
# Unbound
## Unbound class variable
Name lookups within a class scope fall back to globals, but lookups of class attributes don't.
```py
def bool_instance() -> bool:
return True
flag = bool_instance()
x = 1
class C:
y = x
if flag:
x = 2
# error: [possibly-unbound-attribute] "Attribute `x` on type `Literal[C]` is possibly unbound"
reveal_type(C.x) # revealed: Literal[2]
reveal_type(C.y) # revealed: Literal[1]
```
## Possibly unbound in class and global scope
```py
def bool_instance() -> bool:
return True
if bool_instance():
x = "abc"
class C:
if bool_instance():
x = 1
# error: [possibly-unresolved-reference]
y = x
reveal_type(C.y) # revealed: Literal[1] | Literal["abc"]
```
## Unbound function local
An unbound function local that has definitions in the scope does not fall back to globals.
```py
x = 1
def f():
# error: [unresolved-reference]
# revealed: Unknown
reveal_type(x)
x = 2
# revealed: Literal[2]
reveal_type(x)
```

View file

@ -5123,16 +5123,6 @@ mod tests {
assert_diagnostic_messages(diagnostics, expected);
}
#[test]
fn from_import_with_no_module_name() -> anyhow::Result<()> {
// This test checks that invalid syntax in a `StmtImportFrom` node
// leads to the type being inferred as `Unknown`
let mut db = setup_db();
db.write_file("src/foo.py", "from import bar")?;
assert_public_ty(&db, "src/foo.py", "bar", "Unknown");
Ok(())
}
#[test]
fn resolve_method() -> anyhow::Result<()> {
let mut db = setup_db();
@ -5282,112 +5272,6 @@ mod tests {
Ok(())
}
#[test]
fn bytes_type() -> anyhow::Result<()> {
let mut db = setup_db();
db.write_dedented(
"src/a.py",
"
w = b'red' b'knot'
x = b'hello'
y = b'world' + b'!'
z = b'\\xff\\x00'
",
)?;
assert_public_ty(&db, "src/a.py", "w", "Literal[b\"redknot\"]");
assert_public_ty(&db, "src/a.py", "x", "Literal[b\"hello\"]");
assert_public_ty(&db, "src/a.py", "y", "Literal[b\"world!\"]");
assert_public_ty(&db, "src/a.py", "z", "Literal[b\"\\xff\\x00\"]");
Ok(())
}
#[test]
fn ellipsis_type() -> anyhow::Result<()> {
let mut db = setup_db();
db.write_dedented(
"src/a.py",
"
x = ...
",
)?;
// TODO: sys.version_info
assert_public_ty(&db, "src/a.py", "x", "EllipsisType | ellipsis");
Ok(())
}
#[test]
fn import_cycle() -> anyhow::Result<()> {
let mut db = setup_db();
db.write_dedented(
"src/a.py",
"
class A: pass
import b
class C(b.B): pass
",
)?;
db.write_dedented(
"src/b.py",
"
from a import A
class B(A): pass
",
)?;
let a = system_path_to_file(&db, "src/a.py").expect("file to exist");
let c_ty = global_symbol(&db, a, "C").expect_type();
let c_class = c_ty.expect_class_literal().class;
let mut c_mro = c_class.iter_mro(&db);
let b_ty = c_mro.nth(1).unwrap();
let b_class = b_ty.expect_class_base();
assert_eq!(b_class.name(&db), "B");
let mut b_mro = b_class.iter_mro(&db);
let a_ty = b_mro.nth(1).unwrap();
let a_class = a_ty.expect_class_base();
assert_eq!(a_class.name(&db), "A");
Ok(())
}
/// An unbound function local that has definitions in the scope does not fall back to globals.
#[test]
fn unbound_function_local() -> anyhow::Result<()> {
let mut db = setup_db();
db.write_dedented(
"src/a.py",
"
x = 1
def f():
y = x
x = 2
",
)?;
let file = system_path_to_file(&db, "src/a.py").expect("file to exist");
let index = semantic_index(&db, file);
let function_scope = index
.child_scopes(FileScopeId::global())
.next()
.unwrap()
.0
.to_scope_id(&db, file);
let y_ty = symbol(&db, function_scope, "y").expect_type();
let x_ty = symbol(&db, function_scope, "x").expect_type();
assert_eq!(y_ty.display(&db).to_string(), "Unknown");
assert_eq!(x_ty.display(&db).to_string(), "Literal[2]");
Ok(())
}
/// A name reference to a never-defined symbol in a function is implicitly a global lookup.
#[test]
fn implicit_global_in_function() -> anyhow::Result<()> {
@ -5562,89 +5446,6 @@ mod tests {
Ok(())
}
#[test]
fn nonlocal_name_reference() -> anyhow::Result<()> {
let mut db = setup_db();
db.write_dedented(
"/src/a.py",
"
def f():
x = 1
def g():
y = x
",
)?;
assert_scope_ty(&db, "/src/a.py", &["f", "g"], "y", "Literal[1]");
Ok(())
}
#[test]
fn nonlocal_name_reference_multi_level() -> anyhow::Result<()> {
let mut db = setup_db();
db.write_dedented(
"/src/a.py",
"
def f():
x = 1
def g():
def h():
y = x
",
)?;
assert_scope_ty(&db, "/src/a.py", &["f", "g", "h"], "y", "Literal[1]");
Ok(())
}
#[test]
fn nonlocal_name_reference_skips_class_scope() -> anyhow::Result<()> {
let mut db = setup_db();
db.write_dedented(
"/src/a.py",
"
def f():
x = 1
class C:
x = 2
def g():
y = x
",
)?;
assert_scope_ty(&db, "/src/a.py", &["f", "C", "g"], "y", "Literal[1]");
Ok(())
}
#[test]
fn nonlocal_name_reference_skips_annotation_only_assignment() -> anyhow::Result<()> {
let mut db = setup_db();
db.write_dedented(
"/src/a.py",
"
def f():
x = 1
def g():
// it's pretty weird to have an annotated assignment in a function where the
// name is otherwise not defined; maybe should be an error?
x: int
def h():
y = x
",
)?;
assert_scope_ty(&db, "/src/a.py", &["f", "g", "h"], "y", "Literal[1]");
Ok(())
}
#[test]
fn basic_comprehension() -> anyhow::Result<()> {
let mut db = setup_db();

View file

@ -328,15 +328,6 @@ impl<'db> ClassBase<'db> {
Display { base: self, db }
}
#[cfg(test)]
#[track_caller]
pub(super) fn expect_class_base(self) -> Class<'db> {
match self {
ClassBase::Class(class) => class,
_ => panic!("Expected a `ClassBase::Class()` variant"),
}
}
/// Return a `ClassBase` representing the class `builtins.object`
fn object(db: &'db dyn Db) -> Self {
KnownClass::Object