red-knot: Implement the not operator for all Type variants (#13432)

Signed-off-by: haaris <haarisrahman@gmail.com>
Co-authored-by: Carl Meyer <carl@oddbird.net>
This commit is contained in:
haarisr 2024-09-25 13:44:19 -07:00 committed by GitHub
parent bbb044ebda
commit 7c83af419c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 214 additions and 3 deletions

View file

@ -936,7 +936,6 @@ impl Truthiness {
matches!(self, Truthiness::Ambiguous)
}
#[allow(unused)]
const fn negate(self) -> Self {
match self {
Self::AlwaysTrue => Self::AlwaysFalse,
@ -945,7 +944,6 @@ impl Truthiness {
}
}
#[allow(unused)]
fn into_type(self, db: &dyn Db) -> Type {
match self {
Self::AlwaysTrue => Type::BooleanLiteral(true),

View file

@ -2210,7 +2210,7 @@ impl<'db> TypeInferenceBuilder<'db> {
match (op, self.infer_expression(operand)) {
(UnaryOp::USub, Type::IntLiteral(value)) => Type::IntLiteral(-value),
(UnaryOp::Not, Type::BooleanLiteral(value)) => Type::BooleanLiteral(!value),
(UnaryOp::Not, ty) => ty.bool(self.db).negate().into_type(self.db),
_ => Type::Unknown, // TODO other unary op types
}
}
@ -3161,6 +3161,127 @@ mod tests {
Ok(())
}
#[test]
fn not_none_literal() -> anyhow::Result<()> {
let mut db = setup_db();
db.write_file(
"src/a.py",
r#"
a = not None
b = not not None
"#,
)?;
assert_public_ty(&db, "src/a.py", "a", "Literal[True]");
assert_public_ty(&db, "src/a.py", "b", "Literal[False]");
Ok(())
}
#[test]
fn not_function() -> anyhow::Result<()> {
let mut db = setup_db();
db.write_file(
"src/a.py",
r#"
from typing import reveal_type
def f():
return 1
a = not f
b = not reveal_type
"#,
)?;
assert_public_ty(&db, "src/a.py", "a", "Literal[False]");
// TODO Unknown should not be part of the type of typing.reveal_type
// assert_public_ty(&db, "src/a.py", "b", "Literal[False]");
Ok(())
}
#[test]
fn not_module() -> anyhow::Result<()> {
let mut db = setup_db();
db.write_files([
(
"src/a.py",
"import b; import warnings;
x = not b;
z = not warnings",
),
("src/b.py", "y = 1"),
])?;
assert_public_ty(&db, "src/a.py", "x", "Literal[False]");
assert_public_ty(&db, "src/a.py", "z", "Literal[False]");
Ok(())
}
#[test]
fn not_union() -> anyhow::Result<()> {
let mut db = setup_db();
db.write_file(
"src/a.py",
r#"
if flag:
p = 1
q = 3.3
r = "hello"
s = "world"
t = 0
else:
p = "hello"
q = 4
r = ""
s = 0
t = ""
a = not p
b = not q
c = not r
d = not s
e = not t
"#,
)?;
assert_public_ty(&db, "src/a.py", "a", "Literal[False]");
assert_public_ty(&db, "src/a.py", "b", "bool");
assert_public_ty(&db, "src/a.py", "c", "bool");
assert_public_ty(&db, "src/a.py", "d", "bool");
assert_public_ty(&db, "src/a.py", "e", "Literal[True]");
Ok(())
}
#[test]
fn not_integer_literal() -> anyhow::Result<()> {
let mut db = setup_db();
db.write_file(
"src/a.py",
r#"
a = not 1
b = not 1234567890987654321
e = not 0
x = not -1
y = not -1234567890987654321
z = not --987
"#,
)?;
assert_public_ty(&db, "src/a.py", "a", "Literal[False]");
assert_public_ty(&db, "src/a.py", "b", "Literal[False]");
assert_public_ty(&db, "src/a.py", "e", "Literal[True]");
assert_public_ty(&db, "src/a.py", "x", "Literal[False]");
assert_public_ty(&db, "src/a.py", "y", "Literal[False]");
assert_public_ty(&db, "src/a.py", "z", "Literal[False]");
Ok(())
}
#[test]
fn not_boolean_literal() -> anyhow::Result<()> {
let mut db = setup_db();
@ -3183,6 +3304,98 @@ mod tests {
Ok(())
}
#[test]
fn not_string_literal() -> anyhow::Result<()> {
let mut db = setup_db();
db.write_file(
"src/a.py",
r#"
a = not "hello"
b = not ""
c = not "0"
d = not "hello" + "world"
"#,
)?;
assert_public_ty(&db, "src/a.py", "a", "Literal[False]");
assert_public_ty(&db, "src/a.py", "b", "Literal[True]");
assert_public_ty(&db, "src/a.py", "c", "Literal[False]");
assert_public_ty(&db, "src/a.py", "d", "Literal[False]");
Ok(())
}
#[test]
fn not_literal_string() -> anyhow::Result<()> {
let mut db = setup_db();
let content = format!(
r#"
v = not "{y}"
w = not 10*"{y}"
x = not "{y}"*10
z = not 0*"{y}"
u = not (-100)*"{y}"
"#,
y = "a".repeat(TypeInferenceBuilder::MAX_STRING_LITERAL_SIZE + 1),
);
db.write_dedented("src/a.py", &content)?;
assert_public_ty(&db, "src/a.py", "v", "bool");
assert_public_ty(&db, "src/a.py", "w", "bool");
assert_public_ty(&db, "src/a.py", "x", "bool");
assert_public_ty(&db, "src/a.py", "z", "Literal[True]");
assert_public_ty(&db, "src/a.py", "u", "Literal[True]");
Ok(())
}
#[test]
fn not_bytes_literal() -> anyhow::Result<()> {
let mut db = setup_db();
db.write_file(
"src/a.py",
r#"
a = not b"hello"
b = not b""
c = not b"0"
d = not b"hello" + b"world"
"#,
)?;
assert_public_ty(&db, "src/a.py", "a", "Literal[False]");
assert_public_ty(&db, "src/a.py", "b", "Literal[True]");
assert_public_ty(&db, "src/a.py", "c", "Literal[False]");
assert_public_ty(&db, "src/a.py", "d", "Literal[False]");
Ok(())
}
#[test]
fn not_tuple() -> anyhow::Result<()> {
let mut db = setup_db();
db.write_file(
"src/a.py",
r#"
a = not (1,)
b = not (1, 2)
c = not (1, 2, 3)
d = not ()
e = not ("hello",)
f = not (1, "hello")
"#,
)?;
assert_public_ty(&db, "src/a.py", "a", "Literal[False]");
assert_public_ty(&db, "src/a.py", "b", "Literal[False]");
assert_public_ty(&db, "src/a.py", "c", "Literal[False]");
assert_public_ty(&db, "src/a.py", "d", "Literal[True]");
assert_public_ty(&db, "src/a.py", "e", "Literal[False]");
assert_public_ty(&db, "src/a.py", "f", "Literal[False]");
Ok(())
}
#[test]
fn string_type() -> anyhow::Result<()> {
let mut db = setup_db();