From 5810c8188a2cfc36a8026ae068c985aa9a2f5f2b Mon Sep 17 00:00:00 2001 From: Justin Ridgewell Date: Mon, 8 Aug 2022 20:20:45 -0400 Subject: Implement IntoFuture type inference --- crates/hir-expand/src/mod_path.rs | 1 + crates/hir-expand/src/name.rs | 2 ++ crates/hir-ty/src/infer.rs | 5 ++- crates/hir-ty/src/tests/traits.rs | 25 ++++++++++++++ crates/hir/src/lib.rs | 2 ++ crates/hir/src/source_analyzer.rs | 2 ++ crates/ide-assists/src/utils/suggest_name.rs | 1 + crates/ide-completion/src/completions/keyword.rs | 44 +++++++++++++----------- crates/test-utils/src/minicore.rs | 15 ++++++++ 9 files changed, 75 insertions(+), 22 deletions(-) diff --git a/crates/hir-expand/src/mod_path.rs b/crates/hir-expand/src/mod_path.rs index fea09521e87..9ddafe01810 100644 --- a/crates/hir-expand/src/mod_path.rs +++ b/crates/hir-expand/src/mod_path.rs @@ -257,6 +257,7 @@ macro_rules! __known_path { (core::ops::RangeToInclusive) => {}; (core::ops::RangeInclusive) => {}; (core::future::Future) => {}; + (core::future::IntoFuture) => {}; (core::ops::Try) => {}; ($path:path) => { compile_error!("Please register your known path in the path module") diff --git a/crates/hir-expand/src/name.rs b/crates/hir-expand/src/name.rs index 47d191822d8..757db79db4d 100644 --- a/crates/hir-expand/src/name.rs +++ b/crates/hir-expand/src/name.rs @@ -258,6 +258,7 @@ pub mod known { Try, Ok, Future, + IntoFuture, Result, Option, Output, @@ -391,6 +392,7 @@ pub mod known { future_trait, index, index_mut, + into_future, mul_assign, mul, neg, diff --git a/crates/hir-ty/src/infer.rs b/crates/hir-ty/src/infer.rs index 46eeea0e6fc..95a7229e879 100644 --- a/crates/hir-ty/src/infer.rs +++ b/crates/hir-ty/src/infer.rs @@ -875,7 +875,10 @@ impl<'a> InferenceContext<'a> { } fn resolve_future_future_output(&self) -> Option { - let trait_ = self.resolve_lang_item(name![future_trait])?.as_trait()?; + let trait_ = self + .resolver + .resolve_known_trait(self.db.upcast(), &path![core::future::IntoFuture]) + .or_else(|| self.resolve_lang_item(name![future_trait])?.as_trait())?; self.db.trait_data(trait_).associated_type_by_name(&name![Output]) } diff --git a/crates/hir-ty/src/tests/traits.rs b/crates/hir-ty/src/tests/traits.rs index 75802a5eb4d..730ebe2357d 100644 --- a/crates/hir-ty/src/tests/traits.rs +++ b/crates/hir-ty/src/tests/traits.rs @@ -137,6 +137,31 @@ fn not_send() -> Box + 'static> { ); } +#[test] +fn into_future_trait() { + check_types( + r#" +//- minicore: future +struct Futurable; +impl core::future::IntoFuture for Futurable { + type Output = u64; + type IntoFuture = IntFuture; +} + +struct IntFuture; +impl core::future::Future for IntFuture { + type Output = u64; +} + +fn test() { + let r = Futurable; + let v = r.await; + v; +} //^ u64 +"#, + ); +} + #[test] fn infer_try() { check_types( diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index 8f984210e11..800ea58ba29 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -2780,6 +2780,8 @@ impl Type { /// Checks that particular type `ty` implements `std::future::Future`. /// This function is used in `.await` syntax completion. pub fn impls_future(&self, db: &dyn HirDatabase) -> bool { + // FIXME: This should be checking for IntoFuture trait, but I don't know how to find the + // right TraitId in this crate. let std_future_trait = db .lang_item(self.env.krate, SmolStr::new_inline("future_trait")) .and_then(|it| it.as_trait()); diff --git a/crates/hir/src/source_analyzer.rs b/crates/hir/src/source_analyzer.rs index f5e2e443070..756772cf84e 100644 --- a/crates/hir/src/source_analyzer.rs +++ b/crates/hir/src/source_analyzer.rs @@ -269,6 +269,8 @@ impl SourceAnalyzer { db: &dyn HirDatabase, await_expr: &ast::AwaitExpr, ) -> Option { + // FIXME This should be pointing to the poll of IntoFuture::Output's Future impl, but I + // don't know how to resolve the Output type so that we can query for its poll method. let ty = self.ty_of_expr(db, &await_expr.expr()?.into())?; let op_fn = db diff --git a/crates/ide-assists/src/utils/suggest_name.rs b/crates/ide-assists/src/utils/suggest_name.rs index 779cdbc93c5..68d59daef48 100644 --- a/crates/ide-assists/src/utils/suggest_name.rs +++ b/crates/ide-assists/src/utils/suggest_name.rs @@ -55,6 +55,7 @@ const USELESS_METHODS: &[&str] = &[ "iter", "into_iter", "iter_mut", + "into_future", ]; pub(crate) fn for_generic_parameter(ty: &ast::ImplTraitType) -> SmolStr { diff --git a/crates/ide-completion/src/completions/keyword.rs b/crates/ide-completion/src/completions/keyword.rs index 3989a451bde..032b23725f8 100644 --- a/crates/ide-completion/src/completions/keyword.rs +++ b/crates/ide-completion/src/completions/keyword.rs @@ -75,16 +75,17 @@ impl Future for A {} fn foo(a: A) { a.$0 } "#, expect![[r#" - kw await expr.await - sn box Box::new(expr) - sn call function(expr) - sn dbg dbg!(expr) - sn dbgr dbg!(&expr) - sn let let - sn letm let mut - sn match match expr {} - sn ref &expr - sn refm &mut expr + kw await expr.await + me into_future() (as IntoFuture) fn(self) -> ::IntoFuture + sn box Box::new(expr) + sn call function(expr) + sn dbg dbg!(expr) + sn dbgr dbg!(&expr) + sn let let + sn letm let mut + sn match match expr {} + sn ref &expr + sn refm &mut expr "#]], ); @@ -98,18 +99,19 @@ fn foo() { } "#, expect![[r#" - kw await expr.await - sn box Box::new(expr) - sn call function(expr) - sn dbg dbg!(expr) - sn dbgr dbg!(&expr) - sn let let - sn letm let mut - sn match match expr {} - sn ref &expr - sn refm &mut expr + kw await expr.await + me into_future() (use core::future::IntoFuture) fn(self) -> ::IntoFuture + sn box Box::new(expr) + sn call function(expr) + sn dbg dbg!(expr) + sn dbgr dbg!(&expr) + sn let let + sn letm let mut + sn match match expr {} + sn ref &expr + sn refm &mut expr "#]], - ) + ); } #[test] diff --git a/crates/test-utils/src/minicore.rs b/crates/test-utils/src/minicore.rs index f48d1ec66aa..6df29db4745 100644 --- a/crates/test-utils/src/minicore.rs +++ b/crates/test-utils/src/minicore.rs @@ -471,6 +471,21 @@ pub mod future { #[lang = "poll"] fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll; } + + pub trait IntoFuture { + type Output; + type IntoFuture: Future; + #[lang = "into_future"] + fn into_future(self) -> Self::IntoFuture; + } + + impl IntoFuture for F { + type Output = F::Output; + type IntoFuture = F; + fn into_future(self) -> F { + self + } + } } pub mod task { pub enum Poll { -- cgit 1.4.1-3-g733a5 From dc3219bb11f032e2e6f1d16deab2b5eabe9463d7 Mon Sep 17 00:00:00 2001 From: Justin Ridgewell Date: Tue, 9 Aug 2022 16:25:25 -0400 Subject: Suggest `.await` when type impls IntoFuture --- crates/hir/src/lib.rs | 23 ++++++++++++++------- crates/ide-completion/src/completions/keyword.rs | 26 ++++++++++++++++++++++++ 2 files changed, 42 insertions(+), 7 deletions(-) diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index 800ea58ba29..7f16634afe1 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -2780,19 +2780,28 @@ impl Type { /// Checks that particular type `ty` implements `std::future::Future`. /// This function is used in `.await` syntax completion. pub fn impls_future(&self, db: &dyn HirDatabase) -> bool { - // FIXME: This should be checking for IntoFuture trait, but I don't know how to find the - // right TraitId in this crate. - let std_future_trait = db - .lang_item(self.env.krate, SmolStr::new_inline("future_trait")) - .and_then(|it| it.as_trait()); - let std_future_trait = match std_future_trait { + let trait_ = db + .lang_item(self.env.krate, SmolStr::new_inline("into_future")) + .and_then(|it| { + let into_future_fn = it.as_function()?; + let assoc_item = as_assoc_item(db, AssocItem::Function, into_future_fn)?; + let into_future_trait = assoc_item.containing_trait_or_trait_impl(db)?; + Some(into_future_trait.id) + }) + .or_else(|| { + let future_trait = + db.lang_item(self.env.krate, SmolStr::new_inline("future_trait"))?; + future_trait.as_trait() + }); + + let trait_ = match trait_ { Some(it) => it, None => return false, }; let canonical_ty = Canonical { value: self.ty.clone(), binders: CanonicalVarKinds::empty(Interner) }; - method_resolution::implements_trait(&canonical_ty, db, self.env.clone(), std_future_trait) + method_resolution::implements_trait(&canonical_ty, db, self.env.clone(), trait_) } /// Checks that particular type `ty` implements `std::ops::FnOnce`. diff --git a/crates/ide-completion/src/completions/keyword.rs b/crates/ide-completion/src/completions/keyword.rs index 032b23725f8..1d03c8cc5ca 100644 --- a/crates/ide-completion/src/completions/keyword.rs +++ b/crates/ide-completion/src/completions/keyword.rs @@ -114,6 +114,32 @@ fn foo() { ); } + #[test] + fn test_completion_await_impls_into_future() { + check( + r#" +//- minicore: future +use core::future::*; +struct A {} +impl IntoFuture for A {} +fn foo(a: A) { a.$0 } +"#, + expect![[r#" + kw await expr.await + me into_future() (as IntoFuture) fn(self) -> ::IntoFuture + sn box Box::new(expr) + sn call function(expr) + sn dbg dbg!(expr) + sn dbgr dbg!(&expr) + sn let let + sn letm let mut + sn match match expr {} + sn ref &expr + sn refm &mut expr + "#]], + ); + } + #[test] fn let_semi() { cov_mark::check!(let_semi); -- cgit 1.4.1-3-g733a5 From dcbe892d7ceec113a148a89bc6d1c339c55586d1 Mon Sep 17 00:00:00 2001 From: Jonas Schievink Date: Mon, 15 Aug 2022 13:51:45 +0200 Subject: Add an HIR pretty-printer --- crates/hir-def/src/body.rs | 5 + crates/hir-def/src/body/pretty.rs | 621 +++++++++++++++++++++++++++++++++ crates/hir-def/src/builtin_type.rs | 35 ++ crates/hir-def/src/expr.rs | 6 +- crates/hir-def/src/item_tree/pretty.rs | 179 +--------- crates/hir-def/src/item_tree/tests.rs | 12 +- crates/hir-def/src/lib.rs | 1 + crates/hir-def/src/pretty.rs | 209 +++++++++++ crates/hir-def/src/type_ref.rs | 4 + crates/hir/src/lib.rs | 29 +- crates/ide/src/view_hir.rs | 14 +- editors/code/src/commands.ts | 2 +- 12 files changed, 914 insertions(+), 203 deletions(-) create mode 100644 crates/hir-def/src/body/pretty.rs create mode 100644 crates/hir-def/src/pretty.rs diff --git a/crates/hir-def/src/body.rs b/crates/hir-def/src/body.rs index 080a307b1f8..1d818d96267 100644 --- a/crates/hir-def/src/body.rs +++ b/crates/hir-def/src/body.rs @@ -4,6 +4,7 @@ mod lower; #[cfg(test)] mod tests; pub mod scope; +mod pretty; use std::{ops::Index, sync::Arc}; @@ -352,6 +353,10 @@ impl Body { } } + pub fn pretty_print(&self, db: &dyn DefDatabase, owner: DefWithBodyId) -> String { + pretty::print_body_hir(db, self, owner) + } + fn new( db: &dyn DefDatabase, expander: Expander, diff --git a/crates/hir-def/src/body/pretty.rs b/crates/hir-def/src/body/pretty.rs new file mode 100644 index 00000000000..ddd476efe5c --- /dev/null +++ b/crates/hir-def/src/body/pretty.rs @@ -0,0 +1,621 @@ +//! A pretty-printer for HIR. + +use std::fmt::{self, Write}; + +use crate::{ + expr::{Array, BindingAnnotation, Literal, Statement}, + pretty::{print_generic_args, print_path, print_type_ref}, + type_ref::TypeRef, +}; + +use super::*; + +pub(super) fn print_body_hir(db: &dyn DefDatabase, body: &Body, owner: DefWithBodyId) -> String { + let needs_semi; + let header = match owner { + DefWithBodyId::FunctionId(it) => { + needs_semi = false; + let item_tree_id = it.lookup(db).id; + format!("fn {}(…) ", item_tree_id.item_tree(db)[item_tree_id.value].name) + } + DefWithBodyId::StaticId(it) => { + needs_semi = true; + let item_tree_id = it.lookup(db).id; + format!("static {} = ", item_tree_id.item_tree(db)[item_tree_id.value].name) + } + DefWithBodyId::ConstId(it) => { + needs_semi = true; + let item_tree_id = it.lookup(db).id; + let name = match &item_tree_id.item_tree(db)[item_tree_id.value].name { + Some(name) => name.to_string(), + None => "_".to_string(), + }; + format!("const {} = ", name) + } + }; + + let mut p = Printer { body, buf: header, indent_level: 0, needs_indent: false }; + p.print_expr(body.body_expr); + if needs_semi { + p.buf.push(';'); + } + p.buf +} + +macro_rules! w { + ($dst:expr, $($arg:tt)*) => { + { let _ = write!($dst, $($arg)*); } + }; +} + +macro_rules! wln { + ($dst:expr) => { + { let _ = writeln!($dst); } + }; + ($dst:expr, $($arg:tt)*) => { + { let _ = writeln!($dst, $($arg)*); } + }; +} + +struct Printer<'a> { + body: &'a Body, + buf: String, + indent_level: usize, + needs_indent: bool, +} + +impl<'a> Write for Printer<'a> { + fn write_str(&mut self, s: &str) -> fmt::Result { + for line in s.split_inclusive('\n') { + if self.needs_indent { + match self.buf.chars().rev().skip_while(|ch| *ch == ' ').next() { + Some('\n') | None => {} + _ => self.buf.push('\n'), + } + self.buf.push_str(&" ".repeat(self.indent_level)); + self.needs_indent = false; + } + + self.buf.push_str(line); + self.needs_indent = line.ends_with('\n'); + } + + Ok(()) + } +} + +impl<'a> Printer<'a> { + fn indented(&mut self, f: impl FnOnce(&mut Self)) { + self.indent_level += 1; + wln!(self); + f(self); + self.indent_level -= 1; + self.buf = self.buf.trim_end_matches('\n').to_string(); + } + + fn whitespace(&mut self) { + match self.buf.chars().next_back() { + None | Some('\n' | ' ') => {} + _ => self.buf.push(' '), + } + } + + fn newline(&mut self) { + match self.buf.chars().rev().skip_while(|ch| *ch == ' ').next() { + Some('\n') | None => {} + _ => writeln!(self).unwrap(), + } + } + + fn print_expr(&mut self, expr: ExprId) { + let expr = &self.body[expr]; + + match expr { + Expr::Missing => w!(self, "�"), + Expr::Underscore => w!(self, "_"), + Expr::Path(path) => self.print_path(path), + Expr::If { condition, then_branch, else_branch } => { + w!(self, "if "); + self.print_expr(*condition); + w!(self, " "); + self.print_expr(*then_branch); + if let Some(els) = *else_branch { + w!(self, " else "); + self.print_expr(els); + } + } + Expr::Let { pat, expr } => { + w!(self, "let "); + self.print_pat(*pat); + w!(self, " = "); + self.print_expr(*expr); + } + Expr::Loop { body, label } => { + if let Some(lbl) = label { + w!(self, "{}: ", self.body[*lbl].name); + } + w!(self, "loop "); + self.print_expr(*body); + } + Expr::While { condition, body, label } => { + if let Some(lbl) = label { + w!(self, "{}: ", self.body[*lbl].name); + } + w!(self, "while "); + self.print_expr(*condition); + self.print_expr(*body); + } + Expr::For { iterable, pat, body, label } => { + if let Some(lbl) = label { + w!(self, "{}: ", self.body[*lbl].name); + } + w!(self, "for "); + self.print_pat(*pat); + w!(self, " in "); + self.print_expr(*iterable); + self.print_expr(*body); + } + Expr::Call { callee, args, is_assignee_expr: _ } => { + self.print_expr(*callee); + w!(self, "("); + if !args.is_empty() { + self.indented(|p| { + for arg in &**args { + p.print_expr(*arg); + wln!(p, ","); + } + }); + } + w!(self, ")"); + } + Expr::MethodCall { receiver, method_name, args, generic_args } => { + self.print_expr(*receiver); + w!(self, ".{}", method_name); + if let Some(args) = generic_args { + w!(self, "::<"); + print_generic_args(args, self).unwrap(); + w!(self, ">"); + } + w!(self, "("); + if !args.is_empty() { + self.indented(|p| { + for arg in &**args { + p.print_expr(*arg); + wln!(p, ","); + } + }); + } + w!(self, ")"); + } + Expr::Match { expr, arms } => { + w!(self, "match "); + self.print_expr(*expr); + w!(self, " {{"); + self.indented(|p| { + for arm in &**arms { + p.print_pat(arm.pat); + if let Some(guard) = arm.guard { + w!(p, " if "); + p.print_expr(guard); + } + w!(p, " => "); + p.print_expr(arm.expr); + wln!(p, ","); + } + }); + wln!(self, "}}"); + } + Expr::Continue { label } => { + w!(self, "continue"); + if let Some(label) = label { + w!(self, " {}", label); + } + } + Expr::Break { expr, label } => { + w!(self, "break"); + if let Some(label) = label { + w!(self, " {}", label); + } + if let Some(expr) = expr { + self.whitespace(); + self.print_expr(*expr); + } + } + Expr::Return { expr } => { + w!(self, "return"); + if let Some(expr) = expr { + self.whitespace(); + self.print_expr(*expr); + } + } + Expr::Yield { expr } => { + w!(self, "yield"); + if let Some(expr) = expr { + self.whitespace(); + self.print_expr(*expr); + } + } + Expr::RecordLit { path, fields, spread, ellipsis, is_assignee_expr: _ } => { + match path { + Some(path) => self.print_path(path), + None => w!(self, "�"), + } + + w!(self, "{{"); + self.indented(|p| { + for field in &**fields { + w!(p, "{}: ", field.name); + p.print_expr(field.expr); + wln!(p, ","); + } + if let Some(spread) = spread { + w!(p, ".."); + p.print_expr(*spread); + wln!(p); + } + if *ellipsis { + wln!(p, ".."); + } + }); + w!(self, "}}"); + } + Expr::Field { expr, name } => { + self.print_expr(*expr); + w!(self, ".{}", name); + } + Expr::Await { expr } => { + self.print_expr(*expr); + w!(self, ".await"); + } + Expr::Try { expr } => { + self.print_expr(*expr); + w!(self, "?"); + } + Expr::TryBlock { body } => { + w!(self, "try "); + self.print_expr(*body); + } + Expr::Async { body } => { + w!(self, "async "); + self.print_expr(*body); + } + Expr::Const { body } => { + w!(self, "const "); + self.print_expr(*body); + } + Expr::Cast { expr, type_ref } => { + self.print_expr(*expr); + w!(self, " as "); + self.print_type_ref(type_ref); + } + Expr::Ref { expr, rawness, mutability } => { + w!(self, "&"); + if rawness.is_raw() { + w!(self, "raw "); + } + if mutability.is_mut() { + w!(self, "mut "); + } + self.print_expr(*expr); + } + Expr::Box { expr } => { + w!(self, "box "); + self.print_expr(*expr); + } + Expr::UnaryOp { expr, op } => { + let op = match op { + ast::UnaryOp::Deref => "*", + ast::UnaryOp::Not => "!", + ast::UnaryOp::Neg => "-", + }; + w!(self, "{}", op); + self.print_expr(*expr); + } + Expr::BinaryOp { lhs, rhs, op } => { + let (bra, ket) = match op { + None | Some(ast::BinaryOp::Assignment { .. }) => ("", ""), + _ => ("(", ")"), + }; + w!(self, "{}", bra); + self.print_expr(*lhs); + w!(self, "{} ", ket); + match op { + Some(op) => w!(self, "{}", op), + None => w!(self, "�"), // :) + } + w!(self, " {}", bra); + self.print_expr(*rhs); + w!(self, "{}", ket); + } + Expr::Range { lhs, rhs, range_type } => { + if let Some(lhs) = lhs { + w!(self, "("); + self.print_expr(*lhs); + w!(self, ") "); + } + let range = match range_type { + ast::RangeOp::Exclusive => "..", + ast::RangeOp::Inclusive => "..=", + }; + w!(self, "{}", range); + if let Some(rhs) = rhs { + w!(self, "("); + self.print_expr(*rhs); + w!(self, ") "); + } + } + Expr::Index { base, index } => { + self.print_expr(*base); + w!(self, "["); + self.print_expr(*index); + w!(self, "]"); + } + Expr::Closure { args, arg_types, ret_type, body } => { + w!(self, "|"); + for (i, (pat, ty)) in args.iter().zip(arg_types.iter()).enumerate() { + if i != 0 { + w!(self, ", "); + } + self.print_pat(*pat); + if let Some(ty) = ty { + w!(self, ": "); + self.print_type_ref(ty); + } + } + w!(self, "|"); + if let Some(ret_ty) = ret_type { + w!(self, " -> "); + self.print_type_ref(ret_ty); + } + self.whitespace(); + self.print_expr(*body); + } + Expr::Tuple { exprs, is_assignee_expr: _ } => { + w!(self, "("); + for expr in exprs.iter() { + self.print_expr(*expr); + w!(self, ", "); + } + w!(self, ")"); + } + Expr::Unsafe { body } => { + w!(self, "unsafe "); + self.print_expr(*body); + } + Expr::Array(arr) => { + w!(self, "["); + if !matches!(arr, Array::ElementList { elements, .. } if elements.is_empty()) { + self.indented(|p| match arr { + Array::ElementList { elements, is_assignee_expr: _ } => { + for elem in elements.iter() { + p.print_expr(*elem); + w!(p, ", "); + } + } + Array::Repeat { initializer, repeat } => { + p.print_expr(*initializer); + w!(p, "; "); + p.print_expr(*repeat); + } + }); + self.newline(); + } + w!(self, "]"); + } + Expr::Literal(lit) => self.print_literal(lit), + Expr::Block { id: _, statements, tail, label } => { + self.whitespace(); + if let Some(lbl) = label { + w!(self, "{}: ", self.body[*lbl].name); + } + w!(self, "{{"); + if !statements.is_empty() || tail.is_some() { + self.indented(|p| { + for stmt in &**statements { + p.print_stmt(stmt); + } + if let Some(tail) = tail { + p.print_expr(*tail); + } + p.newline(); + }); + } + w!(self, "}}"); + } + Expr::MacroStmts { statements, tail } => { + w!(self, "{{ // macro statements"); + self.indented(|p| { + for stmt in statements.iter() { + p.print_stmt(stmt); + } + if let Some(tail) = tail { + p.print_expr(*tail); + } + }); + self.newline(); + w!(self, "}}"); + } + } + } + + fn print_pat(&mut self, pat: PatId) { + let pat = &self.body[pat]; + + match pat { + Pat::Missing => w!(self, "�"), + Pat::Wild => w!(self, "_"), + Pat::Tuple { args, ellipsis } => { + w!(self, "("); + for (i, pat) in args.iter().enumerate() { + if i != 0 { + w!(self, ", "); + } + if *ellipsis == Some(i) { + w!(self, ".., "); + } + self.print_pat(*pat); + } + w!(self, ")"); + } + Pat::Or(pats) => { + for (i, pat) in pats.iter().enumerate() { + if i != 0 { + w!(self, " | "); + } + self.print_pat(*pat); + } + } + Pat::Record { path, args, ellipsis } => { + match path { + Some(path) => self.print_path(path), + None => w!(self, "�"), + } + + w!(self, " {{"); + self.indented(|p| { + for arg in args.iter() { + w!(p, "{}: ", arg.name); + p.print_pat(arg.pat); + wln!(p, ","); + } + if *ellipsis { + wln!(p, ".."); + } + }); + w!(self, "}}"); + } + Pat::Range { start, end } => { + self.print_expr(*start); + w!(self, "..."); + self.print_expr(*end); + } + Pat::Slice { prefix, slice, suffix } => { + w!(self, "["); + for pat in prefix.iter() { + self.print_pat(*pat); + w!(self, ", "); + } + if let Some(pat) = slice { + self.print_pat(*pat); + w!(self, ", "); + } + for pat in suffix.iter() { + self.print_pat(*pat); + w!(self, ", "); + } + w!(self, "]"); + } + Pat::Path(path) => self.print_path(path), + Pat::Lit(expr) => self.print_expr(*expr), + Pat::Bind { mode, name, subpat } => { + let mode = match mode { + BindingAnnotation::Unannotated => "", + BindingAnnotation::Mutable => "mut ", + BindingAnnotation::Ref => "ref ", + BindingAnnotation::RefMut => "ref mut ", + }; + w!(self, "{}{}", mode, name); + if let Some(pat) = subpat { + self.whitespace(); + self.print_pat(*pat); + } + } + Pat::TupleStruct { path, args, ellipsis } => { + match path { + Some(path) => self.print_path(path), + None => w!(self, "�"), + } + w!(self, "("); + for (i, arg) in args.iter().enumerate() { + if i != 0 { + w!(self, ", "); + } + if *ellipsis == Some(i) { + w!(self, ", .."); + } + self.print_pat(*arg); + } + w!(self, ")"); + } + Pat::Ref { pat, mutability } => { + w!(self, "&"); + if mutability.is_mut() { + w!(self, "mut "); + } + self.print_pat(*pat); + } + Pat::Box { inner } => { + w!(self, "box "); + self.print_pat(*inner); + } + Pat::ConstBlock(c) => { + w!(self, "const "); + self.print_expr(*c); + } + } + } + + fn print_stmt(&mut self, stmt: &Statement) { + match stmt { + Statement::Let { pat, type_ref, initializer, else_branch } => { + w!(self, "let "); + self.print_pat(*pat); + if let Some(ty) = type_ref { + w!(self, ": "); + self.print_type_ref(ty); + } + if let Some(init) = initializer { + w!(self, " = "); + self.print_expr(*init); + } + if let Some(els) = else_branch { + w!(self, " else "); + self.print_expr(*els); + } + wln!(self, ";"); + } + Statement::Expr { expr, has_semi } => { + self.print_expr(*expr); + if *has_semi { + w!(self, ";"); + } + wln!(self); + } + } + } + + fn print_literal(&mut self, literal: &Literal) { + match literal { + Literal::String(it) => w!(self, "{:?}", it), + Literal::ByteString(it) => w!(self, "\"{}\"", it.escape_ascii()), + Literal::Char(it) => w!(self, "'{}'", it.escape_debug()), + Literal::Bool(it) => w!(self, "{}", it), + Literal::Int(i, suffix) => { + w!(self, "{}", i); + if let Some(suffix) = suffix { + w!(self, "{}", suffix); + } + } + Literal::Uint(i, suffix) => { + w!(self, "{}", i); + if let Some(suffix) = suffix { + w!(self, "{}", suffix); + } + } + Literal::Float(f, suffix) => { + w!(self, "{}", f); + if let Some(suffix) = suffix { + w!(self, "{}", suffix); + } + } + } + } + + fn print_type_ref(&mut self, ty: &TypeRef) { + print_type_ref(ty, self).unwrap(); + } + + fn print_path(&mut self, path: &Path) { + print_path(path, self).unwrap(); + } +} diff --git a/crates/hir-def/src/builtin_type.rs b/crates/hir-def/src/builtin_type.rs index 25a408036ff..dd69c3ab473 100644 --- a/crates/hir-def/src/builtin_type.rs +++ b/crates/hir-def/src/builtin_type.rs @@ -156,3 +156,38 @@ impl BuiltinFloat { Some(res) } } + +impl fmt::Display for BuiltinInt { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(match self { + BuiltinInt::Isize => "isize", + BuiltinInt::I8 => "i8", + BuiltinInt::I16 => "i16", + BuiltinInt::I32 => "i32", + BuiltinInt::I64 => "i64", + BuiltinInt::I128 => "i128", + }) + } +} + +impl fmt::Display for BuiltinUint { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(match self { + BuiltinUint::Usize => "usize", + BuiltinUint::U8 => "u8", + BuiltinUint::U16 => "u16", + BuiltinUint::U32 => "u32", + BuiltinUint::U64 => "u64", + BuiltinUint::U128 => "u128", + }) + } +} + +impl fmt::Display for BuiltinFloat { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(match self { + BuiltinFloat::F32 => "f32", + BuiltinFloat::F64 => "f64", + }) + } +} diff --git a/crates/hir-def/src/expr.rs b/crates/hir-def/src/expr.rs index c1b3788acb7..4381b43c258 100644 --- a/crates/hir-def/src/expr.rs +++ b/crates/hir-def/src/expr.rs @@ -12,6 +12,8 @@ //! //! See also a neighboring `body` module. +use std::fmt; + use hir_expand::name::Name; use la_arena::{Idx, RawIdx}; @@ -52,8 +54,8 @@ impl FloatTypeWrapper { } } -impl std::fmt::Display for FloatTypeWrapper { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { +impl fmt::Display for FloatTypeWrapper { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{:?}", f64::from_bits(self.0)) } } diff --git a/crates/hir-def/src/item_tree/pretty.rs b/crates/hir-def/src/item_tree/pretty.rs index f12d9a1273c..34dd817fd13 100644 --- a/crates/hir-def/src/item_tree/pretty.rs +++ b/crates/hir-def/src/item_tree/pretty.rs @@ -2,13 +2,10 @@ use std::fmt::{self, Write}; -use itertools::Itertools; - use crate::{ attr::RawAttrs, generics::{TypeOrConstParamData, WherePredicate, WherePredicateTypeTarget}, - path::GenericArg, - type_ref::TraitBoundModifier, + pretty::{print_path, print_type_bounds, print_type_ref}, visibility::RawVisibility, }; @@ -464,183 +461,15 @@ impl<'a> Printer<'a> { } fn print_type_ref(&mut self, type_ref: &TypeRef) { - // FIXME: deduplicate with `HirDisplay` impl - match type_ref { - TypeRef::Never => w!(self, "!"), - TypeRef::Placeholder => w!(self, "_"), - TypeRef::Tuple(fields) => { - w!(self, "("); - for (i, field) in fields.iter().enumerate() { - if i != 0 { - w!(self, ", "); - } - self.print_type_ref(field); - } - w!(self, ")"); - } - TypeRef::Path(path) => self.print_path(path), - TypeRef::RawPtr(pointee, mtbl) => { - let mtbl = match mtbl { - Mutability::Shared => "*const", - Mutability::Mut => "*mut", - }; - w!(self, "{} ", mtbl); - self.print_type_ref(pointee); - } - TypeRef::Reference(pointee, lt, mtbl) => { - let mtbl = match mtbl { - Mutability::Shared => "", - Mutability::Mut => "mut ", - }; - w!(self, "&"); - if let Some(lt) = lt { - w!(self, "{} ", lt.name); - } - w!(self, "{}", mtbl); - self.print_type_ref(pointee); - } - TypeRef::Array(elem, len) => { - w!(self, "["); - self.print_type_ref(elem); - w!(self, "; {}]", len); - } - TypeRef::Slice(elem) => { - w!(self, "["); - self.print_type_ref(elem); - w!(self, "]"); - } - TypeRef::Fn(args_and_ret, varargs) => { - let ((_, return_type), args) = - args_and_ret.split_last().expect("TypeRef::Fn is missing return type"); - w!(self, "fn("); - for (i, (_, typeref)) in args.iter().enumerate() { - if i != 0 { - w!(self, ", "); - } - self.print_type_ref(typeref); - } - if *varargs { - if !args.is_empty() { - w!(self, ", "); - } - w!(self, "..."); - } - w!(self, ") -> "); - self.print_type_ref(return_type); - } - TypeRef::Macro(_ast_id) => { - w!(self, ""); - } - TypeRef::Error => w!(self, "{{unknown}}"), - TypeRef::ImplTrait(bounds) => { - w!(self, "impl "); - self.print_type_bounds(bounds); - } - TypeRef::DynTrait(bounds) => { - w!(self, "dyn "); - self.print_type_bounds(bounds); - } - } + print_type_ref(type_ref, self).unwrap(); } fn print_type_bounds(&mut self, bounds: &[Interned]) { - for (i, bound) in bounds.iter().enumerate() { - if i != 0 { - w!(self, " + "); - } - - match bound.as_ref() { - TypeBound::Path(path, modifier) => { - match modifier { - TraitBoundModifier::None => (), - TraitBoundModifier::Maybe => w!(self, "?"), - } - self.print_path(path) - } - TypeBound::ForLifetime(lifetimes, path) => { - w!(self, "for<{}> ", lifetimes.iter().format(", ")); - self.print_path(path); - } - TypeBound::Lifetime(lt) => w!(self, "{}", lt.name), - TypeBound::Error => w!(self, "{{unknown}}"), - } - } + print_type_bounds(bounds, self).unwrap(); } fn print_path(&mut self, path: &Path) { - match path.type_anchor() { - Some(anchor) => { - w!(self, "<"); - self.print_type_ref(anchor); - w!(self, ">::"); - } - None => match path.kind() { - PathKind::Plain => {} - PathKind::Super(0) => w!(self, "self::"), - PathKind::Super(n) => { - for _ in 0..*n { - w!(self, "super::"); - } - } - PathKind::Crate => w!(self, "crate::"), - PathKind::Abs => w!(self, "::"), - PathKind::DollarCrate(_) => w!(self, "$crate::"), - }, - } - - for (i, segment) in path.segments().iter().enumerate() { - if i != 0 { - w!(self, "::"); - } - - w!(self, "{}", segment.name); - if let Some(generics) = segment.args_and_bindings { - // NB: these are all in type position, so `::<` turbofish syntax is not necessary - w!(self, "<"); - let mut first = true; - let args = if generics.has_self_type { - let (self_ty, args) = generics.args.split_first().unwrap(); - w!(self, "Self="); - self.print_generic_arg(self_ty); - first = false; - args - } else { - &generics.args - }; - for arg in args { - if !first { - w!(self, ", "); - } - first = false; - self.print_generic_arg(arg); - } - for binding in &generics.bindings { - if !first { - w!(self, ", "); - } - first = false; - w!(self, "{}", binding.name); - if !binding.bounds.is_empty() { - w!(self, ": "); - self.print_type_bounds(&binding.bounds); - } - if let Some(ty) = &binding.type_ref { - w!(self, " = "); - self.print_type_ref(ty); - } - } - - w!(self, ">"); - } - } - } - - fn print_generic_arg(&mut self, arg: &GenericArg) { - match arg { - GenericArg::Type(ty) => self.print_type_ref(ty), - GenericArg::Const(c) => w!(self, "{}", c), - GenericArg::Lifetime(lt) => w!(self, "{}", lt.name), - } + print_path(path, self).unwrap(); } fn print_generic_params(&mut self, params: &GenericParams) { diff --git a/crates/hir-def/src/item_tree/tests.rs b/crates/hir-def/src/item_tree/tests.rs index 5cdf36cc61b..e30d9652bb5 100644 --- a/crates/hir-def/src/item_tree/tests.rs +++ b/crates/hir-def/src/item_tree/tests.rs @@ -283,10 +283,10 @@ struct S { "#, expect![[r#" pub(self) struct S { - pub(self) a: Mixed<'a, T, Item = (), OtherItem = u8>, - pub(self) b: Qualified::Syntax, - pub(self) c: ::Path<'a>, - pub(self) d: dyn for<'a> Trait<'a>, + pub(self) a: Mixed::<'a, T, Item = (), OtherItem = u8>, + pub(self) b: Qualified::::Syntax, + pub(self) c: ::Path::<'a>, + pub(self) d: dyn for<'a> Trait::<'a>, } "#]], ) @@ -329,7 +329,7 @@ trait Tr<'a, T: 'a>: Super where Self: for<'a> Tr<'a, T> {} T: Copy, U: ?Sized; - impl<'a, 'b, T, const K: u8> S<'a, 'b, T, K> + impl<'a, 'b, T, const K: u8> S::<'a, 'b, T, K> where T: Copy, T: 'a, @@ -352,7 +352,7 @@ trait Tr<'a, T: 'a>: Super where Self: for<'a> Tr<'a, T> {} where Self: Super, T: 'a, - Self: for<'a> Tr<'a, T> + Self: for<'a> Tr::<'a, T> { } "#]], diff --git a/crates/hir-def/src/lib.rs b/crates/hir-def/src/lib.rs index 56603f4b154..32ebfda4fd9 100644 --- a/crates/hir-def/src/lib.rs +++ b/crates/hir-def/src/lib.rs @@ -53,6 +53,7 @@ pub mod import_map; mod test_db; #[cfg(test)] mod macro_expansion_tests; +mod pretty; use std::{ hash::{Hash, Hasher}, diff --git a/crates/hir-def/src/pretty.rs b/crates/hir-def/src/pretty.rs new file mode 100644 index 00000000000..6636c8a23ca --- /dev/null +++ b/crates/hir-def/src/pretty.rs @@ -0,0 +1,209 @@ +//! Display and pretty printing routines. + +use std::fmt::{self, Write}; + +use hir_expand::mod_path::PathKind; +use itertools::Itertools; + +use crate::{ + intern::Interned, + path::{GenericArg, GenericArgs, Path}, + type_ref::{Mutability, TraitBoundModifier, TypeBound, TypeRef}, +}; + +pub(crate) fn print_path(path: &Path, buf: &mut dyn Write) -> fmt::Result { + match path.type_anchor() { + Some(anchor) => { + write!(buf, "<")?; + print_type_ref(anchor, buf)?; + write!(buf, ">::")?; + } + None => match path.kind() { + PathKind::Plain => {} + PathKind::Super(0) => write!(buf, "self")?, + PathKind::Super(n) => { + for i in 0..*n { + if i == 0 { + buf.write_str("super")?; + } else { + buf.write_str("::super")?; + } + } + } + PathKind::Crate => write!(buf, "crate")?, + PathKind::Abs => {} + PathKind::DollarCrate(_) => write!(buf, "$crate")?, + }, + } + + for (i, segment) in path.segments().iter().enumerate() { + if i != 0 || !matches!(path.kind(), PathKind::Plain) { + write!(buf, "::")?; + } + + write!(buf, "{}", segment.name)?; + if let Some(generics) = segment.args_and_bindings { + write!(buf, "::<")?; + print_generic_args(generics, buf)?; + + write!(buf, ">")?; + } + } + + Ok(()) +} + +pub(crate) fn print_generic_args(generics: &GenericArgs, buf: &mut dyn Write) -> fmt::Result { + let mut first = true; + let args = if generics.has_self_type { + let (self_ty, args) = generics.args.split_first().unwrap(); + write!(buf, "Self=")?; + print_generic_arg(self_ty, buf)?; + first = false; + args + } else { + &generics.args + }; + for arg in args { + if !first { + write!(buf, ", ")?; + } + first = false; + print_generic_arg(arg, buf)?; + } + for binding in &generics.bindings { + if !first { + write!(buf, ", ")?; + } + first = false; + write!(buf, "{}", binding.name)?; + if !binding.bounds.is_empty() { + write!(buf, ": ")?; + print_type_bounds(&binding.bounds, buf)?; + } + if let Some(ty) = &binding.type_ref { + write!(buf, " = ")?; + print_type_ref(ty, buf)?; + } + } + Ok(()) +} + +pub(crate) fn print_generic_arg(arg: &GenericArg, buf: &mut dyn Write) -> fmt::Result { + match arg { + GenericArg::Type(ty) => print_type_ref(ty, buf), + GenericArg::Const(c) => write!(buf, "{}", c), + GenericArg::Lifetime(lt) => write!(buf, "{}", lt.name), + } +} + +pub(crate) fn print_type_ref(type_ref: &TypeRef, buf: &mut dyn Write) -> fmt::Result { + // FIXME: deduplicate with `HirDisplay` impl + match type_ref { + TypeRef::Never => write!(buf, "!")?, + TypeRef::Placeholder => write!(buf, "_")?, + TypeRef::Tuple(fields) => { + write!(buf, "(")?; + for (i, field) in fields.iter().enumerate() { + if i != 0 { + write!(buf, ", ")?; + } + print_type_ref(field, buf)?; + } + write!(buf, ")")?; + } + TypeRef::Path(path) => print_path(path, buf)?, + TypeRef::RawPtr(pointee, mtbl) => { + let mtbl = match mtbl { + Mutability::Shared => "*const", + Mutability::Mut => "*mut", + }; + write!(buf, "{} ", mtbl)?; + print_type_ref(pointee, buf)?; + } + TypeRef::Reference(pointee, lt, mtbl) => { + let mtbl = match mtbl { + Mutability::Shared => "", + Mutability::Mut => "mut ", + }; + write!(buf, "&")?; + if let Some(lt) = lt { + write!(buf, "{} ", lt.name)?; + } + write!(buf, "{}", mtbl)?; + print_type_ref(pointee, buf)?; + } + TypeRef::Array(elem, len) => { + write!(buf, "[")?; + print_type_ref(elem, buf)?; + write!(buf, "; {}]", len)?; + } + TypeRef::Slice(elem) => { + write!(buf, "[")?; + print_type_ref(elem, buf)?; + write!(buf, "]")?; + } + TypeRef::Fn(args_and_ret, varargs) => { + let ((_, return_type), args) = + args_and_ret.split_last().expect("TypeRef::Fn is missing return type"); + write!(buf, "fn(")?; + for (i, (_, typeref)) in args.iter().enumerate() { + if i != 0 { + write!(buf, ", ")?; + } + print_type_ref(typeref, buf)?; + } + if *varargs { + if !args.is_empty() { + write!(buf, ", ")?; + } + write!(buf, "...")?; + } + write!(buf, ") -> ")?; + print_type_ref(return_type, buf)?; + } + TypeRef::Macro(_ast_id) => { + write!(buf, "")?; + } + TypeRef::Error => write!(buf, "{{unknown}}")?, + TypeRef::ImplTrait(bounds) => { + write!(buf, "impl ")?; + print_type_bounds(bounds, buf)?; + } + TypeRef::DynTrait(bounds) => { + write!(buf, "dyn ")?; + print_type_bounds(bounds, buf)?; + } + } + + Ok(()) +} + +pub(crate) fn print_type_bounds( + bounds: &[Interned], + buf: &mut dyn Write, +) -> fmt::Result { + for (i, bound) in bounds.iter().enumerate() { + if i != 0 { + write!(buf, " + ")?; + } + + match bound.as_ref() { + TypeBound::Path(path, modifier) => { + match modifier { + TraitBoundModifier::None => (), + TraitBoundModifier::Maybe => write!(buf, "?")?, + } + print_path(path, buf)?; + } + TypeBound::ForLifetime(lifetimes, path) => { + write!(buf, "for<{}> ", lifetimes.iter().format(", "))?; + print_path(path, buf)?; + } + TypeBound::Lifetime(lt) => write!(buf, "{}", lt.name)?, + TypeBound::Error => write!(buf, "{{unknown}}")?, + } + } + + Ok(()) +} diff --git a/crates/hir-def/src/type_ref.rs b/crates/hir-def/src/type_ref.rs index 9248059627d..5b4c71be7fb 100644 --- a/crates/hir-def/src/type_ref.rs +++ b/crates/hir-def/src/type_ref.rs @@ -77,6 +77,10 @@ impl Rawness { Rawness::Ref } } + + pub fn is_raw(&self) -> bool { + matches!(self, Self::RawPtr) + } } #[derive(Clone, PartialEq, Eq, Hash, Debug)] diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index 8f984210e11..3561bdeba7f 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -72,7 +72,7 @@ use itertools::Itertools; use nameres::diagnostics::DefDiagnosticKind; use once_cell::unsync::Lazy; use rustc_hash::FxHashSet; -use stdx::{format_to, impl_from, never}; +use stdx::{impl_from, never}; use syntax::{ ast::{self, HasAttrs as _, HasDocComments, HasName}, AstNode, AstPtr, SmolStr, SyntaxNodePtr, TextRange, T, @@ -1136,6 +1136,20 @@ impl DefWithBody { } } + fn id(&self) -> DefWithBodyId { + match self { + DefWithBody::Function(it) => it.id.into(), + DefWithBody::Static(it) => it.id.into(), + DefWithBody::Const(it) => it.id.into(), + } + } + + /// A textual representation of the HIR of this def's body for debugging purposes. + pub fn debug_hir(self, db: &dyn HirDatabase) -> String { + let body = db.body(self.id()); + body.pretty_print(db.upcast(), self.id()) + } + pub fn diagnostics(self, db: &dyn HirDatabase, acc: &mut Vec) { let krate = self.module(db).id.krate(); @@ -1470,19 +1484,6 @@ impl Function { let def_map = db.crate_def_map(loc.krate(db).into()); def_map.fn_as_proc_macro(self.id).map(|id| Macro { id: id.into() }) } - - /// A textual representation of the HIR of this function for debugging purposes. - pub fn debug_hir(self, db: &dyn HirDatabase) -> String { - let body = db.body(self.id.into()); - - let mut result = String::new(); - format_to!(result, "HIR expressions in the body of `{}`:\n", self.name(db)); - for (id, expr) in body.exprs.iter() { - format_to!(result, "{:?}: {:?}\n", id, expr); - } - - result - } } // Note: logically, this belongs to `hir_ty`, but we are not using it there yet. diff --git a/crates/ide/src/view_hir.rs b/crates/ide/src/view_hir.rs index bf0835ed7e0..d2bbbf6d26a 100644 --- a/crates/ide/src/view_hir.rs +++ b/crates/ide/src/view_hir.rs @@ -1,4 +1,4 @@ -use hir::{Function, Semantics}; +use hir::{DefWithBody, Semantics}; use ide_db::base_db::FilePosition; use ide_db::RootDatabase; use syntax::{algo::find_node_at_offset, ast, AstNode}; @@ -19,8 +19,12 @@ fn body_hir(db: &RootDatabase, position: FilePosition) -> Option { let sema = Semantics::new(db); let source_file = sema.parse(position.file_id); - let function = find_node_at_offset::(source_file.syntax(), position.offset)?; - - let function: Function = sema.to_def(&function)?; - Some(function.debug_hir(db)) + let item = find_node_at_offset::(source_file.syntax(), position.offset)?; + let def: DefWithBody = match item { + ast::Item::Fn(it) => sema.to_def(&it)?.into(), + ast::Item::Const(it) => sema.to_def(&it)?.into(), + ast::Item::Static(it) => sema.to_def(&it)?.into(), + _ => return None, + }; + Some(def.debug_hir(db)) } diff --git a/editors/code/src/commands.ts b/editors/code/src/commands.ts index 1b793bb0b15..f58de9da1bf 100644 --- a/editors/code/src/commands.ts +++ b/editors/code/src/commands.ts @@ -433,7 +433,7 @@ export function syntaxTree(ctx: Ctx): Cmd { // The contents of the file come from the `TextDocumentContentProvider` export function viewHir(ctx: Ctx): Cmd { const tdcp = new (class implements vscode.TextDocumentContentProvider { - readonly uri = vscode.Uri.parse("rust-analyzer-hir://viewHir/hir.txt"); + readonly uri = vscode.Uri.parse("rust-analyzer-hir://viewHir/hir.rs"); readonly eventEmitter = new vscode.EventEmitter(); constructor() { vscode.workspace.onDidChangeTextDocument( -- cgit 1.4.1-3-g733a5 From 3f149a63d226cbdedd466d872677da765ada9df8 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Mon, 15 Aug 2022 16:16:59 +0200 Subject: Simplify --- crates/hir-expand/src/ast_id_map.rs | 21 ++++++++------------- crates/hir-expand/src/db.rs | 6 +++++- crates/hir-expand/src/lib.rs | 1 - crates/ide/src/hover.rs | 2 ++ 4 files changed, 15 insertions(+), 15 deletions(-) diff --git a/crates/hir-expand/src/ast_id_map.rs b/crates/hir-expand/src/ast_id_map.rs index c1ddef03ba3..11c0a6764e9 100644 --- a/crates/hir-expand/src/ast_id_map.rs +++ b/crates/hir-expand/src/ast_id_map.rs @@ -15,7 +15,7 @@ use std::{ use la_arena::{Arena, Idx}; use profile::Count; use rustc_hash::FxHasher; -use syntax::{ast, match_ast, AstNode, AstPtr, SyntaxNode, SyntaxNodePtr}; +use syntax::{ast, AstNode, AstPtr, SyntaxNode, SyntaxNodePtr}; /// `AstId` points to an AST node in a specific file. pub struct FileAstId { @@ -92,18 +92,12 @@ impl AstIdMap { // change parent's id. This means that, say, adding a new function to a // trait does not change ids of top-level items, which helps caching. bdfs(node, |it| { - match_ast! { - match it { - ast::Item(module_item) => { - res.alloc(module_item.syntax()); - true - }, - ast::BlockExpr(block) => { - res.alloc(block.syntax()); - true - }, - _ => false, - } + let kind = it.kind(); + if ast::Item::can_cast(kind) || ast::BlockExpr::can_cast(kind) { + res.alloc(&it); + true + } else { + false } }); res.map = hashbrown::HashMap::with_capacity_and_hasher(res.arena.len(), ()); @@ -123,6 +117,7 @@ impl AstIdMap { let raw = self.erased_ast_id(item.syntax()); FileAstId { raw, _ty: PhantomData } } + fn erased_ast_id(&self, item: &SyntaxNode) -> ErasedFileAstId { let ptr = SyntaxNodePtr::new(item); let hash = hash_ptr(&ptr); diff --git a/crates/hir-expand/src/db.rs b/crates/hir-expand/src/db.rs index bd60c3d2686..bc97ee15c7d 100644 --- a/crates/hir-expand/src/db.rs +++ b/crates/hir-expand/src/db.rs @@ -321,7 +321,11 @@ fn censor_for_macro_input(loc: &MacroCallLoc, node: &SyntaxNode) -> FxHashSet), BuiltIn(BuiltinFnLikeExpander, AstId), - // FIXME: maybe just Builtin and rename BuiltinFnLikeExpander to BuiltinExpander BuiltInAttr(BuiltinAttrExpander, AstId), BuiltInDerive(BuiltinDeriveExpander, AstId), BuiltInEager(EagerExpander, AstId), diff --git a/crates/ide/src/hover.rs b/crates/ide/src/hover.rs index 3ada181f1ed..fa2c6f09ab4 100644 --- a/crates/ide/src/hover.rs +++ b/crates/ide/src/hover.rs @@ -119,6 +119,8 @@ pub(crate) fn hover( } let in_attr = matches!(original_token.parent().and_then(ast::TokenTree::cast), Some(tt) if tt.syntax().ancestors().any(|it| ast::Meta::can_cast(it.kind()))); + // prefer descending the same token kind in attribute expansions, in normal macros text + // equivalency is more important let descended = if in_attr { [sema.descend_into_macros_with_kind_preference(original_token.clone())].into() } else { -- cgit 1.4.1-3-g733a5 From 88b19cc39b14446cc12e02a20c6f489f9883b760 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Mon, 15 Aug 2022 16:41:51 +0200 Subject: Make resolve_name_in_module a bit more lazy --- crates/hir-def/src/nameres/path_resolution.rs | 32 +++++++++++++-------------- crates/hir-def/src/per_ns.rs | 12 ++++++++++ 2 files changed, 28 insertions(+), 16 deletions(-) diff --git a/crates/hir-def/src/nameres/path_resolution.rs b/crates/hir-def/src/nameres/path_resolution.rs index cc5fc0a52a1..32514ffb135 100644 --- a/crates/hir-def/src/nameres/path_resolution.rs +++ b/crates/hir-def/src/nameres/path_resolution.rs @@ -397,14 +397,15 @@ impl DefMap { Some(_) | None => from_scope.or(from_builtin), }, }; - let from_extern_prelude = self - .extern_prelude - .get(name) - .map_or(PerNs::none(), |&it| PerNs::types(it.into(), Visibility::Public)); - let from_prelude = self.resolve_in_prelude(db, name); + let extern_prelude = || { + self.extern_prelude + .get(name) + .map_or(PerNs::none(), |&it| PerNs::types(it.into(), Visibility::Public)) + }; + let prelude = || self.resolve_in_prelude(db, name); - from_legacy_macro.or(from_scope_or_builtin).or(from_extern_prelude).or(from_prelude) + from_legacy_macro.or(from_scope_or_builtin).or_else(extern_prelude).or_else(prelude) } fn resolve_name_in_crate_root_or_extern_prelude( @@ -412,20 +413,19 @@ impl DefMap { db: &dyn DefDatabase, name: &Name, ) -> PerNs { - let arc; - let crate_def_map = match self.block { + let from_crate_root = match self.block { Some(_) => { - arc = self.crate_root(db).def_map(db); - &arc + let def_map = self.crate_root(db).def_map(db); + def_map[def_map.root].scope.get(name) } - None => self, + None => self[self.root].scope.get(name), + }; + let from_extern_prelude = || { + self.resolve_name_in_extern_prelude(db, name) + .map_or(PerNs::none(), |it| PerNs::types(it.into(), Visibility::Public)) }; - let from_crate_root = crate_def_map[crate_def_map.root].scope.get(name); - let from_extern_prelude = self - .resolve_name_in_extern_prelude(db, name) - .map_or(PerNs::none(), |it| PerNs::types(it.into(), Visibility::Public)); - from_crate_root.or(from_extern_prelude) + from_crate_root.or_else(from_extern_prelude) } fn resolve_in_prelude(&self, db: &dyn DefDatabase, name: &Name) -> PerNs { diff --git a/crates/hir-def/src/per_ns.rs b/crates/hir-def/src/per_ns.rs index bf5bf10c4ca..2bc1f8e926e 100644 --- a/crates/hir-def/src/per_ns.rs +++ b/crates/hir-def/src/per_ns.rs @@ -43,6 +43,10 @@ impl PerNs { self.types.is_none() && self.values.is_none() && self.macros.is_none() } + pub fn is_full(&self) -> bool { + self.types.is_some() && self.values.is_some() && self.macros.is_some() + } + pub fn take_types(self) -> Option { self.types.map(|it| it.0) } @@ -84,6 +88,14 @@ impl PerNs { } } + pub fn or_else(self, f: impl FnOnce() -> PerNs) -> PerNs { + if self.is_full() { + self + } else { + self.or(f()) + } + } + pub fn iter_items(self) -> impl Iterator { let _p = profile::span("PerNs::iter_items"); self.types -- cgit 1.4.1-3-g733a5 From 8c60813096fca9ce5b81bdb22894ca1e1240fdb5 Mon Sep 17 00:00:00 2001 From: Jonas Schievink Date: Mon, 15 Aug 2022 17:44:52 +0200 Subject: Fix lowering of empty macro expressions in trailing position --- crates/hir-def/src/body/lower.rs | 10 +++++++++- crates/hir-ty/src/tests/regression.rs | 17 +++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/crates/hir-def/src/body/lower.rs b/crates/hir-def/src/body/lower.rs index 66f9c24e872..f6ec8bf7e9e 100644 --- a/crates/hir-def/src/body/lower.rs +++ b/crates/hir-def/src/body/lower.rs @@ -551,9 +551,17 @@ impl ExprCollector<'_> { } } ast::Expr::MacroStmts(e) => { - let statements = e.statements().filter_map(|s| self.collect_stmt(s)).collect(); + let statements: Box<[_]> = + e.statements().filter_map(|s| self.collect_stmt(s)).collect(); let tail = e.expr().map(|e| self.collect_expr(e)); + if e.syntax().children().next().is_none() { + // HACK: make sure that macros that expand to nothing aren't treated as a `()` + // expression when used in block tail position. + cov_mark::hit!(empty_macro_in_trailing_position_is_removed); + return None; + } + self.alloc_expr(Expr::MacroStmts { tail, statements }, syntax_ptr) } ast::Expr::UnderscoreExpr(_) => self.alloc_expr(Expr::Underscore, syntax_ptr), diff --git a/crates/hir-ty/src/tests/regression.rs b/crates/hir-ty/src/tests/regression.rs index 93a88ab58ef..1b5ed0603bf 100644 --- a/crates/hir-ty/src/tests/regression.rs +++ b/crates/hir-ty/src/tests/regression.rs @@ -1648,3 +1648,20 @@ fn main() { "#]], ); } + +#[test] +fn trailing_empty_macro() { + cov_mark::check!(empty_macro_in_trailing_position_is_removed); + check_no_mismatches( + r#" +macro_rules! m2 { + ($($t:tt)*) => {$($t)*}; +} + +fn macrostmts() -> u8 { + m2! { 0 } + m2! {} +} + "#, + ); +} -- cgit 1.4.1-3-g733a5 From 91358bd937ff0fdace4371f313fa0cda42d3d81c Mon Sep 17 00:00:00 2001 From: yue4u Date: Tue, 16 Aug 2022 01:24:21 +0900 Subject: fix: format literal lookup --- crates/ide-completion/src/completions/record.rs | 10 +++++----- crates/ide-completion/src/render.rs | 4 +++- crates/ide-completion/src/render/literal.rs | 16 ++++++++++------ crates/ide-completion/src/render/pattern.rs | 18 ++++++++++++------ crates/ide-completion/src/render/union_literal.rs | 9 ++++++--- crates/ide-completion/src/render/variant.rs | 9 +++++++++ crates/ide-completion/src/tests/pattern.rs | 2 +- 7 files changed, 46 insertions(+), 22 deletions(-) diff --git a/crates/ide-completion/src/completions/record.rs b/crates/ide-completion/src/completions/record.rs index bfb98b9f277..5d96fbd30a8 100644 --- a/crates/ide-completion/src/completions/record.rs +++ b/crates/ide-completion/src/completions/record.rs @@ -129,7 +129,7 @@ mod tests { #[test] fn literal_struct_completion_edit() { check_edit( - "FooDesc {…}", + "FooDesc{}", r#" struct FooDesc { pub bar: bool } @@ -154,7 +154,7 @@ fn baz() { #[test] fn literal_struct_impl_self_completion() { check_edit( - "Self {…}", + "Self{}", r#" struct Foo { bar: u64, @@ -180,7 +180,7 @@ impl Foo { ); check_edit( - "Self(…)", + "Self()", r#" mod submod { pub struct Foo(pub u64); @@ -209,7 +209,7 @@ impl submod::Foo { #[test] fn literal_struct_completion_from_sub_modules() { check_edit( - "submod::Struct {…}", + "submod::Struct{}", r#" mod submod { pub struct Struct { @@ -238,7 +238,7 @@ fn f() -> submod::Struct { #[test] fn literal_struct_complexion_module() { check_edit( - "FooDesc {…}", + "FooDesc{}", r#" mod _69latrick { pub struct FooDesc { pub six: bool, pub neuf: Vec, pub bar: bool } diff --git a/crates/ide-completion/src/render.rs b/crates/ide-completion/src/render.rs index 693007ca307..a2cf6d68e5b 100644 --- a/crates/ide-completion/src/render.rs +++ b/crates/ide-completion/src/render.rs @@ -565,6 +565,7 @@ fn main() { Foo::Fo$0 } kind: SymbolKind( Variant, ), + lookup: "Foo{}", detail: "Foo { x: i32, y: i32 }", }, ] @@ -591,6 +592,7 @@ fn main() { Foo::Fo$0 } kind: SymbolKind( Variant, ), + lookup: "Foo()", detail: "Foo(i32, i32)", }, ] @@ -707,7 +709,7 @@ fn main() { let _: m::Spam = S$0 } kind: SymbolKind( Variant, ), - lookup: "Spam::Bar(…)", + lookup: "Spam::Bar()", detail: "m::Spam::Bar(i32)", relevance: CompletionRelevance { exact_name_match: false, diff --git a/crates/ide-completion/src/render/literal.rs b/crates/ide-completion/src/render/literal.rs index 707dea206be..af9c88a7e0a 100644 --- a/crates/ide-completion/src/render/literal.rs +++ b/crates/ide-completion/src/render/literal.rs @@ -10,8 +10,8 @@ use crate::{ render::{ compute_ref_match, compute_type_match, variant::{ - format_literal_label, render_record_lit, render_tuple_lit, visible_fields, - RenderedLiteral, + format_literal_label, format_literal_lookup, render_record_lit, render_tuple_lit, + visible_fields, RenderedLiteral, }, RenderContext, }, @@ -97,13 +97,20 @@ fn render( if !should_add_parens { kind = StructKind::Unit; } + let label = format_literal_label(&qualified_name, kind); + let lookup = if qualified { + format_literal_lookup(&short_qualified_name.to_string(), kind) + } else { + format_literal_lookup(&qualified_name, kind) + }; let mut item = CompletionItem::new( CompletionItemKind::SymbolKind(thing.symbol_kind()), ctx.source_range(), - format_literal_label(&qualified_name, kind), + label, ); + item.lookup_by(lookup); item.detail(rendered.detail); match snippet_cap { @@ -111,9 +118,6 @@ fn render( None => item.insert_text(rendered.literal), }; - if qualified { - item.lookup_by(format_literal_label(&short_qualified_name.to_string(), kind)); - } item.set_documentation(thing.docs(db)).set_deprecated(thing.is_deprecated(&ctx)); let ty = thing.ty(db); diff --git a/crates/ide-completion/src/render/pattern.rs b/crates/ide-completion/src/render/pattern.rs index 1c1299e33b6..c845ff21aab 100644 --- a/crates/ide-completion/src/render/pattern.rs +++ b/crates/ide-completion/src/render/pattern.rs @@ -8,7 +8,7 @@ use syntax::SmolStr; use crate::{ context::{ParamContext, ParamKind, PathCompletionCtx, PatternContext}, render::{ - variant::{format_literal_label, visible_fields}, + variant::{format_literal_label, format_literal_lookup, visible_fields}, RenderContext, }, CompletionItem, CompletionItemKind, @@ -34,9 +34,10 @@ pub(crate) fn render_struct_pat( let (name, escaped_name) = (name.unescaped().to_smol_str(), name.to_smol_str()); let kind = strukt.kind(ctx.db()); let label = format_literal_label(name.as_str(), kind); + let lookup = format_literal_lookup(name.as_str(), kind); let pat = render_pat(&ctx, pattern_ctx, &escaped_name, kind, &visible_fields, fields_omitted)?; - Some(build_completion(ctx, label, pat, strukt)) + Some(build_completion(ctx, label, lookup, pat, strukt)) } pub(crate) fn render_variant_pat( @@ -60,11 +61,14 @@ pub(crate) fn render_variant_pat( } }; - let (label, pat) = match path_ctx { - Some(PathCompletionCtx { has_call_parens: true, .. }) => (name, escaped_name.to_string()), + let (label, lookup, pat) = match path_ctx { + Some(PathCompletionCtx { has_call_parens: true, .. }) => { + (name.clone(), name, escaped_name.to_string()) + } _ => { let kind = variant.kind(ctx.db()); let label = format_literal_label(name.as_str(), kind); + let lookup = format_literal_lookup(name.as_str(), kind); let pat = render_pat( &ctx, pattern_ctx, @@ -73,16 +77,17 @@ pub(crate) fn render_variant_pat( &visible_fields, fields_omitted, )?; - (label, pat) + (label, lookup, pat) } }; - Some(build_completion(ctx, label, pat, variant)) + Some(build_completion(ctx, label, lookup, pat, variant)) } fn build_completion( ctx: RenderContext<'_>, label: SmolStr, + lookup: SmolStr, pat: String, def: impl HasAttrs + Copy, ) -> CompletionItem { @@ -90,6 +95,7 @@ fn build_completion( item.set_documentation(ctx.docs(def)) .set_deprecated(ctx.is_deprecated(def)) .detail(&pat) + .lookup_by(lookup) .set_relevance(ctx.completion_relevance()); match ctx.snippet_cap() { Some(snippet_cap) => item.insert_snippet(snippet_cap, pat), diff --git a/crates/ide-completion/src/render/union_literal.rs b/crates/ide-completion/src/render/union_literal.rs index bb32330f276..54e97dd57ba 100644 --- a/crates/ide-completion/src/render/union_literal.rs +++ b/crates/ide-completion/src/render/union_literal.rs @@ -6,7 +6,7 @@ use itertools::Itertools; use crate::{ render::{ - variant::{format_literal_label, visible_fields}, + variant::{format_literal_label, format_literal_lookup, visible_fields}, RenderContext, }, CompletionItem, CompletionItemKind, @@ -24,13 +24,16 @@ pub(crate) fn render_union_literal( Some(p) => (p.unescaped().to_string(), p.to_string()), None => (name.unescaped().to_string(), name.to_string()), }; - + let label = format_literal_label(&name.to_smol_str(), StructKind::Record); + let lookup = format_literal_lookup(&name.to_smol_str(), StructKind::Record); let mut item = CompletionItem::new( CompletionItemKind::SymbolKind(SymbolKind::Union), ctx.source_range(), - format_literal_label(&name.to_smol_str(), StructKind::Record), + label, ); + item.lookup_by(lookup); + let fields = un.fields(ctx.db()); let (fields, fields_omitted) = visible_fields(ctx.completion, &fields, un)?; diff --git a/crates/ide-completion/src/render/variant.rs b/crates/ide-completion/src/render/variant.rs index 664845330eb..24e6abdc9ad 100644 --- a/crates/ide-completion/src/render/variant.rs +++ b/crates/ide-completion/src/render/variant.rs @@ -94,3 +94,12 @@ pub(crate) fn format_literal_label(name: &str, kind: StructKind) -> SmolStr { StructKind::Unit => name.into(), } } + +/// Format a struct, etc. literal option for lookup used in completions filtering. +pub(crate) fn format_literal_lookup(name: &str, kind: StructKind) -> SmolStr { + match kind { + StructKind::Tuple => SmolStr::from_iter([name, "()"]), + StructKind::Record => SmolStr::from_iter([name, "{}"]), + StructKind::Unit => name.into(), + } +} diff --git a/crates/ide-completion/src/tests/pattern.rs b/crates/ide-completion/src/tests/pattern.rs index 30ddbe2dc6f..85c4dbd6625 100644 --- a/crates/ide-completion/src/tests/pattern.rs +++ b/crates/ide-completion/src/tests/pattern.rs @@ -467,7 +467,7 @@ fn foo() { fn completes_enum_variant_pat() { cov_mark::check!(enum_variant_pattern_path); check_edit( - "RecordVariant {…}", + "RecordVariant{}", r#" enum Enum { RecordVariant { field: u32 } -- cgit 1.4.1-3-g733a5 From 14db0809335f51e5f239682d63895866f4ebdb8c Mon Sep 17 00:00:00 2001 From: Aleksandr Pak Date: Tue, 16 Aug 2022 17:06:32 +0300 Subject: feat: add inline_type_alias_uses assist --- .../ide-assists/src/handlers/inline_type_alias.rs | 234 ++++++++++++++++++--- crates/ide-assists/src/lib.rs | 1 + crates/ide-assists/src/tests/generated.rs | 25 +++ 3 files changed, 227 insertions(+), 33 deletions(-) diff --git a/crates/ide-assists/src/handlers/inline_type_alias.rs b/crates/ide-assists/src/handlers/inline_type_alias.rs index 054663a06a1..6b1cf9b90dc 100644 --- a/crates/ide-assists/src/handlers/inline_type_alias.rs +++ b/crates/ide-assists/src/handlers/inline_type_alias.rs @@ -1,9 +1,9 @@ // Some ideas for future improvements: // - Support replacing aliases which are used in expressions, e.g. `A::new()`. -// - "inline_alias_to_users" assist #10881. // - Remove unused aliases if there are no longer any users, see inline_call.rs. use hir::{HasSource, PathResolution}; +use ide_db::{defs::Definition, search::FileReference}; use itertools::Itertools; use std::collections::HashMap; use syntax::{ @@ -16,6 +16,78 @@ use crate::{ AssistId, AssistKind, }; +// Assist: inline_type_alias_uses +// +// Inline a type alias into all of its uses where possible. +// +// ``` +// type $0A = i32; +// fn id(x: A) -> A { +// x +// }; +// fn foo() { +// let _: A = 3; +// } +// ``` +// -> +// ``` +// type A = i32; +// fn id(x: i32) -> i32 { +// x +// }; +// fn foo() { +// let _: i32 = 3; +// } +pub(crate) fn inline_type_alias_uses(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { + let name = ctx.find_node_at_offset::()?; + let ast_alias = name.syntax().parent().and_then(ast::TypeAlias::cast)?; + + let hir_alias = ctx.sema.to_def(&ast_alias)?; + let concrete_type = ast_alias.ty()?; + + let usages = Definition::TypeAlias(hir_alias).usages(&ctx.sema); + if !usages.at_least_one() { + return None; + } + + // until this is ok + + acc.add( + AssistId("inline_type_alias_uses", AssistKind::RefactorInline), + "Inline type alias into all uses", + name.syntax().text_range(), + |builder| { + let usages = usages.all(); + + let mut inline_refs_for_file = |file_id, refs: Vec| { + builder.edit_file(file_id); + + let path_types: Vec = refs + .into_iter() + .filter_map(|file_ref| match file_ref.name { + ast::NameLike::NameRef(path_type) => { + path_type.syntax().ancestors().find_map(ast::PathType::cast) + } + _ => None, + }) + .collect(); + + for (target, replacement) in path_types.into_iter().filter_map(|path_type| { + let replacement = inline(&ast_alias, &path_type)?.to_text(&concrete_type); + let target = path_type.syntax().text_range(); + Some((target, replacement)) + }) { + builder.replace(target, replacement); + } + }; + + for (file_id, refs) in usages.into_iter() { + inline_refs_for_file(file_id, refs); + } + }, + ) +} + // Assist: inline_type_alias // // Replace a type alias with its concrete type. @@ -36,11 +108,6 @@ use crate::{ // } // ``` pub(crate) fn inline_type_alias(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { - enum Replacement { - Generic { lifetime_map: LifetimeMap, const_and_type_map: ConstAndTypeMap }, - Plain, - } - let alias_instance = ctx.find_node_at_offset::()?; let concrete_type; let replacement; @@ -59,23 +126,7 @@ pub(crate) fn inline_type_alias(acc: &mut Assists, ctx: &AssistContext<'_>) -> O _ => { let alias = get_type_alias(&ctx, &alias_instance)?; concrete_type = alias.ty()?; - - replacement = if let Some(alias_generics) = alias.generic_param_list() { - if alias_generics.generic_params().next().is_none() { - cov_mark::hit!(no_generics_params); - return None; - } - - let instance_args = - alias_instance.syntax().descendants().find_map(ast::GenericArgList::cast); - - Replacement::Generic { - lifetime_map: LifetimeMap::new(&instance_args, &alias_generics)?, - const_and_type_map: ConstAndTypeMap::new(&instance_args, &alias_generics)?, - } - } else { - Replacement::Plain - }; + replacement = inline(&alias, &alias_instance)?; } } @@ -85,19 +136,45 @@ pub(crate) fn inline_type_alias(acc: &mut Assists, ctx: &AssistContext<'_>) -> O AssistId("inline_type_alias", AssistKind::RefactorInline), "Inline type alias", target, - |builder| { - let replacement_text = match replacement { - Replacement::Generic { lifetime_map, const_and_type_map } => { - create_replacement(&lifetime_map, &const_and_type_map, &concrete_type) - } - Replacement::Plain => concrete_type.to_string(), - }; - - builder.replace(target, replacement_text); - }, + |builder| builder.replace(target, replacement.to_text(&concrete_type)), ) } +impl Replacement { + fn to_text(&self, concrete_type: &ast::Type) -> String { + match self { + Replacement::Generic { lifetime_map, const_and_type_map } => { + create_replacement(&lifetime_map, &const_and_type_map, &concrete_type) + } + Replacement::Plain => concrete_type.to_string(), + } + } +} + +enum Replacement { + Generic { lifetime_map: LifetimeMap, const_and_type_map: ConstAndTypeMap }, + Plain, +} + +fn inline(alias_def: &ast::TypeAlias, alias_instance: &ast::PathType) -> Option { + let repl = if let Some(alias_generics) = alias_def.generic_param_list() { + if alias_generics.generic_params().next().is_none() { + cov_mark::hit!(no_generics_params); + return None; + } + let instance_args = + alias_instance.syntax().descendants().find_map(ast::GenericArgList::cast); + + Replacement::Generic { + lifetime_map: LifetimeMap::new(&instance_args, &alias_generics)?, + const_and_type_map: ConstAndTypeMap::new(&instance_args, &alias_generics)?, + } + } else { + Replacement::Plain + }; + Some(repl) +} + struct LifetimeMap(HashMap); impl LifetimeMap { @@ -835,4 +912,95 @@ trait Tr { "#, ); } + + mod inline_type_alias_uses { + use crate::{handlers::inline_type_alias::inline_type_alias_uses, tests::check_assist}; + + #[test] + fn inline_uses() { + check_assist( + inline_type_alias_uses, + r#" +type $0A = u32; + +fn foo() { + let _: A = 3; + let _: A = 4; +} +"#, + r#" +type A = u32; + +fn foo() { + let _: u32 = 3; + let _: u32 = 4; +} +"#, + ); + } + + #[test] + fn inline_uses_across_files() { + check_assist( + inline_type_alias_uses, + r#" +//- /lib.rs +mod foo; +type $0T = Vec; +fn f() -> T<&str> { + vec!["hello"] +} + +//- /foo.rs +use super::T; +fn foo() { + let _: T = Vec::new(); +} +"#, + r#" +//- /lib.rs +mod foo; +type T = Vec; +fn f() -> Vec<&str> { + vec!["hello"] +} + +//- /foo.rs +use super::T; +fn foo() { + let _: Vec = Vec::new(); +} +"#, + ); + } + + #[test] + fn inline_uses_across_files_fails() { + check_assist( + inline_type_alias_uses, + r#" +//- /lib.rs +mod foo; +type $0I = i32; + +//- /foo.rs +use super::I; +fn foo() { + let _: I = 0; +} +"#, + r#" +//- /lib.rs +mod foo; +type I = i32; + +//- /foo.rs +use super::I; +fn foo() { + let _: i32 = 0; +} +"#, + ); + } + } } diff --git a/crates/ide-assists/src/lib.rs b/crates/ide-assists/src/lib.rs index fe87aa15fcf..7fb35143fa2 100644 --- a/crates/ide-assists/src/lib.rs +++ b/crates/ide-assists/src/lib.rs @@ -243,6 +243,7 @@ mod handlers { inline_call::inline_into_callers, inline_local_variable::inline_local_variable, inline_type_alias::inline_type_alias, + inline_type_alias::inline_type_alias_uses, introduce_named_generic::introduce_named_generic, introduce_named_lifetime::introduce_named_lifetime, invert_if::invert_if, diff --git a/crates/ide-assists/src/tests/generated.rs b/crates/ide-assists/src/tests/generated.rs index 6eaab48a32b..22319f36134 100644 --- a/crates/ide-assists/src/tests/generated.rs +++ b/crates/ide-assists/src/tests/generated.rs @@ -1356,6 +1356,31 @@ fn main() { ) } +#[test] +fn doctest_inline_type_alias_uses() { + check_doc_test( + "inline_type_alias_uses", + r#####" +type $0A = i32; +fn id(x: A) -> A { + x +}; +fn foo() { + let _: A = 3; +} +"#####, + r#####" +type A = i32; +fn id(x: i32) -> i32 { + x +}; +fn foo() { + let _: i32 = 3; +} +"#####, + ) +} + #[test] fn doctest_introduce_named_generic() { check_doc_test( -- cgit 1.4.1-3-g733a5 From 0616cee92b47b539a802171f7e6817663175502d Mon Sep 17 00:00:00 2001 From: Jonas Schievink Date: Tue, 16 Aug 2022 16:51:40 +0200 Subject: Add a setting for keyword hover popups --- crates/ide/src/hover.rs | 1 + crates/ide/src/hover/render.rs | 2 +- crates/ide/src/hover/tests.rs | 42 +++++++++++++++++++++++++++++++------- crates/ide/src/static_index.rs | 7 +++++-- crates/rust-analyzer/src/config.rs | 4 ++++ docs/user/generated_config.adoc | 6 ++++++ editors/code/package.json | 5 +++++ 7 files changed, 57 insertions(+), 10 deletions(-) diff --git a/crates/ide/src/hover.rs b/crates/ide/src/hover.rs index fa2c6f09ab4..3687b597fc6 100644 --- a/crates/ide/src/hover.rs +++ b/crates/ide/src/hover.rs @@ -27,6 +27,7 @@ use crate::{ pub struct HoverConfig { pub links_in_hover: bool, pub documentation: Option, + pub keywords: bool, } impl HoverConfig { diff --git a/crates/ide/src/hover/render.rs b/crates/ide/src/hover/render.rs index 6c50a4e6adc..d52adaee535 100644 --- a/crates/ide/src/hover/render.rs +++ b/crates/ide/src/hover/render.rs @@ -230,7 +230,7 @@ pub(super) fn keyword( config: &HoverConfig, token: &SyntaxToken, ) -> Option { - if !token.kind().is_keyword() || !config.documentation.is_some() { + if !token.kind().is_keyword() || !config.documentation.is_some() || !config.keywords { return None; } let parent = token.parent()?; diff --git a/crates/ide/src/hover/tests.rs b/crates/ide/src/hover/tests.rs index c6274264b8f..685eb4521eb 100644 --- a/crates/ide/src/hover/tests.rs +++ b/crates/ide/src/hover/tests.rs @@ -8,7 +8,11 @@ fn check_hover_no_result(ra_fixture: &str) { let (analysis, position) = fixture::position(ra_fixture); let hover = analysis .hover( - &HoverConfig { links_in_hover: true, documentation: Some(HoverDocFormat::Markdown) }, + &HoverConfig { + links_in_hover: true, + documentation: Some(HoverDocFormat::Markdown), + keywords: true, + }, FileRange { file_id: position.file_id, range: TextRange::empty(position.offset) }, ) .unwrap(); @@ -20,7 +24,11 @@ fn check(ra_fixture: &str, expect: Expect) { let (analysis, position) = fixture::position(ra_fixture); let hover = analysis .hover( - &HoverConfig { links_in_hover: true, documentation: Some(HoverDocFormat::Markdown) }, + &HoverConfig { + links_in_hover: true, + documentation: Some(HoverDocFormat::Markdown), + keywords: true, + }, FileRange { file_id: position.file_id, range: TextRange::empty(position.offset) }, ) .unwrap() @@ -37,7 +45,11 @@ fn check_hover_no_links(ra_fixture: &str, expect: Expect) { let (analysis, position) = fixture::position(ra_fixture); let hover = analysis .hover( - &HoverConfig { links_in_hover: false, documentation: Some(HoverDocFormat::Markdown) }, + &HoverConfig { + links_in_hover: false, + documentation: Some(HoverDocFormat::Markdown), + keywords: true, + }, FileRange { file_id: position.file_id, range: TextRange::empty(position.offset) }, ) .unwrap() @@ -54,7 +66,11 @@ fn check_hover_no_markdown(ra_fixture: &str, expect: Expect) { let (analysis, position) = fixture::position(ra_fixture); let hover = analysis .hover( - &HoverConfig { links_in_hover: true, documentation: Some(HoverDocFormat::PlainText) }, + &HoverConfig { + links_in_hover: true, + documentation: Some(HoverDocFormat::PlainText), + keywords: true, + }, FileRange { file_id: position.file_id, range: TextRange::empty(position.offset) }, ) .unwrap() @@ -71,7 +87,11 @@ fn check_actions(ra_fixture: &str, expect: Expect) { let (analysis, file_id, position) = fixture::range_or_position(ra_fixture); let hover = analysis .hover( - &HoverConfig { links_in_hover: true, documentation: Some(HoverDocFormat::Markdown) }, + &HoverConfig { + links_in_hover: true, + documentation: Some(HoverDocFormat::Markdown), + keywords: true, + }, FileRange { file_id, range: position.range_or_empty() }, ) .unwrap() @@ -83,7 +103,11 @@ fn check_hover_range(ra_fixture: &str, expect: Expect) { let (analysis, range) = fixture::range(ra_fixture); let hover = analysis .hover( - &HoverConfig { links_in_hover: false, documentation: Some(HoverDocFormat::Markdown) }, + &HoverConfig { + links_in_hover: false, + documentation: Some(HoverDocFormat::Markdown), + keywords: true, + }, range, ) .unwrap() @@ -95,7 +119,11 @@ fn check_hover_range_no_results(ra_fixture: &str) { let (analysis, range) = fixture::range(ra_fixture); let hover = analysis .hover( - &HoverConfig { links_in_hover: false, documentation: Some(HoverDocFormat::Markdown) }, + &HoverConfig { + links_in_hover: false, + documentation: Some(HoverDocFormat::Markdown), + keywords: true, + }, range, ) .unwrap(); diff --git a/crates/ide/src/static_index.rs b/crates/ide/src/static_index.rs index cc79ee55b7d..9e5eb909508 100644 --- a/crates/ide/src/static_index.rs +++ b/crates/ide/src/static_index.rs @@ -130,8 +130,11 @@ impl StaticIndex<'_> { syntax::NodeOrToken::Node(_) => None, syntax::NodeOrToken::Token(x) => Some(x), }); - let hover_config = - HoverConfig { links_in_hover: true, documentation: Some(HoverDocFormat::Markdown) }; + let hover_config = HoverConfig { + links_in_hover: true, + documentation: Some(HoverDocFormat::Markdown), + keywords: true, + }; let tokens = tokens.filter(|token| { matches!( token.kind(), diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs index 1629c1dd328..f2fae586024 100644 --- a/crates/rust-analyzer/src/config.rs +++ b/crates/rust-analyzer/src/config.rs @@ -244,6 +244,9 @@ config_data! { /// Whether to show documentation on hover. hover_documentation_enable: bool = "true", + /// Whether to show keyword hover popups. Only applies when + /// `#rust-analyzer.hover.documentation.enable#` is set. + hover_documentation_keywords: bool = "true", /// Use markdown syntax for links in hover. hover_links_enable: bool = "true", @@ -1187,6 +1190,7 @@ impl Config { HoverDocFormat::PlainText } }), + keywords: self.data.hover_documentation_keywords, } } diff --git a/docs/user/generated_config.adoc b/docs/user/generated_config.adoc index b0f2f1614db..00a6e3c43c5 100644 --- a/docs/user/generated_config.adoc +++ b/docs/user/generated_config.adoc @@ -318,6 +318,12 @@ Whether to show `Run` action. Only applies when -- Whether to show documentation on hover. -- +[[rust-analyzer.hover.documentation.keywords]]rust-analyzer.hover.documentation.keywords (default: `true`):: ++ +-- +Whether to show keyword hover popups. Only applies when +`#rust-analyzer.hover.documentation.enable#` is set. +-- [[rust-analyzer.hover.links.enable]]rust-analyzer.hover.links.enable (default: `true`):: + -- diff --git a/editors/code/package.json b/editors/code/package.json index fbdc69c8013..6352e968110 100644 --- a/editors/code/package.json +++ b/editors/code/package.json @@ -756,6 +756,11 @@ "default": true, "type": "boolean" }, + "rust-analyzer.hover.documentation.keywords": { + "markdownDescription": "Whether to show keyword hover popups. Only applies when\n`#rust-analyzer.hover.documentation.enable#` is set.", + "default": true, + "type": "boolean" + }, "rust-analyzer.hover.links.enable": { "markdownDescription": "Use markdown syntax for links in hover.", "default": true, -- cgit 1.4.1-3-g733a5 From 6d6356b103225522166496975f98b15d9827b6c8 Mon Sep 17 00:00:00 2001 From: Aleksandr Pak Date: Tue, 16 Aug 2022 18:29:22 +0300 Subject: fixup! feat: add inline_type_alias_uses assist --- crates/ide-assists/src/handlers/inline_type_alias.rs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/crates/ide-assists/src/handlers/inline_type_alias.rs b/crates/ide-assists/src/handlers/inline_type_alias.rs index 6b1cf9b90dc..c4e5bc9c7cb 100644 --- a/crates/ide-assists/src/handlers/inline_type_alias.rs +++ b/crates/ide-assists/src/handlers/inline_type_alias.rs @@ -975,7 +975,7 @@ fn foo() { } #[test] - fn inline_uses_across_files_fails() { + fn inline_uses_across_files_2() { check_assist( inline_type_alias_uses, r#" @@ -990,11 +990,6 @@ fn foo() { } "#, r#" -//- /lib.rs -mod foo; -type I = i32; - -//- /foo.rs use super::I; fn foo() { let _: i32 = 0; -- cgit 1.4.1-3-g733a5 From 7fb7c248c7b37699da7eb5944f45a9b59121b827 Mon Sep 17 00:00:00 2001 From: Jonas Schievink Date: Tue, 16 Aug 2022 18:12:15 +0200 Subject: Add `.enable` suffix --- crates/rust-analyzer/src/config.rs | 6 +++--- docs/user/generated_config.adoc | 2 +- editors/code/package.json | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs index f2fae586024..e63cfb163ca 100644 --- a/crates/rust-analyzer/src/config.rs +++ b/crates/rust-analyzer/src/config.rs @@ -243,10 +243,10 @@ config_data! { hover_actions_run_enable: bool = "true", /// Whether to show documentation on hover. - hover_documentation_enable: bool = "true", + hover_documentation_enable: bool = "true", /// Whether to show keyword hover popups. Only applies when /// `#rust-analyzer.hover.documentation.enable#` is set. - hover_documentation_keywords: bool = "true", + hover_documentation_keywords_enable: bool = "true", /// Use markdown syntax for links in hover. hover_links_enable: bool = "true", @@ -1190,7 +1190,7 @@ impl Config { HoverDocFormat::PlainText } }), - keywords: self.data.hover_documentation_keywords, + keywords: self.data.hover_documentation_keywords_enable, } } diff --git a/docs/user/generated_config.adoc b/docs/user/generated_config.adoc index 00a6e3c43c5..e3dbeec1fbd 100644 --- a/docs/user/generated_config.adoc +++ b/docs/user/generated_config.adoc @@ -318,7 +318,7 @@ Whether to show `Run` action. Only applies when -- Whether to show documentation on hover. -- -[[rust-analyzer.hover.documentation.keywords]]rust-analyzer.hover.documentation.keywords (default: `true`):: +[[rust-analyzer.hover.documentation.keywords.enable]]rust-analyzer.hover.documentation.keywords.enable (default: `true`):: + -- Whether to show keyword hover popups. Only applies when diff --git a/editors/code/package.json b/editors/code/package.json index 6352e968110..0714b356fe0 100644 --- a/editors/code/package.json +++ b/editors/code/package.json @@ -756,7 +756,7 @@ "default": true, "type": "boolean" }, - "rust-analyzer.hover.documentation.keywords": { + "rust-analyzer.hover.documentation.keywords.enable": { "markdownDescription": "Whether to show keyword hover popups. Only applies when\n`#rust-analyzer.hover.documentation.enable#` is set.", "default": true, "type": "boolean" -- cgit 1.4.1-3-g733a5 From 1f73cbe8390d4a8ebe4e4039891567d8358c2c70 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Tue, 16 Aug 2022 19:13:10 +0200 Subject: Revert #12947, trigger workspace switches on all structure changes again --- crates/rust-analyzer/src/global_state.rs | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/crates/rust-analyzer/src/global_state.rs b/crates/rust-analyzer/src/global_state.rs index b5f6aef2e1a..c55bbbbe6ef 100644 --- a/crates/rust-analyzer/src/global_state.rs +++ b/crates/rust-analyzer/src/global_state.rs @@ -8,7 +8,7 @@ use std::{sync::Arc, time::Instant}; use crossbeam_channel::{unbounded, Receiver, Sender}; use flycheck::FlycheckHandle; use ide::{Analysis, AnalysisHost, Cancellable, Change, FileId}; -use ide_db::base_db::{CrateId, FileLoader, SourceDatabase, SourceDatabaseExt}; +use ide_db::base_db::{CrateId, FileLoader, SourceDatabase}; use lsp_types::{SemanticTokens, Url}; use parking_lot::{Mutex, RwLock}; use proc_macro_api::ProcMacroServer; @@ -176,9 +176,9 @@ impl GlobalState { pub(crate) fn process_changes(&mut self) -> bool { let _p = profile::span("GlobalState::process_changes"); - let mut fs_refresh_changes = Vec::new(); // A file was added or deleted let mut has_structure_changes = false; + let mut workspace_structure_change = None; let (change, changed_files) = { let mut change = Change::new(); @@ -192,7 +192,7 @@ impl GlobalState { if let Some(path) = vfs.file_path(file.file_id).as_path() { let path = path.to_path_buf(); if reload::should_refresh_for_change(&path, file.change_kind) { - fs_refresh_changes.push((path, file.file_id)); + workspace_structure_change = Some(path); } if file.is_created_or_deleted() { has_structure_changes = true; @@ -227,11 +227,10 @@ impl GlobalState { { let raw_database = self.analysis_host.raw_database(); - let workspace_structure_change = - fs_refresh_changes.into_iter().find(|&(_, file_id)| { - !raw_database.source_root(raw_database.file_source_root(file_id)).is_library - }); - if let Some((path, _)) = workspace_structure_change { + // FIXME: ideally we should only trigger a workspace fetch for non-library changes + // but somethings going wrong with the source root business when we add a new local + // crate see https://github.com/rust-lang/rust-analyzer/issues/13029 + if let Some(path) = workspace_structure_change { self.fetch_workspaces_queue .request_op(format!("workspace vfs file change: {}", path.display())); } -- cgit 1.4.1-3-g733a5 From a0b257c9d91d00c054376facc6681f563f1a5e1b Mon Sep 17 00:00:00 2001 From: David Barsky Date: Tue, 16 Aug 2022 13:38:50 -0400 Subject: chore: remove unused `currentExtensionIsNightly()` in config.ts --- editors/code/src/config.ts | 6 ------ 1 file changed, 6 deletions(-) diff --git a/editors/code/src/config.ts b/editors/code/src/config.ts index 1c58040d58c..b83582a344a 100644 --- a/editors/code/src/config.ts +++ b/editors/code/src/config.ts @@ -5,8 +5,6 @@ import { log } from "./util"; export type UpdatesChannel = "stable" | "nightly"; -const NIGHTLY_TAG = "nightly"; - export type RunnableEnvCfg = | undefined | Record @@ -175,10 +173,6 @@ export class Config { gotoTypeDef: this.get("hover.actions.gotoTypeDef.enable"), }; } - - get currentExtensionIsNightly() { - return this.package.releaseTag === NIGHTLY_TAG; - } } export async function updateConfig(config: vscode.WorkspaceConfiguration) { -- cgit 1.4.1-3-g733a5 From 39d17efde7d2da436f349c1b0ca55c37bae0254f Mon Sep 17 00:00:00 2001 From: Dorian Scheidt Date: Sat, 13 Aug 2022 13:29:41 -0500 Subject: feat: Generate static method using Self::assoc() syntax This change improves the `generate_function` assist to support generating static methods/associated functions using the `Self::assoc()` syntax. Previously, one could generate a static method, but only when specifying the type name directly (like `Foo::assoc()`). After this change, `Self` is supported as well as the type name. Fixes #13012 --- .../ide-assists/src/handlers/generate_function.rs | 64 ++++++++++++++++++---- 1 file changed, 53 insertions(+), 11 deletions(-) diff --git a/crates/ide-assists/src/handlers/generate_function.rs b/crates/ide-assists/src/handlers/generate_function.rs index d564a054089..49345cb983f 100644 --- a/crates/ide-assists/src/handlers/generate_function.rs +++ b/crates/ide-assists/src/handlers/generate_function.rs @@ -61,7 +61,7 @@ fn gen_fn(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { } let fn_name = &*name_ref.text(); - let target_module; + let mut target_module = None; let mut adt_name = None; let (target, file, insert_offset) = match path.qualifier() { @@ -78,16 +78,11 @@ fn gen_fn(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { } } - let current_module = ctx.sema.scope(call.syntax())?.module(); - let module = adt.module(ctx.sema.db); - target_module = if current_module == module { None } else { Some(module) }; - if current_module.krate() != module.krate() { - return None; - } - let (impl_, file) = get_adt_source(ctx, &adt, fn_name)?; - let (target, insert_offset) = get_method_target(ctx, &module, &impl_)?; - adt_name = if impl_.is_none() { Some(adt.name(ctx.sema.db)) } else { None }; - (target, file, insert_offset) + static_method_target(ctx, &call, adt, &mut target_module, fn_name, &mut adt_name)? + } + Some(hir::PathResolution::SelfType(impl_)) => { + let adt = impl_.self_ty(ctx.db()).as_adt()?; + static_method_target(ctx, &call, adt, &mut target_module, fn_name, &mut adt_name)? } _ => { return None; @@ -399,6 +394,26 @@ fn get_method_target( Some((target.clone(), get_insert_offset(&target))) } +fn static_method_target( + ctx: &AssistContext<'_>, + call: &CallExpr, + adt: hir::Adt, + target_module: &mut Option, + fn_name: &str, + adt_name: &mut Option, +) -> Option<(GeneratedFunctionTarget, FileId, TextSize)> { + let current_module = ctx.sema.scope(call.syntax())?.module(); + let module = adt.module(ctx.sema.db); + *target_module = if current_module == module { None } else { Some(module) }; + if current_module.krate() != module.krate() { + return None; + } + let (impl_, file) = get_adt_source(ctx, &adt, fn_name)?; + let (target, insert_offset) = get_method_target(ctx, &module, &impl_)?; + *adt_name = if impl_.is_none() { Some(adt.name(ctx.sema.db)) } else { None }; + Some((target, file, insert_offset)) +} + fn get_insert_offset(target: &GeneratedFunctionTarget) -> TextSize { match &target { GeneratedFunctionTarget::BehindItem(it) => it.text_range().end(), @@ -1633,6 +1648,33 @@ fn bar() ${0:-> _} { ) } + #[test] + fn create_static_method_within_an_impl_with_self_syntax() { + check_assist( + generate_function, + r" +struct S; +impl S { + fn foo(&self) { + Self::bar$0(); + } +} +", + r" +struct S; +impl S { + fn foo(&self) { + Self::bar(); + } + + fn bar() ${0:-> _} { + todo!() + } +} +", + ) + } + #[test] fn no_panic_on_invalid_global_path() { check_assist( -- cgit 1.4.1-3-g733a5 From cebf95718c32b1024a6845992bee96c50f830f3a Mon Sep 17 00:00:00 2001 From: Justin Ridgewell Date: Tue, 16 Aug 2022 17:53:10 -0400 Subject: Find IntoFuture::IntoFuture's poll method --- crates/hir/src/lib.rs | 5 ++-- crates/hir/src/source_analyzer.rs | 34 ++++++++++++++++++++++------ crates/ide-completion/src/completions/dot.rs | 2 +- crates/ide/src/goto_definition.rs | 34 ++++++++++++++++++++++++++++ 4 files changed, 65 insertions(+), 10 deletions(-) diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index 7f16634afe1..46d24b9f146 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -2777,9 +2777,10 @@ impl Type { self.ty.is_unknown() } - /// Checks that particular type `ty` implements `std::future::Future`. + /// Checks that particular type `ty` implements `std::future::IntoFuture` or + /// `std::future::Future`. /// This function is used in `.await` syntax completion. - pub fn impls_future(&self, db: &dyn HirDatabase) -> bool { + pub fn impls_into_future(&self, db: &dyn HirDatabase) -> bool { let trait_ = db .lang_item(self.env.krate, SmolStr::new_inline("into_future")) .and_then(|it| { diff --git a/crates/hir/src/source_analyzer.rs b/crates/hir/src/source_analyzer.rs index 756772cf84e..9418afa91da 100644 --- a/crates/hir/src/source_analyzer.rs +++ b/crates/hir/src/source_analyzer.rs @@ -27,6 +27,7 @@ use hir_def::{ use hir_expand::{ builtin_fn_macro::BuiltinFnLikeExpander, hygiene::Hygiene, + mod_path::path, name, name::{AsName, Name}, HirFileId, InFile, @@ -269,16 +270,35 @@ impl SourceAnalyzer { db: &dyn HirDatabase, await_expr: &ast::AwaitExpr, ) -> Option { - // FIXME This should be pointing to the poll of IntoFuture::Output's Future impl, but I - // don't know how to resolve the Output type so that we can query for its poll method. - let ty = self.ty_of_expr(db, &await_expr.expr()?.into())?; + let mut ty = self.ty_of_expr(db, &await_expr.expr()?.into())?.clone(); + + let into_future_trait = self + .resolver + .resolve_known_trait(db.upcast(), &path![core::future::IntoFuture]) + .map(Trait::from); + + if let Some(into_future_trait) = into_future_trait { + let type_ = Type::new_with_resolver(db, &self.resolver, ty.clone()); + if type_.impls_trait(db, into_future_trait, &[]) { + let items = into_future_trait.items(db); + let into_future_type = items.into_iter().find_map(|item| match item { + AssocItem::TypeAlias(alias) + if alias.name(db) == hir_expand::name![IntoFuture] => + { + Some(alias) + } + _ => None, + })?; + let future_trait = type_.normalize_trait_assoc_type(db, &[], into_future_type)?; + ty = future_trait.ty; + } + } - let op_fn = db + let poll_fn = db .lang_item(self.resolver.krate(), hir_expand::name![poll].to_smol_str())? .as_function()?; - let substs = hir_ty::TyBuilder::subst_for_def(db, op_fn).push(ty.clone()).build(); - - Some(self.resolve_impl_method_or_trait_def(db, op_fn, &substs)) + let substs = hir_ty::TyBuilder::subst_for_def(db, poll_fn).push(ty.clone()).build(); + Some(self.resolve_impl_method_or_trait_def(db, poll_fn, &substs)) } pub(crate) fn resolve_prefix_expr( diff --git a/crates/ide-completion/src/completions/dot.rs b/crates/ide-completion/src/completions/dot.rs index cf40ca489c0..02004ff7b68 100644 --- a/crates/ide-completion/src/completions/dot.rs +++ b/crates/ide-completion/src/completions/dot.rs @@ -19,7 +19,7 @@ pub(crate) fn complete_dot( }; // Suggest .await syntax for types that implement Future trait - if receiver_ty.impls_future(ctx.db) { + if receiver_ty.impls_into_future(ctx.db) { let mut item = CompletionItem::new(CompletionItemKind::Keyword, ctx.source_range(), "await"); item.detail("expr.await"); diff --git a/crates/ide/src/goto_definition.rs b/crates/ide/src/goto_definition.rs index b2123b9a879..c7a922e6cc6 100644 --- a/crates/ide/src/goto_definition.rs +++ b/crates/ide/src/goto_definition.rs @@ -1664,6 +1664,40 @@ fn f() { ); } + #[test] + fn goto_await_into_future_poll() { + check( + r#" +//- minicore: future + +struct Futurable; + +impl core::future::IntoFuture for Futurable { + type IntoFuture = MyFut; +} + +struct MyFut; + +impl core::future::Future for MyFut { + type Output = (); + + fn poll( + //^^^^ + self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_> + ) -> std::task::Poll + { + () + } +} + +fn f() { + Futurable.await$0; +} +"#, + ); + } + #[test] fn goto_try_op() { check( -- cgit 1.4.1-3-g733a5 From 557c5b4dc57cb0fd5455bec35f2a97a4626e3df1 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Wed, 17 Aug 2022 09:32:25 +0200 Subject: minor: Change tracing event level in apply_change --- crates/ide-db/src/apply_change.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/ide-db/src/apply_change.rs b/crates/ide-db/src/apply_change.rs index f8134c552f7..b1ee9b58d5b 100644 --- a/crates/ide-db/src/apply_change.rs +++ b/crates/ide-db/src/apply_change.rs @@ -20,7 +20,7 @@ impl RootDatabase { pub fn apply_change(&mut self, change: Change) { let _p = profile::span("RootDatabase::apply_change"); self.request_cancellation(); - tracing::info!("apply_change {:?}", change); + tracing::trace!("apply_change {:?}", change); if let Some(roots) = &change.roots { let mut local_roots = FxHashSet::default(); let mut library_roots = FxHashSet::default(); -- cgit 1.4.1-3-g733a5 From a3409c3a83fb1d8139bac8b3e438f56f2ac633a3 Mon Sep 17 00:00:00 2001 From: Ryo Yoshida Date: Tue, 16 Aug 2022 10:08:45 +0900 Subject: fix: escape keywords used as names in earlier editions --- crates/hir-expand/src/name.rs | 10 ++++++++-- crates/ide-db/src/search.rs | 4 +++- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/crates/hir-expand/src/name.rs b/crates/hir-expand/src/name.rs index 87c663eec8e..a1bb353374b 100644 --- a/crates/hir-expand/src/name.rs +++ b/crates/hir-expand/src/name.rs @@ -90,10 +90,16 @@ impl Name { /// Resolve a name from the text of token. fn resolve(raw_text: &str) -> Name { - // When `raw_text` starts with "r#" but the name does not coincide with any - // keyword, we never need the prefix so we strip it. match raw_text.strip_prefix("r#") { + // When `raw_text` starts with "r#" but the name does not coincide with any + // keyword, we never need the prefix so we strip it. Some(text) if !is_raw_identifier(text) => Name::new_text(SmolStr::new(text)), + // Keywords (in the current edition) *can* be used as a name in earlier editions of + // Rust, e.g. "try" in Rust 2015. Even in such cases, we keep track of them in their + // escaped form. + None if is_raw_identifier(raw_text) => { + Name::new_text(SmolStr::from_iter(["r#", raw_text])) + } _ => Name::new_text(raw_text.into()), } } diff --git a/crates/ide-db/src/search.rs b/crates/ide-db/src/search.rs index 9eaabeec7a4..2f4aa113170 100644 --- a/crates/ide-db/src/search.rs +++ b/crates/ide-db/src/search.rs @@ -402,7 +402,9 @@ impl<'a> FindUsages<'a> { .or_else(|| ty.as_builtin().map(|builtin| builtin.name())) }) }; - self.def.name(sema.db).or_else(self_kw_refs).map(|it| it.to_smol_str()) + // We need to unescape the name in case it is written without "r#" in earlier + // editions of Rust where it isn't a keyword. + self.def.name(sema.db).or_else(self_kw_refs).map(|it| it.unescaped().to_smol_str()) } }; let name = match &name { -- cgit 1.4.1-3-g733a5 From 313b004ef76d3352f36e5128b16ca5212de97d49 Mon Sep 17 00:00:00 2001 From: Aleksandr Pak Date: Wed, 17 Aug 2022 12:50:33 +0300 Subject: fixup! feat: add inline_type_alias_uses assist --- crates/ide-assists/src/handlers/inline_type_alias.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/ide-assists/src/handlers/inline_type_alias.rs b/crates/ide-assists/src/handlers/inline_type_alias.rs index c4e5bc9c7cb..9adf6381c1c 100644 --- a/crates/ide-assists/src/handlers/inline_type_alias.rs +++ b/crates/ide-assists/src/handlers/inline_type_alias.rs @@ -66,7 +66,7 @@ pub(crate) fn inline_type_alias_uses(acc: &mut Assists, ctx: &AssistContext<'_>) .into_iter() .filter_map(|file_ref| match file_ref.name { ast::NameLike::NameRef(path_type) => { - path_type.syntax().ancestors().find_map(ast::PathType::cast) + path_type.syntax().ancestors().nth(3).and_then(ast::PathType::cast) } _ => None, }) -- cgit 1.4.1-3-g733a5 From 23747419ca52414d3ecf6f69f1e530e47ab1e937 Mon Sep 17 00:00:00 2001 From: Dezhi Wu Date: Wed, 17 Aug 2022 21:44:58 +0800 Subject: fix: a bunch of typos This PR will fix some typos detected by [typos]. There are also some other typos in the function names, variable names, and file names, which I leave as they are. I'm more certain that typos in comments should be fixed. [typos]: https://github.com/crate-ci/typos --- crates/flycheck/src/lib.rs | 2 +- crates/hir-def/src/nameres/proc_macro.rs | 2 +- crates/hir-expand/src/lib.rs | 2 +- crates/ide-assists/src/handlers/extract_module.rs | 6 +++--- .../src/handlers/replace_turbofish_with_explicit_type.rs | 2 +- crates/ide-assists/src/utils/suggest_name.rs | 4 ++-- crates/ide-completion/src/completions/postfix/format_like.rs | 4 ++-- crates/ide-completion/src/tests/flyimport.rs | 2 +- crates/ide-db/src/rename.rs | 2 +- crates/ide-diagnostics/src/handlers/missing_match_arms.rs | 2 +- crates/mbe/src/expander/matcher.rs | 2 +- crates/project-model/src/lib.rs | 2 +- crates/project-model/src/workspace.rs | 2 +- crates/rust-analyzer/src/bin/logger.rs | 2 +- crates/syntax/src/hacks.rs | 2 +- crates/vfs/src/lib.rs | 2 +- docs/dev/architecture.md | 2 +- editors/code/src/commands.ts | 2 +- editors/code/src/toolchain.ts | 2 +- 19 files changed, 23 insertions(+), 23 deletions(-) diff --git a/crates/flycheck/src/lib.rs b/crates/flycheck/src/lib.rs index 3347940ec6d..2bebea2fbfe 100644 --- a/crates/flycheck/src/lib.rs +++ b/crates/flycheck/src/lib.rs @@ -345,7 +345,7 @@ impl CargoActor { // // Because cargo only outputs one JSON object per line, we can // simply skip a line if it doesn't parse, which just ignores any - // erroneus output. + // erroneous output. let mut error = String::new(); let mut read_at_least_one_message = false; diff --git a/crates/hir-def/src/nameres/proc_macro.rs b/crates/hir-def/src/nameres/proc_macro.rs index 5089ef2d817..52b79cd0fdd 100644 --- a/crates/hir-def/src/nameres/proc_macro.rs +++ b/crates/hir-def/src/nameres/proc_macro.rs @@ -45,7 +45,7 @@ impl Attrs { kind: ProcMacroKind::CustomDerive { helpers: Box::new([]) }, }), - // `#[proc_macro_derive(Trait, attibutes(helper1, helper2, ...))]` + // `#[proc_macro_derive(Trait, attributes(helper1, helper2, ...))]` [ TokenTree::Leaf(Leaf::Ident(trait_name)), TokenTree::Leaf(Leaf::Punct(comma)), diff --git a/crates/hir-expand/src/lib.rs b/crates/hir-expand/src/lib.rs index f6b97fd5405..d753d88470c 100644 --- a/crates/hir-expand/src/lib.rs +++ b/crates/hir-expand/src/lib.rs @@ -616,7 +616,7 @@ impl ExpansionInfo { let token_id = match token_id_in_attr_input { Some(token_id) => token_id, - // the token is not inside an attribute's input so do the lookup in the macro_arg as ususal + // the token is not inside an attribute's input so do the lookup in the macro_arg as usual None => { let relative_range = token.value.text_range().checked_sub(self.arg.value.text_range().start())?; diff --git a/crates/ide-assists/src/handlers/extract_module.rs b/crates/ide-assists/src/handlers/extract_module.rs index b3c4d306ac1..897980c6650 100644 --- a/crates/ide-assists/src/handlers/extract_module.rs +++ b/crates/ide-assists/src/handlers/extract_module.rs @@ -29,7 +29,7 @@ use super::remove_unused_param::range_to_remove; // Assist: extract_module // -// Extracts a selected region as seperate module. All the references, visibility and imports are +// Extracts a selected region as separate module. All the references, visibility and imports are // resolved. // // ``` @@ -105,7 +105,7 @@ pub(crate) fn extract_module(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti // //- Thirdly, resolving all the imports this includes removing paths from imports // outside the module, shifting/cloning them inside new module, or shifting the imports, or making - // new import statemnts + // new import statements //We are getting item usages and record_fields together, record_fields //for change_visibility and usages for first point mentioned above in the process @@ -661,7 +661,7 @@ fn check_intersection_and_push( import_path: TextRange, ) { if import_paths_to_be_removed.len() > 0 { - // Text ranges recieved here for imports are extended to the + // Text ranges received here for imports are extended to the // next/previous comma which can cause intersections among them // and later deletion of these can cause panics similar // to reported in #11766. So to mitigate it, we diff --git a/crates/ide-assists/src/handlers/replace_turbofish_with_explicit_type.rs b/crates/ide-assists/src/handlers/replace_turbofish_with_explicit_type.rs index 6112e09455a..5242f3b5100 100644 --- a/crates/ide-assists/src/handlers/replace_turbofish_with_explicit_type.rs +++ b/crates/ide-assists/src/handlers/replace_turbofish_with_explicit_type.rs @@ -88,7 +88,7 @@ pub(crate) fn replace_turbofish_with_explicit_type( }, ); } else if let Some(InferType(t)) = let_stmt.ty() { - // If there's a type inferrence underscore, we can offer to replace it with the type in + // If there's a type inference underscore, we can offer to replace it with the type in // the turbofish. // let x: _ = fn::<...>(); let underscore_range = t.syntax().text_range(); diff --git a/crates/ide-assists/src/utils/suggest_name.rs b/crates/ide-assists/src/utils/suggest_name.rs index 779cdbc93c5..662ddd063f3 100644 --- a/crates/ide-assists/src/utils/suggest_name.rs +++ b/crates/ide-assists/src/utils/suggest_name.rs @@ -75,7 +75,7 @@ pub(crate) fn for_generic_parameter(ty: &ast::ImplTraitType) -> SmolStr { /// In current implementation, the function tries to get the name from /// the following sources: /// -/// * if expr is an argument to function/method, use paramter name +/// * if expr is an argument to function/method, use parameter name /// * if expr is a function/method call, use function name /// * expression type name if it exists (E.g. `()`, `fn() -> ()` or `!` do not have names) /// * fallback: `var_name` @@ -85,7 +85,7 @@ pub(crate) fn for_generic_parameter(ty: &ast::ImplTraitType) -> SmolStr { /// Currently it sticks to the first name found. // FIXME: Microoptimize and return a `SmolStr` here. pub(crate) fn for_variable(expr: &ast::Expr, sema: &Semantics<'_, RootDatabase>) -> String { - // `from_param` does not benifit from stripping + // `from_param` does not benefit from stripping // it need the largest context possible // so we check firstmost if let Some(name) = from_param(expr, sema) { diff --git a/crates/ide-completion/src/completions/postfix/format_like.rs b/crates/ide-completion/src/completions/postfix/format_like.rs index 6b94347e0ad..b273a4cb53b 100644 --- a/crates/ide-completion/src/completions/postfix/format_like.rs +++ b/crates/ide-completion/src/completions/postfix/format_like.rs @@ -173,7 +173,7 @@ impl FormatStrParser { } } (State::Expr, ':') if chars.peek().copied() == Some(':') => { - // path seperator + // path separator current_expr.push_str("::"); chars.next(); } @@ -185,7 +185,7 @@ impl FormatStrParser { current_expr = String::new(); self.state = State::FormatOpts; } else { - // We're inside of braced expression, assume that it's a struct field name/value delimeter. + // We're inside of braced expression, assume that it's a struct field name/value delimiter. current_expr.push(chr); } } diff --git a/crates/ide-completion/src/tests/flyimport.rs b/crates/ide-completion/src/tests/flyimport.rs index 0bba7f2459c..a63ef006875 100644 --- a/crates/ide-completion/src/tests/flyimport.rs +++ b/crates/ide-completion/src/tests/flyimport.rs @@ -159,7 +159,7 @@ pub mod some_module { pub struct ThiiiiiirdStruct; // contains all letters from the query, but not in the beginning, displayed second pub struct AfterThirdStruct; - // contains all letters from the query in the begginning, displayed first + // contains all letters from the query in the beginning, displayed first pub struct ThirdStruct; } diff --git a/crates/ide-db/src/rename.rs b/crates/ide-db/src/rename.rs index 517fe3f246d..49b81265ea5 100644 --- a/crates/ide-db/src/rename.rs +++ b/crates/ide-db/src/rename.rs @@ -82,7 +82,7 @@ impl Definition { } /// Textual range of the identifier which will change when renaming this - /// `Definition`. Note that some definitions, like buitin types, can't be + /// `Definition`. Note that some definitions, like builtin types, can't be /// renamed. pub fn range_for_rename(self, sema: &Semantics<'_, RootDatabase>) -> Option { let res = match self { diff --git a/crates/ide-diagnostics/src/handlers/missing_match_arms.rs b/crates/ide-diagnostics/src/handlers/missing_match_arms.rs index 9e66fbfb752..5fcaf405b14 100644 --- a/crates/ide-diagnostics/src/handlers/missing_match_arms.rs +++ b/crates/ide-diagnostics/src/handlers/missing_match_arms.rs @@ -750,7 +750,7 @@ fn main() { enum Foo { A } fn main() { // FIXME: this should not bail out but current behavior is such as the old algorithm. - // ExprValidator::validate_match(..) checks types of top level patterns incorrecly. + // ExprValidator::validate_match(..) checks types of top level patterns incorrectly. match Foo::A { ref _x => {} Foo::A => {} diff --git a/crates/mbe/src/expander/matcher.rs b/crates/mbe/src/expander/matcher.rs index 5020e9abaf3..c1aa14d6b7e 100644 --- a/crates/mbe/src/expander/matcher.rs +++ b/crates/mbe/src/expander/matcher.rs @@ -321,7 +321,7 @@ struct MatchState<'t> { /// The KleeneOp of this sequence if we are in a repetition. sep_kind: Option, - /// Number of tokens of seperator parsed + /// Number of tokens of separator parsed sep_parsed: Option, /// Matched meta variables bindings diff --git a/crates/project-model/src/lib.rs b/crates/project-model/src/lib.rs index e3f83084ac8..b81b7432f65 100644 --- a/crates/project-model/src/lib.rs +++ b/crates/project-model/src/lib.rs @@ -3,7 +3,7 @@ //! //! Pure model is represented by the [`base_db::CrateGraph`] from another crate. //! -//! In this crate, we are conserned with "real world" project models. +//! In this crate, we are concerned with "real world" project models. //! //! Specifically, here we have a representation for a Cargo project //! ([`CargoWorkspace`]) and for manually specified layout ([`ProjectJson`]). diff --git a/crates/project-model/src/workspace.rs b/crates/project-model/src/workspace.rs index daabb299f76..8d6f50f5587 100644 --- a/crates/project-model/src/workspace.rs +++ b/crates/project-model/src/workspace.rs @@ -770,7 +770,7 @@ fn handle_rustc_crates( queue.push_back(root_pkg); while let Some(pkg) = queue.pop_front() { // Don't duplicate packages if they are dependended on a diamond pattern - // N.B. if this line is ommitted, we try to analyse over 4_800_000 crates + // N.B. if this line is omitted, we try to analyse over 4_800_000 crates // which is not ideal if rustc_pkg_crates.contains_key(&pkg) { continue; diff --git a/crates/rust-analyzer/src/bin/logger.rs b/crates/rust-analyzer/src/bin/logger.rs index 0b69f75bc0d..298814af5a4 100644 --- a/crates/rust-analyzer/src/bin/logger.rs +++ b/crates/rust-analyzer/src/bin/logger.rs @@ -52,7 +52,7 @@ impl Logger { // merge chalk filter to our main filter (from RA_LOG env). // // The acceptable syntax of CHALK_DEBUG is `target[span{field=value}]=level`. - // As the value should only affect chalk crates, we'd better mannually + // As the value should only affect chalk crates, we'd better manually // specify the target. And for simplicity, CHALK_DEBUG only accept the value // that specify level. let chalk_level_dir = std::env::var("CHALK_DEBUG") diff --git a/crates/syntax/src/hacks.rs b/crates/syntax/src/hacks.rs index a047f61fa03..ec3d3d444c3 100644 --- a/crates/syntax/src/hacks.rs +++ b/crates/syntax/src/hacks.rs @@ -1,4 +1,4 @@ -//! Things which exist to solve practial issues, but which shouldn't exist. +//! Things which exist to solve practical issues, but which shouldn't exist. //! //! Please avoid adding new usages of the functions in this module diff --git a/crates/vfs/src/lib.rs b/crates/vfs/src/lib.rs index 10fae41d081..7badb1c363b 100644 --- a/crates/vfs/src/lib.rs +++ b/crates/vfs/src/lib.rs @@ -64,7 +64,7 @@ pub struct FileId(pub u32); /// Storage for all files read by rust-analyzer. /// -/// For more informations see the [crate-level](crate) documentation. +/// For more information see the [crate-level](crate) documentation. #[derive(Default)] pub struct Vfs { interner: PathInterner, diff --git a/docs/dev/architecture.md b/docs/dev/architecture.md index 51e26c58a91..c173a239fea 100644 --- a/docs/dev/architecture.md +++ b/docs/dev/architecture.md @@ -485,7 +485,7 @@ Mind the code--architecture gap: at the moment, we are using fewer feature flags ### Serialization In Rust, it is easy (often too easy) to add serialization to any type by adding `#[derive(Serialize)]`. -This easiness is misleading -- serializable types impose significant backwards compatability constraints. +This easiness is misleading -- serializable types impose significant backwards compatibility constraints. If a type is serializable, then it is a part of some IPC boundary. You often don't control the other side of this boundary, so changing serializable types is hard. diff --git a/editors/code/src/commands.ts b/editors/code/src/commands.ts index f58de9da1bf..12f666401fd 100644 --- a/editors/code/src/commands.ts +++ b/editors/code/src/commands.ts @@ -655,7 +655,7 @@ function crateGraph(ctx: Ctx, full: boolean): Cmd { html, body { margin:0; padding:0; overflow:hidden } svg { position:fixed; top:0; left:0; height:100%; width:100% } - /* Disable the graphviz backgroud and fill the polygons */ + /* Disable the graphviz background and fill the polygons */ .graph > polygon { display:none; } :is(.node,.edge) polygon { fill: white; } diff --git a/editors/code/src/toolchain.ts b/editors/code/src/toolchain.ts index bac163da9f3..e1ca4954280 100644 --- a/editors/code/src/toolchain.ts +++ b/editors/code/src/toolchain.ts @@ -158,7 +158,7 @@ export const getPathForExecutable = memoizeAsync( try { // hmm, `os.homedir()` seems to be infallible - // it is not mentioned in docs and cannot be infered by the type signature... + // it is not mentioned in docs and cannot be inferred by the type signature... const standardPath = vscode.Uri.joinPath( vscode.Uri.file(os.homedir()), ".cargo", -- cgit 1.4.1-3-g733a5 From 85b9568e2d7cc3c4b9a9d420ab3d4b91037bc6a5 Mon Sep 17 00:00:00 2001 From: Dorian Scheidt Date: Wed, 17 Aug 2022 11:57:14 -0500 Subject: feat: Run test mod from anywhere in parent file The "Run" feature of rust-analyzer is super useful, especially for running individual tests or test-modules during development. One common pattern in rust development is to develop tests in the same file as production code, inside a module (usually called `test` or `tests`) marked with `#[cfg(test)]`. Unforunately, this pattern is not well supported by r-a today, as a test module won't show up as a runnable unless the cursor is inside it. In my experience, it is quite common to want to run the tests associated with some production code immediately after editing it, not only after editing the tests themselves. As such it would be better if test modules were available from the "Run" menu even when the cursor is outside the test module. This change updates the filtration logic for runnables in `handlers::handle_runnables` to special case `RunnableKind::TestMod`, making test modules available regardless of the cursor location. Other `RunnableKind`s are unnaffected. Fixes #9589 --- crates/rust-analyzer/src/handlers.rs | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/crates/rust-analyzer/src/handlers.rs b/crates/rust-analyzer/src/handlers.rs index 943d043bc19..6337d49c240 100644 --- a/crates/rust-analyzer/src/handlers.rs +++ b/crates/rust-analyzer/src/handlers.rs @@ -703,10 +703,8 @@ pub(crate) fn handle_runnables( let mut res = Vec::new(); for runnable in snap.analysis.runnables(file_id)? { - if let Some(offset) = offset { - if !runnable.nav.full_range.contains_inclusive(offset) { - continue; - } + if should_skip_for_offset(&runnable, offset) { + continue; } if should_skip_target(&runnable, cargo_spec.as_ref()) { continue; @@ -772,6 +770,14 @@ pub(crate) fn handle_runnables( Ok(res) } +fn should_skip_for_offset(runnable: &Runnable, offset: Option) -> bool { + match offset { + None => false, + _ if matches!(&runnable.kind, RunnableKind::TestMod { .. }) => false, + Some(offset) => !runnable.nav.full_range.contains_inclusive(offset), + } +} + pub(crate) fn handle_related_tests( snap: GlobalStateSnapshot, params: lsp_types::TextDocumentPositionParams, -- cgit 1.4.1-3-g733a5 From 12abaf8ddde026fd733c7e783cc3081f4c22cce2 Mon Sep 17 00:00:00 2001 From: Ryo Yoshida Date: Thu, 18 Aug 2022 01:30:04 +0900 Subject: fix: resolve associated types of bare dyn types --- crates/hir-ty/src/lower.rs | 61 +++++++++++++++++---------------------- crates/hir-ty/src/tests/traits.rs | 28 ++++++++++++++++++ 2 files changed, 54 insertions(+), 35 deletions(-) diff --git a/crates/hir-ty/src/lower.rs b/crates/hir-ty/src/lower.rs index 239f66bcb7e..ae115c8c0da 100644 --- a/crates/hir-ty/src/lower.rs +++ b/crates/hir-ty/src/lower.rs @@ -238,18 +238,7 @@ impl<'a> TyLoweringContext<'a> { }) .intern(Interner) } - TypeRef::DynTrait(bounds) => { - let self_ty = - TyKind::BoundVar(BoundVar::new(DebruijnIndex::INNERMOST, 0)).intern(Interner); - let bounds = self.with_shifted_in(DebruijnIndex::ONE, |ctx| { - QuantifiedWhereClauses::from_iter( - Interner, - bounds.iter().flat_map(|b| ctx.lower_type_bound(b, self_ty.clone(), false)), - ) - }); - let bounds = crate::make_single_type_binders(bounds); - TyKind::Dyn(DynTy { bounds, lifetime: static_lifetime() }).intern(Interner) - } + TypeRef::DynTrait(bounds) => self.lower_dyn_trait(bounds), TypeRef::ImplTrait(bounds) => { match self.impl_trait_mode { ImplTraitLoweringMode::Opaque => { @@ -468,29 +457,10 @@ impl<'a> TyLoweringContext<'a> { } } 0 => { - let self_ty = Some( - TyKind::BoundVar(BoundVar::new(DebruijnIndex::INNERMOST, 0)) - .intern(Interner), - ); - let trait_ref = self.with_shifted_in(DebruijnIndex::ONE, |ctx| { - ctx.lower_trait_ref_from_resolved_path( - trait_, - resolved_segment, - self_ty, - ) - }); - let dyn_ty = DynTy { - bounds: crate::make_single_type_binders( - QuantifiedWhereClauses::from_iter( - Interner, - Some(crate::wrap_empty_binders(WhereClause::Implemented( - trait_ref, - ))), - ), - ), - lifetime: static_lifetime(), - }; - TyKind::Dyn(dyn_ty).intern(Interner) + // Trait object type without dyn; this should be handled in upstream. See + // `lower_path()`. + stdx::never!("unexpected fully resolved trait path"); + TyKind::Error.intern(Interner) } _ => { // FIXME report error (ambiguous associated type) @@ -555,11 +525,20 @@ impl<'a> TyLoweringContext<'a> { let (ty, res) = self.lower_ty_ext(type_ref); return self.lower_ty_relative_path(ty, res, path.segments()); } + let (resolution, remaining_index) = match self.resolver.resolve_path_in_type_ns(self.db.upcast(), path.mod_path()) { Some(it) => it, None => return (TyKind::Error.intern(Interner), None), }; + + if matches!(resolution, TypeNs::TraitId(_)) && remaining_index.is_none() { + // trait object type without dyn + let bound = TypeBound::Path(path.clone(), TraitBoundModifier::None); + let ty = self.lower_dyn_trait(&[Interned::new(bound)]); + return (ty, None); + } + let (resolved_segment, remaining_segments) = match remaining_index { None => ( path.segments().last().expect("resolved path has at least one element"), @@ -987,6 +966,18 @@ impl<'a> TyLoweringContext<'a> { }) } + fn lower_dyn_trait(&self, bounds: &[Interned]) -> Ty { + let self_ty = TyKind::BoundVar(BoundVar::new(DebruijnIndex::INNERMOST, 0)).intern(Interner); + let bounds = self.with_shifted_in(DebruijnIndex::ONE, |ctx| { + QuantifiedWhereClauses::from_iter( + Interner, + bounds.iter().flat_map(|b| ctx.lower_type_bound(b, self_ty.clone(), false)), + ) + }); + let bounds = crate::make_single_type_binders(bounds); + TyKind::Dyn(DynTy { bounds, lifetime: static_lifetime() }).intern(Interner) + } + fn lower_impl_trait( &self, bounds: &[Interned], diff --git a/crates/hir-ty/src/tests/traits.rs b/crates/hir-ty/src/tests/traits.rs index 75802a5eb4d..c128a051f72 100644 --- a/crates/hir-ty/src/tests/traits.rs +++ b/crates/hir-ty/src/tests/traits.rs @@ -1476,6 +1476,34 @@ fn test(x: Trait, y: &Trait) -> u64 { 165..172 'z.foo()': u64 "#]], ); + + check_infer_with_mismatches( + r#" +//- minicore: fn, coerce_unsized +struct S; +impl S { + fn foo(&self) {} +} +fn f(_: &Fn(S)) {} +fn main() { + f(&|number| number.foo()); +} + "#, + expect![[r#" + 31..35 'self': &S + 37..39 '{}': () + 47..48 '_': &dyn Fn(S) + 58..60 '{}': () + 71..105 '{ ...()); }': () + 77..78 'f': fn f(&dyn Fn(S)) + 77..102 'f(&|nu...foo())': () + 79..101 '&|numb....foo()': &|S| -> () + 80..101 '|numbe....foo()': |S| -> () + 81..87 'number': S + 89..95 'number': S + 89..101 'number.foo()': () + "#]], + ) } #[test] -- cgit 1.4.1-3-g733a5 From dac27679f75725c8a3e8ff5fc009f3bc59ce3523 Mon Sep 17 00:00:00 2001 From: Ryo Yoshida Date: Thu, 18 Aug 2022 08:46:06 +0900 Subject: fix: resolve path `Self` alone in value namespace --- crates/hir-ty/src/infer.rs | 1 + crates/hir-ty/src/tests/patterns.rs | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+) diff --git a/crates/hir-ty/src/infer.rs b/crates/hir-ty/src/infer.rs index 46eeea0e6fc..7440439522e 100644 --- a/crates/hir-ty/src/infer.rs +++ b/crates/hir-ty/src/infer.rs @@ -734,6 +734,7 @@ impl<'a> InferenceContext<'a> { let ty = self.insert_type_vars(ty.substitute(Interner, &substs)); return (ty, Some(strukt.into())); } + ValueNs::ImplSelf(impl_id) => (TypeNs::SelfType(impl_id), None), _ => return (self.err_ty(), None), }, Some(ResolveValueResult::Partial(typens, unresolved)) => (typens, Some(unresolved)), diff --git a/crates/hir-ty/src/tests/patterns.rs b/crates/hir-ty/src/tests/patterns.rs index 94efe7bc11a..eb04bf87783 100644 --- a/crates/hir-ty/src/tests/patterns.rs +++ b/crates/hir-ty/src/tests/patterns.rs @@ -488,6 +488,42 @@ fn infer_adt_pattern() { ); } +#[test] +fn tuple_struct_destructured_with_self() { + check_infer( + r#" +struct Foo(usize,); +impl Foo { + fn f() { + let Self(s,) = &Foo(0,); + let Self(s,) = &mut Foo(0,); + let Self(s,) = Foo(0,); + } +} + "#, + expect![[r#" + 42..151 '{ ... }': () + 56..64 'Self(s,)': Foo + 61..62 's': &usize + 67..75 '&Foo(0,)': &Foo + 68..71 'Foo': Foo(usize) -> Foo + 68..75 'Foo(0,)': Foo + 72..73 '0': usize + 89..97 'Self(s,)': Foo + 94..95 's': &mut usize + 100..112 '&mut Foo(0,)': &mut Foo + 105..108 'Foo': Foo(usize) -> Foo + 105..112 'Foo(0,)': Foo + 109..110 '0': usize + 126..134 'Self(s,)': Foo + 131..132 's': usize + 137..140 'Foo': Foo(usize) -> Foo + 137..144 'Foo(0,)': Foo + 141..142 '0': usize + "#]], + ); +} + #[test] fn enum_variant_through_self_in_pattern() { check_infer( -- cgit 1.4.1-3-g733a5 From d39677c1ebca7a764172abe1999123d7100963d7 Mon Sep 17 00:00:00 2001 From: Dorian Scheidt Date: Thu, 18 Aug 2022 18:10:37 -0500 Subject: Rename static_method_target -> assoc_fn_target --- crates/ide-assists/src/handlers/generate_function.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/ide-assists/src/handlers/generate_function.rs b/crates/ide-assists/src/handlers/generate_function.rs index 49345cb983f..f5d2b27cb25 100644 --- a/crates/ide-assists/src/handlers/generate_function.rs +++ b/crates/ide-assists/src/handlers/generate_function.rs @@ -78,11 +78,11 @@ fn gen_fn(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { } } - static_method_target(ctx, &call, adt, &mut target_module, fn_name, &mut adt_name)? + assoc_fn_target(ctx, &call, adt, &mut target_module, fn_name, &mut adt_name)? } Some(hir::PathResolution::SelfType(impl_)) => { let adt = impl_.self_ty(ctx.db()).as_adt()?; - static_method_target(ctx, &call, adt, &mut target_module, fn_name, &mut adt_name)? + assoc_fn_target(ctx, &call, adt, &mut target_module, fn_name, &mut adt_name)? } _ => { return None; @@ -394,7 +394,7 @@ fn get_method_target( Some((target.clone(), get_insert_offset(&target))) } -fn static_method_target( +fn assoc_fn_target( ctx: &AssistContext<'_>, call: &CallExpr, adt: hir::Adt, -- cgit 1.4.1-3-g733a5 From 61308607893b1a3c2a9de19d2f80c811d7ad643b Mon Sep 17 00:00:00 2001 From: Dorian Scheidt Date: Thu, 18 Aug 2022 18:12:53 -0500 Subject: use fn_name instead of name_ref.text() --- crates/ide-assists/src/handlers/generate_function.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/ide-assists/src/handlers/generate_function.rs b/crates/ide-assists/src/handlers/generate_function.rs index f5d2b27cb25..20855adf3c5 100644 --- a/crates/ide-assists/src/handlers/generate_function.rs +++ b/crates/ide-assists/src/handlers/generate_function.rs @@ -73,7 +73,7 @@ fn gen_fn(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { Some(hir::PathResolution::Def(hir::ModuleDef::Adt(adt))) => { if let hir::Adt::Enum(_) = adt { // Don't suggest generating function if the name starts with an uppercase letter - if name_ref.text().starts_with(char::is_uppercase) { + if fn_name.starts_with(char::is_uppercase) { return None; } } -- cgit 1.4.1-3-g733a5 From 30eabeb95d047b91268a70c5170b6374c6bab622 Mon Sep 17 00:00:00 2001 From: Dorian Scheidt Date: Thu, 18 Aug 2022 18:14:51 -0500 Subject: extract fn_target_info --- .../ide-assists/src/handlers/generate_function.rs | 42 +++++++++++++--------- 1 file changed, 26 insertions(+), 16 deletions(-) diff --git a/crates/ide-assists/src/handlers/generate_function.rs b/crates/ide-assists/src/handlers/generate_function.rs index 20855adf3c5..ba8bfe0c19b 100644 --- a/crates/ide-assists/src/handlers/generate_function.rs +++ b/crates/ide-assists/src/handlers/generate_function.rs @@ -61,9 +61,31 @@ fn gen_fn(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { } let fn_name = &*name_ref.text(); + let (target_module, adt_name, target, file, insert_offset) = + fn_target_info(path, ctx, &call, fn_name)?; + let function_builder = FunctionBuilder::from_call(ctx, &call, fn_name, target_module, target)?; + let text_range = call.syntax().text_range(); + let label = format!("Generate {} function", function_builder.fn_name); + add_func_to_accumulator( + acc, + ctx, + text_range, + function_builder, + insert_offset, + file, + adt_name, + label, + ) +} + +fn fn_target_info( + path: ast::Path, + ctx: &AssistContext<'_>, + call: &CallExpr, + fn_name: &str, +) -> Option<(Option, Option, GeneratedFunctionTarget, FileId, TextSize)> { let mut target_module = None; let mut adt_name = None; - let (target, file, insert_offset) = match path.qualifier() { Some(qualifier) => match ctx.sema.resolve_path(&qualifier) { Some(hir::PathResolution::Def(hir::ModuleDef::Module(module))) => { @@ -78,11 +100,11 @@ fn gen_fn(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { } } - assoc_fn_target(ctx, &call, adt, &mut target_module, fn_name, &mut adt_name)? + assoc_fn_target(ctx, call, adt, &mut target_module, fn_name, &mut adt_name)? } Some(hir::PathResolution::SelfType(impl_)) => { let adt = impl_.self_ty(ctx.db()).as_adt()?; - assoc_fn_target(ctx, &call, adt, &mut target_module, fn_name, &mut adt_name)? + assoc_fn_target(ctx, call, adt, &mut target_module, fn_name, &mut adt_name)? } _ => { return None; @@ -93,19 +115,7 @@ fn gen_fn(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { get_fn_target(ctx, &target_module, call.clone())? } }; - let function_builder = FunctionBuilder::from_call(ctx, &call, fn_name, target_module, target)?; - let text_range = call.syntax().text_range(); - let label = format!("Generate {} function", function_builder.fn_name); - add_func_to_accumulator( - acc, - ctx, - text_range, - function_builder, - insert_offset, - file, - adt_name, - label, - ) + Some((target_module, adt_name, target, file, insert_offset)) } fn gen_method(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { -- cgit 1.4.1-3-g733a5 From e513d7b5049092c3797cc30e13d658efb11ad17a Mon Sep 17 00:00:00 2001 From: Dorian Scheidt Date: Thu, 18 Aug 2022 18:20:06 -0500 Subject: Replace tuple with TargetInfo struct --- crates/ide-assists/src/handlers/generate_function.rs | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/crates/ide-assists/src/handlers/generate_function.rs b/crates/ide-assists/src/handlers/generate_function.rs index ba8bfe0c19b..dee5fea2330 100644 --- a/crates/ide-assists/src/handlers/generate_function.rs +++ b/crates/ide-assists/src/handlers/generate_function.rs @@ -61,7 +61,7 @@ fn gen_fn(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { } let fn_name = &*name_ref.text(); - let (target_module, adt_name, target, file, insert_offset) = + let TargetInfo { target_module, adt_name, target, file, insert_offset } = fn_target_info(path, ctx, &call, fn_name)?; let function_builder = FunctionBuilder::from_call(ctx, &call, fn_name, target_module, target)?; let text_range = call.syntax().text_range(); @@ -78,12 +78,20 @@ fn gen_fn(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { ) } +struct TargetInfo { + target_module: Option, + adt_name: Option, + target: GeneratedFunctionTarget, + file: FileId, + insert_offset: TextSize, +} + fn fn_target_info( path: ast::Path, ctx: &AssistContext<'_>, call: &CallExpr, fn_name: &str, -) -> Option<(Option, Option, GeneratedFunctionTarget, FileId, TextSize)> { +) -> Option { let mut target_module = None; let mut adt_name = None; let (target, file, insert_offset) = match path.qualifier() { @@ -115,7 +123,7 @@ fn fn_target_info( get_fn_target(ctx, &target_module, call.clone())? } }; - Some((target_module, adt_name, target, file, insert_offset)) + Some(TargetInfo { target_module, adt_name, target, file, insert_offset }) } fn gen_method(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { -- cgit 1.4.1-3-g733a5 From e27af5fae3e44ebd2339c3dd3fcaa1e9b59de598 Mon Sep 17 00:00:00 2001 From: Dorian Scheidt Date: Thu, 18 Aug 2022 18:21:58 -0500 Subject: Reorder args with flip_comma --- crates/ide-assists/src/handlers/generate_function.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/ide-assists/src/handlers/generate_function.rs b/crates/ide-assists/src/handlers/generate_function.rs index dee5fea2330..2283805a305 100644 --- a/crates/ide-assists/src/handlers/generate_function.rs +++ b/crates/ide-assists/src/handlers/generate_function.rs @@ -62,7 +62,7 @@ fn gen_fn(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { let fn_name = &*name_ref.text(); let TargetInfo { target_module, adt_name, target, file, insert_offset } = - fn_target_info(path, ctx, &call, fn_name)?; + fn_target_info(ctx, path, &call, fn_name)?; let function_builder = FunctionBuilder::from_call(ctx, &call, fn_name, target_module, target)?; let text_range = call.syntax().text_range(); let label = format!("Generate {} function", function_builder.fn_name); @@ -87,8 +87,8 @@ struct TargetInfo { } fn fn_target_info( - path: ast::Path, ctx: &AssistContext<'_>, + path: ast::Path, call: &CallExpr, fn_name: &str, ) -> Option { -- cgit 1.4.1-3-g733a5 From 2086c48cff23aff651c18636e8227993a85cd6ac Mon Sep 17 00:00:00 2001 From: Dorian Scheidt Date: Thu, 18 Aug 2022 18:26:54 -0500 Subject: Remove mut out params via assoc_fn_target_info --- crates/ide-assists/src/handlers/generate_function.rs | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/crates/ide-assists/src/handlers/generate_function.rs b/crates/ide-assists/src/handlers/generate_function.rs index 2283805a305..adb5ec2b99d 100644 --- a/crates/ide-assists/src/handlers/generate_function.rs +++ b/crates/ide-assists/src/handlers/generate_function.rs @@ -92,8 +92,8 @@ fn fn_target_info( call: &CallExpr, fn_name: &str, ) -> Option { - let mut target_module = None; - let mut adt_name = None; + let target_module; + let adt_name = None; let (target, file, insert_offset) = match path.qualifier() { Some(qualifier) => match ctx.sema.resolve_path(&qualifier) { Some(hir::PathResolution::Def(hir::ModuleDef::Module(module))) => { @@ -108,11 +108,11 @@ fn fn_target_info( } } - assoc_fn_target(ctx, call, adt, &mut target_module, fn_name, &mut adt_name)? + return assoc_fn_target_info(ctx, call, adt, fn_name); } Some(hir::PathResolution::SelfType(impl_)) => { let adt = impl_.self_ty(ctx.db()).as_adt()?; - assoc_fn_target(ctx, call, adt, &mut target_module, fn_name, &mut adt_name)? + return assoc_fn_target_info(ctx, call, adt, fn_name); } _ => { return None; @@ -412,6 +412,18 @@ fn get_method_target( Some((target.clone(), get_insert_offset(&target))) } +fn assoc_fn_target_info( + ctx: &AssistContext<'_>, + call: &CallExpr, + adt: hir::Adt, + fn_name: &str, +) -> Option { + let mut target_module = None; + let mut adt_name = None; + let (target, file, insert_offset) = + assoc_fn_target(ctx, call, adt, &mut target_module, fn_name, &mut adt_name)?; + Some(TargetInfo { target_module, adt_name, target, file, insert_offset }) +} fn assoc_fn_target( ctx: &AssistContext<'_>, call: &CallExpr, -- cgit 1.4.1-3-g733a5 From 96c04c5e1c54ceef0b16679ecb71f64548150826 Mon Sep 17 00:00:00 2001 From: Dorian Scheidt Date: Thu, 18 Aug 2022 18:28:14 -0500 Subject: inline assoc_fn_target --- .../ide-assists/src/handlers/generate_function.rs | 35 +++++++++------------- 1 file changed, 14 insertions(+), 21 deletions(-) diff --git a/crates/ide-assists/src/handlers/generate_function.rs b/crates/ide-assists/src/handlers/generate_function.rs index adb5ec2b99d..6c045a3333d 100644 --- a/crates/ide-assists/src/handlers/generate_function.rs +++ b/crates/ide-assists/src/handlers/generate_function.rs @@ -420,29 +420,22 @@ fn assoc_fn_target_info( ) -> Option { let mut target_module = None; let mut adt_name = None; - let (target, file, insert_offset) = - assoc_fn_target(ctx, call, adt, &mut target_module, fn_name, &mut adt_name)?; + let (target, file, insert_offset) = { + let target_module: &mut Option = &mut target_module; + let adt_name: &mut Option = &mut adt_name; + let current_module = ctx.sema.scope(call.syntax())?.module(); + let module = adt.module(ctx.sema.db); + *target_module = if current_module == module { None } else { Some(module) }; + if current_module.krate() != module.krate() { + return None; + } + let (impl_, file) = get_adt_source(ctx, &adt, fn_name)?; + let (target, insert_offset) = get_method_target(ctx, &module, &impl_)?; + *adt_name = if impl_.is_none() { Some(adt.name(ctx.sema.db)) } else { None }; + Some((target, file, insert_offset)) + }?; Some(TargetInfo { target_module, adt_name, target, file, insert_offset }) } -fn assoc_fn_target( - ctx: &AssistContext<'_>, - call: &CallExpr, - adt: hir::Adt, - target_module: &mut Option, - fn_name: &str, - adt_name: &mut Option, -) -> Option<(GeneratedFunctionTarget, FileId, TextSize)> { - let current_module = ctx.sema.scope(call.syntax())?.module(); - let module = adt.module(ctx.sema.db); - *target_module = if current_module == module { None } else { Some(module) }; - if current_module.krate() != module.krate() { - return None; - } - let (impl_, file) = get_adt_source(ctx, &adt, fn_name)?; - let (target, insert_offset) = get_method_target(ctx, &module, &impl_)?; - *adt_name = if impl_.is_none() { Some(adt.name(ctx.sema.db)) } else { None }; - Some((target, file, insert_offset)) -} fn get_insert_offset(target: &GeneratedFunctionTarget) -> TextSize { match &target { -- cgit 1.4.1-3-g733a5 From 2e4a4f1a9c1cba63ec44562f0ab0d5b5783ad561 Mon Sep 17 00:00:00 2001 From: Dorian Scheidt Date: Thu, 18 Aug 2022 18:28:43 -0500 Subject: Cleanup inline --- .../ide-assists/src/handlers/generate_function.rs | 25 ++++++++-------------- 1 file changed, 9 insertions(+), 16 deletions(-) diff --git a/crates/ide-assists/src/handlers/generate_function.rs b/crates/ide-assists/src/handlers/generate_function.rs index 6c045a3333d..cf28f49e034 100644 --- a/crates/ide-assists/src/handlers/generate_function.rs +++ b/crates/ide-assists/src/handlers/generate_function.rs @@ -418,22 +418,15 @@ fn assoc_fn_target_info( adt: hir::Adt, fn_name: &str, ) -> Option { - let mut target_module = None; - let mut adt_name = None; - let (target, file, insert_offset) = { - let target_module: &mut Option = &mut target_module; - let adt_name: &mut Option = &mut adt_name; - let current_module = ctx.sema.scope(call.syntax())?.module(); - let module = adt.module(ctx.sema.db); - *target_module = if current_module == module { None } else { Some(module) }; - if current_module.krate() != module.krate() { - return None; - } - let (impl_, file) = get_adt_source(ctx, &adt, fn_name)?; - let (target, insert_offset) = get_method_target(ctx, &module, &impl_)?; - *adt_name = if impl_.is_none() { Some(adt.name(ctx.sema.db)) } else { None }; - Some((target, file, insert_offset)) - }?; + let current_module = ctx.sema.scope(call.syntax())?.module(); + let module = adt.module(ctx.sema.db); + let target_module = if current_module == module { None } else { Some(module) }; + if current_module.krate() != module.krate() { + return None; + } + let (impl_, file) = get_adt_source(ctx, &adt, fn_name)?; + let (target, insert_offset) = get_method_target(ctx, &module, &impl_)?; + let adt_name = if impl_.is_none() { Some(adt.name(ctx.sema.db)) } else { None }; Some(TargetInfo { target_module, adt_name, target, file, insert_offset }) } -- cgit 1.4.1-3-g733a5 From d8e7419c647ff1cd24263fb8e6fbfd609d17d05b Mon Sep 17 00:00:00 2001 From: Dorian Scheidt Date: Thu, 18 Aug 2022 18:32:35 -0500 Subject: Generate and use TargetInfo::new --- crates/ide-assists/src/handlers/generate_function.rs | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/crates/ide-assists/src/handlers/generate_function.rs b/crates/ide-assists/src/handlers/generate_function.rs index cf28f49e034..892b166a2ce 100644 --- a/crates/ide-assists/src/handlers/generate_function.rs +++ b/crates/ide-assists/src/handlers/generate_function.rs @@ -86,6 +86,18 @@ struct TargetInfo { insert_offset: TextSize, } +impl TargetInfo { + fn new( + target_module: Option, + adt_name: Option, + target: GeneratedFunctionTarget, + file: FileId, + insert_offset: TextSize, + ) -> Self { + Self { target_module, adt_name, target, file, insert_offset } + } +} + fn fn_target_info( ctx: &AssistContext<'_>, path: ast::Path, @@ -123,7 +135,7 @@ fn fn_target_info( get_fn_target(ctx, &target_module, call.clone())? } }; - Some(TargetInfo { target_module, adt_name, target, file, insert_offset }) + Some(TargetInfo::new(target_module, adt_name, target, file, insert_offset)) } fn gen_method(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { @@ -427,7 +439,7 @@ fn assoc_fn_target_info( let (impl_, file) = get_adt_source(ctx, &adt, fn_name)?; let (target, insert_offset) = get_method_target(ctx, &module, &impl_)?; let adt_name = if impl_.is_none() { Some(adt.name(ctx.sema.db)) } else { None }; - Some(TargetInfo { target_module, adt_name, target, file, insert_offset }) + Some(TargetInfo::new(target_module, adt_name, target, file, insert_offset)) } fn get_insert_offset(target: &GeneratedFunctionTarget) -> TextSize { -- cgit 1.4.1-3-g733a5 From 48ea3825b8951a39e27503ea16cacc9147255d79 Mon Sep 17 00:00:00 2001 From: Dorian Scheidt Date: Thu, 18 Aug 2022 18:39:42 -0500 Subject: Introduce and use get_fn_target_info --- .../ide-assists/src/handlers/generate_function.rs | 32 +++++++++++----------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/crates/ide-assists/src/handlers/generate_function.rs b/crates/ide-assists/src/handlers/generate_function.rs index 892b166a2ce..e26c76da189 100644 --- a/crates/ide-assists/src/handlers/generate_function.rs +++ b/crates/ide-assists/src/handlers/generate_function.rs @@ -104,13 +104,10 @@ fn fn_target_info( call: &CallExpr, fn_name: &str, ) -> Option { - let target_module; - let adt_name = None; - let (target, file, insert_offset) = match path.qualifier() { + match path.qualifier() { Some(qualifier) => match ctx.sema.resolve_path(&qualifier) { Some(hir::PathResolution::Def(hir::ModuleDef::Module(module))) => { - target_module = Some(module); - get_fn_target(ctx, &target_module, call.clone())? + get_fn_target_info(ctx, &Some(module), call.clone()) } Some(hir::PathResolution::Def(hir::ModuleDef::Adt(adt))) => { if let hir::Adt::Enum(_) = adt { @@ -120,22 +117,16 @@ fn fn_target_info( } } - return assoc_fn_target_info(ctx, call, adt, fn_name); + assoc_fn_target_info(ctx, call, adt, fn_name) } Some(hir::PathResolution::SelfType(impl_)) => { let adt = impl_.self_ty(ctx.db()).as_adt()?; - return assoc_fn_target_info(ctx, call, adt, fn_name); - } - _ => { - return None; + assoc_fn_target_info(ctx, call, adt, fn_name) } + _ => None, }, - _ => { - target_module = None; - get_fn_target(ctx, &target_module, call.clone())? - } - }; - Some(TargetInfo::new(target_module, adt_name, target, file, insert_offset)) + _ => get_fn_target_info(ctx, &None, call.clone()), + } } fn gen_method(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { @@ -391,6 +382,15 @@ fn make_return_type( (ret_type, should_focus_return_type) } +fn get_fn_target_info( + ctx: &AssistContext<'_>, + target_module: &Option, + call: CallExpr, +) -> Option { + let (target, file, insert_offset) = get_fn_target(ctx, target_module, call)?; + Some(TargetInfo::new(*target_module, None, target, file, insert_offset)) +} + fn get_fn_target( ctx: &AssistContext<'_>, target_module: &Option, -- cgit 1.4.1-3-g733a5 From b6fe46055bd9b16fd74df43cbbcb89e612a82b4d Mon Sep 17 00:00:00 2001 From: ice1000 Date: Fri, 19 Aug 2022 01:06:00 +0000 Subject: feat: Improved inline_call to replace `Self` --- crates/ide-assists/src/handlers/inline_call.rs | 38 +++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/crates/ide-assists/src/handlers/inline_call.rs b/crates/ide-assists/src/handlers/inline_call.rs index 80d3b925593..c78c5eaa9f4 100644 --- a/crates/ide-assists/src/handlers/inline_call.rs +++ b/crates/ide-assists/src/handlers/inline_call.rs @@ -13,7 +13,7 @@ use ide_db::{ use itertools::{izip, Itertools}; use syntax::{ ast::{self, edit_in_place::Indent, HasArgList, PathExpr}, - ted, AstNode, + ted, AstNode, SyntaxKind, }; use crate::{ @@ -311,6 +311,16 @@ fn inline( } else { fn_body.clone_for_update() }; + // TODO: use if-let chains - https://github.com/rust-lang/rust/pull/94927 + if let Some(i) = body.syntax().ancestors().find_map(ast::Impl::cast) { + if let Some(st) = i.self_ty() { + for tok in body.syntax().descendants_with_tokens().filter_map(|t| t.into_token()) { + if tok.kind() == SyntaxKind::SELF_TYPE_KW { + ted::replace(tok, st.syntax()); + } + } + } + } let usages_for_locals = |local| { Definition::Local(local) .usages(sema) @@ -345,6 +355,7 @@ fn inline( } }) .collect(); + if function.self_param(sema.db).is_some() { let this = || make::name_ref("this").syntax().clone_for_update(); if let Some(self_local) = params[0].2.as_local(sema.db) { @@ -1188,6 +1199,31 @@ fn bar() -> u32 { x } } +"#, + ) + } + + #[test] + fn inline_call_with_self_type() { + check_assist( + inline_call, + r#" +struct A(u32); +impl A { + fn f() -> Self { Self(114514) } +} +fn main() { + A::f$0(); +} +"#, + r#" +struct A(u32); +impl A { + fn f() -> Self { Self(114514) } +} +fn main() { + A(114514); +} "#, ) } -- cgit 1.4.1-3-g733a5 From 5a6c51ebb8549467abeeac69bc2e12494cde5b29 Mon Sep 17 00:00:00 2001 From: ice1000 Date: Fri, 19 Aug 2022 01:08:59 +0000 Subject: fix: use functional programming --- crates/ide-assists/src/handlers/inline_call.rs | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/crates/ide-assists/src/handlers/inline_call.rs b/crates/ide-assists/src/handlers/inline_call.rs index c78c5eaa9f4..b5d092e39b0 100644 --- a/crates/ide-assists/src/handlers/inline_call.rs +++ b/crates/ide-assists/src/handlers/inline_call.rs @@ -13,7 +13,7 @@ use ide_db::{ use itertools::{izip, Itertools}; use syntax::{ ast::{self, edit_in_place::Indent, HasArgList, PathExpr}, - ted, AstNode, SyntaxKind, + ted, AstNode, NodeOrToken, SyntaxKind, }; use crate::{ @@ -311,15 +311,12 @@ fn inline( } else { fn_body.clone_for_update() }; - // TODO: use if-let chains - https://github.com/rust-lang/rust/pull/94927 - if let Some(i) = body.syntax().ancestors().find_map(ast::Impl::cast) { - if let Some(st) = i.self_ty() { - for tok in body.syntax().descendants_with_tokens().filter_map(|t| t.into_token()) { - if tok.kind() == SyntaxKind::SELF_TYPE_KW { - ted::replace(tok, st.syntax()); - } - } - } + if let Some(t) = body.syntax().ancestors().find_map(ast::Impl::cast).and_then(|i| i.self_ty()) { + body.syntax() + .descendants_with_tokens() + .filter_map(NodeOrToken::into_token) + .filter(|tok| tok.kind() == SyntaxKind::SELF_TYPE_KW) + .for_each(|tok| ted::replace(tok, t.syntax())); } let usages_for_locals = |local| { Definition::Local(local) -- cgit 1.4.1-3-g733a5 From 45b7b6a60a7404d093f4afe81a0da10824cef7b8 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Fri, 19 Aug 2022 08:52:31 +0200 Subject: Implement lsp extension for cancelling running flychecks --- crates/flycheck/src/lib.rs | 26 +++++++++++++++++++++----- crates/rust-analyzer/src/handlers.rs | 6 ++++++ crates/rust-analyzer/src/lsp_ext.rs | 8 ++++++++ crates/rust-analyzer/src/main_loop.rs | 7 ++++--- docs/dev/lsp-extensions.md | 2 +- editors/code/package.json | 5 +++++ editors/code/src/commands.ts | 6 ++++++ editors/code/src/lsp_ext.ts | 30 +++++++++++++++++------------- editors/code/src/main.ts | 1 + 9 files changed, 69 insertions(+), 22 deletions(-) diff --git a/crates/flycheck/src/lib.rs b/crates/flycheck/src/lib.rs index 2bebea2fbfe..c22945c81fc 100644 --- a/crates/flycheck/src/lib.rs +++ b/crates/flycheck/src/lib.rs @@ -77,8 +77,13 @@ impl FlycheckHandle { } /// Schedule a re-start of the cargo check worker. - pub fn update(&self) { - self.sender.send(Restart).unwrap(); + pub fn restart(&self) { + self.sender.send(Restart::Yes).unwrap(); + } + + /// Stop this cargo check worker. + pub fn cancel(&self) { + self.sender.send(Restart::No).unwrap(); } pub fn id(&self) -> usize { @@ -122,7 +127,10 @@ pub enum Progress { DidCancel, } -struct Restart; +enum Restart { + Yes, + No, +} struct FlycheckActor { id: usize, @@ -149,6 +157,7 @@ impl FlycheckActor { config: FlycheckConfig, workspace_root: AbsPathBuf, ) -> FlycheckActor { + tracing::info!(%id, ?workspace_root, "Spawning flycheck"); FlycheckActor { id, sender, config, workspace_root, cargo_handle: None } } fn progress(&self, progress: Progress) { @@ -164,10 +173,13 @@ impl FlycheckActor { fn run(mut self, inbox: Receiver) { while let Some(event) = self.next_event(&inbox) { match event { - Event::Restart(Restart) => { + Event::Restart(Restart::No) => { + self.cancel_check_process(); + } + Event::Restart(Restart::Yes) => { // Cancel the previously spawned process self.cancel_check_process(); - while let Ok(Restart) = inbox.recv_timeout(Duration::from_millis(50)) {} + while let Ok(_) = inbox.recv_timeout(Duration::from_millis(50)) {} let command = self.check_command(); tracing::debug!(?command, "will restart flycheck"); @@ -223,6 +235,10 @@ impl FlycheckActor { fn cancel_check_process(&mut self) { if let Some(cargo_handle) = self.cargo_handle.take() { + tracing::debug!( + command = ?self.check_command(), + "did cancel flycheck" + ); cargo_handle.cancel(); self.progress(Progress::DidCancel); } diff --git a/crates/rust-analyzer/src/handlers.rs b/crates/rust-analyzer/src/handlers.rs index 6337d49c240..fdb3205b0a9 100644 --- a/crates/rust-analyzer/src/handlers.rs +++ b/crates/rust-analyzer/src/handlers.rs @@ -51,6 +51,12 @@ pub(crate) fn handle_workspace_reload(state: &mut GlobalState, _: ()) -> Result< Ok(()) } +pub(crate) fn handle_cancel_flycheck(state: &mut GlobalState, _: ()) -> Result<()> { + let _p = profile::span("handle_stop_flycheck"); + state.flycheck.iter().for_each(|flycheck| flycheck.cancel()); + Ok(()) +} + pub(crate) fn handle_analyzer_status( snap: GlobalStateSnapshot, params: lsp_ext::AnalyzerStatusParams, diff --git a/crates/rust-analyzer/src/lsp_ext.rs b/crates/rust-analyzer/src/lsp_ext.rs index 5f0e108624b..e61c8b643d2 100644 --- a/crates/rust-analyzer/src/lsp_ext.rs +++ b/crates/rust-analyzer/src/lsp_ext.rs @@ -129,6 +129,14 @@ pub struct ExpandedMacro { pub expansion: String, } +pub enum CancelFlycheck {} + +impl Request for CancelFlycheck { + type Params = (); + type Result = (); + const METHOD: &'static str = "rust-analyzer/cancelFlycheck"; +} + pub enum MatchingBrace {} impl Request for MatchingBrace { diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs index 77419998249..f187547019a 100644 --- a/crates/rust-analyzer/src/main_loop.rs +++ b/crates/rust-analyzer/src/main_loop.rs @@ -288,7 +288,7 @@ impl GlobalState { if became_quiescent { // Project has loaded properly, kick off initial flycheck - self.flycheck.iter().for_each(FlycheckHandle::update); + self.flycheck.iter().for_each(FlycheckHandle::restart); if self.config.prefill_caches() { self.prime_caches_queue.request_op("became quiescent".to_string()); } @@ -590,6 +590,7 @@ impl GlobalState { .on_sync_mut::(handlers::handle_workspace_reload) .on_sync_mut::(handlers::handle_memory_usage) .on_sync_mut::(handlers::handle_shuffle_crate_graph) + .on_sync_mut::(handlers::handle_cancel_flycheck) .on_sync::(handlers::handle_join_lines) .on_sync::(handlers::handle_on_enter) .on_sync::(handlers::handle_selection_range) @@ -779,7 +780,7 @@ impl GlobalState { for (id, _) in workspace_ids.clone() { if id == flycheck.id() { updated = true; - flycheck.update(); + flycheck.restart(); continue; } } @@ -798,7 +799,7 @@ impl GlobalState { // No specific flycheck was triggered, so let's trigger all of them. if !updated { for flycheck in &this.flycheck { - flycheck.update(); + flycheck.restart(); } } Ok(()) diff --git a/docs/dev/lsp-extensions.md b/docs/dev/lsp-extensions.md index 5040643d34a..6d2c7d7b063 100644 --- a/docs/dev/lsp-extensions.md +++ b/docs/dev/lsp-extensions.md @@ -1,5 +1,5 @@