1 //! FIXME: write short doc here
3 use crate::{db::RootDatabase, FileId};
4 use hir::{HirDisplay, SourceAnalyzer, Ty};
6 ast::{self, AstNode, TypeAscriptionOwner},
7 match_ast, SmolStr, SourceFile, SyntaxKind, SyntaxNode, TextRange,
10 #[derive(Debug, PartialEq, Eq)]
16 pub struct InlayHint {
22 pub(crate) fn inlay_hints(db: &RootDatabase, file_id: FileId, file: &SourceFile) -> Vec<InlayHint> {
25 .map(|node| get_inlay_hints(db, file_id, &node).unwrap_or_default())
34 ) -> Option<Vec<InlayHint>> {
35 let analyzer = SourceAnalyzer::new(db, hir::Source::new(file_id.into(), node), None);
39 if it.ascribed_type().is_some() {
43 Some(get_pat_type_hints(db, &analyzer, pat, false))
45 ast::LambdaExpr(it) => {
46 it.param_list().map(|param_list| {
49 .filter(|closure_param| closure_param.ascribed_type().is_none())
50 .filter_map(|closure_param| closure_param.pat())
51 .map(|root_pat| get_pat_type_hints(db, &analyzer, root_pat, false))
58 Some(get_pat_type_hints(db, &analyzer, pat, false))
61 let pat = it.condition()?.pat()?;
62 Some(get_pat_type_hints(db, &analyzer, pat, true))
64 ast::WhileExpr(it) => {
65 let pat = it.condition()?.pat()?;
66 Some(get_pat_type_hints(db, &analyzer, pat, true))
68 ast::MatchArmList(it) => {
72 .map(|match_arm| match_arm.pats())
74 .map(|root_pat| get_pat_type_hints(db, &analyzer, root_pat, true))
84 fn get_pat_type_hints(
86 analyzer: &SourceAnalyzer,
88 skip_root_pat_hint: bool,
90 let original_pat = &root_pat.clone();
92 get_leaf_pats(root_pat)
94 .filter(|pat| !skip_root_pat_hint || pat != original_pat)
96 get_node_displayable_type(db, &analyzer, &pat)
97 .map(|pat_type| (pat.syntax().text_range(), pat_type))
99 .map(|(range, pat_type)| InlayHint {
101 kind: InlayKind::TypeHint,
102 label: pat_type.display(db).to_string().into(),
107 fn get_leaf_pats(root_pat: ast::Pat) -> Vec<ast::Pat> {
108 let mut pats_to_process = std::collections::VecDeque::<ast::Pat>::new();
109 pats_to_process.push_back(root_pat);
111 let mut leaf_pats = Vec::new();
113 while let Some(maybe_leaf_pat) = pats_to_process.pop_front() {
114 match &maybe_leaf_pat {
115 ast::Pat::BindPat(bind_pat) => {
116 if let Some(pat) = bind_pat.pat() {
117 pats_to_process.push_back(pat);
119 leaf_pats.push(maybe_leaf_pat);
122 ast::Pat::TuplePat(tuple_pat) => {
123 for arg_pat in tuple_pat.args() {
124 pats_to_process.push_back(arg_pat);
127 ast::Pat::RecordPat(record_pat) => {
128 if let Some(pat_list) = record_pat.record_field_pat_list() {
129 pats_to_process.extend(
132 .filter_map(|record_field_pat| {
135 .filter(|pat| pat.syntax().kind() != SyntaxKind::BIND_PAT)
137 .chain(pat_list.bind_pats().map(|bind_pat| {
138 bind_pat.pat().unwrap_or_else(|| ast::Pat::from(bind_pat))
143 ast::Pat::TupleStructPat(tuple_struct_pat) => {
144 for arg_pat in tuple_struct_pat.args() {
145 pats_to_process.push_back(arg_pat);
154 fn get_node_displayable_type(
156 analyzer: &SourceAnalyzer,
159 analyzer.type_of_pat(db, node_pat).and_then(|resolved_type| {
160 if let Ty::Apply(_) = resolved_type {
170 use crate::mock_analysis::single_file;
171 use insta::assert_debug_snapshot;
175 let (analysis, file_id) = single_file(
178 enum CustomOption<T> {
185 a: CustomOption<u32>,
190 struct InnerStruct {}
197 let test = InnerStruct {};
199 let test = vec![222];
200 let test: Vec<_> = (0..3).collect();
201 let test = (0..3).collect::<Vec<i128>>();
202 let test = (0..3).collect::<Vec<_>>();
204 let mut test = Vec::new();
207 let test = (42, 'a');
208 let (a, (b, c, (d, e), f)) = (2, (3, 4, (6.6, 7.7), 5));
212 assert_debug_snapshot!(analysis.inlay_hints(file_id).unwrap(), @r###"
232 label: "(i32, char)",
270 fn closure_parameter() {
271 let (analysis, file_id) = single_file(
275 (0..2).for_each(|increment| {
281 assert_debug_snapshot!(analysis.inlay_hints(file_id).unwrap(), @r###"
299 fn for_expression() {
300 let (analysis, file_id) = single_file(
304 for increment in 0..2 {
310 assert_debug_snapshot!(analysis.inlay_hints(file_id).unwrap(), @r###"
329 let (analysis, file_id) = single_file(
332 enum CustomOption<T> {
339 a: CustomOption<u32>,
344 let test = CustomOption::Some(Test { a: CustomOption::Some(3), b: 1 });
345 if let CustomOption::None = &test {};
346 if let test = &test {};
347 if let CustomOption::Some(test) = &test {};
348 if let CustomOption::Some(Test { a, b }) = &test {};
349 if let CustomOption::Some(Test { a: x, b: y }) = &test {};
350 if let CustomOption::Some(Test { a: CustomOption::Some(x), b: y }) = &test {};
351 if let CustomOption::Some(Test { a: CustomOption::None, b: y }) = &test {};
352 if let CustomOption::Some(Test { b: y, .. }) = &test {};
354 if test == CustomOption::None {}
358 assert_debug_snapshot!(analysis.inlay_hints(file_id).unwrap(), @r###"
363 label: "CustomOption<Test>",
373 label: "&CustomOption<u32>",
392 let (analysis, file_id) = single_file(
395 enum CustomOption<T> {
402 a: CustomOption<u32>,
407 let test = CustomOption::Some(Test { a: CustomOption::Some(3), b: 1 });
408 while let CustomOption::None = &test {};
409 while let test = &test {};
410 while let CustomOption::Some(test) = &test {};
411 while let CustomOption::Some(Test { a, b }) = &test {};
412 while let CustomOption::Some(Test { a: x, b: y }) = &test {};
413 while let CustomOption::Some(Test { a: CustomOption::Some(x), b: y }) = &test {};
414 while let CustomOption::Some(Test { a: CustomOption::None, b: y }) = &test {};
415 while let CustomOption::Some(Test { b: y, .. }) = &test {};
417 while test == CustomOption::None {}
421 assert_debug_snapshot!(analysis.inlay_hints(file_id).unwrap(), @r###"
426 label: "CustomOption<Test>",
436 label: "&CustomOption<u32>",
454 fn match_arm_list() {
455 let (analysis, file_id) = single_file(
458 enum CustomOption<T> {
465 a: CustomOption<u32>,
470 match CustomOption::Some(Test { a: CustomOption::Some(3), b: 1 }) {
471 CustomOption::None => (),
473 CustomOption::Some(test) => (),
474 CustomOption::Some(Test { a, b }) => (),
475 CustomOption::Some(Test { a: x, b: y }) => (),
476 CustomOption::Some(Test { a: CustomOption::Some(x), b: y }) => (),
477 CustomOption::Some(Test { a: CustomOption::None, b: y }) => (),
478 CustomOption::Some(Test { b: y, .. }) => (),
484 assert_debug_snapshot!(analysis.inlay_hints(file_id).unwrap(), @r###"
494 label: "CustomOption<u32>",