Unnamed repository; edit this file 'description' to name the repository.
Auto merge of #14266 - Veykril:generalize-eager-lazy, r=Veykril
feature: Make replace_or_with_or_else assists more generally applicable
bors 2023-03-07
parent 1bfe96e · parent 0ce0608 · commit 31c12ec
-rw-r--r--crates/hir/src/lib.rs10
-rw-r--r--crates/ide-assists/src/handlers/replace_method_eager_lazy.rs310
-rw-r--r--crates/ide-assists/src/handlers/replace_or_with_or_else.rs364
-rw-r--r--crates/ide-assists/src/lib.rs6
-rw-r--r--crates/ide-assists/src/tests/generated.rs80
-rw-r--r--crates/ide-ssr/src/matching.rs2
-rw-r--r--crates/ide/src/inlay_hints/bind_pat.rs117
-rw-r--r--crates/ide/src/inlay_hints/chaining.rs12
-rw-r--r--crates/ide/src/signature_help.rs2
-rw-r--r--crates/test-utils/src/minicore.rs34
10 files changed, 415 insertions, 522 deletions
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs
index df6484db53..a5df94885f 100644
--- a/crates/hir/src/lib.rs
+++ b/crates/hir/src/lib.rs
@@ -1677,6 +1677,10 @@ impl Function {
.collect()
}
+ pub fn num_params(self, db: &dyn HirDatabase) -> usize {
+ db.function_data(self.id).params.len()
+ }
+
pub fn method_params(self, db: &dyn HirDatabase) -> Option<Vec<Param>> {
if self.self_param(db).is_none() {
return None;
@@ -3857,11 +3861,13 @@ impl Type {
}
}
+// FIXME: Document this
#[derive(Debug)]
pub struct Callable {
ty: Type,
sig: CallableSig,
callee: Callee,
+ /// Whether this is a method that was called with method call syntax.
pub(crate) is_bound_method: bool,
}
@@ -3895,14 +3901,14 @@ impl Callable {
Other => CallableKind::Other,
}
}
- pub fn receiver_param(&self, db: &dyn HirDatabase) -> Option<ast::SelfParam> {
+ pub fn receiver_param(&self, db: &dyn HirDatabase) -> Option<(ast::SelfParam, Type)> {
let func = match self.callee {
Callee::Def(CallableDefId::FunctionId(it)) if self.is_bound_method => it,
_ => return None,
};
let src = func.lookup(db.upcast()).source(db.upcast());
let param_list = src.value.param_list()?;
- param_list.self_param()
+ Some((param_list.self_param()?, self.ty.derived(self.sig.params()[0].clone())))
}
pub fn n_params(&self) -> usize {
self.sig.params().len() - if self.is_bound_method { 1 } else { 0 }
diff --git a/crates/ide-assists/src/handlers/replace_method_eager_lazy.rs b/crates/ide-assists/src/handlers/replace_method_eager_lazy.rs
new file mode 100644
index 0000000000..a7e3ed793f
--- /dev/null
+++ b/crates/ide-assists/src/handlers/replace_method_eager_lazy.rs
@@ -0,0 +1,310 @@
+use ide_db::assists::{AssistId, AssistKind};
+use syntax::{
+ ast::{self, make, Expr, HasArgList},
+ AstNode,
+};
+
+use crate::{AssistContext, Assists};
+
+// Assist: replace_with_lazy_method
+//
+// Replace `unwrap_or` with `unwrap_or_else` and `ok_or` with `ok_or_else`.
+//
+// ```
+// # //- minicore:option, fn
+// fn foo() {
+// let a = Some(1);
+// a.unwra$0p_or(2);
+// }
+// ```
+// ->
+// ```
+// fn foo() {
+// let a = Some(1);
+// a.unwrap_or_else(|| 2);
+// }
+// ```
+pub(crate) fn replace_with_lazy_method(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+ let call: ast::MethodCallExpr = ctx.find_node_at_offset()?;
+ let scope = ctx.sema.scope(call.syntax())?;
+
+ let last_arg = call.arg_list()?.args().next()?;
+ let method_name = call.name_ref()?;
+
+ let callable = ctx.sema.resolve_method_call_as_callable(&call)?;
+ let (_, receiver_ty) = callable.receiver_param(ctx.sema.db)?;
+ let n_params = callable.n_params() + 1;
+
+ let method_name_lazy = format!(
+ "{method_name}{}",
+ if method_name.text().ends_with("or") { "_else" } else { "_with" }
+ );
+
+ receiver_ty.iterate_method_candidates_with_traits(
+ ctx.sema.db,
+ &scope,
+ &scope.visible_traits().0,
+ None,
+ None,
+ |func| {
+ let valid = func.name(ctx.sema.db).as_str() == Some(&*method_name_lazy)
+ && func.num_params(ctx.sema.db) == n_params
+ && {
+ let params = func.params_without_self(ctx.sema.db);
+ let last_p = params.first()?;
+ // FIXME: Check that this has the form of `() -> T` where T is the current type of the argument
+ last_p.ty().impls_fnonce(ctx.sema.db)
+ };
+ valid.then_some(func)
+ },
+ )?;
+
+ acc.add(
+ AssistId("replace_with_lazy_method", AssistKind::RefactorRewrite),
+ format!("Replace {method_name} with {method_name_lazy}"),
+ call.syntax().text_range(),
+ |builder| {
+ builder.replace(method_name.syntax().text_range(), method_name_lazy);
+ let closured = into_closure(&last_arg);
+ builder.replace_ast(last_arg, closured);
+ },
+ )
+}
+
+fn into_closure(param: &Expr) -> Expr {
+ (|| {
+ if let ast::Expr::CallExpr(call) = param {
+ if call.arg_list()?.args().count() == 0 {
+ Some(call.expr()?)
+ } else {
+ None
+ }
+ } else {
+ None
+ }
+ })()
+ .unwrap_or_else(|| make::expr_closure(None, param.clone()))
+}
+
+// Assist: replace_with_eager_method
+//
+// Replace `unwrap_or_else` with `unwrap_or` and `ok_or_else` with `ok_or`.
+//
+// ```
+// # //- minicore:option, fn
+// fn foo() {
+// let a = Some(1);
+// a.unwra$0p_or_else(|| 2);
+// }
+// ```
+// ->
+// ```
+// fn foo() {
+// let a = Some(1);
+// a.unwrap_or(2);
+// }
+// ```
+pub(crate) fn replace_with_eager_method(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+ let call: ast::MethodCallExpr = ctx.find_node_at_offset()?;
+ let scope = ctx.sema.scope(call.syntax())?;
+
+ let last_arg = call.arg_list()?.args().next()?;
+ let method_name = call.name_ref()?;
+
+ let callable = ctx.sema.resolve_method_call_as_callable(&call)?;
+ let (_, receiver_ty) = callable.receiver_param(ctx.sema.db)?;
+ let n_params = callable.n_params() + 1;
+ let params = callable.params(ctx.sema.db);
+
+ // FIXME: Check that the arg is of the form `() -> T`
+ if !params.first()?.1.impls_fnonce(ctx.sema.db) {
+ return None;
+ }
+
+ let method_name_text = method_name.text();
+ let method_name_eager = method_name_text
+ .strip_suffix("_else")
+ .or_else(|| method_name_text.strip_suffix("_with"))?;
+
+ receiver_ty.iterate_method_candidates_with_traits(
+ ctx.sema.db,
+ &scope,
+ &scope.visible_traits().0,
+ None,
+ None,
+ |func| {
+ let valid = func.name(ctx.sema.db).as_str() == Some(&*method_name_eager)
+ && func.num_params(ctx.sema.db) == n_params;
+ valid.then_some(func)
+ },
+ )?;
+
+ acc.add(
+ AssistId("replace_with_eager_method", AssistKind::RefactorRewrite),
+ format!("Replace {method_name} with {method_name_eager}"),
+ call.syntax().text_range(),
+ |builder| {
+ builder.replace(method_name.syntax().text_range(), method_name_eager);
+ let called = into_call(&last_arg);
+ builder.replace_ast(last_arg, called);
+ },
+ )
+}
+
+fn into_call(param: &Expr) -> Expr {
+ (|| {
+ if let ast::Expr::ClosureExpr(closure) = param {
+ if closure.param_list()?.params().count() == 0 {
+ Some(closure.body()?)
+ } else {
+ None
+ }
+ } else {
+ None
+ }
+ })()
+ .unwrap_or_else(|| make::expr_call(param.clone(), make::arg_list(Vec::new())))
+}
+
+#[cfg(test)]
+mod tests {
+ use crate::tests::check_assist;
+
+ use super::*;
+
+ #[test]
+ fn replace_or_with_or_else_simple() {
+ check_assist(
+ replace_with_lazy_method,
+ r#"
+//- minicore: option, fn
+fn foo() {
+ let foo = Some(1);
+ return foo.unwrap_$0or(2);
+}
+"#,
+ r#"
+fn foo() {
+ let foo = Some(1);
+ return foo.unwrap_or_else(|| 2);
+}
+"#,
+ )
+ }
+
+ #[test]
+ fn replace_or_with_or_else_call() {
+ check_assist(
+ replace_with_lazy_method,
+ r#"
+//- minicore: option, fn
+fn foo() {
+ let foo = Some(1);
+ return foo.unwrap_$0or(x());
+}
+"#,
+ r#"
+fn foo() {
+ let foo = Some(1);
+ return foo.unwrap_or_else(x);
+}
+"#,
+ )
+ }
+
+ #[test]
+ fn replace_or_with_or_else_block() {
+ check_assist(
+ replace_with_lazy_method,
+ r#"
+//- minicore: option, fn
+fn foo() {
+ let foo = Some(1);
+ return foo.unwrap_$0or({
+ let mut x = bar();
+ for i in 0..10 {
+ x += i;
+ }
+ x
+ });
+}
+"#,
+ r#"
+fn foo() {
+ let foo = Some(1);
+ return foo.unwrap_or_else(|| {
+ let mut x = bar();
+ for i in 0..10 {
+ x += i;
+ }
+ x
+ });
+}
+"#,
+ )
+ }
+
+ #[test]
+ fn replace_or_else_with_or_simple() {
+ check_assist(
+ replace_with_eager_method,
+ r#"
+//- minicore: option, fn
+fn foo() {
+ let foo = Some(1);
+ return foo.unwrap_$0or_else(|| 2);
+}
+"#,
+ r#"
+fn foo() {
+ let foo = Some(1);
+ return foo.unwrap_or(2);
+}
+"#,
+ )
+ }
+
+ #[test]
+ fn replace_or_else_with_or_call() {
+ check_assist(
+ replace_with_eager_method,
+ r#"
+//- minicore: option, fn
+fn foo() {
+ let foo = Some(1);
+ return foo.unwrap_$0or_else(x);
+}
+
+fn x() -> i32 { 0 }
+"#,
+ r#"
+fn foo() {
+ let foo = Some(1);
+ return foo.unwrap_or(x());
+}
+
+fn x() -> i32 { 0 }
+"#,
+ )
+ }
+
+ #[test]
+ fn replace_or_else_with_or_map() {
+ check_assist(
+ replace_with_eager_method,
+ r#"
+//- minicore: option, fn
+fn foo() {
+ let foo = Some("foo");
+ return foo.map$0_or_else(|| 42, |v| v.len());
+}
+"#,
+ r#"
+fn foo() {
+ let foo = Some("foo");
+ return foo.map_or(42, |v| v.len());
+}
+"#,
+ )
+ }
+}
diff --git a/crates/ide-assists/src/handlers/replace_or_with_or_else.rs b/crates/ide-assists/src/handlers/replace_or_with_or_else.rs
deleted file mode 100644
index f0ed3c4fe6..0000000000
--- a/crates/ide-assists/src/handlers/replace_or_with_or_else.rs
+++ /dev/null
@@ -1,364 +0,0 @@
-use ide_db::{
- assists::{AssistId, AssistKind},
- famous_defs::FamousDefs,
-};
-use syntax::{
- ast::{self, make, Expr, HasArgList},
- AstNode,
-};
-
-use crate::{AssistContext, Assists};
-
-// Assist: replace_or_with_or_else
-//
-// Replace `unwrap_or` with `unwrap_or_else` and `ok_or` with `ok_or_else`.
-//
-// ```
-// # //- minicore:option
-// fn foo() {
-// let a = Some(1);
-// a.unwra$0p_or(2);
-// }
-// ```
-// ->
-// ```
-// fn foo() {
-// let a = Some(1);
-// a.unwrap_or_else(|| 2);
-// }
-// ```
-pub(crate) fn replace_or_with_or_else(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
- let call: ast::MethodCallExpr = ctx.find_node_at_offset()?;
-
- let kind = is_option_or_result(call.receiver()?, ctx)?;
-
- let (name, arg_list) = (call.name_ref()?, call.arg_list()?);
-
- let mut map_or = false;
-
- let replace = match &*name.text() {
- "unwrap_or" => "unwrap_or_else".to_string(),
- "or" => "or_else".to_string(),
- "ok_or" if kind == Kind::Option => "ok_or_else".to_string(),
- "map_or" => {
- map_or = true;
- "map_or_else".to_string()
- }
- _ => return None,
- };
-
- let arg = match arg_list.args().collect::<Vec<_>>().as_slice() {
- [] => make::arg_list(Vec::new()),
- [first] => {
- let param = into_closure(first);
- make::arg_list(vec![param])
- }
- [first, second] if map_or => {
- let param = into_closure(first);
- make::arg_list(vec![param, second.clone()])
- }
- _ => return None,
- };
-
- acc.add(
- AssistId("replace_or_with_or_else", AssistKind::RefactorRewrite),
- format!("Replace {name} with {replace}"),
- call.syntax().text_range(),
- |builder| {
- builder.replace(name.syntax().text_range(), replace);
- builder.replace_ast(arg_list, arg)
- },
- )
-}
-
-fn into_closure(param: &Expr) -> Expr {
- (|| {
- if let ast::Expr::CallExpr(call) = param {
- if call.arg_list()?.args().count() == 0 {
- Some(call.expr()?)
- } else {
- None
- }
- } else {
- None
- }
- })()
- .unwrap_or_else(|| make::expr_closure(None, param.clone()))
-}
-
-// Assist: replace_or_else_with_or
-//
-// Replace `unwrap_or_else` with `unwrap_or` and `ok_or_else` with `ok_or`.
-//
-// ```
-// # //- minicore:option
-// fn foo() {
-// let a = Some(1);
-// a.unwra$0p_or_else(|| 2);
-// }
-// ```
-// ->
-// ```
-// fn foo() {
-// let a = Some(1);
-// a.unwrap_or(2);
-// }
-// ```
-pub(crate) fn replace_or_else_with_or(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
- let call: ast::MethodCallExpr = ctx.find_node_at_offset()?;
-
- let kind = is_option_or_result(call.receiver()?, ctx)?;
-
- let (name, arg_list) = (call.name_ref()?, call.arg_list()?);
-
- let mut map_or = false;
- let replace = match &*name.text() {
- "unwrap_or_else" => "unwrap_or".to_string(),
- "or_else" => "or".to_string(),
- "ok_or_else" if kind == Kind::Option => "ok_or".to_string(),
- "map_or_else" => {
- map_or = true;
- "map_or".to_string()
- }
- _ => return None,
- };
-
- let arg = match arg_list.args().collect::<Vec<_>>().as_slice() {
- [] => make::arg_list(Vec::new()),
- [first] => {
- let param = into_call(first);
- make::arg_list(vec![param])
- }
- [first, second] if map_or => {
- let param = into_call(first);
- make::arg_list(vec![param, second.clone()])
- }
- _ => return None,
- };
-
- acc.add(
- AssistId("replace_or_else_with_or", AssistKind::RefactorRewrite),
- format!("Replace {name} with {replace}"),
- call.syntax().text_range(),
- |builder| {
- builder.replace(name.syntax().text_range(), replace);
- builder.replace_ast(arg_list, arg)
- },
- )
-}
-
-fn into_call(param: &Expr) -> Expr {
- (|| {
- if let ast::Expr::ClosureExpr(closure) = param {
- if closure.param_list()?.params().count() == 0 {
- Some(closure.body()?)
- } else {
- None
- }
- } else {
- None
- }
- })()
- .unwrap_or_else(|| make::expr_call(param.clone(), make::arg_list(Vec::new())))
-}
-
-#[derive(PartialEq, Eq)]
-enum Kind {
- Option,
- Result,
-}
-
-fn is_option_or_result(receiver: Expr, ctx: &AssistContext<'_>) -> Option<Kind> {
- let ty = ctx.sema.type_of_expr(&receiver)?.adjusted().as_adt()?.as_enum()?;
- let option_enum =
- FamousDefs(&ctx.sema, ctx.sema.scope(receiver.syntax())?.krate()).core_option_Option();
-
- if let Some(option_enum) = option_enum {
- if ty == option_enum {
- return Some(Kind::Option);
- }
- }
-
- let result_enum =
- FamousDefs(&ctx.sema, ctx.sema.scope(receiver.syntax())?.krate()).core_result_Result();
-
- if let Some(result_enum) = result_enum {
- if ty == result_enum {
- return Some(Kind::Result);
- }
- }
-
- None
-}
-
-#[cfg(test)]
-mod tests {
- use crate::tests::{check_assist, check_assist_not_applicable};
-
- use super::*;
-
- #[test]
- fn replace_or_with_or_else_simple() {
- check_assist(
- replace_or_with_or_else,
- r#"
-//- minicore: option
-fn foo() {
- let foo = Some(1);
- return foo.unwrap_$0or(2);
-}
-"#,
- r#"
-fn foo() {
- let foo = Some(1);
- return foo.unwrap_or_else(|| 2);
-}
-"#,
- )
- }
-
- #[test]
- fn replace_or_with_or_else_call() {
- check_assist(
- replace_or_with_or_else,
- r#"
-//- minicore: option
-fn foo() {
- let foo = Some(1);
- return foo.unwrap_$0or(x());
-}
-"#,
- r#"
-fn foo() {
- let foo = Some(1);
- return foo.unwrap_or_else(x);
-}
-"#,
- )
- }
-
- #[test]
- fn replace_or_with_or_else_block() {
- check_assist(
- replace_or_with_or_else,
- r#"
-//- minicore: option
-fn foo() {
- let foo = Some(1);
- return foo.unwrap_$0or({
- let mut x = bar();
- for i in 0..10 {
- x += i;
- }
- x
- });
-}
-"#,
- r#"
-fn foo() {
- let foo = Some(1);
- return foo.unwrap_or_else(|| {
- let mut x = bar();
- for i in 0..10 {
- x += i;
- }
- x
- });
-}
-"#,
- )
- }
-
- #[test]
- fn replace_or_else_with_or_simple() {
- check_assist(
- replace_or_else_with_or,
- r#"
-//- minicore: option
-fn foo() {
- let foo = Some(1);
- return foo.unwrap_$0or_else(|| 2);
-}
-"#,
- r#"
-fn foo() {
- let foo = Some(1);
- return foo.unwrap_or(2);
-}
-"#,
- )
- }
-
- #[test]
- fn replace_or_else_with_or_call() {
- check_assist(
- replace_or_else_with_or,
- r#"
-//- minicore: option
-fn foo() {
- let foo = Some(1);
- return foo.unwrap_$0or_else(x);
-}
-"#,
- r#"
-fn foo() {
- let foo = Some(1);
- return foo.unwrap_or(x());
-}
-"#,
- )
- }
-
- #[test]
- fn replace_or_else_with_or_result() {
- check_assist(
- replace_or_else_with_or,
- r#"
-//- minicore: result
-fn foo() {
- let foo = Ok(1);
- return foo.unwrap_$0or_else(x);
-}
-"#,
- r#"
-fn foo() {
- let foo = Ok(1);
- return foo.unwrap_or(x());
-}
-"#,
- )
- }
-
- #[test]
- fn replace_or_else_with_or_map() {
- check_assist(
- replace_or_else_with_or,
- r#"
-//- minicore: result
-fn foo() {
- let foo = Ok("foo");
- return foo.map$0_or_else(|| 42, |v| v.len());
-}
-"#,
- r#"
-fn foo() {
- let foo = Ok("foo");
- return foo.map_or(42, |v| v.len());
-}
-"#,
- )
- }
-
- #[test]
- fn replace_or_else_with_or_not_applicable() {
- check_assist_not_applicable(
- replace_or_else_with_or,
- r#"
-fn foo() {
- let foo = Ok(1);
- return foo.unwrap_$0or_else(x);
-}
-"#,
- )
- }
-}
diff --git a/crates/ide-assists/src/lib.rs b/crates/ide-assists/src/lib.rs
index 276cf5f5dd..4d489b62b5 100644
--- a/crates/ide-assists/src/lib.rs
+++ b/crates/ide-assists/src/lib.rs
@@ -188,7 +188,7 @@ mod handlers {
mod replace_try_expr_with_match;
mod replace_derive_with_manual_impl;
mod replace_if_let_with_match;
- mod replace_or_with_or_else;
+ mod replace_method_eager_lazy;
mod replace_arith_op;
mod introduce_named_generic;
mod replace_let_with_if_let;
@@ -297,8 +297,8 @@ mod handlers {
replace_if_let_with_match::replace_if_let_with_match,
replace_if_let_with_match::replace_match_with_if_let,
replace_let_with_if_let::replace_let_with_if_let,
- replace_or_with_or_else::replace_or_else_with_or,
- replace_or_with_or_else::replace_or_with_or_else,
+ replace_method_eager_lazy::replace_with_eager_method,
+ replace_method_eager_lazy::replace_with_lazy_method,
replace_turbofish_with_explicit_type::replace_turbofish_with_explicit_type,
replace_qualified_name_with_use::replace_qualified_name_with_use,
replace_arith_op::replace_arith_with_wrapping,
diff --git a/crates/ide-assists/src/tests/generated.rs b/crates/ide-assists/src/tests/generated.rs
index 524af20013..e5a8d675a9 100644
--- a/crates/ide-assists/src/tests/generated.rs
+++ b/crates/ide-assists/src/tests/generated.rs
@@ -2314,46 +2314,6 @@ fn handle(action: Action) {
}
#[test]
-fn doctest_replace_or_else_with_or() {
- check_doc_test(
- "replace_or_else_with_or",
- r#####"
-//- minicore:option
-fn foo() {
- let a = Some(1);
- a.unwra$0p_or_else(|| 2);
-}
-"#####,
- r#####"
-fn foo() {
- let a = Some(1);
- a.unwrap_or(2);
-}
-"#####,
- )
-}
-
-#[test]
-fn doctest_replace_or_with_or_else() {
- check_doc_test(
- "replace_or_with_or_else",
- r#####"
-//- minicore:option
-fn foo() {
- let a = Some(1);
- a.unwra$0p_or(2);
-}
-"#####,
- r#####"
-fn foo() {
- let a = Some(1);
- a.unwrap_or_else(|| 2);
-}
-"#####,
- )
-}
-
-#[test]
fn doctest_replace_qualified_name_with_use() {
check_doc_test(
"replace_qualified_name_with_use",
@@ -2428,6 +2388,46 @@ fn main() {
}
#[test]
+fn doctest_replace_with_eager_method() {
+ check_doc_test(
+ "replace_with_eager_method",
+ r#####"
+//- minicore:option, fn
+fn foo() {
+ let a = Some(1);
+ a.unwra$0p_or_else(|| 2);
+}
+"#####,
+ r#####"
+fn foo() {
+ let a = Some(1);
+ a.unwrap_or(2);
+}
+"#####,
+ )
+}
+
+#[test]
+fn doctest_replace_with_lazy_method() {
+ check_doc_test(
+ "replace_with_lazy_method",
+ r#####"
+//- minicore:option, fn
+fn foo() {
+ let a = Some(1);
+ a.unwra$0p_or(2);
+}
+"#####,
+ r#####"
+fn foo() {
+ let a = Some(1);
+ a.unwrap_or_else(|| 2);
+}
+"#####,
+ )
+}
+
+#[test]
fn doctest_sort_items() {
check_doc_test(
"sort_items",
diff --git a/crates/ide-ssr/src/matching.rs b/crates/ide-ssr/src/matching.rs
index 57b5ab6abd..a8e8836908 100644
--- a/crates/ide-ssr/src/matching.rs
+++ b/crates/ide-ssr/src/matching.rs
@@ -561,7 +561,7 @@ impl<'db, 'sema> Matcher<'db, 'sema> {
.sema
.resolve_method_call_as_callable(code)
.and_then(|callable| callable.receiver_param(self.sema.db))
- .map(|self_param| self_param.kind())
+ .map(|(self_param, _)| self_param.kind())
.unwrap_or(ast::SelfParamKind::Owned);
}
}
diff --git a/crates/ide/src/inlay_hints/bind_pat.rs b/crates/ide/src/inlay_hints/bind_pat.rs
index 4af7f9bdb7..6a50927333 100644
--- a/crates/ide/src/inlay_hints/bind_pat.rs
+++ b/crates/ide/src/inlay_hints/bind_pat.rs
@@ -176,15 +176,12 @@ fn pat_is_enum_variant(db: &RootDatabase, bind_pat: &ast::IdentPat, pat_ty: &hir
mod tests {
// This module also contains tests for super::closure_ret
- use expect_test::expect;
use syntax::{TextRange, TextSize};
use test_utils::extract_annotations;
use crate::{fixture, inlay_hints::InlayHintsConfig};
- use crate::inlay_hints::tests::{
- check, check_expect, check_with_config, DISABLED_CONFIG, TEST_CONFIG,
- };
+ use crate::inlay_hints::tests::{check, check_with_config, DISABLED_CONFIG, TEST_CONFIG};
use crate::ClosureReturnTypeHints;
#[track_caller]
@@ -278,8 +275,7 @@ fn main() {
#[test]
fn iterator_hint_regression_issue_12674() {
// Ensure we don't crash while solving the projection type of iterators.
- check_expect(
- InlayHintsConfig { chaining_hints: true, ..DISABLED_CONFIG },
+ let (analysis, file_id) = fixture::file(
r#"
//- minicore: iterators
struct S<T>(T);
@@ -302,107 +298,18 @@ impl<'a, T> Iterator for SliceIter<'a, T> {
fn main(a: SliceIter<'_, Container>) {
a
- .filter_map(|c| Some(c.elements.iter().filter_map(|v| Some(v))))
- .map(|e| e);
+ .filter_map(|c| Some(c.elements.iter().filter_map(|v| Some(v))))
+ .map(|e| e);
}
- "#,
- expect![[r#"
- [
- InlayHint {
- range: 484..554,
- kind: Chaining,
- label: [
- "impl ",
- InlayHintLabelPart {
- text: "Iterator",
- linked_location: Some(
- FileRange {
- file_id: FileId(
- 1,
- ),
- range: 2611..2619,
- },
- ),
- tooltip: "",
- },
- "<",
- InlayHintLabelPart {
- text: "Item",
- linked_location: Some(
- FileRange {
- file_id: FileId(
- 1,
- ),
- range: 2643..2647,
- },
- ),
- tooltip: "",
- },
- " = impl ",
- InlayHintLabelPart {
- text: "Iterator",
- linked_location: Some(
- FileRange {
- file_id: FileId(
- 1,
- ),
- range: 2611..2619,
- },
- ),
- tooltip: "",
- },
- "<",
- InlayHintLabelPart {
- text: "Item",
- linked_location: Some(
- FileRange {
- file_id: FileId(
- 1,
- ),
- range: 2643..2647,
- },
- ),
- tooltip: "",
- },
- " = &&str>>",
- ],
- },
- InlayHint {
- range: 484..485,
- kind: Chaining,
- label: [
- "",
- InlayHintLabelPart {
- text: "SliceIter",
- linked_location: Some(
- FileRange {
- file_id: FileId(
- 0,
- ),
- range: 289..298,
- },
- ),
- tooltip: "",
- },
- "<",
- InlayHintLabelPart {
- text: "Container",
- linked_location: Some(
- FileRange {
- file_id: FileId(
- 0,
- ),
- range: 238..247,
- },
- ),
- tooltip: "",
- },
- ">",
- ],
- },
- ]
- "#]],
+"#,
);
+ analysis
+ .inlay_hints(
+ &InlayHintsConfig { chaining_hints: true, ..DISABLED_CONFIG },
+ file_id,
+ None,
+ )
+ .unwrap();
}
#[test]
diff --git a/crates/ide/src/inlay_hints/chaining.rs b/crates/ide/src/inlay_hints/chaining.rs
index 0c54f084c1..0a7513e465 100644
--- a/crates/ide/src/inlay_hints/chaining.rs
+++ b/crates/ide/src/inlay_hints/chaining.rs
@@ -435,7 +435,7 @@ fn main() {
file_id: FileId(
1,
),
- range: 2611..2619,
+ range: 3386..3394,
},
),
tooltip: "",
@@ -448,7 +448,7 @@ fn main() {
file_id: FileId(
1,
),
- range: 2643..2647,
+ range: 3418..3422,
},
),
tooltip: "",
@@ -468,7 +468,7 @@ fn main() {
file_id: FileId(
1,
),
- range: 2611..2619,
+ range: 3386..3394,
},
),
tooltip: "",
@@ -481,7 +481,7 @@ fn main() {
file_id: FileId(
1,
),
- range: 2643..2647,
+ range: 3418..3422,
},
),
tooltip: "",
@@ -501,7 +501,7 @@ fn main() {
file_id: FileId(
1,
),
- range: 2611..2619,
+ range: 3386..3394,
},
),
tooltip: "",
@@ -514,7 +514,7 @@ fn main() {
file_id: FileId(
1,
),
- range: 2643..2647,
+ range: 3418..3422,
},
),
tooltip: "",
diff --git a/crates/ide/src/signature_help.rs b/crates/ide/src/signature_help.rs
index d32ae83c8f..2c08c457b3 100644
--- a/crates/ide/src/signature_help.rs
+++ b/crates/ide/src/signature_help.rs
@@ -172,7 +172,7 @@ fn signature_help_for_call(
res.signature.push('(');
{
- if let Some(self_param) = callable.receiver_param(db) {
+ if let Some((self_param, _)) = callable.receiver_param(db) {
format_to!(res.signature, "{}", self_param)
}
let mut buf = String::new();
diff --git a/crates/test-utils/src/minicore.rs b/crates/test-utils/src/minicore.rs
index 7b48e42489..b6336e2216 100644
--- a/crates/test-utils/src/minicore.rs
+++ b/crates/test-utils/src/minicore.rs
@@ -534,6 +534,40 @@ pub mod option {
None => panic!("called `Option::unwrap()` on a `None` value"),
}
}
+
+ pub fn and<U>(self, optb: Option<U>) -> Option<U> {
+ loop {}
+ }
+ pub fn unwrap_or(self, default: T) -> T {
+ loop {}
+ }
+ // region:fn
+ pub fn and_then<U, F>(self, f: F) -> Option<U>
+ where
+ F: FnOnce(T) -> Option<U>,
+ {
+ loop {}
+ }
+ pub fn unwrap_or_else<F>(self, f: F) -> T
+ where
+ F: FnOnce() -> T,
+ {
+ loop {}
+ }
+ pub fn map_or<U, F>(self, default: U, f: F) -> U
+ where
+ F: FnOnce(T) -> U,
+ {
+ loop {}
+ }
+ pub fn map_or_else<U, D, F>(self, default: D, f: F) -> U
+ where
+ D: FnOnce() -> U,
+ F: FnOnce(T) -> U,
+ {
+ loop {}
+ }
+ // endregion:fn
}
}
// endregion:option