From f32081fa185b3a9df021f277c2c27fbd123d0951 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Wed, 25 Sep 2019 14:29:41 +0300 Subject: move assists to subdir --- crates/ra_assists/src/assists/add_explicit_type.rs | 86 ++++++++++++++++++++++ 1 file changed, 86 insertions(+) create mode 100644 crates/ra_assists/src/assists/add_explicit_type.rs (limited to 'crates/ra_assists/src/assists/add_explicit_type.rs') 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 @@ +use hir::{db::HirDatabase, HirDisplay, Ty}; +use ra_syntax::{ + ast::{self, AstNode, LetStmt, NameOwner}, + T, +}; + +use crate::{Assist, AssistCtx, AssistId}; + +/// Add explicit type assist. +pub(crate) fn add_explicit_type(mut ctx: AssistCtx) -> Option { + let stmt = ctx.node_at_offset::()?; + let expr = stmt.initializer()?; + let pat = stmt.pat()?; + // Must be a binding + let pat = match pat { + ast::Pat::BindPat(bind_pat) => bind_pat, + _ => return None, + }; + let pat_range = pat.syntax().text_range(); + // The binding must have a name + let name = pat.name()?; + let name_range = name.syntax().text_range(); + // Assist not applicable if the type has already been specified + if stmt.syntax().children_with_tokens().any(|child| child.kind() == T![:]) { + return None; + } + // Infer type + let db = ctx.db; + let analyzer = hir::SourceAnalyzer::new(db, ctx.frange.file_id, stmt.syntax(), None); + let ty = analyzer.type_of(db, &expr)?; + // Assist not applicable if the type is unknown + if is_unknown(&ty) { + return None; + } + + ctx.add_action(AssistId("add_explicit_type"), "add explicit type", |edit| { + edit.target(pat_range); + edit.insert(name_range.end(), format!(": {}", ty.display(db))); + }); + ctx.build() +} + +/// Returns true if any type parameter is unknown +fn is_unknown(ty: &Ty) -> bool { + match ty { + Ty::Unknown => true, + Ty::Apply(a_ty) => a_ty.parameters.iter().any(is_unknown), + _ => false, + } +} + +#[cfg(test)] +mod tests { + use super::*; + + use crate::helpers::{check_assist, check_assist_not_applicable, check_assist_target}; + + #[test] + fn add_explicit_type_target() { + check_assist_target(add_explicit_type, "fn f() { let a<|> = 1; }", "a"); + } + + #[test] + fn add_explicit_type_works_for_simple_expr() { + check_assist( + add_explicit_type, + "fn f() { let a<|> = 1; }", + "fn f() { let a<|>: i32 = 1; }", + ); + } + + #[test] + fn add_explicit_type_not_applicable_if_ty_not_inferred() { + check_assist_not_applicable(add_explicit_type, "fn f() { let a<|> = None; }"); + } + + #[test] + fn add_explicit_type_not_applicable_if_ty_already_specified() { + check_assist_not_applicable(add_explicit_type, "fn f() { let a<|>: i32 = 1; }"); + } + + #[test] + fn add_explicit_type_not_applicable_if_specified_ty_is_tuple() { + check_assist_not_applicable(add_explicit_type, "fn f() { let a<|>: (i32, i32) = (3, 4); }"); + } +} -- cgit v1.2.3