From c3afd8478beb46371146357a3e2a77a100a70057 Mon Sep 17 00:00:00 2001 From: Alex Waygood Date: Tue, 20 Aug 2024 13:07:07 +0100 Subject: [PATCH] Add failing tests --- crates/red_knot_python_semantic/src/types.rs | 73 ++++++++++++++++--- .../src/types/infer.rs | 10 +++ crates/ruff_benchmark/benches/red_knot.rs | 1 + 3 files changed, 74 insertions(+), 10 deletions(-) diff --git a/crates/red_knot_python_semantic/src/types.rs b/crates/red_knot_python_semantic/src/types.rs index 07dd1c6e1d48ed..70defd31df98e9 100644 --- a/crates/red_knot_python_semantic/src/types.rs +++ b/crates/red_knot_python_semantic/src/types.rs @@ -360,12 +360,13 @@ mod tests { use crate::db::tests::TestDb; use crate::{Program, ProgramSettings, PythonVersion, SearchPathSettings}; - #[test] - fn check_types() -> anyhow::Result<()> { - let mut db = TestDb::new(); + use super::TypeCheckDiagnostics; - db.write_file("src/foo.py", "import bar\n") - .context("Failed to write foo.py")?; + fn setup_db() -> TestDb { + let db = TestDb::new(); + db.memory_file_system() + .create_directory_all("/src") + .unwrap(); Program::from_settings( &db, @@ -381,16 +382,68 @@ mod tests { ) .expect("Valid search path settings"); + db + } + + fn assert_diagnostic_messages(diagnostics: &TypeCheckDiagnostics, expected: &[&str]) { + let messages: Vec<&str> = diagnostics + .iter() + .map(|diagnostic| diagnostic.message()) + .collect(); + assert_eq!(&messages, expected); + } + + #[test] + fn check_types() -> anyhow::Result<()> { + let mut db = setup_db(); + + db.write_file("src/foo.py", "import bar\n") + .context("Failed to write foo.py")?; + let foo = system_path_to_file(&db, "src/foo.py").context("Failed to resolve foo.py")?; let diagnostics = super::check_types(&db, foo); + assert_diagnostic_messages(&diagnostics, &["Import 'bar' could not be resolved."]); + + Ok(()) + } - assert_eq!(diagnostics.len(), 1); - assert_eq!( - diagnostics[0].message(), - "Import 'bar' could not be resolved." + #[test] + fn unresolved_import() { + let mut db = setup_db(); + + db.write_files([ + ("/src/a.py", "import foo as foo"), + ("/src/b.py", "from a import foo"), + ]) + .unwrap(); + + let a_file = system_path_to_file(&db, "/src/a.py").expect("Expected `a.py` to exist!"); + let a_file_diagnostics = super::check_types(&db, a_file); + assert_diagnostic_messages( + &a_file_diagnostics, + &["Import 'foo' could not be resolved."], ); - Ok(()) + // Importing the unresolved import into a second first-party file does not trigger + // an additional "unresolved import" violation + let b_file = system_path_to_file(&db, "/src/b.py").expect("Expected `by.py` to exist!"); + let b_file_diagnostics = super::check_types(&db, b_file); + assert_eq!(&*b_file_diagnostics, &[]); + } + + #[test] + fn unresolved_import_from_resolved_module() { + let mut db = setup_db(); + + db.write_files([("/src/a.py", ""), ("/src/b.py", "from a import thing")]) + .unwrap(); + + let b_file = system_path_to_file(&db, "/src/b.py").expect("Expected `b.py` to exist"); + let b_file_diagnostics = super::check_types(&db, b_file); + assert_diagnostic_messages( + &b_file_diagnostics, + &["Could not resolve import of 'thing' from 'a'"], + ); } } diff --git a/crates/red_knot_python_semantic/src/types/infer.rs b/crates/red_knot_python_semantic/src/types/infer.rs index 9cf7c47b776cb3..fb996478bf8898 100644 --- a/crates/red_knot_python_semantic/src/types/infer.rs +++ b/crates/red_knot_python_semantic/src/types/infer.rs @@ -2015,6 +2015,16 @@ mod tests { Ok(()) } + #[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_base_class_by_name() -> anyhow::Result<()> { let mut db = setup_db(); diff --git a/crates/ruff_benchmark/benches/red_knot.rs b/crates/ruff_benchmark/benches/red_knot.rs index 2aac42364eec46..1e035ebf5cbefe 100644 --- a/crates/ruff_benchmark/benches/red_knot.rs +++ b/crates/ruff_benchmark/benches/red_knot.rs @@ -19,6 +19,7 @@ struct Case { const TOMLLIB_312_URL: &str = "https://raw.githubusercontent.com/python/cpython/8e8a4baf652f6e1cee7acde9d78c4b6154539748/Lib/tomllib"; static EXPECTED_DIAGNOSTICS: &[&str] = &[ + "/src/tomllib/_parser.py:7:29: Could not resolve import of 'Iterable' from 'collections.abc'", "Line 69 is too long (89 characters)", "Use double quotes for strings", "Use double quotes for strings",