aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
Diffstat (limited to 'crates')
-rw-r--r--crates/hir_ty/src/infer.rs5
-rw-r--r--crates/hir_ty/src/infer/expr.rs2
-rw-r--r--crates/hir_ty/src/tests/coercion.rs101
-rw-r--r--crates/hir_ty/src/tests/traits.rs37
-rw-r--r--crates/rust-analyzer/src/caps.rs4
-rw-r--r--crates/rust-analyzer/src/config.rs13
-rw-r--r--crates/rust-analyzer/src/handlers.rs239
-rw-r--r--crates/rust-analyzer/src/main_loop.rs1
8 files changed, 293 insertions, 109 deletions
diff --git a/crates/hir_ty/src/infer.rs b/crates/hir_ty/src/infer.rs
index db3c937ff..edb65622f 100644
--- a/crates/hir_ty/src/infer.rs
+++ b/crates/hir_ty/src/infer.rs
@@ -580,7 +580,10 @@ impl<'a> InferenceContext<'a> {
580 fn resolve_ops_try_ok(&self) -> Option<TypeAliasId> { 580 fn resolve_ops_try_ok(&self) -> Option<TypeAliasId> {
581 let path = path![core::ops::Try]; 581 let path = path![core::ops::Try];
582 let trait_ = self.resolver.resolve_known_trait(self.db.upcast(), &path)?; 582 let trait_ = self.resolver.resolve_known_trait(self.db.upcast(), &path)?;
583 self.db.trait_data(trait_).associated_type_by_name(&name![Ok]) 583 let trait_data = self.db.trait_data(trait_);
584 trait_data
585 .associated_type_by_name(&name![Ok])
586 .or_else(|| trait_data.associated_type_by_name(&name![Output]))
584 } 587 }
585 588
586 fn resolve_ops_neg_output(&self) -> Option<TypeAliasId> { 589 fn resolve_ops_neg_output(&self) -> Option<TypeAliasId> {
diff --git a/crates/hir_ty/src/infer/expr.rs b/crates/hir_ty/src/infer/expr.rs
index eab8fac91..79a732106 100644
--- a/crates/hir_ty/src/infer/expr.rs
+++ b/crates/hir_ty/src/infer/expr.rs
@@ -805,7 +805,7 @@ impl<'a> InferenceContext<'a> {
805 None => self.table.new_float_var(), 805 None => self.table.new_float_var(),
806 }, 806 },
807 }, 807 },
808 Expr::MacroStmts { tail } => self.infer_expr(*tail, expected), 808 Expr::MacroStmts { tail } => self.infer_expr_inner(*tail, expected),
809 }; 809 };
810 // use a new type variable if we got unknown here 810 // use a new type variable if we got unknown here
811 let ty = self.insert_type_vars_shallow(ty); 811 let ty = self.insert_type_vars_shallow(ty);
diff --git a/crates/hir_ty/src/tests/coercion.rs b/crates/hir_ty/src/tests/coercion.rs
index bb568ea37..6dac7e103 100644
--- a/crates/hir_ty/src/tests/coercion.rs
+++ b/crates/hir_ty/src/tests/coercion.rs
@@ -832,11 +832,9 @@ fn coerce_unsize_super_trait_cycle() {
832 ); 832 );
833} 833}
834 834
835#[ignore]
836#[test] 835#[test]
837fn coerce_unsize_generic() { 836fn coerce_unsize_generic() {
838 // FIXME: Implement this 837 // FIXME: fix the type mismatches here
839 // https://doc.rust-lang.org/reference/type-coercions.html#unsized-coercions
840 check_infer_with_mismatches( 838 check_infer_with_mismatches(
841 r#" 839 r#"
842 #[lang = "unsize"] 840 #[lang = "unsize"]
@@ -854,8 +852,58 @@ fn coerce_unsize_generic() {
854 let _: &Bar<[usize]> = &Bar(Foo { t: [1, 2, 3] }); 852 let _: &Bar<[usize]> = &Bar(Foo { t: [1, 2, 3] });
855 } 853 }
856 "#, 854 "#,
857 expect![[r" 855 expect![[r#"
858 "]], 856 209..317 '{ ... }); }': ()
857 219..220 '_': &Foo<[usize]>
858 238..259 '&Foo {..., 3] }': &Foo<[usize]>
859 239..259 'Foo { ..., 3] }': Foo<[usize]>
860 248..257 '[1, 2, 3]': [usize; 3]
861 249..250 '1': usize
862 252..253 '2': usize
863 255..256 '3': usize
864 269..270 '_': &Bar<[usize]>
865 288..314 '&Bar(F... 3] })': &Bar<[i32; 3]>
866 289..292 'Bar': Bar<[i32; 3]>(Foo<[i32; 3]>) -> Bar<[i32; 3]>
867 289..314 'Bar(Fo... 3] })': Bar<[i32; 3]>
868 293..313 'Foo { ..., 3] }': Foo<[i32; 3]>
869 302..311 '[1, 2, 3]': [i32; 3]
870 303..304 '1': i32
871 306..307 '2': i32
872 309..310 '3': i32
873 248..257: expected [usize], got [usize; 3]
874 288..314: expected &Bar<[usize]>, got &Bar<[i32; 3]>
875 "#]],
876 );
877}
878
879#[test]
880fn coerce_unsize_apit() {
881 // FIXME: #8984
882 check_infer_with_mismatches(
883 r#"
884#[lang = "sized"]
885pub trait Sized {}
886#[lang = "unsize"]
887pub trait Unsize<T> {}
888#[lang = "coerce_unsized"]
889pub trait CoerceUnsized<T> {}
890
891impl<T: Unsize<U>, U> CoerceUnsized<&U> for &T {}
892
893trait Foo {}
894
895fn test(f: impl Foo) {
896 let _: &dyn Foo = &f;
897}
898 "#,
899 expect![[r#"
900 210..211 'f': impl Foo
901 223..252 '{ ... &f; }': ()
902 233..234 '_': &dyn Foo
903 247..249 '&f': &impl Foo
904 248..249 'f': impl Foo
905 247..249: expected &dyn Foo, got &impl Foo
906 "#]],
859 ); 907 );
860} 908}
861 909
@@ -912,3 +960,46 @@ fn test() -> i32 {
912 "#, 960 "#,
913 ) 961 )
914} 962}
963
964#[test]
965fn panic_macro() {
966 check_infer_with_mismatches(
967 r#"
968mod panic {
969 #[macro_export]
970 pub macro panic_2015 {
971 () => (
972 $crate::panicking::panic()
973 ),
974 }
975}
976
977mod panicking {
978 pub fn panic() -> ! { loop {} }
979}
980
981#[rustc_builtin_macro = "core_panic"]
982macro_rules! panic {
983 // Expands to either `$crate::panic::panic_2015` or `$crate::panic::panic_2021`
984 // depending on the edition of the caller.
985 ($($arg:tt)*) => {
986 /* compiler built-in */
987 };
988}
989
990fn main() {
991 panic!()
992}
993 "#,
994 expect![[r#"
995 174..185 '{ loop {} }': !
996 176..183 'loop {}': !
997 181..183 '{}': ()
998 !0..24 '$crate...:panic': fn panic() -> !
999 !0..26 '$crate...anic()': !
1000 !0..26 '$crate...anic()': !
1001 !0..28 '$crate...015!()': !
1002 454..470 '{ ...c!() }': ()
1003 "#]],
1004 );
1005}
diff --git a/crates/hir_ty/src/tests/traits.rs b/crates/hir_ty/src/tests/traits.rs
index a5a2df54c..71905baeb 100644
--- a/crates/hir_ty/src/tests/traits.rs
+++ b/crates/hir_ty/src/tests/traits.rs
@@ -161,6 +161,43 @@ mod result {
161} 161}
162 162
163#[test] 163#[test]
164fn infer_tryv2() {
165 check_types(
166 r#"
167//- /main.rs crate:main deps:core
168fn test() {
169 let r: Result<i32, u64> = Result::Ok(1);
170 let v = r?;
171 v;
172} //^ i32
173
174//- /core.rs crate:core
175#[prelude_import] use ops::*;
176mod ops {
177 trait Try {
178 type Output;
179 type Residual;
180 }
181}
182
183#[prelude_import] use result::*;
184mod result {
185 enum Infallible {}
186 enum Result<O, E> {
187 Ok(O),
188 Err(E)
189 }
190
191 impl<O, E> crate::ops::Try for Result<O, E> {
192 type Output = O;
193 type Error = Result<Infallible, E>;
194 }
195}
196"#,
197 );
198}
199
200#[test]
164fn infer_for_loop() { 201fn infer_for_loop() {
165 check_types( 202 check_types(
166 r#" 203 r#"
diff --git a/crates/rust-analyzer/src/caps.rs b/crates/rust-analyzer/src/caps.rs
index b2317618a..4d88932ca 100644
--- a/crates/rust-analyzer/src/caps.rs
+++ b/crates/rust-analyzer/src/caps.rs
@@ -1,4 +1,4 @@
1//! Advertizes the capabilities of the LSP Server. 1//! Advertises the capabilities of the LSP Server.
2use std::env; 2use std::env;
3 3
4use lsp_types::{ 4use lsp_types::{
@@ -54,7 +54,7 @@ pub fn server_capabilities(client_caps: &ClientCapabilities) -> ServerCapabiliti
54 code_action_provider: Some(code_action_capabilities(client_caps)), 54 code_action_provider: Some(code_action_capabilities(client_caps)),
55 code_lens_provider: Some(CodeLensOptions { resolve_provider: Some(true) }), 55 code_lens_provider: Some(CodeLensOptions { resolve_provider: Some(true) }),
56 document_formatting_provider: Some(OneOf::Left(true)), 56 document_formatting_provider: Some(OneOf::Left(true)),
57 document_range_formatting_provider: None, 57 document_range_formatting_provider: Some(OneOf::Left(true)),
58 document_on_type_formatting_provider: Some(DocumentOnTypeFormattingOptions { 58 document_on_type_formatting_provider: Some(DocumentOnTypeFormattingOptions {
59 first_trigger_character: "=".to_string(), 59 first_trigger_character: "=".to_string(),
60 more_trigger_character: Some(vec![".".to_string(), ">".to_string(), "{".to_string()]), 60 more_trigger_character: Some(vec![".".to_string(), ">".to_string(), "{".to_string()]),
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs
index 7c02a507c..7620a2fe1 100644
--- a/crates/rust-analyzer/src/config.rs
+++ b/crates/rust-analyzer/src/config.rs
@@ -218,6 +218,10 @@ config_data! {
218 /// Advanced option, fully override the command rust-analyzer uses for 218 /// Advanced option, fully override the command rust-analyzer uses for
219 /// formatting. 219 /// formatting.
220 rustfmt_overrideCommand: Option<Vec<String>> = "null", 220 rustfmt_overrideCommand: Option<Vec<String>> = "null",
221 /// Enables the use of rustfmt's unstable range formatting command for the
222 /// `textDocument/rangeFormatting` request. The rustfmt option is unstable and only
223 /// available on a nightly build.
224 rustfmt_enableRangeFormatting: bool = "false",
221 225
222 /// Workspace symbol search scope. 226 /// Workspace symbol search scope.
223 workspace_symbol_search_scope: WorskpaceSymbolSearchScopeDef = "\"workspace\"", 227 workspace_symbol_search_scope: WorskpaceSymbolSearchScopeDef = "\"workspace\"",
@@ -305,7 +309,7 @@ pub struct NotificationsConfig {
305 309
306#[derive(Debug, Clone)] 310#[derive(Debug, Clone)]
307pub enum RustfmtConfig { 311pub enum RustfmtConfig {
308 Rustfmt { extra_args: Vec<String> }, 312 Rustfmt { extra_args: Vec<String>, enable_range_formatting: bool },
309 CustomCommand { command: String, args: Vec<String> }, 313 CustomCommand { command: String, args: Vec<String> },
310} 314}
311 315
@@ -584,9 +588,10 @@ impl Config {
584 let command = args.remove(0); 588 let command = args.remove(0);
585 RustfmtConfig::CustomCommand { command, args } 589 RustfmtConfig::CustomCommand { command, args }
586 } 590 }
587 Some(_) | None => { 591 Some(_) | None => RustfmtConfig::Rustfmt {
588 RustfmtConfig::Rustfmt { extra_args: self.data.rustfmt_extraArgs.clone() } 592 extra_args: self.data.rustfmt_extraArgs.clone(),
589 } 593 enable_range_formatting: self.data.rustfmt_enableRangeFormatting,
594 },
590 } 595 }
591 } 596 }
592 pub fn flycheck(&self) -> Option<FlycheckConfig> { 597 pub fn flycheck(&self) -> Option<FlycheckConfig> {
diff --git a/crates/rust-analyzer/src/handlers.rs b/crates/rust-analyzer/src/handlers.rs
index f48210424..456744603 100644
--- a/crates/rust-analyzer/src/handlers.rs
+++ b/crates/rust-analyzer/src/handlers.rs
@@ -27,7 +27,7 @@ use lsp_types::{
27}; 27};
28use project_model::TargetKind; 28use project_model::TargetKind;
29use serde::{Deserialize, Serialize}; 29use serde::{Deserialize, Serialize};
30use serde_json::to_value; 30use serde_json::{json, to_value};
31use stdx::format_to; 31use stdx::format_to;
32use syntax::{algo, ast, AstNode, TextRange, TextSize}; 32use syntax::{algo, ast, AstNode, TextRange, TextSize};
33 33
@@ -955,104 +955,17 @@ pub(crate) fn handle_formatting(
955 params: DocumentFormattingParams, 955 params: DocumentFormattingParams,
956) -> Result<Option<Vec<lsp_types::TextEdit>>> { 956) -> Result<Option<Vec<lsp_types::TextEdit>>> {
957 let _p = profile::span("handle_formatting"); 957 let _p = profile::span("handle_formatting");
958 let file_id = from_proto::file_id(&snap, &params.text_document.uri)?;
959 let file = snap.analysis.file_text(file_id)?;
960 let crate_ids = snap.analysis.crate_for(file_id)?;
961
962 let line_index = snap.file_line_index(file_id)?;
963
964 let mut rustfmt = match snap.config.rustfmt() {
965 RustfmtConfig::Rustfmt { extra_args } => {
966 let mut cmd = process::Command::new(toolchain::rustfmt());
967 cmd.args(extra_args);
968 // try to chdir to the file so we can respect `rustfmt.toml`
969 // FIXME: use `rustfmt --config-path` once
970 // https://github.com/rust-lang/rustfmt/issues/4660 gets fixed
971 match params.text_document.uri.to_file_path() {
972 Ok(mut path) => {
973 // pop off file name
974 if path.pop() && path.is_dir() {
975 cmd.current_dir(path);
976 }
977 }
978 Err(_) => {
979 log::error!(
980 "Unable to get file path for {}, rustfmt.toml might be ignored",
981 params.text_document.uri
982 );
983 }
984 }
985 if let Some(&crate_id) = crate_ids.first() {
986 // Assume all crates are in the same edition
987 let edition = snap.analysis.crate_edition(crate_id)?;
988 cmd.arg("--edition");
989 cmd.arg(edition.to_string());
990 }
991 cmd
992 }
993 RustfmtConfig::CustomCommand { command, args } => {
994 let mut cmd = process::Command::new(command);
995 cmd.args(args);
996 cmd
997 }
998 };
999 958
1000 let mut rustfmt = 959 run_rustfmt(&snap, params.text_document, None)
1001 rustfmt.stdin(Stdio::piped()).stdout(Stdio::piped()).stderr(Stdio::piped()).spawn()?; 960}
1002
1003 rustfmt.stdin.as_mut().unwrap().write_all(file.as_bytes())?;
1004
1005 let output = rustfmt.wait_with_output()?;
1006 let captured_stdout = String::from_utf8(output.stdout)?;
1007 let captured_stderr = String::from_utf8(output.stderr).unwrap_or_default();
1008
1009 if !output.status.success() {
1010 let rustfmt_not_installed =
1011 captured_stderr.contains("not installed") || captured_stderr.contains("not available");
1012
1013 return match output.status.code() {
1014 Some(1) if !rustfmt_not_installed => {
1015 // While `rustfmt` doesn't have a specific exit code for parse errors this is the
1016 // likely cause exiting with 1. Most Language Servers swallow parse errors on
1017 // formatting because otherwise an error is surfaced to the user on top of the
1018 // syntax error diagnostics they're already receiving. This is especially jarring
1019 // if they have format on save enabled.
1020 log::info!("rustfmt exited with status 1, assuming parse error and ignoring");
1021 Ok(None)
1022 }
1023 _ => {
1024 // Something else happened - e.g. `rustfmt` is missing or caught a signal
1025 Err(LspError::new(
1026 -32900,
1027 format!(
1028 r#"rustfmt exited with:
1029 Status: {}
1030 stdout: {}
1031 stderr: {}"#,
1032 output.status, captured_stdout, captured_stderr,
1033 ),
1034 )
1035 .into())
1036 }
1037 };
1038 }
1039 961
1040 let (new_text, new_line_endings) = LineEndings::normalize(captured_stdout); 962pub(crate) fn handle_range_formatting(
963 snap: GlobalStateSnapshot,
964 params: lsp_types::DocumentRangeFormattingParams,
965) -> Result<Option<Vec<lsp_types::TextEdit>>> {
966 let _p = profile::span("handle_range_formatting");
1041 967
1042 if line_index.endings != new_line_endings { 968 run_rustfmt(&snap, params.text_document, Some(params.range))
1043 // If line endings are different, send the entire file.
1044 // Diffing would not work here, as the line endings might be the only
1045 // difference.
1046 Ok(Some(to_proto::text_edit_vec(
1047 &line_index,
1048 TextEdit::replace(TextRange::up_to(TextSize::of(&*file)), new_text),
1049 )))
1050 } else if *file == new_text {
1051 // The document is already formatted correctly -- no edits needed.
1052 Ok(None)
1053 } else {
1054 Ok(Some(to_proto::text_edit_vec(&line_index, diff(&file, &new_text))))
1055 }
1056} 969}
1057 970
1058pub(crate) fn handle_code_action( 971pub(crate) fn handle_code_action(
@@ -1675,6 +1588,140 @@ fn should_skip_target(runnable: &Runnable, cargo_spec: Option<&CargoTargetSpec>)
1675 } 1588 }
1676} 1589}
1677 1590
1591fn run_rustfmt(
1592 snap: &GlobalStateSnapshot,
1593 text_document: TextDocumentIdentifier,
1594 range: Option<lsp_types::Range>,
1595) -> Result<Option<Vec<lsp_types::TextEdit>>> {
1596 let file_id = from_proto::file_id(&snap, &text_document.uri)?;
1597 let file = snap.analysis.file_text(file_id)?;
1598 let crate_ids = snap.analysis.crate_for(file_id)?;
1599
1600 let line_index = snap.file_line_index(file_id)?;
1601
1602 let mut rustfmt = match snap.config.rustfmt() {
1603 RustfmtConfig::Rustfmt { extra_args, enable_range_formatting } => {
1604 let mut cmd = process::Command::new(toolchain::rustfmt());
1605 cmd.args(extra_args);
1606 // try to chdir to the file so we can respect `rustfmt.toml`
1607 // FIXME: use `rustfmt --config-path` once
1608 // https://github.com/rust-lang/rustfmt/issues/4660 gets fixed
1609 match text_document.uri.to_file_path() {
1610 Ok(mut path) => {
1611 // pop off file name
1612 if path.pop() && path.is_dir() {
1613 cmd.current_dir(path);
1614 }
1615 }
1616 Err(_) => {
1617 log::error!(
1618 "Unable to get file path for {}, rustfmt.toml might be ignored",
1619 text_document.uri
1620 );
1621 }
1622 }
1623 if let Some(&crate_id) = crate_ids.first() {
1624 // Assume all crates are in the same edition
1625 let edition = snap.analysis.crate_edition(crate_id)?;
1626 cmd.arg("--edition");
1627 cmd.arg(edition.to_string());
1628 }
1629
1630 if let Some(range) = range {
1631 if !enable_range_formatting {
1632 return Err(LspError::new(
1633 ErrorCode::InvalidRequest as i32,
1634 String::from(
1635 "rustfmt range formatting is unstable. \
1636 Opt-in by using a nightly build of rustfmt and setting \
1637 `rustfmt.enableRangeFormatting` to true in your LSP configuration",
1638 ),
1639 )
1640 .into());
1641 }
1642
1643 let frange = from_proto::file_range(&snap, text_document.clone(), range)?;
1644 let start_line = line_index.index.line_col(frange.range.start()).line;
1645 let end_line = line_index.index.line_col(frange.range.end()).line;
1646
1647 cmd.arg("--unstable-features");
1648 cmd.arg("--file-lines");
1649 cmd.arg(
1650 json!([{
1651 "file": "stdin",
1652 "range": [start_line, end_line]
1653 }])
1654 .to_string(),
1655 );
1656 }
1657
1658 cmd
1659 }
1660 RustfmtConfig::CustomCommand { command, args } => {
1661 let mut cmd = process::Command::new(command);
1662 cmd.args(args);
1663 cmd
1664 }
1665 };
1666
1667 let mut rustfmt =
1668 rustfmt.stdin(Stdio::piped()).stdout(Stdio::piped()).stderr(Stdio::piped()).spawn()?;
1669
1670 rustfmt.stdin.as_mut().unwrap().write_all(file.as_bytes())?;
1671
1672 let output = rustfmt.wait_with_output()?;
1673 let captured_stdout = String::from_utf8(output.stdout)?;
1674 let captured_stderr = String::from_utf8(output.stderr).unwrap_or_default();
1675
1676 if !output.status.success() {
1677 let rustfmt_not_installed =
1678 captured_stderr.contains("not installed") || captured_stderr.contains("not available");
1679
1680 return match output.status.code() {
1681 Some(1) if !rustfmt_not_installed => {
1682 // While `rustfmt` doesn't have a specific exit code for parse errors this is the
1683 // likely cause exiting with 1. Most Language Servers swallow parse errors on
1684 // formatting because otherwise an error is surfaced to the user on top of the
1685 // syntax error diagnostics they're already receiving. This is especially jarring
1686 // if they have format on save enabled.
1687 log::info!("rustfmt exited with status 1, assuming parse error and ignoring");
1688 Ok(None)
1689 }
1690 _ => {
1691 // Something else happened - e.g. `rustfmt` is missing or caught a signal
1692 Err(LspError::new(
1693 -32900,
1694 format!(
1695 r#"rustfmt exited with:
1696 Status: {}
1697 stdout: {}
1698 stderr: {}"#,
1699 output.status, captured_stdout, captured_stderr,
1700 ),
1701 )
1702 .into())
1703 }
1704 };
1705 }
1706
1707 let (new_text, new_line_endings) = LineEndings::normalize(captured_stdout);
1708
1709 if line_index.endings != new_line_endings {
1710 // If line endings are different, send the entire file.
1711 // Diffing would not work here, as the line endings might be the only
1712 // difference.
1713 Ok(Some(to_proto::text_edit_vec(
1714 &line_index,
1715 TextEdit::replace(TextRange::up_to(TextSize::of(&*file)), new_text),
1716 )))
1717 } else if *file == new_text {
1718 // The document is already formatted correctly -- no edits needed.
1719 Ok(None)
1720 } else {
1721 Ok(Some(to_proto::text_edit_vec(&line_index, diff(&file, &new_text))))
1722 }
1723}
1724
1678#[derive(Debug, Serialize, Deserialize)] 1725#[derive(Debug, Serialize, Deserialize)]
1679struct CompletionResolveData { 1726struct CompletionResolveData {
1680 position: lsp_types::TextDocumentPositionParams, 1727 position: lsp_types::TextDocumentPositionParams,
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs
index cb002f700..008758ea0 100644
--- a/crates/rust-analyzer/src/main_loop.rs
+++ b/crates/rust-analyzer/src/main_loop.rs
@@ -543,6 +543,7 @@ impl GlobalState {
543 .on::<lsp_types::request::Rename>(handlers::handle_rename) 543 .on::<lsp_types::request::Rename>(handlers::handle_rename)
544 .on::<lsp_types::request::References>(handlers::handle_references) 544 .on::<lsp_types::request::References>(handlers::handle_references)
545 .on::<lsp_types::request::Formatting>(handlers::handle_formatting) 545 .on::<lsp_types::request::Formatting>(handlers::handle_formatting)
546 .on::<lsp_types::request::RangeFormatting>(handlers::handle_range_formatting)
546 .on::<lsp_types::request::DocumentHighlightRequest>(handlers::handle_document_highlight) 547 .on::<lsp_types::request::DocumentHighlightRequest>(handlers::handle_document_highlight)
547 .on::<lsp_types::request::CallHierarchyPrepare>(handlers::handle_call_hierarchy_prepare) 548 .on::<lsp_types::request::CallHierarchyPrepare>(handlers::handle_call_hierarchy_prepare)
548 .on::<lsp_types::request::CallHierarchyIncomingCalls>( 549 .on::<lsp_types::request::CallHierarchyIncomingCalls>(