Skip to content
This repository was archived by the owner on May 28, 2025. It is now read-only.

Commit bd17933

Browse files
committed
feat: Add return type hints for closures with block bodies
1 parent a40a847 commit bd17933

File tree

8 files changed

+117
-116
lines changed

8 files changed

+117
-116
lines changed

crates/ide/src/inlay_hints.rs

Lines changed: 79 additions & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ pub struct InlayHintsConfig {
1616
pub type_hints: bool,
1717
pub parameter_hints: bool,
1818
pub chaining_hints: bool,
19+
pub closure_return_type_hints: bool,
1920
pub hide_named_constructor_hints: bool,
2021
pub max_length: Option<usize>,
2122
}
@@ -24,6 +25,7 @@ pub struct InlayHintsConfig {
2425
pub enum InlayKind {
2526
TypeHint,
2627
ParameterHint,
28+
ClosureReturnTypeHint,
2729
ChainingHint,
2830
}
2931

@@ -67,48 +69,86 @@ pub(crate) fn inlay_hints(
6769
let file = sema.parse(file_id);
6870
let file = file.syntax();
6971

70-
let mut hints = Vec::new();
72+
let mut acc = Vec::new();
7173

72-
let get_hints = |node| get_hints(&mut hints, &sema, config, node);
74+
let hints = |node| hints(&mut acc, &sema, config, node);
7375
match range_limit {
7476
Some(FileRange { range, .. }) => match file.covering_element(range) {
75-
NodeOrToken::Token(_) => return hints,
77+
NodeOrToken::Token(_) => return acc,
7678
NodeOrToken::Node(n) => n
7779
.descendants()
7880
.filter(|descendant| range.contains_range(descendant.text_range()))
79-
.for_each(get_hints),
81+
.for_each(hints),
8082
},
81-
None => file.descendants().for_each(get_hints),
83+
None => file.descendants().for_each(hints),
8284
};
8385

84-
hints
86+
acc
8587
}
8688

87-
fn get_hints(
89+
fn hints(
8890
hints: &mut Vec<InlayHint>,
8991
sema: &Semantics<RootDatabase>,
9092
config: &InlayHintsConfig,
9193
node: SyntaxNode,
9294
) {
95+
let krate = sema.scope(&node).module().map(|it| it.krate());
96+
let famous_defs = FamousDefs(sema, krate);
9397
if let Some(expr) = ast::Expr::cast(node.clone()) {
94-
get_chaining_hints(hints, sema, config, &expr);
98+
chaining_hints(hints, sema, &famous_defs, config, &expr);
9599
match expr {
96100
ast::Expr::CallExpr(it) => {
97-
get_param_name_hints(hints, sema, config, ast::Expr::from(it));
101+
param_name_hints(hints, sema, config, ast::Expr::from(it));
98102
}
99103
ast::Expr::MethodCallExpr(it) => {
100-
get_param_name_hints(hints, sema, config, ast::Expr::from(it));
104+
param_name_hints(hints, sema, config, ast::Expr::from(it));
105+
}
106+
ast::Expr::ClosureExpr(it) => {
107+
closure_ret_hints(hints, sema, &famous_defs, config, it);
101108
}
102109
_ => (),
103110
}
104111
} else if let Some(it) = ast::IdentPat::cast(node) {
105-
get_bind_pat_hints(hints, sema, config, &it);
112+
bind_pat_hints(hints, sema, config, &it);
106113
}
107114
}
108115

109-
fn get_chaining_hints(
116+
fn closure_ret_hints(
110117
acc: &mut Vec<InlayHint>,
111118
sema: &Semantics<RootDatabase>,
119+
famous_defs: &FamousDefs,
120+
config: &InlayHintsConfig,
121+
closure: ast::ClosureExpr,
122+
) -> Option<()> {
123+
if !config.closure_return_type_hints {
124+
return None;
125+
}
126+
127+
let closure = sema.descend_node_into_attributes(closure.clone()).pop()?;
128+
129+
let param_list = match closure.body() {
130+
Some(ast::Expr::BlockExpr(_)) => closure.param_list()?,
131+
_ => return None,
132+
};
133+
let ty = sema.type_of_expr(&ast::Expr::ClosureExpr(closure))?.adjusted();
134+
let callable = ty.as_callable(sema.db)?;
135+
let ty = callable.return_type();
136+
if ty.is_unit() {
137+
return None;
138+
}
139+
acc.push(InlayHint {
140+
range: param_list.syntax().text_range(),
141+
kind: InlayKind::ClosureReturnTypeHint,
142+
label: hint_iterator(sema, &famous_defs, config, &ty)
143+
.unwrap_or_else(|| ty.display_truncated(sema.db, config.max_length).to_string().into()),
144+
});
145+
Some(())
146+
}
147+
148+
fn chaining_hints(
149+
acc: &mut Vec<InlayHint>,
150+
sema: &Semantics<RootDatabase>,
151+
famous_defs: &FamousDefs,
112152
config: &InlayHintsConfig,
113153
expr: &ast::Expr,
114154
) -> Option<()> {
@@ -122,8 +162,6 @@ fn get_chaining_hints(
122162

123163
let descended = sema.descend_node_into_attributes(expr.clone()).pop();
124164
let desc_expr = descended.as_ref().unwrap_or(expr);
125-
let krate = sema.scope(desc_expr.syntax()).module().map(|it| it.krate());
126-
let famous_defs = FamousDefs(sema, krate);
127165

128166
let mut tokens = expr
129167
.syntax()
@@ -167,7 +205,7 @@ fn get_chaining_hints(
167205
Some(())
168206
}
169207

170-
fn get_param_name_hints(
208+
fn param_name_hints(
171209
acc: &mut Vec<InlayHint>,
172210
sema: &Semantics<RootDatabase>,
173211
config: &InlayHintsConfig,
@@ -207,7 +245,7 @@ fn get_param_name_hints(
207245
Some(())
208246
}
209247

210-
fn get_bind_pat_hints(
248+
fn bind_pat_hints(
211249
acc: &mut Vec<InlayHint>,
212250
sema: &Semantics<RootDatabase>,
213251
config: &InlayHintsConfig,
@@ -567,13 +605,21 @@ mod tests {
567605

568606
use crate::{fixture, inlay_hints::InlayHintsConfig};
569607

608+
const DISABLED_CONFIG: InlayHintsConfig = InlayHintsConfig {
609+
render_colons: false,
610+
type_hints: false,
611+
parameter_hints: false,
612+
chaining_hints: false,
613+
hide_named_constructor_hints: false,
614+
closure_return_type_hints: false,
615+
max_length: None,
616+
};
570617
const TEST_CONFIG: InlayHintsConfig = InlayHintsConfig {
571-
render_colons: true,
572618
type_hints: true,
573619
parameter_hints: true,
574620
chaining_hints: true,
575-
hide_named_constructor_hints: false,
576-
max_length: None,
621+
closure_return_type_hints: true,
622+
..DISABLED_CONFIG
577623
};
578624

579625
#[track_caller]
@@ -584,46 +630,19 @@ mod tests {
584630
#[track_caller]
585631
fn check_params(ra_fixture: &str) {
586632
check_with_config(
587-
InlayHintsConfig {
588-
render_colons: true,
589-
parameter_hints: true,
590-
type_hints: false,
591-
chaining_hints: false,
592-
hide_named_constructor_hints: false,
593-
max_length: None,
594-
},
633+
InlayHintsConfig { parameter_hints: true, ..DISABLED_CONFIG },
595634
ra_fixture,
596635
);
597636
}
598637

599638
#[track_caller]
600639
fn check_types(ra_fixture: &str) {
601-
check_with_config(
602-
InlayHintsConfig {
603-
render_colons: true,
604-
parameter_hints: false,
605-
type_hints: true,
606-
chaining_hints: false,
607-
hide_named_constructor_hints: false,
608-
max_length: None,
609-
},
610-
ra_fixture,
611-
);
640+
check_with_config(InlayHintsConfig { type_hints: true, ..DISABLED_CONFIG }, ra_fixture);
612641
}
613642

614643
#[track_caller]
615644
fn check_chains(ra_fixture: &str) {
616-
check_with_config(
617-
InlayHintsConfig {
618-
render_colons: true,
619-
parameter_hints: false,
620-
type_hints: false,
621-
chaining_hints: true,
622-
hide_named_constructor_hints: false,
623-
max_length: None,
624-
},
625-
ra_fixture,
626-
);
645+
check_with_config(InlayHintsConfig { chaining_hints: true, ..DISABLED_CONFIG }, ra_fixture);
627646
}
628647

629648
#[track_caller]
@@ -646,14 +665,7 @@ mod tests {
646665
#[test]
647666
fn hints_disabled() {
648667
check_with_config(
649-
InlayHintsConfig {
650-
render_colons: true,
651-
type_hints: false,
652-
parameter_hints: false,
653-
chaining_hints: false,
654-
hide_named_constructor_hints: false,
655-
max_length: None,
656-
},
668+
InlayHintsConfig { render_colons: true, ..DISABLED_CONFIG },
657669
r#"
658670
fn foo(a: i32, b: i32) -> i32 { a + b }
659671
fn main() {
@@ -1102,14 +1114,7 @@ fn main() {
11021114
let expected = extract_annotations(&*analysis.file_text(file_id).unwrap());
11031115
let inlay_hints = analysis
11041116
.inlay_hints(
1105-
&InlayHintsConfig {
1106-
render_colons: true,
1107-
parameter_hints: false,
1108-
type_hints: true,
1109-
chaining_hints: false,
1110-
hide_named_constructor_hints: false,
1111-
max_length: None,
1112-
},
1117+
&InlayHintsConfig { type_hints: true, ..DISABLED_CONFIG },
11131118
file_id,
11141119
Some(FileRange {
11151120
file_id,
@@ -1418,7 +1423,7 @@ fn main() {
14181423
parameter_hints: true,
14191424
chaining_hints: true,
14201425
hide_named_constructor_hints: true,
1421-
max_length: None,
1426+
..DISABLED_CONFIG
14221427
},
14231428
r#"
14241429
//- minicore: try, option
@@ -1546,13 +1551,14 @@ fn fallible() -> ControlFlow<()> {
15461551
fn main() {
15471552
let mut start = 0;
15481553
//^^^^^ i32
1549-
(0..2).for_each(|increment| { start += increment; });
1554+
(0..2).for_each(|increment | { start += increment; });
15501555
//^^^^^^^^^ i32
15511556
15521557
let multiply =
15531558
//^^^^^^^^ |i32, i32| -> i32
15541559
| a, b| a * b
15551560
//^ i32 ^ i32
1561+
15561562
;
15571563
15581564
let _: i32 = multiply(1, 2);
@@ -1561,6 +1567,8 @@ fn main() {
15611567
15621568
let return_42 = || 42;
15631569
//^^^^^^^^^ || -> i32
1570+
|| { 42 };
1571+
//^^ i32
15641572
}"#,
15651573
);
15661574
}
@@ -1590,14 +1598,7 @@ fn main() {
15901598
#[test]
15911599
fn chaining_hints_ignore_comments() {
15921600
check_expect(
1593-
InlayHintsConfig {
1594-
render_colons: true,
1595-
parameter_hints: false,
1596-
type_hints: false,
1597-
chaining_hints: true,
1598-
hide_named_constructor_hints: false,
1599-
max_length: None,
1600-
},
1601+
InlayHintsConfig { type_hints: false, chaining_hints: true, ..DISABLED_CONFIG },
16011602
r#"
16021603
struct A(B);
16031604
impl A { fn into_b(self) -> B { self.0 } }
@@ -1648,14 +1649,7 @@ fn main() {
16481649
#[test]
16491650
fn struct_access_chaining_hints() {
16501651
check_expect(
1651-
InlayHintsConfig {
1652-
render_colons: true,
1653-
parameter_hints: false,
1654-
type_hints: false,
1655-
chaining_hints: true,
1656-
hide_named_constructor_hints: false,
1657-
max_length: None,
1658-
},
1652+
InlayHintsConfig { chaining_hints: true, ..DISABLED_CONFIG },
16591653
r#"
16601654
struct A { pub b: B }
16611655
struct B { pub c: C }
@@ -1694,14 +1688,7 @@ fn main() {
16941688
#[test]
16951689
fn generic_chaining_hints() {
16961690
check_expect(
1697-
InlayHintsConfig {
1698-
render_colons: true,
1699-
parameter_hints: false,
1700-
type_hints: false,
1701-
chaining_hints: true,
1702-
hide_named_constructor_hints: false,
1703-
max_length: None,
1704-
},
1691+
InlayHintsConfig { chaining_hints: true, ..DISABLED_CONFIG },
17051692
r#"
17061693
struct A<T>(T);
17071694
struct B<T>(T);
@@ -1741,14 +1728,7 @@ fn main() {
17411728
#[test]
17421729
fn shorten_iterator_chaining_hints() {
17431730
check_expect(
1744-
InlayHintsConfig {
1745-
render_colons: true,
1746-
parameter_hints: false,
1747-
type_hints: false,
1748-
chaining_hints: true,
1749-
hide_named_constructor_hints: false,
1750-
max_length: None,
1751-
},
1731+
InlayHintsConfig { chaining_hints: true, ..DISABLED_CONFIG },
17521732
r#"
17531733
//- minicore: iterators
17541734
use core::iter;

crates/ide/src/static_index.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ impl StaticIndex<'_> {
109109
type_hints: true,
110110
parameter_hints: true,
111111
chaining_hints: true,
112+
closure_return_type_hints: true,
112113
hide_named_constructor_hints: false,
113114
max_length: Some(25),
114115
},

crates/rust-analyzer/src/config.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -244,15 +244,17 @@ config_data! {
244244

245245
/// Whether to render trailing colons for parameter hints, and trailing colons for parameter hints.
246246
inlayHints_renderColons: bool = "true",
247-
/// Whether to show inlay type hints for method chains.
248-
inlayHints_chainingHints: bool = "true",
249247
/// Maximum length for inlay hints. Set to null to have an unlimited length.
250248
inlayHints_maxLength: Option<usize> = "25",
251249
/// Whether to show function parameter name inlay hints at the call
252250
/// site.
253251
inlayHints_parameterHints: bool = "true",
254252
/// Whether to show inlay type hints for variables.
255253
inlayHints_typeHints: bool = "true",
254+
/// Whether to show inlay type hints for method chains.
255+
inlayHints_chainingHints: bool = "true",
256+
/// Whether to show inlay type hints for return types of closures with blocks.
257+
inlayHints_closureReturnTypeHints: bool = "false",
256258
/// Whether to hide inlay hints for constructors.
257259
inlayHints_hideNamedConstructorHints: bool = "false",
258260

@@ -852,6 +854,7 @@ impl Config {
852854
type_hints: self.data.inlayHints_typeHints,
853855
parameter_hints: self.data.inlayHints_parameterHints,
854856
chaining_hints: self.data.inlayHints_chainingHints,
857+
closure_return_type_hints: self.data.inlayHints_closureReturnTypeHints,
855858
hide_named_constructor_hints: self.data.inlayHints_hideNamedConstructorHints,
856859
max_length: self.data.inlayHints_maxLength,
857860
}

0 commit comments

Comments
 (0)