Skip to content

Commit 7c83af4

Browse files
haarisrcarljm
andauthored
red-knot: Implement the not operator for all Type variants (#13432)
Signed-off-by: haaris <[email protected]> Co-authored-by: Carl Meyer <[email protected]>
1 parent bbb044e commit 7c83af4

File tree

2 files changed

+214
-3
lines changed

2 files changed

+214
-3
lines changed

crates/red_knot_python_semantic/src/types.rs

-2
Original file line numberDiff line numberDiff line change
@@ -936,7 +936,6 @@ impl Truthiness {
936936
matches!(self, Truthiness::Ambiguous)
937937
}
938938

939-
#[allow(unused)]
940939
const fn negate(self) -> Self {
941940
match self {
942941
Self::AlwaysTrue => Self::AlwaysFalse,
@@ -945,7 +944,6 @@ impl Truthiness {
945944
}
946945
}
947946

948-
#[allow(unused)]
949947
fn into_type(self, db: &dyn Db) -> Type {
950948
match self {
951949
Self::AlwaysTrue => Type::BooleanLiteral(true),

crates/red_knot_python_semantic/src/types/infer.rs

+214-1
Original file line numberDiff line numberDiff line change
@@ -2210,7 +2210,7 @@ impl<'db> TypeInferenceBuilder<'db> {
22102210

22112211
match (op, self.infer_expression(operand)) {
22122212
(UnaryOp::USub, Type::IntLiteral(value)) => Type::IntLiteral(-value),
2213-
(UnaryOp::Not, Type::BooleanLiteral(value)) => Type::BooleanLiteral(!value),
2213+
(UnaryOp::Not, ty) => ty.bool(self.db).negate().into_type(self.db),
22142214
_ => Type::Unknown, // TODO other unary op types
22152215
}
22162216
}
@@ -3161,6 +3161,127 @@ mod tests {
31613161
Ok(())
31623162
}
31633163

3164+
#[test]
3165+
fn not_none_literal() -> anyhow::Result<()> {
3166+
let mut db = setup_db();
3167+
3168+
db.write_file(
3169+
"src/a.py",
3170+
r#"
3171+
a = not None
3172+
b = not not None
3173+
"#,
3174+
)?;
3175+
assert_public_ty(&db, "src/a.py", "a", "Literal[True]");
3176+
assert_public_ty(&db, "src/a.py", "b", "Literal[False]");
3177+
3178+
Ok(())
3179+
}
3180+
3181+
#[test]
3182+
fn not_function() -> anyhow::Result<()> {
3183+
let mut db = setup_db();
3184+
3185+
db.write_file(
3186+
"src/a.py",
3187+
r#"
3188+
from typing import reveal_type
3189+
def f():
3190+
return 1
3191+
3192+
a = not f
3193+
b = not reveal_type
3194+
"#,
3195+
)?;
3196+
3197+
assert_public_ty(&db, "src/a.py", "a", "Literal[False]");
3198+
// TODO Unknown should not be part of the type of typing.reveal_type
3199+
// assert_public_ty(&db, "src/a.py", "b", "Literal[False]");
3200+
Ok(())
3201+
}
3202+
3203+
#[test]
3204+
fn not_module() -> anyhow::Result<()> {
3205+
let mut db = setup_db();
3206+
3207+
db.write_files([
3208+
(
3209+
"src/a.py",
3210+
"import b; import warnings;
3211+
x = not b;
3212+
z = not warnings",
3213+
),
3214+
("src/b.py", "y = 1"),
3215+
])?;
3216+
3217+
assert_public_ty(&db, "src/a.py", "x", "Literal[False]");
3218+
assert_public_ty(&db, "src/a.py", "z", "Literal[False]");
3219+
3220+
Ok(())
3221+
}
3222+
3223+
#[test]
3224+
fn not_union() -> anyhow::Result<()> {
3225+
let mut db = setup_db();
3226+
3227+
db.write_file(
3228+
"src/a.py",
3229+
r#"
3230+
if flag:
3231+
p = 1
3232+
q = 3.3
3233+
r = "hello"
3234+
s = "world"
3235+
t = 0
3236+
else:
3237+
p = "hello"
3238+
q = 4
3239+
r = ""
3240+
s = 0
3241+
t = ""
3242+
3243+
a = not p
3244+
b = not q
3245+
c = not r
3246+
d = not s
3247+
e = not t
3248+
"#,
3249+
)?;
3250+
3251+
assert_public_ty(&db, "src/a.py", "a", "Literal[False]");
3252+
assert_public_ty(&db, "src/a.py", "b", "bool");
3253+
assert_public_ty(&db, "src/a.py", "c", "bool");
3254+
assert_public_ty(&db, "src/a.py", "d", "bool");
3255+
assert_public_ty(&db, "src/a.py", "e", "Literal[True]");
3256+
3257+
Ok(())
3258+
}
3259+
3260+
#[test]
3261+
fn not_integer_literal() -> anyhow::Result<()> {
3262+
let mut db = setup_db();
3263+
3264+
db.write_file(
3265+
"src/a.py",
3266+
r#"
3267+
a = not 1
3268+
b = not 1234567890987654321
3269+
e = not 0
3270+
x = not -1
3271+
y = not -1234567890987654321
3272+
z = not --987
3273+
"#,
3274+
)?;
3275+
assert_public_ty(&db, "src/a.py", "a", "Literal[False]");
3276+
assert_public_ty(&db, "src/a.py", "b", "Literal[False]");
3277+
assert_public_ty(&db, "src/a.py", "e", "Literal[True]");
3278+
assert_public_ty(&db, "src/a.py", "x", "Literal[False]");
3279+
assert_public_ty(&db, "src/a.py", "y", "Literal[False]");
3280+
assert_public_ty(&db, "src/a.py", "z", "Literal[False]");
3281+
3282+
Ok(())
3283+
}
3284+
31643285
#[test]
31653286
fn not_boolean_literal() -> anyhow::Result<()> {
31663287
let mut db = setup_db();
@@ -3183,6 +3304,98 @@ mod tests {
31833304
Ok(())
31843305
}
31853306

3307+
#[test]
3308+
fn not_string_literal() -> anyhow::Result<()> {
3309+
let mut db = setup_db();
3310+
3311+
db.write_file(
3312+
"src/a.py",
3313+
r#"
3314+
a = not "hello"
3315+
b = not ""
3316+
c = not "0"
3317+
d = not "hello" + "world"
3318+
"#,
3319+
)?;
3320+
assert_public_ty(&db, "src/a.py", "a", "Literal[False]");
3321+
assert_public_ty(&db, "src/a.py", "b", "Literal[True]");
3322+
assert_public_ty(&db, "src/a.py", "c", "Literal[False]");
3323+
assert_public_ty(&db, "src/a.py", "d", "Literal[False]");
3324+
3325+
Ok(())
3326+
}
3327+
3328+
#[test]
3329+
fn not_literal_string() -> anyhow::Result<()> {
3330+
let mut db = setup_db();
3331+
let content = format!(
3332+
r#"
3333+
v = not "{y}"
3334+
w = not 10*"{y}"
3335+
x = not "{y}"*10
3336+
z = not 0*"{y}"
3337+
u = not (-100)*"{y}"
3338+
"#,
3339+
y = "a".repeat(TypeInferenceBuilder::MAX_STRING_LITERAL_SIZE + 1),
3340+
);
3341+
db.write_dedented("src/a.py", &content)?;
3342+
3343+
assert_public_ty(&db, "src/a.py", "v", "bool");
3344+
assert_public_ty(&db, "src/a.py", "w", "bool");
3345+
assert_public_ty(&db, "src/a.py", "x", "bool");
3346+
assert_public_ty(&db, "src/a.py", "z", "Literal[True]");
3347+
assert_public_ty(&db, "src/a.py", "u", "Literal[True]");
3348+
3349+
Ok(())
3350+
}
3351+
3352+
#[test]
3353+
fn not_bytes_literal() -> anyhow::Result<()> {
3354+
let mut db = setup_db();
3355+
3356+
db.write_file(
3357+
"src/a.py",
3358+
r#"
3359+
a = not b"hello"
3360+
b = not b""
3361+
c = not b"0"
3362+
d = not b"hello" + b"world"
3363+
"#,
3364+
)?;
3365+
assert_public_ty(&db, "src/a.py", "a", "Literal[False]");
3366+
assert_public_ty(&db, "src/a.py", "b", "Literal[True]");
3367+
assert_public_ty(&db, "src/a.py", "c", "Literal[False]");
3368+
assert_public_ty(&db, "src/a.py", "d", "Literal[False]");
3369+
3370+
Ok(())
3371+
}
3372+
3373+
#[test]
3374+
fn not_tuple() -> anyhow::Result<()> {
3375+
let mut db = setup_db();
3376+
3377+
db.write_file(
3378+
"src/a.py",
3379+
r#"
3380+
a = not (1,)
3381+
b = not (1, 2)
3382+
c = not (1, 2, 3)
3383+
d = not ()
3384+
e = not ("hello",)
3385+
f = not (1, "hello")
3386+
"#,
3387+
)?;
3388+
3389+
assert_public_ty(&db, "src/a.py", "a", "Literal[False]");
3390+
assert_public_ty(&db, "src/a.py", "b", "Literal[False]");
3391+
assert_public_ty(&db, "src/a.py", "c", "Literal[False]");
3392+
assert_public_ty(&db, "src/a.py", "d", "Literal[True]");
3393+
assert_public_ty(&db, "src/a.py", "e", "Literal[False]");
3394+
assert_public_ty(&db, "src/a.py", "f", "Literal[False]");
3395+
3396+
Ok(())
3397+
}
3398+
31863399
#[test]
31873400
fn string_type() -> anyhow::Result<()> {
31883401
let mut db = setup_db();

0 commit comments

Comments
 (0)