diff options
Diffstat (limited to 'crates')
49 files changed, 2237 insertions, 1566 deletions
diff --git a/crates/ra_assists/src/assist_context.rs b/crates/ra_assists/src/assist_context.rs index 3085c4330..a680f752b 100644 --- a/crates/ra_assists/src/assist_context.rs +++ b/crates/ra_assists/src/assist_context.rs | |||
@@ -49,7 +49,7 @@ use crate::{Assist, AssistId, GroupLabel, ResolvedAssist}; | |||
49 | /// easier to just compute the edit eagerly :-) | 49 | /// easier to just compute the edit eagerly :-) |
50 | pub(crate) struct AssistContext<'a> { | 50 | pub(crate) struct AssistContext<'a> { |
51 | pub(crate) sema: Semantics<'a, RootDatabase>, | 51 | pub(crate) sema: Semantics<'a, RootDatabase>, |
52 | pub(super) db: &'a RootDatabase, | 52 | pub(crate) db: &'a RootDatabase, |
53 | pub(crate) frange: FileRange, | 53 | pub(crate) frange: FileRange, |
54 | source_file: SourceFile, | 54 | source_file: SourceFile, |
55 | } | 55 | } |
diff --git a/crates/ra_assists/src/handlers/add_explicit_type.rs b/crates/ra_assists/src/handlers/add_explicit_type.rs index 55409e501..7ced00626 100644 --- a/crates/ra_assists/src/handlers/add_explicit_type.rs +++ b/crates/ra_assists/src/handlers/add_explicit_type.rs | |||
@@ -23,6 +23,7 @@ use crate::{AssistContext, AssistId, Assists}; | |||
23 | // ``` | 23 | // ``` |
24 | pub(crate) fn add_explicit_type(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { | 24 | pub(crate) fn add_explicit_type(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { |
25 | let stmt = ctx.find_node_at_offset::<LetStmt>()?; | 25 | let stmt = ctx.find_node_at_offset::<LetStmt>()?; |
26 | let module = ctx.sema.scope(stmt.syntax()).module()?; | ||
26 | let expr = stmt.initializer()?; | 27 | let expr = stmt.initializer()?; |
27 | let pat = stmt.pat()?; | 28 | let pat = stmt.pat()?; |
28 | // Must be a binding | 29 | // Must be a binding |
@@ -57,17 +58,17 @@ pub(crate) fn add_explicit_type(acc: &mut Assists, ctx: &AssistContext) -> Optio | |||
57 | return None; | 58 | return None; |
58 | } | 59 | } |
59 | 60 | ||
60 | let db = ctx.db; | 61 | let inferred_type = ty.display_source_code(ctx.db, module.into()).ok()?; |
61 | let new_type_string = ty.display_truncated(db, None).to_string(); | ||
62 | acc.add( | 62 | acc.add( |
63 | AssistId("add_explicit_type"), | 63 | AssistId("add_explicit_type"), |
64 | format!("Insert explicit type '{}'", new_type_string), | 64 | format!("Insert explicit type '{}'", inferred_type), |
65 | pat_range, | 65 | pat_range, |
66 | |edit| { | 66 | |builder| match ascribed_ty { |
67 | if let Some(ascribed_ty) = ascribed_ty { | 67 | Some(ascribed_ty) => { |
68 | edit.replace(ascribed_ty.syntax().text_range(), new_type_string); | 68 | builder.replace(ascribed_ty.syntax().text_range(), inferred_type); |
69 | } else { | 69 | } |
70 | edit.insert(name_range.end(), format!(": {}", new_type_string)); | 70 | None => { |
71 | builder.insert(name_range.end(), format!(": {}", inferred_type)); | ||
71 | } | 72 | } |
72 | }, | 73 | }, |
73 | ) | 74 | ) |
diff --git a/crates/ra_assists/src/handlers/add_from_impl_for_enum.rs b/crates/ra_assists/src/handlers/add_from_impl_for_enum.rs index 275184e24..6a49b7dbd 100644 --- a/crates/ra_assists/src/handlers/add_from_impl_for_enum.rs +++ b/crates/ra_assists/src/handlers/add_from_impl_for_enum.rs | |||
@@ -1,8 +1,5 @@ | |||
1 | use ra_ide_db::RootDatabase; | 1 | use ra_ide_db::RootDatabase; |
2 | use ra_syntax::{ | 2 | use ra_syntax::ast::{self, AstNode, NameOwner}; |
3 | ast::{self, AstNode, NameOwner}, | ||
4 | TextSize, | ||
5 | }; | ||
6 | use stdx::format_to; | 3 | use stdx::format_to; |
7 | use test_utils::tested_by; | 4 | use test_utils::tested_by; |
8 | 5 | ||
@@ -69,7 +66,6 @@ impl From<{0}> for {1} {{ | |||
69 | variant_name | 66 | variant_name |
70 | ); | 67 | ); |
71 | edit.insert(start_offset, buf); | 68 | edit.insert(start_offset, buf); |
72 | edit.set_cursor(start_offset + TextSize::of("\n\n")); | ||
73 | }, | 69 | }, |
74 | ) | 70 | ) |
75 | } | 71 | } |
@@ -97,19 +93,20 @@ fn existing_from_impl( | |||
97 | 93 | ||
98 | #[cfg(test)] | 94 | #[cfg(test)] |
99 | mod tests { | 95 | mod tests { |
100 | use super::*; | 96 | use test_utils::covers; |
101 | 97 | ||
102 | use crate::tests::{check_assist, check_assist_not_applicable}; | 98 | use crate::tests::{check_assist, check_assist_not_applicable}; |
103 | use test_utils::covers; | 99 | |
100 | use super::*; | ||
104 | 101 | ||
105 | #[test] | 102 | #[test] |
106 | fn test_add_from_impl_for_enum() { | 103 | fn test_add_from_impl_for_enum() { |
107 | check_assist( | 104 | check_assist( |
108 | add_from_impl_for_enum, | 105 | add_from_impl_for_enum, |
109 | "enum A { <|>One(u32) }", | 106 | "enum A { <|>One(u32) }", |
110 | r#"enum A { One(u32) } | 107 | r#"enum A { <|>One(u32) } |
111 | 108 | ||
112 | <|>impl From<u32> for A { | 109 | impl From<u32> for A { |
113 | fn from(v: u32) -> Self { | 110 | fn from(v: u32) -> Self { |
114 | A::One(v) | 111 | A::One(v) |
115 | } | 112 | } |
@@ -121,10 +118,10 @@ mod tests { | |||
121 | fn test_add_from_impl_for_enum_complicated_path() { | 118 | fn test_add_from_impl_for_enum_complicated_path() { |
122 | check_assist( | 119 | check_assist( |
123 | add_from_impl_for_enum, | 120 | add_from_impl_for_enum, |
124 | "enum A { <|>One(foo::bar::baz::Boo) }", | 121 | r#"enum A { <|>One(foo::bar::baz::Boo) }"#, |
125 | r#"enum A { One(foo::bar::baz::Boo) } | 122 | r#"enum A { <|>One(foo::bar::baz::Boo) } |
126 | 123 | ||
127 | <|>impl From<foo::bar::baz::Boo> for A { | 124 | impl From<foo::bar::baz::Boo> for A { |
128 | fn from(v: foo::bar::baz::Boo) -> Self { | 125 | fn from(v: foo::bar::baz::Boo) -> Self { |
129 | A::One(v) | 126 | A::One(v) |
130 | } | 127 | } |
@@ -184,9 +181,9 @@ impl From<String> for A { | |||
184 | pub trait From<T> { | 181 | pub trait From<T> { |
185 | fn from(T) -> Self; | 182 | fn from(T) -> Self; |
186 | }"#, | 183 | }"#, |
187 | r#"enum A { One(u32), Two(String), } | 184 | r#"enum A { <|>One(u32), Two(String), } |
188 | 185 | ||
189 | <|>impl From<u32> for A { | 186 | impl From<u32> for A { |
190 | fn from(v: u32) -> Self { | 187 | fn from(v: u32) -> Self { |
191 | A::One(v) | 188 | A::One(v) |
192 | } | 189 | } |
diff --git a/crates/ra_assists/src/handlers/add_function.rs b/crates/ra_assists/src/handlers/add_function.rs index 6b5616aa9..de016ae4e 100644 --- a/crates/ra_assists/src/handlers/add_function.rs +++ b/crates/ra_assists/src/handlers/add_function.rs | |||
@@ -1,7 +1,11 @@ | |||
1 | use hir::HirDisplay; | 1 | use hir::HirDisplay; |
2 | use ra_db::FileId; | 2 | use ra_db::FileId; |
3 | use ra_syntax::{ | 3 | use ra_syntax::{ |
4 | ast::{self, edit::IndentLevel, ArgListOwner, AstNode, ModuleItemOwner}, | 4 | ast::{ |
5 | self, | ||
6 | edit::{AstNodeEdit, IndentLevel}, | ||
7 | ArgListOwner, AstNode, ModuleItemOwner, | ||
8 | }, | ||
5 | SyntaxKind, SyntaxNode, TextSize, | 9 | SyntaxKind, SyntaxNode, TextSize, |
6 | }; | 10 | }; |
7 | use rustc_hash::{FxHashMap, FxHashSet}; | 11 | use rustc_hash::{FxHashMap, FxHashSet}; |
@@ -43,16 +47,12 @@ pub(crate) fn add_function(acc: &mut Assists, ctx: &AssistContext) -> Option<()> | |||
43 | return None; | 47 | return None; |
44 | } | 48 | } |
45 | 49 | ||
46 | let target_module = if let Some(qualifier) = path.qualifier() { | 50 | let target_module = match path.qualifier() { |
47 | if let Some(hir::PathResolution::Def(hir::ModuleDef::Module(module))) = | 51 | Some(qualifier) => match ctx.sema.resolve_path(&qualifier) { |
48 | ctx.sema.resolve_path(&qualifier) | 52 | Some(hir::PathResolution::Def(hir::ModuleDef::Module(module))) => Some(module), |
49 | { | 53 | _ => return None, |
50 | Some(module.definition_source(ctx.sema.db)) | 54 | }, |
51 | } else { | 55 | None => None, |
52 | return None; | ||
53 | } | ||
54 | } else { | ||
55 | None | ||
56 | }; | 56 | }; |
57 | 57 | ||
58 | let function_builder = FunctionBuilder::from_call(&ctx, &call, &path, target_module)?; | 58 | let function_builder = FunctionBuilder::from_call(&ctx, &call, &path, target_module)?; |
@@ -83,25 +83,29 @@ struct FunctionBuilder { | |||
83 | } | 83 | } |
84 | 84 | ||
85 | impl FunctionBuilder { | 85 | impl FunctionBuilder { |
86 | /// Prepares a generated function that matches `call` in `generate_in` | 86 | /// Prepares a generated function that matches `call`. |
87 | /// (or as close to `call` as possible, if `generate_in` is `None`) | 87 | /// The function is generated in `target_module` or next to `call` |
88 | fn from_call( | 88 | fn from_call( |
89 | ctx: &AssistContext, | 89 | ctx: &AssistContext, |
90 | call: &ast::CallExpr, | 90 | call: &ast::CallExpr, |
91 | path: &ast::Path, | 91 | path: &ast::Path, |
92 | target_module: Option<hir::InFile<hir::ModuleSource>>, | 92 | target_module: Option<hir::Module>, |
93 | ) -> Option<Self> { | 93 | ) -> Option<Self> { |
94 | let needs_pub = target_module.is_some(); | ||
95 | let mut file = ctx.frange.file_id; | 94 | let mut file = ctx.frange.file_id; |
96 | let target = if let Some(target_module) = target_module { | 95 | let target = match &target_module { |
97 | let (in_file, target) = next_space_for_fn_in_module(ctx.sema.db, target_module)?; | 96 | Some(target_module) => { |
98 | file = in_file; | 97 | let module_source = target_module.definition_source(ctx.db); |
99 | target | 98 | let (in_file, target) = next_space_for_fn_in_module(ctx.sema.db, &module_source)?; |
100 | } else { | 99 | file = in_file; |
101 | next_space_for_fn_after_call_site(&call)? | 100 | target |
101 | } | ||
102 | None => next_space_for_fn_after_call_site(&call)?, | ||
102 | }; | 103 | }; |
104 | let needs_pub = target_module.is_some(); | ||
105 | let target_module = target_module.or_else(|| ctx.sema.scope(target.syntax()).module())?; | ||
103 | let fn_name = fn_name(&path)?; | 106 | let fn_name = fn_name(&path)?; |
104 | let (type_params, params) = fn_args(ctx, &call)?; | 107 | let (type_params, params) = fn_args(ctx, target_module, &call)?; |
108 | |||
105 | Some(Self { target, fn_name, type_params, params, file, needs_pub }) | 109 | Some(Self { target, fn_name, type_params, params, file, needs_pub }) |
106 | } | 110 | } |
107 | 111 | ||
@@ -116,17 +120,16 @@ impl FunctionBuilder { | |||
116 | let (fn_def, insert_offset) = match self.target { | 120 | let (fn_def, insert_offset) = match self.target { |
117 | GeneratedFunctionTarget::BehindItem(it) => { | 121 | GeneratedFunctionTarget::BehindItem(it) => { |
118 | let with_leading_blank_line = ast::make::add_leading_newlines(2, fn_def); | 122 | let with_leading_blank_line = ast::make::add_leading_newlines(2, fn_def); |
119 | let indented = IndentLevel::from_node(&it).increase_indent(with_leading_blank_line); | 123 | let indented = with_leading_blank_line.indent(IndentLevel::from_node(&it)); |
120 | (indented, it.text_range().end()) | 124 | (indented, it.text_range().end()) |
121 | } | 125 | } |
122 | GeneratedFunctionTarget::InEmptyItemList(it) => { | 126 | GeneratedFunctionTarget::InEmptyItemList(it) => { |
123 | let indent_once = IndentLevel(1); | 127 | let indent_once = IndentLevel(1); |
124 | let indent = IndentLevel::from_node(it.syntax()); | 128 | let indent = IndentLevel::from_node(it.syntax()); |
125 | |||
126 | let fn_def = ast::make::add_leading_newlines(1, fn_def); | 129 | let fn_def = ast::make::add_leading_newlines(1, fn_def); |
127 | let fn_def = indent_once.increase_indent(fn_def); | 130 | let fn_def = fn_def.indent(indent_once); |
128 | let fn_def = ast::make::add_trailing_newlines(1, fn_def); | 131 | let fn_def = ast::make::add_trailing_newlines(1, fn_def); |
129 | let fn_def = indent.increase_indent(fn_def); | 132 | let fn_def = fn_def.indent(indent); |
130 | (fn_def, it.syntax().text_range().start() + TextSize::of('{')) | 133 | (fn_def, it.syntax().text_range().start() + TextSize::of('{')) |
131 | } | 134 | } |
132 | }; | 135 | }; |
@@ -144,6 +147,15 @@ enum GeneratedFunctionTarget { | |||
144 | InEmptyItemList(ast::ItemList), | 147 | InEmptyItemList(ast::ItemList), |
145 | } | 148 | } |
146 | 149 | ||
150 | impl GeneratedFunctionTarget { | ||
151 | fn syntax(&self) -> &SyntaxNode { | ||
152 | match self { | ||
153 | GeneratedFunctionTarget::BehindItem(it) => it, | ||
154 | GeneratedFunctionTarget::InEmptyItemList(it) => it.syntax(), | ||
155 | } | ||
156 | } | ||
157 | } | ||
158 | |||
147 | fn fn_name(call: &ast::Path) -> Option<ast::Name> { | 159 | fn fn_name(call: &ast::Path) -> Option<ast::Name> { |
148 | let name = call.segment()?.syntax().to_string(); | 160 | let name = call.segment()?.syntax().to_string(); |
149 | Some(ast::make::name(&name)) | 161 | Some(ast::make::name(&name)) |
@@ -152,17 +164,17 @@ fn fn_name(call: &ast::Path) -> Option<ast::Name> { | |||
152 | /// Computes the type variables and arguments required for the generated function | 164 | /// Computes the type variables and arguments required for the generated function |
153 | fn fn_args( | 165 | fn fn_args( |
154 | ctx: &AssistContext, | 166 | ctx: &AssistContext, |
167 | target_module: hir::Module, | ||
155 | call: &ast::CallExpr, | 168 | call: &ast::CallExpr, |
156 | ) -> Option<(Option<ast::TypeParamList>, ast::ParamList)> { | 169 | ) -> Option<(Option<ast::TypeParamList>, ast::ParamList)> { |
157 | let mut arg_names = Vec::new(); | 170 | let mut arg_names = Vec::new(); |
158 | let mut arg_types = Vec::new(); | 171 | let mut arg_types = Vec::new(); |
159 | for arg in call.arg_list()?.args() { | 172 | for arg in call.arg_list()?.args() { |
160 | let arg_name = match fn_arg_name(&arg) { | 173 | arg_names.push(match fn_arg_name(&arg) { |
161 | Some(name) => name, | 174 | Some(name) => name, |
162 | None => String::from("arg"), | 175 | None => String::from("arg"), |
163 | }; | 176 | }); |
164 | arg_names.push(arg_name); | 177 | arg_types.push(match fn_arg_type(ctx, target_module, &arg) { |
165 | arg_types.push(match fn_arg_type(ctx, &arg) { | ||
166 | Some(ty) => ty, | 178 | Some(ty) => ty, |
167 | None => String::from("()"), | 179 | None => String::from("()"), |
168 | }); | 180 | }); |
@@ -218,12 +230,21 @@ fn fn_arg_name(fn_arg: &ast::Expr) -> Option<String> { | |||
218 | } | 230 | } |
219 | } | 231 | } |
220 | 232 | ||
221 | fn fn_arg_type(ctx: &AssistContext, fn_arg: &ast::Expr) -> Option<String> { | 233 | fn fn_arg_type( |
234 | ctx: &AssistContext, | ||
235 | target_module: hir::Module, | ||
236 | fn_arg: &ast::Expr, | ||
237 | ) -> Option<String> { | ||
222 | let ty = ctx.sema.type_of_expr(fn_arg)?; | 238 | let ty = ctx.sema.type_of_expr(fn_arg)?; |
223 | if ty.is_unknown() { | 239 | if ty.is_unknown() { |
224 | return None; | 240 | return None; |
225 | } | 241 | } |
226 | Some(ty.display(ctx.sema.db).to_string()) | 242 | |
243 | if let Ok(rendered) = ty.display_source_code(ctx.db, target_module.into()) { | ||
244 | Some(rendered) | ||
245 | } else { | ||
246 | None | ||
247 | } | ||
227 | } | 248 | } |
228 | 249 | ||
229 | /// Returns the position inside the current mod or file | 250 | /// Returns the position inside the current mod or file |
@@ -252,10 +273,10 @@ fn next_space_for_fn_after_call_site(expr: &ast::CallExpr) -> Option<GeneratedFu | |||
252 | 273 | ||
253 | fn next_space_for_fn_in_module( | 274 | fn next_space_for_fn_in_module( |
254 | db: &dyn hir::db::AstDatabase, | 275 | db: &dyn hir::db::AstDatabase, |
255 | module: hir::InFile<hir::ModuleSource>, | 276 | module_source: &hir::InFile<hir::ModuleSource>, |
256 | ) -> Option<(FileId, GeneratedFunctionTarget)> { | 277 | ) -> Option<(FileId, GeneratedFunctionTarget)> { |
257 | let file = module.file_id.original_file(db); | 278 | let file = module_source.file_id.original_file(db); |
258 | let assist_item = match module.value { | 279 | let assist_item = match &module_source.value { |
259 | hir::ModuleSource::SourceFile(it) => { | 280 | hir::ModuleSource::SourceFile(it) => { |
260 | if let Some(last_item) = it.items().last() { | 281 | if let Some(last_item) = it.items().last() { |
261 | GeneratedFunctionTarget::BehindItem(last_item.syntax().clone()) | 282 | GeneratedFunctionTarget::BehindItem(last_item.syntax().clone()) |
@@ -599,8 +620,33 @@ fn bar(foo: impl Foo) { | |||
599 | } | 620 | } |
600 | 621 | ||
601 | #[test] | 622 | #[test] |
602 | #[ignore] | 623 | fn borrowed_arg() { |
603 | // FIXME print paths properly to make this test pass | 624 | check_assist( |
625 | add_function, | ||
626 | r" | ||
627 | struct Baz; | ||
628 | fn baz() -> Baz { todo!() } | ||
629 | |||
630 | fn foo() { | ||
631 | bar<|>(&baz()) | ||
632 | } | ||
633 | ", | ||
634 | r" | ||
635 | struct Baz; | ||
636 | fn baz() -> Baz { todo!() } | ||
637 | |||
638 | fn foo() { | ||
639 | bar(&baz()) | ||
640 | } | ||
641 | |||
642 | fn bar(baz: &Baz) { | ||
643 | <|>todo!() | ||
644 | } | ||
645 | ", | ||
646 | ) | ||
647 | } | ||
648 | |||
649 | #[test] | ||
604 | fn add_function_with_qualified_path_arg() { | 650 | fn add_function_with_qualified_path_arg() { |
605 | check_assist( | 651 | check_assist( |
606 | add_function, | 652 | add_function, |
@@ -609,10 +655,8 @@ mod Baz { | |||
609 | pub struct Bof; | 655 | pub struct Bof; |
610 | pub fn baz() -> Bof { Bof } | 656 | pub fn baz() -> Bof { Bof } |
611 | } | 657 | } |
612 | mod Foo { | 658 | fn foo() { |
613 | fn foo() { | 659 | <|>bar(Baz::baz()) |
614 | <|>bar(super::Baz::baz()) | ||
615 | } | ||
616 | } | 660 | } |
617 | ", | 661 | ", |
618 | r" | 662 | r" |
@@ -620,14 +664,12 @@ mod Baz { | |||
620 | pub struct Bof; | 664 | pub struct Bof; |
621 | pub fn baz() -> Bof { Bof } | 665 | pub fn baz() -> Bof { Bof } |
622 | } | 666 | } |
623 | mod Foo { | 667 | fn foo() { |
624 | fn foo() { | 668 | bar(Baz::baz()) |
625 | bar(super::Baz::baz()) | 669 | } |
626 | } | ||
627 | 670 | ||
628 | fn bar(baz: super::Baz::Bof) { | 671 | fn bar(baz: Baz::Bof) { |
629 | <|>todo!() | 672 | <|>todo!() |
630 | } | ||
631 | } | 673 | } |
632 | ", | 674 | ", |
633 | ) | 675 | ) |
@@ -809,6 +851,40 @@ fn foo() { | |||
809 | } | 851 | } |
810 | 852 | ||
811 | #[test] | 853 | #[test] |
854 | #[ignore] | ||
855 | // Ignored until local imports are supported. | ||
856 | // See https://github.com/rust-analyzer/rust-analyzer/issues/1165 | ||
857 | fn qualified_path_uses_correct_scope() { | ||
858 | check_assist( | ||
859 | add_function, | ||
860 | " | ||
861 | mod foo { | ||
862 | pub struct Foo; | ||
863 | } | ||
864 | fn bar() { | ||
865 | use foo::Foo; | ||
866 | let foo = Foo; | ||
867 | baz<|>(foo) | ||
868 | } | ||
869 | ", | ||
870 | " | ||
871 | mod foo { | ||
872 | pub struct Foo; | ||
873 | } | ||
874 | fn bar() { | ||
875 | use foo::Foo; | ||
876 | let foo = Foo; | ||
877 | baz(foo) | ||
878 | } | ||
879 | |||
880 | fn baz(foo: foo::Foo) { | ||
881 | <|>todo!() | ||
882 | } | ||
883 | ", | ||
884 | ) | ||
885 | } | ||
886 | |||
887 | #[test] | ||
812 | fn add_function_in_module_containing_other_items() { | 888 | fn add_function_in_module_containing_other_items() { |
813 | check_assist( | 889 | check_assist( |
814 | add_function, | 890 | add_function, |
@@ -920,21 +996,6 @@ fn bar(baz: ()) {} | |||
920 | } | 996 | } |
921 | 997 | ||
922 | #[test] | 998 | #[test] |
923 | fn add_function_not_applicable_if_function_path_not_singleton() { | ||
924 | // In the future this assist could be extended to generate functions | ||
925 | // if the path is in the same crate (or even the same workspace). | ||
926 | // For the beginning, I think this is fine. | ||
927 | check_assist_not_applicable( | ||
928 | add_function, | ||
929 | r" | ||
930 | fn foo() { | ||
931 | other_crate::bar<|>(); | ||
932 | } | ||
933 | ", | ||
934 | ) | ||
935 | } | ||
936 | |||
937 | #[test] | ||
938 | #[ignore] | 999 | #[ignore] |
939 | fn create_method_with_no_args() { | 1000 | fn create_method_with_no_args() { |
940 | check_assist( | 1001 | check_assist( |
diff --git a/crates/ra_assists/src/handlers/add_missing_impl_members.rs b/crates/ra_assists/src/handlers/add_missing_impl_members.rs index 3482a75bf..c1ce87914 100644 --- a/crates/ra_assists/src/handlers/add_missing_impl_members.rs +++ b/crates/ra_assists/src/handlers/add_missing_impl_members.rs | |||
@@ -2,7 +2,7 @@ use hir::HasSource; | |||
2 | use ra_syntax::{ | 2 | use ra_syntax::{ |
3 | ast::{ | 3 | ast::{ |
4 | self, | 4 | self, |
5 | edit::{self, IndentLevel}, | 5 | edit::{self, AstNodeEdit, IndentLevel}, |
6 | make, AstNode, NameOwner, | 6 | make, AstNode, NameOwner, |
7 | }, | 7 | }, |
8 | SmolStr, | 8 | SmolStr, |
@@ -176,8 +176,7 @@ fn add_body(fn_def: ast::FnDef) -> ast::FnDef { | |||
176 | if fn_def.body().is_some() { | 176 | if fn_def.body().is_some() { |
177 | return fn_def; | 177 | return fn_def; |
178 | } | 178 | } |
179 | let body = make::block_expr(None, Some(make::expr_todo())); | 179 | let body = make::block_expr(None, Some(make::expr_todo())).indent(IndentLevel(1)); |
180 | let body = IndentLevel(1).increase_indent(body); | ||
181 | fn_def.with_body(body) | 180 | fn_def.with_body(body) |
182 | } | 181 | } |
183 | 182 | ||
diff --git a/crates/ra_assists/src/handlers/early_return.rs b/crates/ra_assists/src/handlers/early_return.rs index 810784ad5..66b296081 100644 --- a/crates/ra_assists/src/handlers/early_return.rs +++ b/crates/ra_assists/src/handlers/early_return.rs | |||
@@ -2,7 +2,11 @@ use std::{iter::once, ops::RangeInclusive}; | |||
2 | 2 | ||
3 | use ra_syntax::{ | 3 | use ra_syntax::{ |
4 | algo::replace_children, | 4 | algo::replace_children, |
5 | ast::{self, edit::IndentLevel, make}, | 5 | ast::{ |
6 | self, | ||
7 | edit::{AstNodeEdit, IndentLevel}, | ||
8 | make, | ||
9 | }, | ||
6 | AstNode, | 10 | AstNode, |
7 | SyntaxKind::{FN_DEF, LOOP_EXPR, L_CURLY, R_CURLY, WHILE_EXPR, WHITESPACE}, | 11 | SyntaxKind::{FN_DEF, LOOP_EXPR, L_CURLY, R_CURLY, WHILE_EXPR, WHITESPACE}, |
8 | SyntaxNode, | 12 | SyntaxNode, |
@@ -105,8 +109,7 @@ pub(crate) fn convert_to_guarded_return(acc: &mut Assists, ctx: &AssistContext) | |||
105 | let then_branch = | 109 | let then_branch = |
106 | make::block_expr(once(make::expr_stmt(early_expression).into()), None); | 110 | make::block_expr(once(make::expr_stmt(early_expression).into()), None); |
107 | let cond = invert_boolean_expression(cond_expr); | 111 | let cond = invert_boolean_expression(cond_expr); |
108 | let e = make::expr_if(make::condition(cond, None), then_branch); | 112 | make::expr_if(make::condition(cond, None), then_branch).indent(if_indent_level) |
109 | if_indent_level.increase_indent(e) | ||
110 | }; | 113 | }; |
111 | replace(new_expr.syntax(), &then_block, &parent_block, &if_expr) | 114 | replace(new_expr.syntax(), &then_block, &parent_block, &if_expr) |
112 | } | 115 | } |
@@ -140,7 +143,7 @@ pub(crate) fn convert_to_guarded_return(acc: &mut Assists, ctx: &AssistContext) | |||
140 | make::bind_pat(make::name(&bound_ident.syntax().to_string())).into(), | 143 | make::bind_pat(make::name(&bound_ident.syntax().to_string())).into(), |
141 | Some(match_expr), | 144 | Some(match_expr), |
142 | ); | 145 | ); |
143 | let let_stmt = if_indent_level.increase_indent(let_stmt); | 146 | let let_stmt = let_stmt.indent(if_indent_level); |
144 | replace(let_stmt.syntax(), &then_block, &parent_block, &if_expr) | 147 | replace(let_stmt.syntax(), &then_block, &parent_block, &if_expr) |
145 | } | 148 | } |
146 | }; | 149 | }; |
@@ -153,7 +156,7 @@ pub(crate) fn convert_to_guarded_return(acc: &mut Assists, ctx: &AssistContext) | |||
153 | parent_block: &ast::BlockExpr, | 156 | parent_block: &ast::BlockExpr, |
154 | if_expr: &ast::IfExpr, | 157 | if_expr: &ast::IfExpr, |
155 | ) -> SyntaxNode { | 158 | ) -> SyntaxNode { |
156 | let then_block_items = IndentLevel::from(1).decrease_indent(then_block.clone()); | 159 | let then_block_items = then_block.dedent(IndentLevel::from(1)); |
157 | let end_of_then = then_block_items.syntax().last_child_or_token().unwrap(); | 160 | let end_of_then = then_block_items.syntax().last_child_or_token().unwrap(); |
158 | let end_of_then = | 161 | let end_of_then = |
159 | if end_of_then.prev_sibling_or_token().map(|n| n.kind()) == Some(WHITESPACE) { | 162 | if end_of_then.prev_sibling_or_token().map(|n| n.kind()) == Some(WHITESPACE) { |
diff --git a/crates/ra_assists/src/handlers/replace_if_let_with_match.rs b/crates/ra_assists/src/handlers/replace_if_let_with_match.rs index a59a06efa..65f5fc6ab 100644 --- a/crates/ra_assists/src/handlers/replace_if_let_with_match.rs +++ b/crates/ra_assists/src/handlers/replace_if_let_with_match.rs | |||
@@ -1,6 +1,10 @@ | |||
1 | use ra_fmt::unwrap_trivial_block; | 1 | use ra_fmt::unwrap_trivial_block; |
2 | use ra_syntax::{ | 2 | use ra_syntax::{ |
3 | ast::{self, edit::IndentLevel, make}, | 3 | ast::{ |
4 | self, | ||
5 | edit::{AstNodeEdit, IndentLevel}, | ||
6 | make, | ||
7 | }, | ||
4 | AstNode, | 8 | AstNode, |
5 | }; | 9 | }; |
6 | 10 | ||
@@ -61,10 +65,9 @@ pub(crate) fn replace_if_let_with_match(acc: &mut Assists, ctx: &AssistContext) | |||
61 | make::match_arm(vec![pattern], else_expr) | 65 | make::match_arm(vec![pattern], else_expr) |
62 | }; | 66 | }; |
63 | make::expr_match(expr, make::match_arm_list(vec![then_arm, else_arm])) | 67 | make::expr_match(expr, make::match_arm_list(vec![then_arm, else_arm])) |
68 | .indent(IndentLevel::from_node(if_expr.syntax())) | ||
64 | }; | 69 | }; |
65 | 70 | ||
66 | let match_expr = IndentLevel::from_node(if_expr.syntax()).increase_indent(match_expr); | ||
67 | |||
68 | edit.set_cursor(if_expr.syntax().text_range().start()); | 71 | edit.set_cursor(if_expr.syntax().text_range().start()); |
69 | edit.replace_ast::<ast::Expr>(if_expr.into(), match_expr); | 72 | edit.replace_ast::<ast::Expr>(if_expr.into(), match_expr); |
70 | }) | 73 | }) |
diff --git a/crates/ra_assists/src/handlers/replace_let_with_if_let.rs b/crates/ra_assists/src/handlers/replace_let_with_if_let.rs index d3f214591..482957dc6 100644 --- a/crates/ra_assists/src/handlers/replace_let_with_if_let.rs +++ b/crates/ra_assists/src/handlers/replace_let_with_if_let.rs | |||
@@ -53,8 +53,7 @@ pub(crate) fn replace_let_with_if_let(acc: &mut Assists, ctx: &AssistContext) -> | |||
53 | ) | 53 | ) |
54 | .into(), | 54 | .into(), |
55 | }; | 55 | }; |
56 | let block = | 56 | let block = make::block_expr(None, None).indent(IndentLevel::from_node(let_stmt.syntax())); |
57 | IndentLevel::from_node(let_stmt.syntax()).increase_indent(make::block_expr(None, None)); | ||
58 | let if_ = make::expr_if(make::condition(init, Some(with_placeholder)), block); | 57 | let if_ = make::expr_if(make::condition(init, Some(with_placeholder)), block); |
59 | let stmt = make::expr_stmt(if_); | 58 | let stmt = make::expr_stmt(if_); |
60 | 59 | ||
diff --git a/crates/ra_assists/src/handlers/replace_unwrap_with_match.rs b/crates/ra_assists/src/handlers/replace_unwrap_with_match.rs index a46998b8e..c4b56f6e9 100644 --- a/crates/ra_assists/src/handlers/replace_unwrap_with_match.rs +++ b/crates/ra_assists/src/handlers/replace_unwrap_with_match.rs | |||
@@ -1,7 +1,11 @@ | |||
1 | use std::iter; | 1 | use std::iter; |
2 | 2 | ||
3 | use ra_syntax::{ | 3 | use ra_syntax::{ |
4 | ast::{self, edit::IndentLevel, make}, | 4 | ast::{ |
5 | self, | ||
6 | edit::{AstNodeEdit, IndentLevel}, | ||
7 | make, | ||
8 | }, | ||
5 | AstNode, | 9 | AstNode, |
6 | }; | 10 | }; |
7 | 11 | ||
@@ -51,8 +55,8 @@ pub(crate) fn replace_unwrap_with_match(acc: &mut Assists, ctx: &AssistContext) | |||
51 | let err_arm = make::match_arm(iter::once(make::placeholder_pat().into()), unreachable_call); | 55 | let err_arm = make::match_arm(iter::once(make::placeholder_pat().into()), unreachable_call); |
52 | 56 | ||
53 | let match_arm_list = make::match_arm_list(vec![ok_arm, err_arm]); | 57 | let match_arm_list = make::match_arm_list(vec![ok_arm, err_arm]); |
54 | let match_expr = make::expr_match(caller.clone(), match_arm_list); | 58 | let match_expr = make::expr_match(caller.clone(), match_arm_list) |
55 | let match_expr = IndentLevel::from_node(method_call.syntax()).increase_indent(match_expr); | 59 | .indent(IndentLevel::from_node(method_call.syntax())); |
56 | 60 | ||
57 | edit.set_cursor(caller.syntax().text_range().start()); | 61 | edit.set_cursor(caller.syntax().text_range().start()); |
58 | edit.replace_ast::<ast::Expr>(method_call.into(), match_expr); | 62 | edit.replace_ast::<ast::Expr>(method_call.into(), match_expr); |
diff --git a/crates/ra_assists/src/handlers/unwrap_block.rs b/crates/ra_assists/src/handlers/unwrap_block.rs index eba0631a4..e52ec557e 100644 --- a/crates/ra_assists/src/handlers/unwrap_block.rs +++ b/crates/ra_assists/src/handlers/unwrap_block.rs | |||
@@ -1,6 +1,6 @@ | |||
1 | use crate::{AssistContext, AssistId, Assists}; | 1 | use crate::{AssistContext, AssistId, Assists}; |
2 | 2 | ||
3 | use ast::LoopBodyOwner; | 3 | use ast::{ElseBranch, Expr, LoopBodyOwner}; |
4 | use ra_fmt::unwrap_trivial_block; | 4 | use ra_fmt::unwrap_trivial_block; |
5 | use ra_syntax::{ast, match_ast, AstNode, TextRange, T}; | 5 | use ra_syntax::{ast, match_ast, AstNode, TextRange, T}; |
6 | 6 | ||
@@ -25,19 +25,11 @@ pub(crate) fn unwrap_block(acc: &mut Assists, ctx: &AssistContext) -> Option<()> | |||
25 | let l_curly_token = ctx.find_token_at_offset(T!['{'])?; | 25 | let l_curly_token = ctx.find_token_at_offset(T!['{'])?; |
26 | let block = ast::BlockExpr::cast(l_curly_token.parent())?; | 26 | let block = ast::BlockExpr::cast(l_curly_token.parent())?; |
27 | let parent = block.syntax().parent()?; | 27 | let parent = block.syntax().parent()?; |
28 | let assist_id = AssistId("unwrap_block"); | ||
29 | let assist_label = "Unwrap block"; | ||
30 | |||
28 | let (expr, expr_to_unwrap) = match_ast! { | 31 | let (expr, expr_to_unwrap) = match_ast! { |
29 | match parent { | 32 | match parent { |
30 | ast::IfExpr(if_expr) => { | ||
31 | let expr_to_unwrap = if_expr.blocks().find_map(|expr| extract_expr(ctx.frange.range, expr)); | ||
32 | let expr_to_unwrap = expr_to_unwrap?; | ||
33 | // Find if we are in a else if block | ||
34 | let ancestor = if_expr.syntax().parent().and_then(ast::IfExpr::cast); | ||
35 | |||
36 | match ancestor { | ||
37 | None => (ast::Expr::IfExpr(if_expr), expr_to_unwrap), | ||
38 | Some(ancestor) => (ast::Expr::IfExpr(ancestor), expr_to_unwrap), | ||
39 | } | ||
40 | }, | ||
41 | ast::ForExpr(for_expr) => { | 33 | ast::ForExpr(for_expr) => { |
42 | let block_expr = for_expr.loop_body()?; | 34 | let block_expr = for_expr.loop_body()?; |
43 | let expr_to_unwrap = extract_expr(ctx.frange.range, block_expr)?; | 35 | let expr_to_unwrap = extract_expr(ctx.frange.range, block_expr)?; |
@@ -53,27 +45,62 @@ pub(crate) fn unwrap_block(acc: &mut Assists, ctx: &AssistContext) -> Option<()> | |||
53 | let expr_to_unwrap = extract_expr(ctx.frange.range, block_expr)?; | 45 | let expr_to_unwrap = extract_expr(ctx.frange.range, block_expr)?; |
54 | (ast::Expr::LoopExpr(loop_expr), expr_to_unwrap) | 46 | (ast::Expr::LoopExpr(loop_expr), expr_to_unwrap) |
55 | }, | 47 | }, |
48 | ast::IfExpr(if_expr) => { | ||
49 | let mut resp = None; | ||
50 | |||
51 | let then_branch = if_expr.then_branch()?; | ||
52 | if then_branch.l_curly_token()?.text_range().contains_range(ctx.frange.range) { | ||
53 | if let Some(ancestor) = if_expr.syntax().parent().and_then(ast::IfExpr::cast) { | ||
54 | // For `else if` blocks | ||
55 | let ancestor_then_branch = ancestor.then_branch()?; | ||
56 | let l_curly_token = then_branch.l_curly_token()?; | ||
57 | |||
58 | let target = then_branch.syntax().text_range(); | ||
59 | return acc.add(assist_id, assist_label, target, |edit| { | ||
60 | let range_to_del_else_if = TextRange::new(ancestor_then_branch.syntax().text_range().end(), l_curly_token.text_range().start()); | ||
61 | let range_to_del_rest = TextRange::new(then_branch.syntax().text_range().end(), if_expr.syntax().text_range().end()); | ||
62 | |||
63 | edit.set_cursor(ancestor_then_branch.syntax().text_range().end()); | ||
64 | edit.delete(range_to_del_rest); | ||
65 | edit.delete(range_to_del_else_if); | ||
66 | edit.replace(target, update_expr_string(then_branch.to_string(), &[' ', '{'])); | ||
67 | }); | ||
68 | } else { | ||
69 | resp = Some((ast::Expr::IfExpr(if_expr.clone()), Expr::BlockExpr(then_branch))); | ||
70 | } | ||
71 | } else if let Some(else_branch) = if_expr.else_branch() { | ||
72 | match else_branch { | ||
73 | ElseBranch::Block(else_block) => { | ||
74 | let l_curly_token = else_block.l_curly_token()?; | ||
75 | if l_curly_token.text_range().contains_range(ctx.frange.range) { | ||
76 | let target = else_block.syntax().text_range(); | ||
77 | return acc.add(assist_id, assist_label, target, |edit| { | ||
78 | let range_to_del = TextRange::new(then_branch.syntax().text_range().end(), l_curly_token.text_range().start()); | ||
79 | |||
80 | edit.set_cursor(then_branch.syntax().text_range().end()); | ||
81 | edit.delete(range_to_del); | ||
82 | edit.replace(target, update_expr_string(else_block.to_string(), &[' ', '{'])); | ||
83 | }); | ||
84 | } | ||
85 | }, | ||
86 | ElseBranch::IfExpr(_) => {}, | ||
87 | } | ||
88 | } | ||
89 | |||
90 | resp? | ||
91 | }, | ||
56 | _ => return None, | 92 | _ => return None, |
57 | } | 93 | } |
58 | }; | 94 | }; |
59 | 95 | ||
60 | let target = expr_to_unwrap.syntax().text_range(); | 96 | let target = expr_to_unwrap.syntax().text_range(); |
61 | acc.add(AssistId("unwrap_block"), "Unwrap block", target, |edit| { | 97 | acc.add(assist_id, assist_label, target, |edit| { |
62 | edit.set_cursor(expr.syntax().text_range().start()); | 98 | edit.set_cursor(expr.syntax().text_range().start()); |
63 | 99 | ||
64 | let pat_start: &[_] = &[' ', '{', '\n']; | 100 | edit.replace( |
65 | let expr_to_unwrap = expr_to_unwrap.to_string(); | 101 | expr.syntax().text_range(), |
66 | let expr_string = expr_to_unwrap.trim_start_matches(pat_start); | 102 | update_expr_string(expr_to_unwrap.to_string(), &[' ', '{', '\n']), |
67 | let mut expr_string_lines: Vec<&str> = expr_string.lines().collect(); | 103 | ); |
68 | expr_string_lines.pop(); // Delete last line | ||
69 | |||
70 | let expr_string = expr_string_lines | ||
71 | .into_iter() | ||
72 | .map(|line| line.replacen(" ", "", 1)) // Delete indentation | ||
73 | .collect::<Vec<String>>() | ||
74 | .join("\n"); | ||
75 | |||
76 | edit.replace(expr.syntax().text_range(), expr_string); | ||
77 | }) | 104 | }) |
78 | } | 105 | } |
79 | 106 | ||
@@ -87,6 +114,18 @@ fn extract_expr(cursor_range: TextRange, block: ast::BlockExpr) -> Option<ast::E | |||
87 | } | 114 | } |
88 | } | 115 | } |
89 | 116 | ||
117 | fn update_expr_string(expr_str: String, trim_start_pat: &[char]) -> String { | ||
118 | let expr_string = expr_str.trim_start_matches(trim_start_pat); | ||
119 | let mut expr_string_lines: Vec<&str> = expr_string.lines().collect(); | ||
120 | expr_string_lines.pop(); // Delete last line | ||
121 | |||
122 | expr_string_lines | ||
123 | .into_iter() | ||
124 | .map(|line| line.replacen(" ", "", 1)) // Delete indentation | ||
125 | .collect::<Vec<String>>() | ||
126 | .join("\n") | ||
127 | } | ||
128 | |||
90 | #[cfg(test)] | 129 | #[cfg(test)] |
91 | mod tests { | 130 | mod tests { |
92 | use crate::tests::{check_assist, check_assist_not_applicable}; | 131 | use crate::tests::{check_assist, check_assist_not_applicable}; |
@@ -142,7 +181,13 @@ mod tests { | |||
142 | r#" | 181 | r#" |
143 | fn main() { | 182 | fn main() { |
144 | bar(); | 183 | bar(); |
145 | <|>println!("bar"); | 184 | if true { |
185 | foo(); | ||
186 | |||
187 | //comment | ||
188 | bar(); | ||
189 | }<|> | ||
190 | println!("bar"); | ||
146 | } | 191 | } |
147 | "#, | 192 | "#, |
148 | ); | 193 | ); |
@@ -170,7 +215,127 @@ mod tests { | |||
170 | r#" | 215 | r#" |
171 | fn main() { | 216 | fn main() { |
172 | //bar(); | 217 | //bar(); |
173 | <|>println!("bar"); | 218 | if true { |
219 | println!("true"); | ||
220 | |||
221 | //comment | ||
222 | //bar(); | ||
223 | }<|> | ||
224 | println!("bar"); | ||
225 | } | ||
226 | "#, | ||
227 | ); | ||
228 | } | ||
229 | |||
230 | #[test] | ||
231 | fn simple_if_else_if_nested() { | ||
232 | check_assist( | ||
233 | unwrap_block, | ||
234 | r#" | ||
235 | fn main() { | ||
236 | //bar(); | ||
237 | if true { | ||
238 | println!("true"); | ||
239 | |||
240 | //comment | ||
241 | //bar(); | ||
242 | } else if false { | ||
243 | println!("bar"); | ||
244 | } else if true {<|> | ||
245 | println!("foo"); | ||
246 | } | ||
247 | } | ||
248 | "#, | ||
249 | r#" | ||
250 | fn main() { | ||
251 | //bar(); | ||
252 | if true { | ||
253 | println!("true"); | ||
254 | |||
255 | //comment | ||
256 | //bar(); | ||
257 | } else if false { | ||
258 | println!("bar"); | ||
259 | }<|> | ||
260 | println!("foo"); | ||
261 | } | ||
262 | "#, | ||
263 | ); | ||
264 | } | ||
265 | |||
266 | #[test] | ||
267 | fn simple_if_else_if_nested_else() { | ||
268 | check_assist( | ||
269 | unwrap_block, | ||
270 | r#" | ||
271 | fn main() { | ||
272 | //bar(); | ||
273 | if true { | ||
274 | println!("true"); | ||
275 | |||
276 | //comment | ||
277 | //bar(); | ||
278 | } else if false { | ||
279 | println!("bar"); | ||
280 | } else if true { | ||
281 | println!("foo"); | ||
282 | } else {<|> | ||
283 | println!("else"); | ||
284 | } | ||
285 | } | ||
286 | "#, | ||
287 | r#" | ||
288 | fn main() { | ||
289 | //bar(); | ||
290 | if true { | ||
291 | println!("true"); | ||
292 | |||
293 | //comment | ||
294 | //bar(); | ||
295 | } else if false { | ||
296 | println!("bar"); | ||
297 | } else if true { | ||
298 | println!("foo"); | ||
299 | }<|> | ||
300 | println!("else"); | ||
301 | } | ||
302 | "#, | ||
303 | ); | ||
304 | } | ||
305 | |||
306 | #[test] | ||
307 | fn simple_if_else_if_nested_middle() { | ||
308 | check_assist( | ||
309 | unwrap_block, | ||
310 | r#" | ||
311 | fn main() { | ||
312 | //bar(); | ||
313 | if true { | ||
314 | println!("true"); | ||
315 | |||
316 | //comment | ||
317 | //bar(); | ||
318 | } else if false { | ||
319 | println!("bar"); | ||
320 | } else if true {<|> | ||
321 | println!("foo"); | ||
322 | } else { | ||
323 | println!("else"); | ||
324 | } | ||
325 | } | ||
326 | "#, | ||
327 | r#" | ||
328 | fn main() { | ||
329 | //bar(); | ||
330 | if true { | ||
331 | println!("true"); | ||
332 | |||
333 | //comment | ||
334 | //bar(); | ||
335 | } else if false { | ||
336 | println!("bar"); | ||
337 | }<|> | ||
338 | println!("foo"); | ||
174 | } | 339 | } |
175 | "#, | 340 | "#, |
176 | ); | 341 | ); |
diff --git a/crates/ra_env/src/lib.rs b/crates/ra_env/src/lib.rs deleted file mode 100644 index 413da1982..000000000 --- a/crates/ra_env/src/lib.rs +++ /dev/null | |||
@@ -1,66 +0,0 @@ | |||
1 | //! This crate contains a single public function | ||
2 | //! [`get_path_for_executable`](fn.get_path_for_executable.html). | ||
3 | //! See docs there for more information. | ||
4 | |||
5 | use anyhow::{bail, Result}; | ||
6 | use std::env; | ||
7 | use std::path::{Path, PathBuf}; | ||
8 | use std::process::Command; | ||
9 | |||
10 | /// Return a `PathBuf` to use for the given executable. | ||
11 | /// | ||
12 | /// E.g., `get_path_for_executable("cargo")` may return just `cargo` if that | ||
13 | /// gives a valid Cargo executable; or it may return a full path to a valid | ||
14 | /// Cargo. | ||
15 | pub fn get_path_for_executable(executable_name: impl AsRef<str>) -> Result<PathBuf> { | ||
16 | // The current implementation checks three places for an executable to use: | ||
17 | // 1) Appropriate environment variable (erroring if this is set but not a usable executable) | ||
18 | // example: for cargo, this checks $CARGO environment variable; for rustc, $RUSTC; etc | ||
19 | // 2) `<executable_name>` | ||
20 | // example: for cargo, this tries just `cargo`, which will succeed if `cargo` is on the $PATH | ||
21 | // 3) `~/.cargo/bin/<executable_name>` | ||
22 | // example: for cargo, this tries ~/.cargo/bin/cargo | ||
23 | // It seems that this is a reasonable place to try for cargo, rustc, and rustup | ||
24 | let executable_name = executable_name.as_ref(); | ||
25 | let env_var = executable_name.to_ascii_uppercase(); | ||
26 | if let Ok(path) = env::var(&env_var) { | ||
27 | if is_valid_executable(&path) { | ||
28 | Ok(path.into()) | ||
29 | } else { | ||
30 | bail!( | ||
31 | "`{}` environment variable points to something that's not a valid executable", | ||
32 | env_var | ||
33 | ) | ||
34 | } | ||
35 | } else { | ||
36 | if is_valid_executable(executable_name) { | ||
37 | return Ok(executable_name.into()); | ||
38 | } | ||
39 | if let Some(mut path) = ::home::home_dir() { | ||
40 | path.push(".cargo"); | ||
41 | path.push("bin"); | ||
42 | path.push(executable_name); | ||
43 | if is_valid_executable(&path) { | ||
44 | return Ok(path); | ||
45 | } | ||
46 | } | ||
47 | // This error message may also be caused by $PATH or $CARGO/$RUSTC/etc not being set correctly | ||
48 | // for VSCode, even if they are set correctly in a terminal. | ||
49 | // On macOS in particular, launching VSCode from terminal with `code <dirname>` causes VSCode | ||
50 | // to inherit environment variables including $PATH, $CARGO, $RUSTC, etc from that terminal; | ||
51 | // but launching VSCode from Dock does not inherit environment variables from a terminal. | ||
52 | // For more discussion, see #3118. | ||
53 | bail!( | ||
54 | "Failed to find `{}` executable. Make sure `{}` is in `$PATH`, or set `${}` to point to a valid executable.", | ||
55 | executable_name, executable_name, env_var | ||
56 | ) | ||
57 | } | ||
58 | } | ||
59 | |||
60 | /// Does the given `Path` point to a usable executable? | ||
61 | /// | ||
62 | /// (assumes the executable takes a `--version` switch and writes to stdout, | ||
63 | /// which is true for `cargo`, `rustc`, and `rustup`) | ||
64 | fn is_valid_executable(p: impl AsRef<Path>) -> bool { | ||
65 | Command::new(p.as_ref()).arg("--version").output().is_ok() | ||
66 | } | ||
diff --git a/crates/ra_flycheck/Cargo.toml b/crates/ra_flycheck/Cargo.toml index 578a45894..eac502da5 100644 --- a/crates/ra_flycheck/Cargo.toml +++ b/crates/ra_flycheck/Cargo.toml | |||
@@ -14,7 +14,7 @@ log = "0.4.8" | |||
14 | cargo_metadata = "0.10.0" | 14 | cargo_metadata = "0.10.0" |
15 | serde_json = "1.0.48" | 15 | serde_json = "1.0.48" |
16 | jod-thread = "0.1.1" | 16 | jod-thread = "0.1.1" |
17 | ra_env = { path = "../ra_env" } | 17 | ra_toolchain = { path = "../ra_toolchain" } |
18 | 18 | ||
19 | [dev-dependencies] | 19 | [dev-dependencies] |
20 | insta = "0.16.0" | 20 | insta = "0.16.0" |
diff --git a/crates/ra_flycheck/src/lib.rs b/crates/ra_flycheck/src/lib.rs index 39b02ca94..24af75c95 100644 --- a/crates/ra_flycheck/src/lib.rs +++ b/crates/ra_flycheck/src/lib.rs | |||
@@ -16,7 +16,6 @@ use lsp_types::{ | |||
16 | CodeAction, CodeActionOrCommand, Diagnostic, Url, WorkDoneProgress, WorkDoneProgressBegin, | 16 | CodeAction, CodeActionOrCommand, Diagnostic, Url, WorkDoneProgress, WorkDoneProgressBegin, |
17 | WorkDoneProgressEnd, WorkDoneProgressReport, | 17 | WorkDoneProgressEnd, WorkDoneProgressReport, |
18 | }; | 18 | }; |
19 | use ra_env::get_path_for_executable; | ||
20 | 19 | ||
21 | use crate::conv::{map_rust_diagnostic_to_lsp, MappedRustDiagnostic}; | 20 | use crate::conv::{map_rust_diagnostic_to_lsp, MappedRustDiagnostic}; |
22 | 21 | ||
@@ -218,10 +217,10 @@ impl FlycheckThread { | |||
218 | 217 | ||
219 | let mut cmd = match &self.config { | 218 | let mut cmd = match &self.config { |
220 | FlycheckConfig::CargoCommand { command, all_targets, all_features, extra_args } => { | 219 | FlycheckConfig::CargoCommand { command, all_targets, all_features, extra_args } => { |
221 | let mut cmd = Command::new(get_path_for_executable("cargo").unwrap()); | 220 | let mut cmd = Command::new(ra_toolchain::cargo()); |
222 | cmd.arg(command); | 221 | cmd.arg(command); |
223 | cmd.args(&["--workspace", "--message-format=json", "--manifest-path"]); | 222 | cmd.args(&["--workspace", "--message-format=json", "--manifest-path"]) |
224 | cmd.arg(self.workspace_root.join("Cargo.toml")); | 223 | .arg(self.workspace_root.join("Cargo.toml")); |
225 | if *all_targets { | 224 | if *all_targets { |
226 | cmd.arg("--all-targets"); | 225 | cmd.arg("--all-targets"); |
227 | } | 226 | } |
diff --git a/crates/ra_hir/src/code_model.rs b/crates/ra_hir/src/code_model.rs index 5f480c304..be18c845c 100644 --- a/crates/ra_hir/src/code_model.rs +++ b/crates/ra_hir/src/code_model.rs | |||
@@ -22,8 +22,11 @@ use hir_expand::{ | |||
22 | MacroDefId, MacroDefKind, | 22 | MacroDefId, MacroDefKind, |
23 | }; | 23 | }; |
24 | use hir_ty::{ | 24 | use hir_ty::{ |
25 | autoderef, display::HirFormatter, expr::ExprValidator, method_resolution, ApplicationTy, | 25 | autoderef, |
26 | Canonical, InEnvironment, Substs, TraitEnvironment, Ty, TyDefId, TypeCtor, | 26 | display::{HirDisplayError, HirFormatter}, |
27 | expr::ExprValidator, | ||
28 | method_resolution, ApplicationTy, Canonical, InEnvironment, Substs, TraitEnvironment, Ty, | ||
29 | TyDefId, TypeCtor, | ||
27 | }; | 30 | }; |
28 | use ra_db::{CrateId, CrateName, Edition, FileId}; | 31 | use ra_db::{CrateId, CrateName, Edition, FileId}; |
29 | use ra_prof::profile; | 32 | use ra_prof::profile; |
@@ -1319,7 +1322,7 @@ impl Type { | |||
1319 | } | 1322 | } |
1320 | 1323 | ||
1321 | impl HirDisplay for Type { | 1324 | impl HirDisplay for Type { |
1322 | fn hir_fmt(&self, f: &mut HirFormatter) -> std::fmt::Result { | 1325 | fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> { |
1323 | self.ty.value.hir_fmt(f) | 1326 | self.ty.value.hir_fmt(f) |
1324 | } | 1327 | } |
1325 | } | 1328 | } |
diff --git a/crates/ra_hir_ty/src/diagnostics.rs b/crates/ra_hir_ty/src/diagnostics.rs index c8fd54861..41ac70272 100644 --- a/crates/ra_hir_ty/src/diagnostics.rs +++ b/crates/ra_hir_ty/src/diagnostics.rs | |||
@@ -131,3 +131,31 @@ impl AstDiagnostic for MissingOkInTailExpr { | |||
131 | ast::Expr::cast(node).unwrap() | 131 | ast::Expr::cast(node).unwrap() |
132 | } | 132 | } |
133 | } | 133 | } |
134 | |||
135 | #[derive(Debug)] | ||
136 | pub struct BreakOutsideOfLoop { | ||
137 | pub file: HirFileId, | ||
138 | pub expr: AstPtr<ast::Expr>, | ||
139 | } | ||
140 | |||
141 | impl Diagnostic for BreakOutsideOfLoop { | ||
142 | fn message(&self) -> String { | ||
143 | "break outside of loop".to_string() | ||
144 | } | ||
145 | fn source(&self) -> InFile<SyntaxNodePtr> { | ||
146 | InFile { file_id: self.file, value: self.expr.clone().into() } | ||
147 | } | ||
148 | fn as_any(&self) -> &(dyn Any + Send + 'static) { | ||
149 | self | ||
150 | } | ||
151 | } | ||
152 | |||
153 | impl AstDiagnostic for BreakOutsideOfLoop { | ||
154 | type AST = ast::Expr; | ||
155 | |||
156 | fn ast(&self, db: &impl AstDatabase) -> Self::AST { | ||
157 | let root = db.parse_or_expand(self.file).unwrap(); | ||
158 | let node = self.source().value.to_node(&root); | ||
159 | ast::Expr::cast(node).unwrap() | ||
160 | } | ||
161 | } | ||
diff --git a/crates/ra_hir_ty/src/display.rs b/crates/ra_hir_ty/src/display.rs index d03bbd5a7..b9c4d2e89 100644 --- a/crates/ra_hir_ty/src/display.rs +++ b/crates/ra_hir_ty/src/display.rs | |||
@@ -6,28 +6,42 @@ use crate::{ | |||
6 | db::HirDatabase, utils::generics, ApplicationTy, CallableDef, FnSig, GenericPredicate, | 6 | db::HirDatabase, utils::generics, ApplicationTy, CallableDef, FnSig, GenericPredicate, |
7 | Obligation, ProjectionTy, Substs, TraitRef, Ty, TypeCtor, | 7 | Obligation, ProjectionTy, Substs, TraitRef, Ty, TypeCtor, |
8 | }; | 8 | }; |
9 | use hir_def::{generics::TypeParamProvenance, AdtId, AssocContainerId, Lookup}; | 9 | use hir_def::{ |
10 | find_path, generics::TypeParamProvenance, item_scope::ItemInNs, AdtId, AssocContainerId, | ||
11 | Lookup, ModuleId, | ||
12 | }; | ||
10 | use hir_expand::name::Name; | 13 | use hir_expand::name::Name; |
11 | 14 | ||
12 | pub struct HirFormatter<'a, 'b> { | 15 | pub struct HirFormatter<'a> { |
13 | pub db: &'a dyn HirDatabase, | 16 | pub db: &'a dyn HirDatabase, |
14 | fmt: &'a mut fmt::Formatter<'b>, | 17 | fmt: &'a mut dyn fmt::Write, |
15 | buf: String, | 18 | buf: String, |
16 | curr_size: usize, | 19 | curr_size: usize, |
17 | pub(crate) max_size: Option<usize>, | 20 | pub(crate) max_size: Option<usize>, |
18 | omit_verbose_types: bool, | 21 | omit_verbose_types: bool, |
22 | display_target: DisplayTarget, | ||
19 | } | 23 | } |
20 | 24 | ||
21 | pub trait HirDisplay { | 25 | pub trait HirDisplay { |
22 | fn hir_fmt(&self, f: &mut HirFormatter) -> fmt::Result; | 26 | fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError>; |
23 | 27 | ||
28 | /// Returns a `Display`able type that is human-readable. | ||
29 | /// Use this for showing types to the user (e.g. diagnostics) | ||
24 | fn display<'a>(&'a self, db: &'a dyn HirDatabase) -> HirDisplayWrapper<'a, Self> | 30 | fn display<'a>(&'a self, db: &'a dyn HirDatabase) -> HirDisplayWrapper<'a, Self> |
25 | where | 31 | where |
26 | Self: Sized, | 32 | Self: Sized, |
27 | { | 33 | { |
28 | HirDisplayWrapper(db, self, None, false) | 34 | HirDisplayWrapper { |
35 | db, | ||
36 | t: self, | ||
37 | max_size: None, | ||
38 | omit_verbose_types: false, | ||
39 | display_target: DisplayTarget::Diagnostics, | ||
40 | } | ||
29 | } | 41 | } |
30 | 42 | ||
43 | /// Returns a `Display`able type that is human-readable and tries to be succinct. | ||
44 | /// Use this for showing types to the user where space is constrained (e.g. doc popups) | ||
31 | fn display_truncated<'a>( | 45 | fn display_truncated<'a>( |
32 | &'a self, | 46 | &'a self, |
33 | db: &'a dyn HirDatabase, | 47 | db: &'a dyn HirDatabase, |
@@ -36,16 +50,46 @@ pub trait HirDisplay { | |||
36 | where | 50 | where |
37 | Self: Sized, | 51 | Self: Sized, |
38 | { | 52 | { |
39 | HirDisplayWrapper(db, self, max_size, true) | 53 | HirDisplayWrapper { |
54 | db, | ||
55 | t: self, | ||
56 | max_size, | ||
57 | omit_verbose_types: true, | ||
58 | display_target: DisplayTarget::Diagnostics, | ||
59 | } | ||
60 | } | ||
61 | |||
62 | /// Returns a String representation of `self` that can be inserted into the given module. | ||
63 | /// Use this when generating code (e.g. assists) | ||
64 | fn display_source_code<'a>( | ||
65 | &'a self, | ||
66 | db: &'a dyn HirDatabase, | ||
67 | module_id: ModuleId, | ||
68 | ) -> Result<String, DisplaySourceCodeError> { | ||
69 | let mut result = String::new(); | ||
70 | match self.hir_fmt(&mut HirFormatter { | ||
71 | db, | ||
72 | fmt: &mut result, | ||
73 | buf: String::with_capacity(20), | ||
74 | curr_size: 0, | ||
75 | max_size: None, | ||
76 | omit_verbose_types: false, | ||
77 | display_target: DisplayTarget::SourceCode { module_id }, | ||
78 | }) { | ||
79 | Ok(()) => {} | ||
80 | Err(HirDisplayError::FmtError) => panic!("Writing to String can't fail!"), | ||
81 | Err(HirDisplayError::DisplaySourceCodeError(e)) => return Err(e), | ||
82 | }; | ||
83 | Ok(result) | ||
40 | } | 84 | } |
41 | } | 85 | } |
42 | 86 | ||
43 | impl<'a, 'b> HirFormatter<'a, 'b> { | 87 | impl<'a> HirFormatter<'a> { |
44 | pub fn write_joined<T: HirDisplay>( | 88 | pub fn write_joined<T: HirDisplay>( |
45 | &mut self, | 89 | &mut self, |
46 | iter: impl IntoIterator<Item = T>, | 90 | iter: impl IntoIterator<Item = T>, |
47 | sep: &str, | 91 | sep: &str, |
48 | ) -> fmt::Result { | 92 | ) -> Result<(), HirDisplayError> { |
49 | let mut first = true; | 93 | let mut first = true; |
50 | for e in iter { | 94 | for e in iter { |
51 | if !first { | 95 | if !first { |
@@ -58,14 +102,14 @@ impl<'a, 'b> HirFormatter<'a, 'b> { | |||
58 | } | 102 | } |
59 | 103 | ||
60 | /// This allows using the `write!` macro directly with a `HirFormatter`. | 104 | /// This allows using the `write!` macro directly with a `HirFormatter`. |
61 | pub fn write_fmt(&mut self, args: fmt::Arguments) -> fmt::Result { | 105 | pub fn write_fmt(&mut self, args: fmt::Arguments) -> Result<(), HirDisplayError> { |
62 | // We write to a buffer first to track output size | 106 | // We write to a buffer first to track output size |
63 | self.buf.clear(); | 107 | self.buf.clear(); |
64 | fmt::write(&mut self.buf, args)?; | 108 | fmt::write(&mut self.buf, args)?; |
65 | self.curr_size += self.buf.len(); | 109 | self.curr_size += self.buf.len(); |
66 | 110 | ||
67 | // Then we write to the internal formatter from the buffer | 111 | // Then we write to the internal formatter from the buffer |
68 | self.fmt.write_str(&self.buf) | 112 | self.fmt.write_str(&self.buf).map_err(HirDisplayError::from) |
69 | } | 113 | } |
70 | 114 | ||
71 | pub fn should_truncate(&self) -> bool { | 115 | pub fn should_truncate(&self) -> bool { |
@@ -81,34 +125,82 @@ impl<'a, 'b> HirFormatter<'a, 'b> { | |||
81 | } | 125 | } |
82 | } | 126 | } |
83 | 127 | ||
84 | pub struct HirDisplayWrapper<'a, T>(&'a dyn HirDatabase, &'a T, Option<usize>, bool); | 128 | #[derive(Clone, Copy)] |
129 | enum DisplayTarget { | ||
130 | /// Display types for inlays, doc popups, autocompletion, etc... | ||
131 | /// Showing `{unknown}` or not qualifying paths is fine here. | ||
132 | /// There's no reason for this to fail. | ||
133 | Diagnostics, | ||
134 | /// Display types for inserting them in source files. | ||
135 | /// The generated code should compile, so paths need to be qualified. | ||
136 | SourceCode { module_id: ModuleId }, | ||
137 | } | ||
138 | |||
139 | impl DisplayTarget { | ||
140 | fn is_source_code(&self) -> bool { | ||
141 | matches!(self, Self::SourceCode {..}) | ||
142 | } | ||
143 | } | ||
144 | |||
145 | #[derive(Debug)] | ||
146 | pub enum DisplaySourceCodeError { | ||
147 | PathNotFound, | ||
148 | } | ||
149 | |||
150 | pub enum HirDisplayError { | ||
151 | /// Errors that can occur when generating source code | ||
152 | DisplaySourceCodeError(DisplaySourceCodeError), | ||
153 | /// `FmtError` is required to be compatible with std::fmt::Display | ||
154 | FmtError, | ||
155 | } | ||
156 | impl From<fmt::Error> for HirDisplayError { | ||
157 | fn from(_: fmt::Error) -> Self { | ||
158 | Self::FmtError | ||
159 | } | ||
160 | } | ||
161 | |||
162 | pub struct HirDisplayWrapper<'a, T> { | ||
163 | db: &'a dyn HirDatabase, | ||
164 | t: &'a T, | ||
165 | max_size: Option<usize>, | ||
166 | omit_verbose_types: bool, | ||
167 | display_target: DisplayTarget, | ||
168 | } | ||
85 | 169 | ||
86 | impl<'a, T> fmt::Display for HirDisplayWrapper<'a, T> | 170 | impl<'a, T> fmt::Display for HirDisplayWrapper<'a, T> |
87 | where | 171 | where |
88 | T: HirDisplay, | 172 | T: HirDisplay, |
89 | { | 173 | { |
90 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | 174 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
91 | self.1.hir_fmt(&mut HirFormatter { | 175 | match self.t.hir_fmt(&mut HirFormatter { |
92 | db: self.0, | 176 | db: self.db, |
93 | fmt: f, | 177 | fmt: f, |
94 | buf: String::with_capacity(20), | 178 | buf: String::with_capacity(20), |
95 | curr_size: 0, | 179 | curr_size: 0, |
96 | max_size: self.2, | 180 | max_size: self.max_size, |
97 | omit_verbose_types: self.3, | 181 | omit_verbose_types: self.omit_verbose_types, |
98 | }) | 182 | display_target: self.display_target, |
183 | }) { | ||
184 | Ok(()) => Ok(()), | ||
185 | Err(HirDisplayError::FmtError) => Err(fmt::Error), | ||
186 | Err(HirDisplayError::DisplaySourceCodeError(_)) => { | ||
187 | // This should never happen | ||
188 | panic!("HirDisplay failed when calling Display::fmt!") | ||
189 | } | ||
190 | } | ||
99 | } | 191 | } |
100 | } | 192 | } |
101 | 193 | ||
102 | const TYPE_HINT_TRUNCATION: &str = "…"; | 194 | const TYPE_HINT_TRUNCATION: &str = "…"; |
103 | 195 | ||
104 | impl HirDisplay for &Ty { | 196 | impl HirDisplay for &Ty { |
105 | fn hir_fmt(&self, f: &mut HirFormatter) -> fmt::Result { | 197 | fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> { |
106 | HirDisplay::hir_fmt(*self, f) | 198 | HirDisplay::hir_fmt(*self, f) |
107 | } | 199 | } |
108 | } | 200 | } |
109 | 201 | ||
110 | impl HirDisplay for ApplicationTy { | 202 | impl HirDisplay for ApplicationTy { |
111 | fn hir_fmt(&self, f: &mut HirFormatter) -> fmt::Result { | 203 | fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> { |
112 | if f.should_truncate() { | 204 | if f.should_truncate() { |
113 | return write!(f, "{}", TYPE_HINT_TRUNCATION); | 205 | return write!(f, "{}", TYPE_HINT_TRUNCATION); |
114 | } | 206 | } |
@@ -191,45 +283,66 @@ impl HirDisplay for ApplicationTy { | |||
191 | } | 283 | } |
192 | } | 284 | } |
193 | TypeCtor::Adt(def_id) => { | 285 | TypeCtor::Adt(def_id) => { |
194 | let name = match def_id { | 286 | match f.display_target { |
195 | AdtId::StructId(it) => f.db.struct_data(it).name.clone(), | 287 | DisplayTarget::Diagnostics => { |
196 | AdtId::UnionId(it) => f.db.union_data(it).name.clone(), | 288 | let name = match def_id { |
197 | AdtId::EnumId(it) => f.db.enum_data(it).name.clone(), | 289 | AdtId::StructId(it) => f.db.struct_data(it).name.clone(), |
198 | }; | 290 | AdtId::UnionId(it) => f.db.union_data(it).name.clone(), |
199 | write!(f, "{}", name)?; | 291 | AdtId::EnumId(it) => f.db.enum_data(it).name.clone(), |
292 | }; | ||
293 | write!(f, "{}", name)?; | ||
294 | } | ||
295 | DisplayTarget::SourceCode { module_id } => { | ||
296 | if let Some(path) = find_path::find_path( | ||
297 | f.db.upcast(), | ||
298 | ItemInNs::Types(def_id.into()), | ||
299 | module_id, | ||
300 | ) { | ||
301 | write!(f, "{}", path)?; | ||
302 | } else { | ||
303 | return Err(HirDisplayError::DisplaySourceCodeError( | ||
304 | DisplaySourceCodeError::PathNotFound, | ||
305 | )); | ||
306 | } | ||
307 | } | ||
308 | } | ||
309 | |||
200 | if self.parameters.len() > 0 { | 310 | if self.parameters.len() > 0 { |
201 | let mut non_default_parameters = Vec::with_capacity(self.parameters.len()); | 311 | let mut non_default_parameters = Vec::with_capacity(self.parameters.len()); |
202 | let parameters_to_write = if f.omit_verbose_types() { | 312 | let parameters_to_write = |
203 | match self | 313 | if f.display_target.is_source_code() || f.omit_verbose_types() { |
204 | .ctor | 314 | match self |
205 | .as_generic_def() | 315 | .ctor |
206 | .map(|generic_def_id| f.db.generic_defaults(generic_def_id)) | 316 | .as_generic_def() |
207 | .filter(|defaults| !defaults.is_empty()) | 317 | .map(|generic_def_id| f.db.generic_defaults(generic_def_id)) |
208 | { | 318 | .filter(|defaults| !defaults.is_empty()) |
209 | None => self.parameters.0.as_ref(), | 319 | { |
210 | Some(default_parameters) => { | 320 | None => self.parameters.0.as_ref(), |
211 | for (i, parameter) in self.parameters.iter().enumerate() { | 321 | Some(default_parameters) => { |
212 | match (parameter, default_parameters.get(i)) { | 322 | for (i, parameter) in self.parameters.iter().enumerate() { |
213 | (&Ty::Unknown, _) | (_, None) => { | 323 | match (parameter, default_parameters.get(i)) { |
214 | non_default_parameters.push(parameter.clone()) | 324 | (&Ty::Unknown, _) | (_, None) => { |
325 | non_default_parameters.push(parameter.clone()) | ||
326 | } | ||
327 | (_, Some(default_parameter)) | ||
328 | if parameter != default_parameter => | ||
329 | { | ||
330 | non_default_parameters.push(parameter.clone()) | ||
331 | } | ||
332 | _ => (), | ||
215 | } | 333 | } |
216 | (_, Some(default_parameter)) | ||
217 | if parameter != default_parameter => | ||
218 | { | ||
219 | non_default_parameters.push(parameter.clone()) | ||
220 | } | ||
221 | _ => (), | ||
222 | } | 334 | } |
335 | &non_default_parameters | ||
223 | } | 336 | } |
224 | &non_default_parameters | ||
225 | } | 337 | } |
226 | } | 338 | } else { |
227 | } else { | 339 | self.parameters.0.as_ref() |
228 | self.parameters.0.as_ref() | 340 | }; |
229 | }; | 341 | if !parameters_to_write.is_empty() { |
230 | write!(f, "<")?; | 342 | write!(f, "<")?; |
231 | f.write_joined(parameters_to_write, ", ")?; | 343 | f.write_joined(parameters_to_write, ", ")?; |
232 | write!(f, ">")?; | 344 | write!(f, ">")?; |
345 | } | ||
233 | } | 346 | } |
234 | } | 347 | } |
235 | TypeCtor::AssociatedType(type_alias) => { | 348 | TypeCtor::AssociatedType(type_alias) => { |
@@ -269,7 +382,7 @@ impl HirDisplay for ApplicationTy { | |||
269 | } | 382 | } |
270 | 383 | ||
271 | impl HirDisplay for ProjectionTy { | 384 | impl HirDisplay for ProjectionTy { |
272 | fn hir_fmt(&self, f: &mut HirFormatter) -> fmt::Result { | 385 | fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> { |
273 | if f.should_truncate() { | 386 | if f.should_truncate() { |
274 | return write!(f, "{}", TYPE_HINT_TRUNCATION); | 387 | return write!(f, "{}", TYPE_HINT_TRUNCATION); |
275 | } | 388 | } |
@@ -287,7 +400,7 @@ impl HirDisplay for ProjectionTy { | |||
287 | } | 400 | } |
288 | 401 | ||
289 | impl HirDisplay for Ty { | 402 | impl HirDisplay for Ty { |
290 | fn hir_fmt(&self, f: &mut HirFormatter) -> fmt::Result { | 403 | fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> { |
291 | if f.should_truncate() { | 404 | if f.should_truncate() { |
292 | return write!(f, "{}", TYPE_HINT_TRUNCATION); | 405 | return write!(f, "{}", TYPE_HINT_TRUNCATION); |
293 | } | 406 | } |
@@ -332,7 +445,7 @@ impl HirDisplay for Ty { | |||
332 | fn write_bounds_like_dyn_trait( | 445 | fn write_bounds_like_dyn_trait( |
333 | predicates: &[GenericPredicate], | 446 | predicates: &[GenericPredicate], |
334 | f: &mut HirFormatter, | 447 | f: &mut HirFormatter, |
335 | ) -> fmt::Result { | 448 | ) -> Result<(), HirDisplayError> { |
336 | // Note: This code is written to produce nice results (i.e. | 449 | // Note: This code is written to produce nice results (i.e. |
337 | // corresponding to surface Rust) for types that can occur in | 450 | // corresponding to surface Rust) for types that can occur in |
338 | // actual Rust. It will have weird results if the predicates | 451 | // actual Rust. It will have weird results if the predicates |
@@ -394,7 +507,7 @@ fn write_bounds_like_dyn_trait( | |||
394 | } | 507 | } |
395 | 508 | ||
396 | impl TraitRef { | 509 | impl TraitRef { |
397 | fn hir_fmt_ext(&self, f: &mut HirFormatter, use_as: bool) -> fmt::Result { | 510 | fn hir_fmt_ext(&self, f: &mut HirFormatter, use_as: bool) -> Result<(), HirDisplayError> { |
398 | if f.should_truncate() { | 511 | if f.should_truncate() { |
399 | return write!(f, "{}", TYPE_HINT_TRUNCATION); | 512 | return write!(f, "{}", TYPE_HINT_TRUNCATION); |
400 | } | 513 | } |
@@ -416,19 +529,19 @@ impl TraitRef { | |||
416 | } | 529 | } |
417 | 530 | ||
418 | impl HirDisplay for TraitRef { | 531 | impl HirDisplay for TraitRef { |
419 | fn hir_fmt(&self, f: &mut HirFormatter) -> fmt::Result { | 532 | fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> { |
420 | self.hir_fmt_ext(f, false) | 533 | self.hir_fmt_ext(f, false) |
421 | } | 534 | } |
422 | } | 535 | } |
423 | 536 | ||
424 | impl HirDisplay for &GenericPredicate { | 537 | impl HirDisplay for &GenericPredicate { |
425 | fn hir_fmt(&self, f: &mut HirFormatter) -> fmt::Result { | 538 | fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> { |
426 | HirDisplay::hir_fmt(*self, f) | 539 | HirDisplay::hir_fmt(*self, f) |
427 | } | 540 | } |
428 | } | 541 | } |
429 | 542 | ||
430 | impl HirDisplay for GenericPredicate { | 543 | impl HirDisplay for GenericPredicate { |
431 | fn hir_fmt(&self, f: &mut HirFormatter) -> fmt::Result { | 544 | fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> { |
432 | if f.should_truncate() { | 545 | if f.should_truncate() { |
433 | return write!(f, "{}", TYPE_HINT_TRUNCATION); | 546 | return write!(f, "{}", TYPE_HINT_TRUNCATION); |
434 | } | 547 | } |
@@ -452,15 +565,15 @@ impl HirDisplay for GenericPredicate { | |||
452 | } | 565 | } |
453 | 566 | ||
454 | impl HirDisplay for Obligation { | 567 | impl HirDisplay for Obligation { |
455 | fn hir_fmt(&self, f: &mut HirFormatter) -> fmt::Result { | 568 | fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> { |
456 | match self { | 569 | Ok(match self { |
457 | Obligation::Trait(tr) => write!(f, "Implements({})", tr.display(f.db)), | 570 | Obligation::Trait(tr) => write!(f, "Implements({})", tr.display(f.db))?, |
458 | Obligation::Projection(proj) => write!( | 571 | Obligation::Projection(proj) => write!( |
459 | f, | 572 | f, |
460 | "Normalize({} => {})", | 573 | "Normalize({} => {})", |
461 | proj.projection_ty.display(f.db), | 574 | proj.projection_ty.display(f.db), |
462 | proj.ty.display(f.db) | 575 | proj.ty.display(f.db) |
463 | ), | 576 | )?, |
464 | } | 577 | }) |
465 | } | 578 | } |
466 | } | 579 | } |
diff --git a/crates/ra_hir_ty/src/infer.rs b/crates/ra_hir_ty/src/infer.rs index bd4ef69a0..a21ad8d86 100644 --- a/crates/ra_hir_ty/src/infer.rs +++ b/crates/ra_hir_ty/src/infer.rs | |||
@@ -210,6 +210,13 @@ struct InferenceContext<'a> { | |||
210 | /// closures, but currently this is the only field that will change there, | 210 | /// closures, but currently this is the only field that will change there, |
211 | /// so it doesn't make sense. | 211 | /// so it doesn't make sense. |
212 | return_ty: Ty, | 212 | return_ty: Ty, |
213 | diverges: Diverges, | ||
214 | breakables: Vec<BreakableContext>, | ||
215 | } | ||
216 | |||
217 | #[derive(Clone, Debug)] | ||
218 | struct BreakableContext { | ||
219 | pub may_break: bool, | ||
213 | } | 220 | } |
214 | 221 | ||
215 | impl<'a> InferenceContext<'a> { | 222 | impl<'a> InferenceContext<'a> { |
@@ -224,6 +231,8 @@ impl<'a> InferenceContext<'a> { | |||
224 | owner, | 231 | owner, |
225 | body: db.body(owner), | 232 | body: db.body(owner), |
226 | resolver, | 233 | resolver, |
234 | diverges: Diverges::Maybe, | ||
235 | breakables: Vec::new(), | ||
227 | } | 236 | } |
228 | } | 237 | } |
229 | 238 | ||
@@ -666,15 +675,57 @@ impl Expectation { | |||
666 | } | 675 | } |
667 | } | 676 | } |
668 | 677 | ||
678 | #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] | ||
679 | enum Diverges { | ||
680 | Maybe, | ||
681 | Always, | ||
682 | } | ||
683 | |||
684 | impl Diverges { | ||
685 | fn is_always(self) -> bool { | ||
686 | self == Diverges::Always | ||
687 | } | ||
688 | } | ||
689 | |||
690 | impl std::ops::BitAnd for Diverges { | ||
691 | type Output = Self; | ||
692 | fn bitand(self, other: Self) -> Self { | ||
693 | std::cmp::min(self, other) | ||
694 | } | ||
695 | } | ||
696 | |||
697 | impl std::ops::BitOr for Diverges { | ||
698 | type Output = Self; | ||
699 | fn bitor(self, other: Self) -> Self { | ||
700 | std::cmp::max(self, other) | ||
701 | } | ||
702 | } | ||
703 | |||
704 | impl std::ops::BitAndAssign for Diverges { | ||
705 | fn bitand_assign(&mut self, other: Self) { | ||
706 | *self = *self & other; | ||
707 | } | ||
708 | } | ||
709 | |||
710 | impl std::ops::BitOrAssign for Diverges { | ||
711 | fn bitor_assign(&mut self, other: Self) { | ||
712 | *self = *self | other; | ||
713 | } | ||
714 | } | ||
715 | |||
669 | mod diagnostics { | 716 | mod diagnostics { |
670 | use hir_def::{expr::ExprId, FunctionId}; | 717 | use hir_def::{expr::ExprId, FunctionId}; |
671 | use hir_expand::diagnostics::DiagnosticSink; | 718 | use hir_expand::diagnostics::DiagnosticSink; |
672 | 719 | ||
673 | use crate::{db::HirDatabase, diagnostics::NoSuchField}; | 720 | use crate::{ |
721 | db::HirDatabase, | ||
722 | diagnostics::{BreakOutsideOfLoop, NoSuchField}, | ||
723 | }; | ||
674 | 724 | ||
675 | #[derive(Debug, PartialEq, Eq, Clone)] | 725 | #[derive(Debug, PartialEq, Eq, Clone)] |
676 | pub(super) enum InferenceDiagnostic { | 726 | pub(super) enum InferenceDiagnostic { |
677 | NoSuchField { expr: ExprId, field: usize }, | 727 | NoSuchField { expr: ExprId, field: usize }, |
728 | BreakOutsideOfLoop { expr: ExprId }, | ||
678 | } | 729 | } |
679 | 730 | ||
680 | impl InferenceDiagnostic { | 731 | impl InferenceDiagnostic { |
@@ -690,6 +741,13 @@ mod diagnostics { | |||
690 | let field = source_map.field_syntax(*expr, *field); | 741 | let field = source_map.field_syntax(*expr, *field); |
691 | sink.push(NoSuchField { file: field.file_id, field: field.value }) | 742 | sink.push(NoSuchField { file: field.file_id, field: field.value }) |
692 | } | 743 | } |
744 | InferenceDiagnostic::BreakOutsideOfLoop { expr } => { | ||
745 | let (_, source_map) = db.body_with_source_map(owner.into()); | ||
746 | let ptr = source_map | ||
747 | .expr_syntax(*expr) | ||
748 | .expect("break outside of loop in synthetic syntax"); | ||
749 | sink.push(BreakOutsideOfLoop { file: ptr.file_id, expr: ptr.value }) | ||
750 | } | ||
693 | } | 751 | } |
694 | } | 752 | } |
695 | } | 753 | } |
diff --git a/crates/ra_hir_ty/src/infer/coerce.rs b/crates/ra_hir_ty/src/infer/coerce.rs index 89200255a..173ec59ed 100644 --- a/crates/ra_hir_ty/src/infer/coerce.rs +++ b/crates/ra_hir_ty/src/infer/coerce.rs | |||
@@ -20,21 +20,35 @@ impl<'a> InferenceContext<'a> { | |||
20 | self.coerce_inner(from_ty, &to_ty) | 20 | self.coerce_inner(from_ty, &to_ty) |
21 | } | 21 | } |
22 | 22 | ||
23 | /// Merge two types from different branches, with possible implicit coerce. | 23 | /// Merge two types from different branches, with possible coercion. |
24 | /// | 24 | /// |
25 | /// Note that it is only possible that one type are coerced to another. | 25 | /// Mostly this means trying to coerce one to the other, but |
26 | /// Coercing both types to another least upper bound type is not possible in rustc, | 26 | /// - if we have two function types for different functions, we need to |
27 | /// which will simply result in "incompatible types" error. | 27 | /// coerce both to function pointers; |
28 | /// - if we were concerned with lifetime subtyping, we'd need to look for a | ||
29 | /// least upper bound. | ||
28 | pub(super) fn coerce_merge_branch(&mut self, ty1: &Ty, ty2: &Ty) -> Ty { | 30 | pub(super) fn coerce_merge_branch(&mut self, ty1: &Ty, ty2: &Ty) -> Ty { |
29 | if self.coerce(ty1, ty2) { | 31 | if self.coerce(ty1, ty2) { |
30 | ty2.clone() | 32 | ty2.clone() |
31 | } else if self.coerce(ty2, ty1) { | 33 | } else if self.coerce(ty2, ty1) { |
32 | ty1.clone() | 34 | ty1.clone() |
33 | } else { | 35 | } else { |
34 | tested_by!(coerce_merge_fail_fallback); | 36 | if let (ty_app!(TypeCtor::FnDef(_)), ty_app!(TypeCtor::FnDef(_))) = (ty1, ty2) { |
35 | // For incompatible types, we use the latter one as result | 37 | tested_by!(coerce_fn_reification); |
36 | // to be better recovery for `if` without `else`. | 38 | // Special case: two function types. Try to coerce both to |
37 | ty2.clone() | 39 | // pointers to have a chance at getting a match. See |
40 | // https://github.com/rust-lang/rust/blob/7b805396bf46dce972692a6846ce2ad8481c5f85/src/librustc_typeck/check/coercion.rs#L877-L916 | ||
41 | let sig1 = ty1.callable_sig(self.db).expect("FnDef without callable sig"); | ||
42 | let sig2 = ty2.callable_sig(self.db).expect("FnDef without callable sig"); | ||
43 | let ptr_ty1 = Ty::fn_ptr(sig1); | ||
44 | let ptr_ty2 = Ty::fn_ptr(sig2); | ||
45 | self.coerce_merge_branch(&ptr_ty1, &ptr_ty2) | ||
46 | } else { | ||
47 | tested_by!(coerce_merge_fail_fallback); | ||
48 | // For incompatible types, we use the latter one as result | ||
49 | // to be better recovery for `if` without `else`. | ||
50 | ty2.clone() | ||
51 | } | ||
38 | } | 52 | } |
39 | } | 53 | } |
40 | 54 | ||
@@ -84,9 +98,7 @@ impl<'a> InferenceContext<'a> { | |||
84 | match from_ty.callable_sig(self.db) { | 98 | match from_ty.callable_sig(self.db) { |
85 | None => return false, | 99 | None => return false, |
86 | Some(sig) => { | 100 | Some(sig) => { |
87 | let num_args = sig.params_and_return.len() as u16 - 1; | 101 | from_ty = Ty::fn_ptr(sig); |
88 | from_ty = | ||
89 | Ty::apply(TypeCtor::FnPtr { num_args }, Substs(sig.params_and_return)); | ||
90 | } | 102 | } |
91 | } | 103 | } |
92 | } | 104 | } |
diff --git a/crates/ra_hir_ty/src/infer/expr.rs b/crates/ra_hir_ty/src/infer/expr.rs index 83f946eee..0b67d216a 100644 --- a/crates/ra_hir_ty/src/infer/expr.rs +++ b/crates/ra_hir_ty/src/infer/expr.rs | |||
@@ -1,7 +1,7 @@ | |||
1 | //! Type inference for expressions. | 1 | //! Type inference for expressions. |
2 | 2 | ||
3 | use std::iter::{repeat, repeat_with}; | 3 | use std::iter::{repeat, repeat_with}; |
4 | use std::sync::Arc; | 4 | use std::{mem, sync::Arc}; |
5 | 5 | ||
6 | use hir_def::{ | 6 | use hir_def::{ |
7 | builtin_type::Signedness, | 7 | builtin_type::Signedness, |
@@ -21,11 +21,18 @@ use crate::{ | |||
21 | Ty, TypeCtor, Uncertain, | 21 | Ty, TypeCtor, Uncertain, |
22 | }; | 22 | }; |
23 | 23 | ||
24 | use super::{BindingMode, Expectation, InferenceContext, InferenceDiagnostic, TypeMismatch}; | 24 | use super::{ |
25 | BindingMode, BreakableContext, Diverges, Expectation, InferenceContext, InferenceDiagnostic, | ||
26 | TypeMismatch, | ||
27 | }; | ||
25 | 28 | ||
26 | impl<'a> InferenceContext<'a> { | 29 | impl<'a> InferenceContext<'a> { |
27 | pub(super) fn infer_expr(&mut self, tgt_expr: ExprId, expected: &Expectation) -> Ty { | 30 | pub(super) fn infer_expr(&mut self, tgt_expr: ExprId, expected: &Expectation) -> Ty { |
28 | let ty = self.infer_expr_inner(tgt_expr, expected); | 31 | let ty = self.infer_expr_inner(tgt_expr, expected); |
32 | if ty.is_never() { | ||
33 | // Any expression that produces a value of type `!` must have diverged | ||
34 | self.diverges = Diverges::Always; | ||
35 | } | ||
29 | let could_unify = self.unify(&ty, &expected.ty); | 36 | let could_unify = self.unify(&ty, &expected.ty); |
30 | if !could_unify { | 37 | if !could_unify { |
31 | self.result.type_mismatches.insert( | 38 | self.result.type_mismatches.insert( |
@@ -64,11 +71,18 @@ impl<'a> InferenceContext<'a> { | |||
64 | // if let is desugared to match, so this is always simple if | 71 | // if let is desugared to match, so this is always simple if |
65 | self.infer_expr(*condition, &Expectation::has_type(Ty::simple(TypeCtor::Bool))); | 72 | self.infer_expr(*condition, &Expectation::has_type(Ty::simple(TypeCtor::Bool))); |
66 | 73 | ||
74 | let condition_diverges = mem::replace(&mut self.diverges, Diverges::Maybe); | ||
75 | let mut both_arms_diverge = Diverges::Always; | ||
76 | |||
67 | let then_ty = self.infer_expr_inner(*then_branch, &expected); | 77 | let then_ty = self.infer_expr_inner(*then_branch, &expected); |
78 | both_arms_diverge &= mem::replace(&mut self.diverges, Diverges::Maybe); | ||
68 | let else_ty = match else_branch { | 79 | let else_ty = match else_branch { |
69 | Some(else_branch) => self.infer_expr_inner(*else_branch, &expected), | 80 | Some(else_branch) => self.infer_expr_inner(*else_branch, &expected), |
70 | None => Ty::unit(), | 81 | None => Ty::unit(), |
71 | }; | 82 | }; |
83 | both_arms_diverge &= self.diverges; | ||
84 | |||
85 | self.diverges = condition_diverges | both_arms_diverge; | ||
72 | 86 | ||
73 | self.coerce_merge_branch(&then_ty, &else_ty) | 87 | self.coerce_merge_branch(&then_ty, &else_ty) |
74 | } | 88 | } |
@@ -79,24 +93,43 @@ impl<'a> InferenceContext<'a> { | |||
79 | Ty::Unknown | 93 | Ty::Unknown |
80 | } | 94 | } |
81 | Expr::Loop { body } => { | 95 | Expr::Loop { body } => { |
96 | self.breakables.push(BreakableContext { may_break: false }); | ||
82 | self.infer_expr(*body, &Expectation::has_type(Ty::unit())); | 97 | self.infer_expr(*body, &Expectation::has_type(Ty::unit())); |
98 | |||
99 | let ctxt = self.breakables.pop().expect("breakable stack broken"); | ||
100 | if ctxt.may_break { | ||
101 | self.diverges = Diverges::Maybe; | ||
102 | } | ||
83 | // FIXME handle break with value | 103 | // FIXME handle break with value |
84 | Ty::simple(TypeCtor::Never) | 104 | if ctxt.may_break { |
105 | Ty::unit() | ||
106 | } else { | ||
107 | Ty::simple(TypeCtor::Never) | ||
108 | } | ||
85 | } | 109 | } |
86 | Expr::While { condition, body } => { | 110 | Expr::While { condition, body } => { |
111 | self.breakables.push(BreakableContext { may_break: false }); | ||
87 | // while let is desugared to a match loop, so this is always simple while | 112 | // while let is desugared to a match loop, so this is always simple while |
88 | self.infer_expr(*condition, &Expectation::has_type(Ty::simple(TypeCtor::Bool))); | 113 | self.infer_expr(*condition, &Expectation::has_type(Ty::simple(TypeCtor::Bool))); |
89 | self.infer_expr(*body, &Expectation::has_type(Ty::unit())); | 114 | self.infer_expr(*body, &Expectation::has_type(Ty::unit())); |
115 | let _ctxt = self.breakables.pop().expect("breakable stack broken"); | ||
116 | // the body may not run, so it diverging doesn't mean we diverge | ||
117 | self.diverges = Diverges::Maybe; | ||
90 | Ty::unit() | 118 | Ty::unit() |
91 | } | 119 | } |
92 | Expr::For { iterable, body, pat } => { | 120 | Expr::For { iterable, body, pat } => { |
93 | let iterable_ty = self.infer_expr(*iterable, &Expectation::none()); | 121 | let iterable_ty = self.infer_expr(*iterable, &Expectation::none()); |
94 | 122 | ||
123 | self.breakables.push(BreakableContext { may_break: false }); | ||
95 | let pat_ty = | 124 | let pat_ty = |
96 | self.resolve_associated_type(iterable_ty, self.resolve_into_iter_item()); | 125 | self.resolve_associated_type(iterable_ty, self.resolve_into_iter_item()); |
97 | 126 | ||
98 | self.infer_pat(*pat, &pat_ty, BindingMode::default()); | 127 | self.infer_pat(*pat, &pat_ty, BindingMode::default()); |
128 | |||
99 | self.infer_expr(*body, &Expectation::has_type(Ty::unit())); | 129 | self.infer_expr(*body, &Expectation::has_type(Ty::unit())); |
130 | let _ctxt = self.breakables.pop().expect("breakable stack broken"); | ||
131 | // the body may not run, so it diverging doesn't mean we diverge | ||
132 | self.diverges = Diverges::Maybe; | ||
100 | Ty::unit() | 133 | Ty::unit() |
101 | } | 134 | } |
102 | Expr::Lambda { body, args, ret_type, arg_types } => { | 135 | Expr::Lambda { body, args, ret_type, arg_types } => { |
@@ -132,10 +165,12 @@ impl<'a> InferenceContext<'a> { | |||
132 | // infer the body. | 165 | // infer the body. |
133 | self.coerce(&closure_ty, &expected.ty); | 166 | self.coerce(&closure_ty, &expected.ty); |
134 | 167 | ||
135 | let prev_ret_ty = std::mem::replace(&mut self.return_ty, ret_ty.clone()); | 168 | let prev_diverges = mem::replace(&mut self.diverges, Diverges::Maybe); |
169 | let prev_ret_ty = mem::replace(&mut self.return_ty, ret_ty.clone()); | ||
136 | 170 | ||
137 | self.infer_expr_coerce(*body, &Expectation::has_type(ret_ty)); | 171 | self.infer_expr_coerce(*body, &Expectation::has_type(ret_ty)); |
138 | 172 | ||
173 | self.diverges = prev_diverges; | ||
139 | self.return_ty = prev_ret_ty; | 174 | self.return_ty = prev_ret_ty; |
140 | 175 | ||
141 | closure_ty | 176 | closure_ty |
@@ -165,7 +200,11 @@ impl<'a> InferenceContext<'a> { | |||
165 | self.table.new_type_var() | 200 | self.table.new_type_var() |
166 | }; | 201 | }; |
167 | 202 | ||
203 | let matchee_diverges = self.diverges; | ||
204 | let mut all_arms_diverge = Diverges::Always; | ||
205 | |||
168 | for arm in arms { | 206 | for arm in arms { |
207 | self.diverges = Diverges::Maybe; | ||
169 | let _pat_ty = self.infer_pat(arm.pat, &input_ty, BindingMode::default()); | 208 | let _pat_ty = self.infer_pat(arm.pat, &input_ty, BindingMode::default()); |
170 | if let Some(guard_expr) = arm.guard { | 209 | if let Some(guard_expr) = arm.guard { |
171 | self.infer_expr( | 210 | self.infer_expr( |
@@ -175,9 +214,12 @@ impl<'a> InferenceContext<'a> { | |||
175 | } | 214 | } |
176 | 215 | ||
177 | let arm_ty = self.infer_expr_inner(arm.expr, &expected); | 216 | let arm_ty = self.infer_expr_inner(arm.expr, &expected); |
217 | all_arms_diverge &= self.diverges; | ||
178 | result_ty = self.coerce_merge_branch(&result_ty, &arm_ty); | 218 | result_ty = self.coerce_merge_branch(&result_ty, &arm_ty); |
179 | } | 219 | } |
180 | 220 | ||
221 | self.diverges = matchee_diverges | all_arms_diverge; | ||
222 | |||
181 | result_ty | 223 | result_ty |
182 | } | 224 | } |
183 | Expr::Path(p) => { | 225 | Expr::Path(p) => { |
@@ -191,6 +233,13 @@ impl<'a> InferenceContext<'a> { | |||
191 | // FIXME handle break with value | 233 | // FIXME handle break with value |
192 | self.infer_expr(*expr, &Expectation::none()); | 234 | self.infer_expr(*expr, &Expectation::none()); |
193 | } | 235 | } |
236 | if let Some(ctxt) = self.breakables.last_mut() { | ||
237 | ctxt.may_break = true; | ||
238 | } else { | ||
239 | self.push_diagnostic(InferenceDiagnostic::BreakOutsideOfLoop { | ||
240 | expr: tgt_expr, | ||
241 | }); | ||
242 | } | ||
194 | Ty::simple(TypeCtor::Never) | 243 | Ty::simple(TypeCtor::Never) |
195 | } | 244 | } |
196 | Expr::Return { expr } => { | 245 | Expr::Return { expr } => { |
@@ -501,8 +550,8 @@ impl<'a> InferenceContext<'a> { | |||
501 | } | 550 | } |
502 | Literal::ByteString(..) => { | 551 | Literal::ByteString(..) => { |
503 | let byte_type = Ty::simple(TypeCtor::Int(Uncertain::Known(IntTy::u8()))); | 552 | let byte_type = Ty::simple(TypeCtor::Int(Uncertain::Known(IntTy::u8()))); |
504 | let slice_type = Ty::apply_one(TypeCtor::Slice, byte_type); | 553 | let array_type = Ty::apply_one(TypeCtor::Array, byte_type); |
505 | Ty::apply_one(TypeCtor::Ref(Mutability::Shared), slice_type) | 554 | Ty::apply_one(TypeCtor::Ref(Mutability::Shared), array_type) |
506 | } | 555 | } |
507 | Literal::Char(..) => Ty::simple(TypeCtor::Char), | 556 | Literal::Char(..) => Ty::simple(TypeCtor::Char), |
508 | Literal::Int(_v, ty) => Ty::simple(TypeCtor::Int((*ty).into())), | 557 | Literal::Int(_v, ty) => Ty::simple(TypeCtor::Int((*ty).into())), |
@@ -522,7 +571,6 @@ impl<'a> InferenceContext<'a> { | |||
522 | tail: Option<ExprId>, | 571 | tail: Option<ExprId>, |
523 | expected: &Expectation, | 572 | expected: &Expectation, |
524 | ) -> Ty { | 573 | ) -> Ty { |
525 | let mut diverges = false; | ||
526 | for stmt in statements { | 574 | for stmt in statements { |
527 | match stmt { | 575 | match stmt { |
528 | Statement::Let { pat, type_ref, initializer } => { | 576 | Statement::Let { pat, type_ref, initializer } => { |
@@ -544,9 +592,7 @@ impl<'a> InferenceContext<'a> { | |||
544 | self.infer_pat(*pat, &ty, BindingMode::default()); | 592 | self.infer_pat(*pat, &ty, BindingMode::default()); |
545 | } | 593 | } |
546 | Statement::Expr(expr) => { | 594 | Statement::Expr(expr) => { |
547 | if let ty_app!(TypeCtor::Never) = self.infer_expr(*expr, &Expectation::none()) { | 595 | self.infer_expr(*expr, &Expectation::none()); |
548 | diverges = true; | ||
549 | } | ||
550 | } | 596 | } |
551 | } | 597 | } |
552 | } | 598 | } |
@@ -554,14 +600,22 @@ impl<'a> InferenceContext<'a> { | |||
554 | let ty = if let Some(expr) = tail { | 600 | let ty = if let Some(expr) = tail { |
555 | self.infer_expr_coerce(expr, expected) | 601 | self.infer_expr_coerce(expr, expected) |
556 | } else { | 602 | } else { |
557 | self.coerce(&Ty::unit(), expected.coercion_target()); | 603 | // Citing rustc: if there is no explicit tail expression, |
558 | Ty::unit() | 604 | // that is typically equivalent to a tail expression |
605 | // of `()` -- except if the block diverges. In that | ||
606 | // case, there is no value supplied from the tail | ||
607 | // expression (assuming there are no other breaks, | ||
608 | // this implies that the type of the block will be | ||
609 | // `!`). | ||
610 | if self.diverges.is_always() { | ||
611 | // we don't even make an attempt at coercion | ||
612 | self.table.new_maybe_never_type_var() | ||
613 | } else { | ||
614 | self.coerce(&Ty::unit(), expected.coercion_target()); | ||
615 | Ty::unit() | ||
616 | } | ||
559 | }; | 617 | }; |
560 | if diverges { | 618 | ty |
561 | Ty::simple(TypeCtor::Never) | ||
562 | } else { | ||
563 | ty | ||
564 | } | ||
565 | } | 619 | } |
566 | 620 | ||
567 | fn infer_method_call( | 621 | fn infer_method_call( |
diff --git a/crates/ra_hir_ty/src/lib.rs b/crates/ra_hir_ty/src/lib.rs index a6f56c661..e8f3482fe 100644 --- a/crates/ra_hir_ty/src/lib.rs +++ b/crates/ra_hir_ty/src/lib.rs | |||
@@ -683,6 +683,12 @@ impl Ty { | |||
683 | pub fn unit() -> Self { | 683 | pub fn unit() -> Self { |
684 | Ty::apply(TypeCtor::Tuple { cardinality: 0 }, Substs::empty()) | 684 | Ty::apply(TypeCtor::Tuple { cardinality: 0 }, Substs::empty()) |
685 | } | 685 | } |
686 | pub fn fn_ptr(sig: FnSig) -> Self { | ||
687 | Ty::apply( | ||
688 | TypeCtor::FnPtr { num_args: sig.params().len() as u16 }, | ||
689 | Substs(sig.params_and_return), | ||
690 | ) | ||
691 | } | ||
686 | 692 | ||
687 | pub fn as_reference(&self) -> Option<(&Ty, Mutability)> { | 693 | pub fn as_reference(&self) -> Option<(&Ty, Mutability)> { |
688 | match self { | 694 | match self { |
@@ -730,6 +736,10 @@ impl Ty { | |||
730 | } | 736 | } |
731 | } | 737 | } |
732 | 738 | ||
739 | pub fn is_never(&self) -> bool { | ||
740 | matches!(self, Ty::Apply(ApplicationTy { ctor: TypeCtor::Never, .. })) | ||
741 | } | ||
742 | |||
733 | /// If this is a `dyn Trait` type, this returns the `Trait` part. | 743 | /// If this is a `dyn Trait` type, this returns the `Trait` part. |
734 | pub fn dyn_trait_ref(&self) -> Option<&TraitRef> { | 744 | pub fn dyn_trait_ref(&self) -> Option<&TraitRef> { |
735 | match self { | 745 | match self { |
diff --git a/crates/ra_hir_ty/src/marks.rs b/crates/ra_hir_ty/src/marks.rs index de5cb1d6b..a39740143 100644 --- a/crates/ra_hir_ty/src/marks.rs +++ b/crates/ra_hir_ty/src/marks.rs | |||
@@ -7,5 +7,6 @@ test_utils::marks!( | |||
7 | impl_self_type_match_without_receiver | 7 | impl_self_type_match_without_receiver |
8 | match_ergonomics_ref | 8 | match_ergonomics_ref |
9 | coerce_merge_fail_fallback | 9 | coerce_merge_fail_fallback |
10 | coerce_fn_reification | ||
10 | trait_self_implements_self | 11 | trait_self_implements_self |
11 | ); | 12 | ); |
diff --git a/crates/ra_hir_ty/src/op.rs b/crates/ra_hir_ty/src/op.rs index 54e2bd05a..0870874fc 100644 --- a/crates/ra_hir_ty/src/op.rs +++ b/crates/ra_hir_ty/src/op.rs | |||
@@ -30,7 +30,8 @@ pub(super) fn binary_op_return_ty(op: BinaryOp, lhs_ty: Ty, rhs_ty: Ty) -> Ty { | |||
30 | pub(super) fn binary_op_rhs_expectation(op: BinaryOp, lhs_ty: Ty) -> Ty { | 30 | pub(super) fn binary_op_rhs_expectation(op: BinaryOp, lhs_ty: Ty) -> Ty { |
31 | match op { | 31 | match op { |
32 | BinaryOp::LogicOp(..) => Ty::simple(TypeCtor::Bool), | 32 | BinaryOp::LogicOp(..) => Ty::simple(TypeCtor::Bool), |
33 | BinaryOp::Assignment { op: None } | BinaryOp::CmpOp(CmpOp::Eq { .. }) => match lhs_ty { | 33 | BinaryOp::Assignment { op: None } => lhs_ty, |
34 | BinaryOp::CmpOp(CmpOp::Eq { .. }) => match lhs_ty { | ||
34 | Ty::Apply(ApplicationTy { ctor, .. }) => match ctor { | 35 | Ty::Apply(ApplicationTy { ctor, .. }) => match ctor { |
35 | TypeCtor::Int(..) | 36 | TypeCtor::Int(..) |
36 | | TypeCtor::Float(..) | 37 | | TypeCtor::Float(..) |
diff --git a/crates/ra_hir_ty/src/tests.rs b/crates/ra_hir_ty/src/tests.rs index d60732e19..1fe05c70c 100644 --- a/crates/ra_hir_ty/src/tests.rs +++ b/crates/ra_hir_ty/src/tests.rs | |||
@@ -6,6 +6,7 @@ mod patterns; | |||
6 | mod traits; | 6 | mod traits; |
7 | mod method_resolution; | 7 | mod method_resolution; |
8 | mod macros; | 8 | mod macros; |
9 | mod display_source_code; | ||
9 | 10 | ||
10 | use std::sync::Arc; | 11 | use std::sync::Arc; |
11 | 12 | ||
@@ -16,7 +17,7 @@ use hir_def::{ | |||
16 | item_scope::ItemScope, | 17 | item_scope::ItemScope, |
17 | keys, | 18 | keys, |
18 | nameres::CrateDefMap, | 19 | nameres::CrateDefMap, |
19 | AssocItemId, DefWithBodyId, LocalModuleId, Lookup, ModuleDefId, | 20 | AssocItemId, DefWithBodyId, LocalModuleId, Lookup, ModuleDefId, ModuleId, |
20 | }; | 21 | }; |
21 | use hir_expand::{db::AstDatabase, InFile}; | 22 | use hir_expand::{db::AstDatabase, InFile}; |
22 | use insta::assert_snapshot; | 23 | use insta::assert_snapshot; |
@@ -37,6 +38,18 @@ use crate::{ | |||
37 | // update the snapshots. | 38 | // update the snapshots. |
38 | 39 | ||
39 | fn type_at_pos(db: &TestDB, pos: FilePosition) -> String { | 40 | fn type_at_pos(db: &TestDB, pos: FilePosition) -> String { |
41 | type_at_pos_displayed(db, pos, |ty, _| ty.display(db).to_string()) | ||
42 | } | ||
43 | |||
44 | fn displayed_source_at_pos(db: &TestDB, pos: FilePosition) -> String { | ||
45 | type_at_pos_displayed(db, pos, |ty, module_id| ty.display_source_code(db, module_id).unwrap()) | ||
46 | } | ||
47 | |||
48 | fn type_at_pos_displayed( | ||
49 | db: &TestDB, | ||
50 | pos: FilePosition, | ||
51 | display_fn: impl FnOnce(&Ty, ModuleId) -> String, | ||
52 | ) -> String { | ||
40 | let file = db.parse(pos.file_id).ok().unwrap(); | 53 | let file = db.parse(pos.file_id).ok().unwrap(); |
41 | let expr = algo::find_node_at_offset::<ast::Expr>(file.syntax(), pos.offset).unwrap(); | 54 | let expr = algo::find_node_at_offset::<ast::Expr>(file.syntax(), pos.offset).unwrap(); |
42 | let fn_def = expr.syntax().ancestors().find_map(ast::FnDef::cast).unwrap(); | 55 | let fn_def = expr.syntax().ancestors().find_map(ast::FnDef::cast).unwrap(); |
@@ -49,7 +62,7 @@ fn type_at_pos(db: &TestDB, pos: FilePosition) -> String { | |||
49 | if let Some(expr_id) = source_map.node_expr(InFile::new(pos.file_id.into(), &expr)) { | 62 | if let Some(expr_id) = source_map.node_expr(InFile::new(pos.file_id.into(), &expr)) { |
50 | let infer = db.infer(func.into()); | 63 | let infer = db.infer(func.into()); |
51 | let ty = &infer[expr_id]; | 64 | let ty = &infer[expr_id]; |
52 | return ty.display(db).to_string(); | 65 | return display_fn(ty, module); |
53 | } | 66 | } |
54 | panic!("Can't find expression") | 67 | panic!("Can't find expression") |
55 | } | 68 | } |
@@ -518,3 +531,21 @@ fn missing_record_pat_field_no_diagnostic_if_not_exhaustive() { | |||
518 | 531 | ||
519 | assert_snapshot!(diagnostics, @""); | 532 | assert_snapshot!(diagnostics, @""); |
520 | } | 533 | } |
534 | |||
535 | #[test] | ||
536 | fn break_outside_of_loop() { | ||
537 | let diagnostics = TestDB::with_files( | ||
538 | r" | ||
539 | //- /lib.rs | ||
540 | fn foo() { | ||
541 | break; | ||
542 | } | ||
543 | ", | ||
544 | ) | ||
545 | .diagnostics() | ||
546 | .0; | ||
547 | |||
548 | assert_snapshot!(diagnostics, @r###""break": break outside of loop | ||
549 | "### | ||
550 | ); | ||
551 | } | ||
diff --git a/crates/ra_hir_ty/src/tests/coercion.rs b/crates/ra_hir_ty/src/tests/coercion.rs index e6fb3e123..6dc4b2cd1 100644 --- a/crates/ra_hir_ty/src/tests/coercion.rs +++ b/crates/ra_hir_ty/src/tests/coercion.rs | |||
@@ -384,7 +384,7 @@ fn foo() -> u32 { | |||
384 | } | 384 | } |
385 | "#, true), | 385 | "#, true), |
386 | @r###" | 386 | @r###" |
387 | 17..40 '{ ...own; }': ! | 387 | 17..40 '{ ...own; }': u32 |
388 | 23..37 'return unknown': ! | 388 | 23..37 'return unknown': ! |
389 | 30..37 'unknown': u32 | 389 | 30..37 'unknown': u32 |
390 | "### | 390 | "### |
@@ -514,7 +514,7 @@ fn foo() { | |||
514 | 27..103 '{ ... }': &u32 | 514 | 27..103 '{ ... }': &u32 |
515 | 37..82 'if tru... }': () | 515 | 37..82 'if tru... }': () |
516 | 40..44 'true': bool | 516 | 40..44 'true': bool |
517 | 45..82 '{ ... }': ! | 517 | 45..82 '{ ... }': () |
518 | 59..71 'return &1u32': ! | 518 | 59..71 'return &1u32': ! |
519 | 66..71 '&1u32': &u32 | 519 | 66..71 '&1u32': &u32 |
520 | 67..71 '1u32': u32 | 520 | 67..71 '1u32': u32 |
@@ -546,6 +546,48 @@ fn test() { | |||
546 | } | 546 | } |
547 | 547 | ||
548 | #[test] | 548 | #[test] |
549 | fn coerce_fn_items_in_match_arms() { | ||
550 | covers!(coerce_fn_reification); | ||
551 | assert_snapshot!( | ||
552 | infer_with_mismatches(r#" | ||
553 | fn foo1(x: u32) -> isize { 1 } | ||
554 | fn foo2(x: u32) -> isize { 2 } | ||
555 | fn foo3(x: u32) -> isize { 3 } | ||
556 | fn test() { | ||
557 | let x = match 1 { | ||
558 | 1 => foo1, | ||
559 | 2 => foo2, | ||
560 | _ => foo3, | ||
561 | }; | ||
562 | } | ||
563 | "#, true), | ||
564 | @r###" | ||
565 | 9..10 'x': u32 | ||
566 | 26..31 '{ 1 }': isize | ||
567 | 28..29 '1': isize | ||
568 | 40..41 'x': u32 | ||
569 | 57..62 '{ 2 }': isize | ||
570 | 59..60 '2': isize | ||
571 | 71..72 'x': u32 | ||
572 | 88..93 '{ 3 }': isize | ||
573 | 90..91 '3': isize | ||
574 | 104..193 '{ ... }; }': () | ||
575 | 114..115 'x': fn(u32) -> isize | ||
576 | 118..190 'match ... }': fn(u32) -> isize | ||
577 | 124..125 '1': i32 | ||
578 | 136..137 '1': i32 | ||
579 | 136..137 '1': i32 | ||
580 | 141..145 'foo1': fn foo1(u32) -> isize | ||
581 | 155..156 '2': i32 | ||
582 | 155..156 '2': i32 | ||
583 | 160..164 'foo2': fn foo2(u32) -> isize | ||
584 | 174..175 '_': i32 | ||
585 | 179..183 'foo3': fn foo3(u32) -> isize | ||
586 | "### | ||
587 | ); | ||
588 | } | ||
589 | |||
590 | #[test] | ||
549 | fn coerce_closure_to_fn_ptr() { | 591 | fn coerce_closure_to_fn_ptr() { |
550 | assert_snapshot!( | 592 | assert_snapshot!( |
551 | infer_with_mismatches(r#" | 593 | infer_with_mismatches(r#" |
diff --git a/crates/ra_hir_ty/src/tests/display_source_code.rs b/crates/ra_hir_ty/src/tests/display_source_code.rs new file mode 100644 index 000000000..4088b1d22 --- /dev/null +++ b/crates/ra_hir_ty/src/tests/display_source_code.rs | |||
@@ -0,0 +1,50 @@ | |||
1 | use super::displayed_source_at_pos; | ||
2 | use crate::test_db::TestDB; | ||
3 | use ra_db::fixture::WithFixture; | ||
4 | |||
5 | #[test] | ||
6 | fn qualify_path_to_submodule() { | ||
7 | let (db, pos) = TestDB::with_position( | ||
8 | r#" | ||
9 | //- /main.rs | ||
10 | |||
11 | mod foo { | ||
12 | pub struct Foo; | ||
13 | } | ||
14 | |||
15 | fn bar() { | ||
16 | let foo: foo::Foo = foo::Foo; | ||
17 | foo<|> | ||
18 | } | ||
19 | |||
20 | "#, | ||
21 | ); | ||
22 | assert_eq!("foo::Foo", displayed_source_at_pos(&db, pos)); | ||
23 | } | ||
24 | |||
25 | #[test] | ||
26 | fn omit_default_type_parameters() { | ||
27 | let (db, pos) = TestDB::with_position( | ||
28 | r" | ||
29 | //- /main.rs | ||
30 | struct Foo<T = u8> { t: T } | ||
31 | fn main() { | ||
32 | let foo = Foo { t: 5 }; | ||
33 | foo<|>; | ||
34 | } | ||
35 | ", | ||
36 | ); | ||
37 | assert_eq!("Foo", displayed_source_at_pos(&db, pos)); | ||
38 | |||
39 | let (db, pos) = TestDB::with_position( | ||
40 | r" | ||
41 | //- /main.rs | ||
42 | struct Foo<K, T = u8> { k: K, t: T } | ||
43 | fn main() { | ||
44 | let foo = Foo { k: 400, t: 5 }; | ||
45 | foo<|>; | ||
46 | } | ||
47 | ", | ||
48 | ); | ||
49 | assert_eq!("Foo<i32>", displayed_source_at_pos(&db, pos)); | ||
50 | } | ||
diff --git a/crates/ra_hir_ty/src/tests/macros.rs b/crates/ra_hir_ty/src/tests/macros.rs index 07398ddcc..4c6099aa2 100644 --- a/crates/ra_hir_ty/src/tests/macros.rs +++ b/crates/ra_hir_ty/src/tests/macros.rs | |||
@@ -197,7 +197,7 @@ fn spam() { | |||
197 | !0..6 '1isize': isize | 197 | !0..6 '1isize': isize |
198 | !0..6 '1isize': isize | 198 | !0..6 '1isize': isize |
199 | !0..6 '1isize': isize | 199 | !0..6 '1isize': isize |
200 | 54..457 '{ ...!(); }': ! | 200 | 54..457 '{ ...!(); }': () |
201 | 88..109 'spam!(...am!())': {unknown} | 201 | 88..109 'spam!(...am!())': {unknown} |
202 | 115..134 'for _ ...!() {}': () | 202 | 115..134 'for _ ...!() {}': () |
203 | 119..120 '_': {unknown} | 203 | 119..120 '_': {unknown} |
diff --git a/crates/ra_hir_ty/src/tests/method_resolution.rs b/crates/ra_hir_ty/src/tests/method_resolution.rs index ab87f598a..67f964ab5 100644 --- a/crates/ra_hir_ty/src/tests/method_resolution.rs +++ b/crates/ra_hir_ty/src/tests/method_resolution.rs | |||
@@ -17,8 +17,8 @@ impl<T> [T] { | |||
17 | #[lang = "slice_alloc"] | 17 | #[lang = "slice_alloc"] |
18 | impl<T> [T] {} | 18 | impl<T> [T] {} |
19 | 19 | ||
20 | fn test() { | 20 | fn test(x: &[u8]) { |
21 | <[_]>::foo(b"foo"); | 21 | <[_]>::foo(x); |
22 | } | 22 | } |
23 | "#), | 23 | "#), |
24 | @r###" | 24 | @r###" |
@@ -26,10 +26,11 @@ fn test() { | |||
26 | 56..79 '{ ... }': T | 26 | 56..79 '{ ... }': T |
27 | 66..73 'loop {}': ! | 27 | 66..73 'loop {}': ! |
28 | 71..73 '{}': () | 28 | 71..73 '{}': () |
29 | 133..160 '{ ...o"); }': () | 29 | 131..132 'x': &[u8] |
30 | 139..149 '<[_]>::foo': fn foo<u8>(&[u8]) -> u8 | 30 | 141..163 '{ ...(x); }': () |
31 | 139..157 '<[_]>:..."foo")': u8 | 31 | 147..157 '<[_]>::foo': fn foo<u8>(&[u8]) -> u8 |
32 | 150..156 'b"foo"': &[u8] | 32 | 147..160 '<[_]>::foo(x)': u8 |
33 | 158..159 'x': &[u8] | ||
33 | "### | 34 | "### |
34 | ); | 35 | ); |
35 | } | 36 | } |
diff --git a/crates/ra_hir_ty/src/tests/never_type.rs b/crates/ra_hir_ty/src/tests/never_type.rs index a77209480..082c47208 100644 --- a/crates/ra_hir_ty/src/tests/never_type.rs +++ b/crates/ra_hir_ty/src/tests/never_type.rs | |||
@@ -1,4 +1,6 @@ | |||
1 | use super::type_at; | 1 | use insta::assert_snapshot; |
2 | |||
3 | use super::{infer_with_mismatches, type_at}; | ||
2 | 4 | ||
3 | #[test] | 5 | #[test] |
4 | fn infer_never1() { | 6 | fn infer_never1() { |
@@ -261,3 +263,176 @@ fn test(a: i32) { | |||
261 | ); | 263 | ); |
262 | assert_eq!(t, "f64"); | 264 | assert_eq!(t, "f64"); |
263 | } | 265 | } |
266 | |||
267 | #[test] | ||
268 | fn diverging_expression_1() { | ||
269 | let t = infer_with_mismatches( | ||
270 | r#" | ||
271 | //- /main.rs | ||
272 | fn test1() { | ||
273 | let x: u32 = return; | ||
274 | } | ||
275 | fn test2() { | ||
276 | let x: u32 = { return; }; | ||
277 | } | ||
278 | fn test3() { | ||
279 | let x: u32 = loop {}; | ||
280 | } | ||
281 | fn test4() { | ||
282 | let x: u32 = { loop {} }; | ||
283 | } | ||
284 | fn test5() { | ||
285 | let x: u32 = { if true { loop {}; } else { loop {}; } }; | ||
286 | } | ||
287 | fn test6() { | ||
288 | let x: u32 = { let y: u32 = { loop {}; }; }; | ||
289 | } | ||
290 | "#, | ||
291 | true, | ||
292 | ); | ||
293 | assert_snapshot!(t, @r###" | ||
294 | 25..53 '{ ...urn; }': () | ||
295 | 35..36 'x': u32 | ||
296 | 44..50 'return': ! | ||
297 | 65..98 '{ ...; }; }': () | ||
298 | 75..76 'x': u32 | ||
299 | 84..95 '{ return; }': u32 | ||
300 | 86..92 'return': ! | ||
301 | 110..139 '{ ... {}; }': () | ||
302 | 120..121 'x': u32 | ||
303 | 129..136 'loop {}': ! | ||
304 | 134..136 '{}': () | ||
305 | 151..184 '{ ...} }; }': () | ||
306 | 161..162 'x': u32 | ||
307 | 170..181 '{ loop {} }': u32 | ||
308 | 172..179 'loop {}': ! | ||
309 | 177..179 '{}': () | ||
310 | 196..260 '{ ...} }; }': () | ||
311 | 206..207 'x': u32 | ||
312 | 215..257 '{ if t...}; } }': u32 | ||
313 | 217..255 'if tru... {}; }': u32 | ||
314 | 220..224 'true': bool | ||
315 | 225..237 '{ loop {}; }': u32 | ||
316 | 227..234 'loop {}': ! | ||
317 | 232..234 '{}': () | ||
318 | 243..255 '{ loop {}; }': u32 | ||
319 | 245..252 'loop {}': ! | ||
320 | 250..252 '{}': () | ||
321 | 272..324 '{ ...; }; }': () | ||
322 | 282..283 'x': u32 | ||
323 | 291..321 '{ let ...; }; }': u32 | ||
324 | 297..298 'y': u32 | ||
325 | 306..318 '{ loop {}; }': u32 | ||
326 | 308..315 'loop {}': ! | ||
327 | 313..315 '{}': () | ||
328 | "###); | ||
329 | } | ||
330 | |||
331 | #[test] | ||
332 | fn diverging_expression_2() { | ||
333 | let t = infer_with_mismatches( | ||
334 | r#" | ||
335 | //- /main.rs | ||
336 | fn test1() { | ||
337 | // should give type mismatch | ||
338 | let x: u32 = { loop {}; "foo" }; | ||
339 | } | ||
340 | "#, | ||
341 | true, | ||
342 | ); | ||
343 | assert_snapshot!(t, @r###" | ||
344 | 25..98 '{ ..." }; }': () | ||
345 | 68..69 'x': u32 | ||
346 | 77..95 '{ loop...foo" }': &str | ||
347 | 79..86 'loop {}': ! | ||
348 | 84..86 '{}': () | ||
349 | 88..93 '"foo"': &str | ||
350 | 77..95: expected u32, got &str | ||
351 | 88..93: expected u32, got &str | ||
352 | "###); | ||
353 | } | ||
354 | |||
355 | #[test] | ||
356 | fn diverging_expression_3_break() { | ||
357 | let t = infer_with_mismatches( | ||
358 | r#" | ||
359 | //- /main.rs | ||
360 | fn test1() { | ||
361 | // should give type mismatch | ||
362 | let x: u32 = { loop { break; } }; | ||
363 | } | ||
364 | fn test2() { | ||
365 | // should give type mismatch | ||
366 | let x: u32 = { for a in b { break; }; }; | ||
367 | // should give type mismatch as well | ||
368 | let x: u32 = { for a in b {}; }; | ||
369 | // should give type mismatch as well | ||
370 | let x: u32 = { for a in b { return; }; }; | ||
371 | } | ||
372 | fn test3() { | ||
373 | // should give type mismatch | ||
374 | let x: u32 = { while true { break; }; }; | ||
375 | // should give type mismatch as well -- there's an implicit break, even if it's never hit | ||
376 | let x: u32 = { while true {}; }; | ||
377 | // should give type mismatch as well | ||
378 | let x: u32 = { while true { return; }; }; | ||
379 | } | ||
380 | "#, | ||
381 | true, | ||
382 | ); | ||
383 | assert_snapshot!(t, @r###" | ||
384 | 25..99 '{ ...} }; }': () | ||
385 | 68..69 'x': u32 | ||
386 | 77..96 '{ loop...k; } }': () | ||
387 | 79..94 'loop { break; }': () | ||
388 | 84..94 '{ break; }': () | ||
389 | 86..91 'break': ! | ||
390 | 77..96: expected u32, got () | ||
391 | 79..94: expected u32, got () | ||
392 | 111..357 '{ ...; }; }': () | ||
393 | 154..155 'x': u32 | ||
394 | 163..189 '{ for ...; }; }': () | ||
395 | 165..186 'for a ...eak; }': () | ||
396 | 169..170 'a': {unknown} | ||
397 | 174..175 'b': {unknown} | ||
398 | 176..186 '{ break; }': () | ||
399 | 178..183 'break': ! | ||
400 | 240..241 'x': u32 | ||
401 | 249..267 '{ for ... {}; }': () | ||
402 | 251..264 'for a in b {}': () | ||
403 | 255..256 'a': {unknown} | ||
404 | 260..261 'b': {unknown} | ||
405 | 262..264 '{}': () | ||
406 | 318..319 'x': u32 | ||
407 | 327..354 '{ for ...; }; }': () | ||
408 | 329..351 'for a ...urn; }': () | ||
409 | 333..334 'a': {unknown} | ||
410 | 338..339 'b': {unknown} | ||
411 | 340..351 '{ return; }': () | ||
412 | 342..348 'return': ! | ||
413 | 163..189: expected u32, got () | ||
414 | 249..267: expected u32, got () | ||
415 | 327..354: expected u32, got () | ||
416 | 369..668 '{ ...; }; }': () | ||
417 | 412..413 'x': u32 | ||
418 | 421..447 '{ whil...; }; }': () | ||
419 | 423..444 'while ...eak; }': () | ||
420 | 429..433 'true': bool | ||
421 | 434..444 '{ break; }': () | ||
422 | 436..441 'break': ! | ||
423 | 551..552 'x': u32 | ||
424 | 560..578 '{ whil... {}; }': () | ||
425 | 562..575 'while true {}': () | ||
426 | 568..572 'true': bool | ||
427 | 573..575 '{}': () | ||
428 | 629..630 'x': u32 | ||
429 | 638..665 '{ whil...; }; }': () | ||
430 | 640..662 'while ...urn; }': () | ||
431 | 646..650 'true': bool | ||
432 | 651..662 '{ return; }': () | ||
433 | 653..659 'return': ! | ||
434 | 421..447: expected u32, got () | ||
435 | 560..578: expected u32, got () | ||
436 | 638..665: expected u32, got () | ||
437 | "###); | ||
438 | } | ||
diff --git a/crates/ra_hir_ty/src/tests/simple.rs b/crates/ra_hir_ty/src/tests/simple.rs index 3d3088965..322838f02 100644 --- a/crates/ra_hir_ty/src/tests/simple.rs +++ b/crates/ra_hir_ty/src/tests/simple.rs | |||
@@ -179,7 +179,7 @@ fn test(a: u32, b: isize, c: !, d: &str) { | |||
179 | 17..18 'b': isize | 179 | 17..18 'b': isize |
180 | 27..28 'c': ! | 180 | 27..28 'c': ! |
181 | 33..34 'd': &str | 181 | 33..34 'd': &str |
182 | 42..121 '{ ...f32; }': ! | 182 | 42..121 '{ ...f32; }': () |
183 | 48..49 'a': u32 | 183 | 48..49 'a': u32 |
184 | 55..56 'b': isize | 184 | 55..56 'b': isize |
185 | 62..63 'c': ! | 185 | 62..63 'c': ! |
@@ -414,7 +414,7 @@ fn test() { | |||
414 | 27..31 '5f32': f32 | 414 | 27..31 '5f32': f32 |
415 | 37..41 '5f64': f64 | 415 | 37..41 '5f64': f64 |
416 | 47..54 '"hello"': &str | 416 | 47..54 '"hello"': &str |
417 | 60..68 'b"bytes"': &[u8] | 417 | 60..68 'b"bytes"': &[u8; _] |
418 | 74..77 ''c'': char | 418 | 74..77 ''c'': char |
419 | 83..87 'b'b'': u8 | 419 | 83..87 'b'b'': u8 |
420 | 93..97 '3.14': f64 | 420 | 93..97 '3.14': f64 |
@@ -422,7 +422,7 @@ fn test() { | |||
422 | 113..118 'false': bool | 422 | 113..118 'false': bool |
423 | 124..128 'true': bool | 423 | 124..128 'true': bool |
424 | 134..202 'r#" ... "#': &str | 424 | 134..202 'r#" ... "#': &str |
425 | 208..218 'br#"yolo"#': &[u8] | 425 | 208..218 'br#"yolo"#': &[u8; _] |
426 | "### | 426 | "### |
427 | ); | 427 | ); |
428 | } | 428 | } |
@@ -935,7 +935,7 @@ fn foo() { | |||
935 | 29..33 'true': bool | 935 | 29..33 'true': bool |
936 | 34..51 '{ ... }': i32 | 936 | 34..51 '{ ... }': i32 |
937 | 44..45 '1': i32 | 937 | 44..45 '1': i32 |
938 | 57..80 '{ ... }': ! | 938 | 57..80 '{ ... }': i32 |
939 | 67..73 'return': ! | 939 | 67..73 'return': ! |
940 | 90..93 '_x2': i32 | 940 | 90..93 '_x2': i32 |
941 | 96..149 'if tru... }': i32 | 941 | 96..149 'if tru... }': i32 |
@@ -951,7 +951,7 @@ fn foo() { | |||
951 | 186..190 'true': bool | 951 | 186..190 'true': bool |
952 | 194..195 '3': i32 | 952 | 194..195 '3': i32 |
953 | 205..206 '_': bool | 953 | 205..206 '_': bool |
954 | 210..241 '{ ... }': ! | 954 | 210..241 '{ ... }': i32 |
955 | 224..230 'return': ! | 955 | 224..230 'return': ! |
956 | 257..260 '_x4': i32 | 956 | 257..260 '_x4': i32 |
957 | 263..320 'match ... }': i32 | 957 | 263..320 'match ... }': i32 |
@@ -1687,7 +1687,7 @@ fn foo() -> u32 { | |||
1687 | 17..59 '{ ...; }; }': () | 1687 | 17..59 '{ ...; }; }': () |
1688 | 27..28 'x': || -> usize | 1688 | 27..28 'x': || -> usize |
1689 | 31..56 '|| -> ...n 1; }': || -> usize | 1689 | 31..56 '|| -> ...n 1; }': || -> usize |
1690 | 43..56 '{ return 1; }': ! | 1690 | 43..56 '{ return 1; }': usize |
1691 | 45..53 'return 1': ! | 1691 | 45..53 'return 1': ! |
1692 | 52..53 '1': usize | 1692 | 52..53 '1': usize |
1693 | "### | 1693 | "### |
@@ -1706,7 +1706,7 @@ fn foo() -> u32 { | |||
1706 | 17..48 '{ ...; }; }': () | 1706 | 17..48 '{ ...; }; }': () |
1707 | 27..28 'x': || -> () | 1707 | 27..28 'x': || -> () |
1708 | 31..45 '|| { return; }': || -> () | 1708 | 31..45 '|| { return; }': || -> () |
1709 | 34..45 '{ return; }': ! | 1709 | 34..45 '{ return; }': () |
1710 | 36..42 'return': ! | 1710 | 36..42 'return': ! |
1711 | "### | 1711 | "### |
1712 | ); | 1712 | ); |
@@ -1787,3 +1787,32 @@ fn main() { | |||
1787 | "### | 1787 | "### |
1788 | ) | 1788 | ) |
1789 | } | 1789 | } |
1790 | |||
1791 | #[test] | ||
1792 | fn infer_generic_from_later_assignment() { | ||
1793 | assert_snapshot!( | ||
1794 | infer(r#" | ||
1795 | enum Option<T> { Some(T), None } | ||
1796 | use Option::*; | ||
1797 | |||
1798 | fn test() { | ||
1799 | let mut end = None; | ||
1800 | loop { | ||
1801 | end = Some(true); | ||
1802 | } | ||
1803 | } | ||
1804 | "#), | ||
1805 | @r###" | ||
1806 | 60..130 '{ ... } }': () | ||
1807 | 70..77 'mut end': Option<bool> | ||
1808 | 80..84 'None': Option<bool> | ||
1809 | 90..128 'loop {... }': ! | ||
1810 | 95..128 '{ ... }': () | ||
1811 | 105..108 'end': Option<bool> | ||
1812 | 105..121 'end = ...(true)': () | ||
1813 | 111..115 'Some': Some<bool>(bool) -> Option<bool> | ||
1814 | 111..121 'Some(true)': Option<bool> | ||
1815 | 116..120 'true': bool | ||
1816 | "### | ||
1817 | ); | ||
1818 | } | ||
diff --git a/crates/ra_ide/src/display/function_signature.rs b/crates/ra_ide/src/display/function_signature.rs index f16d42276..9572debd8 100644 --- a/crates/ra_ide/src/display/function_signature.rs +++ b/crates/ra_ide/src/display/function_signature.rs | |||
@@ -84,8 +84,8 @@ impl FunctionSignature { | |||
84 | let ty = field.signature_ty(db); | 84 | let ty = field.signature_ty(db); |
85 | let raw_param = format!("{}", ty.display(db)); | 85 | let raw_param = format!("{}", ty.display(db)); |
86 | 86 | ||
87 | if let Some(param_type) = raw_param.split(':').nth(1) { | 87 | if let Some(param_type) = raw_param.split(':').nth(1).and_then(|it| it.get(1..)) { |
88 | parameter_types.push(param_type[1..].to_string()); | 88 | parameter_types.push(param_type.to_string()); |
89 | } else { | 89 | } else { |
90 | // useful when you have tuple struct | 90 | // useful when you have tuple struct |
91 | parameter_types.push(raw_param.clone()); | 91 | parameter_types.push(raw_param.clone()); |
@@ -129,8 +129,8 @@ impl FunctionSignature { | |||
129 | for field in variant.fields(db).into_iter() { | 129 | for field in variant.fields(db).into_iter() { |
130 | let ty = field.signature_ty(db); | 130 | let ty = field.signature_ty(db); |
131 | let raw_param = format!("{}", ty.display(db)); | 131 | let raw_param = format!("{}", ty.display(db)); |
132 | if let Some(param_type) = raw_param.split(':').nth(1) { | 132 | if let Some(param_type) = raw_param.split(':').nth(1).and_then(|it| it.get(1..)) { |
133 | parameter_types.push(param_type[1..].to_string()); | 133 | parameter_types.push(param_type.to_string()); |
134 | } else { | 134 | } else { |
135 | // The unwrap_or_else is useful when you have tuple | 135 | // The unwrap_or_else is useful when you have tuple |
136 | parameter_types.push(raw_param); | 136 | parameter_types.push(raw_param); |
@@ -197,7 +197,12 @@ impl From<&'_ ast::FnDef> for FunctionSignature { | |||
197 | let raw_param = self_param.syntax().text().to_string(); | 197 | let raw_param = self_param.syntax().text().to_string(); |
198 | 198 | ||
199 | res_types.push( | 199 | res_types.push( |
200 | raw_param.split(':').nth(1).unwrap_or_else(|| " Self")[1..].to_string(), | 200 | raw_param |
201 | .split(':') | ||
202 | .nth(1) | ||
203 | .and_then(|it| it.get(1..)) | ||
204 | .unwrap_or_else(|| "Self") | ||
205 | .to_string(), | ||
201 | ); | 206 | ); |
202 | res.push(raw_param); | 207 | res.push(raw_param); |
203 | } | 208 | } |
@@ -205,8 +210,8 @@ impl From<&'_ ast::FnDef> for FunctionSignature { | |||
205 | res.extend(param_list.params().map(|param| param.syntax().text().to_string())); | 210 | res.extend(param_list.params().map(|param| param.syntax().text().to_string())); |
206 | res_types.extend(param_list.params().map(|param| { | 211 | res_types.extend(param_list.params().map(|param| { |
207 | let param_text = param.syntax().text().to_string(); | 212 | let param_text = param.syntax().text().to_string(); |
208 | match param_text.split(':').nth(1) { | 213 | match param_text.split(':').nth(1).and_then(|it| it.get(1..)) { |
209 | Some(it) => it[1..].to_string(), | 214 | Some(it) => it.to_string(), |
210 | None => param_text, | 215 | None => param_text, |
211 | } | 216 | } |
212 | })); | 217 | })); |
diff --git a/crates/ra_ide/src/display/navigation_target.rs b/crates/ra_ide/src/display/navigation_target.rs index de35c6711..5da28edd2 100644 --- a/crates/ra_ide/src/display/navigation_target.rs +++ b/crates/ra_ide/src/display/navigation_target.rs | |||
@@ -11,7 +11,7 @@ use ra_syntax::{ | |||
11 | TextRange, | 11 | TextRange, |
12 | }; | 12 | }; |
13 | 13 | ||
14 | use crate::FileSymbol; | 14 | use crate::{FileRange, FileSymbol}; |
15 | 15 | ||
16 | use super::short_label::ShortLabel; | 16 | use super::short_label::ShortLabel; |
17 | 17 | ||
@@ -22,10 +22,11 @@ use super::short_label::ShortLabel; | |||
22 | /// code, like a function or a struct, but this is not strictly required. | 22 | /// code, like a function or a struct, but this is not strictly required. |
23 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] | 23 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] |
24 | pub struct NavigationTarget { | 24 | pub struct NavigationTarget { |
25 | // FIXME: use FileRange? | ||
25 | file_id: FileId, | 26 | file_id: FileId, |
27 | full_range: TextRange, | ||
26 | name: SmolStr, | 28 | name: SmolStr, |
27 | kind: SyntaxKind, | 29 | kind: SyntaxKind, |
28 | full_range: TextRange, | ||
29 | focus_range: Option<TextRange>, | 30 | focus_range: Option<TextRange>, |
30 | container_name: Option<SmolStr>, | 31 | container_name: Option<SmolStr>, |
31 | description: Option<String>, | 32 | description: Option<String>, |
@@ -63,6 +64,10 @@ impl NavigationTarget { | |||
63 | self.file_id | 64 | self.file_id |
64 | } | 65 | } |
65 | 66 | ||
67 | pub fn file_range(&self) -> FileRange { | ||
68 | FileRange { file_id: self.file_id, range: self.full_range } | ||
69 | } | ||
70 | |||
66 | pub fn full_range(&self) -> TextRange { | 71 | pub fn full_range(&self) -> TextRange { |
67 | self.full_range | 72 | self.full_range |
68 | } | 73 | } |
diff --git a/crates/ra_project_model/Cargo.toml b/crates/ra_project_model/Cargo.toml index 946deed5d..e4a60f4c0 100644 --- a/crates/ra_project_model/Cargo.toml +++ b/crates/ra_project_model/Cargo.toml | |||
@@ -16,7 +16,7 @@ cargo_metadata = "0.10.0" | |||
16 | ra_arena = { path = "../ra_arena" } | 16 | ra_arena = { path = "../ra_arena" } |
17 | ra_cfg = { path = "../ra_cfg" } | 17 | ra_cfg = { path = "../ra_cfg" } |
18 | ra_db = { path = "../ra_db" } | 18 | ra_db = { path = "../ra_db" } |
19 | ra_env = { path = "../ra_env" } | 19 | ra_toolchain = { path = "../ra_toolchain" } |
20 | ra_proc_macro = { path = "../ra_proc_macro" } | 20 | ra_proc_macro = { path = "../ra_proc_macro" } |
21 | 21 | ||
22 | serde = { version = "1.0.106", features = ["derive"] } | 22 | serde = { version = "1.0.106", features = ["derive"] } |
diff --git a/crates/ra_project_model/src/cargo_workspace.rs b/crates/ra_project_model/src/cargo_workspace.rs index 9152a6d9d..a306ce95f 100644 --- a/crates/ra_project_model/src/cargo_workspace.rs +++ b/crates/ra_project_model/src/cargo_workspace.rs | |||
@@ -11,7 +11,6 @@ use anyhow::{Context, Result}; | |||
11 | use cargo_metadata::{BuildScript, CargoOpt, Message, MetadataCommand, PackageId}; | 11 | use cargo_metadata::{BuildScript, CargoOpt, Message, MetadataCommand, PackageId}; |
12 | use ra_arena::{Arena, Idx}; | 12 | use ra_arena::{Arena, Idx}; |
13 | use ra_db::Edition; | 13 | use ra_db::Edition; |
14 | use ra_env::get_path_for_executable; | ||
15 | use rustc_hash::FxHashMap; | 14 | use rustc_hash::FxHashMap; |
16 | 15 | ||
17 | /// `CargoWorkspace` represents the logical structure of, well, a Cargo | 16 | /// `CargoWorkspace` represents the logical structure of, well, a Cargo |
@@ -147,7 +146,7 @@ impl CargoWorkspace { | |||
147 | cargo_features: &CargoConfig, | 146 | cargo_features: &CargoConfig, |
148 | ) -> Result<CargoWorkspace> { | 147 | ) -> Result<CargoWorkspace> { |
149 | let mut meta = MetadataCommand::new(); | 148 | let mut meta = MetadataCommand::new(); |
150 | meta.cargo_path(get_path_for_executable("cargo")?); | 149 | meta.cargo_path(ra_toolchain::cargo()); |
151 | meta.manifest_path(cargo_toml); | 150 | meta.manifest_path(cargo_toml); |
152 | if cargo_features.all_features { | 151 | if cargo_features.all_features { |
153 | meta.features(CargoOpt::AllFeatures); | 152 | meta.features(CargoOpt::AllFeatures); |
@@ -289,7 +288,7 @@ pub fn load_extern_resources( | |||
289 | cargo_toml: &Path, | 288 | cargo_toml: &Path, |
290 | cargo_features: &CargoConfig, | 289 | cargo_features: &CargoConfig, |
291 | ) -> Result<ExternResources> { | 290 | ) -> Result<ExternResources> { |
292 | let mut cmd = Command::new(get_path_for_executable("cargo")?); | 291 | let mut cmd = Command::new(ra_toolchain::cargo()); |
293 | cmd.args(&["check", "--message-format=json", "--manifest-path"]).arg(cargo_toml); | 292 | cmd.args(&["check", "--message-format=json", "--manifest-path"]).arg(cargo_toml); |
294 | if cargo_features.all_features { | 293 | if cargo_features.all_features { |
295 | cmd.arg("--all-features"); | 294 | cmd.arg("--all-features"); |
diff --git a/crates/ra_project_model/src/lib.rs b/crates/ra_project_model/src/lib.rs index 88a6ffb2a..4f098b706 100644 --- a/crates/ra_project_model/src/lib.rs +++ b/crates/ra_project_model/src/lib.rs | |||
@@ -8,13 +8,12 @@ use std::{ | |||
8 | fs::{read_dir, File, ReadDir}, | 8 | fs::{read_dir, File, ReadDir}, |
9 | io::{self, BufReader}, | 9 | io::{self, BufReader}, |
10 | path::{Path, PathBuf}, | 10 | path::{Path, PathBuf}, |
11 | process::Command, | 11 | process::{Command, Output}, |
12 | }; | 12 | }; |
13 | 13 | ||
14 | use anyhow::{bail, Context, Result}; | 14 | use anyhow::{bail, Context, Result}; |
15 | use ra_cfg::CfgOptions; | 15 | use ra_cfg::CfgOptions; |
16 | use ra_db::{CrateGraph, CrateName, Edition, Env, ExternSource, ExternSourceId, FileId}; | 16 | use ra_db::{CrateGraph, CrateName, Edition, Env, ExternSource, ExternSourceId, FileId}; |
17 | use ra_env::get_path_for_executable; | ||
18 | use rustc_hash::FxHashMap; | 17 | use rustc_hash::FxHashMap; |
19 | use serde_json::from_reader; | 18 | use serde_json::from_reader; |
20 | 19 | ||
@@ -89,46 +88,28 @@ impl ProjectRoot { | |||
89 | } | 88 | } |
90 | 89 | ||
91 | pub fn discover(path: &Path) -> io::Result<Vec<ProjectRoot>> { | 90 | pub fn discover(path: &Path) -> io::Result<Vec<ProjectRoot>> { |
92 | if let Some(project_json) = find_rust_project_json(path) { | 91 | if let Some(project_json) = find_in_parent_dirs(path, "rust-project.json") { |
93 | return Ok(vec![ProjectRoot::ProjectJson(project_json)]); | 92 | return Ok(vec![ProjectRoot::ProjectJson(project_json)]); |
94 | } | 93 | } |
95 | return find_cargo_toml(path) | 94 | return find_cargo_toml(path) |
96 | .map(|paths| paths.into_iter().map(ProjectRoot::CargoToml).collect()); | 95 | .map(|paths| paths.into_iter().map(ProjectRoot::CargoToml).collect()); |
97 | 96 | ||
98 | fn find_rust_project_json(path: &Path) -> Option<PathBuf> { | ||
99 | if path.ends_with("rust-project.json") { | ||
100 | return Some(path.to_path_buf()); | ||
101 | } | ||
102 | |||
103 | let mut curr = Some(path); | ||
104 | while let Some(path) = curr { | ||
105 | let candidate = path.join("rust-project.json"); | ||
106 | if candidate.exists() { | ||
107 | return Some(candidate); | ||
108 | } | ||
109 | curr = path.parent(); | ||
110 | } | ||
111 | |||
112 | None | ||
113 | } | ||
114 | |||
115 | fn find_cargo_toml(path: &Path) -> io::Result<Vec<PathBuf>> { | 97 | fn find_cargo_toml(path: &Path) -> io::Result<Vec<PathBuf>> { |
116 | if path.ends_with("Cargo.toml") { | 98 | match find_in_parent_dirs(path, "Cargo.toml") { |
117 | return Ok(vec![path.to_path_buf()]); | 99 | Some(it) => Ok(vec![it]), |
100 | None => Ok(find_cargo_toml_in_child_dir(read_dir(path)?)), | ||
118 | } | 101 | } |
102 | } | ||
119 | 103 | ||
120 | if let Some(p) = find_cargo_toml_in_parent_dir(path) { | 104 | fn find_in_parent_dirs(path: &Path, target_file_name: &str) -> Option<PathBuf> { |
121 | return Ok(vec![p]); | 105 | if path.ends_with(target_file_name) { |
106 | return Some(path.to_owned()); | ||
122 | } | 107 | } |
123 | 108 | ||
124 | let entities = read_dir(path)?; | ||
125 | Ok(find_cargo_toml_in_child_dir(entities)) | ||
126 | } | ||
127 | |||
128 | fn find_cargo_toml_in_parent_dir(path: &Path) -> Option<PathBuf> { | ||
129 | let mut curr = Some(path); | 109 | let mut curr = Some(path); |
110 | |||
130 | while let Some(path) = curr { | 111 | while let Some(path) = curr { |
131 | let candidate = path.join("Cargo.toml"); | 112 | let candidate = path.join(target_file_name); |
132 | if candidate.exists() { | 113 | if candidate.exists() { |
133 | return Some(candidate); | 114 | return Some(candidate); |
134 | } | 115 | } |
@@ -140,14 +121,11 @@ impl ProjectRoot { | |||
140 | 121 | ||
141 | fn find_cargo_toml_in_child_dir(entities: ReadDir) -> Vec<PathBuf> { | 122 | fn find_cargo_toml_in_child_dir(entities: ReadDir) -> Vec<PathBuf> { |
142 | // Only one level down to avoid cycles the easy way and stop a runaway scan with large projects | 123 | // Only one level down to avoid cycles the easy way and stop a runaway scan with large projects |
143 | let mut valid_canditates = vec![]; | 124 | entities |
144 | for entity in entities.filter_map(Result::ok) { | 125 | .filter_map(Result::ok) |
145 | let candidate = entity.path().join("Cargo.toml"); | 126 | .map(|it| it.path().join("Cargo.toml")) |
146 | if candidate.exists() { | 127 | .filter(|it| it.exists()) |
147 | valid_canditates.push(candidate) | 128 | .collect() |
148 | } | ||
149 | } | ||
150 | valid_canditates | ||
151 | } | 129 | } |
152 | } | 130 | } |
153 | } | 131 | } |
@@ -568,25 +546,18 @@ pub fn get_rustc_cfg_options(target: Option<&String>) -> CfgOptions { | |||
568 | } | 546 | } |
569 | } | 547 | } |
570 | 548 | ||
571 | match (|| -> Result<String> { | 549 | let rustc_cfgs = || -> Result<String> { |
572 | // `cfg(test)` and `cfg(debug_assertion)` are handled outside, so we suppress them here. | 550 | // `cfg(test)` and `cfg(debug_assertion)` are handled outside, so we suppress them here. |
573 | let mut cmd = Command::new(get_path_for_executable("rustc")?); | 551 | let mut cmd = Command::new(ra_toolchain::rustc()); |
574 | cmd.args(&["--print", "cfg", "-O"]); | 552 | cmd.args(&["--print", "cfg", "-O"]); |
575 | if let Some(target) = target { | 553 | if let Some(target) = target { |
576 | cmd.args(&["--target", target.as_str()]); | 554 | cmd.args(&["--target", target.as_str()]); |
577 | } | 555 | } |
578 | let output = cmd.output().context("Failed to get output from rustc --print cfg -O")?; | 556 | let output = output(cmd)?; |
579 | if !output.status.success() { | ||
580 | bail!( | ||
581 | "rustc --print cfg -O exited with exit code ({})", | ||
582 | output | ||
583 | .status | ||
584 | .code() | ||
585 | .map_or(String::from("no exit code"), |code| format!("{}", code)) | ||
586 | ); | ||
587 | } | ||
588 | Ok(String::from_utf8(output.stdout)?) | 557 | Ok(String::from_utf8(output.stdout)?) |
589 | })() { | 558 | }(); |
559 | |||
560 | match rustc_cfgs { | ||
590 | Ok(rustc_cfgs) => { | 561 | Ok(rustc_cfgs) => { |
591 | for line in rustc_cfgs.lines() { | 562 | for line in rustc_cfgs.lines() { |
592 | match line.find('=') { | 563 | match line.find('=') { |
@@ -599,8 +570,21 @@ pub fn get_rustc_cfg_options(target: Option<&String>) -> CfgOptions { | |||
599 | } | 570 | } |
600 | } | 571 | } |
601 | } | 572 | } |
602 | Err(e) => log::error!("failed to get rustc cfgs: {}", e), | 573 | Err(e) => log::error!("failed to get rustc cfgs: {:#}", e), |
603 | } | 574 | } |
604 | 575 | ||
605 | cfg_options | 576 | cfg_options |
606 | } | 577 | } |
578 | |||
579 | fn output(mut cmd: Command) -> Result<Output> { | ||
580 | let output = cmd.output().with_context(|| format!("{:?} failed", cmd))?; | ||
581 | if !output.status.success() { | ||
582 | match String::from_utf8(output.stderr) { | ||
583 | Ok(stderr) if !stderr.is_empty() => { | ||
584 | bail!("{:?} failed, {}\nstderr:\n{}", cmd, output.status, stderr) | ||
585 | } | ||
586 | _ => bail!("{:?} failed, {}", cmd, output.status), | ||
587 | } | ||
588 | } | ||
589 | Ok(output) | ||
590 | } | ||
diff --git a/crates/ra_project_model/src/sysroot.rs b/crates/ra_project_model/src/sysroot.rs index 11c26ad89..a8a196e64 100644 --- a/crates/ra_project_model/src/sysroot.rs +++ b/crates/ra_project_model/src/sysroot.rs | |||
@@ -1,14 +1,15 @@ | |||
1 | //! FIXME: write short doc here | 1 | //! FIXME: write short doc here |
2 | 2 | ||
3 | use anyhow::{bail, Context, Result}; | ||
4 | use std::{ | 3 | use std::{ |
5 | env, ops, | 4 | env, ops, |
6 | path::{Path, PathBuf}, | 5 | path::{Path, PathBuf}, |
7 | process::{Command, Output}, | 6 | process::Command, |
8 | }; | 7 | }; |
9 | 8 | ||
9 | use anyhow::{bail, Result}; | ||
10 | use ra_arena::{Arena, Idx}; | 10 | use ra_arena::{Arena, Idx}; |
11 | use ra_env::get_path_for_executable; | 11 | |
12 | use crate::output; | ||
12 | 13 | ||
13 | #[derive(Default, Debug, Clone)] | 14 | #[derive(Default, Debug, Clone)] |
14 | pub struct Sysroot { | 15 | pub struct Sysroot { |
@@ -85,50 +86,22 @@ impl Sysroot { | |||
85 | } | 86 | } |
86 | } | 87 | } |
87 | 88 | ||
88 | fn create_command_text(program: &str, args: &[&str]) -> String { | ||
89 | format!("{} {}", program, args.join(" ")) | ||
90 | } | ||
91 | |||
92 | fn run_command_in_cargo_dir( | ||
93 | cargo_toml: impl AsRef<Path>, | ||
94 | program: impl AsRef<Path>, | ||
95 | args: &[&str], | ||
96 | ) -> Result<Output> { | ||
97 | let program = program.as_ref().as_os_str().to_str().expect("Invalid Unicode in path"); | ||
98 | let output = Command::new(program) | ||
99 | .current_dir(cargo_toml.as_ref().parent().unwrap()) | ||
100 | .args(args) | ||
101 | .output() | ||
102 | .context(format!("{} failed", create_command_text(program, args)))?; | ||
103 | if !output.status.success() { | ||
104 | match output.status.code() { | ||
105 | Some(code) => bail!( | ||
106 | "failed to run the command: '{}' exited with code {}", | ||
107 | create_command_text(program, args), | ||
108 | code | ||
109 | ), | ||
110 | None => bail!( | ||
111 | "failed to run the command: '{}' terminated by signal", | ||
112 | create_command_text(program, args) | ||
113 | ), | ||
114 | }; | ||
115 | } | ||
116 | Ok(output) | ||
117 | } | ||
118 | |||
119 | fn get_or_install_rust_src(cargo_toml: &Path) -> Result<PathBuf> { | 89 | fn get_or_install_rust_src(cargo_toml: &Path) -> Result<PathBuf> { |
120 | if let Ok(path) = env::var("RUST_SRC_PATH") { | 90 | if let Ok(path) = env::var("RUST_SRC_PATH") { |
121 | return Ok(path.into()); | 91 | return Ok(path.into()); |
122 | } | 92 | } |
123 | let rustc = get_path_for_executable("rustc")?; | 93 | let current_dir = cargo_toml.parent().unwrap(); |
124 | let rustc_output = run_command_in_cargo_dir(cargo_toml, &rustc, &["--print", "sysroot"])?; | 94 | let mut rustc = Command::new(ra_toolchain::rustc()); |
95 | rustc.current_dir(current_dir).args(&["--print", "sysroot"]); | ||
96 | let rustc_output = output(rustc)?; | ||
125 | let stdout = String::from_utf8(rustc_output.stdout)?; | 97 | let stdout = String::from_utf8(rustc_output.stdout)?; |
126 | let sysroot_path = Path::new(stdout.trim()); | 98 | let sysroot_path = Path::new(stdout.trim()); |
127 | let src_path = sysroot_path.join("lib/rustlib/src/rust/src"); | 99 | let src_path = sysroot_path.join("lib/rustlib/src/rust/src"); |
128 | 100 | ||
129 | if !src_path.exists() { | 101 | if !src_path.exists() { |
130 | let rustup = get_path_for_executable("rustup")?; | 102 | let mut rustup = Command::new(ra_toolchain::rustup()); |
131 | run_command_in_cargo_dir(cargo_toml, &rustup, &["component", "add", "rust-src"])?; | 103 | rustup.current_dir(current_dir).args(&["component", "add", "rust-src"]); |
104 | let _output = output(rustup)?; | ||
132 | } | 105 | } |
133 | if !src_path.exists() { | 106 | if !src_path.exists() { |
134 | bail!( | 107 | bail!( |
diff --git a/crates/ra_syntax/src/ast/edit.rs b/crates/ra_syntax/src/ast/edit.rs index 3e6dd6061..24a1e1d91 100644 --- a/crates/ra_syntax/src/ast/edit.rs +++ b/crates/ra_syntax/src/ast/edit.rs | |||
@@ -453,11 +453,7 @@ impl IndentLevel { | |||
453 | IndentLevel(0) | 453 | IndentLevel(0) |
454 | } | 454 | } |
455 | 455 | ||
456 | pub fn increase_indent<N: AstNode>(self, node: N) -> N { | 456 | fn increase_indent(self, node: SyntaxNode) -> SyntaxNode { |
457 | N::cast(self._increase_indent(node.syntax().clone())).unwrap() | ||
458 | } | ||
459 | |||
460 | fn _increase_indent(self, node: SyntaxNode) -> SyntaxNode { | ||
461 | let mut rewriter = SyntaxRewriter::default(); | 457 | let mut rewriter = SyntaxRewriter::default(); |
462 | node.descendants_with_tokens() | 458 | node.descendants_with_tokens() |
463 | .filter_map(|el| el.into_token()) | 459 | .filter_map(|el| el.into_token()) |
@@ -478,11 +474,7 @@ impl IndentLevel { | |||
478 | rewriter.rewrite(&node) | 474 | rewriter.rewrite(&node) |
479 | } | 475 | } |
480 | 476 | ||
481 | pub fn decrease_indent<N: AstNode>(self, node: N) -> N { | 477 | fn decrease_indent(self, node: SyntaxNode) -> SyntaxNode { |
482 | N::cast(self._decrease_indent(node.syntax().clone())).unwrap() | ||
483 | } | ||
484 | |||
485 | fn _decrease_indent(self, node: SyntaxNode) -> SyntaxNode { | ||
486 | let mut rewriter = SyntaxRewriter::default(); | 478 | let mut rewriter = SyntaxRewriter::default(); |
487 | node.descendants_with_tokens() | 479 | node.descendants_with_tokens() |
488 | .filter_map(|el| el.into_token()) | 480 | .filter_map(|el| el.into_token()) |
@@ -521,7 +513,7 @@ fn prev_tokens(token: SyntaxToken) -> impl Iterator<Item = SyntaxToken> { | |||
521 | iter::successors(Some(token), |token| token.prev_token()) | 513 | iter::successors(Some(token), |token| token.prev_token()) |
522 | } | 514 | } |
523 | 515 | ||
524 | pub trait AstNodeEdit: AstNode + Sized { | 516 | pub trait AstNodeEdit: AstNode + Clone + Sized { |
525 | #[must_use] | 517 | #[must_use] |
526 | fn insert_children( | 518 | fn insert_children( |
527 | &self, | 519 | &self, |
@@ -558,9 +550,17 @@ pub trait AstNodeEdit: AstNode + Sized { | |||
558 | } | 550 | } |
559 | rewriter.rewrite_ast(self) | 551 | rewriter.rewrite_ast(self) |
560 | } | 552 | } |
553 | #[must_use] | ||
554 | fn indent(&self, indent: IndentLevel) -> Self { | ||
555 | Self::cast(indent.increase_indent(self.syntax().clone())).unwrap() | ||
556 | } | ||
557 | #[must_use] | ||
558 | fn dedent(&self, indent: IndentLevel) -> Self { | ||
559 | Self::cast(indent.decrease_indent(self.syntax().clone())).unwrap() | ||
560 | } | ||
561 | } | 561 | } |
562 | 562 | ||
563 | impl<N: AstNode> AstNodeEdit for N {} | 563 | impl<N: AstNode + Clone> AstNodeEdit for N {} |
564 | 564 | ||
565 | fn single_node(element: impl Into<SyntaxElement>) -> RangeInclusive<SyntaxElement> { | 565 | fn single_node(element: impl Into<SyntaxElement>) -> RangeInclusive<SyntaxElement> { |
566 | let element = element.into(); | 566 | let element = element.into(); |
@@ -580,7 +580,7 @@ fn test_increase_indent() { | |||
580 | _ => (), | 580 | _ => (), |
581 | }" | 581 | }" |
582 | ); | 582 | ); |
583 | let indented = IndentLevel(2).increase_indent(arm_list); | 583 | let indented = arm_list.indent(IndentLevel(2)); |
584 | assert_eq!( | 584 | assert_eq!( |
585 | indented.syntax().to_string(), | 585 | indented.syntax().to_string(), |
586 | "{ | 586 | "{ |
diff --git a/crates/ra_text_edit/src/lib.rs b/crates/ra_text_edit/src/lib.rs index 3409713ff..37f77cc47 100644 --- a/crates/ra_text_edit/src/lib.rs +++ b/crates/ra_text_edit/src/lib.rs | |||
@@ -75,6 +75,7 @@ impl TextEdit { | |||
75 | self.indels.is_empty() | 75 | self.indels.is_empty() |
76 | } | 76 | } |
77 | 77 | ||
78 | // FXME: impl IntoIter instead | ||
78 | pub fn as_indels(&self) -> &[Indel] { | 79 | pub fn as_indels(&self) -> &[Indel] { |
79 | &self.indels | 80 | &self.indels |
80 | } | 81 | } |
diff --git a/crates/ra_env/Cargo.toml b/crates/ra_toolchain/Cargo.toml index f0a401be5..1873fbe16 100644 --- a/crates/ra_env/Cargo.toml +++ b/crates/ra_toolchain/Cargo.toml | |||
@@ -1,9 +1,8 @@ | |||
1 | [package] | 1 | [package] |
2 | edition = "2018" | 2 | edition = "2018" |
3 | name = "ra_env" | 3 | name = "ra_toolchain" |
4 | version = "0.1.0" | 4 | version = "0.1.0" |
5 | authors = ["rust-analyzer developers"] | 5 | authors = ["rust-analyzer developers"] |
6 | 6 | ||
7 | [dependencies] | 7 | [dependencies] |
8 | anyhow = "1.0.26" | ||
9 | home = "0.5.3" | 8 | home = "0.5.3" |
diff --git a/crates/ra_toolchain/src/lib.rs b/crates/ra_toolchain/src/lib.rs new file mode 100644 index 000000000..3c307a0ea --- /dev/null +++ b/crates/ra_toolchain/src/lib.rs | |||
@@ -0,0 +1,64 @@ | |||
1 | //! This crate contains a single public function | ||
2 | //! [`get_path_for_executable`](fn.get_path_for_executable.html). | ||
3 | //! See docs there for more information. | ||
4 | use std::{env, iter, path::PathBuf}; | ||
5 | |||
6 | pub fn cargo() -> PathBuf { | ||
7 | get_path_for_executable("cargo") | ||
8 | } | ||
9 | |||
10 | pub fn rustc() -> PathBuf { | ||
11 | get_path_for_executable("rustc") | ||
12 | } | ||
13 | |||
14 | pub fn rustup() -> PathBuf { | ||
15 | get_path_for_executable("rustup") | ||
16 | } | ||
17 | |||
18 | /// Return a `PathBuf` to use for the given executable. | ||
19 | /// | ||
20 | /// E.g., `get_path_for_executable("cargo")` may return just `cargo` if that | ||
21 | /// gives a valid Cargo executable; or it may return a full path to a valid | ||
22 | /// Cargo. | ||
23 | fn get_path_for_executable(executable_name: &'static str) -> PathBuf { | ||
24 | // The current implementation checks three places for an executable to use: | ||
25 | // 1) Appropriate environment variable (erroring if this is set but not a usable executable) | ||
26 | // example: for cargo, this checks $CARGO environment variable; for rustc, $RUSTC; etc | ||
27 | // 2) `<executable_name>` | ||
28 | // example: for cargo, this tries just `cargo`, which will succeed if `cargo` is on the $PATH | ||
29 | // 3) `~/.cargo/bin/<executable_name>` | ||
30 | // example: for cargo, this tries ~/.cargo/bin/cargo | ||
31 | // It seems that this is a reasonable place to try for cargo, rustc, and rustup | ||
32 | let env_var = executable_name.to_ascii_uppercase(); | ||
33 | if let Some(path) = env::var_os(&env_var) { | ||
34 | return path.into(); | ||
35 | } | ||
36 | |||
37 | if lookup_in_path(executable_name) { | ||
38 | return executable_name.into(); | ||
39 | } | ||
40 | |||
41 | if let Some(mut path) = home::home_dir() { | ||
42 | path.push(".cargo"); | ||
43 | path.push("bin"); | ||
44 | path.push(executable_name); | ||
45 | if path.is_file() { | ||
46 | return path; | ||
47 | } | ||
48 | } | ||
49 | executable_name.into() | ||
50 | } | ||
51 | |||
52 | fn lookup_in_path(exec: &str) -> bool { | ||
53 | let paths = env::var_os("PATH").unwrap_or_default(); | ||
54 | let mut candidates = env::split_paths(&paths).flat_map(|path| { | ||
55 | let candidate = path.join(&exec); | ||
56 | let with_exe = if env::consts::EXE_EXTENSION == "" { | ||
57 | None | ||
58 | } else { | ||
59 | Some(candidate.with_extension(env::consts::EXE_EXTENSION)) | ||
60 | }; | ||
61 | iter::once(candidate).chain(with_exe) | ||
62 | }); | ||
63 | candidates.any(|it| it.is_file()) | ||
64 | } | ||
diff --git a/crates/rust-analyzer/src/conv.rs b/crates/rust-analyzer/src/conv.rs deleted file mode 100644 index f64c90b5b..000000000 --- a/crates/rust-analyzer/src/conv.rs +++ /dev/null | |||
@@ -1,726 +0,0 @@ | |||
1 | //! Convenience module responsible for translating between rust-analyzer's types |