Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/hir-def/src/expr_store/lower.rs')
| -rw-r--r-- | crates/hir-def/src/expr_store/lower.rs | 190 |
1 files changed, 161 insertions, 29 deletions
diff --git a/crates/hir-def/src/expr_store/lower.rs b/crates/hir-def/src/expr_store/lower.rs index 8818096500..242a0b0b4f 100644 --- a/crates/hir-def/src/expr_store/lower.rs +++ b/crates/hir-def/src/expr_store/lower.rs @@ -35,8 +35,8 @@ use thin_vec::ThinVec; use tt::TextRange; use crate::{ - AdtId, BlockId, BlockLoc, DefWithBodyId, FunctionId, GenericDefId, ImplId, ItemContainerId, - MacroId, ModuleDefId, ModuleId, TraitId, TypeAliasId, UnresolvedMacro, + AdtId, BlockId, BlockLoc, ConstId, DefWithBodyId, FunctionId, GenericDefId, ImplId, + ItemContainerId, MacroId, ModuleDefId, ModuleId, TraitId, TypeAliasId, UnresolvedMacro, attrs::AttrFlags, db::DefDatabase, expr_store::{ @@ -49,9 +49,9 @@ use crate::{ }, hir::{ Array, Binding, BindingAnnotation, BindingId, BindingProblems, CaptureBy, ClosureKind, - CoroutineKind, CoroutineSource, Expr, ExprId, Item, Label, LabelId, Literal, MatchArm, - Movability, OffsetOf, Pat, PatId, RecordFieldPat, RecordLitField, RecordSpread, Statement, - generics::GenericParams, + CoroutineKind, CoroutineSource, Expr, ExprId, Item, Label, LabelId, Literal, LoopSource, + MatchArm, Movability, OffsetOf, Pat, PatId, RecordFieldPat, RecordLitField, RecordSpread, + Statement, generics::GenericParams, }, item_scope::BuiltinShadowMode, item_tree::FieldsShape, @@ -719,6 +719,10 @@ impl<'db> ExprCollector<'db> { ast::Type::DynTraitType(inner) => TypeRef::DynTrait( self.type_bounds_from_ast(inner.type_bound_list(), impl_trait_lower_fn), ), + ast::Type::PatternType(inner) => TypeRef::PatternType( + self.lower_type_ref_opt(inner.ty(), impl_trait_lower_fn), + self.collect_ty_pat_opt(inner.pat()), + ), ast::Type::MacroType(mt) => match mt.macro_call() { Some(mcall) => { let macro_ptr = AstPtr::new(&mcall); @@ -1347,7 +1351,7 @@ impl<'db> ExprCollector<'db> { (self.hygiene_id_for(label.syntax().text_range()), self.collect_label(label)) }); let body = self.collect_labelled_block_opt(label, e.loop_body()); - self.alloc_expr(Expr::Loop { body, label: label.map(|it| it.1) }, syntax_ptr) + self.alloc_expr(Expr::Loop { body, label: label.map(|it| it.1), source: LoopSource::Loop }, syntax_ptr) } ast::Expr::WhileExpr(e) => self.collect_while_loop(syntax_ptr, e), ast::Expr::ForExpr(e) => self.collect_for_loop(syntax_ptr, e), @@ -1890,9 +1894,13 @@ impl<'db> ExprCollector<'db> { }; let record_field_list = e.record_expr_field_list()?; let ellipsis = record_field_list.dotdot_token().is_some(); - // We wanted to emit an error here if `record_field_list.spread().is_some()`, - // but that's already a syntax error in rustc, so we decided not to. - // See https://github.com/rust-lang/rust-analyzer/pull/22206#discussion_r3156097370 + if let Some(spread) = record_field_list.spread() { + self.store.diagnostics.push( + ExpressionStoreDiagnostics::FruInDestructuringAssignment { + node: self.expander.in_file(AstPtr::new(&spread)), + }, + ); + } let args = record_field_list .fields() .filter_map(|f| { @@ -2125,7 +2133,10 @@ impl<'db> ExprCollector<'db> { Expr::If { condition, then_branch: body, else_branch: Some(break_expr) }, syntax_ptr, ); - self.alloc_expr(Expr::Loop { body: if_expr, label: label.map(|it| it.1) }, syntax_ptr) + self.alloc_expr( + Expr::Loop { body: if_expr, label: label.map(|it| it.1), source: LoopSource::While }, + syntax_ptr, + ) } /// Desugar `ast::ForExpr` from: `[opt_ident]: for <pat> in <head> <body>` into: @@ -2151,16 +2162,21 @@ impl<'db> ExprCollector<'db> { ) else { return self.missing_expr(); }; - let head = self.collect_expr_opt(e.iterable()); - let into_iter_fn_expr = self.alloc_expr(Expr::Path(into_iter_fn), syntax_ptr); - let iterator = self.alloc_expr( + let iterable = e.iterable(); + let syntax_ptr_iterable = iterable.as_ref().map_or(syntax_ptr, AstPtr::new); + let loop_body = e.loop_body().map(|it| it.into()); + let head = self.collect_expr_opt(iterable); + let into_iter_fn_expr = + self.alloc_expr_desugared_with_ptr(Expr::Path(into_iter_fn), syntax_ptr_iterable); + let iterator = self.alloc_expr_desugared_with_ptr( Expr::Call { callee: into_iter_fn_expr, args: Box::new([head]) }, - syntax_ptr, + syntax_ptr_iterable, ); let none_arm = MatchArm { pat: self.alloc_pat_desugared(Pat::Path(option_none)), guard: None, - expr: self.alloc_expr(Expr::Break { expr: None, label: None }, syntax_ptr), + expr: self + .alloc_expr_desugared_with_ptr(Expr::Break { expr: None, label: None }, syntax_ptr), }; let some_pat = Pat::TupleStruct { path: option_some, @@ -2173,26 +2189,26 @@ impl<'db> ExprCollector<'db> { let some_arm = MatchArm { pat: self.alloc_pat_desugared(some_pat), guard: None, - expr: self.with_opt_labeled_rib(label, |this| { - this.collect_expr_opt(e.loop_body().map(|it| it.into())) - }), + expr: self.with_opt_labeled_rib(label, |this| this.collect_expr_opt(loop_body)), }; let iter_name = self.generate_new_name(); - let iter_expr = self.alloc_expr(Expr::Path(Path::from(iter_name.clone())), syntax_ptr); - let iter_expr_mut = self.alloc_expr( + let iter_expr = self + .alloc_expr_desugared_with_ptr(Expr::Path(Path::from(iter_name.clone())), syntax_ptr); + let iter_expr_mut = self.alloc_expr_desugared_with_ptr( Expr::Ref { expr: iter_expr, rawness: Rawness::Ref, mutability: Mutability::Mut }, syntax_ptr, ); - let iter_next_fn_expr = self.alloc_expr(Expr::Path(iter_next_fn), syntax_ptr); - let iter_next_expr = self.alloc_expr( + let iter_next_fn_expr = + self.alloc_expr_desugared_with_ptr(Expr::Path(iter_next_fn), syntax_ptr); + let iter_next_expr = self.alloc_expr_desugared_with_ptr( Expr::Call { callee: iter_next_fn_expr, args: Box::new([iter_expr_mut]) }, syntax_ptr, ); - let loop_inner = self.alloc_expr( + let loop_inner = self.alloc_expr_desugared_with_ptr( Expr::Match { expr: iter_next_expr, arms: Box::new([none_arm, some_arm]) }, syntax_ptr, ); - let loop_inner = self.alloc_expr( + let loop_inner = self.alloc_expr_desugared_with_ptr( Expr::Block { id: None, statements: Box::default(), @@ -2201,8 +2217,14 @@ impl<'db> ExprCollector<'db> { }, syntax_ptr, ); - let loop_outer = self - .alloc_expr(Expr::Loop { body: loop_inner, label: label.map(|it| it.1) }, syntax_ptr); + let loop_outer = self.alloc_expr_desugared_with_ptr( + Expr::Loop { + body: loop_inner, + label: label.map(|it| it.1), + source: LoopSource::ForLoop, + }, + syntax_ptr, + ); let iter_binding = self.alloc_binding(iter_name, BindingAnnotation::Mutable, HygieneId::ROOT); let iter_pat = self.alloc_pat_desugared(Pat::Bind { id: iter_binding, subpat: None }); @@ -2562,9 +2584,9 @@ impl<'db> ExprCollector<'db> { } fn collect_extern_fn_param(&mut self, pat: Option<ast::Pat>) -> PatId { - // `extern` functions cannot have pattern-matched parameters, and furthermore, the identifiers - // in their parameters are always interpreted as bindings, even if in a normal function they - // won't be, because they would refer to a path pattern. + // parameters of functions in `extern` blocks can only be simple identifiers and wildcards. + // Furthermore, the identifiers in their parameters are always interpreted as bindings, even + // if in a normal function they won't be, because they would refer to a path pattern. let Some(pat) = pat else { return self.missing_pat() }; match &pat { @@ -2580,6 +2602,7 @@ impl<'db> ExprCollector<'db> { self.add_definition_to_binding(binding, pat); pat } + ast::Pat::WildcardPat(_) => self.alloc_pat(Pat::Wild, AstPtr::new(&pat)), _ => { self.store.diagnostics.push(ExpressionStoreDiagnostics::PatternArgInExternFn { node: self.expander.in_file(AstPtr::new(&pat)), @@ -2782,6 +2805,7 @@ impl<'db> ExprCollector<'db> { let inner = self.collect_pat_opt(inner.pat(), binding_list); Pat::Deref { inner } } + ast::Pat::NotNull(_) => Pat::NotNull, ast::Pat::ConstBlockPat(const_block_pat) => { if let Some(block) = const_block_pat.block_expr() { let expr_id = self.with_label_rib(RibKind::Constant, |this| { @@ -2907,6 +2931,114 @@ impl<'db> ExprCollector<'db> { } } + fn collect_ty_pat_opt(&mut self, pat: Option<ast::Pat>) -> PatId { + match pat { + Some(pat) => self.collect_ty_pat(pat), + None => self.missing_pat(), + } + } + + fn collect_ty_pat(&mut self, pat: ast::Pat) -> PatId { + let ptr = AstPtr::new(&pat); + match pat { + ast::Pat::NotNull(_) => self.alloc_pat(Pat::NotNull, ptr), + ast::Pat::OrPat(pat) => { + let pat = pat.pats().map(|pat| self.collect_ty_pat(pat)).collect(); + self.alloc_pat(Pat::Or(pat), ptr) + } + ast::Pat::RangePat(range_pat) => { + let start = range_pat + .start() + .map(|pat| { + self.with_fresh_binding_expr_root(|this| this.lower_ty_pat_range_side(pat)) + }) + .unwrap_or_else(|| self.lower_ty_pat_range_end(self.lang_items().RangeMin)); + let end = range_pat + .end() + .map(|pat| match range_pat.op_kind() { + Some(ast::RangeOp::Inclusive) | None => self + .with_fresh_binding_expr_root(|this| this.lower_ty_pat_range_side(pat)), + Some(ast::RangeOp::Exclusive) => self.lower_excluded_range_end(pat), + }) + .unwrap_or_else(|| self.lower_ty_pat_range_end(self.lang_items().RangeMax)); + self.alloc_pat( + Pat::Range { + start: Some(start), + end: Some(end), + range_type: ast::RangeOp::Inclusive, + }, + ptr, + ) + } + ast::Pat::MacroPat(pat) => { + let Some(call) = pat.macro_call() else { return self.missing_pat() }; + let ptr = AstPtr::new(&call); + self.collect_macro_call(call, ptr, true, |this, pat| this.collect_ty_pat_opt(pat)) + } + _ => { + // FIXME: Emit an error. + self.alloc_pat(Pat::Missing, ptr) + } + } + } + + fn lower_ty_pat_range_side(&mut self, pat: ast::Pat) -> ExprId { + let ptr = AstPtr::new(&pat); + match &pat { + ast::Pat::LiteralPat(it) => { + let Some((literal, _)) = pat_literal_to_hir(it) else { return self.missing_expr() }; + self.alloc_expr_from_pat(Expr::Literal(literal), ptr) + } + ast::Pat::ConstBlockPat(it) => { + if let Some(block) = it.block_expr() { + let expr_id = self.with_label_rib(RibKind::Constant, |this| { + this.with_binding_owner(|this| this.collect_block(block)) + }); + self.alloc_expr_from_pat(Expr::Const(expr_id), ptr) + } else { + self.missing_expr() + } + } + ast::Pat::PathPat(it) => { + let path = it + .path() + .and_then(|path| self.lower_path(path, &mut Self::impl_trait_error_allocator)); + self.alloc_expr_from_pat(path.map(Expr::Path).unwrap_or(Expr::Missing), ptr) + } + ast::Pat::IdentPat(it) if it.is_simple_ident() => { + let name = it.name().map(|nr| nr.as_name()).unwrap_or_else(Name::missing); + self.alloc_expr_from_pat(Expr::Path(name.into()), ptr) + } + _ => self.missing_expr(), // FIXME: Emit an error. + } + } + + /// When a range has no end specified (`1..` or `1..=`) or no start specified (`..5` or `..=5`), + /// we instead use a constant of the MAX/MIN of the type. + /// This way the type system does not have to handle the lack of a start/end. + fn lower_ty_pat_range_end(&mut self, lang_item: Option<ConstId>) -> ExprId { + self.with_fresh_binding_expr_root(|this| { + this.alloc_expr_desugared( + this.lang_path(lang_item).map(Expr::Path).unwrap_or(Expr::Missing), + ) + }) + } + + /// Lowers the range end of an exclusive range (`2..5`) to an inclusive range 2..=(5 - 1). + /// This way the type system doesn't have to handle the distinction between inclusive/exclusive ranges. + fn lower_excluded_range_end(&mut self, pat: ast::Pat) -> ExprId { + self.with_fresh_binding_expr_root(|this| { + let excluded_end = this.lower_ty_pat_range_side(pat); + let range_sub_path = + this.lang_path(this.lang_items().RangeSub).map(Expr::Path).unwrap_or(Expr::Missing); + let range_sub_path = this.alloc_expr_desugared(range_sub_path); + this.alloc_expr_desugared(Expr::Call { + callee: range_sub_path, + args: Box::new([excluded_end]), + }) + }) + } + // endregion: patterns /// Returns `false` (and emits diagnostics) when `owner` if `#[cfg]`d out, and `true` when |