aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_assists/src/assists/add_explicit_type.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_assists/src/assists/add_explicit_type.rs')
-rw-r--r--crates/ra_assists/src/assists/add_explicit_type.rs86
1 files changed, 86 insertions, 0 deletions
diff --git a/crates/ra_assists/src/assists/add_explicit_type.rs b/crates/ra_assists/src/assists/add_explicit_type.rs
new file mode 100644
index 000000000..78f0f7f28
--- /dev/null
+++ b/crates/ra_assists/src/assists/add_explicit_type.rs
@@ -0,0 +1,86 @@
1use hir::{db::HirDatabase, HirDisplay, Ty};
2use ra_syntax::{
3 ast::{self, AstNode, LetStmt, NameOwner},
4 T,
5};
6
7use crate::{Assist, AssistCtx, AssistId};
8
9/// Add explicit type assist.
10pub(crate) fn add_explicit_type(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
11 let stmt = ctx.node_at_offset::<LetStmt>()?;
12 let expr = stmt.initializer()?;
13 let pat = stmt.pat()?;
14 // Must be a binding
15 let pat = match pat {
16 ast::Pat::BindPat(bind_pat) => bind_pat,
17 _ => return None,
18 };
19 let pat_range = pat.syntax().text_range();
20 // The binding must have a name
21 let name = pat.name()?;
22 let name_range = name.syntax().text_range();
23 // Assist not applicable if the type has already been specified
24 if stmt.syntax().children_with_tokens().any(|child| child.kind() == T![:]) {
25 return None;
26 }
27 // Infer type
28 let db = ctx.db;
29 let analyzer = hir::SourceAnalyzer::new(db, ctx.frange.file_id, stmt.syntax(), None);
30 let ty = analyzer.type_of(db, &expr)?;
31 // Assist not applicable if the type is unknown
32 if is_unknown(&ty) {
33 return None;
34 }
35
36 ctx.add_action(AssistId("add_explicit_type"), "add explicit type", |edit| {
37 edit.target(pat_range);
38 edit.insert(name_range.end(), format!(": {}", ty.display(db)));
39 });
40 ctx.build()
41}
42
43/// Returns true if any type parameter is unknown
44fn is_unknown(ty: &Ty) -> bool {
45 match ty {
46 Ty::Unknown => true,
47 Ty::Apply(a_ty) => a_ty.parameters.iter().any(is_unknown),
48 _ => false,
49 }
50}
51
52#[cfg(test)]
53mod tests {
54 use super::*;
55
56 use crate::helpers::{check_assist, check_assist_not_applicable, check_assist_target};
57
58 #[test]
59 fn add_explicit_type_target() {
60 check_assist_target(add_explicit_type, "fn f() { let a<|> = 1; }", "a");
61 }
62
63 #[test]
64 fn add_explicit_type_works_for_simple_expr() {
65 check_assist(
66 add_explicit_type,
67 "fn f() { let a<|> = 1; }",
68 "fn f() { let a<|>: i32 = 1; }",
69 );
70 }
71
72 #[test]
73 fn add_explicit_type_not_applicable_if_ty_not_inferred() {
74 check_assist_not_applicable(add_explicit_type, "fn f() { let a<|> = None; }");
75 }
76
77 #[test]
78 fn add_explicit_type_not_applicable_if_ty_already_specified() {
79 check_assist_not_applicable(add_explicit_type, "fn f() { let a<|>: i32 = 1; }");
80 }
81
82 #[test]
83 fn add_explicit_type_not_applicable_if_specified_ty_is_tuple() {
84 check_assist_not_applicable(add_explicit_type, "fn f() { let a<|>: (i32, i32) = (3, 4); }");
85 }
86}