diff --git a/crates/hir/src/hir_def/pat.rs b/crates/hir/src/hir_def/pat.rs index 1bf6156fb..261085f3d 100644 --- a/crates/hir/src/hir_def/pat.rs +++ b/crates/hir/src/hir_def/pat.rs @@ -1,8 +1,7 @@ use cranelift_entity::entity_impl; -use crate::{span::pat::LazyPatSpan, HirDb}; - use super::{Body, IdentId, LitKind, Partial, PathId}; +use crate::{span::pat::LazyPatSpan, HirDb}; #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum Pat { @@ -10,7 +9,8 @@ pub enum Pat { Rest, Lit(Partial), Tuple(Vec), - Path(Partial), + /// The second bool is `true` if the pat has `mut` in front of it. + Path(Partial, bool), PathTuple(Partial, Vec), Record(Partial, Vec), Or(PatId, PatId), @@ -20,7 +20,7 @@ impl Pat { /// Return `true` if this pattern is a binding. pub fn is_bind(&self, db: &dyn HirDb) -> bool { match self { - Self::Path(Partial::Present(p)) => p.len(db) == 1, + Self::Path(Partial::Present(p), _) => p.len(db) == 1, _ => false, } } @@ -53,3 +53,18 @@ pub struct RecordPatField { pub label: Partial, pub pat: PatId, } + +impl RecordPatField { + pub fn label(&self, db: &dyn HirDb, body: Body) -> Option { + if let Partial::Present(label) = self.label { + return Some(label); + } + + match self.pat.data(db, body) { + Partial::Present(Pat::Path(Partial::Present(path), _)) if path.is_ident(db) => { + path.last_segment(db).to_opt() + } + _ => None, + } + } +} diff --git a/crates/hir/src/hir_def/path.rs b/crates/hir/src/hir_def/path.rs index 4a2423ad3..552089b42 100644 --- a/crates/hir/src/hir_def/path.rs +++ b/crates/hir/src/hir_def/path.rs @@ -1,6 +1,5 @@ -use crate::{hir_def::Partial, HirDb}; - use super::{kw, IdentId}; +use crate::{hir_def::Partial, HirDb}; #[salsa::interned] pub struct PathId { @@ -17,6 +16,10 @@ impl PathId { self.segments(db).len() } + pub fn is_ident(self, db: &dyn HirDb) -> bool { + self.len(db) == 1 + } + pub fn self_ty(db: &dyn HirDb) -> Self { let self_ty = Partial::Present(kw::SELF_TY); Self::new(db, vec![self_ty]) diff --git a/crates/hir/src/lower/pat.rs b/crates/hir/src/lower/pat.rs index 6a17669d1..a33c96487 100644 --- a/crates/hir/src/lower/pat.rs +++ b/crates/hir/src/lower/pat.rs @@ -1,12 +1,11 @@ use parser::ast; +use super::body::BodyCtxt; use crate::{ hir_def::{pat::*, IdentId, LitKind, PathId}, span::HirOrigin, }; -use super::body::BodyCtxt; - impl Pat { pub(super) fn lower_ast(ctxt: &mut BodyCtxt<'_, '_>, ast: ast::Pat) -> PatId { let pat = match &ast.kind() { @@ -30,9 +29,9 @@ impl Pat { Pat::Tuple(elems) } - ast::PatKind::Path(path) => { - let path = PathId::lower_ast_partial(ctxt.f_ctxt, path.path()); - Pat::Path(path) + ast::PatKind::Path(path_ast) => { + let path = PathId::lower_ast_partial(ctxt.f_ctxt, path_ast.path()); + Pat::Path(path, path_ast.mut_token().is_some()) } ast::PatKind::PathTuple(path_tup) => { diff --git a/crates/hir/src/span/pat.rs b/crates/hir/src/span/pat.rs index a5052d996..9811609ee 100644 --- a/crates/hir/src/span/pat.rs +++ b/crates/hir/src/span/pat.rs @@ -46,6 +46,10 @@ define_lazy_span_node!( define_lazy_span_node!( LazyPathPatSpan, ast::PathPat, + @token { + (mut_token, mut_token), + } + @node { (path, path, LazyPathSpan), } diff --git a/crates/hir/src/visitor.rs b/crates/hir/src/visitor.rs index b775fad94..158b0c6d8 100644 --- a/crates/hir/src/visitor.rs +++ b/crates/hir/src/visitor.rs @@ -1133,7 +1133,7 @@ where } } - Pat::Path(path) => { + Pat::Path(path, _) => { if let Some(path) = path.to_opt() { ctxt.with_new_ctxt( |span| span.into_path_pat().path_moved(), diff --git a/crates/parser2/src/ast/pat.rs b/crates/parser2/src/ast/pat.rs index 80b2d3582..09ba246aa 100644 --- a/crates/parser2/src/ast/pat.rs +++ b/crates/parser2/src/ast/pat.rs @@ -33,11 +33,6 @@ impl Pat { _ => unreachable!(), } } - - /// Returns the `mut` keyword if the patter is mutable. - pub fn mut_token(&self) -> Option { - support::token(self.syntax(), SK::MutKw) - } } ast_node! { @@ -91,6 +86,11 @@ impl PathPat { pub fn path(&self) -> Option { support::child(self.syntax()) } + + /// Returns the `mut` keyword if the patter is mutable. + pub fn mut_token(&self) -> Option { + support::token(self.syntax(), SK::MutKw) + } } ast_node! { @@ -175,11 +175,10 @@ pub enum PatKind { #[cfg(test)] mod tests { - use crate::{lexer::Lexer, parser::Parser}; - use wasm_bindgen_test::wasm_bindgen_test; use super::*; + use crate::{lexer::Lexer, parser::Parser}; fn parse_pat(source: &str) -> T where @@ -282,9 +281,13 @@ mod tests { assert!(matches!(field.pat().unwrap().kind(), PatKind::Path(_))); } 2 => { + let PatKind::Path(pat) = field.pat().unwrap().kind() else { + panic!("unexpected record pat"); + }; + assert!(field.name().is_none()); assert!(matches!(field.pat().unwrap().kind(), PatKind::Path(_))); - assert!(field.pat().unwrap().mut_token().is_some()); + assert!(pat.mut_token().is_some()); } _ => panic!("unexpected record pat"), } diff --git a/crates/parser2/src/parser/pat.rs b/crates/parser2/src/parser/pat.rs index dc76f2a84..3a9fe1ee1 100644 --- a/crates/parser2/src/parser/pat.rs +++ b/crates/parser2/src/parser/pat.rs @@ -1,15 +1,38 @@ +use super::{define_scope, path::PathScope, token_stream::TokenStream, Parser}; use crate::{ - parser::lit::{is_lit, LitScope}, + parser::{ + lit::{is_lit, LitScope}, + token_stream::LexicalToken, + }, SyntaxKind, }; -use super::{define_scope, path::PathScope, token_stream::TokenStream, Parser}; - pub fn parse_pat(parser: &mut Parser) -> bool { use SyntaxKind::*; parser.bump_trivias(); let checkpoint = parser.checkpoint(); - parser.bump_if(SyntaxKind::MutKw); + let has_mut = parser.bump_if(SyntaxKind::MutKw); + + let token = parser.current_token(); + if has_mut { + match token.as_ref().map(|t| t.syntax_kind()) { + Some(Underscore | Dot2 | LParen) => { + parser.error_msg_on_current_token(&format!( + "`mut` is not allowed on `{}`", + token.unwrap().text() + )); + } + + Some(kind) if is_lit(kind) => { + parser.error_msg_on_current_token(&format!( + "`mut` is not allowed on `{}`", + token.unwrap().text() + )); + } + + _ => {} + } + } let success = match parser.current_kind() { Some(Underscore) => parser.parse(WildCardPatScope::default(), Some(checkpoint)),