Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/ide/src/inlay_hints.rs')
| -rw-r--r-- | crates/ide/src/inlay_hints.rs | 178 |
1 files changed, 79 insertions, 99 deletions
diff --git a/crates/ide/src/inlay_hints.rs b/crates/ide/src/inlay_hints.rs index 428b8d1109..d04bd87b7b 100644 --- a/crates/ide/src/inlay_hints.rs +++ b/crates/ide/src/inlay_hints.rs @@ -16,6 +16,7 @@ pub struct InlayHintsConfig { pub type_hints: bool, pub parameter_hints: bool, pub chaining_hints: bool, + pub closure_return_type_hints: bool, pub hide_named_constructor_hints: bool, pub max_length: Option<usize>, } @@ -24,6 +25,7 @@ pub struct InlayHintsConfig { pub enum InlayKind { TypeHint, ParameterHint, + ClosureReturnTypeHint, ChainingHint, } @@ -67,48 +69,86 @@ pub(crate) fn inlay_hints( let file = sema.parse(file_id); let file = file.syntax(); - let mut hints = Vec::new(); + let mut acc = Vec::new(); - let get_hints = |node| get_hints(&mut hints, &sema, config, node); + let hints = |node| hints(&mut acc, &sema, config, node); match range_limit { Some(FileRange { range, .. }) => match file.covering_element(range) { - NodeOrToken::Token(_) => return hints, + NodeOrToken::Token(_) => return acc, NodeOrToken::Node(n) => n .descendants() .filter(|descendant| range.contains_range(descendant.text_range())) - .for_each(get_hints), + .for_each(hints), }, - None => file.descendants().for_each(get_hints), + None => file.descendants().for_each(hints), }; - hints + acc } -fn get_hints( +fn hints( hints: &mut Vec<InlayHint>, sema: &Semantics<RootDatabase>, config: &InlayHintsConfig, node: SyntaxNode, ) { + let krate = sema.scope(&node).module().map(|it| it.krate()); + let famous_defs = FamousDefs(sema, krate); if let Some(expr) = ast::Expr::cast(node.clone()) { - get_chaining_hints(hints, sema, config, &expr); + chaining_hints(hints, sema, &famous_defs, config, &expr); match expr { ast::Expr::CallExpr(it) => { - get_param_name_hints(hints, sema, config, ast::Expr::from(it)); + param_name_hints(hints, sema, config, ast::Expr::from(it)); } ast::Expr::MethodCallExpr(it) => { - get_param_name_hints(hints, sema, config, ast::Expr::from(it)); + param_name_hints(hints, sema, config, ast::Expr::from(it)); + } + ast::Expr::ClosureExpr(it) => { + closure_ret_hints(hints, sema, &famous_defs, config, it); } _ => (), } } else if let Some(it) = ast::IdentPat::cast(node) { - get_bind_pat_hints(hints, sema, config, &it); + bind_pat_hints(hints, sema, config, &it); } } -fn get_chaining_hints( +fn closure_ret_hints( acc: &mut Vec<InlayHint>, sema: &Semantics<RootDatabase>, + famous_defs: &FamousDefs, + config: &InlayHintsConfig, + closure: ast::ClosureExpr, +) -> Option<()> { + if !config.closure_return_type_hints { + return None; + } + + let closure = sema.descend_node_into_attributes(closure.clone()).pop()?; + + let param_list = match closure.body() { + Some(ast::Expr::BlockExpr(_)) => closure.param_list()?, + _ => return None, + }; + let ty = sema.type_of_expr(&ast::Expr::ClosureExpr(closure))?.adjusted(); + let callable = ty.as_callable(sema.db)?; + let ty = callable.return_type(); + if ty.is_unit() { + return None; + } + acc.push(InlayHint { + range: param_list.syntax().text_range(), + kind: InlayKind::ClosureReturnTypeHint, + label: hint_iterator(sema, &famous_defs, config, &ty) + .unwrap_or_else(|| ty.display_truncated(sema.db, config.max_length).to_string().into()), + }); + Some(()) +} + +fn chaining_hints( + acc: &mut Vec<InlayHint>, + sema: &Semantics<RootDatabase>, + famous_defs: &FamousDefs, config: &InlayHintsConfig, expr: &ast::Expr, ) -> Option<()> { @@ -122,8 +162,6 @@ fn get_chaining_hints( let descended = sema.descend_node_into_attributes(expr.clone()).pop(); let desc_expr = descended.as_ref().unwrap_or(expr); - let krate = sema.scope(desc_expr.syntax()).module().map(|it| it.krate()); - let famous_defs = FamousDefs(sema, krate); let mut tokens = expr .syntax() @@ -167,7 +205,7 @@ fn get_chaining_hints( Some(()) } -fn get_param_name_hints( +fn param_name_hints( acc: &mut Vec<InlayHint>, sema: &Semantics<RootDatabase>, config: &InlayHintsConfig, @@ -207,7 +245,7 @@ fn get_param_name_hints( Some(()) } -fn get_bind_pat_hints( +fn bind_pat_hints( acc: &mut Vec<InlayHint>, sema: &Semantics<RootDatabase>, config: &InlayHintsConfig, @@ -567,13 +605,21 @@ mod tests { use crate::{fixture, inlay_hints::InlayHintsConfig}; + const DISABLED_CONFIG: InlayHintsConfig = InlayHintsConfig { + render_colons: false, + type_hints: false, + parameter_hints: false, + chaining_hints: false, + hide_named_constructor_hints: false, + closure_return_type_hints: false, + max_length: None, + }; const TEST_CONFIG: InlayHintsConfig = InlayHintsConfig { - render_colons: true, type_hints: true, parameter_hints: true, chaining_hints: true, - hide_named_constructor_hints: false, - max_length: None, + closure_return_type_hints: true, + ..DISABLED_CONFIG }; #[track_caller] @@ -584,46 +630,19 @@ mod tests { #[track_caller] fn check_params(ra_fixture: &str) { check_with_config( - InlayHintsConfig { - render_colons: true, - parameter_hints: true, - type_hints: false, - chaining_hints: false, - hide_named_constructor_hints: false, - max_length: None, - }, + InlayHintsConfig { parameter_hints: true, ..DISABLED_CONFIG }, ra_fixture, ); } #[track_caller] fn check_types(ra_fixture: &str) { - check_with_config( - InlayHintsConfig { - render_colons: true, - parameter_hints: false, - type_hints: true, - chaining_hints: false, - hide_named_constructor_hints: false, - max_length: None, - }, - ra_fixture, - ); + check_with_config(InlayHintsConfig { type_hints: true, ..DISABLED_CONFIG }, ra_fixture); } #[track_caller] fn check_chains(ra_fixture: &str) { - check_with_config( - InlayHintsConfig { - render_colons: true, - parameter_hints: false, - type_hints: false, - chaining_hints: true, - hide_named_constructor_hints: false, - max_length: None, - }, - ra_fixture, - ); + check_with_config(InlayHintsConfig { chaining_hints: true, ..DISABLED_CONFIG }, ra_fixture); } #[track_caller] @@ -646,14 +665,7 @@ mod tests { #[test] fn hints_disabled() { check_with_config( - InlayHintsConfig { - render_colons: true, - type_hints: false, - parameter_hints: false, - chaining_hints: false, - hide_named_constructor_hints: false, - max_length: None, - }, + InlayHintsConfig { render_colons: true, ..DISABLED_CONFIG }, r#" fn foo(a: i32, b: i32) -> i32 { a + b } fn main() { @@ -1102,14 +1114,7 @@ fn main() { let expected = extract_annotations(&*analysis.file_text(file_id).unwrap()); let inlay_hints = analysis .inlay_hints( - &InlayHintsConfig { - render_colons: true, - parameter_hints: false, - type_hints: true, - chaining_hints: false, - hide_named_constructor_hints: false, - max_length: None, - }, + &InlayHintsConfig { type_hints: true, ..DISABLED_CONFIG }, file_id, Some(FileRange { file_id, @@ -1418,7 +1423,7 @@ fn main() { parameter_hints: true, chaining_hints: true, hide_named_constructor_hints: true, - max_length: None, + ..DISABLED_CONFIG }, r#" //- minicore: try, option @@ -1546,13 +1551,14 @@ fn fallible() -> ControlFlow<()> { fn main() { let mut start = 0; //^^^^^ i32 - (0..2).for_each(|increment| { start += increment; }); + (0..2).for_each(|increment | { start += increment; }); //^^^^^^^^^ i32 let multiply = //^^^^^^^^ |i32, i32| -> i32 | a, b| a * b //^ i32 ^ i32 + ; let _: i32 = multiply(1, 2); @@ -1561,6 +1567,8 @@ fn main() { let return_42 = || 42; //^^^^^^^^^ || -> i32 + || { 42 }; + //^^ i32 }"#, ); } @@ -1590,14 +1598,7 @@ fn main() { #[test] fn chaining_hints_ignore_comments() { check_expect( - InlayHintsConfig { - render_colons: true, - parameter_hints: false, - type_hints: false, - chaining_hints: true, - hide_named_constructor_hints: false, - max_length: None, - }, + InlayHintsConfig { type_hints: false, chaining_hints: true, ..DISABLED_CONFIG }, r#" struct A(B); impl A { fn into_b(self) -> B { self.0 } } @@ -1648,14 +1649,7 @@ fn main() { #[test] fn struct_access_chaining_hints() { check_expect( - InlayHintsConfig { - render_colons: true, - parameter_hints: false, - type_hints: false, - chaining_hints: true, - hide_named_constructor_hints: false, - max_length: None, - }, + InlayHintsConfig { chaining_hints: true, ..DISABLED_CONFIG }, r#" struct A { pub b: B } struct B { pub c: C } @@ -1694,14 +1688,7 @@ fn main() { #[test] fn generic_chaining_hints() { check_expect( - InlayHintsConfig { - render_colons: true, - parameter_hints: false, - type_hints: false, - chaining_hints: true, - hide_named_constructor_hints: false, - max_length: None, - }, + InlayHintsConfig { chaining_hints: true, ..DISABLED_CONFIG }, r#" struct A<T>(T); struct B<T>(T); @@ -1741,14 +1728,7 @@ fn main() { #[test] fn shorten_iterator_chaining_hints() { check_expect( - InlayHintsConfig { - render_colons: true, - parameter_hints: false, - type_hints: false, - chaining_hints: true, - hide_named_constructor_hints: false, - max_length: None, - }, + InlayHintsConfig { chaining_hints: true, ..DISABLED_CONFIG }, r#" //- minicore: iterators use core::iter; |