diff options
14 files changed, 416 insertions, 50 deletions
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs b/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs index df48ce67373..0b9b6da8d51 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs @@ -166,6 +166,17 @@ impl Resolver { db: &dyn DefDatabase, path: &Path, ) -> Option<(TypeNs, Option<usize>, Option<ImportOrExternCrate>)> { + self.resolve_path_in_type_ns_with_prefix_info(db, path).map( + |(resolution, remaining_segments, import, _)| (resolution, remaining_segments, import), + ) + } + + pub fn resolve_path_in_type_ns_with_prefix_info( + &self, + db: &dyn DefDatabase, + path: &Path, + ) -> Option<(TypeNs, Option<usize>, Option<ImportOrExternCrate>, ResolvePathResultPrefixInfo)> + { let path = match path { Path::BarePath(mod_path) => mod_path, Path::Normal(it) => it.mod_path(), @@ -181,7 +192,12 @@ impl Resolver { | LangItemTarget::ImplDef(_) | LangItemTarget::Static(_) => return None, }; - return Some((type_ns, seg.as_ref().map(|_| 1), None)); + return Some(( + type_ns, + seg.as_ref().map(|_| 1), + None, + ResolvePathResultPrefixInfo::default(), + )); } }; let first_name = path.segments().first()?; @@ -197,17 +213,32 @@ impl Resolver { Scope::ExprScope(_) | Scope::MacroDefScope(_) => continue, Scope::GenericParams { params, def } => { if let Some(id) = params.find_type_by_name(first_name, *def) { - return Some((TypeNs::GenericParam(id), remaining_idx(), None)); + return Some(( + TypeNs::GenericParam(id), + remaining_idx(), + None, + ResolvePathResultPrefixInfo::default(), + )); } } &Scope::ImplDefScope(impl_) => { if *first_name == sym::Self_.clone() { - return Some((TypeNs::SelfType(impl_), remaining_idx(), None)); + return Some(( + TypeNs::SelfType(impl_), + remaining_idx(), + None, + ResolvePathResultPrefixInfo::default(), + )); } } &Scope::AdtScope(adt) => { if *first_name == sym::Self_.clone() { - return Some((TypeNs::AdtSelfType(adt), remaining_idx(), None)); + return Some(( + TypeNs::AdtSelfType(adt), + remaining_idx(), + None, + ResolvePathResultPrefixInfo::default(), + )); } } Scope::BlockScope(m) => { @@ -220,18 +251,6 @@ impl Resolver { self.module_scope.resolve_path_in_type_ns(db, path) } - pub fn resolve_path_in_type_ns_fully_with_imports( - &self, - db: &dyn DefDatabase, - path: &Path, - ) -> Option<(TypeNs, Option<ImportOrExternCrate>)> { - let (res, unresolved, imp) = self.resolve_path_in_type_ns(db, path)?; - if unresolved.is_some() { - return None; - } - Some((res, imp)) - } - pub fn resolve_path_in_type_ns_fully( &self, db: &dyn DefDatabase, @@ -986,11 +1005,12 @@ impl ModuleItemMap { &self, db: &dyn DefDatabase, path: &ModPath, - ) -> Option<(TypeNs, Option<usize>, Option<ImportOrExternCrate>)> { - let (module_def, idx, _) = + ) -> Option<(TypeNs, Option<usize>, Option<ImportOrExternCrate>, ResolvePathResultPrefixInfo)> + { + let (module_def, idx, prefix_info) = self.def_map.resolve_path_locally(db, self.module_id, path, BuiltinShadowMode::Other); let (res, import) = to_type_ns(module_def)?; - Some((res, idx, import)) + Some((res, idx, import, prefix_info)) } } diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/mod_path.rs b/src/tools/rust-analyzer/crates/hir-expand/src/mod_path.rs index 4f2f9ec40d0..89eae862bd9 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/mod_path.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/mod_path.rs @@ -398,6 +398,9 @@ macro_rules! __known_path { (core::fmt::Debug) => {}; (std::fmt::format) => {}; (core::ops::Try) => {}; + (core::convert::From) => {}; + (core::convert::TryFrom) => {}; + (core::str::FromStr) => {}; ($path:path) => { compile_error!("Please register your known path in the path module") }; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs index 24f67fd6602..432b8f4d94e 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs @@ -761,8 +761,8 @@ impl<'a> TyLoweringContext<'a> { path: &Path, on_diagnostic: &mut dyn FnMut(&mut Self, PathLoweringDiagnostic), ) -> Option<(TypeNs, Option<usize>)> { - let (resolution, remaining_index, _) = - self.resolver.resolve_path_in_type_ns(self.db.upcast(), path)?; + let (resolution, remaining_index, _, prefix_info) = + self.resolver.resolve_path_in_type_ns_with_prefix_info(self.db.upcast(), path)?; let segments = path.segments(); match path { @@ -771,13 +771,12 @@ impl<'a> TyLoweringContext<'a> { _ => return Some((resolution, remaining_index)), }; - let (module_segments, resolved_segment_idx, resolved_segment) = match remaining_index { - None => ( - segments.strip_last(), - segments.len() - 1, - segments.last().expect("resolved path has at least one element"), - ), - Some(i) => (segments.take(i - 1), i - 1, segments.get(i - 1).unwrap()), + let (module_segments, resolved_segment_idx, enum_segment) = match remaining_index { + None if prefix_info.enum_variant => { + (segments.strip_last_two(), segments.len() - 1, Some(segments.len() - 2)) + } + None => (segments.strip_last(), segments.len() - 1, None), + Some(i) => (segments.take(i - 1), i - 1, None), }; for (i, mod_segment) in module_segments.iter().enumerate() { @@ -792,9 +791,23 @@ impl<'a> TyLoweringContext<'a> { } } + if let Some(enum_segment) = enum_segment { + if segments.get(enum_segment).is_some_and(|it| it.args_and_bindings.is_some()) + && segments.get(enum_segment + 1).is_some_and(|it| it.args_and_bindings.is_some()) + { + on_diagnostic( + self, + PathLoweringDiagnostic::GenericArgsProhibited { + segment: (enum_segment + 1) as u32, + reason: GenericArgsProhibitedReason::EnumVariant, + }, + ); + } + } + self.handle_type_ns_resolution( &resolution, - resolved_segment, + segments.get(resolved_segment_idx).expect("should have resolved segment"), resolved_segment_idx, on_diagnostic, ); diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics.rs b/src/tools/rust-analyzer/crates/hir/src/semantics.rs index af98e5f2fd0..523bc6f10aa 100644 --- a/src/tools/rust-analyzer/crates/hir/src/semantics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/semantics.rs @@ -1439,6 +1439,10 @@ impl<'db> SemanticsImpl<'db> { self.analyze(call.syntax())?.resolve_method_call_fallback(self.db, call) } + pub fn resolve_known_blanket_dual_impls(&self, call: &ast::MethodCallExpr) -> Option<Function> { + self.analyze(call.syntax())?.resolve_known_blanket_dual_impls(self.db, call) + } + fn resolve_range_pat(&self, range_pat: &ast::RangePat) -> Option<StructId> { self.analyze(range_pat.syntax())?.resolve_range_pat(self.db, range_pat) } diff --git a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs index b699ccde412..6b78d7a3631 100644 --- a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs +++ b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs @@ -322,6 +322,68 @@ impl SourceAnalyzer { } } + // If the method is into(), try_into(), parse(), resolve it to from, try_from, from_str. + pub(crate) fn resolve_known_blanket_dual_impls( + &self, + db: &dyn HirDatabase, + call: &ast::MethodCallExpr, + ) -> Option<Function> { + // e.g. if the method call is let b = a.into(), + // - receiver_type is A (type of a) + // - return_type is B (type of b) + // We will find the definition of B::from(a: A). + let callable = self.resolve_method_call_as_callable(db, call)?; + let (_, receiver_type) = callable.receiver_param(db)?; + let return_type = callable.return_type(); + let (search_method, substs) = match call.name_ref()?.text().as_str() { + "into" => { + let trait_ = + self.resolver.resolve_known_trait(db.upcast(), &path![core::convert::From])?; + ( + self.trait_fn(db, trait_, "from")?, + hir_ty::TyBuilder::subst_for_def(db, trait_, None) + .push(return_type.ty) + .push(receiver_type.ty) + .build(), + ) + } + "try_into" => { + let trait_ = self + .resolver + .resolve_known_trait(db.upcast(), &path![core::convert::TryFrom])?; + ( + self.trait_fn(db, trait_, "try_from")?, + hir_ty::TyBuilder::subst_for_def(db, trait_, None) + // If the method is try_into() or parse(), return_type is Result<T, Error>. + // Get T from type arguments of Result<T, Error>. + .push(return_type.type_arguments().next()?.ty) + .push(receiver_type.ty) + .build(), + ) + } + "parse" => { + let trait_ = + self.resolver.resolve_known_trait(db.upcast(), &path![core::str::FromStr])?; + ( + self.trait_fn(db, trait_, "from_str")?, + hir_ty::TyBuilder::subst_for_def(db, trait_, None) + .push(return_type.type_arguments().next()?.ty) + .build(), + ) + } + _ => return None, + }; + + let found_method = self.resolve_impl_method_or_trait_def(db, search_method, substs); + // If found_method == search_method, the method in trait itself is resolved. + // It means the blanket dual impl is not found. + if found_method == search_method { + None + } else { + Some(found_method.into()) + } + } + pub(crate) fn resolve_expr_as_callable( &self, db: &dyn HirDatabase, @@ -1247,6 +1309,18 @@ impl SourceAnalyzer { Some((trait_id, fn_id)) } + fn trait_fn( + &self, + db: &dyn HirDatabase, + trait_id: TraitId, + method_name: &str, + ) -> Option<FunctionId> { + db.trait_data(trait_id).items.iter().find_map(|(item_name, item)| match item { + AssocItemId::FunctionId(t) if item_name.as_str() == method_name => Some(*t), + _ => None, + }) + } + fn ty_of_expr(&self, db: &dyn HirDatabase, expr: &ast::Expr) -> Option<&Ty> { self.infer.as_ref()?.type_of_expr_or_pat(self.expr_id(db, expr)?) } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/assist_context.rs b/src/tools/rust-analyzer/crates/ide-assists/src/assist_context.rs index 074d943719f..64e77b2d698 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/assist_context.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/assist_context.rs @@ -109,6 +109,10 @@ impl<'a> AssistContext<'a> { self.trimmed_range } + pub(crate) fn source_file(&self) -> &SourceFile { + &self.source_file + } + pub(crate) fn token_at_offset(&self) -> TokenAtOffset<SyntaxToken> { self.source_file.syntax().token_at_offset(self.offset()) } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs index 0cc807aff64..97321f4ec1e 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs @@ -4,6 +4,7 @@ use ide_db::{ syntax_helpers::{suggest_name, LexedStr}, }; use syntax::{ + algo::ancestors_at_offset, ast::{ self, edit::IndentLevel, edit_in_place::Indent, make, syntax_factory::SyntaxFactory, AstNode, @@ -68,7 +69,10 @@ pub(crate) fn extract_variable(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op let node = if ctx.has_empty_selection() { if let Some(t) = ctx.token_at_offset().find(|it| it.kind() == T![;]) { t.parent().and_then(ast::ExprStmt::cast)?.syntax().clone() - } else if let Some(expr) = ctx.find_node_at_offset::<ast::Expr>() { + } else if let Some(expr) = ancestors_at_offset(ctx.source_file().syntax(), ctx.offset()) + .next() + .and_then(ast::Expr::cast) + { expr.syntax().ancestors().find_map(valid_target_expr)?.syntax().clone() } else { return None; @@ -469,11 +473,11 @@ mod tests { extract_variable, r#" fn main() -> i32 { - if true { + if$0 true { 1 } else { 2 - }$0 + } } "#, r#" @@ -581,11 +585,11 @@ fn main() { extract_variable, r#" fn main() -> i32 { - if true { + if$0 true { 1 } else { 2 - }$0 + } } "#, r#" @@ -676,11 +680,11 @@ fn main() { extract_variable, r#" fn main() -> i32 { - if true { + if$0 true { 1 } else { 2 - }$0 + } } "#, r#" diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/generic_args_prohibited.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/generic_args_prohibited.rs index 2b59c1a22f6..7d62daf716c 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/generic_args_prohibited.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/generic_args_prohibited.rs @@ -604,4 +604,23 @@ fn bar() { "#, ); } + + #[test] + fn enum_variant_type_ns() { + check_diagnostics( + r#" +enum KvnDeserializerErr<I> { + UnexpectedKeyword { found: I, expected: I }, +} + +fn foo() { + let _x: KvnDeserializerErr<()> = + KvnDeserializerErr::<()>::UnexpectedKeyword { found: (), expected: () }; + let _x: KvnDeserializerErr<()> = + KvnDeserializerErr::<()>::UnexpectedKeyword::<()> { found: (), expected: () }; + // ^^^^^^ 💡 error: you can specify generic arguments on either the enum or the variant, but not both +} + "#, + ); + } } diff --git a/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs b/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs index 6c739de82d9..f804cc36772 100644 --- a/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs +++ b/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs @@ -81,6 +81,10 @@ pub(crate) fn goto_definition( return Some(RangeInfo::new(original_token.text_range(), navs)); } + if let Some(navs) = find_definition_for_known_blanket_dual_impls(sema, &original_token) { + return Some(RangeInfo::new(original_token.text_range(), navs)); + } + let navs = sema .descend_into_macros_no_opaque(original_token.clone()) .into_iter() @@ -125,6 +129,18 @@ pub(crate) fn goto_definition( Some(RangeInfo::new(original_token.text_range(), navs)) } +// If the token is into(), try_into(), parse(), search the definition of From, TryFrom, FromStr. +fn find_definition_for_known_blanket_dual_impls( + sema: &Semantics<'_, RootDatabase>, + original_token: &SyntaxToken, +) -> Option<Vec<NavigationTarget>> { + let method_call = ast::MethodCallExpr::cast(original_token.parent()?.parent()?)?; + let target_method = sema.resolve_known_blanket_dual_impls(&method_call)?; + + let def = Definition::from(target_method); + Some(def_to_nav(sema.db, def)) +} + fn try_lookup_include_path( sema: &Semantics<'_, RootDatabase>, token: ast::String, @@ -3022,4 +3038,150 @@ fn foo() { "#, ); } + #[test] + fn into_call_to_from_definition() { + check( + r#" +//- minicore: from +struct A; + +struct B; + +impl From<A> for B { + fn from(value: A) -> Self { + //^^^^ + B + } +} + +fn f() { + let a = A; + let b: B = a.into$0(); +} + "#, + ); + } + + #[test] + fn into_call_to_from_definition_with_trait_bounds() { + check( + r#" +//- minicore: from, iterator +struct A; + +impl<T> From<T> for A +where + T: IntoIterator<Item = i64>, +{ + fn from(value: T) -> Self { + //^^^^ + A + } +} + +fn f() { + let a: A = [1, 2, 3].into$0(); +} + "#, + ); + } + + #[test] + fn goto_into_definition_if_exists() { + check( + r#" +//- minicore: from +struct A; + +struct B; + +impl Into<B> for A { + fn into(self) -> B { + //^^^^ + B + } +} + +fn f() { + let a = A; + let b: B = a.into$0(); +} + "#, + ); + } + + #[test] + fn try_into_call_to_try_from_definition() { + check( + r#" +//- minicore: from +struct A; + +struct B; + +impl TryFrom<A> for B { + type Error = String; + + fn try_from(value: A) -> Result<Self, Self::Error> { + //^^^^^^^^ + Ok(B) + } +} + +fn f() { + let a = A; + let b: Result<B, _> = a.try_into$0(); +} + "#, + ); + } + + #[test] + fn goto_try_into_definition_if_exists() { + check( + r#" +//- minicore: from +struct A; + +struct B; + +impl TryInto<B> for A { + type Error = String; + + fn try_into(self) -> Result<B, Self::Error> { + //^^^^^^^^ + Ok(B) + } +} + +fn f() { + let a = A; + let b: Result<B, _> = a.try_into$0(); +} + "#, + ); + } + + #[test] + fn parse_call_to_from_str_definition() { + check( + r#" +//- minicore: from, str +struct A; + +impl FromStr for A { + type Error = String; + + fn from_str(value: &str) -> Result<Self, Self::Error> { + //^^^^^^^^ + Ok(A) + } +} + +fn f() { + let a: Result<A, _> = "aaaaaa".parse$0(); +} + "#, + ); + } } diff --git a/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs b/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs index 131b21a46b8..b3b46421b50 100644 --- a/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs +++ b/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs @@ -174,6 +174,7 @@ define_symbols! { const_param_ty, Context, Continue, + convert, copy, Copy, core_panic, @@ -239,6 +240,8 @@ define_symbols! { format_unsafe_arg, format, freeze, + From, + FromStr, from_output, from_residual, from_usize, @@ -457,6 +460,7 @@ define_symbols! { transmute_trait, transparent, Try, + TryFrom, tuple_trait, u128, u16, diff --git a/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs b/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs index 4462c2ce1e1..fd06736a252 100644 --- a/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs +++ b/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs @@ -32,7 +32,7 @@ //! error: fmt //! fmt: option, result, transmute, coerce_unsized, copy, clone, derive //! fn: tuple -//! from: sized +//! from: sized, result //! future: pin //! coroutine: pin //! dispatch_from_dyn: unsize, pin @@ -332,6 +332,25 @@ pub mod convert { t } } + + pub trait TryFrom<T>: Sized { + type Error; + fn try_from(value: T) -> Result<Self, Self::Error>; + } + pub trait TryInto<T>: Sized { + type Error; + fn try_into(self) -> Result<T, Self::Error>; + } + + impl<T, U> TryInto<U> for T + where + U: TryFrom<T>, + { + type Error = U::Error; + fn try_into(self) -> Result<U, U::Error> { + U::try_from(self) + } + } // endregion:from // region:as_ref @@ -1555,6 +1574,15 @@ pub mod str { pub const unsafe fn from_utf8_unchecked(v: &[u8]) -> &str { "" } + pub trait FromStr: Sized { + type Err; + fn from_str(s: &str) -> Result<Self, Self::Err>; + } + impl str { + pub fn parse<F: FromStr>(&self) -> Result<F, F::Err> { + FromStr::from_str(self) + } + } } // endregion:str @@ -1814,7 +1842,7 @@ pub mod prelude { cmp::{Eq, PartialEq}, // :eq cmp::{Ord, PartialOrd}, // :ord convert::AsRef, // :as_ref - convert::{From, Into}, // :from + convert::{From, Into, TryFrom, TryInto}, // :from default::Default, // :default iter::{IntoIterator, Iterator}, // :iterator macros::builtin::{derive, derive_const}, // :derive @@ -1829,6 +1857,7 @@ pub mod prelude { option::Option::{self, None, Some}, // :option panic, // :panic result::Result::{self, Err, Ok}, // :result + str::FromStr, // :str }; } diff --git a/src/tools/rust-analyzer/lib/lsp-server/src/msg.rs b/src/tools/rust-analyzer/lib/lsp-server/src/msg.rs index 11f98f50790..074bc43388a 100644 --- a/src/tools/rust-analyzer/lib/lsp-server/src/msg.rs +++ b/src/tools/rust-analyzer/lib/lsp-server/src/msg.rs @@ -181,15 +181,15 @@ impl Message { Ok(Some(msg)) } - pub fn write(self, w: &mut impl Write) -> io::Result<()> { + pub fn write(&self, w: &mut impl Write) -> io::Result<()> { self._write(w) } - fn _write(self, w: &mut dyn Write) -> io::Result<()> { + fn _write(&self, w: &mut dyn Write) -> io::Result<()> { #[derive(Serialize)] - struct JsonRpc { + struct JsonRpc<'a> { jsonrpc: &'static str, #[serde(flatten)] - msg: Message, + msg: &'a Message, } let text = serde_json::to_string(&JsonRpc { jsonrpc: "2.0", msg: self })?; write_msg_text(w, &text) diff --git a/src/tools/rust-analyzer/lib/lsp-server/src/socket.rs b/src/tools/rust-analyzer/lib/lsp-server/src/socket.rs index 36d728456f7..48400abf229 100644 --- a/src/tools/rust-analyzer/lib/lsp-server/src/socket.rs +++ b/src/tools/rust-analyzer/lib/lsp-server/src/socket.rs @@ -15,8 +15,11 @@ pub(crate) fn socket_transport( stream: TcpStream, ) -> (Sender<Message>, Receiver<Message>, IoThreads) { let (reader_receiver, reader) = make_reader(stream.try_clone().unwrap()); - let (writer_sender, writer) = make_write(stream); - let io_threads = make_io_threads(reader, writer); + let (writer_sender, writer, messages_to_drop) = make_write(stream); + let dropper = std::thread::spawn(move || { + messages_to_drop.into_iter().for_each(drop); + }); + let io_threads = make_io_threads(reader, writer, dropper); (writer_sender, reader_receiver, io_threads) } @@ -36,11 +39,21 @@ fn make_reader(stream: TcpStream) -> (Receiver<Message>, thread::JoinHandle<io:: (reader_receiver, reader) } -fn make_write(mut stream: TcpStream) -> (Sender<Message>, thread::JoinHandle<io::Result<()>>) { +fn make_write( + mut stream: TcpStream, +) -> (Sender<Message>, thread::JoinHandle<io::Result<()>>, Receiver<Message>) { let (writer_sender, writer_receiver) = bounded::<Message>(0); + let (drop_sender, drop_receiver) = bounded::<Message>(0); let writer = thread::spawn(move || { - writer_receiver.into_iter().try_for_each(|it| it.write(&mut stream)).unwrap(); + writer_receiver + .into_iter() + .try_for_each(|it| { + let result = it.write(&mut stream); + let _ = drop_sender.send(it); + result + }) + .unwrap(); Ok(()) }); - (writer_sender, writer) + (writer_sender, writer, drop_receiver) } diff --git a/src/tools/rust-analyzer/lib/lsp-server/src/stdio.rs b/src/tools/rust-analyzer/lib/lsp-server/src/stdio.rs index 279a6bce080..8344c9f56b5 100644 --- a/src/tools/rust-analyzer/lib/lsp-server/src/stdio.rs +++ b/src/tools/rust-analyzer/lib/lsp-server/src/stdio.rs @@ -11,15 +11,24 @@ use crate::Message; /// Creates an LSP connection via stdio. pub(crate) fn stdio_transport() -> (Sender<Message>, Receiver<Message>, IoThreads) { + let (drop_sender, drop_receiver) = bounded::<Message>(0); let (writer_sender, writer_receiver) = bounded::<Message>(0); let writer = thread::Builder::new() .name("LspServerWriter".to_owned()) .spawn(move || { let stdout = stdout(); let mut stdout = stdout.lock(); - writer_receiver.into_iter().try_for_each(|it| it.write(&mut stdout)) + writer_receiver.into_iter().try_for_each(|it| { + let result = it.write(&mut stdout); + let _ = drop_sender.send(it); + result + }) }) .unwrap(); + let dropper = thread::Builder::new() + .name("LspMessageDropper".to_owned()) + .spawn(move || drop_receiver.into_iter().for_each(drop)) + .unwrap(); let (reader_sender, reader_receiver) = bounded::<Message>(0); let reader = thread::Builder::new() .name("LspServerReader".to_owned()) @@ -41,7 +50,7 @@ pub(crate) fn stdio_transport() -> (Sender<Message>, Receiver<Message>, IoThread Ok(()) }) .unwrap(); - let threads = IoThreads { reader, writer }; + let threads = IoThreads { reader, writer, dropper }; (writer_sender, reader_receiver, threads) } @@ -49,13 +58,15 @@ pub(crate) fn stdio_transport() -> (Sender<Message>, Receiver<Message>, IoThread pub(crate) fn make_io_threads( reader: thread::JoinHandle<io::Result<()>>, writer: thread::JoinHandle<io::Result<()>>, + dropper: thread::JoinHandle<()>, ) -> IoThreads { - IoThreads { reader, writer } + IoThreads { reader, writer, dropper } } pub struct IoThreads { reader: thread::JoinHandle<io::Result<()>>, writer: thread::JoinHandle<io::Result<()>>, + dropper: thread::JoinHandle<()>, } impl IoThreads { @@ -64,6 +75,12 @@ impl IoThreads { Ok(r) => r?, Err(err) => std::panic::panic_any(err), } + match self.dropper.join() { + Ok(_) => (), + Err(err) => { + std::panic::panic_any(err); + } + } match self.writer.join() { Ok(r) => r, Err(err) => { |
