diff --git a/crates/project/src/lsp_store/semantic_tokens.rs b/crates/project/src/lsp_store/semantic_tokens.rs index 00e4a4c1890278526edc0174a8b6fcf9652226f5..ddf9b9e024765bd2e29036a7649ef4478ba6bcf1 100644 --- a/crates/project/src/lsp_store/semantic_tokens.rs +++ b/crates/project/src/lsp_store/semantic_tokens.rs @@ -656,8 +656,8 @@ impl ServerSemanticTokens { pub(crate) fn apply(&mut self, edits: &[SemanticTokensEdit]) { for edit in edits { - let start = edit.start as usize; - let end = start + edit.delete_count as usize; + let start = (edit.start as usize).min(self.data.len()); + let end = (start + edit.delete_count as usize).min(self.data.len()); self.data.splice(start..end, edit.data.iter().copied()); } } @@ -1003,4 +1003,38 @@ mod tests { ] ); } + + #[test] + fn applies_out_of_bounds_delta_edit_without_panic() { + let mut tokens = ServerSemanticTokens::from_full(vec![2, 5, 3, 0, 3, 0, 5, 4, 1, 0], None); + + // start beyond data length + tokens.apply(&[SemanticTokensEdit { + start: 100, + delete_count: 5, + data: vec![1, 2, 3, 4, 5], + }]); + assert_eq!( + tokens.data, + vec![2, 5, 3, 0, 3, 0, 5, 4, 1, 0, 1, 2, 3, 4, 5] + ); + + // delete_count extends past data length + let mut tokens = ServerSemanticTokens::from_full(vec![2, 5, 3, 0, 3], None); + tokens.apply(&[SemanticTokensEdit { + start: 3, + delete_count: 100, + data: vec![9, 9], + }]); + assert_eq!(tokens.data, vec![2, 5, 3, 9, 9]); + + // empty data + let mut tokens = ServerSemanticTokens::from_full(Vec::new(), None); + tokens.apply(&[SemanticTokensEdit { + start: 0, + delete_count: 5, + data: vec![1, 2, 3, 4, 5], + }]); + assert_eq!(tokens.data, vec![1, 2, 3, 4, 5]); + } }