From 5c4ebad9b9d4fddfdcdd00b0f65345509adda35f Mon Sep 17 00:00:00 2001 From: Yann Hamdaoui Date: Thu, 25 Apr 2024 18:18:03 +0200 Subject: [PATCH] Add support for constant pattern This commit adds support for constant patterns, that is patterns matching specific booleans, strings, numbers or the null value. --- core/src/parser/grammar.lalrpop | 18 ++++ core/src/pretty.rs | 33 ++++++- core/src/term/mod.rs | 15 +++- core/src/term/pattern/compile.rs | 40 +++++++++ core/src/term/pattern/mod.rs | 89 +++++++++++++------ core/src/transform/desugar_destructuring.rs | 14 ++- core/src/transform/free_vars.rs | 2 + core/src/typecheck/pattern.rs | 45 +++++++++- .../destructuring/{pass => }/assign.ncl | 0 .../destructuring/{pass => }/atbind.ncl | 0 .../destructuring/constant_bool_fail.ncl | 5 ++ .../destructuring/constant_null_fail.ncl | 5 ++ .../destructuring/constant_number_fail.ncl | 5 ++ .../destructuring/constant_string_fail.ncl | 5 ++ .../inputs/destructuring/constants.ncl | 7 ++ .../destructuring/{pass => }/default.ncl | 0 .../inputs/destructuring/{pass => }/fun.ncl | 0 .../inputs/destructuring/{pass => }/mixed.ncl | 0 .../destructuring/{pass => }/nested.ncl | 0 .../{pass => }/nested_duplicated_ident.ncl | 0 .../inputs/destructuring/{pass => }/open.ncl | 0 .../{pass => }/preserves_types.ncl | 0 .../inputs/destructuring/{pass => }/rest.ncl | 0 .../destructuring/{pass => }/simple.ncl | 0 .../{pass => }/type_annotations.ncl | 0 .../destructuring/{pass => }/typecontract.ncl | 0 lsp/nls/src/pattern.rs | 2 + 27 files changed, 247 insertions(+), 38 deletions(-) rename core/tests/integration/inputs/destructuring/{pass => }/assign.ncl (100%) rename core/tests/integration/inputs/destructuring/{pass => }/atbind.ncl (100%) create mode 100644 core/tests/integration/inputs/destructuring/constant_bool_fail.ncl create mode 100644 core/tests/integration/inputs/destructuring/constant_null_fail.ncl create mode 100644 core/tests/integration/inputs/destructuring/constant_number_fail.ncl create mode 100644 core/tests/integration/inputs/destructuring/constant_string_fail.ncl create mode 100644 core/tests/integration/inputs/destructuring/constants.ncl rename core/tests/integration/inputs/destructuring/{pass => }/default.ncl (100%) rename core/tests/integration/inputs/destructuring/{pass => }/fun.ncl (100%) rename core/tests/integration/inputs/destructuring/{pass => }/mixed.ncl (100%) rename core/tests/integration/inputs/destructuring/{pass => }/nested.ncl (100%) rename core/tests/integration/inputs/destructuring/{pass => }/nested_duplicated_ident.ncl (100%) rename core/tests/integration/inputs/destructuring/{pass => }/open.ncl (100%) rename core/tests/integration/inputs/destructuring/{pass => }/preserves_types.ncl (100%) rename core/tests/integration/inputs/destructuring/{pass => }/rest.ncl (100%) rename core/tests/integration/inputs/destructuring/{pass => }/simple.ncl (100%) rename core/tests/integration/inputs/destructuring/{pass => }/type_annotations.ncl (100%) rename core/tests/integration/inputs/destructuring/{pass => }/typecontract.ncl (100%) diff --git a/core/src/parser/grammar.lalrpop b/core/src/parser/grammar.lalrpop index 116d50fef0..d38cc4bf91 100644 --- a/core/src/parser/grammar.lalrpop +++ b/core/src/parser/grammar.lalrpop @@ -575,6 +575,7 @@ PatternF: Pattern = { #[inline] PatternDataF: PatternData = { RecordPattern => PatternData::Record(<>), + ConstantPattern => PatternData::Constant(<>), EnumPatternF => PatternData::Enum(<>), Ident => PatternData::Any(<>), }; @@ -586,6 +587,23 @@ Pattern: Pattern = PatternF<"">; // A pattern restricted to function arguments. PatternFun: Pattern = PatternF<"function">; +ConstantPattern: ConstantPattern = { + => ConstantPattern { + data, + pos: mk_pos(src_id, start, end) + } +}; + +ConstantPatternData: ConstantPatternData = { + Bool => ConstantPatternData::Bool(<>), + NumberLiteral => ConstantPatternData::Number(<>), + // We could accept multiline strings here, but it's unlikely that this will + // result in very readable match expressions. For now we restrict ourselves + // to standard string; we can always extend to multiline later if needed + StandardStaticString => ConstantPatternData::String(<>.into()), + "null" => ConstantPatternData::Null, +}; + RecordPattern: RecordPattern = { "{" ",")*> "}" =>? { let tail = match last { diff --git a/core/src/pretty.rs b/core/src/pretty.rs index 30f66fd85b..929e29e8ae 100644 --- a/core/src/pretty.rs +++ b/core/src/pretty.rs @@ -2,10 +2,9 @@ use std::fmt; use crate::identifier::LocIdent; use crate::parser::lexer::KEYWORDS; -use crate::term::pattern::{EnumPattern, Pattern, PatternData, RecordPattern, RecordPatternTail}; -use crate::term::record::RecordData; use crate::term::{ - record::{Field, FieldMetadata}, + pattern::*, + record::{Field, FieldMetadata, RecordData}, *, }; use crate::typ::*; @@ -589,6 +588,34 @@ where PatternData::Any(id) => allocator.as_string(id), PatternData::Record(rp) => rp.pretty(allocator), PatternData::Enum(evp) => evp.pretty(allocator), + PatternData::Constant(cp) => cp.pretty(allocator), + } + } +} + +impl<'a, D, A> Pretty<'a, D, A> for &ConstantPattern +where + D: NickelAllocatorExt<'a, A>, + D::Doc: Clone, + A: Clone + 'a, +{ + fn pretty(self, allocator: &'a D) -> DocBuilder<'a, D, A> { + self.data.pretty(allocator) + } +} + +impl<'a, D, A> Pretty<'a, D, A> for &ConstantPatternData +where + D: NickelAllocatorExt<'a, A>, + D::Doc: Clone, + A: Clone + 'a, +{ + fn pretty(self, allocator: &'a D) -> DocBuilder<'a, D, A> { + match self { + ConstantPatternData::Bool(b) => allocator.as_string(b), + ConstantPatternData::Number(n) => allocator.as_string(format!("{}", n.to_sci())), + ConstantPatternData::String(s) => allocator.escaped_string(s).double_quotes(), + ConstantPatternData::Null => allocator.text("null"), } } } diff --git a/core/src/term/mod.rs b/core/src/term/mod.rs index 405c667252..44c00ad2f0 100644 --- a/core/src/term/mod.rs +++ b/core/src/term/mod.rs @@ -27,7 +27,7 @@ use crate::{ impl_display_from_pretty, label::{Label, MergeLabel}, match_sharedterm, - position::TermPos, + position::{RawSpan, TermPos}, typ::{Type, UnboundTypeVariableError}, typecheck::eq::{contract_eq, type_eq_noenv}, }; @@ -621,6 +621,19 @@ pub struct LabeledType { } impl LabeledType { + /// Create a labeled type from a type and a span, which are the minimal information required to + /// instantiate the type and the underlying label. All other values are set to the defaults. + pub fn new(typ: Type, span: RawSpan) -> Self { + Self { + typ: typ.clone(), + label: Label { + typ: Rc::new(typ), + span, + ..Default::default() + }, + } + } + /// Modify the label's `field_name` field. pub fn with_field_name(self, ident: Option) -> Self { LabeledType { diff --git a/core/src/term/pattern/compile.rs b/core/src/term/pattern/compile.rs index 7187a2d5da..f84cde2d65 100644 --- a/core/src/term/pattern/compile.rs +++ b/core/src/term/pattern/compile.rs @@ -258,6 +258,46 @@ impl CompilePart for PatternData { } PatternData::Record(pat) => pat.compile_part(value_id, bindings_id), PatternData::Enum(pat) => pat.compile_part(value_id, bindings_id), + PatternData::Constant(pat) => pat.compile_part(value_id, bindings_id), + } + } +} + +impl CompilePart for ConstantPattern { + fn compile_part(&self, value_id: LocIdent, bindings_id: LocIdent) -> RichTerm { + self.data.compile_part(value_id, bindings_id) + } +} + +impl CompilePart for ConstantPatternData { + fn compile_part(&self, value_id: LocIdent, bindings_id: LocIdent) -> RichTerm { + let compile_constant = |nickel_type: &str, value: Term| { + // if %typeof% value_id == ' && value_id == then + // bindings_id + // else + // null + + // %typeof% value_id == ' + let type_matches = make::op2( + BinaryOp::Eq(), + make::op1(UnaryOp::Typeof(), Term::Var(value_id)), + Term::Enum(nickel_type.into()), + ); + + // value_id == + let value_matches = make::op2(BinaryOp::Eq(), Term::Var(value_id), value); + + // && + let if_condition = mk_app!(make::op1(UnaryOp::BoolAnd(), type_matches), value_matches); + + make::if_then_else(if_condition, Term::Var(bindings_id), Term::Null) + }; + + match self { + ConstantPatternData::Bool(b) => compile_constant("Bool", Term::Bool(*b)), + ConstantPatternData::Number(n) => compile_constant("Number", Term::Num(n.clone())), + ConstantPatternData::String(s) => compile_constant("String", Term::Str(s.clone())), + ConstantPatternData::Null => compile_constant("Other", Term::Null), } } } diff --git a/core/src/term/pattern/mod.rs b/core/src/term/pattern/mod.rs index 546f9b9545..275d887ee5 100644 --- a/core/src/term/pattern/mod.rs +++ b/core/src/term/pattern/mod.rs @@ -1,24 +1,25 @@ //! Pattern matching and destructuring of Nickel values. use std::collections::{hash_map::Entry, HashMap}; +use super::{ + record::{Field, RecordAttrs, RecordData}, + LabeledType, NickelString, Number, RichTerm, Term, TypeAnnotation, +}; + use crate::{ error::EvalError, identifier::LocIdent, - impl_display_from_pretty, - label::Label, - mk_app, + impl_display_from_pretty, mk_app, parser::error::ParseError, position::TermPos, stdlib::internals, - term::{ - record::{Field, RecordAttrs, RecordData}, - LabeledType, RichTerm, Term, TypeAnnotation, - }, typ::{Type, TypeF}, }; pub mod compile; +/// A small helper to generate a + #[derive(Debug, PartialEq, Clone)] pub enum PatternData { /// A simple pattern consisting of an identifier. Match anything and bind the result to the @@ -28,6 +29,8 @@ pub enum PatternData { Record(RecordPattern), /// An enum pattern as in `'Foo x` or `'Foo` Enum(EnumPattern), + /// A constant pattern as in `42` or `true`. + Constant(ConstantPattern), } /// A generic pattern, that can appear in a match expression (not yet implemented) or in a @@ -102,6 +105,21 @@ pub struct RecordPattern { pub pos: TermPos, } +/// A constant pattern, matching a constant value. +#[derive(Debug, PartialEq, Clone)] +pub struct ConstantPattern { + pub data: ConstantPatternData, + pub pos: TermPos, +} + +#[derive(Debug, PartialEq, Clone)] +pub enum ConstantPatternData { + Bool(bool), + Number(Number), + String(NickelString), + Null, +} + /// The tail of a record pattern which might capture the rest of the record. #[derive(Debug, PartialEq, Clone)] pub enum RecordPatternTail { @@ -200,6 +218,7 @@ impl ElaborateContract for PatternData { PatternData::Any(_) => None, PatternData::Record(pat) => pat.elaborate_contract(), PatternData::Enum(pat) => pat.elaborate_contract(), + PatternData::Constant(pat) => pat.elaborate_contract(), } } } @@ -210,6 +229,32 @@ impl ElaborateContract for Pattern { } } +// Generate the contract `std.contract.Equal ` as a labeled type. +fn contract_eq(value: Term, span: crate::position::RawSpan) -> LabeledType { + let contract = mk_app!(internals::stdlib_contract_equal(), value); + + let typ = Type { + typ: TypeF::Flat(contract), + pos: span.into(), + }; + + LabeledType::new(typ, span) +} + +impl ElaborateContract for ConstantPattern { + fn elaborate_contract(&self) -> Option { + // See [^unwrap-span]. + let span = self.pos.unwrap(); + + Some(match &self.data { + ConstantPatternData::Bool(b) => contract_eq(Term::Bool(*b), span), + ConstantPatternData::String(s) => contract_eq(Term::Str(s.clone()), span), + ConstantPatternData::Number(n) => contract_eq(Term::Num(n.clone()), span), + ConstantPatternData::Null => contract_eq(Term::Null, span), + }) + } +} + impl ElaborateContract for EnumPattern { fn elaborate_contract(&self) -> Option { // TODO[adts]: it would be better to simply build a type like `[| 'tag arg |]` or `[| 'tag @@ -226,18 +271,11 @@ impl ElaborateContract for EnumPattern { pos: self.pos, }; - Some(LabeledType { - typ: typ.clone(), - label: Label { - typ: typ.into(), - // [^unwrap-span]: We need the position to be defined here. Hopefully, - // contract-generating pattern are pattern used in destructuring, and destructuring - // patterns aren't currently generated by the Nickel interpreter. So we should only - // encounter user-written patterns here, which should have a position. - span: self.pos.unwrap(), - ..Default::default() - }, - }) + // [^unwrap-span]: We need the position to be defined here. Hopefully, + // contract-generating pattern are pattern used in destructuring, and destructuring + // patterns aren't currently generated by the Nickel interpreter. So we should only + // encounter user-written patterns here, which should have a position. + Some(LabeledType::new(typ, self.pos.unwrap())) } } @@ -261,19 +299,14 @@ impl ElaborateContract for RecordPattern { pos: self.pos, }; - Some(LabeledType { - typ: typ.clone(), - label: Label { - typ: typ.into(), - // unwrap(): cf [^unwrap-span] - span: self.pos.unwrap(), - ..Default::default() - }, - }) + // unwrap(): see [^unwrap-span] + Some(LabeledType::new(typ, self.pos.unwrap())) } } impl_display_from_pretty!(PatternData); impl_display_from_pretty!(Pattern); +impl_display_from_pretty!(ConstantPatternData); +impl_display_from_pretty!(ConstantPattern); impl_display_from_pretty!(RecordPattern); impl_display_from_pretty!(EnumPattern); diff --git a/core/src/transform/desugar_destructuring.rs b/core/src/transform/desugar_destructuring.rs index 17ffaffca6..e6c3264f8e 100644 --- a/core/src/transform/desugar_destructuring.rs +++ b/core/src/transform/desugar_destructuring.rs @@ -140,10 +140,18 @@ impl Desugar for PatternData { PatternData::Any(id) => Term::Let(id, destr, body, LetAttrs::default()), PatternData::Record(pat) => pat.desugar(destr, body), PatternData::Enum(pat) => pat.desugar(destr, body), + PatternData::Constant(pat) => pat.desugar(destr, body), } } } +impl Desugar for ConstantPattern { + fn desugar(self, destr: RichTerm, body: RichTerm) -> Term { + // See [^seq-patterns] + mk_app!(mk_term::op1(UnaryOp::Seq(), destr), body).into() + } +} + impl Desugar for FieldPattern { // For a field pattern, we assume that the `destr` argument is the whole record being // destructured. We extract the field from `destr`, or use the default value is the field isn't @@ -194,9 +202,9 @@ impl Desugar for EnumPattern { let extracted = mk_term::op1(UnaryOp::EnumUnwrapVariant(), destr.clone()); arg_pat.desugar(extracted, body) } - // If the pattern doesn't bind any argument, it's transparent, and we just proceed with the - // body. However, because of lazyness, the associated contract will never be checked, - // because body doesn't depend on `destr`. + // [^seq-patterns]: If the pattern doesn't bind any argument, it's transparent, and we just + // proceed with the body. However, because of lazyness, the associated contract will never + // be checked, because body doesn't depend on `destr`. // // For patterns that bind variables, it's reasonable to keep them lazy: that is, in `let // 'Foo x = destr in body`, `destr` is checked to be an enum only when `x` is evaluated. diff --git a/core/src/transform/free_vars.rs b/core/src/transform/free_vars.rs index a7cafd2bcc..8cc58620bb 100644 --- a/core/src/transform/free_vars.rs +++ b/core/src/transform/free_vars.rs @@ -255,6 +255,8 @@ impl RemoveBindings for PatternData { PatternData::Enum(enum_variant_pat) => { enum_variant_pat.remove_bindings(working_set); } + // A constant pattern doesn't bind any variable. + PatternData::Constant(_) => (), } } } diff --git a/core/src/typecheck/pattern.rs b/core/src/typecheck/pattern.rs index 9e6c5d32e0..3299365589 100644 --- a/core/src/typecheck/pattern.rs +++ b/core/src/typecheck/pattern.rs @@ -149,8 +149,8 @@ pub fn close_enum(tail: UnifEnumRows, state: &mut State) { pub(super) trait PatternTypes { /// The type produced by the pattern. Depending on the nature of the pattern, this type may - /// vary: for example, a record pattern will record rows, while a general pattern will produce - /// a general [super::UnifType] + /// vary: for example, a record pattern will produce record rows, while a general pattern will + /// produce a general [super::UnifType] type PatType; /// Builds the type associated to the whole pattern, as well as the types associated to each @@ -190,7 +190,7 @@ pub(super) trait PatternTypes { } /// Same as `pattern_types`, but inject the bindings in a working vector instead of returning - /// them. Implementors should implement this method whose signature avoid creating and + /// them. Implementors should implement this method whose signature avoids creating and /// combining many short-lived vectors when walking recursively through a pattern. fn pattern_types_inj( &self, @@ -315,10 +315,49 @@ impl PatternTypes for PatternData { }, )))) } + PatternData::Constant(constant_pat) => { + constant_pat.pattern_types_inj(pt_state, path, state, ctxt, mode) + } } } } +impl PatternTypes for ConstantPattern { + type PatType = UnifType; + + fn pattern_types_inj( + &self, + pt_state: &mut PatTypeState, + path: PatternPath, + state: &mut State, + ctxt: &Context, + mode: TypecheckMode, + ) -> Result { + self.data + .pattern_types_inj(pt_state, path, state, ctxt, mode) + } +} + +impl PatternTypes for ConstantPatternData { + type PatType = UnifType; + + fn pattern_types_inj( + &self, + _pt_state: &mut PatTypeState, + _path: PatternPath, + _state: &mut State, + _ctxt: &Context, + _mode: TypecheckMode, + ) -> Result { + Ok(match self { + ConstantPatternData::Bool(_) => UnifType::concrete(TypeF::Bool), + ConstantPatternData::Number(_) => UnifType::concrete(TypeF::Number), + ConstantPatternData::String(_) => UnifType::concrete(TypeF::String), + ConstantPatternData::Null => UnifType::concrete(TypeF::Dyn), + }) + } +} + impl PatternTypes for FieldPattern { type PatType = UnifRecordRow; diff --git a/core/tests/integration/inputs/destructuring/pass/assign.ncl b/core/tests/integration/inputs/destructuring/assign.ncl similarity index 100% rename from core/tests/integration/inputs/destructuring/pass/assign.ncl rename to core/tests/integration/inputs/destructuring/assign.ncl diff --git a/core/tests/integration/inputs/destructuring/pass/atbind.ncl b/core/tests/integration/inputs/destructuring/atbind.ncl similarity index 100% rename from core/tests/integration/inputs/destructuring/pass/atbind.ncl rename to core/tests/integration/inputs/destructuring/atbind.ncl diff --git a/core/tests/integration/inputs/destructuring/constant_bool_fail.ncl b/core/tests/integration/inputs/destructuring/constant_bool_fail.ncl new file mode 100644 index 0000000000..80b39dc642 --- /dev/null +++ b/core/tests/integration/inputs/destructuring/constant_bool_fail.ncl @@ -0,0 +1,5 @@ +# test.type = 'error' +# +# [test.metadata] +# error = 'EvalError::BlameError' +let true = false in true diff --git a/core/tests/integration/inputs/destructuring/constant_null_fail.ncl b/core/tests/integration/inputs/destructuring/constant_null_fail.ncl new file mode 100644 index 0000000000..3e38d6ac8b --- /dev/null +++ b/core/tests/integration/inputs/destructuring/constant_null_fail.ncl @@ -0,0 +1,5 @@ +# test.type = 'error' +# +# [test.metadata] +# error = 'EvalError::BlameError' +let null = 0 in true diff --git a/core/tests/integration/inputs/destructuring/constant_number_fail.ncl b/core/tests/integration/inputs/destructuring/constant_number_fail.ncl new file mode 100644 index 0000000000..f78dae2617 --- /dev/null +++ b/core/tests/integration/inputs/destructuring/constant_number_fail.ncl @@ -0,0 +1,5 @@ +# test.type = 'error' +# +# [test.metadata] +# error = 'EvalError::BlameError' +let 1 = 1 + 1 in true diff --git a/core/tests/integration/inputs/destructuring/constant_string_fail.ncl b/core/tests/integration/inputs/destructuring/constant_string_fail.ncl new file mode 100644 index 0000000000..6708214d8f --- /dev/null +++ b/core/tests/integration/inputs/destructuring/constant_string_fail.ncl @@ -0,0 +1,5 @@ +# test.type = 'error' +# +# [test.metadata] +# error = 'EvalError::BlameError' +let "ab" = "a" ++ "b" ++ "c" in true diff --git a/core/tests/integration/inputs/destructuring/constants.ncl b/core/tests/integration/inputs/destructuring/constants.ncl new file mode 100644 index 0000000000..a318d5c368 --- /dev/null +++ b/core/tests/integration/inputs/destructuring/constants.ncl @@ -0,0 +1,7 @@ +# test.type = 'pass' +let 5 = 4 + 1 in +let null = null in +let false = !true in +let true = true in +let "ab" = "a" ++ "b" in +true diff --git a/core/tests/integration/inputs/destructuring/pass/default.ncl b/core/tests/integration/inputs/destructuring/default.ncl similarity index 100% rename from core/tests/integration/inputs/destructuring/pass/default.ncl rename to core/tests/integration/inputs/destructuring/default.ncl diff --git a/core/tests/integration/inputs/destructuring/pass/fun.ncl b/core/tests/integration/inputs/destructuring/fun.ncl similarity index 100% rename from core/tests/integration/inputs/destructuring/pass/fun.ncl rename to core/tests/integration/inputs/destructuring/fun.ncl diff --git a/core/tests/integration/inputs/destructuring/pass/mixed.ncl b/core/tests/integration/inputs/destructuring/mixed.ncl similarity index 100% rename from core/tests/integration/inputs/destructuring/pass/mixed.ncl rename to core/tests/integration/inputs/destructuring/mixed.ncl diff --git a/core/tests/integration/inputs/destructuring/pass/nested.ncl b/core/tests/integration/inputs/destructuring/nested.ncl similarity index 100% rename from core/tests/integration/inputs/destructuring/pass/nested.ncl rename to core/tests/integration/inputs/destructuring/nested.ncl diff --git a/core/tests/integration/inputs/destructuring/pass/nested_duplicated_ident.ncl b/core/tests/integration/inputs/destructuring/nested_duplicated_ident.ncl similarity index 100% rename from core/tests/integration/inputs/destructuring/pass/nested_duplicated_ident.ncl rename to core/tests/integration/inputs/destructuring/nested_duplicated_ident.ncl diff --git a/core/tests/integration/inputs/destructuring/pass/open.ncl b/core/tests/integration/inputs/destructuring/open.ncl similarity index 100% rename from core/tests/integration/inputs/destructuring/pass/open.ncl rename to core/tests/integration/inputs/destructuring/open.ncl diff --git a/core/tests/integration/inputs/destructuring/pass/preserves_types.ncl b/core/tests/integration/inputs/destructuring/preserves_types.ncl similarity index 100% rename from core/tests/integration/inputs/destructuring/pass/preserves_types.ncl rename to core/tests/integration/inputs/destructuring/preserves_types.ncl diff --git a/core/tests/integration/inputs/destructuring/pass/rest.ncl b/core/tests/integration/inputs/destructuring/rest.ncl similarity index 100% rename from core/tests/integration/inputs/destructuring/pass/rest.ncl rename to core/tests/integration/inputs/destructuring/rest.ncl diff --git a/core/tests/integration/inputs/destructuring/pass/simple.ncl b/core/tests/integration/inputs/destructuring/simple.ncl similarity index 100% rename from core/tests/integration/inputs/destructuring/pass/simple.ncl rename to core/tests/integration/inputs/destructuring/simple.ncl diff --git a/core/tests/integration/inputs/destructuring/pass/type_annotations.ncl b/core/tests/integration/inputs/destructuring/type_annotations.ncl similarity index 100% rename from core/tests/integration/inputs/destructuring/pass/type_annotations.ncl rename to core/tests/integration/inputs/destructuring/type_annotations.ncl diff --git a/core/tests/integration/inputs/destructuring/pass/typecontract.ncl b/core/tests/integration/inputs/destructuring/typecontract.ncl similarity index 100% rename from core/tests/integration/inputs/destructuring/pass/typecontract.ncl rename to core/tests/integration/inputs/destructuring/typecontract.ncl diff --git a/lsp/nls/src/pattern.rs b/lsp/nls/src/pattern.rs index f1ea21fe91..807f178400 100644 --- a/lsp/nls/src/pattern.rs +++ b/lsp/nls/src/pattern.rs @@ -89,6 +89,8 @@ impl InjectBindings for PatternData { PatternData::Enum(evariant_pat) => { evariant_pat.inject_bindings(bindings, path, parent_deco) } + // Constant patterns don't bind any variable + PatternData::Constant(_) => (), } } }