about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--crates/ide/src/inlay_hints.rs178
-rw-r--r--crates/ide/src/static_index.rs1
-rw-r--r--crates/rust-analyzer/src/config.rs7
-rw-r--r--crates/rust-analyzer/src/to_proto.rs13
-rw-r--r--docs/user/generated_config.adoc15
-rw-r--r--editors/code/package.json15
-rw-r--r--editors/code/src/config.ts1
-rw-r--r--editors/code/src/inlay_hints.ts3
8 files changed, 117 insertions, 116 deletions
diff --git a/crates/ide/src/inlay_hints.rs b/crates/ide/src/inlay_hints.rs
index 428b8d1109f..d04bd87b7bc 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;
diff --git a/crates/ide/src/static_index.rs b/crates/ide/src/static_index.rs
index ef3487545ca..a06793cf516 100644
--- a/crates/ide/src/static_index.rs
+++ b/crates/ide/src/static_index.rs
@@ -109,6 +109,7 @@ impl StaticIndex<'_> {
                     type_hints: true,
                     parameter_hints: true,
                     chaining_hints: true,
+                    closure_return_type_hints: true,
                     hide_named_constructor_hints: false,
                     max_length: Some(25),
                 },
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs
index a93e18a9b09..3d5737316bf 100644
--- a/crates/rust-analyzer/src/config.rs
+++ b/crates/rust-analyzer/src/config.rs
@@ -244,8 +244,6 @@ config_data! {
 
         /// Whether to render trailing colons for parameter hints, and trailing colons for parameter hints.
         inlayHints_renderColons: bool               = "true",
-        /// Whether to show inlay type hints for method chains.
-        inlayHints_chainingHints: bool              = "true",
         /// Maximum length for inlay hints. Set to null to have an unlimited length.
         inlayHints_maxLength: Option<usize>         = "25",
         /// Whether to show function parameter name inlay hints at the call
@@ -253,6 +251,10 @@ config_data! {
         inlayHints_parameterHints: bool             = "true",
         /// Whether to show inlay type hints for variables.
         inlayHints_typeHints: bool                  = "true",
+        /// Whether to show inlay type hints for method chains.
+        inlayHints_chainingHints: bool              = "true",
+        /// Whether to show inlay type hints for return types of closures with blocks.
+        inlayHints_closureReturnTypeHints: bool     = "false",
         /// Whether to hide inlay hints for constructors.
         inlayHints_hideNamedConstructorHints: bool  = "false",
 
@@ -852,6 +854,7 @@ impl Config {
             type_hints: self.data.inlayHints_typeHints,
             parameter_hints: self.data.inlayHints_parameterHints,
             chaining_hints: self.data.inlayHints_chainingHints,
+            closure_return_type_hints: self.data.inlayHints_closureReturnTypeHints,
             hide_named_constructor_hints: self.data.inlayHints_hideNamedConstructorHints,
             max_length: self.data.inlayHints_maxLength,
         }
diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs
index 6a1c2f56ca5..e9e8bdb6c5f 100644
--- a/crates/rust-analyzer/src/to_proto.rs
+++ b/crates/rust-analyzer/src/to_proto.rs
@@ -422,26 +422,31 @@ pub(crate) fn inlay_hint(
         label: lsp_ext::InlayHintLabel::String(match inlay_hint.kind {
             InlayKind::ParameterHint if render_colons => format!("{}:", inlay_hint.label),
             InlayKind::TypeHint if render_colons => format!(": {}", inlay_hint.label),
+            InlayKind::ClosureReturnTypeHint => format!(" -> {}", inlay_hint.label),
             _ => inlay_hint.label.to_string(),
         }),
         position: match inlay_hint.kind {
             InlayKind::ParameterHint => position(line_index, inlay_hint.range.start()),
-            InlayKind::TypeHint | InlayKind::ChainingHint => {
+            InlayKind::ClosureReturnTypeHint | InlayKind::TypeHint | InlayKind::ChainingHint => {
                 position(line_index, inlay_hint.range.end())
             }
         },
         kind: match inlay_hint.kind {
             InlayKind::ParameterHint => Some(lsp_ext::InlayHintKind::PARAMETER),
-            InlayKind::TypeHint | InlayKind::ChainingHint => Some(lsp_ext::InlayHintKind::TYPE),
+            InlayKind::ClosureReturnTypeHint | InlayKind::TypeHint | InlayKind::ChainingHint => {
+                Some(lsp_ext::InlayHintKind::TYPE)
+            }
         },
         tooltip: None,
         padding_left: Some(match inlay_hint.kind {
             InlayKind::TypeHint => !render_colons,
-            InlayKind::ParameterHint => false,
+            InlayKind::ParameterHint | InlayKind::ClosureReturnTypeHint => false,
             InlayKind::ChainingHint => true,
         }),
         padding_right: Some(match inlay_hint.kind {
-            InlayKind::TypeHint | InlayKind::ChainingHint => false,
+            InlayKind::TypeHint | InlayKind::ChainingHint | InlayKind::ClosureReturnTypeHint => {
+                false
+            }
             InlayKind::ParameterHint => true,
         }),
     }
diff --git a/docs/user/generated_config.adoc b/docs/user/generated_config.adoc
index dd819abdd29..5793b4c057d 100644
--- a/docs/user/generated_config.adoc
+++ b/docs/user/generated_config.adoc
@@ -352,11 +352,6 @@ Whether to show `Run` action. Only applies when
 --
 Whether to render trailing colons for parameter hints, and trailing colons for parameter hints.
 --
-[[rust-analyzer.inlayHints.chainingHints]]rust-analyzer.inlayHints.chainingHints (default: `true`)::
-+
---
-Whether to show inlay type hints for method chains.
---
 [[rust-analyzer.inlayHints.maxLength]]rust-analyzer.inlayHints.maxLength (default: `25`)::
 +
 --
@@ -373,6 +368,16 @@ site.
 --
 Whether to show inlay type hints for variables.
 --
+[[rust-analyzer.inlayHints.chainingHints]]rust-analyzer.inlayHints.chainingHints (default: `true`)::
++
+--
+Whether to show inlay type hints for method chains.
+--
+[[rust-analyzer.inlayHints.closureReturnTypeHints]]rust-analyzer.inlayHints.closureReturnTypeHints (default: `false`)::
++
+--
+Whether to show inlay type hints for return types of closures with blocks.
+--
 [[rust-analyzer.inlayHints.hideNamedConstructorHints]]rust-analyzer.inlayHints.hideNamedConstructorHints (default: `false`)::
 +
 --
diff --git a/editors/code/package.json b/editors/code/package.json
index 6ccd6af80c0..83158197741 100644
--- a/editors/code/package.json
+++ b/editors/code/package.json
@@ -771,11 +771,6 @@
                     "default": true,
                     "type": "boolean"
                 },
-                "rust-analyzer.inlayHints.chainingHints": {
-                    "markdownDescription": "Whether to show inlay type hints for method chains.",
-                    "default": true,
-                    "type": "boolean"
-                },
                 "rust-analyzer.inlayHints.maxLength": {
                     "markdownDescription": "Maximum length for inlay hints. Set to null to have an unlimited length.",
                     "default": 25,
@@ -795,6 +790,16 @@
                     "default": true,
                     "type": "boolean"
                 },
+                "rust-analyzer.inlayHints.chainingHints": {
+                    "markdownDescription": "Whether to show inlay type hints for method chains.",
+                    "default": true,
+                    "type": "boolean"
+                },
+                "rust-analyzer.inlayHints.closureReturnTypeHints": {
+                    "markdownDescription": "Whether to show inlay type hints for return types of closures with blocks.",
+                    "default": false,
+                    "type": "boolean"
+                },
                 "rust-analyzer.inlayHints.hideNamedConstructorHints": {
                     "markdownDescription": "Whether to hide inlay hints for constructors.",
                     "default": false,
diff --git a/editors/code/src/config.ts b/editors/code/src/config.ts
index cdbaa67fe22..5d3ef404594 100644
--- a/editors/code/src/config.ts
+++ b/editors/code/src/config.ts
@@ -103,6 +103,7 @@ export class Config {
             typeHints: this.get<boolean>("inlayHints.typeHints"),
             parameterHints: this.get<boolean>("inlayHints.parameterHints"),
             chainingHints: this.get<boolean>("inlayHints.chainingHints"),
+            closureReturnTypeHints: this.get<boolean>("inlayHints.closureReturnTypeHints"),
             hideNamedConstructorHints: this.get<boolean>("inlayHints.hideNamedConstructorHints"),
             smallerHints: this.get<boolean>("inlayHints.smallerHints"),
             maxLength: this.get<null | number>("inlayHints.maxLength"),
diff --git a/editors/code/src/inlay_hints.ts b/editors/code/src/inlay_hints.ts
index 441370a677a..3f7ddedddf3 100644
--- a/editors/code/src/inlay_hints.ts
+++ b/editors/code/src/inlay_hints.ts
@@ -14,7 +14,8 @@ export function activateInlayHints(ctx: Ctx) {
 
             const anyEnabled = ctx.config.inlayHints.typeHints
                 || ctx.config.inlayHints.parameterHints
-                || ctx.config.inlayHints.chainingHints;
+                || ctx.config.inlayHints.chainingHints
+                || ctx.config.inlayHints.closureReturnTypeHints;
             const enabled = ctx.config.inlayHints.enable && anyEnabled;
             if (!enabled) return;