diff --git a/crates/oxc_language_server/src/linter/isolated_lint_handler.rs b/crates/oxc_language_server/src/linter/isolated_lint_handler.rs index f682596ceae35..8962f26743ba3 100644 --- a/crates/oxc_language_server/src/linter/isolated_lint_handler.rs +++ b/crates/oxc_language_server/src/linter/isolated_lint_handler.rs @@ -125,7 +125,7 @@ impl IsolatedLintHandler { let mut messages: Vec = self .runner - .run_source(&Arc::from(path.as_os_str()), &fs) + .run_source(&[Arc::from(path.as_os_str())], &fs) .into_iter() .map(|message| message_to_lsp_diagnostic(message, uri, source_text, rope)) .collect(); diff --git a/crates/oxc_linter/src/lint_runner.rs b/crates/oxc_linter/src/lint_runner.rs index 5379059131e99..2eb6c7fce64a1 100644 --- a/crates/oxc_linter/src/lint_runner.rs +++ b/crates/oxc_linter/src/lint_runner.rs @@ -235,25 +235,25 @@ impl LintRunner { /// Returns an error if type-aware linting fails. pub fn run_source( &self, - file: &Arc, + files: &[Arc], file_system: &(dyn crate::RuntimeFileSystem + Sync + Send), ) -> Vec { - let mut messages = self.lint_service.run_source(file_system, vec![Arc::clone(file)]); + let mut messages = self.lint_service.run_source(file_system, files.to_owned()); if let Some(type_aware_linter) = &self.type_aware_linter { - let tsgo_messages = - match type_aware_linter.lint_source(file, file_system, self.directives_store.map()) - { - Ok(msgs) => msgs, - Err(err) => { - vec![Message::new( - OxcDiagnostic::warn(format!( - "Failed to run type-aware linting: `{err}`", - )), - PossibleFixes::None, - )] - } - }; + let tsgo_messages = match type_aware_linter.lint_source( + files, + file_system, + self.directives_store.map(), + ) { + Ok(msgs) => msgs, + Err(err) => { + vec![Message::new( + OxcDiagnostic::warn(format!("Failed to run type-aware linting: `{err}`",)), + PossibleFixes::None, + )] + } + }; messages.extend(tsgo_messages); } diff --git a/crates/oxc_linter/src/tsgolint.rs b/crates/oxc_linter/src/tsgolint.rs index 330a768637b77..502423c61f0db 100644 --- a/crates/oxc_linter/src/tsgolint.rs +++ b/crates/oxc_linter/src/tsgolint.rs @@ -320,29 +320,37 @@ impl TsGoLintState { /// A human-readable error message indicating why the linting failed. pub fn lint_source( &self, - path: &Arc, + paths: &[Arc], file_system: &(dyn crate::RuntimeFileSystem + Sync + Send), disable_directives_map: Arc>>, ) -> Result, String> { + if paths.is_empty() { + return Ok(vec![]); + } + let mut resolved_configs: FxHashMap = FxHashMap::default(); - let mut source_overrides = FxHashMap::default(); + let mut source_overrides: FxHashMap = FxHashMap::default(); let allocator = Allocator::default(); - let Ok(source_text) = file_system.read_to_arena_str(Path::new(path.as_ref()), &allocator) - else { - return Err(format!("Failed to read source text for file: {}", path.to_string_lossy())); - }; - // Clone source_text to own it for the spawned thread - let source_text_owned = source_text.to_string(); - source_overrides.insert(path.to_string_lossy().to_string(), source_text_owned.clone()); + // Read all sources into overrides + for path in paths { + let path_ref = Path::new(path.as_ref()); + let Ok(source_text) = file_system.read_to_arena_str(path_ref, &allocator) else { + return Err(format!( + "Failed to read source text for file: {}", + path.to_string_lossy() + )); + }; + source_overrides.insert(path.to_string_lossy().to_string(), source_text.to_string()); + } - let json_input = self.json_input( - std::slice::from_ref(path), - Some(source_overrides), - &mut resolved_configs, - ); + let json_input = + self.json_input(paths, Some(source_overrides.clone()), &mut resolved_configs); + + // Get the file name of the first path for internal diagnostic filtering let path_file_name = - Path::new(path.as_ref()).file_name().unwrap_or_default().to_os_string(); + Path::new(paths[0].as_ref()).file_name().unwrap_or_default().to_os_string(); + let mut child = self.spawn_tsgolint(&json_input)?; let handler = std::thread::spawn(move || { let stdout = child.stdout.take().expect("Failed to open tsgolint stdout"); @@ -390,6 +398,15 @@ impl TsGoLintState { continue; } + // Use the corresponding source override text + let Some(source_text_owned) = source_overrides + .get(&path.to_string_lossy().to_string()) + .cloned() + else { + // should never happen, because we populated source_overrides above + continue; + }; + let mut message = Message::from_tsgo_lint_diagnostic( tsgolint_diagnostic, &source_text_owned,