diff options
author | Florian Diebold <[email protected]> | 2020-02-21 15:56:34 +0000 |
---|---|---|
committer | Florian Diebold <[email protected]> | 2020-02-21 16:01:19 +0000 |
commit | f1f45f9191d60c52dbedec717aee0de4a0580bcc (patch) | |
tree | baebf708dcdc154fbe4fe903906e11e344a09802 /crates | |
parent | e3037c2631ecb55996b676ce2c18b9df1858abaa (diff) |
Fix handling of const patterns
E.g. in `match x { None => ... }`, `None` is a path pattern (resolving to the
option variant), not a binding. To determine this, we need to try to resolve the
name during lowering. This isn't too hard since we already need to resolve names
for macro expansion anyway (though maybe a bit hacky).
Fixes #1618.
Diffstat (limited to 'crates')
-rw-r--r-- | crates/ra_hir_def/src/adt.rs | 1 | ||||
-rw-r--r-- | crates/ra_hir_def/src/body/lower.rs | 38 | ||||
-rw-r--r-- | crates/ra_hir_def/src/expr.rs | 2 | ||||
-rw-r--r-- | crates/ra_hir_ty/src/infer/pat.rs | 4 | ||||
-rw-r--r-- | crates/ra_hir_ty/src/tests/patterns.rs | 46 |
5 files changed, 85 insertions, 6 deletions
diff --git a/crates/ra_hir_def/src/adt.rs b/crates/ra_hir_def/src/adt.rs index 985f409e8..2bdfc2b8d 100644 --- a/crates/ra_hir_def/src/adt.rs +++ b/crates/ra_hir_def/src/adt.rs | |||
@@ -174,6 +174,7 @@ impl HasChildSource for VariantId { | |||
174 | } | 174 | } |
175 | } | 175 | } |
176 | 176 | ||
177 | #[derive(Debug, Copy, Clone, PartialEq, Eq)] | ||
177 | pub enum StructKind { | 178 | pub enum StructKind { |
178 | Tuple, | 179 | Tuple, |
179 | Record, | 180 | Record, |
diff --git a/crates/ra_hir_def/src/body/lower.rs b/crates/ra_hir_def/src/body/lower.rs index b1626fa11..b3fb6d452 100644 --- a/crates/ra_hir_def/src/body/lower.rs +++ b/crates/ra_hir_def/src/body/lower.rs | |||
@@ -15,6 +15,7 @@ use ra_syntax::{ | |||
15 | use test_utils::tested_by; | 15 | use test_utils::tested_by; |
16 | 16 | ||
17 | use crate::{ | 17 | use crate::{ |
18 | adt::StructKind, | ||
18 | body::{Body, BodySourceMap, Expander, PatPtr}, | 19 | body::{Body, BodySourceMap, Expander, PatPtr}, |
19 | builtin_type::{BuiltinFloat, BuiltinInt}, | 20 | builtin_type::{BuiltinFloat, BuiltinInt}, |
20 | db::DefDatabase, | 21 | db::DefDatabase, |
@@ -22,11 +23,12 @@ use crate::{ | |||
22 | ArithOp, Array, BinaryOp, BindingAnnotation, CmpOp, Expr, ExprId, Literal, LogicOp, | 23 | ArithOp, Array, BinaryOp, BindingAnnotation, CmpOp, Expr, ExprId, Literal, LogicOp, |
23 | MatchArm, Ordering, Pat, PatId, RecordFieldPat, RecordLitField, Statement, | 24 | MatchArm, Ordering, Pat, PatId, RecordFieldPat, RecordLitField, Statement, |
24 | }, | 25 | }, |
26 | item_scope::BuiltinShadowMode, | ||
25 | path::GenericArgs, | 27 | path::GenericArgs, |
26 | path::Path, | 28 | path::Path, |
27 | type_ref::{Mutability, TypeRef}, | 29 | type_ref::{Mutability, TypeRef}, |
28 | ConstLoc, ContainerId, DefWithBodyId, EnumLoc, FunctionLoc, Intern, ModuleDefId, StaticLoc, | 30 | AdtId, ConstLoc, ContainerId, DefWithBodyId, EnumLoc, FunctionLoc, Intern, ModuleDefId, |
29 | StructLoc, TraitLoc, TypeAliasLoc, UnionLoc, | 31 | StaticLoc, StructLoc, TraitLoc, TypeAliasLoc, UnionLoc, |
30 | }; | 32 | }; |
31 | 33 | ||
32 | pub(super) fn lower( | 34 | pub(super) fn lower( |
@@ -571,7 +573,37 @@ where | |||
571 | let name = bp.name().map(|nr| nr.as_name()).unwrap_or_else(Name::missing); | 573 | let name = bp.name().map(|nr| nr.as_name()).unwrap_or_else(Name::missing); |
572 | let annotation = BindingAnnotation::new(bp.is_mutable(), bp.is_ref()); | 574 | let annotation = BindingAnnotation::new(bp.is_mutable(), bp.is_ref()); |
573 | let subpat = bp.pat().map(|subpat| self.collect_pat(subpat)); | 575 | let subpat = bp.pat().map(|subpat| self.collect_pat(subpat)); |
574 | Pat::Bind { name, mode: annotation, subpat } | 576 | if annotation == BindingAnnotation::Unannotated && subpat.is_none() { |
577 | // This could also be a single-segment path pattern. To | ||
578 | // decide that, we need to try resolving the name. | ||
579 | let (resolved, _) = self.expander.crate_def_map.resolve_path( | ||
580 | self.db, | ||
581 | self.expander.module.local_id, | ||
582 | &name.clone().into(), | ||
583 | BuiltinShadowMode::Other, | ||
584 | ); | ||
585 | match resolved.take_values() { | ||
586 | Some(ModuleDefId::ConstId(_)) => Pat::Path(name.into()), | ||
587 | Some(ModuleDefId::EnumVariantId(_)) => { | ||
588 | // this is only really valid for unit variants, but | ||
589 | // shadowing other enum variants with a pattern is | ||
590 | // an error anyway | ||
591 | Pat::Path(name.into()) | ||
592 | } | ||
593 | Some(ModuleDefId::AdtId(AdtId::StructId(s))) | ||
594 | if self.db.struct_data(s).variant_data.kind() != StructKind::Record => | ||
595 | { | ||
596 | // Funnily enough, record structs *can* be shadowed | ||
597 | // by pattern bindings (but unit or tuple structs | ||
598 | // can't). | ||
599 | Pat::Path(name.into()) | ||
600 | } | ||
601 | // shadowing statics is an error as well, so we just ignore that case here | ||
602 | _ => Pat::Bind { name, mode: annotation, subpat }, | ||
603 | } | ||
604 | } else { | ||
605 | Pat::Bind { name, mode: annotation, subpat } | ||
606 | } | ||
575 | } | 607 | } |
576 | ast::Pat::TupleStructPat(p) => { | 608 | ast::Pat::TupleStructPat(p) => { |
577 | let path = p.path().and_then(|path| self.expander.parse_path(path)); | 609 | let path = p.path().and_then(|path| self.expander.parse_path(path)); |
diff --git a/crates/ra_hir_def/src/expr.rs b/crates/ra_hir_def/src/expr.rs index 9707c5527..66d004717 100644 --- a/crates/ra_hir_def/src/expr.rs +++ b/crates/ra_hir_def/src/expr.rs | |||
@@ -48,7 +48,7 @@ pub enum Literal { | |||
48 | 48 | ||
49 | #[derive(Debug, Clone, Eq, PartialEq)] | 49 | #[derive(Debug, Clone, Eq, PartialEq)] |
50 | pub enum Expr { | 50 | pub enum Expr { |
51 | /// This is produced if syntax tree does not have a required expression piece. | 51 | /// This is produced if the syntax tree does not have a required expression piece. |
52 | Missing, | 52 | Missing, |
53 | Path(Path), | 53 | Path(Path), |
54 | If { | 54 | If { |
diff --git a/crates/ra_hir_ty/src/infer/pat.rs b/crates/ra_hir_ty/src/infer/pat.rs index a495ecbfe..bf8ea192b 100644 --- a/crates/ra_hir_ty/src/infer/pat.rs +++ b/crates/ra_hir_ty/src/infer/pat.rs | |||
@@ -189,7 +189,9 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { | |||
189 | }; | 189 | }; |
190 | // use a new type variable if we got Ty::Unknown here | 190 | // use a new type variable if we got Ty::Unknown here |
191 | let ty = self.insert_type_vars_shallow(ty); | 191 | let ty = self.insert_type_vars_shallow(ty); |
192 | self.unify(&ty, expected); | 192 | if !self.unify(&ty, expected) { |
193 | // FIXME record mismatch, we need to change the type of self.type_mismatches for that | ||
194 | } | ||
193 | let ty = self.resolve_ty_as_possible(ty); | 195 | let ty = self.resolve_ty_as_possible(ty); |
194 | self.write_pat_ty(pat, ty.clone()); | 196 | self.write_pat_ty(pat, ty.clone()); |
195 | ty | 197 | ty |
diff --git a/crates/ra_hir_ty/src/tests/patterns.rs b/crates/ra_hir_ty/src/tests/patterns.rs index e25d6dbc4..81d00c2af 100644 --- a/crates/ra_hir_ty/src/tests/patterns.rs +++ b/crates/ra_hir_ty/src/tests/patterns.rs | |||
@@ -1,4 +1,4 @@ | |||
1 | use super::infer; | 1 | use super::{infer, infer_with_mismatches}; |
2 | use insta::assert_snapshot; | 2 | use insta::assert_snapshot; |
3 | use test_utils::covers; | 3 | use test_utils::covers; |
4 | 4 | ||
@@ -236,3 +236,47 @@ fn test(a1: A<u32>, o: Option<u64>) { | |||
236 | "### | 236 | "### |
237 | ); | 237 | ); |
238 | } | 238 | } |
239 | |||
240 | #[test] | ||
241 | fn infer_const_pattern() { | ||
242 | assert_snapshot!( | ||
243 | infer_with_mismatches(r#" | ||
244 | enum Option<T> { None } | ||
245 | use Option::None; | ||
246 | struct Foo; | ||
247 | const Bar: usize = 1; | ||
248 | |||
249 | fn test() { | ||
250 | let a: Option<u32> = None; | ||
251 | let b: Option<i64> = match a { | ||
252 | None => None, | ||
253 | }; | ||
254 | let _: () = match () { Foo => Foo }; // Expected mismatch | ||
255 | let _: () = match () { Bar => Bar }; // Expected mismatch | ||
256 | } | ||
257 | "#, true), | ||
258 | @r###" | ||
259 | [74; 75) '1': usize | ||
260 | [88; 310) '{ ...atch }': () | ||
261 | [98; 99) 'a': Option<u32> | ||
262 | [115; 119) 'None': Option<u32> | ||
263 | [129; 130) 'b': Option<i64> | ||
264 | [146; 183) 'match ... }': Option<i64> | ||
265 | [152; 153) 'a': Option<u32> | ||
266 | [164; 168) 'None': Option<u32> | ||
267 | [172; 176) 'None': Option<i64> | ||
268 | [193; 194) '_': () | ||
269 | [201; 224) 'match ... Foo }': Foo | ||
270 | [207; 209) '()': () | ||
271 | [212; 215) 'Foo': Foo | ||
272 | [219; 222) 'Foo': Foo | ||
273 | [255; 256) '_': () | ||
274 | [263; 286) 'match ... Bar }': usize | ||
275 | [269; 271) '()': () | ||
276 | [274; 277) 'Bar': usize | ||
277 | [281; 284) 'Bar': usize | ||
278 | [201; 224): expected (), got Foo | ||
279 | [263; 286): expected (), got usize | ||
280 | "### | ||
281 | ); | ||
282 | } | ||