ide/inlay_hints/
closure_ret.rs1use hir::DisplayTarget;
5use ide_db::{famous_defs::FamousDefs, text_edit::TextEditBuilder};
6use syntax::ast::{self, AstNode};
7
8use crate::{
9 ClosureReturnTypeHints, InlayHint, InlayHintPosition, InlayHintsConfig, InlayKind,
10 inlay_hints::{closure_has_block_body, label_of_ty, ty_to_text_edit},
11};
12
13pub(super) fn hints(
14 acc: &mut Vec<InlayHint>,
15 famous_defs @ FamousDefs(sema, _): &FamousDefs<'_, '_>,
16 config: &InlayHintsConfig<'_>,
17 display_target: DisplayTarget,
18 closure: ast::ClosureExpr,
19) -> Option<()> {
20 if config.closure_return_type_hints == ClosureReturnTypeHints::Never {
21 return None;
22 }
23
24 let ret_type = closure.ret_type().map(|rt| (rt.thin_arrow_token(), rt.ty().is_some()));
25 let arrow = match ret_type {
26 Some((_, true)) => return None,
27 Some((arrow, _)) => arrow,
28 None => None,
29 };
30
31 let has_block_body = closure_has_block_body(&closure);
32 if !has_block_body && config.closure_return_type_hints == ClosureReturnTypeHints::WithBlock {
33 return None;
34 }
35
36 let param_list = closure.param_list()?;
37
38 let resolve_parent = Some(closure.syntax().text_range());
39 let descended_closure = sema.descend_node_into_attributes(closure.clone()).pop()?;
40 let ty = sema.type_of_expr(&ast::Expr::ClosureExpr(descended_closure.clone()))?.adjusted();
41 let callable = ty.as_callable(sema.db)?;
42 let ty = callable.return_type();
43 if arrow.is_none() && ty.is_unit() {
44 return None;
45 }
46
47 let mut label = label_of_ty(famous_defs, config, &ty, display_target)?;
48
49 if arrow.is_none() {
50 label.prepend_str(" -> ");
51 }
52
53 let offset_to_insert_ty =
54 arrow.as_ref().map_or_else(|| param_list.syntax().text_range(), |t| t.text_range()).end();
55
56 let insert_braces = |builder: &mut TextEditBuilder| {
58 if !has_block_body && let Some(range) = closure.body().map(|b| b.syntax().text_range()) {
59 builder.insert(range.start(), "{ ".to_owned());
60 builder.insert(range.end(), " }".to_owned());
61 }
62 };
63
64 let text_edit = ty_to_text_edit(
65 sema,
66 config,
67 descended_closure.syntax(),
68 &ty,
69 offset_to_insert_ty,
70 &insert_braces,
71 if arrow.is_none() { " -> " } else { "" },
72 );
73
74 acc.push(InlayHint {
75 range: param_list.syntax().text_range(),
76 kind: InlayKind::Type,
77 label,
78 text_edit,
79 position: InlayHintPosition::After,
80 pad_left: false,
81 pad_right: false,
82 resolve_parent,
83 });
84 Some(())
85}
86
87#[cfg(test)]
88mod tests {
89 use crate::inlay_hints::tests::{DISABLED_CONFIG, check_with_config};
90
91 use super::*;
92
93 #[test]
94 fn return_type_hints_for_closure_without_block() {
95 check_with_config(
96 InlayHintsConfig {
97 closure_return_type_hints: ClosureReturnTypeHints::Always,
98 ..DISABLED_CONFIG
99 },
100 r#"
101fn main() {
102 let a = || { 0 };
103 //^^ -> i32
104 let b = || 0;
105 //^^ -> i32
106}"#,
107 );
108 }
109}