diff options
Diffstat (limited to 'crates')
-rw-r--r-- | crates/ra_hir_ty/src/diagnostics.rs | 28 | ||||
-rw-r--r-- | crates/ra_hir_ty/src/infer.rs | 60 | ||||
-rw-r--r-- | crates/ra_hir_ty/src/infer/coerce.rs | 34 | ||||
-rw-r--r-- | crates/ra_hir_ty/src/infer/expr.rs | 84 | ||||
-rw-r--r-- | crates/ra_hir_ty/src/lib.rs | 10 | ||||
-rw-r--r-- | crates/ra_hir_ty/src/marks.rs | 1 | ||||
-rw-r--r-- | crates/ra_hir_ty/src/tests.rs | 18 | ||||
-rw-r--r-- | crates/ra_hir_ty/src/tests/coercion.rs | 46 | ||||
-rw-r--r-- | crates/ra_hir_ty/src/tests/macros.rs | 2 | ||||
-rw-r--r-- | crates/ra_hir_ty/src/tests/never_type.rs | 177 | ||||
-rw-r--r-- | crates/ra_hir_ty/src/tests/simple.rs | 10 | ||||
-rw-r--r-- | crates/ra_project_model/src/lib.rs | 58 | ||||
-rw-r--r-- | crates/rust-analyzer/src/main_loop.rs | 20 |
13 files changed, 464 insertions, 84 deletions
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/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 614c352a0..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 } => { |
@@ -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/tests.rs b/crates/ra_hir_ty/src/tests.rs index 623d00010..1fe05c70c 100644 --- a/crates/ra_hir_ty/src/tests.rs +++ b/crates/ra_hir_ty/src/tests.rs | |||
@@ -531,3 +531,21 @@ fn missing_record_pat_field_no_diagnostic_if_not_exhaustive() { | |||
531 | 531 | ||
532 | assert_snapshot!(diagnostics, @""); | 532 | assert_snapshot!(diagnostics, @""); |
533 | } | 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/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/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 e17a17900..3820175f6 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': ! |
@@ -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 | ); |
diff --git a/crates/ra_project_model/src/lib.rs b/crates/ra_project_model/src/lib.rs index 5a0a87ed7..4f098b706 100644 --- a/crates/ra_project_model/src/lib.rs +++ b/crates/ra_project_model/src/lib.rs | |||
@@ -88,46 +88,28 @@ impl ProjectRoot { | |||
88 | } | 88 | } |
89 | 89 | ||
90 | pub fn discover(path: &Path) -> io::Result<Vec<ProjectRoot>> { | 90 | pub fn discover(path: &Path) -> io::Result<Vec<ProjectRoot>> { |
91 | if let Some(project_json) = find_rust_project_json(path) { | 91 | if let Some(project_json) = find_in_parent_dirs(path, "rust-project.json") { |
92 | return Ok(vec![ProjectRoot::ProjectJson(project_json)]); | 92 | return Ok(vec![ProjectRoot::ProjectJson(project_json)]); |
93 | } | 93 | } |
94 | return find_cargo_toml(path) | 94 | return find_cargo_toml(path) |
95 | .map(|paths| paths.into_iter().map(ProjectRoot::CargoToml).collect()); | 95 | .map(|paths| paths.into_iter().map(ProjectRoot::CargoToml).collect()); |
96 | 96 | ||
97 | fn find_rust_project_json(path: &Path) -> Option<PathBuf> { | ||
98 | if path.ends_with("rust-project.json") { | ||
99 | return Some(path.to_path_buf()); | ||
100 | } | ||
101 | |||
102 | let mut curr = Some(path); | ||
103 | while let Some(path) = curr { | ||
104 | let candidate = path.join("rust-project.json"); | ||
105 | if candidate.exists() { | ||
106 | return Some(candidate); | ||
107 | } | ||
108 | curr = path.parent(); | ||
109 | } | ||
110 | |||
111 | None | ||
112 | } | ||
113 | |||
114 | fn find_cargo_toml(path: &Path) -> io::Result<Vec<PathBuf>> { | 97 | fn find_cargo_toml(path: &Path) -> io::Result<Vec<PathBuf>> { |
115 | if path.ends_with("Cargo.toml") { | 98 | match find_in_parent_dirs(path, "Cargo.toml") { |
116 | 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)?)), | ||
117 | } | 101 | } |
102 | } | ||
118 | 103 | ||
119 | 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> { |
120 | return Ok(vec![p]); | 105 | if path.ends_with(target_file_name) { |
106 | return Some(path.to_owned()); | ||
121 | } | 107 | } |
122 | 108 | ||
123 | let entities = read_dir(path)?; | ||
124 | Ok(find_cargo_toml_in_child_dir(entities)) | ||
125 | } | ||
126 | |||
127 | fn find_cargo_toml_in_parent_dir(path: &Path) -> Option<PathBuf> { | ||
128 | let mut curr = Some(path); | 109 | let mut curr = Some(path); |
110 | |||
129 | while let Some(path) = curr { | 111 | while let Some(path) = curr { |
130 | let candidate = path.join("Cargo.toml"); | 112 | let candidate = path.join(target_file_name); |
131 | if candidate.exists() { | 113 | if candidate.exists() { |
132 | return Some(candidate); | 114 | return Some(candidate); |
133 | } | 115 | } |
@@ -139,14 +121,11 @@ impl ProjectRoot { | |||
139 | 121 | ||
140 | fn find_cargo_toml_in_child_dir(entities: ReadDir) -> Vec<PathBuf> { | 122 | fn find_cargo_toml_in_child_dir(entities: ReadDir) -> Vec<PathBuf> { |
141 | // 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 |
142 | let mut valid_canditates = vec![]; | 124 | entities |
143 | for entity in entities.filter_map(Result::ok) { | 125 | .filter_map(Result::ok) |
144 | let candidate = entity.path().join("Cargo.toml"); | 126 | .map(|it| it.path().join("Cargo.toml")) |
145 | if candidate.exists() { | 127 | .filter(|it| it.exists()) |
146 | valid_canditates.push(candidate) | 128 | .collect() |
147 | } | ||
148 | } | ||
149 | valid_canditates | ||
150 | } | 129 | } |
151 | } | 130 | } |
152 | } | 131 | } |
@@ -600,7 +579,12 @@ pub fn get_rustc_cfg_options(target: Option<&String>) -> CfgOptions { | |||
600 | fn output(mut cmd: Command) -> Result<Output> { | 579 | fn output(mut cmd: Command) -> Result<Output> { |
601 | let output = cmd.output().with_context(|| format!("{:?} failed", cmd))?; | 580 | let output = cmd.output().with_context(|| format!("{:?} failed", cmd))?; |
602 | if !output.status.success() { | 581 | if !output.status.success() { |
603 | bail!("{:?} failed, {}", cmd, output.status) | 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 | } | ||
604 | } | 588 | } |
605 | Ok(output) | 589 | Ok(output) |
606 | } | 590 | } |
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs index b77f0c5a9..17b0b95b9 100644 --- a/crates/rust-analyzer/src/main_loop.rs +++ b/crates/rust-analyzer/src/main_loop.rs | |||
@@ -96,23 +96,21 @@ pub fn main_loop(ws_roots: Vec<PathBuf>, config: Config, connection: Connection) | |||
96 | let mut world_state = { | 96 | let mut world_state = { |
97 | let workspaces = { | 97 | let workspaces = { |
98 | // FIXME: support dynamic workspace loading. | 98 | // FIXME: support dynamic workspace loading. |
99 | let mut visited = FxHashSet::default(); | 99 | let project_roots: FxHashSet<_> = ws_roots |
100 | let project_roots = ws_roots | ||
101 | .iter() | 100 | .iter() |
102 | .filter_map(|it| ra_project_model::ProjectRoot::discover(it).ok()) | 101 | .filter_map(|it| ra_project_model::ProjectRoot::discover(it).ok()) |
103 | .flatten() | 102 | .flatten() |
104 | .filter(|it| visited.insert(it.clone())) | 103 | .collect(); |
105 | .collect::<Vec<_>>(); | ||
106 | 104 | ||
107 | if project_roots.is_empty() && config.notifications.cargo_toml_not_found { | 105 | if project_roots.is_empty() && config.notifications.cargo_toml_not_found { |
108 | show_message( | 106 | show_message( |
109 | req::MessageType::Error, | 107 | req::MessageType::Error, |
110 | format!( | 108 | format!( |
111 | "rust-analyzer failed to discover workspace, no Cargo.toml found, dirs searched: {}", | 109 | "rust-analyzer failed to discover workspace, no Cargo.toml found, dirs searched: {}", |
112 | ws_roots.iter().format_with(", ", |it, f| f(&it.display())) | 110 | ws_roots.iter().format_with(", ", |it, f| f(&it.display())) |
113 | ), | 111 | ), |
114 | &connection.sender, | 112 | &connection.sender, |
115 | ); | 113 | ); |
116 | }; | 114 | }; |
117 | 115 | ||
118 | project_roots | 116 | project_roots |