From c2f74590bd23b807c83b2651f5f55a1c08985569 Mon Sep 17 00:00:00 2001 From: Sysix <3897725+Sysix@users.noreply.github.com> Date: Wed, 24 Sep 2025 08:58:15 +0000 Subject: [PATCH] perf(language_server): avoid cloning on message conversion (#14058) Tested it with one `no-debugger` rule on a file (100 times). Main: ``` heaptrack stats: allocations: 172985 leaked allocations: 580 temporary allocations: 53112 ``` This PR: ``` heaptrack stats: allocations: 170985 leaked allocations: 580 temporary allocations: 53111 ``` --- crates/oxc_diagnostics/src/lib.rs | 5 ++ crates/oxc_linter/src/lsp.rs | 65 ++++++++---------------- crates/oxc_linter/src/service/runtime.rs | 4 +- crates/oxc_linter/src/tsgolint.rs | 2 +- 4 files changed, 30 insertions(+), 46 deletions(-) diff --git a/crates/oxc_diagnostics/src/lib.rs b/crates/oxc_diagnostics/src/lib.rs index 3cc9e56776d6f..45c6b6c1f0911 100644 --- a/crates/oxc_diagnostics/src/lib.rs +++ b/crates/oxc_diagnostics/src/lib.rs @@ -338,4 +338,9 @@ impl OxcDiagnostic { pub fn with_source_code(self, code: T) -> Error { Error::from(self).with_source_code(code) } + + /// Consumes the diagnostic and returns the inner owned data. + pub fn inner_owned(self) -> OxcDiagnosticInner { + *self.inner + } } diff --git a/crates/oxc_linter/src/lsp.rs b/crates/oxc_linter/src/lsp.rs index 1e000aa614976..81e0fc0703245 100644 --- a/crates/oxc_linter/src/lsp.rs +++ b/crates/oxc_linter/src/lsp.rs @@ -69,11 +69,13 @@ pub struct MessageWithPosition<'a> { // we assume that the fix offset will not exceed 2GB in either direction #[expect(clippy::cast_possible_truncation)] pub fn oxc_diagnostic_to_message_with_position<'a>( - diagnostic: &OxcDiagnostic, + diagnostic: OxcDiagnostic, source_text: &str, rope: &Rope, ) -> MessageWithPosition<'a> { - let labels = diagnostic.labels.as_ref().map(|labels| { + let inner = diagnostic.inner_owned(); + + let labels = inner.labels.as_ref().map(|labels| { labels .iter() .map(|labeled_span| { @@ -89,56 +91,33 @@ pub fn oxc_diagnostic_to_message_with_position<'a>( }); MessageWithPosition { - message: diagnostic.message.clone(), - severity: diagnostic.severity, - help: diagnostic.help.clone(), - url: diagnostic.url.clone(), - code: diagnostic.code.clone(), + message: inner.message, + severity: inner.severity, + help: inner.help, + url: inner.url, + code: inner.code, labels, fixes: PossibleFixesWithPosition::None, } } -// clippy: the source field is checked and assumed to be less than 4GB, and -// we assume that the fix offset will not exceed 2GB in either direction -#[expect(clippy::cast_possible_truncation)] pub fn message_to_message_with_position<'a>( - message: &Message<'a>, + message: Message<'a>, source_text: &str, rope: &Rope, ) -> MessageWithPosition<'a> { - let labels = message.error.labels.as_ref().map(|labels| { - labels - .iter() - .map(|labeled_span| { - let offset = labeled_span.offset() as u32; - let start_position = offset_to_position(rope, offset, source_text); - let end_position = - offset_to_position(rope, offset + labeled_span.len() as u32, source_text); - let message = labeled_span.label().map(|label| Cow::Owned(label.to_string())); - - SpanPositionMessage::new(start_position, end_position).with_message(message) - }) - .collect::>() - }); - - MessageWithPosition { - message: message.error.message.clone(), - severity: message.error.severity, - help: message.error.help.clone(), - url: message.error.url.clone(), - code: message.error.code.clone(), - labels, - fixes: match &message.fixes { - PossibleFixes::None => PossibleFixesWithPosition::None, - PossibleFixes::Single(fix) => { - PossibleFixesWithPosition::Single(fix_to_fix_with_position(fix, rope, source_text)) - } - PossibleFixes::Multiple(fixes) => PossibleFixesWithPosition::Multiple( - fixes.iter().map(|fix| fix_to_fix_with_position(fix, rope, source_text)).collect(), - ), - }, - } + let mut result = oxc_diagnostic_to_message_with_position(message.error, source_text, rope); + result.fixes = match &message.fixes { + PossibleFixes::None => PossibleFixesWithPosition::None, + PossibleFixes::Single(fix) => { + PossibleFixesWithPosition::Single(fix_to_fix_with_position(fix, rope, source_text)) + } + PossibleFixes::Multiple(fixes) => PossibleFixesWithPosition::Multiple( + fixes.iter().map(|fix| fix_to_fix_with_position(fix, rope, source_text)).collect(), + ), + }; + + result } #[derive(Debug)] diff --git a/crates/oxc_linter/src/service/runtime.rs b/crates/oxc_linter/src/service/runtime.rs index 5e2ed107a5638..8ce4896bb4633 100644 --- a/crates/oxc_linter/src/service/runtime.rs +++ b/crates/oxc_linter/src/service/runtime.rs @@ -729,7 +729,7 @@ impl Runtime { messages.lock().unwrap().extend( diagnostics.into_iter().map(|diagnostic| { oxc_diagnostic_to_message_with_position( - &diagnostic, + diagnostic, source_text, rope, ) @@ -754,7 +754,7 @@ impl Runtime { messages.lock().unwrap().extend(section_messages.iter().map(|message| { let message = message_cloner.clone_message(message); - message_to_message_with_position(&message, source_text, rope) + message_to_message_with_position(message, source_text, rope) })); }, ); diff --git a/crates/oxc_linter/src/tsgolint.rs b/crates/oxc_linter/src/tsgolint.rs index 49e9ad6025c64..34ae6f0c869f3 100644 --- a/crates/oxc_linter/src/tsgolint.rs +++ b/crates/oxc_linter/src/tsgolint.rs @@ -383,7 +383,7 @@ impl TsGoLintState { let mut message_with_position: MessageWithPosition<'_> = message_to_message_with_position( - &Message::from_tsgo_lint_diagnostic( + Message::from_tsgo_lint_diagnostic( tsgolint_diagnostic, &source_text, ),