diff --git a/language_service/src/compilation.rs b/language_service/src/compilation.rs index e721f911d3..fb2e428437 100644 --- a/language_service/src/compilation.rs +++ b/language_service/src/compilation.rs @@ -72,6 +72,21 @@ impl Compilation { language_features, ); + // Compute new lints and append them to the errors Vec. + // Lints are only computed if the erros vector is empty. For performance + // reasons we don't want to waste time running lints every few keystrokes, + // if the user is in the middle of typing a statement, for example. + if errors.is_empty() { + let lints = qsc::linter::run_lints(&unit, Some(lints_config)); + let lints: Vec<_> = lints + .into_iter() + .map(|lint| { + WithSource::from_map(&unit.sources, qsc::compile::ErrorKind::Lint(lint)) + }) + .collect(); + errors.extend(lints); + } + let package_id = package_store.insert(unit); let unit = package_store .get(package_id) @@ -106,6 +121,7 @@ impl Compilation { cells: I, target_profile: Profile, language_features: LanguageFeatures, + lints_config: &[LintConfig], ) -> Self where I: Iterator, Arc)>, @@ -138,6 +154,21 @@ impl Compilation { .get(package_id) .expect("expected to find user package"); + // Compute new lints and append them to the errors Vec. + // Lints are only computed if the erros vector is empty. For performance + // reasons we don't want to waste time running lints every few keystrokes, + // if the user is in the middle of typing a statement, for example. + if errors.is_empty() { + let lints = qsc::linter::run_lints(unit, Some(lints_config)); + let lints: Vec<_> = lints + .into_iter() + .map(|lint| { + WithSource::from_map(&unit.sources, qsc::compile::ErrorKind::Lint(lint)) + }) + .collect(); + errors.extend(lints); + } + run_fir_passes( &mut errors, target_profile, @@ -234,7 +265,7 @@ impl Compilation { lints_config, ), CompilationKind::Notebook => { - Self::new_notebook(sources, target_profile, language_features) + Self::new_notebook(sources, target_profile, language_features, lints_config) } }; self.package_store = new.package_store; diff --git a/language_service/src/state.rs b/language_service/src/state.rs index 75c99417d0..fa40339fc6 100644 --- a/language_service/src/state.rs +++ b/language_service/src/state.rs @@ -387,6 +387,10 @@ impl<'a> CompilationStateUpdater<'a> { }), configuration.target_profile, notebook_configuration.language_features.unwrap_or_default(), + notebook_configuration + .lints_config + .as_ref() + .unwrap_or(&vec![]), ); state.compilations.insert( diff --git a/language_service/src/state/tests.rs b/language_service/src/state/tests.rs index 483617f2cf..ca00069791 100644 --- a/language_service/src/state/tests.rs +++ b/language_service/src/state/tests.rs @@ -644,6 +644,68 @@ fn notebook_document_errors() { ); } +#[test] +fn notebook_document_lints() { + let errors = RefCell::new(Vec::new()); + let mut updater = new_updater(&errors); + + updater.update_notebook_document( + "notebook.ipynb", + &NotebookMetadata::default(), + [ + ("cell1", 1, "operation Foo() : Unit { let x = 4;;;; }"), + ("cell2", 1, "operation Bar() : Unit { let y = 5 / 0; }"), + ] + .into_iter(), + ); + + expect_errors( + &errors, + &expect![[r#" + [ + ( + "cell1", + Some( + 1, + ), + [ + Lint( + Lint { + span: Span { + lo: 35, + hi: 38, + }, + level: Warn, + message: "redundant semicolons", + help: "remove the redundant semicolons", + }, + ), + ], + ), + ( + "cell2", + Some( + 1, + ), + [ + Lint( + Lint { + span: Span { + lo: 74, + hi: 79, + }, + level: Warn, + message: "attempt to divide by zero", + help: "division by zero is not allowed", + }, + ), + ], + ), + ] + "#]], + ); +} + #[test] fn notebook_update_remove_cell_clears_errors() { let errors = RefCell::new(Vec::new());