aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock6
-rw-r--r--bors.toml2
-rw-r--r--crates/ra_assists/src/assist_ctx.rs2
-rw-r--r--crates/ra_assists/src/assists/add_custom_impl.rs5
-rw-r--r--crates/ra_assists/src/assists/add_derive.rs2
-rw-r--r--crates/ra_assists/src/assists/add_explicit_type.rs12
-rw-r--r--crates/ra_assists/src/assists/add_impl.rs2
-rw-r--r--crates/ra_assists/src/assists/add_import.rs2
-rw-r--r--crates/ra_assists/src/assists/add_missing_impl_members.rs4
-rw-r--r--crates/ra_assists/src/assists/add_new.rs2
-rw-r--r--crates/ra_assists/src/assists/apply_demorgan.rs2
-rw-r--r--crates/ra_assists/src/assists/change_visibility.rs18
-rw-r--r--crates/ra_assists/src/assists/early_return.rs2
-rw-r--r--crates/ra_assists/src/assists/fill_match_arms.rs2
-rw-r--r--crates/ra_assists/src/assists/flip_binexpr.rs2
-rw-r--r--crates/ra_assists/src/assists/flip_comma.rs2
-rw-r--r--crates/ra_assists/src/assists/flip_trait_bound.rs2
-rw-r--r--crates/ra_assists/src/assists/inline_local_variable.rs2
-rw-r--r--crates/ra_assists/src/assists/introduce_variable.rs2
-rw-r--r--crates/ra_assists/src/assists/invert_if.rs2
-rw-r--r--crates/ra_assists/src/assists/merge_match_arms.rs2
-rw-r--r--crates/ra_assists/src/assists/move_bounds.rs2
-rw-r--r--crates/ra_assists/src/assists/move_guard.rs4
-rw-r--r--crates/ra_assists/src/assists/raw_string.rs8
-rw-r--r--crates/ra_assists/src/assists/remove_dbg.rs2
-rw-r--r--crates/ra_assists/src/assists/replace_if_let_with_match.rs2
-rw-r--r--crates/ra_assists/src/assists/split_import.rs2
-rw-r--r--crates/ra_assists/src/lib.rs11
-rw-r--r--crates/ra_hir/src/code_model.rs88
-rw-r--r--crates/ra_hir/src/lib.rs4
-rw-r--r--crates/ra_hir/src/source_analyzer.rs (renamed from crates/ra_hir/src/source_binder.rs)195
-rw-r--r--crates/ra_hir_expand/src/builtin_macro.rs96
-rw-r--r--crates/ra_hir_ty/src/infer/expr.rs19
-rw-r--r--crates/ra_hir_ty/src/infer/path.rs9
-rw-r--r--crates/ra_hir_ty/src/method_resolution.rs106
-rw-r--r--crates/ra_hir_ty/src/tests/macros.rs4
-rw-r--r--crates/ra_ide/src/completion/complete_dot.rs20
-rw-r--r--crates/ra_ide/src/completion/complete_path.rs26
-rw-r--r--crates/ra_lsp_server/src/main_loop/handlers.rs1
-rw-r--r--crates/ra_syntax/src/validation.rs61
-rw-r--r--editors/code/package-lock.json30
-rw-r--r--editors/code/package.json2
-rw-r--r--editors/code/src/config.ts16
43 files changed, 406 insertions, 379 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 12a8896a3..29e70d8fc 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -45,7 +45,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
45 45
46[[package]] 46[[package]]
47name = "backtrace" 47name = "backtrace"
48version = "0.3.40" 48version = "0.3.41"
49source = "registry+https://github.com/rust-lang/crates.io-index" 49source = "registry+https://github.com/rust-lang/crates.io-index"
50dependencies = [ 50dependencies = [
51 "backtrace-sys 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)", 51 "backtrace-sys 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -1113,7 +1113,7 @@ dependencies = [
1113name = "ra_prof" 1113name = "ra_prof"
1114version = "0.1.0" 1114version = "0.1.0"
1115dependencies = [ 1115dependencies = [
1116 "backtrace 0.3.40 (registry+https://github.com/rust-lang/crates.io-index)", 1116 "backtrace 0.3.41 (registry+https://github.com/rust-lang/crates.io-index)",
1117 "itertools 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", 1117 "itertools 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)",
1118 "jemalloc-ctl 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", 1118 "jemalloc-ctl 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
1119 "jemallocator 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", 1119 "jemallocator 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -1780,7 +1780,7 @@ dependencies = [
1780"checksum atty 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)" = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" 1780"checksum atty 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)" = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
1781"checksum autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "1d49d90015b3c36167a20fe2810c5cd875ad504b39cff3d4eae7977e6b7c1cb2" 1781"checksum autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "1d49d90015b3c36167a20fe2810c5cd875ad504b39cff3d4eae7977e6b7c1cb2"
1782"checksum autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d" 1782"checksum autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d"
1783"checksum backtrace 0.3.40 (registry+https://github.com/rust-lang/crates.io-index)" = "924c76597f0d9ca25d762c25a4d369d51267536465dc5064bdf0eb073ed477ea" 1783"checksum backtrace 0.3.41 (registry+https://github.com/rust-lang/crates.io-index)" = "a4ed64ae6d9ebfd9893193c4b2532b1292ec97bd8271c9d7d0fa90cd78a34cba"
1784"checksum backtrace-sys 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)" = "5d6575f128516de27e3ce99689419835fce9643a9b215a14d2b5b685be018491" 1784"checksum backtrace-sys 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)" = "5d6575f128516de27e3ce99689419835fce9643a9b215a14d2b5b685be018491"
1785"checksum base64 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b41b7ea54a0c9d92199de89e20e58d49f02f8e699814ef3fdf266f6f748d15c7" 1785"checksum base64 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b41b7ea54a0c9d92199de89e20e58d49f02f8e699814ef3fdf266f6f748d15c7"
1786"checksum bit-set 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e84c238982c4b1e1ee668d136c510c67a13465279c0cb367ea6baf6310620a80" 1786"checksum bit-set 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e84c238982c4b1e1ee668d136c510c67a13465279c0cb367ea6baf6310620a80"
diff --git a/bors.toml b/bors.toml
index 0bc71860f..bf5553df4 100644
--- a/bors.toml
+++ b/bors.toml
@@ -1,6 +1,6 @@
1status = [ 1status = [
2 "Rust (ubuntu-latest)", 2 "Rust (ubuntu-latest)",
3 "Rust (windows-latest)", 3 # "Rust (windows-latest)",
4 "Rust (macos-latest)", 4 "Rust (macos-latest)",
5 "TypeScript" 5 "TypeScript"
6] 6]
diff --git a/crates/ra_assists/src/assist_ctx.rs b/crates/ra_assists/src/assist_ctx.rs
index 28152f724..1a65b5dc0 100644
--- a/crates/ra_assists/src/assist_ctx.rs
+++ b/crates/ra_assists/src/assist_ctx.rs
@@ -84,6 +84,8 @@ impl<'a, DB: HirDatabase> AssistCtx<'a, DB> {
84 f: impl FnOnce(&mut AssistBuilder), 84 f: impl FnOnce(&mut AssistBuilder),
85 ) -> Option<Assist> { 85 ) -> Option<Assist> {
86 let label = AssistLabel { label: label.into(), id }; 86 let label = AssistLabel { label: label.into(), id };
87 assert!(label.label.chars().nth(0).unwrap().is_uppercase());
88
87 let assist = if self.should_compute_edit { 89 let assist = if self.should_compute_edit {
88 let action = { 90 let action = {
89 let mut edit = AssistBuilder::default(); 91 let mut edit = AssistBuilder::default();
diff --git a/crates/ra_assists/src/assists/add_custom_impl.rs b/crates/ra_assists/src/assists/add_custom_impl.rs
index 9b8955710..f91034967 100644
--- a/crates/ra_assists/src/assists/add_custom_impl.rs
+++ b/crates/ra_assists/src/assists/add_custom_impl.rs
@@ -49,7 +49,10 @@ pub(crate) fn add_custom_impl(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist
49 let annotated_name = annotated.syntax().text().to_string(); 49 let annotated_name = annotated.syntax().text().to_string();
50 let start_offset = annotated.syntax().parent()?.text_range().end(); 50 let start_offset = annotated.syntax().parent()?.text_range().end();
51 51
52 ctx.add_assist(AssistId("add_custom_impl"), "add custom impl", |edit| { 52 let label =
53 format!("Add custom impl '{}' for '{}'", trait_token.text().as_str(), annotated_name);
54
55 ctx.add_assist(AssistId("add_custom_impl"), label, |edit| {
53 edit.target(attr.syntax().text_range()); 56 edit.target(attr.syntax().text_range());
54 57
55 let new_attr_input = input 58 let new_attr_input = input
diff --git a/crates/ra_assists/src/assists/add_derive.rs b/crates/ra_assists/src/assists/add_derive.rs
index 764b17bd8..6d9af3905 100644
--- a/crates/ra_assists/src/assists/add_derive.rs
+++ b/crates/ra_assists/src/assists/add_derive.rs
@@ -28,7 +28,7 @@ use crate::{Assist, AssistCtx, AssistId};
28pub(crate) fn add_derive(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> { 28pub(crate) fn add_derive(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
29 let nominal = ctx.find_node_at_offset::<ast::NominalDef>()?; 29 let nominal = ctx.find_node_at_offset::<ast::NominalDef>()?;
30 let node_start = derive_insertion_offset(&nominal)?; 30 let node_start = derive_insertion_offset(&nominal)?;
31 ctx.add_assist(AssistId("add_derive"), "add `#[derive]`", |edit| { 31 ctx.add_assist(AssistId("add_derive"), "Add `#[derive]`", |edit| {
32 let derive_attr = nominal 32 let derive_attr = nominal
33 .attrs() 33 .attrs()
34 .filter_map(|x| x.as_simple_call()) 34 .filter_map(|x| x.as_simple_call())
diff --git a/crates/ra_assists/src/assists/add_explicit_type.rs b/crates/ra_assists/src/assists/add_explicit_type.rs
index 2c602a79e..f9f826b88 100644
--- a/crates/ra_assists/src/assists/add_explicit_type.rs
+++ b/crates/ra_assists/src/assists/add_explicit_type.rs
@@ -47,10 +47,14 @@ pub(crate) fn add_explicit_type(ctx: AssistCtx<impl HirDatabase>) -> Option<Assi
47 return None; 47 return None;
48 } 48 }
49 49
50 ctx.add_assist(AssistId("add_explicit_type"), "add explicit type", |edit| { 50 ctx.add_assist(
51 edit.target(pat_range); 51 AssistId("add_explicit_type"),
52 edit.insert(name_range.end(), format!(": {}", ty.display(db))); 52 format!("Insert explicit type '{}'", ty.display(db)),
53 }) 53 |edit| {
54 edit.target(pat_range);
55 edit.insert(name_range.end(), format!(": {}", ty.display(db)));
56 },
57 )
54} 58}
55 59
56#[cfg(test)] 60#[cfg(test)]
diff --git a/crates/ra_assists/src/assists/add_impl.rs b/crates/ra_assists/src/assists/add_impl.rs
index 7da0cfd0d..4b326c837 100644
--- a/crates/ra_assists/src/assists/add_impl.rs
+++ b/crates/ra_assists/src/assists/add_impl.rs
@@ -30,7 +30,7 @@ use crate::{Assist, AssistCtx, AssistId};
30pub(crate) fn add_impl(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> { 30pub(crate) fn add_impl(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
31 let nominal = ctx.find_node_at_offset::<ast::NominalDef>()?; 31 let nominal = ctx.find_node_at_offset::<ast::NominalDef>()?;
32 let name = nominal.name()?; 32 let name = nominal.name()?;
33 ctx.add_assist(AssistId("add_impl"), "add impl", |edit| { 33 ctx.add_assist(AssistId("add_impl"), format!("Implement {}", name.text().as_str()), |edit| {
34 edit.target(nominal.syntax().text_range()); 34 edit.target(nominal.syntax().text_range());
35 let type_params = nominal.type_param_list(); 35 let type_params = nominal.type_param_list();
36 let start_offset = nominal.syntax().text_range().end(); 36 let start_offset = nominal.syntax().text_range().end();
diff --git a/crates/ra_assists/src/assists/add_import.rs b/crates/ra_assists/src/assists/add_import.rs
index b8752cbad..bf6cfe865 100644
--- a/crates/ra_assists/src/assists/add_import.rs
+++ b/crates/ra_assists/src/assists/add_import.rs
@@ -72,7 +72,7 @@ pub(crate) fn add_import(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
72 } 72 }
73 }; 73 };
74 74
75 ctx.add_assist(AssistId("add_import"), format!("import {}", fmt_segments(&segments)), |edit| { 75 ctx.add_assist(AssistId("add_import"), format!("Import {}", fmt_segments(&segments)), |edit| {
76 apply_auto_import(&position, &path, &segments, edit.text_edit_builder()); 76 apply_auto_import(&position, &path, &segments, edit.text_edit_builder());
77 }) 77 })
78} 78}
diff --git a/crates/ra_assists/src/assists/add_missing_impl_members.rs b/crates/ra_assists/src/assists/add_missing_impl_members.rs
index bf1136193..5bb937bde 100644
--- a/crates/ra_assists/src/assists/add_missing_impl_members.rs
+++ b/crates/ra_assists/src/assists/add_missing_impl_members.rs
@@ -48,7 +48,7 @@ pub(crate) fn add_missing_impl_members(ctx: AssistCtx<impl HirDatabase>) -> Opti
48 ctx, 48 ctx,
49 AddMissingImplMembersMode::NoDefaultMethods, 49 AddMissingImplMembersMode::NoDefaultMethods,
50 "add_impl_missing_members", 50 "add_impl_missing_members",
51 "add missing impl members", 51 "Implement missing members",
52 ) 52 )
53} 53}
54 54
@@ -89,7 +89,7 @@ pub(crate) fn add_missing_default_members(ctx: AssistCtx<impl HirDatabase>) -> O
89 ctx, 89 ctx,
90 AddMissingImplMembersMode::DefaultMethodsOnly, 90 AddMissingImplMembersMode::DefaultMethodsOnly,
91 "add_impl_default_members", 91 "add_impl_default_members",
92 "add impl default members", 92 "Implement default members",
93 ) 93 )
94} 94}
95 95
diff --git a/crates/ra_assists/src/assists/add_new.rs b/crates/ra_assists/src/assists/add_new.rs
index b2f946fac..d148d6e73 100644
--- a/crates/ra_assists/src/assists/add_new.rs
+++ b/crates/ra_assists/src/assists/add_new.rs
@@ -43,7 +43,7 @@ pub(crate) fn add_new(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
43 // Return early if we've found an existing new fn 43 // Return early if we've found an existing new fn
44 let impl_block = find_struct_impl(&ctx, &strukt)?; 44 let impl_block = find_struct_impl(&ctx, &strukt)?;
45 45
46 ctx.add_assist(AssistId("add_new"), "add new fn", |edit| { 46 ctx.add_assist(AssistId("add_new"), "Add default constructor", |edit| {
47 edit.target(strukt.syntax().text_range()); 47 edit.target(strukt.syntax().text_range());
48 48
49 let mut buf = String::with_capacity(512); 49 let mut buf = String::with_capacity(512);
diff --git a/crates/ra_assists/src/assists/apply_demorgan.rs b/crates/ra_assists/src/assists/apply_demorgan.rs
index 7c57c0560..666dce4e6 100644
--- a/crates/ra_assists/src/assists/apply_demorgan.rs
+++ b/crates/ra_assists/src/assists/apply_demorgan.rs
@@ -39,7 +39,7 @@ pub(crate) fn apply_demorgan(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist>
39 let not_lhs = invert_boolean_expression(&lhs)?; 39 let not_lhs = invert_boolean_expression(&lhs)?;
40 let not_rhs = invert_boolean_expression(&rhs)?; 40 let not_rhs = invert_boolean_expression(&rhs)?;
41 41
42 ctx.add_assist(AssistId("apply_demorgan"), "apply demorgan's law", |edit| { 42 ctx.add_assist(AssistId("apply_demorgan"), "Apply De Morgan's law", |edit| {
43 edit.target(op_range); 43 edit.target(op_range);
44 edit.replace(op_range, opposite_op); 44 edit.replace(op_range, opposite_op);
45 edit.replace(lhs_range, format!("!({}", not_lhs.syntax().text())); 45 edit.replace(lhs_range, format!("!({}", not_lhs.syntax().text()));
diff --git a/crates/ra_assists/src/assists/change_visibility.rs b/crates/ra_assists/src/assists/change_visibility.rs
index 132c9dc1d..fd766bb46 100644
--- a/crates/ra_assists/src/assists/change_visibility.rs
+++ b/crates/ra_assists/src/assists/change_visibility.rs
@@ -57,7 +57,7 @@ fn add_vis(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
57 (vis_offset(field.syntax()), ident.text_range()) 57 (vis_offset(field.syntax()), ident.text_range())
58 }; 58 };
59 59
60 ctx.add_assist(AssistId("change_visibility"), "make pub(crate)", |edit| { 60 ctx.add_assist(AssistId("change_visibility"), "Change visibility to pub(crate)", |edit| {
61 edit.target(target); 61 edit.target(target);
62 edit.insert(offset, "pub(crate) "); 62 edit.insert(offset, "pub(crate) ");
63 edit.set_cursor(offset); 63 edit.set_cursor(offset);
@@ -77,14 +77,18 @@ fn vis_offset(node: &SyntaxNode) -> TextUnit {
77 77
78fn change_vis(ctx: AssistCtx<impl HirDatabase>, vis: ast::Visibility) -> Option<Assist> { 78fn change_vis(ctx: AssistCtx<impl HirDatabase>, vis: ast::Visibility) -> Option<Assist> {
79 if vis.syntax().text() == "pub" { 79 if vis.syntax().text() == "pub" {
80 return ctx.add_assist(AssistId("change_visibility"), "change to pub(crate)", |edit| { 80 return ctx.add_assist(
81 edit.target(vis.syntax().text_range()); 81 AssistId("change_visibility"),
82 edit.replace(vis.syntax().text_range(), "pub(crate)"); 82 "Change Visibility to pub(crate)",
83 edit.set_cursor(vis.syntax().text_range().start()) 83 |edit| {
84 }); 84 edit.target(vis.syntax().text_range());
85 edit.replace(vis.syntax().text_range(), "pub(crate)");
86 edit.set_cursor(vis.syntax().text_range().start())
87 },
88 );
85 } 89 }
86 if vis.syntax().text() == "pub(crate)" { 90 if vis.syntax().text() == "pub(crate)" {
87 return ctx.add_assist(AssistId("change_visibility"), "change to pub", |edit| { 91 return ctx.add_assist(AssistId("change_visibility"), "Change visibility to pub", |edit| {
88 edit.target(vis.syntax().text_range()); 92 edit.target(vis.syntax().text_range());
89 edit.replace(vis.syntax().text_range(), "pub"); 93 edit.replace(vis.syntax().text_range(), "pub");
90 edit.set_cursor(vis.syntax().text_range().start()); 94 edit.set_cursor(vis.syntax().text_range().start());
diff --git a/crates/ra_assists/src/assists/early_return.rs b/crates/ra_assists/src/assists/early_return.rs
index 023917aca..487ee9eef 100644
--- a/crates/ra_assists/src/assists/early_return.rs
+++ b/crates/ra_assists/src/assists/early_return.rs
@@ -95,7 +95,7 @@ pub(crate) fn convert_to_guarded_return(ctx: AssistCtx<impl HirDatabase>) -> Opt
95 then_block.syntax().last_child_or_token().filter(|t| t.kind() == R_CURLY)?; 95 then_block.syntax().last_child_or_token().filter(|t| t.kind() == R_CURLY)?;
96 let cursor_position = ctx.frange.range.start(); 96 let cursor_position = ctx.frange.range.start();
97 97
98 ctx.add_assist(AssistId("convert_to_guarded_return"), "convert to guarded return", |edit| { 98 ctx.add_assist(AssistId("convert_to_guarded_return"), "Convert to guarded return", |edit| {
99 let if_indent_level = IndentLevel::from_node(&if_expr.syntax()); 99 let if_indent_level = IndentLevel::from_node(&if_expr.syntax());
100 let new_block = match if_let_pat { 100 let new_block = match if_let_pat {
101 None => { 101 None => {
diff --git a/crates/ra_assists/src/assists/fill_match_arms.rs b/crates/ra_assists/src/assists/fill_match_arms.rs
index 99d80998c..01758d23a 100644
--- a/crates/ra_assists/src/assists/fill_match_arms.rs
+++ b/crates/ra_assists/src/assists/fill_match_arms.rs
@@ -57,7 +57,7 @@ pub(crate) fn fill_match_arms(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist
57 57
58 let db = ctx.db; 58 let db = ctx.db;
59 59
60 ctx.add_assist(AssistId("fill_match_arms"), "fill match arms", |edit| { 60 ctx.add_assist(AssistId("fill_match_arms"), "Fill match arms", |edit| {
61 let indent_level = IndentLevel::from_node(match_arm_list.syntax()); 61 let indent_level = IndentLevel::from_node(match_arm_list.syntax());
62 62
63 let new_arm_list = { 63 let new_arm_list = {
diff --git a/crates/ra_assists/src/assists/flip_binexpr.rs b/crates/ra_assists/src/assists/flip_binexpr.rs
index 2d91b2a7e..2074087cd 100644
--- a/crates/ra_assists/src/assists/flip_binexpr.rs
+++ b/crates/ra_assists/src/assists/flip_binexpr.rs
@@ -34,7 +34,7 @@ pub(crate) fn flip_binexpr(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
34 return None; 34 return None;
35 } 35 }
36 36
37 ctx.add_assist(AssistId("flip_binexpr"), "flip binary expression", |edit| { 37 ctx.add_assist(AssistId("flip_binexpr"), "Flip binary expression", |edit| {
38 edit.target(op_range); 38 edit.target(op_range);
39 if let FlipAction::FlipAndReplaceOp(new_op) = action { 39 if let FlipAction::FlipAndReplaceOp(new_op) = action {
40 edit.replace(op_range, new_op); 40 edit.replace(op_range, new_op);
diff --git a/crates/ra_assists/src/assists/flip_comma.rs b/crates/ra_assists/src/assists/flip_comma.rs
index 9be1c1dc6..dd0c405ed 100644
--- a/crates/ra_assists/src/assists/flip_comma.rs
+++ b/crates/ra_assists/src/assists/flip_comma.rs
@@ -29,7 +29,7 @@ pub(crate) fn flip_comma(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
29 return None; 29 return None;
30 } 30 }
31 31
32 ctx.add_assist(AssistId("flip_comma"), "flip comma", |edit| { 32 ctx.add_assist(AssistId("flip_comma"), "Flip comma", |edit| {
33 edit.target(comma.text_range()); 33 edit.target(comma.text_range());
34 edit.replace(prev.text_range(), next.to_string()); 34 edit.replace(prev.text_range(), next.to_string());
35 edit.replace(next.text_range(), prev.to_string()); 35 edit.replace(next.text_range(), prev.to_string());
diff --git a/crates/ra_assists/src/assists/flip_trait_bound.rs b/crates/ra_assists/src/assists/flip_trait_bound.rs
index 6017b39dd..50b3fa492 100644
--- a/crates/ra_assists/src/assists/flip_trait_bound.rs
+++ b/crates/ra_assists/src/assists/flip_trait_bound.rs
@@ -33,7 +33,7 @@ pub(crate) fn flip_trait_bound(ctx: AssistCtx<impl HirDatabase>) -> Option<Assis
33 non_trivia_sibling(plus.clone().into(), Direction::Next)?, 33 non_trivia_sibling(plus.clone().into(), Direction::Next)?,
34 ); 34 );
35 35
36 ctx.add_assist(AssistId("flip_trait_bound"), "flip trait bound", |edit| { 36 ctx.add_assist(AssistId("flip_trait_bound"), "Flip trait bounds", |edit| {
37 edit.target(plus.text_range()); 37 edit.target(plus.text_range());
38 edit.replace(before.text_range(), after.to_string()); 38 edit.replace(before.text_range(), after.to_string());
39 edit.replace(after.text_range(), before.to_string()); 39 edit.replace(after.text_range(), before.to_string());
diff --git a/crates/ra_assists/src/assists/inline_local_variable.rs b/crates/ra_assists/src/assists/inline_local_variable.rs
index 18a34502c..164aee90c 100644
--- a/crates/ra_assists/src/assists/inline_local_variable.rs
+++ b/crates/ra_assists/src/assists/inline_local_variable.rs
@@ -93,7 +93,7 @@ pub(crate) fn inline_local_varialbe(ctx: AssistCtx<impl HirDatabase>) -> Option<
93 93
94 ctx.add_assist( 94 ctx.add_assist(
95 AssistId("inline_local_variable"), 95 AssistId("inline_local_variable"),
96 "inline local variable", 96 "Inline variable",
97 move |edit: &mut AssistBuilder| { 97 move |edit: &mut AssistBuilder| {
98 edit.delete(delete_range); 98 edit.delete(delete_range);
99 for (desc, should_wrap) in refs.iter().zip(wrap_in_parens) { 99 for (desc, should_wrap) in refs.iter().zip(wrap_in_parens) {
diff --git a/crates/ra_assists/src/assists/introduce_variable.rs b/crates/ra_assists/src/assists/introduce_variable.rs
index 0623d4475..19e211e0f 100644
--- a/crates/ra_assists/src/assists/introduce_variable.rs
+++ b/crates/ra_assists/src/assists/introduce_variable.rs
@@ -43,7 +43,7 @@ pub(crate) fn introduce_variable(ctx: AssistCtx<impl HirDatabase>) -> Option<Ass
43 if indent.kind() != WHITESPACE { 43 if indent.kind() != WHITESPACE {
44 return None; 44 return None;
45 } 45 }
46 ctx.add_assist(AssistId("introduce_variable"), "introduce variable", move |edit| { 46 ctx.add_assist(AssistId("introduce_variable"), "Extract into variable", move |edit| {
47 let mut buf = String::new(); 47 let mut buf = String::new();
48 48
49 let cursor_offset = if wrap_in_block { 49 let cursor_offset = if wrap_in_block {
diff --git a/crates/ra_assists/src/assists/invert_if.rs b/crates/ra_assists/src/assists/invert_if.rs
index bababa3e2..16352c040 100644
--- a/crates/ra_assists/src/assists/invert_if.rs
+++ b/crates/ra_assists/src/assists/invert_if.rs
@@ -41,7 +41,7 @@ pub(crate) fn invert_if(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
41 let else_node = else_block.syntax(); 41 let else_node = else_block.syntax();
42 let else_range = else_node.text_range(); 42 let else_range = else_node.text_range();
43 let then_range = then_node.text_range(); 43 let then_range = then_node.text_range();
44 return ctx.add_assist(AssistId("invert_if"), "invert if branches", |edit| { 44 return ctx.add_assist(AssistId("invert_if"), "Invert if", |edit| {
45 edit.target(if_range); 45 edit.target(if_range);
46 edit.replace(cond_range, flip_cond.syntax().text()); 46 edit.replace(cond_range, flip_cond.syntax().text());
47 edit.replace(else_range, then_node.text()); 47 edit.replace(else_range, then_node.text());
diff --git a/crates/ra_assists/src/assists/merge_match_arms.rs b/crates/ra_assists/src/assists/merge_match_arms.rs
index e9f2cae91..aca391155 100644
--- a/crates/ra_assists/src/assists/merge_match_arms.rs
+++ b/crates/ra_assists/src/assists/merge_match_arms.rs
@@ -52,7 +52,7 @@ pub(crate) fn merge_match_arms(ctx: AssistCtx<impl HirDatabase>) -> Option<Assis
52 52
53 let cursor_to_end = current_arm.syntax().text_range().end() - ctx.frange.range.start(); 53 let cursor_to_end = current_arm.syntax().text_range().end() - ctx.frange.range.start();
54 54
55 ctx.add_assist(AssistId("merge_match_arms"), "merge match arms", |edit| { 55 ctx.add_assist(AssistId("merge_match_arms"), "Merge match arms", |edit| {
56 fn contains_placeholder(a: &MatchArm) -> bool { 56 fn contains_placeholder(a: &MatchArm) -> bool {
57 a.pats().any(|x| match x { 57 a.pats().any(|x| match x {
58 ra_syntax::ast::Pat::PlaceholderPat(..) => true, 58 ra_syntax::ast::Pat::PlaceholderPat(..) => true,
diff --git a/crates/ra_assists/src/assists/move_bounds.rs b/crates/ra_assists/src/assists/move_bounds.rs
index 3145d7625..355adddc3 100644
--- a/crates/ra_assists/src/assists/move_bounds.rs
+++ b/crates/ra_assists/src/assists/move_bounds.rs
@@ -46,7 +46,7 @@ pub(crate) fn move_bounds_to_where_clause(ctx: AssistCtx<impl HirDatabase>) -> O
46 _ => return None, 46 _ => return None,
47 }; 47 };
48 48
49 ctx.add_assist(AssistId("move_bounds_to_where_clause"), "move_bounds_to_where_clause", |edit| { 49 ctx.add_assist(AssistId("move_bounds_to_where_clause"), "Move to where clause", |edit| {
50 let new_params = type_param_list 50 let new_params = type_param_list
51 .type_params() 51 .type_params()
52 .filter(|it| it.type_bound_list().is_some()) 52 .filter(|it| it.type_bound_list().is_some())
diff --git a/crates/ra_assists/src/assists/move_guard.rs b/crates/ra_assists/src/assists/move_guard.rs
index b49ec6172..41a31e677 100644
--- a/crates/ra_assists/src/assists/move_guard.rs
+++ b/crates/ra_assists/src/assists/move_guard.rs
@@ -41,7 +41,7 @@ pub(crate) fn move_guard_to_arm_body(ctx: AssistCtx<impl HirDatabase>) -> Option
41 let arm_expr = match_arm.expr()?; 41 let arm_expr = match_arm.expr()?;
42 let buf = format!("if {} {{ {} }}", guard_conditions.syntax().text(), arm_expr.syntax().text()); 42 let buf = format!("if {} {{ {} }}", guard_conditions.syntax().text(), arm_expr.syntax().text());
43 43
44 ctx.add_assist(AssistId("move_guard_to_arm_body"), "move guard to arm body", |edit| { 44 ctx.add_assist(AssistId("move_guard_to_arm_body"), "Move guard to arm body", |edit| {
45 edit.target(guard.syntax().text_range()); 45 edit.target(guard.syntax().text_range());
46 let offseting_amount = match space_before_guard.and_then(|it| it.into_token()) { 46 let offseting_amount = match space_before_guard.and_then(|it| it.into_token()) {
47 Some(tok) => { 47 Some(tok) => {
@@ -111,7 +111,7 @@ pub(crate) fn move_arm_cond_to_match_guard(ctx: AssistCtx<impl HirDatabase>) ->
111 111
112 ctx.add_assist( 112 ctx.add_assist(
113 AssistId("move_arm_cond_to_match_guard"), 113 AssistId("move_arm_cond_to_match_guard"),
114 "move condition to match guard", 114 "Move condition to match guard",
115 |edit| { 115 |edit| {
116 edit.target(if_expr.syntax().text_range()); 116 edit.target(if_expr.syntax().text_range());
117 let then_only_expr = then_block.block().and_then(|it| it.statements().next()).is_none(); 117 let then_only_expr = then_block.block().and_then(|it| it.statements().next()).is_none();
diff --git a/crates/ra_assists/src/assists/raw_string.rs b/crates/ra_assists/src/assists/raw_string.rs
index 93912a470..e79c51673 100644
--- a/crates/ra_assists/src/assists/raw_string.rs
+++ b/crates/ra_assists/src/assists/raw_string.rs
@@ -25,7 +25,7 @@ use crate::{Assist, AssistCtx, AssistId};
25pub(crate) fn make_raw_string(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> { 25pub(crate) fn make_raw_string(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
26 let token = ctx.find_token_at_offset(STRING).and_then(ast::String::cast)?; 26 let token = ctx.find_token_at_offset(STRING).and_then(ast::String::cast)?;
27 let value = token.value()?; 27 let value = token.value()?;
28 ctx.add_assist(AssistId("make_raw_string"), "make raw string", |edit| { 28 ctx.add_assist(AssistId("make_raw_string"), "Rewrite as raw string", |edit| {
29 edit.target(token.syntax().text_range()); 29 edit.target(token.syntax().text_range());
30 let max_hash_streak = count_hashes(&value); 30 let max_hash_streak = count_hashes(&value);
31 let mut hashes = String::with_capacity(max_hash_streak + 1); 31 let mut hashes = String::with_capacity(max_hash_streak + 1);
@@ -54,7 +54,7 @@ pub(crate) fn make_raw_string(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist
54pub(crate) fn make_usual_string(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> { 54pub(crate) fn make_usual_string(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
55 let token = ctx.find_token_at_offset(RAW_STRING).and_then(ast::RawString::cast)?; 55 let token = ctx.find_token_at_offset(RAW_STRING).and_then(ast::RawString::cast)?;
56 let value = token.value()?; 56 let value = token.value()?;
57 ctx.add_assist(AssistId("make_usual_string"), "make usual string", |edit| { 57 ctx.add_assist(AssistId("make_usual_string"), "Rewrite as regular string", |edit| {
58 edit.target(token.syntax().text_range()); 58 edit.target(token.syntax().text_range());
59 // parse inside string to escape `"` 59 // parse inside string to escape `"`
60 let escaped = value.escape_default().to_string(); 60 let escaped = value.escape_default().to_string();
@@ -79,7 +79,7 @@ pub(crate) fn make_usual_string(ctx: AssistCtx<impl HirDatabase>) -> Option<Assi
79// ``` 79// ```
80pub(crate) fn add_hash(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> { 80pub(crate) fn add_hash(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
81 let token = ctx.find_token_at_offset(RAW_STRING)?; 81 let token = ctx.find_token_at_offset(RAW_STRING)?;
82 ctx.add_assist(AssistId("add_hash"), "add hash to raw string", |edit| { 82 ctx.add_assist(AssistId("add_hash"), "Add # to raw string", |edit| {
83 edit.target(token.text_range()); 83 edit.target(token.text_range());
84 edit.insert(token.text_range().start() + TextUnit::of_char('r'), "#"); 84 edit.insert(token.text_range().start() + TextUnit::of_char('r'), "#");
85 edit.insert(token.text_range().end(), "#"); 85 edit.insert(token.text_range().end(), "#");
@@ -108,7 +108,7 @@ pub(crate) fn remove_hash(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
108 // no hash to remove 108 // no hash to remove
109 return None; 109 return None;
110 } 110 }
111 ctx.add_assist(AssistId("remove_hash"), "remove hash from raw string", |edit| { 111 ctx.add_assist(AssistId("remove_hash"), "Remove hash from raw string", |edit| {
112 edit.target(token.text_range()); 112 edit.target(token.text_range());
113 let result = &text[2..text.len() - 1]; 113 let result = &text[2..text.len() - 1];
114 let result = if result.starts_with('\"') { 114 let result = if result.starts_with('\"') {
diff --git a/crates/ra_assists/src/assists/remove_dbg.rs b/crates/ra_assists/src/assists/remove_dbg.rs
index aedf8747f..cf211ab84 100644
--- a/crates/ra_assists/src/assists/remove_dbg.rs
+++ b/crates/ra_assists/src/assists/remove_dbg.rs
@@ -58,7 +58,7 @@ pub(crate) fn remove_dbg(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
58 text.slice(without_parens).to_string() 58 text.slice(without_parens).to_string()
59 }; 59 };
60 60
61 ctx.add_assist(AssistId("remove_dbg"), "remove dbg!()", |edit| { 61 ctx.add_assist(AssistId("remove_dbg"), "Remove dbg!()", |edit| {
62 edit.target(macro_call.syntax().text_range()); 62 edit.target(macro_call.syntax().text_range());
63 edit.replace(macro_range, macro_content); 63 edit.replace(macro_range, macro_content);
64 edit.set_cursor(cursor_pos); 64 edit.set_cursor(cursor_pos);
diff --git a/crates/ra_assists/src/assists/replace_if_let_with_match.rs b/crates/ra_assists/src/assists/replace_if_let_with_match.rs
index 3272801ff..c9b62e5ff 100644
--- a/crates/ra_assists/src/assists/replace_if_let_with_match.rs
+++ b/crates/ra_assists/src/assists/replace_if_let_with_match.rs
@@ -42,7 +42,7 @@ pub(crate) fn replace_if_let_with_match(ctx: AssistCtx<impl HirDatabase>) -> Opt
42 ast::ElseBranch::IfExpr(_) => return None, 42 ast::ElseBranch::IfExpr(_) => return None,
43 }; 43 };
44 44
45 ctx.add_assist(AssistId("replace_if_let_with_match"), "replace with match", |edit| { 45 ctx.add_assist(AssistId("replace_if_let_with_match"), "Replace with match", |edit| {
46 let match_expr = build_match_expr(expr, pat, then_block, else_block); 46 let match_expr = build_match_expr(expr, pat, then_block, else_block);
47 edit.target(if_expr.syntax().text_range()); 47 edit.target(if_expr.syntax().text_range());
48 edit.replace_node_and_indent(if_expr.syntax(), match_expr); 48 edit.replace_node_and_indent(if_expr.syntax(), match_expr);
diff --git a/crates/ra_assists/src/assists/split_import.rs b/crates/ra_assists/src/assists/split_import.rs
index 5f8d6b0be..6038c4858 100644
--- a/crates/ra_assists/src/assists/split_import.rs
+++ b/crates/ra_assists/src/assists/split_import.rs
@@ -32,7 +32,7 @@ pub(crate) fn split_import(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
32 None => top_path.syntax().text_range().end(), 32 None => top_path.syntax().text_range().end(),
33 }; 33 };
34 34
35 ctx.add_assist(AssistId("split_import"), "split import", |edit| { 35 ctx.add_assist(AssistId("split_import"), "Split import", |edit| {
36 edit.target(colon_colon.text_range()); 36 edit.target(colon_colon.text_range());
37 edit.insert(l_curly, "{"); 37 edit.insert(l_curly, "{");
38 edit.insert(r_curly, "}"); 38 edit.insert(r_curly, "}");
diff --git a/crates/ra_assists/src/lib.rs b/crates/ra_assists/src/lib.rs
index 712ff6f6a..150b34ac7 100644
--- a/crates/ra_assists/src/lib.rs
+++ b/crates/ra_assists/src/lib.rs
@@ -292,8 +292,11 @@ mod tests {
292 let assists = super::assists(&db, frange); 292 let assists = super::assists(&db, frange);
293 let mut assists = assists.iter(); 293 let mut assists = assists.iter();
294 294
295 assert_eq!(assists.next().expect("expected assist").0.label, "make pub(crate)"); 295 assert_eq!(
296 assert_eq!(assists.next().expect("expected assist").0.label, "add `#[derive]`"); 296 assists.next().expect("expected assist").0.label,
297 "Change visibility to pub(crate)"
298 );
299 assert_eq!(assists.next().expect("expected assist").0.label, "Add `#[derive]`");
297 } 300 }
298 301
299 #[test] 302 #[test]
@@ -312,7 +315,7 @@ mod tests {
312 let assists = super::assists(&db, frange); 315 let assists = super::assists(&db, frange);
313 let mut assists = assists.iter(); 316 let mut assists = assists.iter();
314 317
315 assert_eq!(assists.next().expect("expected assist").0.label, "introduce variable"); 318 assert_eq!(assists.next().expect("expected assist").0.label, "Extract into variable");
316 assert_eq!(assists.next().expect("expected assist").0.label, "replace with match"); 319 assert_eq!(assists.next().expect("expected assist").0.label, "Replace with match");
317 } 320 }
318} 321}
diff --git a/crates/ra_hir/src/code_model.rs b/crates/ra_hir/src/code_model.rs
index a177cebca..3b479356f 100644
--- a/crates/ra_hir/src/code_model.rs
+++ b/crates/ra_hir/src/code_model.rs
@@ -21,8 +21,8 @@ use hir_expand::{
21 MacroDefId, 21 MacroDefId,
22}; 22};
23use hir_ty::{ 23use hir_ty::{
24 autoderef, display::HirFormatter, expr::ExprValidator, ApplicationTy, Canonical, InEnvironment, 24 autoderef, display::HirFormatter, expr::ExprValidator, method_resolution, ApplicationTy,
25 TraitEnvironment, Ty, TyDefId, TypeCtor, TypeWalk, 25 Canonical, InEnvironment, TraitEnvironment, Ty, TyDefId, TypeCtor, TypeWalk,
26}; 26};
27use ra_db::{CrateId, Edition, FileId}; 27use ra_db::{CrateId, Edition, FileId};
28use ra_prof::profile; 28use ra_prof::profile;
@@ -120,7 +120,8 @@ impl_froms!(
120 BuiltinType 120 BuiltinType
121); 121);
122 122
123pub use hir_def::{attr::Attrs, visibility::Visibility}; 123pub use hir_def::{attr::Attrs, visibility::Visibility, AssocItemId};
124use rustc_hash::FxHashSet;
124 125
125impl Module { 126impl Module {
126 pub(crate) fn new(krate: Crate, crate_module_id: LocalModuleId) -> Module { 127 pub(crate) fn new(krate: Crate, crate_module_id: LocalModuleId) -> Module {
@@ -878,6 +879,28 @@ impl Type {
878 } 879 }
879 } 880 }
880 881
882 /// Checks that particular type `ty` implements `std::future::Future`.
883 /// This function is used in `.await` syntax completion.
884 pub fn impls_future(&self, db: &impl HirDatabase) -> bool {
885 let krate = self.krate;
886
887 let std_future_trait =
888 db.lang_item(krate, "future_trait".into()).and_then(|it| it.as_trait());
889 let std_future_trait = match std_future_trait {
890 Some(it) => it,
891 None => return false,
892 };
893
894 let canonical_ty = Canonical { value: self.ty.value.clone(), num_vars: 0 };
895 method_resolution::implements_trait(
896 &canonical_ty,
897 db,
898 self.ty.environment.clone(),
899 krate,
900 std_future_trait,
901 )
902 }
903
881 // FIXME: this method is broken, as it doesn't take closures into account. 904 // FIXME: this method is broken, as it doesn't take closures into account.
882 pub fn as_callable(&self) -> Option<CallableDef> { 905 pub fn as_callable(&self) -> Option<CallableDef> {
883 Some(self.ty.value.as_callable()?.0) 906 Some(self.ty.value.as_callable()?.0)
@@ -986,6 +1009,65 @@ impl Type {
986 None 1009 None
987 } 1010 }
988 1011
1012 pub fn iterate_method_candidates<T>(
1013 &self,
1014 db: &impl HirDatabase,
1015 krate: Crate,
1016 traits_in_scope: &FxHashSet<TraitId>,
1017 name: Option<&Name>,
1018 mut callback: impl FnMut(&Ty, Function) -> Option<T>,
1019 ) -> Option<T> {
1020 // There should be no inference vars in types passed here
1021 // FIXME check that?
1022 // FIXME replace Unknown by bound vars here
1023 let canonical = Canonical { value: self.ty.value.clone(), num_vars: 0 };
1024
1025 let env = self.ty.environment.clone();
1026 let krate = krate.id;
1027
1028 method_resolution::iterate_method_candidates(
1029 &canonical,
1030 db,
1031 env,
1032 krate,
1033 traits_in_scope,
1034 name,
1035 method_resolution::LookupMode::MethodCall,
1036 |ty, it| match it {
1037 AssocItemId::FunctionId(f) => callback(ty, f.into()),
1038 _ => None,
1039 },
1040 )
1041 }
1042
1043 pub fn iterate_path_candidates<T>(
1044 &self,
1045 db: &impl HirDatabase,
1046 krate: Crate,
1047 traits_in_scope: &FxHashSet<TraitId>,
1048 name: Option<&Name>,
1049 mut callback: impl FnMut(&Ty, AssocItem) -> Option<T>,
1050 ) -> Option<T> {
1051 // There should be no inference vars in types passed here
1052 // FIXME check that?
1053 // FIXME replace Unknown by bound vars here
1054 let canonical = Canonical { value: self.ty.value.clone(), num_vars: 0 };
1055
1056 let env = self.ty.environment.clone();
1057 let krate = krate.id;
1058
1059 method_resolution::iterate_method_candidates(
1060 &canonical,
1061 db,
1062 env,
1063 krate,
1064 traits_in_scope,
1065 name,
1066 method_resolution::LookupMode::Path,
1067 |ty, it| callback(ty, it.into()),
1068 )
1069 }
1070
989 pub fn as_adt(&self) -> Option<Adt> { 1071 pub fn as_adt(&self) -> Option<Adt> {
990 let (adt, _subst) = self.ty.value.as_adt()?; 1072 let (adt, _subst) = self.ty.value.as_adt()?;
991 Some(adt.into()) 1073 Some(adt.into())
diff --git a/crates/ra_hir/src/lib.rs b/crates/ra_hir/src/lib.rs
index 3d13978d4..a1cf89010 100644
--- a/crates/ra_hir/src/lib.rs
+++ b/crates/ra_hir/src/lib.rs
@@ -27,7 +27,7 @@ macro_rules! impl_froms {
27} 27}
28 28
29pub mod db; 29pub mod db;
30pub mod source_binder; 30pub mod source_analyzer;
31 31
32pub mod diagnostics; 32pub mod diagnostics;
33 33
@@ -46,7 +46,7 @@ pub use crate::{
46 }, 46 },
47 from_source::FromSource, 47 from_source::FromSource,
48 has_source::HasSource, 48 has_source::HasSource,
49 source_binder::{PathResolution, ScopeEntryWithSyntax, SourceAnalyzer}, 49 source_analyzer::{PathResolution, ScopeEntryWithSyntax, SourceAnalyzer},
50}; 50};
51 51
52pub use hir_def::{ 52pub use hir_def::{
diff --git a/crates/ra_hir/src/source_binder.rs b/crates/ra_hir/src/source_analyzer.rs
index a2a9d968c..76e0bff34 100644
--- a/crates/ra_hir/src/source_binder.rs
+++ b/crates/ra_hir/src/source_analyzer.rs
@@ -15,17 +15,13 @@ use hir_def::{
15 }, 15 },
16 expr::{ExprId, PatId}, 16 expr::{ExprId, PatId},
17 nameres::ModuleSource, 17 nameres::ModuleSource,
18 path::path,
19 resolver::{self, resolver_for_scope, HasResolver, Resolver, TypeNs, ValueNs}, 18 resolver::{self, resolver_for_scope, HasResolver, Resolver, TypeNs, ValueNs},
20 AssocItemId, DefWithBodyId, 19 DefWithBodyId, TraitId,
21}; 20};
22use hir_expand::{ 21use hir_expand::{
23 hygiene::Hygiene, name::AsName, AstId, HirFileId, InFile, MacroCallId, MacroCallKind, 22 hygiene::Hygiene, name::AsName, AstId, HirFileId, InFile, MacroCallId, MacroCallKind,
24}; 23};
25use hir_ty::{ 24use hir_ty::{InEnvironment, InferenceResult, TraitEnvironment};
26 method_resolution::{self, implements_trait},
27 Canonical, InEnvironment, InferenceResult, TraitEnvironment, Ty,
28};
29use ra_prof::profile; 25use ra_prof::profile;
30use ra_syntax::{ 26use ra_syntax::{
31 ast::{self, AstNode}, 27 ast::{self, AstNode},
@@ -33,71 +29,13 @@ use ra_syntax::{
33 SyntaxKind::*, 29 SyntaxKind::*,
34 SyntaxNode, SyntaxNodePtr, SyntaxToken, TextRange, TextUnit, 30 SyntaxNode, SyntaxNodePtr, SyntaxToken, TextRange, TextUnit,
35}; 31};
32use rustc_hash::FxHashSet;
36 33
37use crate::{ 34use crate::{
38 db::HirDatabase, Adt, AssocItem, Const, DefWithBody, Enum, EnumVariant, FromSource, Function, 35 db::HirDatabase, Adt, Const, DefWithBody, Enum, EnumVariant, FromSource, Function, ImplBlock,
39 ImplBlock, Local, MacroDef, Name, Path, ScopeDef, Static, Struct, Trait, Type, TypeAlias, 36 Local, MacroDef, Name, Path, ScopeDef, Static, Struct, Trait, Type, TypeAlias, TypeParam,
40 TypeParam,
41}; 37};
42 38
43fn try_get_resolver_for_node(db: &impl HirDatabase, node: InFile<&SyntaxNode>) -> Option<Resolver> {
44 match_ast! {
45 match (node.value) {
46 ast::Module(it) => {
47 let src = node.with_value(it);
48 Some(crate::Module::from_declaration(db, src)?.id.resolver(db))
49 },
50 ast::SourceFile(it) => {
51 let src = node.with_value(ModuleSource::SourceFile(it));
52 Some(crate::Module::from_definition(db, src)?.id.resolver(db))
53 },
54 ast::StructDef(it) => {
55 let src = node.with_value(it);
56 Some(Struct::from_source(db, src)?.id.resolver(db))
57 },
58 ast::EnumDef(it) => {
59 let src = node.with_value(it);
60 Some(Enum::from_source(db, src)?.id.resolver(db))
61 },
62 ast::ImplBlock(it) => {
63 let src = node.with_value(it);
64 Some(ImplBlock::from_source(db, src)?.id.resolver(db))
65 },
66 ast::TraitDef(it) => {
67 let src = node.with_value(it);
68 Some(Trait::from_source(db, src)?.id.resolver(db))
69 },
70 _ => match node.value.kind() {
71 FN_DEF | CONST_DEF | STATIC_DEF => {
72 let def = def_with_body_from_child_node(db, node)?;
73 let def = DefWithBodyId::from(def);
74 Some(def.resolver(db))
75 }
76 // FIXME add missing cases
77 _ => None
78 }
79 }
80 }
81}
82
83fn def_with_body_from_child_node(
84 db: &impl HirDatabase,
85 child: InFile<&SyntaxNode>,
86) -> Option<DefWithBody> {
87 let _p = profile("def_with_body_from_child_node");
88 child.cloned().ancestors_with_macros(db).find_map(|node| {
89 let n = &node.value;
90 match_ast! {
91 match n {
92 ast::FnDef(def) => { return Function::from_source(db, node.with_value(def)).map(DefWithBody::from); },
93 ast::ConstDef(def) => { return Const::from_source(db, node.with_value(def)).map(DefWithBody::from); },
94 ast::StaticDef(def) => { return Static::from_source(db, node.with_value(def)).map(DefWithBody::from); },
95 _ => { None },
96 }
97 }
98 })
99}
100
101/// `SourceAnalyzer` is a convenience wrapper which exposes HIR API in terms of 39/// `SourceAnalyzer` is a convenience wrapper which exposes HIR API in terms of
102/// original source files. It should not be used inside the HIR itself. 40/// original source files. It should not be used inside the HIR itself.
103#[derive(Debug)] 41#[derive(Debug)]
@@ -409,68 +347,9 @@ impl SourceAnalyzer {
409 .collect() 347 .collect()
410 } 348 }
411 349
412 pub fn iterate_method_candidates<T>( 350 /// Note: `FxHashSet<TraitId>` should be treated as an opaque type, passed into `Type
413 &self, 351 pub fn traits_in_scope(&self, db: &impl HirDatabase) -> FxHashSet<TraitId> {
414 db: &impl HirDatabase, 352 self.resolver.traits_in_scope(db)
415 ty: &Type,
416 name: Option<&Name>,
417 mut callback: impl FnMut(&Ty, Function) -> Option<T>,
418 ) -> Option<T> {
419 // There should be no inference vars in types passed here
420 // FIXME check that?
421 // FIXME replace Unknown by bound vars here
422 let canonical = Canonical { value: ty.ty.value.clone(), num_vars: 0 };
423 method_resolution::iterate_method_candidates(
424 &canonical,
425 db,
426 &self.resolver,
427 name,
428 method_resolution::LookupMode::MethodCall,
429 |ty, it| match it {
430 AssocItemId::FunctionId(f) => callback(ty, f.into()),
431 _ => None,
432 },
433 )
434 }
435
436 pub fn iterate_path_candidates<T>(
437 &self,
438 db: &impl HirDatabase,
439 ty: &Type,
440 name: Option<&Name>,
441 mut callback: impl FnMut(&Ty, AssocItem) -> Option<T>,
442 ) -> Option<T> {
443 // There should be no inference vars in types passed here
444 // FIXME check that?
445 // FIXME replace Unknown by bound vars here
446 let canonical = Canonical { value: ty.ty.value.clone(), num_vars: 0 };
447 method_resolution::iterate_method_candidates(
448 &canonical,
449 db,
450 &self.resolver,
451 name,
452 method_resolution::LookupMode::Path,
453 |ty, it| callback(ty, it.into()),
454 )
455 }
456
457 /// Checks that particular type `ty` implements `std::future::Future`.
458 /// This function is used in `.await` syntax completion.
459 pub fn impls_future(&self, db: &impl HirDatabase, ty: Type) -> bool {
460 let std_future_path = path![std::future::Future];
461
462 let std_future_trait = match self.resolver.resolve_known_trait(db, &std_future_path) {
463 Some(it) => it.into(),
464 _ => return false,
465 };
466
467 let krate = match self.resolver.krate() {
468 Some(krate) => krate,
469 _ => return false,
470 };
471
472 let canonical_ty = Canonical { value: ty.ty.value, num_vars: 0 };
473 implements_trait(&canonical_ty, db, &self.resolver, krate.into(), std_future_trait)
474 } 353 }
475 354
476 pub fn expand( 355 pub fn expand(
@@ -487,6 +366,64 @@ impl SourceAnalyzer {
487 } 366 }
488} 367}
489 368
369fn try_get_resolver_for_node(db: &impl HirDatabase, node: InFile<&SyntaxNode>) -> Option<Resolver> {
370 match_ast! {
371 match (node.value) {
372 ast::Module(it) => {
373 let src = node.with_value(it);
374 Some(crate::Module::from_declaration(db, src)?.id.resolver(db))
375 },
376 ast::SourceFile(it) => {
377 let src = node.with_value(ModuleSource::SourceFile(it));
378 Some(crate::Module::from_definition(db, src)?.id.resolver(db))
379 },
380 ast::StructDef(it) => {
381 let src = node.with_value(it);
382 Some(Struct::from_source(db, src)?.id.resolver(db))
383 },
384 ast::EnumDef(it) => {
385 let src = node.with_value(it);
386 Some(Enum::from_source(db, src)?.id.resolver(db))
387 },
388 ast::ImplBlock(it) => {
389 let src = node.with_value(it);
390 Some(ImplBlock::from_source(db, src)?.id.resolver(db))
391 },
392 ast::TraitDef(it) => {
393 let src = node.with_value(it);
394 Some(Trait::from_source(db, src)?.id.resolver(db))
395 },
396 _ => match node.value.kind() {
397 FN_DEF | CONST_DEF | STATIC_DEF => {
398 let def = def_with_body_from_child_node(db, node)?;
399 let def = DefWithBodyId::from(def);
400 Some(def.resolver(db))
401 }
402 // FIXME add missing cases
403 _ => None
404 }
405 }
406 }
407}
408
409fn def_with_body_from_child_node(
410 db: &impl HirDatabase,
411 child: InFile<&SyntaxNode>,
412) -> Option<DefWithBody> {
413 let _p = profile("def_with_body_from_child_node");
414 child.cloned().ancestors_with_macros(db).find_map(|node| {
415 let n = &node.value;
416 match_ast! {
417 match n {
418 ast::FnDef(def) => { return Function::from_source(db, node.with_value(def)).map(DefWithBody::from); },
419 ast::ConstDef(def) => { return Const::from_source(db, node.with_value(def)).map(DefWithBody::from); },
420 ast::StaticDef(def) => { return Static::from_source(db, node.with_value(def)).map(DefWithBody::from); },
421 _ => { None },
422 }
423 }
424 })
425}
426
490fn scope_for( 427fn scope_for(
491 scopes: &ExprScopes, 428 scopes: &ExprScopes,
492 source_map: &BodySourceMap, 429 source_map: &BodySourceMap,
diff --git a/crates/ra_hir_expand/src/builtin_macro.rs b/crates/ra_hir_expand/src/builtin_macro.rs
index e9e275670..f3f959ac6 100644
--- a/crates/ra_hir_expand/src/builtin_macro.rs
+++ b/crates/ra_hir_expand/src/builtin_macro.rs
@@ -1,8 +1,8 @@
1//! Builtin macro 1//! Builtin macro
2use crate::db::AstDatabase; 2use crate::db::AstDatabase;
3use crate::{ 3use crate::{
4 ast::{self, AstNode}, 4 ast::{self},
5 name, AstId, CrateId, HirFileId, MacroCallId, MacroDefId, MacroDefKind, TextUnit, 5 name, AstId, CrateId, MacroCallId, MacroDefId, MacroDefKind, TextUnit,
6}; 6};
7 7
8use crate::quote; 8use crate::quote;
@@ -61,48 +61,13 @@ register_builtin! {
61 (format_args_nl, FormatArgsNl) => format_args_expand 61 (format_args_nl, FormatArgsNl) => format_args_expand
62} 62}
63 63
64fn to_line_number(db: &dyn AstDatabase, file: HirFileId, pos: TextUnit) -> usize {
65 let file_id = file.original_file(db);
66
67 // FIXME: if the file is coming from macro, we return a dummy value for now.
68 if file.call_node(db).map(|it| it.file_id != file_id.into()).unwrap_or(true) {
69 return 0;
70 }
71
72 let text = db.file_text(file_id);
73 let mut line_num = 1;
74
75 let pos = pos.to_usize();
76 if pos > text.len() {
77 // FIXME: `pos` at the moment could be an offset inside the "wrong" file
78 // in this case, when we know it's wrong, we return a dummy value
79 return 0;
80 }
81 // Count line end
82 for (i, c) in text.chars().enumerate() {
83 if i == pos {
84 break;
85 }
86 if c == '\n' {
87 line_num += 1;
88 }
89 }
90 line_num
91}
92
93fn line_expand( 64fn line_expand(
94 db: &dyn AstDatabase, 65 _db: &dyn AstDatabase,
95 id: MacroCallId, 66 _id: MacroCallId,
96 _tt: &tt::Subtree, 67 _tt: &tt::Subtree,
97) -> Result<tt::Subtree, mbe::ExpandError> { 68) -> Result<tt::Subtree, mbe::ExpandError> {
98 let loc = db.lookup_intern_macro(id); 69 // dummy implementation for type-checking purposes
99 70 let line_num = 0;
100 let arg = loc.kind.arg(db).ok_or_else(|| mbe::ExpandError::UnexpectedToken)?;
101 let arg_start = arg.text_range().start();
102
103 let file = id.as_file();
104 let line_num = to_line_number(db, file, arg_start);
105
106 let expanded = quote! { 71 let expanded = quote! {
107 #line_num 72 #line_num
108 }; 73 };
@@ -154,48 +119,13 @@ fn option_env_expand(
154 Ok(expanded) 119 Ok(expanded)
155} 120}
156 121
157fn to_col_number(db: &dyn AstDatabase, file: HirFileId, pos: TextUnit) -> usize {
158 let file_id = file.original_file(db);
159 // FIXME: if the file is coming from macro, we return a dummy value for now.
160 if file.call_node(db).map(|it| it.file_id != file_id.into()).unwrap_or(true) {
161 return 0;
162 }
163 let text = db.file_text(file_id);
164
165 let pos = pos.to_usize();
166 if pos > text.len() {
167 // FIXME: `pos` at the moment could be an offset inside the "wrong" file
168 // in this case we return a dummy value so that we don't `panic!`
169 return 0;
170 }
171
172 let mut col_num = 1;
173 for c in text[..pos].chars().rev() {
174 if c == '\n' {
175 break;
176 }
177 col_num += 1;
178 }
179 col_num
180}
181
182fn column_expand( 122fn column_expand(
183 db: &dyn AstDatabase, 123 _db: &dyn AstDatabase,
184 id: MacroCallId, 124 _id: MacroCallId,
185 _tt: &tt::Subtree, 125 _tt: &tt::Subtree,
186) -> Result<tt::Subtree, mbe::ExpandError> { 126) -> Result<tt::Subtree, mbe::ExpandError> {
187 let loc = db.lookup_intern_macro(id); 127 // dummy implementation for type-checking purposes
188 let macro_call = match loc.kind { 128 let col_num = 0;
189 crate::MacroCallKind::FnLike(ast_id) => ast_id.to_node(db),
190 _ => panic!("column macro called as attr"),
191 };
192
193 let _arg = macro_call.token_tree().ok_or_else(|| mbe::ExpandError::UnexpectedToken)?;
194 let col_start = macro_call.syntax().text_range().start();
195
196 let file = id.as_file();
197 let col_num = to_col_number(db, file, col_start);
198
199 let expanded = quote! { 129 let expanded = quote! {
200 #col_num 130 #col_num
201 }; 131 };
@@ -284,7 +214,7 @@ fn format_args_expand(
284#[cfg(test)] 214#[cfg(test)]
285mod tests { 215mod tests {
286 use super::*; 216 use super::*;
287 use crate::{name::AsName, test_db::TestDB, MacroCallKind, MacroCallLoc}; 217 use crate::{name::AsName, test_db::TestDB, AstNode, MacroCallKind, MacroCallLoc};
288 use ra_db::{fixture::WithFixture, SourceDatabase}; 218 use ra_db::{fixture::WithFixture, SourceDatabase};
289 use ra_syntax::ast::NameOwner; 219 use ra_syntax::ast::NameOwner;
290 220
@@ -330,7 +260,7 @@ mod tests {
330 "#, 260 "#,
331 ); 261 );
332 262
333 assert_eq!(expanded, "13"); 263 assert_eq!(expanded, "0");
334 } 264 }
335 265
336 #[test] 266 #[test]
@@ -343,7 +273,7 @@ mod tests {
343 "#, 273 "#,
344 ); 274 );
345 275
346 assert_eq!(expanded, "4"); 276 assert_eq!(expanded, "0");
347 } 277 }
348 278
349 #[test] 279 #[test]
diff --git a/crates/ra_hir_ty/src/infer/expr.rs b/crates/ra_hir_ty/src/infer/expr.rs
index 3af05394c..d6a17e469 100644
--- a/crates/ra_hir_ty/src/infer/expr.rs
+++ b/crates/ra_hir_ty/src/infer/expr.rs
@@ -569,12 +569,19 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
569 ) -> Ty { 569 ) -> Ty {
570 let receiver_ty = self.infer_expr(receiver, &Expectation::none()); 570 let receiver_ty = self.infer_expr(receiver, &Expectation::none());
571 let canonicalized_receiver = self.canonicalizer().canonicalize_ty(receiver_ty.clone()); 571 let canonicalized_receiver = self.canonicalizer().canonicalize_ty(receiver_ty.clone());
572 let resolved = method_resolution::lookup_method( 572
573 &canonicalized_receiver.value, 573 let traits_in_scope = self.resolver.traits_in_scope(self.db);
574 self.db, 574
575 method_name, 575 let resolved = self.resolver.krate().and_then(|krate| {
576 &self.resolver, 576 method_resolution::lookup_method(
577 ); 577 &canonicalized_receiver.value,
578 self.db,
579 self.trait_env.clone(),
580 krate,
581 &traits_in_scope,
582 method_name,
583 )
584 });
578 let (derefed_receiver_ty, method_ty, def_generics) = match resolved { 585 let (derefed_receiver_ty, method_ty, def_generics) = match resolved {
579 Some((ty, func)) => { 586 Some((ty, func)) => {
580 let ty = canonicalized_receiver.decanonicalize_ty(ty); 587 let ty = canonicalized_receiver.decanonicalize_ty(ty);
diff --git a/crates/ra_hir_ty/src/infer/path.rs b/crates/ra_hir_ty/src/infer/path.rs
index ffd358367..2c1d4831d 100644
--- a/crates/ra_hir_ty/src/infer/path.rs
+++ b/crates/ra_hir_ty/src/infer/path.rs
@@ -11,7 +11,7 @@ use hir_expand::name::Name;
11 11
12use crate::{db::HirDatabase, method_resolution, Substs, Ty, TypeWalk, ValueTyDefId}; 12use crate::{db::HirDatabase, method_resolution, Substs, Ty, TypeWalk, ValueTyDefId};
13 13
14use super::{ExprOrPatId, InferenceContext, TraitRef}; 14use super::{ExprOrPatId, InferenceContext, TraitEnvironment, TraitRef};
15 15
16impl<'a, D: HirDatabase> InferenceContext<'a, D> { 16impl<'a, D: HirDatabase> InferenceContext<'a, D> {
17 pub(super) fn infer_path( 17 pub(super) fn infer_path(
@@ -193,11 +193,16 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
193 } 193 }
194 194
195 let canonical_ty = self.canonicalizer().canonicalize_ty(ty.clone()); 195 let canonical_ty = self.canonicalizer().canonicalize_ty(ty.clone());
196 let env = TraitEnvironment::lower(self.db, &self.resolver);
197 let krate = self.resolver.krate()?;
198 let traits_in_scope = self.resolver.traits_in_scope(self.db);
196 199
197 method_resolution::iterate_method_candidates( 200 method_resolution::iterate_method_candidates(
198 &canonical_ty.value, 201 &canonical_ty.value,
199 self.db, 202 self.db,
200 &self.resolver.clone(), 203 env,
204 krate,
205 &traits_in_scope,
201 Some(name), 206 Some(name),
202 method_resolution::LookupMode::Path, 207 method_resolution::LookupMode::Path,
203 move |_ty, item| { 208 move |_ty, item| {
diff --git a/crates/ra_hir_ty/src/method_resolution.rs b/crates/ra_hir_ty/src/method_resolution.rs
index 888dc3116..5bacbbd7c 100644
--- a/crates/ra_hir_ty/src/method_resolution.rs
+++ b/crates/ra_hir_ty/src/method_resolution.rs
@@ -6,13 +6,13 @@ use std::sync::Arc;
6 6
7use arrayvec::ArrayVec; 7use arrayvec::ArrayVec;
8use hir_def::{ 8use hir_def::{
9 lang_item::LangItemTarget, resolver::Resolver, type_ref::Mutability, AssocContainerId, 9 lang_item::LangItemTarget, type_ref::Mutability, AssocContainerId, AssocItemId, FunctionId,
10 AssocItemId, FunctionId, HasModule, ImplId, Lookup, TraitId, 10 HasModule, ImplId, Lookup, TraitId,
11}; 11};
12use hir_expand::name::Name; 12use hir_expand::name::Name;
13use ra_db::CrateId; 13use ra_db::CrateId;
14use ra_prof::profile; 14use ra_prof::profile;
15use rustc_hash::FxHashMap; 15use rustc_hash::{FxHashMap, FxHashSet};
16 16
17use super::Substs; 17use super::Substs;
18use crate::{ 18use crate::{
@@ -144,14 +144,24 @@ impl Ty {
144pub(crate) fn lookup_method( 144pub(crate) fn lookup_method(
145 ty: &Canonical<Ty>, 145 ty: &Canonical<Ty>,
146 db: &impl HirDatabase, 146 db: &impl HirDatabase,
147 env: Arc<TraitEnvironment>,
148 krate: CrateId,
149 traits_in_scope: &FxHashSet<TraitId>,
147 name: &Name, 150 name: &Name,
148 resolver: &Resolver,
149) -> Option<(Ty, FunctionId)> { 151) -> Option<(Ty, FunctionId)> {
150 iterate_method_candidates(ty, db, resolver, Some(name), LookupMode::MethodCall, |ty, f| match f 152 iterate_method_candidates(
151 { 153 ty,
152 AssocItemId::FunctionId(f) => Some((ty.clone(), f)), 154 db,
153 _ => None, 155 env,
154 }) 156 krate,
157 &traits_in_scope,
158 Some(name),
159 LookupMode::MethodCall,
160 |ty, f| match f {
161 AssocItemId::FunctionId(f) => Some((ty.clone(), f)),
162 _ => None,
163 },
164 )
155} 165}
156 166
157/// Whether we're looking up a dotted method call (like `v.len()`) or a path 167/// Whether we're looking up a dotted method call (like `v.len()`) or a path
@@ -172,7 +182,9 @@ pub enum LookupMode {
172pub fn iterate_method_candidates<T>( 182pub fn iterate_method_candidates<T>(
173 ty: &Canonical<Ty>, 183 ty: &Canonical<Ty>,
174 db: &impl HirDatabase, 184 db: &impl HirDatabase,
175 resolver: &Resolver, 185 env: Arc<TraitEnvironment>,
186 krate: CrateId,
187 traits_in_scope: &FxHashSet<TraitId>,
176 name: Option<&Name>, 188 name: Option<&Name>,
177 mode: LookupMode, 189 mode: LookupMode,
178 mut callback: impl FnMut(&Ty, AssocItemId) -> Option<T>, 190 mut callback: impl FnMut(&Ty, AssocItemId) -> Option<T>,
@@ -187,9 +199,7 @@ pub fn iterate_method_candidates<T>(
187 // Also note that when we've got a receiver like &S, even if the method we 199 // Also note that when we've got a receiver like &S, even if the method we
188 // find in the end takes &self, we still do the autoderef step (just as 200 // find in the end takes &self, we still do the autoderef step (just as
189 // rustc does an autoderef and then autoref again). 201 // rustc does an autoderef and then autoref again).
190 let environment = TraitEnvironment::lower(db, resolver); 202 let ty = InEnvironment { value: ty.clone(), environment: env.clone() };
191 let ty = InEnvironment { value: ty.clone(), environment };
192 let krate = resolver.krate()?;
193 203
194 // We have to be careful about the order we're looking at candidates 204 // We have to be careful about the order we're looking at candidates
195 // in here. Consider the case where we're resolving `x.clone()` 205 // in here. Consider the case where we're resolving `x.clone()`
@@ -209,7 +219,9 @@ pub fn iterate_method_candidates<T>(
209 if let Some(result) = iterate_method_candidates_with_autoref( 219 if let Some(result) = iterate_method_candidates_with_autoref(
210 &deref_chain[i..], 220 &deref_chain[i..],
211 db, 221 db,
212 resolver, 222 env.clone(),
223 krate,
224 traits_in_scope,
213 name, 225 name,
214 &mut callback, 226 &mut callback,
215 ) { 227 ) {
@@ -220,7 +232,15 @@ pub fn iterate_method_candidates<T>(
220 } 232 }
221 LookupMode::Path => { 233 LookupMode::Path => {
222 // No autoderef for path lookups 234 // No autoderef for path lookups
223 iterate_method_candidates_for_self_ty(&ty, db, resolver, name, &mut callback) 235 iterate_method_candidates_for_self_ty(
236 &ty,
237 db,
238 env,
239 krate,
240 traits_in_scope,
241 name,
242 &mut callback,
243 )
224 } 244 }
225 } 245 }
226} 246}
@@ -228,7 +248,9 @@ pub fn iterate_method_candidates<T>(
228fn iterate_method_candidates_with_autoref<T>( 248fn iterate_method_candidates_with_autoref<T>(
229 deref_chain: &[Canonical<Ty>], 249 deref_chain: &[Canonical<Ty>],
230 db: &impl HirDatabase, 250 db: &impl HirDatabase,
231 resolver: &Resolver, 251 env: Arc<TraitEnvironment>,
252 krate: CrateId,
253 traits_in_scope: &FxHashSet<TraitId>,
232 name: Option<&Name>, 254 name: Option<&Name>,
233 mut callback: impl FnMut(&Ty, AssocItemId) -> Option<T>, 255 mut callback: impl FnMut(&Ty, AssocItemId) -> Option<T>,
234) -> Option<T> { 256) -> Option<T> {
@@ -236,7 +258,9 @@ fn iterate_method_candidates_with_autoref<T>(
236 &deref_chain[0], 258 &deref_chain[0],
237 &deref_chain[1..], 259 &deref_chain[1..],
238 db, 260 db,
239 resolver, 261 env.clone(),
262 krate,
263 &traits_in_scope,
240 name, 264 name,
241 &mut callback, 265 &mut callback,
242 ) { 266 ) {
@@ -250,7 +274,9 @@ fn iterate_method_candidates_with_autoref<T>(
250 &refed, 274 &refed,
251 deref_chain, 275 deref_chain,
252 db, 276 db,
253 resolver, 277 env.clone(),
278 krate,
279 &traits_in_scope,
254 name, 280 name,
255 &mut callback, 281 &mut callback,
256 ) { 282 ) {
@@ -264,7 +290,9 @@ fn iterate_method_candidates_with_autoref<T>(
264 &ref_muted, 290 &ref_muted,
265 deref_chain, 291 deref_chain,
266 db, 292 db,
267 resolver, 293 env.clone(),
294 krate,
295 &traits_in_scope,
268 name, 296 name,
269 &mut callback, 297 &mut callback,
270 ) { 298 ) {
@@ -277,14 +305,15 @@ fn iterate_method_candidates_by_receiver<T>(
277 receiver_ty: &Canonical<Ty>, 305 receiver_ty: &Canonical<Ty>,
278 rest_of_deref_chain: &[Canonical<Ty>], 306 rest_of_deref_chain: &[Canonical<Ty>],
279 db: &impl HirDatabase, 307 db: &impl HirDatabase,
280 resolver: &Resolver, 308 env: Arc<TraitEnvironment>,
309 krate: CrateId,
310 traits_in_scope: &FxHashSet<TraitId>,
281 name: Option<&Name>, 311 name: Option<&Name>,
282 mut callback: impl FnMut(&Ty, AssocItemId) -> Option<T>, 312 mut callback: impl FnMut(&Ty, AssocItemId) -> Option<T>,
283) -> Option<T> { 313) -> Option<T> {
284 // We're looking for methods with *receiver* type receiver_ty. These could 314 // We're looking for methods with *receiver* type receiver_ty. These could
285 // be found in any of the derefs of receiver_ty, so we have to go through 315 // be found in any of the derefs of receiver_ty, so we have to go through
286 // that. 316 // that.
287 let krate = resolver.krate()?;
288 for self_ty in std::iter::once(receiver_ty).chain(rest_of_deref_chain) { 317 for self_ty in std::iter::once(receiver_ty).chain(rest_of_deref_chain) {
289 if let Some(result) = 318 if let Some(result) =
290 iterate_inherent_methods(self_ty, db, name, Some(receiver_ty), krate, &mut callback) 319 iterate_inherent_methods(self_ty, db, name, Some(receiver_ty), krate, &mut callback)
@@ -296,7 +325,9 @@ fn iterate_method_candidates_by_receiver<T>(
296 if let Some(result) = iterate_trait_method_candidates( 325 if let Some(result) = iterate_trait_method_candidates(
297 self_ty, 326 self_ty,
298 db, 327 db,
299 resolver, 328 env.clone(),
329 krate,
330 &traits_in_scope,
300 name, 331 name,
301 Some(receiver_ty), 332 Some(receiver_ty),
302 &mut callback, 333 &mut callback,
@@ -310,17 +341,25 @@ fn iterate_method_candidates_by_receiver<T>(
310fn iterate_method_candidates_for_self_ty<T>( 341fn iterate_method_candidates_for_self_ty<T>(
311 self_ty: &Canonical<Ty>, 342 self_ty: &Canonical<Ty>,
312 db: &impl HirDatabase, 343 db: &impl HirDatabase,
313 resolver: &Resolver, 344 env: Arc<TraitEnvironment>,
345 krate: CrateId,
346 traits_in_scope: &FxHashSet<TraitId>,
314 name: Option<&Name>, 347 name: Option<&Name>,
315 mut callback: impl FnMut(&Ty, AssocItemId) -> Option<T>, 348 mut callback: impl FnMut(&Ty, AssocItemId) -> Option<T>,
316) -> Option<T> { 349) -> Option<T> {
317 let krate = resolver.krate()?;
318 if let Some(result) = iterate_inherent_methods(self_ty, db, name, None, krate, &mut callback) { 350 if let Some(result) = iterate_inherent_methods(self_ty, db, name, None, krate, &mut callback) {
319 return Some(result); 351 return Some(result);
320 } 352 }
321 if let Some(result) = 353 if let Some(result) = iterate_trait_method_candidates(
322 iterate_trait_method_candidates(self_ty, db, resolver, name, None, &mut callback) 354 self_ty,
323 { 355 db,
356 env,
357 krate,
358 traits_in_scope,
359 name,
360 None,
361 &mut callback,
362 ) {
324 return Some(result); 363 return Some(result);
325 } 364 }
326 None 365 None
@@ -329,14 +368,13 @@ fn iterate_method_candidates_for_self_ty<T>(
329fn iterate_trait_method_candidates<T>( 368fn iterate_trait_method_candidates<T>(
330 self_ty: &Canonical<Ty>, 369 self_ty: &Canonical<Ty>,
331 db: &impl HirDatabase, 370 db: &impl HirDatabase,
332 resolver: &Resolver, 371 env: Arc<TraitEnvironment>,
372 krate: CrateId,
373 traits_in_scope: &FxHashSet<TraitId>,
333 name: Option<&Name>, 374 name: Option<&Name>,
334 receiver_ty: Option<&Canonical<Ty>>, 375 receiver_ty: Option<&Canonical<Ty>>,
335 mut callback: impl FnMut(&Ty, AssocItemId) -> Option<T>, 376 mut callback: impl FnMut(&Ty, AssocItemId) -> Option<T>,
336) -> Option<T> { 377) -> Option<T> {
337 let krate = resolver.krate()?;
338 // FIXME: maybe put the trait_env behind a query (need to figure out good input parameters for that)
339 let env = TraitEnvironment::lower(db, resolver);
340 // if ty is `impl Trait` or `dyn Trait`, the trait doesn't need to be in scope 378 // if ty is `impl Trait` or `dyn Trait`, the trait doesn't need to be in scope
341 let inherent_trait = self_ty.value.inherent_trait().into_iter(); 379 let inherent_trait = self_ty.value.inherent_trait().into_iter();
342 // if we have `T: Trait` in the param env, the trait doesn't need to be in scope 380 // if we have `T: Trait` in the param env, the trait doesn't need to be in scope
@@ -344,8 +382,7 @@ fn iterate_trait_method_candidates<T>(
344 .trait_predicates_for_self_ty(&self_ty.value) 382 .trait_predicates_for_self_ty(&self_ty.value)
345 .map(|tr| tr.trait_) 383 .map(|tr| tr.trait_)
346 .flat_map(|t| all_super_traits(db, t)); 384 .flat_map(|t| all_super_traits(db, t));
347 let traits = 385 let traits = inherent_trait.chain(traits_from_env).chain(traits_in_scope.iter().copied());
348 inherent_trait.chain(traits_from_env).chain(resolver.traits_in_scope(db).into_iter());
349 'traits: for t in traits { 386 'traits: for t in traits {
350 let data = db.trait_data(t); 387 let data = db.trait_data(t);
351 388
@@ -465,7 +502,7 @@ fn transform_receiver_ty(
465pub fn implements_trait( 502pub fn implements_trait(
466 ty: &Canonical<Ty>, 503 ty: &Canonical<Ty>,
467 db: &impl HirDatabase, 504 db: &impl HirDatabase,
468 resolver: &Resolver, 505 env: Arc<TraitEnvironment>,
469 krate: CrateId, 506 krate: CrateId,
470 trait_: TraitId, 507 trait_: TraitId,
471) -> bool { 508) -> bool {
@@ -474,7 +511,6 @@ pub fn implements_trait(
474 // anyway, but currently Chalk doesn't implement `dyn/impl Trait` yet 511 // anyway, but currently Chalk doesn't implement `dyn/impl Trait` yet
475 return true; 512 return true;
476 } 513 }
477 let env = TraitEnvironment::lower(db, resolver);
478 let goal = generic_implements_goal(db, env, trait_, ty.clone()); 514 let goal = generic_implements_goal(db, env, trait_, ty.clone());
479 let solution = db.trait_solve(krate.into(), goal); 515 let solution = db.trait_solve(krate.into(), goal);
480 516
diff --git a/crates/ra_hir_ty/src/tests/macros.rs b/crates/ra_hir_ty/src/tests/macros.rs
index 9d09d93a7..652420ea8 100644
--- a/crates/ra_hir_ty/src/tests/macros.rs
+++ b/crates/ra_hir_ty/src/tests/macros.rs
@@ -374,7 +374,7 @@ fn main() {
374} 374}
375"#), 375"#),
376 @r###" 376 @r###"
377 ![0; 1) '6': i32 377 ![0; 1) '0': i32
378 [64; 88) '{ ...!(); }': () 378 [64; 88) '{ ...!(); }': ()
379 [74; 75) 'x': i32 379 [74; 75) 'x': i32
380 "### 380 "###
@@ -412,7 +412,7 @@ fn main() {
412} 412}
413"#), 413"#),
414 @r###" 414 @r###"
415 ![0; 2) '13': i32 415 ![0; 1) '0': i32
416 [66; 92) '{ ...!(); }': () 416 [66; 92) '{ ...!(); }': ()
417 [76; 77) 'x': i32 417 [76; 77) 'x': i32
418 "### 418 "###
diff --git a/crates/ra_ide/src/completion/complete_dot.rs b/crates/ra_ide/src/completion/complete_dot.rs
index 210a685e4..2ca78c927 100644
--- a/crates/ra_ide/src/completion/complete_dot.rs
+++ b/crates/ra_ide/src/completion/complete_dot.rs
@@ -27,7 +27,7 @@ pub(super) fn complete_dot(acc: &mut Completions, ctx: &CompletionContext) {
27 complete_methods(acc, ctx, &receiver_ty); 27 complete_methods(acc, ctx, &receiver_ty);
28 28
29 // Suggest .await syntax for types that implement Future trait 29 // Suggest .await syntax for types that implement Future trait
30 if ctx.analyzer.impls_future(ctx.db, receiver_ty) { 30 if receiver_ty.impls_future(ctx.db) {
31 CompletionItem::new(CompletionKind::Keyword, ctx.source_range(), "await") 31 CompletionItem::new(CompletionKind::Keyword, ctx.source_range(), "await")
32 .detail("expr.await") 32 .detail("expr.await")
33 .insert_text("await") 33 .insert_text("await")
@@ -53,13 +53,16 @@ fn complete_fields(acc: &mut Completions, ctx: &CompletionContext, receiver: &Ty
53} 53}
54 54
55fn complete_methods(acc: &mut Completions, ctx: &CompletionContext, receiver: &Type) { 55fn complete_methods(acc: &mut Completions, ctx: &CompletionContext, receiver: &Type) {
56 let mut seen_methods = FxHashSet::default(); 56 if let Some(krate) = ctx.module.map(|it| it.krate()) {
57 ctx.analyzer.iterate_method_candidates(ctx.db, receiver, None, |_ty, func| { 57 let mut seen_methods = FxHashSet::default();
58 if func.has_self_param(ctx.db) && seen_methods.insert(func.name(ctx.db)) { 58 let traits_in_scope = ctx.analyzer.traits_in_scope(ctx.db);
59 acc.add_function(ctx, func); 59 receiver.iterate_method_candidates(ctx.db, krate, &traits_in_scope, None, |_ty, func| {
60 } 60 if func.has_self_param(ctx.db) && seen_methods.insert(func.name(ctx.db)) {
61 None::<()> 61 acc.add_function(ctx, func);
62 }); 62 }
63 None::<()>
64 });
65 }
63} 66}
64 67
65#[cfg(test)] 68#[cfg(test)]
@@ -525,6 +528,7 @@ mod tests {
525 528
526 //- /std/lib.rs 529 //- /std/lib.rs
527 pub mod future { 530 pub mod future {
531 #[lang = "future_trait"]
528 pub trait Future {} 532 pub trait Future {}
529 } 533 }
530 "###, CompletionKind::Keyword), 534 "###, CompletionKind::Keyword),
diff --git a/crates/ra_ide/src/completion/complete_path.rs b/crates/ra_ide/src/completion/complete_path.rs
index 0dce9dc2d..af24e9f48 100644
--- a/crates/ra_ide/src/completion/complete_path.rs
+++ b/crates/ra_ide/src/completion/complete_path.rs
@@ -49,22 +49,24 @@ pub(super) fn complete_path(acc: &mut Completions, ctx: &CompletionContext) {
49 hir::ModuleDef::TypeAlias(a) => a.ty(ctx.db), 49 hir::ModuleDef::TypeAlias(a) => a.ty(ctx.db),
50 _ => unreachable!(), 50 _ => unreachable!(),
51 }; 51 };
52 ctx.analyzer.iterate_path_candidates(ctx.db, &ty, None, |_ty, item| {
53 match item {
54 hir::AssocItem::Function(func) => {
55 if !func.has_self_param(ctx.db) {
56 acc.add_function(ctx, func);
57 }
58 }
59 hir::AssocItem::Const(ct) => acc.add_const(ctx, ct),
60 hir::AssocItem::TypeAlias(ty) => acc.add_type_alias(ctx, ty),
61 }
62 None::<()>
63 });
64 // Iterate assoc types separately 52 // Iterate assoc types separately
65 // FIXME: complete T::AssocType 53 // FIXME: complete T::AssocType
66 let krate = ctx.module.map(|m| m.krate()); 54 let krate = ctx.module.map(|m| m.krate());
67 if let Some(krate) = krate { 55 if let Some(krate) = krate {
56 let traits_in_scope = ctx.analyzer.traits_in_scope(ctx.db);
57 ty.iterate_path_candidates(ctx.db, krate, &traits_in_scope, None, |_ty, item| {
58 match item {
59 hir::AssocItem::Function(func) => {
60 if !func.has_self_param(ctx.db) {
61 acc.add_function(ctx, func);
62 }
63 }
64 hir::AssocItem::Const(ct) => acc.add_const(ctx, ct),
65 hir::AssocItem::TypeAlias(ty) => acc.add_type_alias(ctx, ty),
66 }
67 None::<()>
68 });
69
68 ty.iterate_impl_items(ctx.db, krate, |item| { 70 ty.iterate_impl_items(ctx.db, krate, |item| {
69 match item { 71 match item {
70 hir::AssocItem::Function(_) | hir::AssocItem::Const(_) => {} 72 hir::AssocItem::Function(_) | hir::AssocItem::Const(_) => {}
diff --git a/crates/ra_lsp_server/src/main_loop/handlers.rs b/crates/ra_lsp_server/src/main_loop/handlers.rs
index a9a8538b7..f2db575ea 100644
--- a/crates/ra_lsp_server/src/main_loop/handlers.rs
+++ b/crates/ra_lsp_server/src/main_loop/handlers.rs
@@ -710,6 +710,7 @@ pub fn handle_code_action(
710 title: command.title.clone(), 710 title: command.title.clone(),
711 kind: match assist.id { 711 kind: match assist.id {
712 AssistId("introduce_variable") => Some("refactor.extract.variable".to_string()), 712 AssistId("introduce_variable") => Some("refactor.extract.variable".to_string()),
713 AssistId("add_custom_impl") => Some("refactor.rewrite.add_custom_impl".to_string()),
713 _ => None, 714 _ => None,
714 }, 715 },
715 diagnostics: None, 716 diagnostics: None,
diff --git a/crates/ra_syntax/src/validation.rs b/crates/ra_syntax/src/validation.rs
index 222ac15f8..445e3b3e4 100644
--- a/crates/ra_syntax/src/validation.rs
+++ b/crates/ra_syntax/src/validation.rs
@@ -111,55 +111,46 @@ pub(crate) fn validate(root: &SyntaxNode) -> Vec<SyntaxError> {
111 errors 111 errors
112} 112}
113 113
114// FIXME: kill duplication
115fn validate_literal(literal: ast::Literal, acc: &mut Vec<SyntaxError>) { 114fn validate_literal(literal: ast::Literal, acc: &mut Vec<SyntaxError>) {
115 fn unquote(text: &str, prefix_len: usize, end_delimiter: char) -> Option<&str> {
116 text.rfind(end_delimiter).and_then(|end| text.get(prefix_len..end))
117 }
118
116 let token = literal.token(); 119 let token = literal.token();
117 let text = token.text().as_str(); 120 let text = token.text().as_str();
121
122 let mut push_err = |prefix_len, (off, err): (usize, unescape::EscapeError)| {
123 let off = token.text_range().start() + TextUnit::from_usize(off + prefix_len);
124 acc.push(SyntaxError::new(err.into(), off));
125 };
126
118 match token.kind() { 127 match token.kind() {
119 BYTE => { 128 BYTE => {
120 if let Some(end) = text.rfind('\'') { 129 if let Some(Err(e)) = unquote(text, 2, '\'').map(unescape::unescape_byte) {
121 if let Some(without_quotes) = text.get(2..end) { 130 push_err(2, e);
122 if let Err((off, err)) = unescape::unescape_byte(without_quotes) {
123 let off = token.text_range().start() + TextUnit::from_usize(off + 2);
124 acc.push(SyntaxError::new(err.into(), off))
125 }
126 }
127 } 131 }
128 } 132 }
129 CHAR => { 133 CHAR => {
130 if let Some(end) = text.rfind('\'') { 134 if let Some(Err(e)) = unquote(text, 1, '\'').map(unescape::unescape_char) {
131 if let Some(without_quotes) = text.get(1..end) { 135 push_err(1, e);
132 if let Err((off, err)) = unescape::unescape_char(without_quotes) {
133 let off = token.text_range().start() + TextUnit::from_usize(off + 1);
134 acc.push(SyntaxError::new(err.into(), off))
135 }
136 }
137 } 136 }
138 } 137 }
139 BYTE_STRING => { 138 BYTE_STRING => {
140 if let Some(end) = text.rfind('\"') { 139 if let Some(without_quotes) = unquote(text, 2, '"') {
141 if let Some(without_quotes) = text.get(2..end) { 140 unescape::unescape_byte_str(without_quotes, &mut |range, char| {
142 unescape::unescape_byte_str(without_quotes, &mut |range, char| { 141 if let Err(err) = char {
143 if let Err(err) = char { 142 push_err(2, (range.start, err));
144 let off = range.start; 143 }
145 let off = token.text_range().start() + TextUnit::from_usize(off + 2); 144 })
146 acc.push(SyntaxError::new(err.into(), off))
147 }
148 })
149 }
150 } 145 }
151 } 146 }
152 STRING => { 147 STRING => {
153 if let Some(end) = text.rfind('\"') { 148 if let Some(without_quotes) = unquote(text, 1, '"') {
154 if let Some(without_quotes) = text.get(1..end) { 149 unescape::unescape_str(without_quotes, &mut |range, char| {
155 unescape::unescape_str(without_quotes, &mut |range, char| { 150 if let Err(err) = char {
156 if let Err(err) = char { 151 push_err(1, (range.start, err));
157 let off = range.start; 152 }
158 let off = token.text_range().start() + TextUnit::from_usize(off + 1); 153 })
159 acc.push(SyntaxError::new(err.into(), off))
160 }
161 })
162 }
163 } 154 }
164 } 155 }
165 _ => (), 156 _ => (),
diff --git a/editors/code/package-lock.json b/editors/code/package-lock.json
index 3059323aa..05c57c7ff 100644
--- a/editors/code/package-lock.json
+++ b/editors/code/package-lock.json
@@ -889,32 +889,32 @@
889 } 889 }
890 }, 890 },
891 "vscode-jsonrpc": { 891 "vscode-jsonrpc": {
892 "version": "5.0.0-next.5", 892 "version": "5.0.0",
893 "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-5.0.0-next.5.tgz", 893 "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-5.0.0.tgz",
894 "integrity": "sha512-k9akfglxWgr0dtLNscq2uBq48XJwnhf4EaDxn05KQowRwR0DkNML0zeYqFRLtXZe6x5vpL5ppyu4o6GqL+23YQ==" 894 "integrity": "sha512-QeAniC/xTWauVQgyNgEqNJ0Qm/Jw8QySGRQhRFPwb8c4FPp9k6QNgJp0ayXWws5qhdaHkiXkGPlzjOPZFQQKLw=="
895 }, 895 },
896 "vscode-languageclient": { 896 "vscode-languageclient": {
897 "version": "6.0.0-next.9", 897 "version": "6.0.0",
898 "resolved": "https://registry.npmjs.org/vscode-languageclient/-/vscode-languageclient-6.0.0-next.9.tgz", 898 "resolved": "https://registry.npmjs.org/vscode-languageclient/-/vscode-languageclient-6.0.0.tgz",
899 "integrity": "sha512-NEpeeFM9FKrrRqlBHXGfwpkhtnjruDz3zfFBP+Cymr10qigAEtE/JsODJsIG/ErGqjh3/JXxu8SUOVTGu5oK+w==", 899 "integrity": "sha512-6MDksAP79GRbcHFsXS6ndo12s0m/h7eNdS/IanRgWxaezqB4a4KzHIHrE0bE+nSFB+snaSToGs1bxnPeKdO5fQ==",
900 "requires": { 900 "requires": {
901 "semver": "^6.3.0", 901 "semver": "^6.3.0",
902 "vscode-languageserver-protocol": "^3.15.0-next.14" 902 "vscode-languageserver-protocol": "^3.15.0"
903 } 903 }
904 }, 904 },
905 "vscode-languageserver-protocol": { 905 "vscode-languageserver-protocol": {
906 "version": "3.15.0-next.14", 906 "version": "3.15.0",
907 "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.15.0-next.14.tgz", 907 "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.15.0.tgz",
908 "integrity": "sha512-xUwwno6Q6RFd2Z2EWV9D3dQlsKPnHyiZMNWq+EC7JJdp2WH1gRlD+KPX4UGRCnJK0WI5omqHV313IESPwRY5xA==", 908 "integrity": "sha512-PZEopQzHR3Lo422HeDxCpGN0sYz+kReO+du3F/AcFT1cCPunoVsDJv7ikEolFRKEn+hEIZiAaIX4yoSZ+ip5Nw==",
909 "requires": { 909 "requires": {
910 "vscode-jsonrpc": "^5.0.0-next.5", 910 "vscode-jsonrpc": "^5.0.0",
911 "vscode-languageserver-types": "^3.15.0-next.9" 911 "vscode-languageserver-types": "3.15.0"
912 } 912 }
913 }, 913 },
914 "vscode-languageserver-types": { 914 "vscode-languageserver-types": {
915 "version": "3.15.0-next.9", 915 "version": "3.15.0",
916 "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.15.0-next.9.tgz", 916 "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.15.0.tgz",
917 "integrity": "sha512-Rl/8qJ6932nrHCdPn+9y0x08uLVQaSLRG+U4JzhyKpWU4eJbVaDRoAcz1Llj7CErJGbPr6kdBvShPy5fRfR+Uw==" 917 "integrity": "sha512-AXteNagMhBWnZ6gNN0UB4HTiD/7TajgfHl6jaM6O7qz3zDJw0H3Jf83w05phihnBRCML+K6Ockh8f8bL0OObPw=="
918 }, 918 },
919 "wrappy": { 919 "wrappy": {
920 "version": "1.0.2", 920 "version": "1.0.2",
diff --git a/editors/code/package.json b/editors/code/package.json
index ed637d114..d4c310734 100644
--- a/editors/code/package.json
+++ b/editors/code/package.json
@@ -25,7 +25,7 @@
25 "dependencies": { 25 "dependencies": {
26 "jsonc-parser": "^2.1.0", 26 "jsonc-parser": "^2.1.0",
27 "seedrandom": "^3.0.5", 27 "seedrandom": "^3.0.5",
28 "vscode-languageclient": "^6.0.0-next.9" 28 "vscode-languageclient": "^6.0.0"
29 }, 29 },
30 "devDependencies": { 30 "devDependencies": {
31 "@rollup/plugin-commonjs": "^11.0.0", 31 "@rollup/plugin-commonjs": "^11.0.0",
diff --git a/editors/code/src/config.ts b/editors/code/src/config.ts
index ec2790b63..fc21c8813 100644
--- a/editors/code/src/config.ts
+++ b/editors/code/src/config.ts
@@ -42,6 +42,7 @@ export class Config {
42 42
43 private prevEnhancedTyping: null | boolean = null; 43 private prevEnhancedTyping: null | boolean = null;
44 private prevCargoFeatures: null | CargoFeatures = null; 44 private prevCargoFeatures: null | CargoFeatures = null;
45 private prevCargoWatchOptions: null | CargoWatchOptions = null;
45 46
46 constructor(ctx: vscode.ExtensionContext) { 47 constructor(ctx: vscode.ExtensionContext) {
47 vscode.workspace.onDidChangeConfiguration(_ => this.refresh(), ctx.subscriptions); 48 vscode.workspace.onDidChangeConfiguration(_ => this.refresh(), ctx.subscriptions);
@@ -174,6 +175,21 @@ export class Config {
174 } 175 }
175 this.prevCargoFeatures = { ...this.cargoFeatures }; 176 this.prevCargoFeatures = { ...this.cargoFeatures };
176 177
178 if (this.prevCargoWatchOptions !== null) {
179 const changed =
180 this.cargoWatchOptions.enable !== this.prevCargoWatchOptions.enable ||
181 this.cargoWatchOptions.command !== this.prevCargoWatchOptions.command ||
182 this.cargoWatchOptions.allTargets !== this.prevCargoWatchOptions.allTargets ||
183 this.cargoWatchOptions.arguments.length !== this.prevCargoWatchOptions.arguments.length ||
184 this.cargoWatchOptions.arguments.some(
185 (v, i) => v !== this.prevCargoWatchOptions!.arguments[i],
186 );
187 if (changed) {
188 requireReloadMessage = 'Changing cargo-watch options requires a reload';
189 }
190 }
191 this.prevCargoWatchOptions = { ...this.cargoWatchOptions };
192
177 if (requireReloadMessage !== null) { 193 if (requireReloadMessage !== null) {
178 const reloadAction = 'Reload now'; 194 const reloadAction = 'Reload now';
179 vscode.window 195 vscode.window