Unnamed repository; edit this file 'description' to name the repository.
244 files changed, 7212 insertions, 2991 deletions
diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index b9427e1927..ca9e9719ad 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -56,7 +56,7 @@ jobs: - name: Install Rust toolchain run: | RUSTC_VERSION=$(cat rust-version) - rustup-toolchain-install-master ${RUSTC_VERSION} -c cargo -c rust-src -c rustfmt + rustup-toolchain-install-master ${RUSTC_VERSION} -c cargo -c rust-src -c rustfmt -c rustc-dev -c llvm-tools rustup default ${RUSTC_VERSION} # Emulate a nightly toolchain, because the toolchain installed above does not have "nightly" @@ -69,10 +69,10 @@ jobs: run: echo "::add-matcher::.github/rust.json" - name: Test - run: cargo test --features sysroot-abi -p proc-macro-srv -p proc-macro-srv-cli -p proc-macro-api -- --quiet + run: cargo test --features in-rust-tree -p proc-macro-srv -p proc-macro-srv-cli -p proc-macro-api -- --quiet - name: Check salsa dependency - run: "! (cargo tree -p proc-macro-srv-cli | grep -q salsa)" + run: "! (cargo tree -p proc-macro-srv-cli -p proc-macro-srv -p proc-macro-srv-cli -p proc-macro-api -i salsa)" rust: if: github.repository == 'rust-lang/rust-analyzer' diff --git a/Cargo.lock b/Cargo.lock index cbbeef0030..440ceb362d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1887,7 +1887,6 @@ dependencies = [ "object", "paths", "proc-macro-test", - "ra-ap-rustc_lexer", "span", "temp-dir", ] @@ -1939,7 +1938,6 @@ dependencies = [ name = "profile" version = "0.0.0" dependencies = [ - "cfg-if", "libc", "perf-event", "tikv-jemalloc-ctl", diff --git a/crates/base-db/src/lib.rs b/crates/base-db/src/lib.rs index a209a0e631..ff439119c9 100644 --- a/crates/base-db/src/lib.rs +++ b/crates/base-db/src/lib.rs @@ -7,6 +7,7 @@ extern crate rustc_driver as _; pub use salsa; pub use salsa_macros; +use span::TextSize; // FIXME: Rename this crate, base db is non descriptive mod change; @@ -49,6 +50,7 @@ macro_rules! impl_intern_key { #[salsa_macros::interned(no_lifetime, revisions = usize::MAX)] #[derive(PartialOrd, Ord)] pub struct $id { + #[returns(ref)] pub loc: $loc, } @@ -280,6 +282,8 @@ pub trait SourceDatabase: salsa::Database { fn crates_map(&self) -> Arc<CratesMap>; fn nonce_and_revision(&self) -> (Nonce, salsa::Revision); + + fn line_column(&self, file: FileId, offset: TextSize) -> Result<(u32, u32), ()>; } static NEXT_NONCE: AtomicUsize = AtomicUsize::new(0); diff --git a/crates/cfg/src/lib.rs b/crates/cfg/src/lib.rs index 3e3d67cb4a..5923849d62 100644 --- a/crates/cfg/src/lib.rs +++ b/crates/cfg/src/lib.rs @@ -222,7 +222,7 @@ impl fmt::Display for CfgDiff { for (i, atom) in self.disable.iter().enumerate() { let sep = match i { 0 => "", - _ if i == self.enable.len() - 1 => " and ", + _ if i == self.disable.len() - 1 => " and ", _ => ", ", }; f.write_str(sep)?; diff --git a/crates/cfg/src/tests.rs b/crates/cfg/src/tests.rs index bfc9220a05..45cba042b3 100644 --- a/crates/cfg/src/tests.rs +++ b/crates/cfg/src/tests.rs @@ -194,6 +194,10 @@ fn hints() { check_enable_hints("#![cfg(test)]", &opts, &[]); check_enable_hints("#![cfg(not(test))]", &opts, &["disable test"]); + + opts.insert_atom(Symbol::intern("a")); + opts.insert_atom(Symbol::intern("b")); + check_enable_hints("#![cfg(all(not(a), not(b)))]", &opts, &["disable a and b"]); } /// Tests that we don't suggest hints for cfgs that express an inconsistent formula. diff --git a/crates/hir-def/src/db.rs b/crates/hir-def/src/db.rs index 11e5c54246..1776b7c84b 100644 --- a/crates/hir-def/src/db.rs +++ b/crates/hir-def/src/db.rs @@ -7,8 +7,7 @@ use hir_expand::{ use triomphe::Arc; use crate::{ - AssocItemId, AttrDefId, Macro2Loc, MacroExpander, MacroId, MacroRulesLoc, MacroRulesLocFlags, - TraitId, + AssocItemId, AttrDefId, MacroExpander, MacroId, MacroRulesLocFlags, TraitId, attrs::AttrFlags, item_tree::{ItemTree, file_item_tree}, nameres::crate_def_map, @@ -76,12 +75,13 @@ fn macro_def(db: &dyn DefDatabase, id: MacroId) -> MacroDefId { MacroExpander::BuiltInAttr(it) => MacroDefKind::BuiltInAttr(in_file, it), MacroExpander::BuiltInDerive(it) => MacroDefKind::BuiltInDerive(in_file, it), MacroExpander::BuiltInEager(it) => MacroDefKind::BuiltInEager(in_file, it), + MacroExpander::UnimplementedBuiltIn => MacroDefKind::UnimplementedBuiltIn(in_file), } }; match id { MacroId::Macro2Id(it) => { - let loc: Macro2Loc = it.lookup(db); + let loc = it.lookup(db); MacroDefId { krate: loc.container.krate(db), @@ -92,7 +92,7 @@ fn macro_def(db: &dyn DefDatabase, id: MacroId) -> MacroDefId { } } MacroId::MacroRulesId(it) => { - let loc: MacroRulesLoc = it.lookup(db); + let loc = it.lookup(db); MacroDefId { krate: loc.container.krate(db), diff --git a/crates/hir-def/src/expr_store.rs b/crates/hir-def/src/expr_store.rs index 4dc7267231..8768413ce5 100644 --- a/crates/hir-def/src/expr_store.rs +++ b/crates/hir-def/src/expr_store.rs @@ -332,6 +332,7 @@ pub enum ExpressionStoreDiagnostics { AwaitOutsideOfAsync { node: InFile<AstPtr<ast::AwaitExpr>>, location: String }, UndeclaredLabel { node: InFile<AstPtr<ast::Lifetime>>, name: Name }, PatternArgInExternFn { node: InFile<AstPtr<ast::Pat>> }, + FruInDestructuringAssignment { node: InFile<AstPtr<ast::Expr>> }, } impl ExpressionStoreBuilder { @@ -616,7 +617,7 @@ impl ExpressionStore { visitor.on_expr_opt(*end); } Pat::Lit(expr) | Pat::ConstBlock(expr) | Pat::Expr(expr) => visitor.on_expr(*expr), - Pat::Path(_) | Pat::Wild | Pat::Missing | Pat::Rest => {} + Pat::Path(_) | Pat::Wild | Pat::Missing | Pat::Rest | Pat::NotNull => {} &Pat::Bind { subpat, id: _ } => visitor.on_pat_opt(subpat), Pat::Or(args) | Pat::Tuple { args, ellipsis: _ } => visitor.on_pats(args), Pat::TupleStruct { args, ellipsis: _, path } => { @@ -719,7 +720,7 @@ impl ExpressionStore { } visitor.on_expr_opt(*tail); } - Expr::Loop { body, label: _ } => visitor.on_expr(*body), + Expr::Loop { body, label: _, source: _ } => visitor.on_expr(*body), Expr::Call { callee, args } => { visitor.on_expr(*callee); visitor.on_exprs(args); @@ -855,6 +856,10 @@ impl ExpressionStore { pub fn visit_type_ref_children(&self, type_ref: TypeRefId, mut visitor: impl StoreVisitor) { match &self[type_ref] { TypeRef::Never | TypeRef::Placeholder | TypeRef::TypeParam(_) | TypeRef::Error => {} + &TypeRef::PatternType(ty, pat) => { + visitor.on_type(ty); + visitor.on_pat(pat) + } TypeRef::Tuple(types) => visitor.on_types(types), TypeRef::Path(path) => visitor.on_path(path), TypeRef::RawPtr(inner, _) | TypeRef::Slice(inner) => visitor.on_type(*inner), diff --git a/crates/hir-def/src/expr_store/body.rs b/crates/hir-def/src/expr_store/body.rs index 2fb47e59c5..01423d5109 100644 --- a/crates/hir-def/src/expr_store/body.rs +++ b/crates/hir-def/src/expr_store/body.rs @@ -9,7 +9,7 @@ use syntax::ast; use triomphe::Arc; use crate::{ - DefWithBodyId, HasModule, + DefWithBodyId, ExpressionStoreOwnerId, HasModule, db::DefDatabase, expr_store::{ ExpressionStore, ExpressionStoreSourceMap, SelfParamPtr, lower::lower_body, pretty, @@ -160,12 +160,12 @@ impl Body { pub fn pretty_print_pat( &self, db: &dyn DefDatabase, - owner: DefWithBodyId, + owner: ExpressionStoreOwnerId, pat: PatId, oneline: bool, edition: Edition, ) -> String { - pretty::print_pat_hir(db, self, owner.into(), pat, oneline, edition) + pretty::print_pat_hir(db, self, owner, pat, oneline, edition) } } 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 diff --git a/crates/hir-def/src/expr_store/pretty.rs b/crates/hir-def/src/expr_store/pretty.rs index 34cedbd728..293adfc9bd 100644 --- a/crates/hir-def/src/expr_store/pretty.rs +++ b/crates/hir-def/src/expr_store/pretty.rs @@ -567,7 +567,7 @@ impl Printer<'_> { w!(self, " = "); self.print_expr_in(prec, *expr); } - Expr::Loop { body, label } => { + Expr::Loop { body, label, source: _ } => { if let Some(lbl) = label { w!(self, "{}: ", self.store[*lbl].name.display(self.db, self.edition)); } @@ -906,6 +906,7 @@ impl Printer<'_> { Pat::Missing => w!(self, "�"), Pat::Rest => w!(self, ".."), Pat::Wild => w!(self, "_"), + Pat::NotNull => w!(self, "!null"), Pat::Tuple { args, ellipsis } => { w!(self, "("); for (i, pat) in args.iter().enumerate() { @@ -1134,6 +1135,7 @@ impl Printer<'_> { LangItemTarget::TypeAliasId(it) => write_name!(it), LangItemTarget::TraitId(it) => write_name!(it), LangItemTarget::EnumVariantId(it) => write_name!(it), + LangItemTarget::ConstId(it) => write_name!(it), } if let Some(s) = s { @@ -1346,6 +1348,11 @@ impl Printer<'_> { w!(self, "dyn "); self.print_type_bounds(bounds); } + TypeRef::PatternType(ty, pat) => { + self.print_type_ref(*ty); + w!(self, " is "); + self.print_pat(*pat); + } } } diff --git a/crates/hir-def/src/expr_store/scope.rs b/crates/hir-def/src/expr_store/scope.rs index ddb8285137..7a2c8dc3ff 100644 --- a/crates/hir-def/src/expr_store/scope.rs +++ b/crates/hir-def/src/expr_store/scope.rs @@ -350,7 +350,7 @@ fn compute_expr_scopes( Expr::Unsafe { id, statements, tail } => { handle_block(*id, statements, *tail, None, scopes, scope, const_scope); } - Expr::Loop { body: body_expr, label } => { + Expr::Loop { body: body_expr, label, source: _ } => { let mut scope = scopes.new_labeled_scope(*scope, make_label(*label)); compute_expr_scopes(scopes, *body_expr, &mut scope, const_scope); } diff --git a/crates/hir-def/src/expr_store/tests/body/block.rs b/crates/hir-def/src/expr_store/tests/body/block.rs index 71fcced2d8..906c4835c7 100644 --- a/crates/hir-def/src/expr_store/tests/body/block.rs +++ b/crates/hir-def/src/expr_store/tests/body/block.rs @@ -199,6 +199,11 @@ fn f() { 4401, ), ), + containing_module_inside_def_map: None, + name_or_empty: Name { + symbol: "", + ctx: (), + }, }"#]], ); } diff --git a/crates/hir-def/src/find_path.rs b/crates/hir-def/src/find_path.rs index 2a1b7f7cb0..6aaf8a674e 100644 --- a/crates/hir-def/src/find_path.rs +++ b/crates/hir-def/src/find_path.rs @@ -142,9 +142,7 @@ fn find_path_inner(ctx: &FindPathCtx<'_>, item: ItemInNs, max_len: usize) -> Opt // - if the item is an enum variant, refer to it via the enum let loc = variant.lookup(ctx.db); if let Some(mut path) = find_path_inner(ctx, ItemInNs::Types(loc.parent.into()), max_len) { - path.push_segment( - loc.parent.enum_variants(ctx.db).variants[loc.index as usize].1.clone(), - ); + path.push_segment(loc.name.clone()); return Some(path); } // If this doesn't work, it seems we have no way of referring to the diff --git a/crates/hir-def/src/hir.rs b/crates/hir-def/src/hir.rs index 6eba859264..2f7724c72e 100644 --- a/crates/hir-def/src/hir.rs +++ b/crates/hir-def/src/hir.rs @@ -224,6 +224,7 @@ pub enum Expr { Loop { body: ExprId, label: Option<LabelId>, + source: LoopSource, }, Call { callee: ExprId, @@ -389,6 +390,17 @@ impl Expr { } } +/// The loop type that yielded an `Expr::Loop`. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum LoopSource { + /// A `loop { .. }` loop. + Loop, + /// A `while _ { .. }` loop. + While, + /// A `for _ in _ { .. }` loop. + ForLoop, +} + #[derive(Debug, Clone, PartialEq, Eq)] pub struct OffsetOf { pub container: TypeRefId, @@ -717,6 +729,7 @@ pub enum Pat { Deref { inner: PatId, }, + NotNull, ConstBlock(ExprId), /// An expression inside a pattern. That can only occur inside assignments. /// @@ -734,7 +747,8 @@ impl Pat { | Pat::Wild | Pat::Missing | Pat::Rest - | Pat::Expr(_) => {} + | Pat::Expr(_) + | Pat::NotNull => {} Pat::Bind { subpat, .. } => { subpat.iter().copied().for_each(f); } diff --git a/crates/hir-def/src/hir/type_ref.rs b/crates/hir-def/src/hir/type_ref.rs index 43e29c1f5f..6cd8377b5f 100644 --- a/crates/hir-def/src/hir/type_ref.rs +++ b/crates/hir-def/src/hir/type_ref.rs @@ -9,7 +9,7 @@ use thin_vec::ThinVec; use crate::{ LifetimeParamId, TypeParamId, expr_store::{ExpressionStore, path::Path}, - hir::ExprId, + hir::{ExprId, PatId}, }; #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] @@ -139,6 +139,7 @@ pub enum TypeRef { ImplTrait(ThinVec<TypeBound>), DynTrait(ThinVec<TypeBound>), TypeParam(TypeParamId), + PatternType(TypeRefId, PatId), Error, } diff --git a/crates/hir-def/src/import_map.rs b/crates/hir-def/src/import_map.rs index ba077b1b2e..f8be211bf9 100644 --- a/crates/hir-def/src/import_map.rs +++ b/crates/hir-def/src/import_map.rs @@ -12,7 +12,7 @@ use span::Edition; use stdx::format_to; use crate::{ - AssocItemId, AttrDefId, Complete, FxIndexMap, ModuleDefId, ModuleId, TraitId, + AdtId, AssocItemId, AttrDefId, Complete, EnumId, FxIndexMap, ModuleDefId, ModuleId, TraitId, attrs::AttrFlags, db::DefDatabase, item_scope::{ImportOrExternCrate, ItemInNs}, @@ -208,7 +208,8 @@ impl ImportMap { complete: do_not_complete, }; - if let Some(ModuleDefId::TraitId(tr)) = item.as_module_def_id() { + let module_def = item.as_module_def_id(); + if let Some(ModuleDefId::TraitId(tr)) = module_def { Self::collect_trait_assoc_items( db, &mut map, @@ -216,6 +217,8 @@ impl ImportMap { matches!(item, ItemInNs::Types(_)), &import_info, ); + } else if let Some(ModuleDefId::AdtId(AdtId::EnumId(enum_))) = module_def { + Self::collect_enum_variants(db, &mut map, enum_, &import_info); } let (infos, _) = @@ -224,7 +227,7 @@ impl ImportMap { infos.push(import_info); // If we've just added a module, descend into it. - if let Some(ModuleDefId::ModuleId(mod_id)) = item.as_module_def_id() { + if let Some(ModuleDefId::ModuleId(mod_id)) = module_def { worklist.push(mod_id); } } @@ -234,6 +237,33 @@ impl ImportMap { map } + fn collect_enum_variants( + db: &dyn DefDatabase, + map: &mut ImportMapIndex, + enum_: EnumId, + enum_import_info: &ImportInfo, + ) { + let _p = tracing::info_span!("collect_enum_variants").entered(); + for (variant_name, &(variant, _)) in &enum_.enum_variants(db).variants { + let attr_id = variant.into(); + let attrs = AttrFlags::query(db, attr_id); + let do_not_complete = Complete::extract(false, attrs); + let variant_info = ImportInfo { + container: enum_import_info.container, + name: variant_name.clone(), + is_doc_hidden: attrs.contains(AttrFlags::IS_DOC_HIDDEN), + is_unstable: attrs.contains(AttrFlags::IS_UNSTABLE), + complete: do_not_complete, + }; + + let (infos, _) = map + .entry(ItemInNs::Types(variant.into())) + .or_insert_with(|| (SmallVec::new(), IsTraitAssocItem::No)); + infos.reserve_exact(1); + infos.push(variant_info); + } + } + fn collect_trait_assoc_items( db: &dyn DefDatabase, map: &mut ImportMapIndex, diff --git a/crates/hir-def/src/lang_item.rs b/crates/hir-def/src/lang_item.rs index c2cbb9eda7..f9d5a843bd 100644 --- a/crates/hir-def/src/lang_item.rs +++ b/crates/hir-def/src/lang_item.rs @@ -7,7 +7,7 @@ use intern::{Symbol, sym}; use stdx::impl_from; use crate::{ - AdtId, AssocItemId, AttrDefId, Crate, EnumId, EnumVariantId, FunctionId, ImplId, + AdtId, AssocItemId, AttrDefId, ConstId, Crate, EnumId, EnumVariantId, FunctionId, ImplId, ItemContainerId, MacroId, ModuleDefId, StaticId, StructId, TraitId, TypeAliasId, UnionId, attrs::AttrFlags, db::DefDatabase, @@ -25,10 +25,11 @@ pub enum LangItemTarget { TypeAliasId(TypeAliasId), TraitId(TraitId), EnumVariantId(EnumVariantId), + ConstId(ConstId), } impl_from!( - EnumId, FunctionId, ImplId, StaticId, StructId, UnionId, TypeAliasId, TraitId, EnumVariantId for LangItemTarget + EnumId, FunctionId, ImplId, StaticId, StructId, UnionId, TypeAliasId, TraitId, EnumVariantId, ConstId for LangItemTarget ); /// Salsa query. This will look for lang items in a specific crate. @@ -51,7 +52,7 @@ pub fn crate_lang_items(db: &dyn DefDatabase, krate: Crate) -> Option<Box<LangIt match assoc { AssocItemId::FunctionId(f) => lang_items.collect_lang_item(db, f), AssocItemId::TypeAliasId(t) => lang_items.collect_lang_item(db, t), - AssocItemId::ConstId(_) => (), + AssocItemId::ConstId(c) => lang_items.collect_lang_item(db, c), } } } @@ -68,13 +69,13 @@ pub fn crate_lang_items(db: &dyn DefDatabase, krate: Crate) -> Option<Box<LangIt AssocItemId::TypeAliasId(alias) => { lang_items.collect_lang_item(db, alias) } - AssocItemId::ConstId(_) => {} + AssocItemId::ConstId(c) => lang_items.collect_lang_item(db, c), } }); } ModuleDefId::AdtId(AdtId::EnumId(e)) => { lang_items.collect_lang_item(db, e); - e.enum_variants(db).variants.iter().for_each(|&(id, _, _)| { + e.enum_variants(db).variants.values().for_each(|&(id, _)| { lang_items.collect_lang_item(db, id); }); } @@ -93,6 +94,7 @@ pub fn crate_lang_items(db: &dyn DefDatabase, krate: Crate) -> Option<Box<LangIt ModuleDefId::TypeAliasId(t) => { lang_items.collect_lang_item(db, t); } + ModuleDefId::ConstId(c) => lang_items.collect_lang_item(db, c), _ => {} } } @@ -646,6 +648,10 @@ language_item_table! { LangItems => FieldBase, sym::field_base, TypeAliasId; FieldType, sym::field_type, TypeAliasId; + RangeMin, sym::RangeMin, ConstId; + RangeMax, sym::RangeMax, ConstId; + RangeSub, sym::RangeSub, FunctionId; + @non_lang_core_traits: core::default, Default; core::fmt, Debug; diff --git a/crates/hir-def/src/lib.rs b/crates/hir-def/src/lib.rs index 3aaf89102f..a1bb82e7f2 100644 --- a/crates/hir-def/src/lib.rs +++ b/crates/hir-def/src/lib.rs @@ -48,7 +48,7 @@ pub mod find_path; pub mod import_map; pub mod visibility; -use intern::Interned; +use intern::{Interned, sym}; use rustc_abi::ExternAbi; use thin_vec::ThinVec; @@ -298,7 +298,7 @@ impl EnumId { pub fn enum_variants_with_diagnostics( self, db: &dyn DefDatabase, - ) -> &(EnumVariants, Option<ThinVec<InactiveEnumVariantCode>>) { + ) -> &(EnumVariants, ThinVec<InactiveEnumVariantCode>) { EnumVariants::of(db, self) } } @@ -367,20 +367,35 @@ impl ExternBlockId { } } -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +#[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct EnumVariantLoc { pub id: AstId<ast::Variant>, pub parent: EnumId, - pub index: u32, + pub name: Name, } impl_intern!(EnumVariantId, EnumVariantLoc); impl_loc!(EnumVariantLoc, id: Variant, parent: EnumId); +impl EnumVariantLoc { + pub fn index(&self, db: &dyn DefDatabase) -> usize { + self.parent + .enum_variants(db) + .variants + .get_full(&self.name) + .expect("parent enum should include this variant") + .0 + } +} + impl EnumVariantId { pub fn fields(self, db: &dyn DefDatabase) -> &VariantFields { VariantFields::of(db, self.into()) } + pub fn index(self, db: &dyn DefDatabase) -> usize { + self.loc(db).index(db) + } + pub fn fields_with_source_map( self, db: &dyn DefDatabase, @@ -427,6 +442,7 @@ pub enum MacroExpander { BuiltInAttr(BuiltinAttrExpander), BuiltInDerive(BuiltinDeriveExpander), BuiltInEager(EagerExpander), + UnimplementedBuiltIn, } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] @@ -457,6 +473,11 @@ pub struct ModuleIdLt<'db> { /// `BlockId` of that block expression. If `None`, this module is part of the crate-level /// `DefMap` of `krate`. pub block: Option<BlockId>, + /// The parent module of this module, or `None` if this is the root module inside the def + /// map (including for block def maps). + pub containing_module_inside_def_map: Option<ModuleIdLt<'db>>, + /// The name of this module, or [`sym::__empty`] for the root module. + name_or_empty: Name, } pub type ModuleId = ModuleIdLt<'static>; @@ -502,21 +523,23 @@ impl ModuleId { } pub fn name(self, db: &dyn DefDatabase) -> Option<Name> { - let def_map = self.def_map(db); - let parent = def_map[self].parent?; - def_map[parent].children.iter().find_map(|(name, module_id)| { - if *module_id == self { Some(name.clone()) } else { None } - }) + let name = self.name_or_empty(db); + if *name.symbol() == sym::__empty { None } else { Some(name) } } /// Returns the module containing `self`, either the parent `mod`, or the module (or block) containing /// the block, if `self` corresponds to a block expression. pub fn containing_module(self, db: &dyn DefDatabase) -> Option<ModuleId> { - self.def_map(db).containing_module(self) + self.containing_module_inside_def_map(db) + .or_else(|| self.block(db).map(|block| block.loc(db).module)) + .map(|module| { + // SAFETY: Not sure. + unsafe { module.to_static() } + }) } pub fn is_block_module(self, db: &dyn DefDatabase) -> bool { - self.block(db).is_some() && self.def_map(db).root_module_id() == self + self.block(db).is_some() && self.containing_module_inside_def_map(db).is_none() } } @@ -837,6 +860,12 @@ impl From<VariantId> for ExpressionStoreOwnerId { } } +impl From<ImplId> for ExpressionStoreOwnerId { + fn from(id: ImplId) -> Self { + ExpressionStoreOwnerId::Signature(id.into()) + } +} + impl GenericDefId { pub fn file_id_and_params_of( self, diff --git a/crates/hir-def/src/nameres.rs b/crates/hir-def/src/nameres.rs index 88e3408a33..0c7e7a3ca0 100644 --- a/crates/hir-def/src/nameres.rs +++ b/crates/hir-def/src/nameres.rs @@ -66,7 +66,7 @@ use hir_expand::{ EditionedFileId, ErasedAstId, HirFileId, InFile, MacroCallId, mod_path::ModPath, name::Name, proc_macro::ProcMacroKind, }; -use intern::Symbol; +use intern::{Symbol, sym}; use itertools::Itertools; use rustc_hash::FxHashMap; use span::{Edition, FileAstId, FileId, ROOT_ERASED_FILE_AST_ID}; @@ -427,7 +427,7 @@ pub(crate) fn crate_local_def_map(db: &dyn DefDatabase, crate_id: Crate) -> DefM #[salsa_macros::tracked(returns(ref))] pub fn block_def_map(db: &dyn DefDatabase, block_id: BlockId) -> DefMap { - let BlockLoc { ast_id, module } = block_id.lookup(db); + let BlockLoc { ast_id, module } = *block_id.lookup(db); let visibility = Visibility::Module(module, VisibilityExplicitness::Implicit); let module_data = @@ -465,7 +465,16 @@ impl DefMap { block: Option<BlockInfo>, ) -> DefMap { let mut modules = ModulesMap::new(); - let root = unsafe { ModuleIdLt::new(db, krate, block.map(|it| it.block)).to_static() }; + let root = unsafe { + ModuleIdLt::new( + db, + krate, + block.map(|it| it.block), + None, + Name::new_symbol_root(sym::__empty), + ) + .to_static() + }; modules.insert(root, module_data); DefMap { @@ -841,6 +850,7 @@ pub(crate) fn macro_styles_from_id(db: &dyn DefDatabase, macro_id: MacroId) -> M MacroExpander::BuiltIn(_) | MacroExpander::BuiltInEager(_) => MacroCallStyles::FN_LIKE, MacroExpander::BuiltInAttr(_) => MacroCallStyles::ATTR, MacroExpander::BuiltInDerive(_) => MacroCallStyles::DERIVE, + MacroExpander::UnimplementedBuiltIn => MacroCallStyles::all(), // Unknown. } } diff --git a/crates/hir-def/src/nameres/assoc.rs b/crates/hir-def/src/nameres/assoc.rs index b1d554738f..7b5b39cb08 100644 --- a/crates/hir-def/src/nameres/assoc.rs +++ b/crates/hir-def/src/nameres/assoc.rs @@ -50,7 +50,7 @@ impl TraitItems { db: &dyn DefDatabase, tr: TraitId, ) -> (TraitItems, DefDiagnostics) { - let ItemLoc { container: module_id, id: ast_id } = tr.lookup(db); + let ItemLoc { container: module_id, id: ast_id } = *tr.lookup(db); let ast_id_map = db.ast_id_map(ast_id.file_id); let source = ast_id.with_value(ast_id_map.get(ast_id.value)).to_node(db); if source.eq_token().is_some() { @@ -115,7 +115,7 @@ impl ImplItems { #[salsa::tracked(returns(ref))] pub fn of(db: &dyn DefDatabase, id: ImplId) -> (ImplItems, DefDiagnostics) { let _p = tracing::info_span!("impl_items_with_diagnostics_query").entered(); - let ItemLoc { container: module_id, id: ast_id } = id.lookup(db); + let ItemLoc { container: module_id, id: ast_id } = *id.lookup(db); let collector = AssocItemCollector::new(db, module_id, ItemContainerId::ImplId(id), ast_id.file_id); diff --git a/crates/hir-def/src/nameres/collector.rs b/crates/hir-def/src/nameres/collector.rs index 5ef51dbb2c..a916cda730 100644 --- a/crates/hir-def/src/nameres/collector.rs +++ b/crates/hir-def/src/nameres/collector.rs @@ -1027,7 +1027,7 @@ impl<'db> DefCollector<'db> { .enum_variants(self.db) .variants .iter() - .map(|&(variant, ref name, _)| { + .map(|(name, &(variant, _))| { let res = PerNs::both(variant.into(), variant.into(), vis, None); (Some(name.clone()), res) }) @@ -2428,6 +2428,8 @@ impl ModCollector<'_, '_> { self.def_collector.db, self.def_collector.def_map.krate, self.def_collector.def_map.block_id(), + Some(self.module_id), + name.clone(), ) .to_static() }; @@ -2560,7 +2562,7 @@ impl ModCollector<'_, '_> { .def_map .diagnostics .push(DefDiagnostic::unimplemented_builtin_macro(self.module_id, f_ast_id)); - return; + MacroExpander::UnimplementedBuiltIn } } } else { @@ -2639,7 +2641,7 @@ impl ModCollector<'_, '_> { .def_map .diagnostics .push(DefDiagnostic::unimplemented_builtin_macro(self.module_id, f_ast_id)); - return; + MacroExpander::UnimplementedBuiltIn } } else { // Case 2: normal `macro` diff --git a/crates/hir-def/src/nameres/path_resolution.rs b/crates/hir-def/src/nameres/path_resolution.rs index cf33cecf5f..54fd30fe6b 100644 --- a/crates/hir-def/src/nameres/path_resolution.rs +++ b/crates/hir-def/src/nameres/path_resolution.rs @@ -519,12 +519,8 @@ impl DefMap { // enum variant cov_mark::hit!(can_import_enum_variant); - let res = e - .enum_variants(db) - .variants - .iter() - .find(|(_, name, _)| name == segment) - .map(|&(variant, _, shape)| match shape { + let res = e.enum_variants(db).variants.get(segment).map(|&(variant, shape)| { + match shape { FieldsShape::Record => { PerNs::types(variant.into(), Visibility::Public, None) } @@ -534,7 +530,8 @@ impl DefMap { Visibility::Public, None, ), - }); + } + }); // FIXME: Need to filter visibility here and below? Not sure. return match res { Some(res) => { diff --git a/crates/hir-def/src/resolver.rs b/crates/hir-def/src/resolver.rs index 8320221ffc..0062e6c170 100644 --- a/crates/hir-def/src/resolver.rs +++ b/crates/hir-def/src/resolver.rs @@ -194,7 +194,8 @@ impl<'db> Resolver<'db> { LangItemTarget::TraitId(it) => TypeNs::TraitId(it), LangItemTarget::FunctionId(_) | LangItemTarget::ImplId(_) - | LangItemTarget::StaticId(_) => return None, + | LangItemTarget::StaticId(_) + | LangItemTarget::ConstId(_) => return None, }; return Some(( type_ns, @@ -337,6 +338,7 @@ impl<'db> Resolver<'db> { LangItemTarget::StaticId(it) => ValueNs::StaticId(it), LangItemTarget::StructId(it) => ValueNs::StructId(it), LangItemTarget::EnumVariantId(it) => ValueNs::EnumVariantId(it), + LangItemTarget::ConstId(it) => ValueNs::ConstId(it), LangItemTarget::UnionId(_) | LangItemTarget::ImplId(_) | LangItemTarget::TypeAliasId(_) @@ -356,7 +358,8 @@ impl<'db> Resolver<'db> { LangItemTarget::TraitId(it) => TypeNs::TraitId(it), LangItemTarget::FunctionId(_) | LangItemTarget::ImplId(_) - | LangItemTarget::StaticId(_) => return None, + | LangItemTarget::StaticId(_) + | LangItemTarget::ConstId(_) => return None, }; // Remaining segments start from 0 because lang paths have no segments other than the remaining. return Some(( diff --git a/crates/hir-def/src/signatures.rs b/crates/hir-def/src/signatures.rs index e58befae20..e9307a1255 100644 --- a/crates/hir-def/src/signatures.rs +++ b/crates/hir-def/src/signatures.rs @@ -1,6 +1,6 @@ //! Item signature IR definitions -use std::{cell::LazyCell, ops::Not as _}; +use std::cell::LazyCell; use bitflags::bitflags; use cfg::{CfgExpr, CfgOptions}; @@ -19,8 +19,9 @@ use thin_vec::ThinVec; use triomphe::Arc; use crate::{ - ConstId, EnumId, EnumVariantId, EnumVariantLoc, ExternBlockId, FunctionId, HasModule, ImplId, - ItemContainerId, ModuleId, StaticId, StructId, TraitId, TypeAliasId, UnionId, VariantId, + ConstId, EnumId, EnumVariantId, EnumVariantLoc, ExternBlockId, FunctionId, FxIndexMap, + HasModule, ImplId, ItemContainerId, ModuleId, StaticId, StructId, TraitId, TypeAliasId, + UnionId, VariantId, attrs::AttrFlags, db::DefDatabase, expr_store::{ @@ -1055,7 +1056,7 @@ pub struct InactiveEnumVariantCode { #[derive(Debug, Clone, PartialEq, Eq)] pub struct EnumVariants { - pub variants: Box<[(EnumVariantId, Name, FieldsShape)]>, + pub variants: FxIndexMap<Name, (EnumVariantId, FieldsShape)>, } #[salsa::tracked] @@ -1064,30 +1065,31 @@ impl EnumVariants { pub(crate) fn of( db: &dyn DefDatabase, e: EnumId, - ) -> (EnumVariants, Option<ThinVec<InactiveEnumVariantCode>>) { + ) -> (EnumVariants, ThinVec<InactiveEnumVariantCode>) { let loc = e.lookup(db); let source = loc.source(db); let ast_id_map = db.ast_id_map(source.file_id); let mut diagnostics = ThinVec::new(); let cfg_options = loc.container.krate(db).cfg_options(db); - let mut index = 0; let Some(variants) = source.value.variant_list() else { - return (EnumVariants { variants: Box::default() }, None); + return (EnumVariants { variants: FxIndexMap::default() }, ThinVec::new()); }; - let variants = variants + let mut variants = variants .variants() .filter_map(|variant| { let ast_id = ast_id_map.ast_id(&variant); match AttrFlags::is_cfg_enabled_for(&variant, cfg_options) { Ok(()) => { - let enum_variant = - EnumVariantLoc { id: source.with_value(ast_id), parent: e, index } - .intern(db); - index += 1; let name = as_name_opt(variant.name()); + let enum_variant = EnumVariantLoc { + id: source.with_value(ast_id), + parent: e, + name: name.clone(), + } + .intern(db); let shape = adt_shape(variant.kind()); - Some((enum_variant, name, shape)) + Some((name, (enum_variant, shape))) } Err(cfg) => { diagnostics.push(InactiveEnumVariantCode { @@ -1099,34 +1101,38 @@ impl EnumVariants { } } }) - .collect(); + .collect::<FxIndexMap<_, _>>(); + variants.shrink_to_fit(); + diagnostics.shrink_to_fit(); - (EnumVariants { variants }, diagnostics.is_empty().not().then_some(diagnostics)) + (EnumVariants { variants }, diagnostics) } } impl EnumVariants { pub fn variant(&self, name: &Name) -> Option<EnumVariantId> { - self.variants.iter().find_map(|(v, n, _)| if n == name { Some(*v) } else { None }) + self.variants.get(name).map(|&(id, _)| id) } pub fn variant_name_by_id(&self, variant_id: EnumVariantId) -> Option<Name> { self.variants .iter() - .find_map(|(id, name, _)| if *id == variant_id { Some(name.clone()) } else { None }) + .find_map(|(name, (id, _))| if *id == variant_id { Some(name.clone()) } else { None }) } // [Adopted from rustc](https://github.com/rust-lang/rust/blob/bd53aa3bf7a24a70d763182303bd75e5fc51a9af/compiler/rustc_middle/src/ty/adt.rs#L446-L448) pub fn is_payload_free(&self, db: &dyn DefDatabase) -> bool { - self.variants.iter().all(|&(v, _, _)| { + self.variants.values().all(|&(v, shape)| { // The condition check order is slightly modified from rustc // to improve performance by early returning with relatively fast checks - let variant = v.fields(db); - if !variant.fields().is_empty() { - return false; - } + // The outer if condition is whether this variant has const ctor or not - if !matches!(variant.shape, FieldsShape::Unit) { + if !matches!(shape, FieldsShape::Unit) { + let variant = v.fields(db); + if !variant.fields().is_empty() { + return false; + } + let body = Body::of(db, v.into()); // A variant with explicit discriminant if !matches!(body[body.root_expr()], crate::hir::Expr::Missing) { diff --git a/crates/hir-def/src/test_db.rs b/crates/hir-def/src/test_db.rs index b854d2aa21..913b99223d 100644 --- a/crates/hir-def/src/test_db.rs +++ b/crates/hir-def/src/test_db.rs @@ -142,6 +142,10 @@ impl SourceDatabase for TestDB { fn nonce_and_revision(&self) -> (Nonce, salsa::Revision) { (self.nonce, salsa::plumbing::ZalsaDatabase::zalsa(self).current_revision()) } + + fn line_column(&self, _file: FileId, _offset: syntax::TextSize) -> Result<(u32, u32), ()> { + Err(()) + } } impl TestDB { diff --git a/crates/hir-expand/src/builtin/derive_macro.rs b/crates/hir-expand/src/builtin/derive_macro.rs index 8b031e3647..c4da558fda 100644 --- a/crates/hir-expand/src/builtin/derive_macro.rs +++ b/crates/hir-expand/src/builtin/derive_macro.rs @@ -22,7 +22,7 @@ use crate::{ use syntax::{ ast::{ self, AstNode, FieldList, HasAttrs, HasGenericArgs, HasGenericParams, HasModuleItem, - HasName, HasTypeBounds, make, + HasName, HasTypeBounds, }, syntax_editor::{GetOrCreateWhereClause, SyntaxEditor}, }; @@ -1435,6 +1435,7 @@ fn coerce_pointee_expand( param_name: &str, replacement: &str, ) -> bool { + let make = editor.make(); return match ty { ast::Type::ArrayType(ty) => ty .ty() @@ -1462,8 +1463,8 @@ fn coerce_pointee_expand( if path.as_single_name_ref().is_some_and(|name| name.text() == param_name) { editor.replace( path.syntax(), - make::path_from_segments( - [make::path_segment(make::name_ref(replacement))], + make.path_from_segments( + [make.path_segment(make.name_ref(replacement))], false, ) .syntax(), @@ -1494,6 +1495,9 @@ fn coerce_pointee_expand( ast::Type::TupleType(ty) => any_long(ty.fields(), |ty| { substitute_type_in_bound(editor, ty, param_name, replacement) }), + ast::Type::PatternType(ty) => ty + .ty() + .is_some_and(|ty| substitute_type_in_bound(editor, ty, param_name, replacement)), ast::Type::InferType(_) | ast::Type::MacroType(_) | ast::Type::NeverType(_) => false, }; diff --git a/crates/hir-expand/src/builtin/fn_macro.rs b/crates/hir-expand/src/builtin/fn_macro.rs index eb7175c686..9181ad88b6 100644 --- a/crates/hir-expand/src/builtin/fn_macro.rs +++ b/crates/hir-expand/src/builtin/fn_macro.rs @@ -137,6 +137,7 @@ register_builtin! { (const_format_args, ConstFormatArgs) => format_args_expand, (format_args_nl, FormatArgsNl) => format_args_nl_expand, (quote, Quote) => quote_expand, + (pattern_type, PatternType) => pattern_type_expand, EagerExpander: (compile_error, CompileError) => compile_error_expand, @@ -994,3 +995,15 @@ fn unescape_str(s: &str) -> Cow<'_, str> { Cow::Borrowed(s) } } + +fn pattern_type_expand( + _db: &dyn ExpandDatabase, + _arg_id: MacroCallId, + tt: &tt::TopSubtree, + call_site: Span, +) -> ExpandResult<tt::TopSubtree> { + let mut tt = tt.clone(); + tt.set_top_subtree_delimiter_kind(tt::DelimiterKind::Invisible); + let pound = mk_pound(call_site); + ExpandResult::ok(quote! {call_site => builtin #pound pattern_type ( #tt ) }) +} diff --git a/crates/hir-expand/src/db.rs b/crates/hir-expand/src/db.rs index beae6e843e..513115684f 100644 --- a/crates/hir-expand/src/db.rs +++ b/crates/hir-expand/src/db.rs @@ -43,6 +43,7 @@ pub enum TokenExpander<'db> { BuiltInAttr(BuiltinAttrExpander), /// `derive(Copy)` and such. BuiltInDerive(BuiltinDeriveExpander), + UnimplementedBuiltIn, /// The thing we love the most here in rust-analyzer -- procedural macros. ProcMacro(CustomProcMacroExpander), } @@ -149,8 +150,8 @@ fn syntax_context(db: &dyn ExpandDatabase, file: HirFileId, edition: Edition) -> match file { HirFileId::FileId(_) => SyntaxContext::root(edition), HirFileId::MacroFile(m) => { - let kind = m.loc(db).kind; - db.macro_arg_considering_derives(m, &kind).2.ctx + let kind = &m.loc(db).kind; + db.macro_arg_considering_derives(m, kind).2.ctx } } } @@ -311,6 +312,7 @@ pub fn expand_speculative( it.expand(db, actual_macro_call, &tt, span).map_err(Into::into) } MacroDefKind::BuiltInAttr(_, it) => it.expand(db, actual_macro_call, &tt, span), + MacroDefKind::UnimplementedBuiltIn(_) => expand_unimplemented_builtin_macro(span), }; let expand_to = loc.expand_to(); @@ -335,6 +337,13 @@ pub fn expand_speculative( Some((node.syntax_node(), token)) } +fn expand_unimplemented_builtin_macro(span: Span) -> ExpandResult<tt::TopSubtree> { + ExpandResult::new( + tt::TopSubtree::empty(tt::DelimSpan::from_single(span)), + ExpandError::other(span, "this built-in macro is not implemented"), + ) +} + #[salsa::tracked(lru = 1024, returns(ref))] fn ast_id_map(db: &dyn ExpandDatabase, file_id: HirFileId) -> AstIdMap { AstIdMap::from_source(&db.parse_or_expand(file_id)) @@ -396,7 +405,7 @@ pub(crate) fn parse_with_map( /// This resolves the [MacroCallId] to check if it is a derive macro if so get the [macro_arg] for the derive. /// Other wise return the [macro_arg] for the macro_call_id. /// -/// This is not connected to the database so it does not cached the result. However, the inner [macro_arg] query is +/// This is not connected to the database so it does not cache the result. However, the inner [macro_arg] query is #[allow(deprecated)] // we are macro_arg_considering_derives fn macro_arg_considering_derives<'db>( db: &'db dyn ExpandDatabase, @@ -538,15 +547,16 @@ impl<'db> TokenExpander<'db> { MacroDefKind::BuiltInDerive(_, expander) => TokenExpander::BuiltInDerive(expander), MacroDefKind::BuiltInEager(_, expander) => TokenExpander::BuiltInEager(expander), MacroDefKind::ProcMacro(_, expander, _) => TokenExpander::ProcMacro(expander), + MacroDefKind::UnimplementedBuiltIn(_) => TokenExpander::UnimplementedBuiltIn, } } } -fn macro_expand( - db: &dyn ExpandDatabase, +fn macro_expand<'db>( + db: &'db dyn ExpandDatabase, macro_call_id: MacroCallId, - loc: MacroCallLoc, -) -> ExpandResult<(Cow<'_, tt::TopSubtree>, MatchedArmIndex)> { + loc: &MacroCallLoc, +) -> ExpandResult<(Cow<'db, tt::TopSubtree>, MatchedArmIndex)> { let _p = tracing::info_span!("macro_expand").entered(); let (ExpandResult { value: (tt, matched_arm), err }, span) = match loc.def.kind { @@ -572,6 +582,9 @@ fn macro_expand( MacroDefKind::BuiltInDerive(_, it) => { it.expand(db, macro_call_id, arg, span).map_err(Into::into).zip_val(None) } + MacroDefKind::UnimplementedBuiltIn(_) => { + expand_unimplemented_builtin_macro(span).zip_val(None) + } MacroDefKind::BuiltInEager(_, it) => { // This might look a bit odd, but we do not expand the inputs to eager macros here. // Eager macros inputs are expanded, well, eagerly when we collect the macro calls. diff --git a/crates/hir-expand/src/eager.rs b/crates/hir-expand/src/eager.rs index a19f58709b..f8a560834a 100644 --- a/crates/hir-expand/src/eager.rs +++ b/crates/hir-expand/src/eager.rs @@ -246,7 +246,8 @@ fn eager_macro_recur( | MacroDefKind::BuiltIn(..) | MacroDefKind::BuiltInAttr(..) | MacroDefKind::BuiltInDerive(..) - | MacroDefKind::ProcMacro(..) => { + | MacroDefKind::ProcMacro(..) + | MacroDefKind::UnimplementedBuiltIn(..) => { let ExpandResult { value: (parse, tm), err } = lazy_expand( db, &def, diff --git a/crates/hir-expand/src/lib.rs b/crates/hir-expand/src/lib.rs index 0850d6156d..9c5714be2d 100644 --- a/crates/hir-expand/src/lib.rs +++ b/crates/hir-expand/src/lib.rs @@ -81,7 +81,7 @@ macro_rules! impl_intern_lookup { impl $crate::Lookup for $id { type Database = dyn $db; type Data = $loc; - fn lookup(&self, db: &Self::Database) -> Self::Data { + fn lookup<'db>(&self, db: &'db Self::Database) -> &'db Self::Data { self.loc(db) } } @@ -98,7 +98,7 @@ pub trait Intern { pub trait Lookup { type Database: ?Sized; type Data; - fn lookup(&self, db: &Self::Database) -> Self::Data; + fn lookup<'db>(&self, db: &'db Self::Database) -> &'db Self::Data; } impl_intern_lookup!(ExpandDatabase, MacroCallId, MacroCallLoc); @@ -249,6 +249,7 @@ pub enum MacroDefKind { BuiltInAttr(AstId<ast::Macro>, BuiltinAttrExpander), BuiltInDerive(AstId<ast::Macro>, BuiltinDeriveExpander), BuiltInEager(AstId<ast::Macro>, EagerExpander), + UnimplementedBuiltIn(AstId<ast::Macro>), ProcMacro(AstId<ast::Fn>, CustomProcMacroExpander, ProcMacroKind), } @@ -265,7 +266,8 @@ impl MacroDefKind { | MacroDefKind::BuiltInAttr(id, _) | MacroDefKind::BuiltInDerive(id, _) | MacroDefKind::BuiltInEager(id, _) - | MacroDefKind::Declarative(id, ..) => id.erase(), + | MacroDefKind::Declarative(id, ..) + | MacroDefKind::UnimplementedBuiltIn(id) => id.erase(), } } } @@ -500,6 +502,7 @@ impl MacroCallId { MacroDefKind::ProcMacro(_, _, ProcMacroKind::Attr) => MacroKind::Attr, MacroDefKind::ProcMacro(_, _, ProcMacroKind::Bang) => MacroKind::ProcMacro, MacroDefKind::BuiltInAttr(..) => MacroKind::AttrBuiltIn, + MacroDefKind::UnimplementedBuiltIn(..) => MacroKind::Declarative, } } @@ -551,7 +554,8 @@ impl MacroDefId { | MacroDefKind::BuiltIn(id, _) | MacroDefKind::BuiltInAttr(id, _) | MacroDefKind::BuiltInDerive(id, _) - | MacroDefKind::BuiltInEager(id, _) => { + | MacroDefKind::BuiltInEager(id, _) + | MacroDefKind::UnimplementedBuiltIn(id) => { id.with_value(db.ast_id_map(id.file_id).get(id.value).text_range()) } MacroDefKind::ProcMacro(id, _, _) => { @@ -567,7 +571,8 @@ impl MacroDefId { | MacroDefKind::BuiltIn(id, _) | MacroDefKind::BuiltInAttr(id, _) | MacroDefKind::BuiltInDerive(id, _) - | MacroDefKind::BuiltInEager(id, _) => Either::Left(id), + | MacroDefKind::BuiltInEager(id, _) + | MacroDefKind::UnimplementedBuiltIn(id) => Either::Left(id), } } @@ -577,9 +582,9 @@ impl MacroDefId { pub fn is_attribute(&self) -> bool { match self.kind { - MacroDefKind::BuiltInAttr(..) | MacroDefKind::ProcMacro(_, _, ProcMacroKind::Attr) => { - true - } + MacroDefKind::BuiltInAttr(..) + | MacroDefKind::ProcMacro(_, _, ProcMacroKind::Attr) + | MacroDefKind::UnimplementedBuiltIn(_) => true, MacroDefKind::Declarative(_, styles) => styles.contains(MacroCallStyles::ATTR), _ => false, } @@ -588,7 +593,8 @@ impl MacroDefId { pub fn is_derive(&self) -> bool { match self.kind { MacroDefKind::BuiltInDerive(..) - | MacroDefKind::ProcMacro(_, _, ProcMacroKind::CustomDerive) => true, + | MacroDefKind::ProcMacro(_, _, ProcMacroKind::CustomDerive) + | MacroDefKind::UnimplementedBuiltIn(_) => true, MacroDefKind::Declarative(_, styles) => styles.contains(MacroCallStyles::DERIVE), _ => false, } @@ -601,6 +607,7 @@ impl MacroDefId { | MacroDefKind::ProcMacro(_, _, ProcMacroKind::Bang) | MacroDefKind::BuiltInEager(..) | MacroDefKind::Declarative(..) + | MacroDefKind::UnimplementedBuiltIn(_) ) } @@ -714,24 +721,27 @@ impl MacroCallKind { /// - fn_like! {}, it spans the path and token tree /// - #\[derive], it spans the `#[derive(...)]` attribute and the annotated item /// - #\[attr], it spans the `#[attr(...)]` attribute and the annotated item - pub fn original_call_range_with_input(self, db: &dyn ExpandDatabase) -> FileRange { - let mut kind = self; + pub fn original_call_range_with_input(&self, db: &dyn ExpandDatabase) -> FileRange { + let get_range = |kind: &_| match kind { + MacroCallKind::FnLike { ast_id, .. } => ast_id.erase(), + MacroCallKind::Derive { ast_id, .. } => ast_id.erase(), + MacroCallKind::Attr { ast_id, .. } => ast_id.erase(), + }; + + let mut ast_id = get_range(self); + let mut file_id = self.file_id(); let file_id = loop { - match kind.file_id() { + match file_id { HirFileId::MacroFile(file) => { - kind = file.loc(db).kind; + let kind = &file.loc(db).kind; + ast_id = get_range(kind); + file_id = kind.file_id(); } HirFileId::FileId(file_id) => break file_id, } }; - let range = match kind { - MacroCallKind::FnLike { ast_id, .. } => ast_id.to_ptr(db).text_range(), - MacroCallKind::Derive { ast_id, .. } => ast_id.to_ptr(db).text_range(), - MacroCallKind::Attr { ast_id, .. } => ast_id.to_ptr(db).text_range(), - }; - - FileRange { range, file_id } + FileRange { range: ast_id.to_ptr(db).text_range(), file_id } } /// Returns the original file range that best describes the location of this macro call. @@ -739,18 +749,8 @@ impl MacroCallKind { /// Here we try to roughly match what rustc does to improve diagnostics: fn-like macros /// get the macro path (rustc shows the whole `ast::MacroCall`), attribute macros get the /// attribute's range, and derives get only the specific derive that is being referred to. - pub fn original_call_range(self, db: &dyn ExpandDatabase, krate: Crate) -> FileRange { - let mut kind = self; - let file_id = loop { - match kind.file_id() { - HirFileId::MacroFile(file) => { - kind = file.loc(db).kind; - } - HirFileId::FileId(file_id) => break file_id, - } - }; - - let range = match kind { + pub fn original_call_range(&self, db: &dyn ExpandDatabase, krate: Crate) -> FileRange { + let get_range = |kind: &_| match kind { MacroCallKind::FnLike { ast_id, .. } => { let node = ast_id.to_node(db); node.path() @@ -761,11 +761,24 @@ impl MacroCallKind { } MacroCallKind::Derive { ast_id, derive_attr_index, .. } => { // FIXME: should be the range of the macro name, not the whole derive - derive_attr_index.find_attr_range(db, krate, ast_id).1.syntax().text_range() + derive_attr_index.find_attr_range(db, krate, *ast_id).1.syntax().text_range() } // FIXME: handle `cfg_attr` MacroCallKind::Attr { ast_id, censored_attr_ids: attr_ids, .. } => { - attr_ids.invoc_attr().find_attr_range(db, krate, ast_id).1.syntax().text_range() + attr_ids.invoc_attr().find_attr_range(db, krate, *ast_id).1.syntax().text_range() + } + }; + + let mut range = get_range(self); + let mut file_id = self.file_id(); + let file_id = loop { + match file_id { + HirFileId::MacroFile(file) => { + let kind = &file.loc(db).kind; + range = get_range(kind); + file_id = kind.file_id(); + } + HirFileId::FileId(file_id) => break file_id, } }; @@ -797,7 +810,7 @@ pub struct ExpansionInfo<'db> { arg: InFile<Option<SyntaxNode>>, exp_map: &'db ExpansionSpanMap, arg_map: SpanMap<'db>, - loc: MacroCallLoc, + loc: &'db MacroCallLoc, } impl<'db> ExpansionInfo<'db> { @@ -1056,6 +1069,7 @@ intern::impl_internable!(ModPath); #[salsa_macros::interned(no_lifetime, debug, revisions = usize::MAX)] #[doc(alias = "MacroFileId")] pub struct MacroCallId { + #[returns(ref)] pub loc: MacroCallLoc, } diff --git a/crates/hir-expand/src/prettify_macro_expansion_.rs b/crates/hir-expand/src/prettify_macro_expansion_.rs index 79e6f0f5b7..687bd1a714 100644 --- a/crates/hir-expand/src/prettify_macro_expansion_.rs +++ b/crates/hir-expand/src/prettify_macro_expansion_.rs @@ -2,8 +2,7 @@ use base_db::Crate; use rustc_hash::FxHashMap; -use syntax::NodeOrToken; -use syntax::{SyntaxNode, ast::make}; +use syntax::SyntaxNode; use crate::{db::ExpandDatabase, span_map::ExpansionSpanMap}; @@ -22,7 +21,7 @@ pub fn prettify_macro_expansion( let mut syntax_ctx_id_to_dollar_crate_replacement = FxHashMap::default(); syntax_bridge::prettify_macro_expansion::prettify_macro_expansion( syn, - &mut |dollar_crate| { + &mut |dollar_crate, make| { let ctx = span_map.span_at(dollar_crate.text_range().start() + span_offset).ctx; let replacement = syntax_ctx_id_to_dollar_crate_replacement.entry(ctx).or_insert_with(|| { @@ -38,13 +37,13 @@ pub fn prettify_macro_expansion( // is inserted, and also understandable to the user. // Lastly, if nothing else found, resort to leaving `$crate`. if target_crate_id == macro_def_crate { - make::tokens::crate_kw() + make.token(syntax::SyntaxKind::CRATE_KW) } else if let Some(dep) = target_crate.dependencies.iter().find(|dep| dep.crate_id == macro_def_crate) { - make::tokens::ident(dep.name.as_str()) + make.ident(dep.name.as_str()) } else if let Some(crate_name) = ¯o_def_crate.extra_data(db).display_name { - make::tokens::ident(crate_name.crate_name().as_str()) + make.ident(crate_name.crate_name().as_str()) } else { dollar_crate.clone() } @@ -53,12 +52,7 @@ pub fn prettify_macro_expansion( // The parent may have many children, and looking for the token may yield incorrect results. return None; } - // We need to `clone_subtree()` but rowan doesn't provide such operation for tokens. - let parent = replacement.parent().unwrap().clone_subtree().clone_for_update(); - parent - .children_with_tokens() - .filter_map(NodeOrToken::into_token) - .find(|it| it.kind() == replacement.kind()) + Some(replacement.clone()) }, |_| (), ) diff --git a/crates/hir-ty/src/builtin_derive.rs b/crates/hir-ty/src/builtin_derive.rs index fe60fbc510..928e3da6e8 100644 --- a/crates/hir-ty/src/builtin_derive.rs +++ b/crates/hir-ty/src/builtin_derive.rs @@ -193,7 +193,7 @@ pub fn predicates(db: &dyn HirDatabase, impl_: BuiltinDeriveImplId) -> GenericPr else { // Malformed derive. return GenericPredicates::from_explicit_own_predicates(StoredEarlyBinder::bind( - Clauses::default().store(), + Clauses::empty(interner).store(), )); }; let duplicated_bounds = @@ -305,7 +305,7 @@ fn simple_trait_predicates<'db>( loc.trait_, ), AdtId::EnumId(id) => { - for &(variant_id, _, _) in &id.enum_variants(interner.db).variants { + for &(variant_id, _) in id.enum_variants(interner.db).variants.values() { extend_assoc_type_bounds( interner, &mut assoc_type_bounds, diff --git a/crates/hir-ty/src/consteval.rs b/crates/hir-ty/src/consteval.rs index e1d6cec594..2c43feeb3b 100644 --- a/crates/hir-ty/src/consteval.rs +++ b/crates/hir-ty/src/consteval.rs @@ -5,7 +5,7 @@ mod tests; use base_db::Crate; use hir_def::{ - ConstId, EnumVariantId, ExpressionStoreOwnerId, GenericDefId, HasModule, StaticId, + ConstId, EnumVariantId, ExpressionStoreOwnerId, HasModule, StaticId, attrs::AttrFlags, expr_store::{Body, ExpressionStore, HygieneId, path::Path}, hir::{Expr, ExprId, Literal}, @@ -303,6 +303,7 @@ pub(crate) enum CreateConstError<'db> { UsedForbiddenParam, ResolveToNonConst, DoesNotResolve, + ConstHasGenerics, UnderscoreExpr, TypeMismatch { #[expect(unused, reason = "will need this for diagnostics")] @@ -321,9 +322,11 @@ pub(crate) fn path_to_const<'a, 'db>( let resolution = resolver .resolve_path_in_value_ns_fully(db, path, HygieneId::ROOT) .ok_or(CreateConstError::DoesNotResolve)?; + let no_generics = |def| crate::generics::generics(db, def).has_no_params(); let konst = match resolution { - ValueNs::ConstId(id) => GeneralConstId::ConstId(id), + ValueNs::ConstId(id) if no_generics(id.into()) => GeneralConstId::ConstId(id), ValueNs::StaticId(id) => GeneralConstId::StaticId(id), + ValueNs::ConstId(_) => return Err(CreateConstError::ConstHasGenerics), ValueNs::GenericParam(param) => { let index = generics().type_or_const_param_idx(param.into()); if forbid_params_after.is_some_and(|forbid_after| index >= forbid_after) { @@ -363,7 +366,10 @@ pub(crate) fn create_anon_const<'a, 'db>( Expr::Path(path) if let konst = path_to_const(interner.db, resolver, generics, forbid_params_after, path) - && !matches!(konst, Err(CreateConstError::DoesNotResolve)) => + && !matches!( + konst, + Err(CreateConstError::DoesNotResolve | CreateConstError::ConstHasGenerics) + ) => { konst } @@ -400,12 +406,10 @@ pub(crate) fn const_eval_discriminant_variant( let body = Body::of(db, def); let loc = variant_id.lookup(db); if matches!(body[body.root_expr()], Expr::Missing) { - let prev_idx = loc.index.checked_sub(1); + let prev_idx = loc.index(db).checked_sub(1); let value = match prev_idx { Some(prev_idx) => { - 1 + db.const_eval_discriminant( - loc.parent.enum_variants(db).variants[prev_idx as usize].0, - )? + 1 + db.const_eval_discriminant(loc.parent.enum_variants(db).variants[prev_idx].0)? } _ => 0, }; @@ -418,8 +422,11 @@ pub(crate) fn const_eval_discriminant_variant( let mir_body = db.monomorphized_mir_body( def.into(), GenericArgs::empty(interner).store(), - ParamEnvAndCrate { param_env: db.trait_environment(def.into()), krate: def.krate(db) } - .store(), + ParamEnvAndCrate { + param_env: db.trait_environment(def.generic_def(db)), + krate: def.krate(db), + } + .store(), )?; let c = interpret_mir(db, mir_body, false, None)?.0?; let c = if is_signed { allocation_as_isize(c) } else { allocation_as_usize(c) as i128 }; @@ -455,12 +462,8 @@ pub(crate) fn const_eval<'db>( let body = db.monomorphized_mir_body( def.into(), subst, - ParamEnvAndCrate { - param_env: db - .trait_environment(ExpressionStoreOwnerId::from(GenericDefId::from(def))), - krate: def.krate(db), - } - .store(), + ParamEnvAndCrate { param_env: db.trait_environment(def.into()), krate: def.krate(db) } + .store(), )?; let c = interpret_mir(db, body, false, trait_env.as_ref().map(|env| env.as_ref()))?.0?; Ok(c.store()) @@ -499,7 +502,7 @@ pub(crate) fn anon_const_eval<'db>( def.into(), subst, ParamEnvAndCrate { - param_env: db.trait_environment(def.loc(db).owner), + param_env: db.trait_environment(def.loc(db).owner.generic_def(db)), krate: def.krate(db), } .store(), @@ -537,12 +540,8 @@ pub(crate) fn const_eval_static<'db>( let body = db.monomorphized_mir_body( def.into(), GenericArgs::empty(interner).store(), - ParamEnvAndCrate { - param_env: db - .trait_environment(ExpressionStoreOwnerId::from(GenericDefId::from(def))), - krate: def.krate(db), - } - .store(), + ParamEnvAndCrate { param_env: db.trait_environment(def.into()), krate: def.krate(db) } + .store(), )?; let c = interpret_mir(db, body, false, None)?.0?; Ok(c.store()) diff --git a/crates/hir-ty/src/consteval/tests.rs b/crates/hir-ty/src/consteval/tests.rs index f158661069..5421b97db2 100644 --- a/crates/hir-ty/src/consteval/tests.rs +++ b/crates/hir-ty/src/consteval/tests.rs @@ -287,6 +287,9 @@ fn floating_point_casts() { check_number(r#"const GOAL: i8 = (0./0.) as i8"#, 0); check_number(r#"const GOAL: i8 = (1./0.) as i8"#, 127); check_number(r#"const GOAL: i8 = (-1./0.) as i8"#, -128); + check_number(r#"const GOAL: u8 = (1./0.) as u8"#, 255); + check_number(r#"const GOAL: u8 = 256.0f32 as u8"#, 255); + check_number(r#"const GOAL: u16 = 1e10f32 as u16"#, 65535); check_number(r#"const GOAL: i64 = 1e18f64 as f32 as i64"#, 999999984306749440); } diff --git a/crates/hir-ty/src/db.rs b/crates/hir-ty/src/db.rs index c24a5b943d..511ab85610 100644 --- a/crates/hir-ty/src/db.rs +++ b/crates/hir-ty/src/db.rs @@ -247,7 +247,7 @@ pub trait HirDatabase: DefDatabase + std::fmt::Debug { #[salsa::invoke(crate::lower::trait_environment)] #[salsa::transparent] - fn trait_environment<'db>(&'db self, def: ExpressionStoreOwnerId) -> ParamEnv<'db>; + fn trait_environment<'db>(&'db self, def: GenericDefId) -> ParamEnv<'db>; #[salsa::invoke(crate::lower::generic_defaults_with_diagnostics)] #[salsa::transparent] diff --git a/crates/hir-ty/src/diagnostics/decl_check.rs b/crates/hir-ty/src/diagnostics/decl_check.rs index 76fbb66b06..ace3616339 100644 --- a/crates/hir-ty/src/diagnostics/decl_check.rs +++ b/crates/hir-ty/src/diagnostics/decl_check.rs @@ -504,15 +504,15 @@ impl<'a> DeclValidator<'a> { fn validate_enum_variants(&mut self, enum_id: EnumId) { let data = enum_id.enum_variants(self.db); - for (variant_id, _, _) in data.variants.iter() { + for (variant_id, _) in data.variants.values() { self.validate_enum_variant_fields(*variant_id); } let edition = self.edition(enum_id); let mut enum_variants_replacements = data .variants - .iter() - .filter_map(|(_, name, _)| { + .keys() + .filter_map(|name| { to_camel_case(&name.display_no_db(edition).to_smolstr()).map(|new_name| { Replacement { current_name: name.clone(), diff --git a/crates/hir-ty/src/diagnostics/expr.rs b/crates/hir-ty/src/diagnostics/expr.rs index 760ebd27e0..be4de11ceb 100644 --- a/crates/hir-ty/src/diagnostics/expr.rs +++ b/crates/hir-ty/src/diagnostics/expr.rs @@ -83,7 +83,7 @@ impl<'db> BodyValidationDiagnostic<'db> { let _p = tracing::info_span!("BodyValidationDiagnostic::collect").entered(); let infer = InferenceResult::of(db, owner); let body = Body::of(db, owner); - let env = db.trait_environment(owner.into()); + let env = db.trait_environment(owner.generic_def(db)); let interner = DbInterner::new_with(db, owner.krate(db)); let infcx = interner.infer_ctxt().build(TypingMode::typeck_for_body(interner, owner.into())); diff --git a/crates/hir-ty/src/diagnostics/match_check.rs b/crates/hir-ty/src/diagnostics/match_check.rs index 8356329d96..14bff65220 100644 --- a/crates/hir-ty/src/diagnostics/match_check.rs +++ b/crates/hir-ty/src/diagnostics/match_check.rs @@ -330,13 +330,7 @@ impl<'db> HirDisplay<'db> for Pat<'db> { match variant { VariantId::EnumVariantId(v) => { let loc = v.lookup(f.db); - write!( - f, - "{}", - loc.parent.enum_variants(f.db).variants[loc.index as usize] - .1 - .display(f.db, f.edition()) - )?; + write!(f, "{}", loc.name.display(f.db, f.edition()))?; } VariantId::StructId(s) => write!( f, diff --git a/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs b/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs index 46959aaa5a..984ac1abfb 100644 --- a/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs +++ b/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs @@ -49,8 +49,7 @@ pub(crate) struct EnumVariantContiguousIndex(usize); impl EnumVariantContiguousIndex { fn from_enum_variant_id(db: &dyn HirDatabase, target_evid: EnumVariantId) -> Self { // Find the index of this variant in the list of variants. - use hir_def::Lookup; - let i = target_evid.lookup(db).index as usize; + let i = target_evid.index(db); EnumVariantContiguousIndex(i) } @@ -438,7 +437,7 @@ impl<'a, 'db> PatCx for MatchCheckCtx<'a, 'db> { ConstructorSet::NoConstructors } else { let mut variants = IndexVec::with_capacity(enum_data.variants.len()); - for &(variant, _, _) in enum_data.variants.iter() { + for &(variant, _) in enum_data.variants.values() { let is_uninhabited = is_enum_variant_uninhabited_from( cx.infcx, variant, subst, cx.module, self.env, ); diff --git a/crates/hir-ty/src/diagnostics/unsafe_check.rs b/crates/hir-ty/src/diagnostics/unsafe_check.rs index c37a194d47..3c69c6a44b 100644 --- a/crates/hir-ty/src/diagnostics/unsafe_check.rs +++ b/crates/hir-ty/src/diagnostics/unsafe_check.rs @@ -255,9 +255,8 @@ impl<'db> UnsafeVisitor<'db> { | Pat::Box { .. } | Pat::Deref { .. } | Pat::Expr(..) - | Pat::ConstBlock(..) => { - self.on_unsafe_op(current.into(), UnsafetyReason::UnionField) - } + | Pat::ConstBlock(..) + | Pat::NotNull => self.on_unsafe_op(current.into(), UnsafetyReason::UnionField), // `Or` only wraps other patterns, and `Missing`/`Wild` do not constitute a read. Pat::Missing | Pat::Rest | Pat::Wild | Pat::Or(_) => {} } diff --git a/crates/hir-ty/src/display.rs b/crates/hir-ty/src/display.rs index bc726b652f..8edb178cd7 100644 --- a/crates/hir-ty/src/display.rs +++ b/crates/hir-ty/src/display.rs @@ -15,7 +15,7 @@ use hir_def::{ expr_store::{ExpressionStore, path::Path}, find_path::{self, PrefixKind}, hir::{ - ClosureKind as HirClosureKind, CoroutineKind, + ClosureKind as HirClosureKind, CoroutineKind, PatId, generics::{GenericParams, TypeOrConstParamData, TypeParamProvenance, WherePredicate}, }, item_scope::ItemInNs, @@ -780,7 +780,7 @@ fn render_const_scalar<'db>( memory_map: &MemoryMap<'db>, ty: Ty<'db>, ) -> Result { - let param_env = ParamEnv::empty(); + let param_env = ParamEnv::empty(f.interner); let infcx = f.interner.infer_ctxt().build(TypingMode::PostAnalysis); let ty = infcx.at(&ObligationCause::dummy(), param_env).deeply_normalize(ty).unwrap_or(ty); render_const_scalar_inner(f, b, memory_map, ty, param_env) @@ -968,9 +968,7 @@ fn render_const_scalar_inner<'db>( s.fields(f.db), f, field_types, - f.db.trait_environment(ExpressionStoreOwnerId::from(GenericDefId::from( - def, - ))), + f.db.trait_environment(def.into()), &layout, args, b, @@ -990,21 +988,13 @@ fn render_const_scalar_inner<'db>( return f.write_str("<failed-to-detect-variant>"); }; let loc = var_id.lookup(f.db); - write!( - f, - "{}", - loc.parent.enum_variants(f.db).variants[loc.index as usize] - .1 - .display(f.db, f.edition()) - )?; + write!(f, "{}", loc.name.display(f.db, f.edition()))?; let field_types = f.db.field_types(var_id.into()); render_variant_after_name( var_id.fields(f.db), f, field_types, - f.db.trait_environment(ExpressionStoreOwnerId::from(GenericDefId::from( - def, - ))), + f.db.trait_environment(def.into()), var_layout, args, b, @@ -1065,7 +1055,7 @@ fn render_const_scalar_from_valtree<'db>( ty: Ty<'db>, valtree: ValTree<'db>, ) -> Result { - let param_env = ParamEnv::empty(); + let param_env = ParamEnv::empty(f.interner); let infcx = f.interner.infer_ctxt().build(TypingMode::PostAnalysis); let ty = infcx.at(&ObligationCause::dummy(), param_env).deeply_normalize(ty).unwrap_or(ty); render_const_scalar_from_valtree_inner(f, ty, valtree, param_env) @@ -1362,13 +1352,7 @@ impl<'db> HirDisplay<'db> for Ty<'db> { } CallableDefId::EnumVariantId(e) => { let loc = e.lookup(db); - write!( - f, - "{}", - loc.parent.enum_variants(db).variants[loc.index as usize] - .1 - .display(db, f.edition()) - )? + write!(f, "{}", loc.name.display(db, f.edition()))? } }; f.end_location_link(); @@ -2362,39 +2346,58 @@ pub fn write_visibility<'db>( } pub trait HirDisplayWithExpressionStore<'db> { - fn hir_fmt(&self, f: &mut HirFormatter<'_, 'db>, store: &ExpressionStore) -> Result; + fn hir_fmt( + &self, + f: &mut HirFormatter<'_, 'db>, + owner: ExpressionStoreOwnerId, + store: &ExpressionStore, + ) -> Result; } impl<'db, T: ?Sized + HirDisplayWithExpressionStore<'db>> HirDisplayWithExpressionStore<'db> for &'_ T { - fn hir_fmt(&self, f: &mut HirFormatter<'_, 'db>, store: &ExpressionStore) -> Result { - T::hir_fmt(&**self, f, store) + fn hir_fmt( + &self, + f: &mut HirFormatter<'_, 'db>, + owner: ExpressionStoreOwnerId, + store: &ExpressionStore, + ) -> Result { + T::hir_fmt(&**self, f, owner, store) } } pub fn hir_display_with_store<'a, 'db, T: HirDisplayWithExpressionStore<'db> + 'a>( value: T, + owner: ExpressionStoreOwnerId, store: &'a ExpressionStore, ) -> impl HirDisplay<'db> + 'a { - ExpressionStoreAdapter(value, store) + ExpressionStoreAdapter(value, owner, store) } -struct ExpressionStoreAdapter<'a, T>(T, &'a ExpressionStore); +struct ExpressionStoreAdapter<'a, T>(T, ExpressionStoreOwnerId, &'a ExpressionStore); impl<'a, T> ExpressionStoreAdapter<'a, T> { - fn wrap(store: &'a ExpressionStore) -> impl Fn(T) -> ExpressionStoreAdapter<'a, T> { - move |value| ExpressionStoreAdapter(value, store) + fn wrap( + owner: ExpressionStoreOwnerId, + store: &'a ExpressionStore, + ) -> impl Fn(T) -> ExpressionStoreAdapter<'a, T> { + move |value| ExpressionStoreAdapter(value, owner, store) } } impl<'db, T: HirDisplayWithExpressionStore<'db>> HirDisplay<'db> for ExpressionStoreAdapter<'_, T> { fn hir_fmt(&self, f: &mut HirFormatter<'_, 'db>) -> Result { - T::hir_fmt(&self.0, f, self.1) + T::hir_fmt(&self.0, f, self.1, self.2) } } impl<'db> HirDisplayWithExpressionStore<'db> for LifetimeRefId { - fn hir_fmt(&self, f: &mut HirFormatter<'_, 'db>, store: &ExpressionStore) -> Result { + fn hir_fmt( + &self, + f: &mut HirFormatter<'_, 'db>, + _owner: ExpressionStoreOwnerId, + store: &ExpressionStore, + ) -> Result { match &store[*self] { LifetimeRef::Named(name) => write!(f, "{}", name.display(f.db, f.edition())), LifetimeRef::Static => write!(f, "'static"), @@ -2413,7 +2416,12 @@ impl<'db> HirDisplayWithExpressionStore<'db> for LifetimeRefId { } impl<'db> HirDisplayWithExpressionStore<'db> for TypeRefId { - fn hir_fmt(&self, f: &mut HirFormatter<'_, 'db>, store: &ExpressionStore) -> Result { + fn hir_fmt( + &self, + f: &mut HirFormatter<'_, 'db>, + owner: ExpressionStoreOwnerId, + store: &ExpressionStore, + ) -> Result { match &store[*self] { TypeRef::Never => write!(f, "!")?, TypeRef::TypeParam(param) => { @@ -2438,7 +2446,7 @@ impl<'db> HirDisplayWithExpressionStore<'db> for TypeRefId { } _ => None, }) - .map(ExpressionStoreAdapter::wrap(store)), + .map(ExpressionStoreAdapter::wrap(owner, store)), " + ", )?; } @@ -2447,20 +2455,20 @@ impl<'db> HirDisplayWithExpressionStore<'db> for TypeRefId { TypeRef::Placeholder => write!(f, "_")?, TypeRef::Tuple(elems) => { write!(f, "(")?; - f.write_joined(elems.iter().map(ExpressionStoreAdapter::wrap(store)), ", ")?; + f.write_joined(elems.iter().map(ExpressionStoreAdapter::wrap(owner, store)), ", ")?; if elems.len() == 1 { write!(f, ",")?; } write!(f, ")")?; } - TypeRef::Path(path) => path.hir_fmt(f, store)?, + TypeRef::Path(path) => path.hir_fmt(f, owner, store)?, TypeRef::RawPtr(inner, mutability) => { let mutability = match mutability { hir_def::type_ref::Mutability::Shared => "*const ", hir_def::type_ref::Mutability::Mut => "*mut ", }; write!(f, "{mutability}")?; - inner.hir_fmt(f, store)?; + inner.hir_fmt(f, owner, store)?; } TypeRef::Reference(ref_) => { let mutability = match ref_.mutability { @@ -2469,22 +2477,22 @@ impl<'db> HirDisplayWithExpressionStore<'db> for TypeRefId { }; write!(f, "&")?; if let Some(lifetime) = &ref_.lifetime { - lifetime.hir_fmt(f, store)?; + lifetime.hir_fmt(f, owner, store)?; write!(f, " ")?; } write!(f, "{mutability}")?; - ref_.ty.hir_fmt(f, store)?; + ref_.ty.hir_fmt(f, owner, store)?; } TypeRef::Array(array) => { write!(f, "[")?; - array.ty.hir_fmt(f, store)?; + array.ty.hir_fmt(f, owner, store)?; write!(f, "; ")?; - array.len.hir_fmt(f, store)?; + array.len.hir_fmt(f, owner, store)?; write!(f, "]")?; } TypeRef::Slice(inner) => { write!(f, "[")?; - inner.hir_fmt(f, store)?; + inner.hir_fmt(f, owner, store)?; write!(f, "]")?; } TypeRef::Fn(fn_) => { @@ -2504,7 +2512,7 @@ impl<'db> HirDisplayWithExpressionStore<'db> for TypeRefId { write!(f, "{}: ", name.display(f.db, f.edition()))?; } - param_type.hir_fmt(f, store)?; + param_type.hir_fmt(f, owner, store)?; if index != function_parameters.len() - 1 { write!(f, ", ")?; @@ -2518,18 +2526,29 @@ impl<'db> HirDisplayWithExpressionStore<'db> for TypeRefId { TypeRef::Tuple(tup) if tup.is_empty() => {} _ => { write!(f, " -> ")?; - return_type.hir_fmt(f, store)?; + return_type.hir_fmt(f, owner, store)?; } } } } TypeRef::ImplTrait(bounds) => { write!(f, "impl ")?; - f.write_joined(bounds.iter().map(ExpressionStoreAdapter::wrap(store)), " + ")?; + f.write_joined( + bounds.iter().map(ExpressionStoreAdapter::wrap(owner, store)), + " + ", + )?; } TypeRef::DynTrait(bounds) => { write!(f, "dyn ")?; - f.write_joined(bounds.iter().map(ExpressionStoreAdapter::wrap(store)), " + ")?; + f.write_joined( + bounds.iter().map(ExpressionStoreAdapter::wrap(owner, store)), + " + ", + )?; + } + TypeRef::PatternType(ty, pat) => { + ty.hir_fmt(f, owner, store)?; + write!(f, " is ")?; + pat.hir_fmt(f, owner, store)?; } TypeRef::Error => write!(f, "{{error}}")?, } @@ -2538,7 +2557,12 @@ impl<'db> HirDisplayWithExpressionStore<'db> for TypeRefId { } impl<'db> HirDisplayWithExpressionStore<'db> for ConstRef { - fn hir_fmt(&self, f: &mut HirFormatter<'_, 'db>, _store: &ExpressionStore) -> Result { + fn hir_fmt( + &self, + f: &mut HirFormatter<'_, 'db>, + _owner: ExpressionStoreOwnerId, + _store: &ExpressionStore, + ) -> Result { // FIXME write!(f, "{{const}}")?; @@ -2546,17 +2570,45 @@ impl<'db> HirDisplayWithExpressionStore<'db> for ConstRef { } } +impl<'db> HirDisplayWithExpressionStore<'db> for PatId { + fn hir_fmt( + &self, + f: &mut HirFormatter<'_, 'db>, + owner: ExpressionStoreOwnerId, + store: &ExpressionStore, + ) -> Result { + write!( + f, + "{}", + hir_def::expr_store::pretty::print_pat_hir( + f.db, + store, + owner, + *self, + false, + f.edition() + ) + )?; + Ok(()) + } +} + impl<'db> HirDisplayWithExpressionStore<'db> for TypeBound { - fn hir_fmt(&self, f: &mut HirFormatter<'_, 'db>, store: &ExpressionStore) -> Result { + fn hir_fmt( + &self, + f: &mut HirFormatter<'_, 'db>, + owner: ExpressionStoreOwnerId, + store: &ExpressionStore, + ) -> Result { match self { &TypeBound::Path(path, modifier) => { match modifier { TraitBoundModifier::None => (), TraitBoundModifier::Maybe => write!(f, "?")?, } - store[path].hir_fmt(f, store) + store[path].hir_fmt(f, owner, store) } - TypeBound::Lifetime(lifetime) => lifetime.hir_fmt(f, store), + TypeBound::Lifetime(lifetime) => lifetime.hir_fmt(f, owner, store), TypeBound::ForLifetime(lifetimes, path) => { let edition = f.edition(); write!( @@ -2564,7 +2616,7 @@ impl<'db> HirDisplayWithExpressionStore<'db> for TypeBound { "for<{}> ", lifetimes.iter().map(|it| it.display(f.db, edition)).format(", ") )?; - store[*path].hir_fmt(f, store) + store[*path].hir_fmt(f, owner, store) } TypeBound::Use(args) => { write!(f, "use<")?; @@ -2572,7 +2624,7 @@ impl<'db> HirDisplayWithExpressionStore<'db> for TypeBound { let last = args.len().saturating_sub(1); for (idx, arg) in args.iter().enumerate() { match arg { - UseArgRef::Lifetime(lt) => lt.hir_fmt(f, store)?, + UseArgRef::Lifetime(lt) => lt.hir_fmt(f, owner, store)?, UseArgRef::Name(n) => write!(f, "{}", n.display(f.db, edition))?, } if idx != last { @@ -2587,11 +2639,16 @@ impl<'db> HirDisplayWithExpressionStore<'db> for TypeBound { } impl<'db> HirDisplayWithExpressionStore<'db> for Path { - fn hir_fmt(&self, f: &mut HirFormatter<'_, 'db>, store: &ExpressionStore) -> Result { + fn hir_fmt( + &self, + f: &mut HirFormatter<'_, 'db>, + owner: ExpressionStoreOwnerId, + store: &ExpressionStore, + ) -> Result { match (self.type_anchor(), self.kind()) { (Some(anchor), _) => { write!(f, "<")?; - anchor.hir_fmt(f, store)?; + anchor.hir_fmt(f, owner, store)?; write!(f, ">")?; } (_, PathKind::Plain) => {} @@ -2634,7 +2691,7 @@ impl<'db> HirDisplayWithExpressionStore<'db> for Path { }); if let Some(ty) = trait_self_ty { write!(f, "<")?; - ty.hir_fmt(f, store)?; + ty.hir_fmt(f, owner, store)?; write!(f, " as ")?; // Now format the path of the trait... } @@ -2664,17 +2721,17 @@ impl<'db> HirDisplayWithExpressionStore<'db> for Path { if let Some(v) = tuple { if v.len() == 1 { write!(f, "(")?; - v[0].hir_fmt(f, store)?; + v[0].hir_fmt(f, owner, store)?; write!(f, ")")?; } else { - generic_args.args[0].hir_fmt(f, store)?; + generic_args.args[0].hir_fmt(f, owner, store)?; } } if let Some(ret) = generic_args.bindings[0].type_ref && !matches!(&store[ret], TypeRef::Tuple(v) if v.is_empty()) { write!(f, " -> ")?; - ret.hir_fmt(f, store)?; + ret.hir_fmt(f, owner, store)?; } } hir_def::expr_store::path::GenericArgsParentheses::No => { @@ -2687,7 +2744,7 @@ impl<'db> HirDisplayWithExpressionStore<'db> for Path { } else { write!(f, ", ")?; } - arg.hir_fmt(f, store)?; + arg.hir_fmt(f, owner, store)?; } for binding in generic_args.bindings.iter() { if first { @@ -2700,7 +2757,7 @@ impl<'db> HirDisplayWithExpressionStore<'db> for Path { match &binding.type_ref { Some(ty) => { write!(f, " = ")?; - ty.hir_fmt(f, store)? + ty.hir_fmt(f, owner, store)? } None => { write!(f, ": ")?; @@ -2708,7 +2765,7 @@ impl<'db> HirDisplayWithExpressionStore<'db> for Path { binding .bounds .iter() - .map(ExpressionStoreAdapter::wrap(store)), + .map(ExpressionStoreAdapter::wrap(owner, store)), " + ", )?; } @@ -2735,14 +2792,21 @@ impl<'db> HirDisplayWithExpressionStore<'db> for Path { } impl<'db> HirDisplayWithExpressionStore<'db> for hir_def::expr_store::path::GenericArg { - fn hir_fmt(&self, f: &mut HirFormatter<'_, 'db>, store: &ExpressionStore) -> Result { + fn hir_fmt( + &self, + f: &mut HirFormatter<'_, 'db>, + owner: ExpressionStoreOwnerId, + store: &ExpressionStore, + ) -> Result { match self { - hir_def::expr_store::path::GenericArg::Type(ty) => ty.hir_fmt(f, store), + hir_def::expr_store::path::GenericArg::Type(ty) => ty.hir_fmt(f, owner, store), hir_def::expr_store::path::GenericArg::Const(_c) => { // write!(f, "{}", c.display(f.db, f.edition())) write!(f, "<expr>") } - hir_def::expr_store::path::GenericArg::Lifetime(lifetime) => lifetime.hir_fmt(f, store), + hir_def::expr_store::path::GenericArg::Lifetime(lifetime) => { + lifetime.hir_fmt(f, owner, store) + } } } } diff --git a/crates/hir-ty/src/drop.rs b/crates/hir-ty/src/drop.rs index 61e6720a29..e1fbc85960 100644 --- a/crates/hir-ty/src/drop.rs +++ b/crates/hir-ty/src/drop.rs @@ -6,7 +6,6 @@ use hir_def::{ }; use rustc_hash::FxHashSet; use rustc_type_ir::inherent::{AdtDef, GenericArgs as _, IntoKind}; -use stdx::never; use crate::{ consteval, @@ -100,8 +99,8 @@ fn has_drop_glue_impl<'db>( AdtId::EnumId(id) => id .enum_variants(db) .variants - .iter() - .map(|&(variant, _, _)| { + .values() + .map(|&(variant, _)| { db.field_types(variant.into()) .iter() .map(|(_, field_ty)| { @@ -177,9 +176,7 @@ fn has_drop_glue_impl<'db>( } } TyKind::Infer(..) => unreachable!("inference vars shouldn't exist out of inference"), - TyKind::Pat(..) | TyKind::UnsafeBinder(..) => { - never!("we do not handle pattern and unsafe binder types"); - DropGlue::None - } + TyKind::Pat(ty, _) => has_drop_glue_impl(infcx, ty, env, visited), + TyKind::UnsafeBinder(ty) => has_drop_glue_impl(infcx, ty.skip_binder(), env, visited), } } diff --git a/crates/hir-ty/src/infer.rs b/crates/hir-ty/src/infer.rs index 39ffb91a8c..2df2789a2e 100644 --- a/crates/hir-ty/src/infer.rs +++ b/crates/hir-ty/src/infer.rs @@ -297,11 +297,19 @@ pub enum InferenceDiagnostic { #[type_visitable(ignore)] has_rest: bool, }, + ArrayPatternWithoutFixedLength { + #[type_visitable(ignore)] + pat: PatId, + }, ExpectedArrayOrSlicePat { #[type_visitable(ignore)] pat: PatId, found: StoredTy, }, + InvalidRangePatType { + #[type_visitable(ignore)] + pat: PatId, + }, DuplicateField { #[type_visitable(ignore)] field: ExprOrPatId, @@ -361,6 +369,12 @@ pub enum InferenceDiagnostic { #[type_visitable(ignore)] expr: ExprId, }, + NonExhaustiveRecordPat { + #[type_visitable(ignore)] + pat: PatId, + #[type_visitable(ignore)] + variant: VariantId, + }, FunctionalRecordUpdateOnNonStruct { #[type_visitable(ignore)] base_expr: ExprId, @@ -386,6 +400,21 @@ pub enum InferenceDiagnostic { call_expr: ExprId, found: StoredTy, }, + CannotBeDereferenced { + #[type_visitable(ignore)] + expr: ExprId, + found: StoredTy, + }, + CannotImplicitlyDerefTraitObject { + #[type_visitable(ignore)] + pat: PatId, + found: StoredTy, + }, + CannotIndexInto { + #[type_visitable(ignore)] + expr: ExprId, + found: StoredTy, + }, TypedHole { #[type_visitable(ignore)] expr: ExprId, @@ -428,6 +457,10 @@ pub enum InferenceDiagnostic { #[type_visitable(ignore)] def: GenericDefId, }, + MethodCallIllegalSizedBound { + #[type_visitable(ignore)] + call_expr: ExprId, + }, MethodCallIncorrectGenericsOrder { #[type_visitable(ignore)] expr: ExprId, @@ -459,6 +492,20 @@ pub enum InferenceDiagnostic { found: StoredTy, }, SolverDiagnostic(SolverDiagnostic), + ExplicitDropMethodUse { + #[type_visitable(ignore)] + kind: ExplicitDropMethodUseKind, + }, + MutableRefBinding { + #[type_visitable(ignore)] + pat: PatId, + }, +} + +#[derive(Debug, PartialEq, Eq, Clone)] +pub enum ExplicitDropMethodUseKind { + MethodCall(ExprId), + Path(ExprOrPatId), } /// Represents coercing a value to a different type of value. @@ -1289,7 +1336,7 @@ impl<'body, 'db> InferenceContext<'body, 'db> { resolver: Resolver<'db>, allow_using_generic_params: bool, ) -> Self { - let trait_env = db.trait_environment(store_owner); + let trait_env = db.trait_environment(generic_def); let table = unify::InferenceTable::new(db, trait_env, resolver.krate(), store_owner); let types = crate::next_solver::default_types(db); InferenceContext { @@ -2438,8 +2485,8 @@ impl<'body, 'db> InferenceContext<'body, 'db> { }; let args = path_ctx.substs_from_path_segment(it.into(), true, None, false, node.into()); + let interner = path_ctx.interner(); drop(ctx); - let interner = DbInterner::conjure(); let ty = self.db.ty(it.into()).instantiate(interner, args).skip_norm_wip(); let ty = self.insert_type_vars(ty); diff --git a/crates/hir-ty/src/infer/closure/analysis/expr_use_visitor.rs b/crates/hir-ty/src/infer/closure/analysis/expr_use_visitor.rs index deafff6b43..2642844e38 100644 --- a/crates/hir-ty/src/infer/closure/analysis/expr_use_visitor.rs +++ b/crates/hir-ty/src/infer/closure/analysis/expr_use_visitor.rs @@ -1023,6 +1023,7 @@ impl<'a, 'b, 'db, D: Delegate<'db>> ExprUseVisitor<'a, 'b, 'db, D> { | Pat::Tuple { .. } | Pat::Wild | Pat::Missing + | Pat::NotNull | Pat::Rest => { // If the PatKind is Or, Box, Ref, Guard, or Tuple, the relevant accesses // are made later as these patterns contains subpatterns. @@ -1473,7 +1474,7 @@ impl<'db, D: Delegate<'db>> ExprUseVisitor<'_, '_, 'db, D> { fn variant_index_for_adt(&self, pat_id: PatId) -> Result<(u32, VariantId)> { let variant = self.cx.result.variant_resolution_for_pat(pat_id).ok_or(ErrorGuaranteed)?; let variant_idx = match variant { - VariantId::EnumVariantId(variant) => variant.loc(self.cx.db).index, + VariantId::EnumVariantId(variant) => variant.index(self.cx.db) as u32, VariantId::StructId(_) | VariantId::UnionId(_) => 0, }; Ok((variant_idx, variant)) @@ -1696,6 +1697,7 @@ impl<'db, D: Delegate<'db>> ExprUseVisitor<'_, '_, 'db, D> { | Pat::Range { .. } | Pat::Missing | Pat::Rest + | Pat::NotNull | Pat::Wild => { // always ok } diff --git a/crates/hir-ty/src/infer/expr.rs b/crates/hir-ty/src/infer/expr.rs index 0675b5e857..75e6440334 100644 --- a/crates/hir-ty/src/infer/expr.rs +++ b/crates/hir-ty/src/infer/expr.rs @@ -8,7 +8,8 @@ use hir_def::{ expr_store::path::{GenericArgs as HirGenericArgs, Path}, hir::{ Array, AsmOperand, AsmOptions, BinaryOp, BindingAnnotation, Expr, ExprId, ExprOrPatId, - InlineAsmKind, LabelId, Pat, PatId, RecordLitField, RecordSpread, Statement, UnaryOp, + InlineAsmKind, LabelId, LoopSource, Pat, PatId, RecordLitField, RecordSpread, Statement, + UnaryOp, }, resolver::ValueNs, signatures::VariantFields, @@ -201,6 +202,7 @@ impl<'db> InferenceContext<'_, 'db> { | Pat::Slice { .. } | Pat::ConstBlock(_) | Pat::Record { .. } + | Pat::NotNull | Pat::Missing => true, Pat::Expr(_) => unreachable!( "we don't call pat_guaranteed_to_constitute_read_for_never() with assignments" @@ -305,7 +307,7 @@ impl<'db> InferenceContext<'_, 'db> { } } else { if let Some(expected_ty) = expected.only_has_type(&mut self.table) { - _ = self.demand_eqtype(expr.into(), ty, expected_ty); + _ = self.demand_eqtype(expr.into(), expected_ty, ty); } ty } @@ -400,24 +402,29 @@ impl<'db> InferenceContext<'_, 'db> { }) .1 } - &Expr::Loop { body, label } => { - let ty = expected.coercion_target_type(&mut self.table, tgt_expr.into()); + &Expr::Loop { body, label, source } => { + let coerce = match source { + // you can only use break with a value from a normal `loop { }` + LoopSource::Loop => { + Some(expected.coercion_target_type(&mut self.table, body.into())) + } + LoopSource::While | LoopSource::ForLoop => None, + }; let (breaks, ()) = - self.with_breakable_ctx(BreakableKind::Loop, Some(ty), label, |this| { - this.infer_expr( + self.with_breakable_ctx(BreakableKind::Loop, coerce, label, |this| { + this.infer_expr_suptype_coerce_never( body, &Expectation::HasType(this.types.types.unit), ExprIsRead::Yes, ); }); - match breaks { - Some(breaks) => { - self.diverges = Diverges::Maybe; - breaks - } - None => self.types.types.never, + if breaks.may_break { + self.diverges = Diverges::Maybe; + } else { + self.diverges = Diverges::Always; } + breaks.coerce.map(|c| c.complete(self)).unwrap_or(self.types.types.unit) } Expr::Closure { body, args, ret_type, arg_types, closure_kind, capture_by: _ } => self .infer_closure( @@ -728,8 +735,13 @@ impl<'db> InferenceContext<'_, 'db> { self.table.select_obligations_where_possible(); trait_element_ty } - // FIXME: Report an error. - None => self.types.types.error, + None => { + self.push_diagnostic(InferenceDiagnostic::CannotIndexInto { + expr: tgt_expr, + found: base_t.store(), + }); + self.types.types.error + } } } Expr::Tuple { exprs, .. } => { @@ -1294,7 +1306,10 @@ impl<'db> InferenceContext<'_, 'db> { if let Some(ty) = self.lookup_derefing(expr, oprnd, oprnd_t) { oprnd_t = ty; } else { - // FIXME: Report an error. + self.push_diagnostic(InferenceDiagnostic::CannotBeDereferenced { + expr, + found: oprnd_t.store(), + }); oprnd_t = self.types.types.error; } } @@ -1490,10 +1505,11 @@ impl<'db> InferenceContext<'_, 'db> { label: Option<LabelId>, expected: &Expectation<'db>, ) -> Ty<'db> { + let prev_diverges = self.diverges; let coerce_ty = expected.coercion_target_type(&mut self.table, expr.into()); let g = self.resolver.update_to_inner_scope(self.db, self.store_owner, expr); - let (break_ty, ty) = + let (ctxt, tail_expr_ty) = self.with_breakable_ctx(BreakableKind::Block, Some(coerce_ty), label, |this| { for stmt in statements { match stmt { @@ -1561,42 +1577,54 @@ impl<'db> InferenceContext<'_, 'db> { } } - // FIXME: This should make use of the breakable CoerceMany - if let Some(expr) = tail { - this.infer_expr_coerce(expr, expected, ExprIsRead::Yes) - } else { - // Citing rustc: if there is no explicit tail expression, - // that is typically equivalent to a tail expression - // of `()` -- except if the block diverges. In that - // case, there is no value supplied from the tail - // expression (assuming there are no other breaks, - // this implies that the type of the block will be - // `!`). - if this.diverges.is_always() { - // we don't even make an attempt at coercion - this.table.new_maybe_never_var(expr.into()) - } else if let Some(t) = expected.only_has_type(&mut this.table) { - if this - .coerce( - expr, - this.types.types.unit, - t, - AllowTwoPhase::No, - ExprIsRead::Yes, - ) - .is_err() - { - this.emit_type_mismatch(expr.into(), t, this.types.types.unit); - } - t - } else { - this.types.types.unit - } - } + // check the tail expression **without** holding the + // `enclosing_breakables` lock below. + tail.map(|expr| (expr, this.infer_expr_inner(expr, expected, ExprIsRead::Yes))) }); + + let mut coerce = ctxt.coerce.unwrap(); + if let Some((tail_expr, tail_expr_ty)) = tail_expr_ty { + let cause = ObligationCause::new(tail_expr); + coerce.coerce_inner( + self, + &cause, + tail_expr, + tail_expr_ty, + false, + false, + ExprIsRead::Yes, + ); + } else { + // Subtle: if there is no explicit tail expression, + // that is typically equivalent to a tail expression + // of `()` -- except if the block diverges. In that + // case, there is no value supplied from the tail + // expression (assuming there are no other breaks, + // this implies that the type of the block will be + // `!`). + // + // #41425 -- label the implicit `()` as being the + // "found type" here, rather than the "expected type". + if !self.diverges.is_always() { + coerce.coerce_forced_unit( + self, + expr, + &ObligationCause::new(expr), + false, + ExprIsRead::Yes, + ); + } + } + + if ctxt.may_break { + // If we can break from the block, then the block's exit is always reachable + // (... as long as the entry is reachable) - regardless of the tail of the block. + self.diverges = prev_diverges; + } + self.resolver.reset_to_guard(g); - break_ty.unwrap_or(ty) + coerce.complete(self) } fn lookup_field( @@ -2169,13 +2197,13 @@ impl<'db> InferenceContext<'_, 'db> { ty: Option<Ty<'db>>, label: Option<LabelId>, cb: impl FnOnce(&mut Self) -> T, - ) -> (Option<Ty<'db>>, T) { + ) -> (BreakableContext<'db>, T) { self.breakables.push({ BreakableContext { kind, may_break: false, coerce: ty.map(CoerceMany::new), label } }); let res = cb(self); let ctx = self.breakables.pop().expect("breakable stack broken"); - (if ctx.may_break { ctx.coerce.map(|ctx| ctx.complete(self)) } else { None }, res) + (ctx, res) } } diff --git a/crates/hir-ty/src/infer/mutability.rs b/crates/hir-ty/src/infer/mutability.rs index c3b532638f..483f54a227 100644 --- a/crates/hir-ty/src/infer/mutability.rs +++ b/crates/hir-ty/src/infer/mutability.rs @@ -160,7 +160,7 @@ impl<'db> InferenceContext<'_, 'db> { | Expr::Range { rhs: Some(expr), lhs: None, range_type: _ } | Expr::Await { expr } | Expr::Box { expr } - | Expr::Loop { body: expr, label: _ } + | Expr::Loop { body: expr, label: _, source: _ } | Expr::Cast { expr, type_ref: _ } => { self.infer_mut_expr(*expr, Mutability::Not); } diff --git a/crates/hir-ty/src/infer/pat.rs b/crates/hir-ty/src/infer/pat.rs index f21438647c..c36c29d6c7 100644 --- a/crates/hir-ty/src/infer/pat.rs +++ b/crates/hir-ty/src/infer/pat.rs @@ -448,7 +448,7 @@ impl<'a, 'db> InferenceContext<'a, 'db> { ) } Pat::Missing => self.types.types.error, - Pat::Wild | Pat::Rest => expected, + Pat::Wild | Pat::Rest | Pat::NotNull => expected, // We allow any type here; we ensure that the type is uninhabited during match checking. // Pat::Never => expected, Pat::Path(_) => { @@ -662,6 +662,8 @@ impl<'a, 'db> InferenceContext<'a, 'db> { Pat::Ref { .. } // No need to do anything on a missing pattern. | Pat::Missing + // No need to do anything on a `NotNull` pattern, they are only allowed in type contexts. + | Pat::NotNull // A `_`/`..` pattern works with any expected type, so there's no need to do anything. | Pat::Wild | Pat::Rest // Bindings also work with whatever the expected type is, @@ -843,7 +845,7 @@ impl<'a, 'db> InferenceContext<'a, 'db> { if let (Some((true, ..)), _) | (_, Some((true, ..))) = (lhs, rhs) { // There exists a side that didn't meet our criteria that the end-point // be of a numeric or char type, as checked in `calc_side` above. - // FIXME: Emit an error. + self.push_diagnostic(InferenceDiagnostic::InvalidRangePatType { pat }); return self.types.types.error; } @@ -891,14 +893,14 @@ impl<'a, 'db> InferenceContext<'a, 'db> { let user_bind_annot = BindingMode::from_annotation(binding_data.mode); let bm = match user_bind_annot { BindingMode(ByRef::No, Mutability::Mut) if let ByRef::Yes(_) = def_br => { - // Only mention the experimental `mut_ref` feature if if we're in edition 2024 and + // Only mention the experimental `mut_ref` feature if we're in edition 2024 and // using other experimental matching features compatible with it. if self.edition.at_least_2024() && (self.features.ref_pat_eat_one_layer_2024 || self.features.ref_pat_eat_one_layer_2024_structural) { if !self.features.mut_ref { - // FIXME: Emit an error: binding cannot be both mutable and by-reference. + self.push_diagnostic(InferenceDiagnostic::MutableRefBinding { pat }); } BindingMode(def_br, Mutability::Mut) @@ -957,22 +959,23 @@ impl<'a, 'db> InferenceContext<'a, 'db> { local_ty } - fn check_dereferenceable(&self, expected: Ty<'db>, inner: PatId) -> Result<(), ()> { + fn check_dereferenceable( + &mut self, + expected: Ty<'db>, + pat: PatId, + inner: PatId, + ) -> Result<(), ()> { if let Pat::Bind { .. } = self.store[inner] && let Some(pointee_ty) = self.shallow_resolve(expected).builtin_deref(true) && let TyKind::Dynamic(..) = pointee_ty.kind() { // This is "x = dyn SomeTrait" being reduced from // "let &x = &dyn SomeTrait" or "let box x = Box<dyn SomeTrait>", an error. - // FIXME: Emit an error. rustc emits this message: - const _CANNOT_IMPLICITLY_DEREF_POINTER_TRAIT_OBJ: &str = "\ -This error indicates that a pointer to a trait type cannot be implicitly dereferenced by a \ -pattern. Every trait defines a type, but because the size of trait implementors isn't fixed, \ -this type has no compile-time size. Therefore, all accesses to trait types must be through \ -pointers. If you encounter this error you should try to avoid dereferencing the pointer. - -You can read more about trait objects in the Trait Objects section of the Reference: \ -https://doc.rust-lang.org/reference/types.html#trait-objects"; + self.push_diagnostic(InferenceDiagnostic::CannotImplicitlyDerefTraitObject { + pat, + found: expected.store(), + }); + return Err(()); } Ok(()) } @@ -1155,7 +1158,7 @@ https://doc.rust-lang.org/reference/types.html#trait-objects"; fn check_record_pat_fields( &mut self, adt_ty: Ty<'db>, - _pat: PatId, + pat: PatId, variant: VariantId, fields: &[RecordFieldPat], has_rest_pat: bool, @@ -1233,7 +1236,7 @@ https://doc.rust-lang.org/reference/types.html#trait-objects"; // Require `..` if struct has non_exhaustive attribute. let non_exhaustive = self.has_applicable_non_exhaustive(variant.into()); if non_exhaustive && !has_rest_pat { - // FIXME: Emit an error. + self.push_diagnostic(InferenceDiagnostic::NonExhaustiveRecordPat { pat, variant }); } // Report an error if an incorrect number of fields was specified. @@ -1258,7 +1261,7 @@ https://doc.rust-lang.org/reference/types.html#trait-objects"; ) -> Ty<'db> { let interner = self.interner(); let (box_ty, inner_ty) = self - .check_dereferenceable(expected, inner) + .check_dereferenceable(expected, pat, inner) .map(|()| { // Here, `demand::subtype` is good enough, but I don't // think any errors can be introduced by using `demand::eqtype`. @@ -1471,7 +1474,7 @@ https://doc.rust-lang.org/reference/types.html#trait-objects"; } } - let (ref_ty, inner_ty) = match self.check_dereferenceable(expected, inner) { + let (ref_ty, inner_ty) = match self.check_dereferenceable(expected, pat, inner) { Ok(()) => { // `demand::subtype` would be good enough, but using `eqtype` turns // out to be equally general. See (note_1) for details. @@ -1699,7 +1702,7 @@ https://doc.rust-lang.org/reference/types.html#trait-objects"; // We have a variable-length pattern and don't know the array length. // This happens if we have e.g., // `let [a, b, ..] = arr` where `arr: [T; N]` where `const N: usize`. - // FIXME: Emit an error: cannot pattern-match on an array without a fixed length. + self.push_diagnostic(InferenceDiagnostic::ArrayPatternWithoutFixedLength { pat }); }; // If we get here, we must have emitted an error. diff --git a/crates/hir-ty/src/infer/path.rs b/crates/hir-ty/src/infer/path.rs index 704f15cc86..0ec72edc3d 100644 --- a/crates/hir-ty/src/infer/path.rs +++ b/crates/hir-ty/src/infer/path.rs @@ -11,7 +11,7 @@ use rustc_type_ir::inherent::{SliceLike, Ty as _}; use stdx::never; use crate::{ - InferenceDiagnostic, Span, ValueTyDefId, + ExplicitDropMethodUseKind, InferenceDiagnostic, Span, ValueTyDefId, infer::{ InferenceTyLoweringVarsCtx, diagnostics::InferenceTyLoweringContext as TyLoweringContext, }, @@ -33,6 +33,14 @@ impl<'db> InferenceContext<'_, 'db> { ) -> Option<(ValueNs, Ty<'db>)> { let (value, self_subst) = self.resolve_value_path_inner(path, id, false)?; + if let ValueNs::FunctionId(f) = value + && self.lang_items.Drop_drop.is_some_and(|drop_fn| drop_fn == f) + { + self.push_diagnostic(InferenceDiagnostic::ExplicitDropMethodUse { + kind: ExplicitDropMethodUseKind::Path(id), + }); + } + let (value_def, generic_def, substs) = match self.resolve_value_path(path, id, value, self_subst)? { ValuePathResolution::GenericDef(value_def, generic_def, substs) => { @@ -183,7 +191,30 @@ impl<'db> InferenceContext<'_, 'db> { match value_or_partial { ResolveValueResult::ValueNs(it) => { drop_ctx(ctx, no_diagnostics); - (it, None) + + let args = if let Path::LangItem(..) = path { + let def_and_container = match it { + ValueNs::ConstId(it) => Some((it.into(), it.loc(self.db).container)), + ValueNs::FunctionId(it) => Some((it.into(), it.loc(self.db).container)), + _ => None, + }; + let def_and_container = + def_and_container.and_then(|(def, container)| match container { + ItemContainerId::ImplId(it) => Some((def, it.into())), + ItemContainerId::TraitId(it) => Some((def, it.into())), + ItemContainerId::ExternBlockId(_) + | ItemContainerId::ModuleId(_) => None, + }); + def_and_container.map(|(def, container)| { + let args = self.infcx().fresh_args_for_item(id.into(), container); + self.write_assoc_resolution(id, def, args); + args + }) + } else { + None + }; + + (it, args) } ResolveValueResult::Partial(def, remaining_index) => { // there may be more intermediate segments between the resolved one and diff --git a/crates/hir-ty/src/infer/unify.rs b/crates/hir-ty/src/infer/unify.rs index f9ad76b0c1..4c808714d9 100644 --- a/crates/hir-ty/src/infer/unify.rs +++ b/crates/hir-ty/src/infer/unify.rs @@ -577,7 +577,10 @@ pub(super) mod resolve_completely { diagnostics.retain_mut(|diagnostic| { self.resolve_completely(diagnostic); - if let InferenceDiagnostic::ExpectedFunction { found: ty, .. } + if let InferenceDiagnostic::CannotBeDereferenced { found: ty, .. } + | InferenceDiagnostic::CannotImplicitlyDerefTraitObject { found: ty, .. } + | InferenceDiagnostic::CannotIndexInto { found: ty, .. } + | InferenceDiagnostic::ExpectedFunction { found: ty, .. } | InferenceDiagnostic::ExpectedArrayOrSlicePat { found: ty, .. } | InferenceDiagnostic::UnresolvedField { receiver: ty, .. } | InferenceDiagnostic::UnresolvedMethodCall { receiver: ty, .. } = diagnostic diff --git a/crates/hir-ty/src/inhabitedness.rs b/crates/hir-ty/src/inhabitedness.rs index 0070d14f37..26253fbabf 100644 --- a/crates/hir-ty/src/inhabitedness.rs +++ b/crates/hir-ty/src/inhabitedness.rs @@ -125,7 +125,7 @@ impl<'a, 'db> UninhabitedFrom<'a, 'db> { AdtId::EnumId(e) => { let enum_data = e.enum_variants(self.db()); - for &(variant, _, _) in enum_data.variants.iter() { + for &(variant, _) in enum_data.variants.values() { let variant_inhabitedness = self.visit_variant(variant.into(), subst); match variant_inhabitedness { Break(VisiblyUninhabited) => (), diff --git a/crates/hir-ty/src/layout.rs b/crates/hir-ty/src/layout.rs index 3e569076ad..2b0a93e61d 100644 --- a/crates/hir-ty/src/layout.rs +++ b/crates/hir-ty/src/layout.rs @@ -10,12 +10,12 @@ use hir_def::{ use la_arena::{Idx, RawIdx}; use rustc_abi::{ - AddressSpace, Float, Integer, LayoutCalculator, Primitive, ReprOptions, Scalar, StructKind, - TargetDataLayout, WrappingRange, + AddressSpace, BackendRepr, FieldsShape, Float, Integer, LayoutCalculator, Niche, Primitive, + ReprOptions, Scalar, Size, StructKind, TargetDataLayout, WrappingRange, }; use rustc_index::IndexVec; use rustc_type_ir::{ - FloatTy, IntTy, UintTy, + FloatTy, IntTy, TypeVisitableExt as _, UintTy, inherent::{GenericArgs as _, IntoKind}, }; use triomphe::Arc; @@ -25,7 +25,8 @@ use crate::{ consteval::try_const_usize, db::HirDatabase, next_solver::{ - DbInterner, GenericArgs, StoredTy, Ty, TyKind, TypingMode, + Const, ConstKind, DbInterner, GenericArgs, PatternKind, StoredTy, Ty, TyKind, TypingMode, + ValueConst, infer::{DbInternerInferExt, traits::ObligationCause}, }, traits::StoredParamEnvAndCrate, @@ -37,6 +38,9 @@ pub use self::{adt::layout_of_adt_query, target::target_data_layout_query}; pub(crate) mod adt; pub(crate) mod target; +#[cfg(test)] +mod tests; + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct RustcEnumVariantIdx(pub usize); @@ -341,12 +345,151 @@ pub fn layout_of_ty_query( return db .layout_of_ty(args.as_coroutine_closure().tupled_upvars_ty().store(), trait_env); } - TyKind::CoroutineWitness(_, _) => { return Err(LayoutError::NotImplemented); } - TyKind::Pat(_, _) | TyKind::UnsafeBinder(_) => { + TyKind::Pat(ty, pat) => { + let mut layout = (*db.layout_of_ty(ty.store(), trait_env.clone())?).clone(); + match pat.kind() { + PatternKind::Range { start, end } => { + if let BackendRepr::Scalar(scalar) = &mut layout.backend_repr { + scalar.valid_range_mut().start = extract_const_value(start)? + .try_to_bits(db, trait_env.as_ref()) + .ok_or(LayoutError::Unknown)?; + + scalar.valid_range_mut().end = extract_const_value(end)? + .try_to_bits(db, trait_env.as_ref()) + .ok_or(LayoutError::Unknown)?; + + // FIXME(pattern_types): create implied bounds from pattern types in signatures + // that require that the range end is >= the range start so that we can't hit + // this error anymore without first having hit a trait solver error. + // Very fuzzy on the details here, but pattern types are an internal impl detail, + // so we can just go with this for now + if scalar.is_signed() { + let range = scalar.valid_range_mut(); + let start = layout.size.sign_extend(range.start); + let end = layout.size.sign_extend(range.end); + if end < start { + return Err(LayoutError::HasErrorType); + } + } else { + let range = scalar.valid_range_mut(); + if range.end < range.start { + return Err(LayoutError::HasErrorType); + } + }; + + let niche = Niche { + offset: Size::ZERO, + value: scalar.primitive(), + valid_range: scalar.valid_range(target), + }; + + layout.largest_niche = Some(niche); + } else { + panic!("pattern type with range but not scalar layout: {ty:?}, {layout:?}") + } + } + PatternKind::NotNull => { + if let BackendRepr::Scalar(scalar) | BackendRepr::ScalarPair(scalar, _) = + &mut layout.backend_repr + { + scalar.valid_range_mut().start = 1; + let niche = Niche { + offset: Size::ZERO, + value: scalar.primitive(), + valid_range: scalar.valid_range(target), + }; + + layout.largest_niche = Some(niche); + } else { + panic!( + "pattern type with `!null` pattern but not scalar/pair layout: {ty:?}, {layout:?}" + ) + } + } + + PatternKind::Or(variants) => match variants[0].kind() { + PatternKind::Range { .. } => { + if let BackendRepr::Scalar(scalar) = &mut layout.backend_repr { + let variants: Result<Vec<_>, _> = variants + .iter() + .map(|pat| match pat.kind() { + PatternKind::Range { start, end } => Ok::<_, LayoutError>(( + extract_const_value(start) + .unwrap() + .try_to_bits(db, trait_env.as_ref()) + .ok_or(LayoutError::Unknown)?, + extract_const_value(end) + .unwrap() + .try_to_bits(db, trait_env.as_ref()) + .ok_or(LayoutError::Unknown)?, + )), + PatternKind::NotNull | PatternKind::Or(_) => { + unreachable!("mixed or patterns are not allowed") + } + }) + .collect(); + let mut variants = variants?; + if !scalar.is_signed() { + return Err(LayoutError::HasErrorType); + } + variants.sort(); + if variants.len() != 2 { + return Err(LayoutError::HasErrorType); + } + + // first is the one starting at the signed in range min + let mut first = variants[0]; + let mut second = variants[1]; + if second.0 + == layout.size.truncate(layout.size.signed_int_min() as u128) + { + (second, first) = (first, second); + } + + if layout.size.sign_extend(first.1) >= layout.size.sign_extend(second.0) + { + return Err(LayoutError::HasErrorType); + } + if layout.size.signed_int_max() as u128 != second.1 { + return Err(LayoutError::HasErrorType); + } + + // Now generate a wrapping range (which aren't allowed in surface syntax). + scalar.valid_range_mut().start = second.0; + scalar.valid_range_mut().end = first.1; + + let niche = Niche { + offset: Size::ZERO, + value: scalar.primitive(), + valid_range: scalar.valid_range(target), + }; + + layout.largest_niche = Some(niche); + } else { + panic!( + "pattern type with range but not scalar layout: {ty:?}, {layout:?}" + ) + } + } + PatternKind::NotNull => panic!("or patterns can't contain `!null` patterns"), + PatternKind::Or(..) => panic!("patterns cannot have nested or patterns"), + }, + } + // Pattern types contain their base as their sole field. + // This allows the rest of the compiler to process pattern types just like + // single field transparent Adts, and only the parts of the compiler that + // specifically care about pattern types will have to handle it. + layout.fields = FieldsShape::Arbitrary { + offsets: [Size::ZERO].into_iter().collect(), + in_memory_order: [RustcFieldIdx::new(0)].into_iter().collect(), + }; + layout + } + TyKind::UnsafeBinder(_) => { return Err(LayoutError::NotImplemented); } @@ -371,6 +514,25 @@ pub(crate) fn layout_of_ty_cycle_result( Err(LayoutError::RecursiveTypeWithoutIndirection) } +fn extract_const_value<'db>(ct: Const<'db>) -> Result<ValueConst<'db>, LayoutError> { + match ct.kind() { + ConstKind::Value(cv) => Ok(cv), + ConstKind::Param(_) + | ConstKind::Expr(_) + | ConstKind::Unevaluated(_) + | ConstKind::Infer(_) + | ConstKind::Bound(..) + | ConstKind::Placeholder(_) => { + if ct.has_param() { + Err(LayoutError::HasPlaceholder) + } else { + Err(LayoutError::Unknown) + } + } + ConstKind::Error(_) => Err(LayoutError::HasErrorConst), + } +} + fn struct_tail_erasing_lifetimes<'a>(db: &'a dyn HirDatabase, pointee: Ty<'a>) -> Ty<'a> { match pointee.kind() { TyKind::Adt(def, args) => { @@ -411,6 +573,3 @@ fn field_ty<'a>( fn scalar_unit(dl: &TargetDataLayout, value: Primitive) -> Scalar { Scalar::Initialized { value, valid_range: WrappingRange::full(value.size(dl)) } } - -#[cfg(test)] -mod tests; diff --git a/crates/hir-ty/src/layout/adt.rs b/crates/hir-ty/src/layout/adt.rs index b7e1697059..22dd53ca2d 100644 --- a/crates/hir-ty/src/layout/adt.rs +++ b/crates/hir-ty/src/layout/adt.rs @@ -60,8 +60,8 @@ pub fn layout_of_adt_query( let variants = e.enum_variants(db); let r = variants .variants - .iter() - .map(|&(v, _, _)| handle_variant(v.into(), v.fields(db))) + .values() + .map(|&(v, _)| handle_variant(v.into(), v.fields(db))) .collect::<Result<SmallVec<_>, _>>()?; (r, AttrFlags::repr(db, e.into()).unwrap_or_default(), false) } diff --git a/crates/hir-ty/src/layout/tests.rs b/crates/hir-ty/src/layout/tests.rs index bc18f05790..afbafb3fea 100644 --- a/crates/hir-ty/src/layout/tests.rs +++ b/crates/hir-ty/src/layout/tests.rs @@ -1,7 +1,7 @@ use base_db::target::TargetData; use either::Either; use hir_def::{ - DefWithBodyId, ExpressionStoreOwnerId, GenericDefId, HasModule, + DefWithBodyId, HasModule, expr_store::Body, signatures::{ EnumSignature, FunctionSignature, StructSignature, TypeAliasSignature, UnionSignature, @@ -92,13 +92,10 @@ fn eval_goal( ), Either::Right(ty_id) => db.ty(ty_id.into()).instantiate_identity().skip_norm_wip(), }; - let param_env = db.trait_environment( - match adt_or_type_alias_id { - Either::Left(adt) => hir_def::GenericDefId::AdtId(adt), - Either::Right(ty) => hir_def::GenericDefId::TypeAliasId(ty), - } - .into(), - ); + let param_env = db.trait_environment(match adt_or_type_alias_id { + Either::Left(adt) => hir_def::GenericDefId::AdtId(adt), + Either::Right(ty) => hir_def::GenericDefId::TypeAliasId(ty), + }); let krate = match adt_or_type_alias_id { Either::Left(it) => it.krate(&db), Either::Right(it) => it.krate(&db), @@ -145,8 +142,7 @@ fn eval_expr( .0; let infer = InferenceResult::of(&db, DefWithBodyId::from(function_id)); let goal_ty = infer.type_of_binding[b].clone(); - let param_env = - db.trait_environment(ExpressionStoreOwnerId::from(GenericDefId::from(function_id))); + let param_env = db.trait_environment(function_id.into()); let krate = function_id.krate(&db); db.layout_of_ty(goal_ty, ParamEnvAndCrate { param_env, krate }.store()) }) @@ -182,6 +178,7 @@ fn check_fail(#[rust_analyzer::rust_fixture] ra_fixture: &str, e: LayoutError) { assert_eq!(r, Err(e)); } +#[rust_analyzer::macro_style(braces)] macro_rules! size_and_align { (minicore: $($x:tt),*;$($t:tt)*) => { { @@ -444,6 +441,7 @@ fn return_position_impl_trait() { // but rustc actually runs this code. let pinned = pin!(inp); struct EmptyWaker; + #[expect(clippy::manual_noop_waker, reason = "we don't have access to std here")] impl Wake for EmptyWaker { fn wake(self: Arc<Self>) { } @@ -529,13 +527,21 @@ fn tuple_ptr_with_dst_tail() { } #[test] -#[ignore = "FIXME: We need to have proper pattern types"] fn non_zero_and_non_null() { size_and_align! { minicore: non_zero, non_null, option; use core::{num::NonZeroU8, ptr::NonNull}; struct Goal(Option<NonZeroU8>, Option<NonNull<i32>>); } + check_size_and_align( + r#" +const END: usize = 10; +struct Goal(core::pattern_type!(usize is 0..=END)); + "#, + "//- minicore: pat\n", + 8, + 8, + ); } #[test] diff --git a/crates/hir-ty/src/lib.rs b/crates/hir-ty/src/lib.rs index 91e3b85aa1..f612bdc266 100644 --- a/crates/hir-ty/src/lib.rs +++ b/crates/hir-ty/src/lib.rs @@ -104,9 +104,10 @@ use crate::{ pub use autoderef::autoderef; pub use infer::{ - Adjust, Adjustment, AutoBorrow, BindingMode, ByRef, InferenceDiagnostic, InferenceResult, - InferenceTyDiagnosticSource, OverloadedDeref, PointerCast, cast::CastError, could_coerce, - could_unify, could_unify_deeply, infer_query_with_inspect, + Adjust, Adjustment, AutoBorrow, BindingMode, ByRef, ExplicitDropMethodUseKind, + InferenceDiagnostic, InferenceResult, InferenceTyDiagnosticSource, OverloadedDeref, + PointerCast, cast::CastError, could_coerce, could_unify, could_unify_deeply, + infer_query_with_inspect, }; pub use lower::{ GenericDefaults, GenericDefaultsRef, GenericPredicates, ImplTraits, LifetimeElisionKind, @@ -384,7 +385,7 @@ pub fn associated_type_shorthand_candidates( let mut dedup_map = FxHashSet::default(); let param_ty = Ty::new_param(interner, param, type_or_const_param_idx(db, param.into())); // We use the ParamEnv and not the predicates because the ParamEnv elaborates bounds. - let param_env = db.trait_environment(ExpressionStoreOwnerId::from(def)); + let param_env = db.trait_environment(def); for clause in param_env.clauses { let ClauseKind::Trait(trait_clause) = clause.kind().skip_binder() else { continue }; if trait_clause.self_ty() != param_ty { diff --git a/crates/hir-ty/src/lower.rs b/crates/hir-ty/src/lower.rs index 5b0bcd2be8..df83b2abb8 100644 --- a/crates/hir-ty/src/lower.rs +++ b/crates/hir-ty/src/lower.rs @@ -18,9 +18,12 @@ use hir_def::{ TypeAliasId, TypeOrConstParamId, TypeParamId, UnionId, VariantId, builtin_type::BuiltinType, expr_store::{ExpressionStore, path::Path}, - hir::generics::{ - GenericParamDataRef, GenericParams, LocalTypeOrConstParamId, TypeOrConstParamData, - TypeParamProvenance, WherePredicate, + hir::{ + ExprId, PatId, + generics::{ + GenericParamDataRef, GenericParams, LocalTypeOrConstParamId, TypeOrConstParamData, + TypeParamProvenance, WherePredicate, + }, }, item_tree::FieldsShape, lang_item::LangItems, @@ -60,10 +63,10 @@ use crate::{ next_solver::{ AliasTy, Binder, BoundExistentialPredicates, Clause, ClauseKind, Clauses, Const, ConstKind, DbInterner, DefaultAny, EarlyBinder, EarlyParamRegion, ErrorGuaranteed, FnSigKind, - FxIndexMap, GenericArg, GenericArgs, ParamConst, ParamEnv, PolyFnSig, Predicate, Region, - StoredClauses, StoredEarlyBinder, StoredGenericArg, StoredGenericArgs, StoredPolyFnSig, - StoredTraitRef, StoredTy, TraitPredicate, TraitRef, Ty, Tys, Unnormalized, abi::Safety, - util::BottomUpFolder, + FxIndexMap, GenericArg, GenericArgs, ParamConst, ParamEnv, PatList, Pattern, PolyFnSig, + Predicate, Region, StoredClauses, StoredEarlyBinder, StoredGenericArg, StoredGenericArgs, + StoredPolyFnSig, StoredTraitRef, StoredTy, TraitPredicate, TraitRef, Ty, Tys, Unnormalized, + abi::Safety, util::BottomUpFolder, }, }; @@ -198,7 +201,7 @@ pub trait TyLoweringInferVarsCtx<'db> { pub struct TyLoweringContext<'db, 'a> { pub db: &'db dyn HirDatabase, - interner: DbInterner<'db>, + pub(crate) interner: DbInterner<'db>, types: &'db crate::next_solver::DefaultAny<'db>, lang_items: &'db LangItems, resolver: &'a Resolver<'db>, @@ -357,6 +360,14 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { } pub(crate) fn lower_const(&mut self, const_ref: ConstRef, const_type: Ty<'db>) -> Const<'db> { + self.lower_expr_as_const(const_ref.expr, const_type) + } + + pub(crate) fn lower_expr_as_const( + &mut self, + expr_id: ExprId, + const_type: Ty<'db>, + ) -> Const<'db> { #[expect(clippy::manual_map, reason = "a `map()` here generates a borrowck error")] let create_var = match &mut self.infer_vars { Some(infer_vars) => Some( @@ -368,7 +379,7 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { self.interner, self.def, self.store, - const_ref.expr, + expr_id, self.resolver, const_type, &|| self.generics.get_or_init(|| generics(self.db, self.generic_def)), @@ -528,11 +539,43 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { } } } + &TypeRef::PatternType(ty, pat) => { + let ty = self.lower_ty(ty); + let Some(pat) = self.lower_pattern_type(pat, ty) else { + return (self.types.types.error, res); + }; + Ty::new_pat(self.interner, ty, pat) + } TypeRef::Error => self.types.types.error, }; (ty, res) } + fn lower_pattern_type(&mut self, pat: PatId, ty: Ty<'db>) -> Option<Pattern<'db>> { + let pat_kind = match self.store[pat] { + hir_def::hir::Pat::Range { start: Some(start), end: Some(end), range_type: _ } => { + rustc_type_ir::PatternKind::Range { + start: self.lower_expr_as_const(start, ty), + end: self.lower_expr_as_const(end, ty), + } + } + hir_def::hir::Pat::NotNull => rustc_type_ir::PatternKind::NotNull, + hir_def::hir::Pat::Or(ref pats) => rustc_type_ir::PatternKind::Or( + PatList::new_from_iter( + self.interner, + pats.iter().map(|&pat| self.lower_pattern_type(pat, ty).ok_or(())), + ) + .ok()?, + ), + hir_def::hir::Pat::Missing => return None, + _ => { + never!("pattern type can only be Range, NotNull or Or"); + return None; + } + }; + Some(Pattern::new(self.interner, pat_kind)) + } + fn lower_fn_ptr(&mut self, fn_: &FnType) -> Ty<'db> { let interner = self.interner; let (params, ret_ty) = fn_.split_params_and_ret(); @@ -2072,12 +2115,12 @@ impl<'db> GenericPredicates { /// A cycle can occur from malformed code. fn generic_predicates_cycle_result( - _db: &dyn HirDatabase, + db: &dyn HirDatabase, _: salsa::Id, _def: GenericDefId, ) -> TyLoweringResult<GenericPredicates> { TyLoweringResult::empty(GenericPredicates::from_explicit_own_predicates( - StoredEarlyBinder::bind(Clauses::default().store()), + StoredEarlyBinder::bind(Clauses::empty(DbInterner::new_no_crate(db)).store()), )) } @@ -2086,7 +2129,7 @@ impl GenericPredicates { pub fn empty() -> &'static GenericPredicates { static EMPTY: OnceLock<GenericPredicates> = OnceLock::new(); EMPTY.get_or_init(|| GenericPredicates { - predicates: StoredEarlyBinder::bind(Clauses::default().store()), + predicates: StoredEarlyBinder::bind(Clauses::new_from_slice(&[]).store()), has_trait_implied_predicate: false, parent_explicit_self_predicates_start: 0, own_predicates_start: 0, @@ -2197,12 +2240,7 @@ pub(crate) fn param_env_from_predicates<'db>( ParamEnv { clauses } } -pub(crate) fn trait_environment<'db>( - db: &'db dyn HirDatabase, - def: ExpressionStoreOwnerId, -) -> ParamEnv<'db> { - let def = def.generic_def(db); - +pub(crate) fn trait_environment<'db>(db: &'db dyn HirDatabase, def: GenericDefId) -> ParamEnv<'db> { return ParamEnv { clauses: trait_environment_query(db, def).as_ref() }; #[salsa::tracked(returns(ref))] diff --git a/crates/hir-ty/src/lower/path.rs b/crates/hir-ty/src/lower/path.rs index ff9718af11..6633215679 100644 --- a/crates/hir-ty/src/lower/path.rs +++ b/crates/hir-ty/src/lower/path.rs @@ -994,6 +994,10 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> { }) }) } + + pub(crate) fn interner(&self) -> DbInterner<'db> { + self.ctx.interner + } } /// A const that were parsed like a type. diff --git a/crates/hir-ty/src/method_resolution.rs b/crates/hir-ty/src/method_resolution.rs index 5e90e371fc..9e0188fd26 100644 --- a/crates/hir-ty/src/method_resolution.rs +++ b/crates/hir-ty/src/method_resolution.rs @@ -35,7 +35,7 @@ use stdx::impl_from; use triomphe::Arc; use crate::{ - Span, all_super_traits, + InferenceDiagnostic, Span, all_super_traits, db::HirDatabase, infer::{InferenceContext, unify::InferenceTable}, lower::GenericPredicates, @@ -148,7 +148,7 @@ impl<'a, 'db> InferenceContext<'a, 'db> { debug!("result = {:?}", result); if result.illegal_sized_bound { - // FIXME: Report an error. + self.push_diagnostic(InferenceDiagnostic::MethodCallIllegalSizedBound { call_expr }); } self.write_expr_adj(receiver, result.adjustments); diff --git a/crates/hir-ty/src/method_resolution/confirm.rs b/crates/hir-ty/src/method_resolution/confirm.rs index c425e69dc5..d960a6547d 100644 --- a/crates/hir-ty/src/method_resolution/confirm.rs +++ b/crates/hir-ty/src/method_resolution/confirm.rs @@ -18,7 +18,7 @@ use crate::{ Adjust, Adjustment, AutoBorrow, IncorrectGenericsLenKind, InferenceDiagnostic, LifetimeElisionKind, PointerCast, Span, db::HirDatabase, - infer::{AllowTwoPhase, AutoBorrowMutability, InferenceContext}, + infer::{AllowTwoPhase, AutoBorrowMutability, ExplicitDropMethodUseKind, InferenceContext}, lower::{ GenericPredicates, path::{GenericArgsLowerer, TypeLikeConst, substs_from_args_and_bindings}, @@ -582,7 +582,9 @@ impl<'a, 'b, 'db> ConfirmContext<'a, 'b, 'db> { fn check_for_illegal_method_calls(&self) { // Disallow calls to the method `drop` defined in the `Drop` trait. if self.ctx.lang_items.Drop_drop.is_some_and(|drop_fn| drop_fn == self.candidate) { - // FIXME: Report an error. + self.ctx.push_diagnostic(InferenceDiagnostic::ExplicitDropMethodUse { + kind: ExplicitDropMethodUseKind::MethodCall(self.call_expr), + }); } } diff --git a/crates/hir-ty/src/method_resolution/probe.rs b/crates/hir-ty/src/method_resolution/probe.rs index 4b2f0cfd70..84edb51023 100644 --- a/crates/hir-ty/src/method_resolution/probe.rs +++ b/crates/hir-ty/src/method_resolution/probe.rs @@ -3,6 +3,7 @@ use std::{cell::RefCell, convert::Infallible, ops::ControlFlow}; +use base_db::FxIndexMap; use hir_def::{ AssocItemId, FunctionId, GenericParamId, ImplId, ItemContainerId, TraitId, hir::generics::GenericParams, @@ -10,7 +11,7 @@ use hir_def::{ }; use hir_expand::name::Name; use rustc_ast_ir::Mutability; -use rustc_hash::{FxHashMap, FxHashSet}; +use rustc_hash::FxHashSet; use rustc_type_ir::{ InferTy, TypeVisitableExt, Upcast, Variance, elaborate::{self, supertrait_def_ids}, @@ -719,7 +720,7 @@ impl<'db> ProbeChoice<'db> for ProbeForNameChoice<'db> { #[derive(Debug)] struct ProbeAllChoice<'db> { - candidates: RefCell<FxHashMap<CandidateId, CandidateWithPrivate<'db>>>, + candidates: RefCell<FxIndexMap<CandidateId, CandidateWithPrivate<'db>>>, considering_visible_candidates: bool, } @@ -1294,6 +1295,15 @@ impl<'a, 'db, Choice: ProbeChoice<'db>> ProbeContext<'a, 'db, Choice> { return ControlFlow::Break(by_value_pick); } + if self.mode == Mode::Path { + // Don't autoref in path mode. + // rustc doesn't do that and it's not a big deal as non-autorefd methods take priority + // and if an autorefd one is selected, we'll register the `NonAutorefdT: Trait` obligation + // (which will fail) anyway. But it does have an impact when probing for all methods, + // which is something we need to stay accurate. + return ControlFlow::Continue(()); + } + let autoref_pick = self.pick_autorefd_method( step, self_ty, diff --git a/crates/hir-ty/src/mir.rs b/crates/hir-ty/src/mir.rs index 5f61b1defb..e82038907c 100644 --- a/crates/hir-ty/src/mir.rs +++ b/crates/hir-ty/src/mir.rs @@ -1,6 +1,6 @@ //! MIR definitions and implementation -use std::{collections::hash_map::Entry, fmt::Display, iter}; +use std::{fmt::Display, iter}; use base_db::Crate; use either::Either; @@ -8,10 +8,14 @@ use hir_def::{ FieldId, StaticId, TupleFieldId, UnionId, VariantId, hir::{BindingId, Expr, ExprId, Ordering, PatId}, }; +use intern::{InternedSlice, InternedSliceRef, impl_slice_internable}; use la_arena::{Arena, ArenaMap, Idx, RawIdx}; use rustc_ast_ir::Mutability; use rustc_hash::FxHashMap; -use rustc_type_ir::inherent::{GenericArgs as _, IntoKind, Ty as _}; +use rustc_type_ir::{ + CollectAndApply, GenericTypeVisitable, + inherent::{GenericArgs as _, IntoKind, Ty as _}, +}; use smallvec::{SmallVec, smallvec}; use stdx::{impl_from, never}; @@ -24,6 +28,7 @@ use crate::{ next_solver::{ Allocation, AllocationData, DbInterner, ErrorGuaranteed, GenericArgs, ParamEnv, StoredAllocation, StoredConst, StoredGenericArgs, StoredTy, Ty, TyKind, + impl_stored_interned_slice, infer::{InferCtxt, traits::ObligationCause}, obligation_ctxt::ObligationCtxt, }, @@ -145,7 +150,7 @@ impl<'db> Operand { } } -#[derive(Debug, Clone, PartialEq, Eq, Hash)] +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] pub enum ProjectionElem<V: PartialEq> { Deref, Field(Either<FieldId, TupleFieldId>), @@ -155,7 +160,7 @@ pub enum ProjectionElem<V: PartialEq> { ConstantIndex { offset: u64, from_end: bool }, Subslice { from: u64, to: u64 }, //Downcast(Option<Symbol>, VariantIdx), - OpaqueCast(StoredTy), + OpaqueCast(std::convert::Infallible), } impl<V: PartialEq> ProjectionElem<V> { @@ -260,97 +265,114 @@ impl<V: PartialEq> ProjectionElem<V> { type PlaceElem = ProjectionElem<LocalId>; -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub struct ProjectionId(u32); +impl<W: crate::next_solver::WorldExposer> GenericTypeVisitable<W> for PlaceElem { + fn generic_visit_with(&self, _: &mut W) {} +} + +impl_slice_internable!(gc; ProjectionStorage, (), PlaceElem); +impl_stored_interned_slice!(ProjectionStorage, Projection, StoredProjection); -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct ProjectionStore { - id_to_proj: FxHashMap<ProjectionId, Box<[PlaceElem]>>, - proj_to_id: FxHashMap<Box<[PlaceElem]>, ProjectionId>, +#[derive(Clone, Copy, PartialEq, Eq, Hash)] +pub struct Projection<'db> { + interned: InternedSliceRef<'db, ProjectionStorage>, } -impl Default for ProjectionStore { - fn default() -> Self { - let mut this = Self { id_to_proj: Default::default(), proj_to_id: Default::default() }; - // Ensure that [] will get the id 0 which is used in `ProjectionId::Empty` - this.intern(Box::new([])); - this +impl<'db> std::fmt::Debug for Projection<'db> { + fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + (*self).as_slice().fmt(fmt) } } -impl ProjectionStore { - pub fn shrink_to_fit(&mut self) { - self.id_to_proj.shrink_to_fit(); - self.proj_to_id.shrink_to_fit(); +impl<'db> Projection<'db> { + pub fn new_from_iter<I, T>(args: I) -> T::Output + where + I: IntoIterator<Item = T>, + T: CollectAndApply<PlaceElem, Self>, + { + CollectAndApply::collect_and_apply(args.into_iter(), Self::new_from_slice) } - pub fn intern_if_exist(&self, projection: &[PlaceElem]) -> Option<ProjectionId> { - self.proj_to_id.get(projection).copied() + #[inline] + pub fn new_from_slice(slice: &[PlaceElem]) -> Self { + Self { interned: InternedSlice::from_header_and_slice((), slice) } } - pub fn intern(&mut self, projection: Box<[PlaceElem]>) -> ProjectionId { - let new_id = ProjectionId(self.proj_to_id.len() as u32); - match self.proj_to_id.entry(projection) { - Entry::Occupied(id) => *id.get(), - Entry::Vacant(e) => { - let key_clone = e.key().clone(); - e.insert(new_id); - self.id_to_proj.insert(new_id, key_clone); - new_id - } - } + #[inline] + pub fn as_slice(self) -> &'db [PlaceElem] { + &self.interned.get().slice + } + + pub fn project(self, projection: PlaceElem) -> Projection<'db> { + Projection::new_from_iter(self.as_slice().iter().copied().chain([projection])) } } -impl ProjectionId { - pub const EMPTY: ProjectionId = ProjectionId(0); +impl<'db> std::ops::Deref for Projection<'db> { + type Target = [PlaceElem]; - pub fn is_empty(self) -> bool { - self == ProjectionId::EMPTY + fn deref(&self) -> &Self::Target { + self.as_slice() } +} - pub fn lookup(self, store: &ProjectionStore) -> &[PlaceElem] { - store.id_to_proj.get(&self).unwrap() +impl StoredProjection { + // FIXME: rename to as_slice + pub fn lookup(&self) -> &[PlaceElem] { + self.as_ref().as_slice() } - pub fn project(self, projection: PlaceElem, store: &mut ProjectionStore) -> ProjectionId { - let mut current = self.lookup(store).to_vec(); - current.push(projection); - store.intern(current.into()) + pub fn is_empty(&self) -> bool { + self.lookup().is_empty() } } -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +// FIXME: would be nicer to rename PlaceRef -> Place, Place -> StoredPlace, but I didn't want to blow up the diff +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +pub struct PlaceRef<'db> { + pub local: LocalId, + pub projection: Projection<'db>, +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct Place { pub local: LocalId, - pub projection: ProjectionId, + pub projection: StoredProjection, } impl Place { - fn is_parent(&self, child: &Place, store: &ProjectionStore) -> bool { + pub fn as_ref<'db>(&self) -> PlaceRef<'db> { + PlaceRef { local: self.local, projection: self.projection.as_ref() } + } +} + +impl<'db> PlaceRef<'db> { + fn is_parent(&self, child: PlaceRef<'db>) -> bool { self.local == child.local - && child.projection.lookup(store).starts_with(self.projection.lookup(store)) + && child.projection.as_slice().starts_with(self.projection.as_slice()) } /// The place itself is not included - fn iterate_over_parents<'a>( - &'a self, - store: &'a ProjectionStore, - ) -> impl Iterator<Item = Place> + 'a { - let projection = self.projection.lookup(store); - (0..projection.len()).map(|x| &projection[0..x]).filter_map(move |x| { - Some(Place { local: self.local, projection: store.intern_if_exist(x)? }) + fn iterate_over_parents<'a>(&'a self) -> impl Iterator<Item = PlaceRef<'db>> + 'a { + let projection = self.projection.as_slice(); + (0..projection.len()).map(move |x| PlaceRef { + local: self.local, + projection: Projection::new_from_slice(&projection[0..x]), }) } - fn project(&self, projection: PlaceElem, store: &mut ProjectionStore) -> Place { - Place { local: self.local, projection: self.projection.project(projection, store) } + fn project(&self, projection: PlaceElem) -> PlaceRef<'db> { + PlaceRef { local: self.local, projection: self.projection.project(projection) } + } + + pub fn store(&self) -> Place { + Place { local: self.local, projection: self.projection.store() } } } -impl From<LocalId> for Place { +impl<'db> From<LocalId> for PlaceRef<'db> { fn from(local: LocalId) -> Self { - Self { local, projection: ProjectionId::EMPTY } + let empty: &[PlaceElem] = &[]; + PlaceRef { local, projection: Projection::new_from_slice(empty) } } } @@ -1081,7 +1103,6 @@ pub struct BasicBlock { #[derive(Debug, Clone, PartialEq, Eq)] pub struct MirBody { - pub projection_store: ProjectionStore, pub basic_blocks: Arena<BasicBlock>, pub locals: Arena<Local>, pub start_block: BasicBlockId, @@ -1099,15 +1120,11 @@ impl MirBody { self.binding_locals.iter().map(|(it, y)| (*y, it)).collect() } - fn walk_places(&mut self, mut f: impl FnMut(&mut Place, &mut ProjectionStore)) { - fn for_operand( - op: &mut Operand, - f: &mut impl FnMut(&mut Place, &mut ProjectionStore), - store: &mut ProjectionStore, - ) { + fn walk_places(&mut self, mut f: impl FnMut(&mut Place)) { + fn for_operand(op: &mut Operand, f: &mut impl FnMut(&mut Place)) { match &mut op.kind { OperandKind::Copy(p) | OperandKind::Move(p) => { - f(p, store); + f(p); } OperandKind::Constant { .. } | OperandKind::Static(_) @@ -1118,25 +1135,25 @@ impl MirBody { for statement in &mut block.statements { match &mut statement.kind { StatementKind::Assign(p, r) => { - f(p, &mut self.projection_store); + f(p); match r { Rvalue::ShallowInitBoxWithAlloc(_) => (), Rvalue::ShallowInitBox(o, _) | Rvalue::UnaryOp(_, o) | Rvalue::Cast(_, o, _) | Rvalue::Repeat(o, _) - | Rvalue::Use(o) => for_operand(o, &mut f, &mut self.projection_store), + | Rvalue::Use(o) => for_operand(o, &mut f), Rvalue::CopyForDeref(p) | Rvalue::Discriminant(p) | Rvalue::Len(p) - | Rvalue::Ref(_, p) => f(p, &mut self.projection_store), + | Rvalue::Ref(_, p) => f(p), Rvalue::CheckedBinaryOp(_, o1, o2) => { - for_operand(o1, &mut f, &mut self.projection_store); - for_operand(o2, &mut f, &mut self.projection_store); + for_operand(o1, &mut f); + for_operand(o2, &mut f); } Rvalue::Aggregate(_, ops) => { for op in ops.iter_mut() { - for_operand(op, &mut f, &mut self.projection_store); + for_operand(op, &mut f); } } Rvalue::ThreadLocalRef(n) @@ -1145,9 +1162,7 @@ impl MirBody { | Rvalue::NullaryOp(n) => match *n {}, } } - StatementKind::FakeRead(p) | StatementKind::Deinit(p) => { - f(p, &mut self.projection_store) - } + StatementKind::FakeRead(p) | StatementKind::Deinit(p) => f(p), StatementKind::StorageLive(_) | StatementKind::StorageDead(_) | StatementKind::Nop => (), @@ -1155,9 +1170,7 @@ impl MirBody { } match &mut block.terminator { Some(x) => match &mut x.kind { - TerminatorKind::SwitchInt { discr, .. } => { - for_operand(discr, &mut f, &mut self.projection_store) - } + TerminatorKind::SwitchInt { discr, .. } => for_operand(discr, &mut f), TerminatorKind::FalseEdge { .. } | TerminatorKind::FalseUnwind { .. } | TerminatorKind::Goto { .. } @@ -1167,24 +1180,23 @@ impl MirBody { | TerminatorKind::Return | TerminatorKind::Unreachable => (), TerminatorKind::Drop { place, .. } => { - f(place, &mut self.projection_store); + f(place); } TerminatorKind::DropAndReplace { place, value, .. } => { - f(place, &mut self.projection_store); - for_operand(value, &mut f, &mut self.projection_store); + f(place); + for_operand(value, &mut f); } TerminatorKind::Call { func, args, destination, .. } => { - for_operand(func, &mut f, &mut self.projection_store); - args.iter_mut() - .for_each(|x| for_operand(x, &mut f, &mut self.projection_store)); - f(destination, &mut self.projection_store); + for_operand(func, &mut f); + args.iter_mut().for_each(|x| for_operand(x, &mut f)); + f(destination); } TerminatorKind::Assert { cond, .. } => { - for_operand(cond, &mut f, &mut self.projection_store); + for_operand(cond, &mut f); } TerminatorKind::Yield { value, resume_arg, .. } => { - for_operand(value, &mut f, &mut self.projection_store); - f(resume_arg, &mut self.projection_store); + for_operand(value, &mut f); + f(resume_arg); } }, None => (), @@ -1202,9 +1214,7 @@ impl MirBody { upvar_locals, param_locals, closures, - projection_store, } = self; - projection_store.shrink_to_fit(); basic_blocks.shrink_to_fit(); locals.shrink_to_fit(); binding_locals.shrink_to_fit(); diff --git a/crates/hir-ty/src/mir/borrowck.rs b/crates/hir-ty/src/mir/borrowck.rs index 940bc57259..ff963fc121 100644 --- a/crates/hir-ty/src/mir/borrowck.rs +++ b/crates/hir-ty/src/mir/borrowck.rs @@ -141,7 +141,7 @@ pub fn borrowck_query( let _p = tracing::info_span!("borrowck_query").entered(); let module = def.module(db); let interner = DbInterner::new_with(db, module.krate(db)); - let env = db.trait_environment(def.expression_store_owner(db)); + let env = db.trait_environment(def.generic_def(db)); // This calculates opaques defining scope which is a bit costly therefore is put outside `all_mir_bodies()`. let typing_mode = TypingMode::borrowck(interner, def.into()); let res = all_mir_bodies( @@ -195,11 +195,11 @@ fn moved_out_of_ref<'db>( ) -> Vec<MovedOutOfRef> { let db = infcx.interner.db; let mut result = vec![]; - let mut for_operand = |op: &Operand, span: MirSpan| match op.kind { + let mut for_operand = |op: &Operand, span: MirSpan| match &op.kind { OperandKind::Copy(p) | OperandKind::Move(p) => { let mut ty: Ty<'db> = body.locals[p.local].ty.as_ref(); let mut is_dereference_of_ref = false; - for proj in p.projection.lookup(&body.projection_store) { + for proj in p.projection.lookup() { if *proj == ProjectionElem::Deref && ty.as_reference().is_some() { is_dereference_of_ref = true; } @@ -290,10 +290,10 @@ fn partially_moved<'db>( ) -> Vec<PartiallyMoved> { let db = infcx.interner.db; let mut result = vec![]; - let mut for_operand = |op: &Operand, span: MirSpan| match op.kind { + let mut for_operand = |op: &Operand, span: MirSpan| match &op.kind { OperandKind::Copy(p) | OperandKind::Move(p) => { let mut ty: Ty<'db> = body.locals[p.local].ty.as_ref(); - for proj in p.projection.lookup(&body.projection_store) { + for proj in p.projection.lookup() { ty = proj.projected_ty(infcx, env, ty, body.owner.module(db).krate(db)); } if !infcx.type_is_copy_modulo_regions(env, ty) && !ty.references_non_lt_error() { @@ -430,7 +430,7 @@ fn place_case<'db>( let db = infcx.interner.db; let mut is_part_of = false; let mut ty = body.locals[lvalue.local].ty.as_ref(); - for proj in lvalue.projection.lookup(&body.projection_store).iter() { + for proj in lvalue.projection.lookup().iter() { match proj { ProjectionElem::Deref if ty.as_adt().is_none() => return ProjectionCase::Indirect, // It's indirect in case of reference and raw ProjectionElem::Deref // It's direct in case of `Box<T>` @@ -470,7 +470,7 @@ fn ever_initialized_map( for statement in &block.statements { match &statement.kind { StatementKind::Assign(p, _) => { - if p.projection.lookup(&body.projection_store).is_empty() && p.local == l { + if p.projection.is_empty() && p.local == l { is_ever_initialized = true; } } @@ -508,9 +508,7 @@ fn ever_initialized_map( | TerminatorKind::Return | TerminatorKind::Unreachable => (), TerminatorKind::Call { target, cleanup, destination, .. } => { - if destination.projection.lookup(&body.projection_store).is_empty() - && destination.local == l - { + if destination.projection.is_empty() && destination.local == l { is_ever_initialized = true; } target.iter().chain(cleanup).for_each(|&it| process(it, is_ever_initialized)); @@ -566,7 +564,7 @@ fn record_usage(local: LocalId, result: &mut ArenaMap<LocalId, MutabilityReason> } fn record_usage_for_operand(arg: &Operand, result: &mut ArenaMap<LocalId, MutabilityReason>) { - if let OperandKind::Copy(p) | OperandKind::Move(p) = arg.kind { + if let OperandKind::Copy(p) | OperandKind::Move(p) = &arg.kind { record_usage(p.local, result); } } @@ -674,7 +672,7 @@ fn mutability_of_locals<'db>( for arg in args.iter() { record_usage_for_operand(arg, &mut result); } - if destination.projection.lookup(&body.projection_store).is_empty() { + if destination.projection.is_empty() { if ever_init_map.get(destination.local).copied().unwrap_or_default() { push_mut_span(destination.local, terminator.span, &mut result); } else { diff --git a/crates/hir-ty/src/mir/eval.rs b/crates/hir-ty/src/mir/eval.rs index 3372f6ec2e..78b70edeee 100644 --- a/crates/hir-ty/src/mir/eval.rs +++ b/crates/hir-ty/src/mir/eval.rs @@ -56,7 +56,7 @@ use crate::{ use super::{ AggregateKind, BasicBlockId, BinOp, CastKind, LocalId, MirBody, MirLowerError, MirSpan, - Operand, OperandKind, Place, PlaceElem, ProjectionElem, ProjectionStore, Rvalue, StatementKind, + Operand, OperandKind, Place, PlaceElem, PlaceRef, ProjectionElem, Rvalue, StatementKind, TerminatorKind, UnOp, return_slot, }; @@ -567,26 +567,26 @@ impl std::fmt::Debug for MirEvalError { type Result<'db, T> = std::result::Result<T, MirEvalError>; #[derive(Debug, Default)] -struct DropFlags { - need_drop: FxHashSet<Place>, +struct DropFlags<'db> { + need_drop: FxHashSet<PlaceRef<'db>>, } -impl DropFlags { - fn add_place(&mut self, p: Place, store: &ProjectionStore) { - if p.iterate_over_parents(store).any(|it| self.need_drop.contains(&it)) { +impl<'db> DropFlags<'db> { + fn add_place(&mut self, p: PlaceRef<'db>) { + if p.iterate_over_parents().any(|it| self.need_drop.contains(&it)) { return; } - self.need_drop.retain(|it| !p.is_parent(it, store)); + self.need_drop.retain(|it| !p.is_parent(*it)); self.need_drop.insert(p); } - fn remove_place(&mut self, p: &Place, store: &ProjectionStore) -> bool { + fn remove_place(&mut self, p: PlaceRef<'db>) -> bool { // FIXME: replace parents with parts - if let Some(parent) = p.iterate_over_parents(store).find(|it| self.need_drop.contains(it)) { + if let Some(parent) = p.iterate_over_parents().find(|it| self.need_drop.contains(it)) { self.need_drop.remove(&parent); return true; } - self.need_drop.remove(p) + self.need_drop.remove(&p) } fn clear(&mut self) { @@ -598,7 +598,7 @@ impl DropFlags { struct Locals<'a> { ptr: ArenaMap<LocalId, Interval>, body: &'a MirBody, - drop_flags: DropFlags, + drop_flags: DropFlags<'a>, } pub struct MirOutput { @@ -685,7 +685,7 @@ impl<'a, 'db: 'a> Evaluator<'a, 'db> { db, random_state: oorandom::Rand64::new(0), param_env: trait_env.unwrap_or_else(|| ParamEnvAndCrate { - param_env: db.trait_environment(owner.expression_store_owner(db)), + param_env: db.trait_environment(owner.generic_def(db)), krate: crate_id, }), crate_id, @@ -757,9 +757,9 @@ impl<'a, 'db: 'a> Evaluator<'a, 'db> { let mut addr = locals.ptr[p.local].addr; let mut ty: Ty<'db> = locals.body.locals[p.local].ty.as_ref(); let mut metadata: Option<IntervalOrOwned> = None; // locals are always sized - for proj in p.projection.lookup(&locals.body.projection_store) { + for proj in p.projection.lookup() { let prev_ty = ty; - ty = self.projected_ty(ty, proj.clone()); + ty = self.projected_ty(ty, *proj); match proj { ProjectionElem::Deref => { metadata = if self.size_align_of(ty, locals)?.is_none() { @@ -844,7 +844,7 @@ impl<'a, 'db: 'a> Evaluator<'a, 'db> { Variants::Multiple { variants, .. } => { &variants[match f.parent { hir_def::VariantId::EnumVariantId(it) => { - RustcEnumVariantIdx(it.lookup(self.db).index as usize) + RustcEnumVariantIdx(it.index(self.db)) } _ => { return Err(MirEvalError::InternalError( @@ -955,7 +955,7 @@ impl<'a, 'db: 'a> Evaluator<'a, 'db> { let addr = self.place_addr(l, locals)?; let result = self.eval_rvalue(r, locals)?; self.copy_from_interval_or_owned(addr, result)?; - locals.drop_flags.add_place(*l, &locals.body.projection_store); + locals.drop_flags.add_place(l.as_ref()); } StatementKind::Deinit(_) => not_supported!("de-init statement"), StatementKind::StorageLive(_) @@ -1008,9 +1008,7 @@ impl<'a, 'db: 'a> Evaluator<'a, 'db> { )?, it => not_supported!("unknown function type {it:?}"), }; - locals - .drop_flags - .add_place(*destination, &locals.body.projection_store); + locals.drop_flags.add_place(destination.as_ref()); if let Some(stack_frame) = stack_frame { self.code_stack.push(my_stack_frame); current_block_idx = stack_frame.locals.body.start_block; @@ -1091,7 +1089,7 @@ impl<'a, 'db: 'a> Evaluator<'a, 'db> { ) -> Result<'db, ()> { let mut remain_args = body.param_locals.len(); for ((l, interval), value) in locals.ptr.iter().skip(1).zip(args) { - locals.drop_flags.add_place(l.into(), &locals.body.projection_store); + locals.drop_flags.add_place(l.into()); match value { IntervalOrOwned::Owned(value) => interval.write_from_bytes(self, &value)?, IntervalOrOwned::Borrowed(value) => interval.write_from_interval(self, value)?, @@ -1594,7 +1592,7 @@ impl<'a, 'db: 'a> Evaluator<'a, 'db> { let max = 1i128 << (dest_bits - 1); (max - 1, -max) } else { - (1i128 << dest_bits, 0) + ((1i128 << dest_bits) - 1, 0) }; let value = (value as i128).min(max).max(min); let result = value.to_le_bytes(); @@ -1709,15 +1707,19 @@ impl<'a, 'db: 'a> Evaluator<'a, 'db> { if let Some(it) = goal(kind) { return Ok(it); } - if let TyKind::Adt(adt_ef, subst) = kind - && let AdtId::StructId(struct_id) = adt_ef.def_id() - { - let field_types = self.db.field_types(struct_id.into()); - if let Some(ty) = - field_types.iter().last().map(|it| it.1.get().instantiate(self.interner(), subst)) - { - return self.coerce_unsized_look_through_fields(ty.skip_norm_wip(), goal); + match kind { + TyKind::Adt(adt_ef, subst) if let AdtId::StructId(struct_id) = adt_ef.def_id() => { + let field_types = self.db.field_types(struct_id.into()); + if let Some(ty) = field_types + .iter() + .last() + .map(|it| it.1.get().instantiate(self.interner(), subst)) + { + return self.coerce_unsized_look_through_fields(ty.skip_norm_wip(), goal); + } } + TyKind::Pat(ty, _) => return self.coerce_unsized_look_through_fields(ty, goal), + _ => (), } Err(MirEvalError::CoerceUnsizedError(ty.store())) } @@ -1837,8 +1839,7 @@ impl<'a, 'db: 'a> Evaluator<'a, 'db> { _ => not_supported!("multi variant layout for non-enums"), }; let mut discriminant = self.const_eval_discriminant(enum_variant_id)?; - let lookup = enum_variant_id.lookup(self.db); - let rustc_enum_variant_idx = RustcEnumVariantIdx(lookup.index as usize); + let rustc_enum_variant_idx = RustcEnumVariantIdx(enum_variant_id.index(self.db)); let variant_layout = variants[rustc_enum_variant_idx].clone(); let have_tag = match tag_encoding { TagEncoding::Direct => true, @@ -1912,7 +1913,7 @@ impl<'a, 'db: 'a> Evaluator<'a, 'db> { fn eval_operand(&mut self, it: &Operand, locals: &mut Locals<'a>) -> Result<'db, Interval> { Ok(match &it.kind { OperandKind::Copy(p) | OperandKind::Move(p) => { - locals.drop_flags.remove_place(p, &locals.body.projection_store); + locals.drop_flags.remove_place(p.as_ref()); self.eval_place(p, locals)? } OperandKind::Static(st) => { @@ -3033,7 +3034,7 @@ impl<'a, 'db: 'a> Evaluator<'a, 'db> { span: MirSpan, ) -> Result<'db, ()> { let (addr, ty, metadata) = self.place_addr_and_ty_and_metadata(place, locals)?; - if !locals.drop_flags.remove_place(place, &locals.body.projection_store) { + if !locals.drop_flags.remove_place(place.as_ref()) { return Ok(()); } let metadata = match metadata { diff --git a/crates/hir-ty/src/mir/eval/tests.rs b/crates/hir-ty/src/mir/eval/tests.rs index 6bf966c3ef..0e94a5b92d 100644 --- a/crates/hir-ty/src/mir/eval/tests.rs +++ b/crates/hir-ty/src/mir/eval/tests.rs @@ -1,4 +1,4 @@ -use hir_def::{GenericDefId, HasModule, signatures::FunctionSignature}; +use hir_def::{HasModule, signatures::FunctionSignature}; use hir_expand::EditionedFileId; use span::Edition; use syntax::{TextRange, TextSize}; @@ -41,7 +41,7 @@ fn eval_main(db: &TestDB, file_id: EditionedFileId) -> Result<(String, String), func_id.into(), GenericArgs::empty(interner).store(), crate::ParamEnvAndCrate { - param_env: db.trait_environment(GenericDefId::from(func_id).into()), + param_env: db.trait_environment(func_id.into()), krate: func_id.krate(db), } .store(), diff --git a/crates/hir-ty/src/mir/lower.rs b/crates/hir-ty/src/mir/lower.rs index 3852db909e..4e52c1f7c3 100644 --- a/crates/hir-ty/src/mir/lower.rs +++ b/crates/hir-ty/src/mir/lower.rs @@ -45,9 +45,9 @@ use crate::{ mir::{ AggregateKind, Arena, BasicBlock, BasicBlockId, BinOp, BorrowKind, CastKind, Either, Expr, FieldId, GenericArgs, Idx, InferenceResult, Local, LocalId, MemoryMap, MirBody, MirSpan, - Mutability, Operand, Place, PlaceElem, PointerCast, ProjectionElem, ProjectionStore, - RawIdx, Rvalue, Statement, StatementKind, SwitchTargets, Terminator, TerminatorKind, - TupleFieldId, Ty, UnOp, VariantId, return_slot, + Mutability, Operand, Place, PlaceElem, PointerCast, Projection, ProjectionElem, RawIdx, + Rvalue, Statement, StatementKind, SwitchTargets, Terminator, TerminatorKind, TupleFieldId, + Ty, UnOp, VariantId, return_slot, }, next_solver::{ Const, DbInterner, ParamConst, ParamEnv, Region, StoredGenericArgs, StoredTy, TyKind, @@ -57,7 +57,7 @@ use crate::{ }, }; -use super::OperandKind; +use super::{OperandKind, PlaceRef}; mod as_place; mod pattern_matching; @@ -276,10 +276,11 @@ impl MirLowerError { db: &dyn HirDatabase, p: &Path, display_target: DisplayTarget, + owner: ExpressionStoreOwnerId, store: &ExpressionStore, ) -> Self { Self::UnresolvedName( - hir_display_with_store(p, store).display(db, display_target).to_string(), + hir_display_with_store(p, owner, store).display(db, display_target).to_string(), ) } } @@ -302,7 +303,6 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { let locals = Arena::new(); let binding_locals: ArenaMap<BindingId, LocalId> = ArenaMap::new(); let mir = MirBody { - projection_store: ProjectionStore::default(), basic_blocks, locals, start_block, @@ -313,8 +313,8 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { closures: vec![], }; let store_owner = owner.expression_store_owner(db); - let resolver = store_owner.resolver(db); - let env = db.trait_environment(store_owner); + let resolver = owner.resolver(db); + let env = db.trait_environment(owner.generic_def(db)); let interner = DbInterner::new_with(db, resolver.krate()); // FIXME(next-solver): Is `non_body_analysis()` correct here? Don't we want to reveal opaque types defined by this body? let infcx = interner.infer_ctxt().build(TypingMode::non_body_analysis()); @@ -370,13 +370,16 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { let Some((p, current)) = self.lower_expr_as_place(current, expr_id, true)? else { return Ok(None); }; - Ok(Some((Operand { kind: OperandKind::Copy(p), span: Some(expr_id.into()) }, current))) + Ok(Some(( + Operand { kind: OperandKind::Copy(p.store()), span: Some(expr_id.into()) }, + current, + ))) } fn lower_expr_to_place_with_adjust( &mut self, expr_id: ExprId, - place: Place, + place: PlaceRef<'db>, current: BasicBlockId, adjustments: &[Adjustment], ) -> Result<'db, Option<BasicBlockId>> { @@ -395,7 +398,7 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { self.push_assignment( current, place, - Operand { kind: OperandKind::Copy(p), span: None }.into(), + Operand { kind: OperandKind::Copy(p.store()), span: None }.into(), expr_id.into(), ); Ok(Some(current)) @@ -421,7 +424,7 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { place, Rvalue::Cast( CastKind::PointerCoercion(*cast), - Operand { kind: OperandKind::Copy(p), span: None }, + Operand { kind: OperandKind::Copy(p.store()), span: None }, last.target.clone(), ), expr_id.into(), @@ -436,7 +439,7 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { fn lower_expr_to_place_with_borrow_adjust( &mut self, expr_id: ExprId, - place: Place, + place: PlaceRef<'db>, current: BasicBlockId, rest: &[Adjustment], m: Mutability, @@ -447,14 +450,14 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { return Ok(None); }; let bk = BorrowKind::from_rustc_mutability(m); - self.push_assignment(current, place, Rvalue::Ref(bk, p), expr_id.into()); + self.push_assignment(current, place, Rvalue::Ref(bk, p.store()), expr_id.into()); Ok(Some(current)) } fn lower_expr_to_place( &mut self, expr_id: ExprId, - place: Place, + place: PlaceRef<'db>, prev_block: BasicBlockId, ) -> Result<'db, Option<BasicBlockId>> { if let Some(adjustments) = self.infer.expr_adjustments.get(&expr_id) { @@ -466,7 +469,7 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { fn lower_expr_to_place_without_adjust( &mut self, expr_id: ExprId, - place: Place, + place: PlaceRef<'db>, mut current: BasicBlockId, ) -> Result<'db, Option<BasicBlockId>> { match &self.store[expr_id] { @@ -517,6 +520,7 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { self.db, p, DisplayTarget::from_crate(self.db, self.krate()), + self.owner.expression_store_owner(self.db), self.store, ) })?; @@ -533,7 +537,7 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { self.push_assignment( current, place, - Operand { kind: OperandKind::Copy(temp), span: None }.into(), + Operand { kind: OperandKind::Copy(temp.store()), span: None }.into(), expr_id.into(), ); Ok(Some(current)) @@ -667,7 +671,7 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { self.lower_block_to_place(statements, current, *tail, place, expr_id.into()) } } - Expr::Loop { body, label } => { + Expr::Loop { body, label, source: _ } => { self.lower_loop(current, place, *label, expr_id.into(), |this, begin| { let scope = this.push_drop_scope(); if let Some((_, mut current)) = this.lower_expr_as_place(begin, *body, true)? { @@ -829,7 +833,9 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { .as_ref() .ok_or(MirLowerError::BreakWithoutLoop)?, }; - let Some(c) = self.lower_expr_to_place(expr, loop_data.place, current)? else { + let Some(c) = + self.lower_expr_to_place(expr, loop_data.place.as_ref(), current)? + else { return Ok(None); }; current = c; @@ -884,10 +890,12 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { }; let variant_id = self.infer.variant_resolution_for_expr(expr_id).ok_or_else(|| { - MirLowerError::UnresolvedName( - hir_display_with_store(path, self.store) - .display(self.db, self.display_target()) - .to_string(), + MirLowerError::unresolved_path( + self.db, + path, + self.display_target(), + self.owner.expression_store_owner(self.db), + self.store, ) })?; let subst = match self.expr_ty_without_adjust(expr_id).kind() { @@ -917,16 +925,18 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { .map(|(i, it)| match it { Some(it) => it, None => { - let p = sp.project( - ProjectionElem::Field(Either::Left(FieldId { + let p = sp.project(ProjectionElem::Field( + Either::Left(FieldId { parent: variant_id, local_id: LocalFieldId::from_raw(RawIdx::from( i as u32, )), - })), - &mut self.result.projection_store, - ); - Operand { kind: OperandKind::Copy(p), span: None } + }), + )); + Operand { + kind: OperandKind::Copy(p.store()), + span: None, + } } }) .collect(), @@ -944,13 +954,10 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { }; let local_id = variant_fields.field(name).ok_or(MirLowerError::UnresolvedField)?; - let place = place.project( - PlaceElem::Field(Either::Left(FieldId { - parent: union_id.into(), - local_id, - })), - &mut self.result.projection_store, - ); + let place = place.project(PlaceElem::Field(Either::Left(FieldId { + parent: union_id.into(), + local_id, + }))); self.lower_expr_to_place(*expr, place, current) } } @@ -997,7 +1004,7 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { return Ok(None); }; let bk = BorrowKind::from_hir_mutability(*mutability); - self.push_assignment(current, place, Rvalue::Ref(bk, p), expr_id.into()); + self.push_assignment(current, place, Rvalue::Ref(bk, p.store()), expr_id.into()); Ok(Some(current)) } Expr::Box { expr } => { @@ -1012,7 +1019,7 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { else { return Ok(None); }; - let p = place.project(ProjectionElem::Deref, &mut self.result.projection_store); + let p = place.project(ProjectionElem::Deref); self.push_assignment(current, p, operand.into(), expr_id.into()); Ok(Some(current)) } @@ -1027,7 +1034,7 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { self.push_assignment( current, place, - Operand { kind: OperandKind::Copy(p), span: None }.into(), + Operand { kind: OperandKind::Copy(p.store()), span: None }.into(), expr_id.into(), ); Ok(Some(current)) @@ -1120,7 +1127,7 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { }; let r_value = Rvalue::CheckedBinaryOp( op.into(), - Operand { kind: OperandKind::Copy(lhs_place), span: None }, + Operand { kind: OperandKind::Copy(lhs_place.store()), span: None }, rhs_op, ); self.push_assignment(current, lhs_place, r_value, expr_id.into()); @@ -1269,16 +1276,16 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { }; Ok(Place { local: this.binding_local(local)?, - projection: this - .result - .projection_store - .intern(convert_closure_capture_projections(self.db, place).collect()), + projection: Projection::new_from_iter(convert_closure_capture_projections( + self.db, place, + )) + .store(), }) }; for (place, _, sources) in &closure_data.fake_reads { let p = convert_place(self, place)?; - self.push_fake_read(current, p, span(sources)); + self.push_fake_read(current, p.as_ref(), span(sources)); } let captures = closure_data.min_captures.values().flatten(); @@ -1290,14 +1297,15 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { let tmp_ty = capture.captured_ty(self.db); // FIXME: Handle more than one span. let capture_span = span(&capture.info.sources); - let tmp: Place = self.temp(tmp_ty, current, capture_span)?.into(); + let tmp = self.temp(tmp_ty, current, capture_span)?.into(); self.push_assignment( current, tmp, Rvalue::Ref(BorrowKind::from_hir(bk), p), capture_span, ); - operands.push(Operand { kind: OperandKind::Move(tmp), span: None }); + operands + .push(Operand { kind: OperandKind::Move(tmp.store()), span: None }); } UpvarCapture::ByValue => { operands.push(Operand { kind: OperandKind::Move(p), span: None }) @@ -1392,24 +1400,24 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { } } - fn push_field_projection(&mut self, place: &mut Place, expr_id: ExprId) -> Result<'db, ()> { + fn push_field_projection( + &mut self, + place: &mut PlaceRef<'db>, + expr_id: ExprId, + ) -> Result<'db, ()> { if let Expr::Field { expr, name } = &self.store[expr_id] { if let TyKind::Tuple(..) = self.expr_ty_after_adjustments(*expr).kind() { let index = name.as_tuple_index().ok_or(MirLowerError::TypeError("named field on tuple"))? as u32; - *place = place.project( - ProjectionElem::Field(Either::Right(TupleFieldId { - tuple: TupleId(!0), // dummy as its unused - index, - })), - &mut self.result.projection_store, - ) + *place = place.project(ProjectionElem::Field(Either::Right(TupleFieldId { + tuple: TupleId(!0), // dummy as its unused + index, + }))) } else { let field = self.infer.field_resolution(expr_id).ok_or(MirLowerError::UnresolvedField)?; - *place = - place.project(ProjectionElem::Field(field), &mut self.result.projection_store); + *place = place.project(ProjectionElem::Field(field)); } } else { not_supported!("") @@ -1432,6 +1440,7 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { self.db, c, DisplayTarget::from_crate(db, owner.krate(db)), + self.owner.expression_store_owner(self.db), self.store, ) }; @@ -1523,7 +1532,7 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { &mut self, const_id: GeneralConstId, prev_block: BasicBlockId, - place: Place, + place: PlaceRef<'db>, subst: GenericArgs<'db>, span: MirSpan, ) -> Result<'db, ()> { @@ -1556,7 +1565,7 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { fn write_bytes_to_place( &mut self, prev_block: BasicBlockId, - place: Place, + place: PlaceRef<'db>, cv: Box<[u8]>, ty: Ty<'db>, span: MirSpan, @@ -1569,7 +1578,7 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { &mut self, variant_id: EnumVariantId, prev_block: BasicBlockId, - place: Place, + place: PlaceRef<'db>, ty: Ty<'db>, fields: Box<[Operand]>, span: MirSpan, @@ -1591,7 +1600,7 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { &mut self, func: Operand, args: impl Iterator<Item = ExprId>, - place: Place, + place: PlaceRef<'db>, mut current: BasicBlockId, is_uninhabited: bool, span: MirSpan, @@ -1616,7 +1625,7 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { &mut self, func: Operand, args: Box<[Operand]>, - place: Place, + place: PlaceRef<'db>, current: BasicBlockId, is_uninhabited: bool, span: MirSpan, @@ -1627,7 +1636,7 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { TerminatorKind::Call { func, args, - destination: place, + destination: place.store(), target: b, cleanup: None, from_hir_call: true, @@ -1667,31 +1676,31 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { self.result.basic_blocks[block].statements.push(statement); } - fn push_fake_read(&mut self, block: BasicBlockId, p: Place, span: MirSpan) { - self.push_statement(block, StatementKind::FakeRead(p).with_span(span)); + fn push_fake_read(&mut self, block: BasicBlockId, p: PlaceRef<'db>, span: MirSpan) { + self.push_statement(block, StatementKind::FakeRead(p.store()).with_span(span)); } fn push_assignment( &mut self, block: BasicBlockId, - place: Place, + place: PlaceRef<'db>, rvalue: Rvalue, span: MirSpan, ) { - self.push_statement(block, StatementKind::Assign(place, rvalue).with_span(span)); + self.push_statement(block, StatementKind::Assign(place.store(), rvalue).with_span(span)); } - fn discr_temp_place(&mut self, current: BasicBlockId) -> Place { + fn discr_temp_place(&mut self, current: BasicBlockId) -> PlaceRef<'db> { match &self.discr_temp { - Some(it) => *it, + Some(it) => it.as_ref(), None => { // FIXME: rustc's ty is dependent on the adt type, maybe we need to do that as well let discr_ty = Ty::new_int(self.interner(), rustc_type_ir::IntTy::I128); - let tmp: Place = self + let tmp: PlaceRef<'_> = self .temp(discr_ty, current, MirSpan::Unknown) .expect("discr_ty is never unsized") .into(); - self.discr_temp = Some(tmp); + self.discr_temp = Some(tmp.store()); tmp } } @@ -1700,7 +1709,7 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { fn lower_loop( &mut self, prev_block: BasicBlockId, - place: Place, + place: PlaceRef<'db>, label: Option<LabelId>, span: MirSpan, f: impl FnOnce(&mut MirLowerCtx<'_, 'db>, BasicBlockId) -> Result<'db, ()>, @@ -1709,7 +1718,7 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { let prev = self.current_loop_blocks.replace(LoopBlocks { begin, end: None, - place, + place: place.store(), drop_scope_index: self.drop_scopes.len(), }); let prev_label = if let Some(label) = label { @@ -1811,7 +1820,7 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { statements: &[hir_def::hir::Statement], mut current: BasicBlockId, tail: Option<ExprId>, - place: Place, + place: PlaceRef<'db>, span: MirSpan, ) -> Result<'db, Option<Idx<BasicBlock>>> { let scope = self.push_drop_scope(); @@ -2063,7 +2072,11 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { let prev = std::mem::replace(current, self.new_basic_block()); self.set_terminator( prev, - TerminatorKind::Drop { place: l.into(), target: *current, unwind: None }, + TerminatorKind::Drop { + place: PlaceRef::from(l).store(), + target: *current, + unwind: None, + }, span, ); } @@ -2197,15 +2210,18 @@ pub fn mir_body_for_closure_query<'db>( projections.push(ProjectionElem::ClosureField(capture_idx)); let capture_param_place = Place { local: closure_local, - projection: ctx.result.projection_store.intern(projections.into_boxed_slice()), - }; - let capture_local_place = Place { - local: capture_local, - projection: ctx.result.projection_store.intern(Box::new([])), + projection: Projection::new_from_slice(&projections).store(), }; + let capture_local_place = + Place { local: capture_local, projection: Projection::new_from_slice(&[]).store() }; let capture_local_rvalue = Rvalue::Use(Operand { kind: OperandKind::Move(capture_param_place), span: None }); - ctx.push_assignment(current, capture_local_place, capture_local_rvalue, MirSpan::Unknown); + ctx.push_assignment( + current, + capture_local_place.as_ref(), + capture_local_rvalue, + MirSpan::Unknown, + ); let local = capture.captured_local(); let local = ctx.binding_local(local)?; @@ -2225,8 +2241,8 @@ pub fn mir_body_for_closure_query<'db>( } let mut err = None; - ctx.result.walk_places(|mir_place, store| { - let mir_projections = mir_place.projection.lookup(store); + ctx.result.walk_places(|mir_place| { + let mir_projections = mir_place.projection.lookup(); if let Some(hir_places) = upvar_map.get(&mir_place.local) { let projections = hir_places.iter().find_map(|hir_place| { let iter = mir_projections @@ -2262,18 +2278,17 @@ pub fn mir_body_for_closure_query<'db>( match projections { Some((skip_projections_up_to, (hir_place, upvar_local))) => { mir_place.local = *upvar_local; - let mut result_projections = Vec::with_capacity( - usize::from(hir_place.is_by_ref()) - + (mir_projections.len() - skip_projections_up_to), - ); - if hir_place.is_by_ref() { - result_projections.push(ProjectionElem::Deref); - } - result_projections - .extend(mir_projections[skip_projections_up_to..].iter().cloned()); - mir_place.projection = store.intern(result_projections.into()); + let maybe_deref: &[PlaceElem] = + if hir_place.is_by_ref() { &[ProjectionElem::Deref] } else { &[] }; + mir_place.projection = Projection::new_from_iter( + maybe_deref + .iter() + .copied() + .chain(mir_projections[skip_projections_up_to..].iter().copied()), + ) + .store(); } - None => err = Some(*mir_place), + None => err = Some(mir_place.clone()), } } }); @@ -2309,10 +2324,7 @@ pub fn mir_body_query<'db>(db: &'db dyn HirDatabase, def: InferBodyId) -> Result .to_string(), InferBodyId::DefWithBodyId(DefWithBodyId::VariantId(it)) => { let loc = it.lookup(db); - loc.parent.enum_variants(db).variants[loc.index as usize] - .1 - .display(db, edition) - .to_string() + loc.name.display(db, edition).to_string() } InferBodyId::AnonConstId(_) => "{const}".to_owned(), }; diff --git a/crates/hir-ty/src/mir/lower/as_place.rs b/crates/hir-ty/src/mir/lower/as_place.rs index 2ed7aedecf..bb00a79480 100644 --- a/crates/hir-ty/src/mir/lower/as_place.rs +++ b/crates/hir-ty/src/mir/lower/as_place.rs @@ -14,15 +14,14 @@ impl<'db> MirLowerCtx<'_, 'db> { &mut self, expr_id: ExprId, prev_block: BasicBlockId, - ) -> Result<'db, Option<(Place, BasicBlockId)>> { + ) -> Result<'db, Option<(PlaceRef<'db>, BasicBlockId)>> { let ty = self.expr_ty_without_adjust(expr_id); - let place = self.temp(ty, prev_block, expr_id.into())?; - let Some(current) = - self.lower_expr_to_place_without_adjust(expr_id, place.into(), prev_block)? + let place = self.temp(ty, prev_block, expr_id.into())?.into(); + let Some(current) = self.lower_expr_to_place_without_adjust(expr_id, place, prev_block)? else { return Ok(None); }; - Ok(Some((place.into(), current))) + Ok(Some((place, current))) } fn lower_expr_to_some_place_with_adjust( @@ -30,18 +29,18 @@ impl<'db> MirLowerCtx<'_, 'db> { expr_id: ExprId, prev_block: BasicBlockId, adjustments: &[Adjustment], - ) -> Result<'db, Option<(Place, BasicBlockId)>> { + ) -> Result<'db, Option<(PlaceRef<'db>, BasicBlockId)>> { let ty = adjustments .last() .map(|it| it.target.as_ref()) .unwrap_or_else(|| self.expr_ty_without_adjust(expr_id)); - let place = self.temp(ty, prev_block, expr_id.into())?; + let place = self.temp(ty, prev_block, expr_id.into())?.into(); let Some(current) = - self.lower_expr_to_place_with_adjust(expr_id, place.into(), prev_block, adjustments)? + self.lower_expr_to_place_with_adjust(expr_id, place, prev_block, adjustments)? else { return Ok(None); }; - Ok(Some((place.into(), current))) + Ok(Some((place, current))) } pub(super) fn lower_expr_as_place_with_adjust( @@ -50,7 +49,7 @@ impl<'db> MirLowerCtx<'_, 'db> { expr_id: ExprId, upgrade_rvalue: bool, adjustments: &[Adjustment], - ) -> Result<'db, Option<(Place, BasicBlockId)>> { + ) -> Result<'db, Option<(PlaceRef<'db>, BasicBlockId)>> { let try_rvalue = |this: &mut MirLowerCtx<'_, 'db>| { if !upgrade_rvalue { return Err(MirLowerError::MutatingRvalue); @@ -69,7 +68,7 @@ impl<'db> MirLowerCtx<'_, 'db> { else { return Ok(None); }; - it.0 = it.0.project(ProjectionElem::Deref, &mut self.result.projection_store); + it.0 = it.0.project(ProjectionElem::Deref); Ok(Some(it)) } Adjust::Deref(Some(od)) => { @@ -108,7 +107,7 @@ impl<'db> MirLowerCtx<'_, 'db> { current: BasicBlockId, expr_id: ExprId, upgrade_rvalue: bool, - ) -> Result<'db, Option<(Place, BasicBlockId)>> { + ) -> Result<'db, Option<(PlaceRef<'db>, BasicBlockId)>> { match self.infer.expr_adjustments.get(&expr_id) { Some(a) => self.lower_expr_as_place_with_adjust(current, expr_id, upgrade_rvalue, a), None => self.lower_expr_as_place_without_adjust(current, expr_id, upgrade_rvalue), @@ -120,7 +119,7 @@ impl<'db> MirLowerCtx<'_, 'db> { current: BasicBlockId, expr_id: ExprId, upgrade_rvalue: bool, - ) -> Result<'db, Option<(Place, BasicBlockId)>> { + ) -> Result<'db, Option<(PlaceRef<'db>, BasicBlockId)>> { let try_rvalue = |this: &mut MirLowerCtx<'_, 'db>| { if !upgrade_rvalue { return Err(MirLowerError::MutatingRvalue); @@ -149,17 +148,14 @@ impl<'db> MirLowerCtx<'_, 'db> { ty, Mutability::Not, ); - let temp: Place = self.temp(ref_ty, current, expr_id.into())?.into(); + let temp = self.temp(ref_ty, current, expr_id.into())?.into(); self.push_assignment( current, temp, Operand { kind: OperandKind::Static(s), span: None }.into(), expr_id.into(), ); - Ok(Some(( - temp.project(ProjectionElem::Deref, &mut self.result.projection_store), - current, - ))) + Ok(Some((temp.project(ProjectionElem::Deref), current))) } _ => try_rvalue(self), } @@ -193,7 +189,7 @@ impl<'db> MirLowerCtx<'_, 'db> { let Some((mut r, current)) = self.lower_expr_as_place(current, *expr, true)? else { return Ok(None); }; - r = r.project(ProjectionElem::Deref, &mut self.result.projection_store); + r = r.project(ProjectionElem::Deref); Ok(Some((r, current))) } Expr::UnaryOp { .. } => try_rvalue(self), @@ -256,8 +252,7 @@ impl<'db> MirLowerCtx<'_, 'db> { else { return Ok(None); }; - p_base = p_base - .project(ProjectionElem::Index(l_index), &mut self.result.projection_store); + p_base = p_base.project(ProjectionElem::Index(l_index)); Ok(Some((p_base, current))) } _ => try_rvalue(self), @@ -267,20 +262,20 @@ impl<'db> MirLowerCtx<'_, 'db> { fn lower_overloaded_index( &mut self, current: BasicBlockId, - place: Place, + place: PlaceRef<'db>, base_ty: Ty<'db>, result_ty: Ty<'db>, index_operand: Operand, span: MirSpan, index_fn: (FunctionId, GenericArgs<'db>), - ) -> Result<'db, Option<(Place, BasicBlockId)>> { + ) -> Result<'db, Option<(PlaceRef<'db>, BasicBlockId)>> { let mutability = match base_ty.as_reference() { Some((_, _, mutability)) => mutability, None => Mutability::Not, }; let result_ref = Ty::new_ref(self.interner(), Region::error(self.interner()), result_ty, mutability); - let mut result: Place = self.temp(result_ref, current, span)?.into(); + let mut result = self.temp(result_ref, current, span)?.into(); let index_fn_op = Operand::const_zst(Ty::new_fn_def( self.interner(), CallableDefId::FunctionId(index_fn.0).into(), @@ -288,7 +283,10 @@ impl<'db> MirLowerCtx<'_, 'db> { )); let Some(current) = self.lower_call( index_fn_op, - Box::new([Operand { kind: OperandKind::Copy(place), span: None }, index_operand]), + Box::new([ + Operand { kind: OperandKind::Copy(place.store()), span: None }, + index_operand, + ]), result, current, false, @@ -297,19 +295,19 @@ impl<'db> MirLowerCtx<'_, 'db> { else { return Ok(None); }; - result = result.project(ProjectionElem::Deref, &mut self.result.projection_store); + result = result.project(ProjectionElem::Deref); Ok(Some((result, current))) } fn lower_overloaded_deref( &mut self, current: BasicBlockId, - place: Place, + place: PlaceRef<'db>, source_ty: Ty<'db>, target_ty: Ty<'db>, span: MirSpan, mutability: bool, - ) -> Result<'db, Option<(Place, BasicBlockId)>> { + ) -> Result<'db, Option<(PlaceRef<'db>, BasicBlockId)>> { let lang_items = self.lang_items(); let (mutability, deref_fn, borrow_kind) = if !mutability { (Mutability::Not, lang_items.Deref_deref, BorrowKind::Shared) @@ -323,18 +321,18 @@ impl<'db> MirLowerCtx<'_, 'db> { let error_region = Region::error(self.interner()); let ty_ref = Ty::new_ref(self.interner(), error_region, source_ty, mutability); let target_ty_ref = Ty::new_ref(self.interner(), error_region, target_ty, mutability); - let ref_place: Place = self.temp(ty_ref, current, span)?.into(); - self.push_assignment(current, ref_place, Rvalue::Ref(borrow_kind, place), span); + let ref_place = self.temp(ty_ref, current, span)?.into(); + self.push_assignment(current, ref_place, Rvalue::Ref(borrow_kind, place.store()), span); let deref_fn = deref_fn.ok_or(MirLowerError::LangItemNotFound)?; let deref_fn_op = Operand::const_zst(Ty::new_fn_def( self.interner(), CallableDefId::FunctionId(deref_fn).into(), GenericArgs::new_from_slice(&[source_ty.into()]), )); - let mut result: Place = self.temp(target_ty_ref, current, span)?.into(); + let mut result = self.temp(target_ty_ref, current, span)?.into(); let Some(current) = self.lower_call( deref_fn_op, - Box::new([Operand { kind: OperandKind::Copy(ref_place), span: None }]), + Box::new([Operand { kind: OperandKind::Copy(ref_place.store()), span: None }]), result, current, false, @@ -343,7 +341,7 @@ impl<'db> MirLowerCtx<'_, 'db> { else { return Ok(None); }; - result = result.project(ProjectionElem::Deref, &mut self.result.projection_store); + result = result.project(ProjectionElem::Deref); Ok(Some((result, current))) } } diff --git a/crates/hir-ty/src/mir/lower/pattern_matching.rs b/crates/hir-ty/src/mir/lower/pattern_matching.rs index c924c5bdf0..c306b6ca15 100644 --- a/crates/hir-ty/src/mir/lower/pattern_matching.rs +++ b/crates/hir-ty/src/mir/lower/pattern_matching.rs @@ -6,10 +6,10 @@ use rustc_type_ir::inherent::{IntoKind, Ty as _}; use crate::{ BindingMode, ByRef, mir::{ - LocalId, MutBorrowKind, Operand, OperandKind, + LocalId, MutBorrowKind, Operand, OperandKind, PlaceRef, Projection, lower::{ BasicBlockId, BinOp, BindingId, BorrowKind, Either, Expr, FieldId, Idx, MemoryMap, - MirLowerCtx, MirLowerError, MirSpan, Pat, PatId, Place, PlaceElem, ProjectionElem, + MirLowerCtx, MirLowerError, MirSpan, Pat, PatId, PlaceElem, ProjectionElem, RecordFieldPat, ResolveValueResult, Result, Rvalue, SwitchTargets, TerminatorKind, TupleFieldId, TupleId, Ty, TyKind, ValueNs, VariantId, }, @@ -65,7 +65,7 @@ impl<'db> MirLowerCtx<'_, 'db> { &mut self, current: BasicBlockId, current_else: Option<BasicBlockId>, - cond_place: Place, + cond_place: PlaceRef<'db>, pattern: PatId, ) -> Result<'db, (BasicBlockId, Option<BasicBlockId>)> { let (current, current_else) = self.pattern_match_inner( @@ -88,7 +88,7 @@ impl<'db> MirLowerCtx<'_, 'db> { pub(super) fn pattern_match_assignment( &mut self, current: BasicBlockId, - value: Place, + value: PlaceRef<'db>, pattern: PatId, ) -> Result<'db, BasicBlockId> { let (current, _) = @@ -116,23 +116,23 @@ impl<'db> MirLowerCtx<'_, 'db> { &mut self, mut current: BasicBlockId, mut current_else: Option<BasicBlockId>, - mut cond_place: Place, + mut cond_place: PlaceRef<'db>, pattern: PatId, mode: MatchingMode, ) -> Result<'db, (BasicBlockId, Option<BasicBlockId>)> { let cnt = self.infer.pat_adjustments.get(&pattern).map(|x| x.len()).unwrap_or_default(); - cond_place.projection = self.result.projection_store.intern( + cond_place.projection = Projection::new_from_iter( cond_place .projection - .lookup(&self.result.projection_store) + .as_slice() .iter() .cloned() - .chain((0..cnt).map(|_| ProjectionElem::Deref)) - .collect::<Vec<_>>() - .into(), + .chain((0..cnt).map(|_| ProjectionElem::Deref)), ); Ok(match &self.store[pattern] { - Pat::Missing | Pat::Rest => return Err(MirLowerError::IncompletePattern), + Pat::Missing | Pat::Rest | Pat::NotNull => { + return Err(MirLowerError::IncompletePattern); + } Pat::Wild => (current, current_else), Pat::Tuple { args, ellipsis } => { let subst = match self.infer.pat_ty(pattern).kind() { @@ -213,7 +213,7 @@ impl<'db> MirLowerCtx<'_, 'db> { self.lower_literal_or_const_to_operand(self.infer.pat_ty(pattern), l)?; let else_target = *current_else.get_or_insert_with(|| self.new_basic_block()); let next = self.new_basic_block(); - let discr: Place = + let discr = self.temp(Ty::new_bool(self.interner()), current, pattern.into())?.into(); self.push_assignment( current, @@ -221,11 +221,11 @@ impl<'db> MirLowerCtx<'_, 'db> { Rvalue::CheckedBinaryOp( binop, lv, - Operand { kind: OperandKind::Copy(cond_place), span: None }, + Operand { kind: OperandKind::Copy(cond_place.store()), span: None }, ), pattern.into(), ); - let discr = Operand { kind: OperandKind::Copy(discr), span: None }; + let discr = Operand { kind: OperandKind::Copy(discr.store()), span: None }; self.set_terminator( current, TerminatorKind::SwitchInt { @@ -261,13 +261,13 @@ impl<'db> MirLowerCtx<'_, 'db> { // emit runtime length check for slice if let TyKind::Slice(_) = pat_ty.kind() { let pattern_len = prefix.len() + suffix.len(); - let place_len: Place = self + let place_len = self .temp(Ty::new_usize(self.interner()), current, pattern.into())? .into(); self.push_assignment( current, place_len, - Rvalue::Len(cond_place), + Rvalue::Len(cond_place.store()), pattern.into(), ); let else_target = @@ -278,7 +278,7 @@ impl<'db> MirLowerCtx<'_, 'db> { current, TerminatorKind::SwitchInt { discr: Operand { - kind: OperandKind::Copy(place_len), + kind: OperandKind::Copy(place_len.store()), span: None, }, targets: SwitchTargets::static_if( @@ -295,7 +295,7 @@ impl<'db> MirLowerCtx<'_, 'db> { MemoryMap::default(), Ty::new_usize(self.interner()), ); - let discr: Place = self + let discr = self .temp(Ty::new_bool(self.interner()), current, pattern.into())? .into(); self.push_assignment( @@ -304,11 +304,15 @@ impl<'db> MirLowerCtx<'_, 'db> { Rvalue::CheckedBinaryOp( BinOp::Le, c, - Operand { kind: OperandKind::Copy(place_len), span: None }, + Operand { + kind: OperandKind::Copy(place_len.store()), + span: None, + }, ), pattern.into(), ); - let discr = Operand { kind: OperandKind::Copy(discr), span: None }; + let discr = + Operand { kind: OperandKind::Copy(discr.store()), span: None }; self.set_terminator( current, TerminatorKind::SwitchInt { @@ -322,10 +326,10 @@ impl<'db> MirLowerCtx<'_, 'db> { } } for (i, &pat) in prefix.iter().enumerate() { - let next_place = cond_place.project( - ProjectionElem::ConstantIndex { offset: i as u64, from_end: false }, - &mut self.result.projection_store, - ); + let next_place = cond_place.project(ProjectionElem::ConstantIndex { + offset: i as u64, + from_end: false, + }); (current, current_else) = self.pattern_match_inner(current, current_else, next_place, pat, mode)?; } @@ -333,13 +337,10 @@ impl<'db> MirLowerCtx<'_, 'db> { && mode != MatchingMode::Check && let Pat::Bind { id, subpat: _ } = self.store[slice] { - let next_place = cond_place.project( - ProjectionElem::Subslice { - from: prefix.len() as u64, - to: suffix.len() as u64, - }, - &mut self.result.projection_store, - ); + let next_place = cond_place.project(ProjectionElem::Subslice { + from: prefix.len() as u64, + to: suffix.len() as u64, + }); let mode = self.infer.binding_modes[slice]; (current, current_else) = self.pattern_match_binding( id, @@ -351,10 +352,10 @@ impl<'db> MirLowerCtx<'_, 'db> { )?; } for (i, &pat) in suffix.iter().enumerate() { - let next_place = cond_place.project( - ProjectionElem::ConstantIndex { offset: i as u64, from_end: true }, - &mut self.result.projection_store, - ); + let next_place = cond_place.project(ProjectionElem::ConstantIndex { + offset: i as u64, + from_end: true, + }); (current, current_else) = self.pattern_match_inner(current, current_else, next_place, pat, mode)?; } @@ -376,6 +377,7 @@ impl<'db> MirLowerCtx<'_, 'db> { self.db, p, self.display_target(), + self.owner.expression_store_owner(self.db), self.store, ) }; @@ -417,19 +419,19 @@ impl<'db> MirLowerCtx<'_, 'db> { } not_supported!("path in pattern position that is not const or variant") }; - let tmp: Place = + let tmp = self.temp(self.infer.pat_ty(pattern), current, pattern.into())?.into(); let span = pattern.into(); self.lower_const(c.into(), current, tmp, subst, span)?; - let tmp2: Place = + let tmp2 = self.temp(Ty::new_bool(self.interner()), current, pattern.into())?.into(); self.push_assignment( current, tmp2, Rvalue::CheckedBinaryOp( BinOp::Eq, - Operand { kind: OperandKind::Copy(tmp), span: None }, - Operand { kind: OperandKind::Copy(cond_place), span: None }, + Operand { kind: OperandKind::Copy(tmp.store()), span: None }, + Operand { kind: OperandKind::Copy(cond_place.store()), span: None }, ), span, ); @@ -438,7 +440,7 @@ impl<'db> MirLowerCtx<'_, 'db> { self.set_terminator( current, TerminatorKind::SwitchInt { - discr: Operand { kind: OperandKind::Copy(tmp2), span: None }, + discr: Operand { kind: OperandKind::Copy(tmp2.store()), span: None }, targets: SwitchTargets::static_if(1, next, else_target), }, span, @@ -491,8 +493,7 @@ impl<'db> MirLowerCtx<'_, 'db> { )? } Pat::Ref { pat, mutability: _ } => { - let cond_place = - cond_place.project(ProjectionElem::Deref, &mut self.result.projection_store); + let cond_place = cond_place.project(ProjectionElem::Deref); self.pattern_match_inner(current, current_else, cond_place, *pat, mode)? } &Pat::Expr(expr) => { @@ -507,7 +508,7 @@ impl<'db> MirLowerCtx<'_, 'db> { self.push_assignment( current, lhs_place, - Operand { kind: OperandKind::Copy(cond_place), span: None }.into(), + Operand { kind: OperandKind::Copy(cond_place.store()), span: None }.into(), expr.into(), ); (current, current_else) @@ -522,7 +523,7 @@ impl<'db> MirLowerCtx<'_, 'db> { &mut self, id: BindingId, mode: BindingMode, - cond_place: Place, + cond_place: PlaceRef<'db>, span: MirSpan, current: BasicBlockId, current_else: Option<BasicBlockId>, @@ -538,7 +539,7 @@ impl<'db> MirLowerCtx<'_, 'db> { current: BasicBlockId, target_place: LocalId, mode: BindingMode, - cond_place: Place, + cond_place: PlaceRef<'db>, span: MirSpan, ) { self.push_assignment( @@ -546,14 +547,15 @@ impl<'db> MirLowerCtx<'_, 'db> { target_place.into(), match mode { BindingMode(ByRef::No, _) => { - Operand { kind: OperandKind::Copy(cond_place), span: None }.into() + Operand { kind: OperandKind::Copy(cond_place.store()), span: None }.into() } BindingMode(ByRef::Yes(rustc_ast_ir::Mutability::Not), _) => { - Rvalue::Ref(BorrowKind::Shared, cond_place) - } - BindingMode(ByRef::Yes(rustc_ast_ir::Mutability::Mut), _) => { - Rvalue::Ref(BorrowKind::Mut { kind: MutBorrowKind::Default }, cond_place) + Rvalue::Ref(BorrowKind::Shared, cond_place.store()) } + BindingMode(ByRef::Yes(rustc_ast_ir::Mutability::Mut), _) => Rvalue::Ref( + BorrowKind::Mut { kind: MutBorrowKind::Default }, + cond_place.store(), + ), }, span, ); @@ -564,24 +566,23 @@ impl<'db> MirLowerCtx<'_, 'db> { current_else: Option<BasicBlockId>, current: BasicBlockId, c: Operand, - cond_place: Place, + cond_place: PlaceRef<'db>, pattern: Idx<Pat>, ) -> Result<'db, (BasicBlockId, Option<BasicBlockId>)> { let then_target = self.new_basic_block(); let else_target = current_else.unwrap_or_else(|| self.new_basic_block()); - let discr: Place = - self.temp(Ty::new_bool(self.interner()), current, pattern.into())?.into(); + let discr = self.temp(Ty::new_bool(self.interner()), current, pattern.into())?.into(); self.push_assignment( current, discr, Rvalue::CheckedBinaryOp( BinOp::Eq, c, - Operand { kind: OperandKind::Copy(cond_place), span: None }, + Operand { kind: OperandKind::Copy(cond_place.store()), span: None }, ), pattern.into(), ); - let discr = Operand { kind: OperandKind::Copy(discr), span: None }; + let discr = Operand { kind: OperandKind::Copy(discr.store()), span: None }; self.set_terminator( current, TerminatorKind::SwitchInt { @@ -595,7 +596,7 @@ impl<'db> MirLowerCtx<'_, 'db> { fn pattern_matching_variant( &mut self, - cond_place: Place, + cond_place: PlaceRef<'db>, variant: VariantId, mut current: BasicBlockId, span: MirSpan, @@ -608,13 +609,18 @@ impl<'db> MirLowerCtx<'_, 'db> { if mode == MatchingMode::Check { let e = self.const_eval_discriminant(v)? as u128; let tmp = self.discr_temp_place(current); - self.push_assignment(current, tmp, Rvalue::Discriminant(cond_place), span); + self.push_assignment( + current, + tmp, + Rvalue::Discriminant(cond_place.store()), + span, + ); let next = self.new_basic_block(); let else_target = current_else.get_or_insert_with(|| self.new_basic_block()); self.set_terminator( current, TerminatorKind::SwitchInt { - discr: Operand { kind: OperandKind::Copy(tmp), span: None }, + discr: Operand { kind: OperandKind::Copy(tmp.store()), span: None }, targets: SwitchTargets::static_if(e, next, *else_target), }, span, @@ -653,7 +659,7 @@ impl<'db> MirLowerCtx<'_, 'db> { v: VariantId, current: BasicBlockId, current_else: Option<BasicBlockId>, - cond_place: &Place, + cond_place: &PlaceRef<'db>, mode: MatchingMode, ) -> Result<'db, (BasicBlockId, Option<BasicBlockId>)> { Ok(match shape { @@ -697,11 +703,11 @@ impl<'db> MirLowerCtx<'_, 'db> { mut current: BasicBlockId, mut current_else: Option<BasicBlockId>, args: impl Iterator<Item = (PlaceElem, PatId)>, - cond_place: &Place, + cond_place: &PlaceRef<'db>, mode: MatchingMode, ) -> Result<'db, (BasicBlockId, Option<BasicBlockId>)> { for (proj, arg) in args { - let cond_place = cond_place.project(proj, &mut self.result.projection_store); + let cond_place = cond_place.project(proj); (current, current_else) = self.pattern_match_inner(current, current_else, cond_place, arg, mode)?; } @@ -715,7 +721,7 @@ impl<'db> MirLowerCtx<'_, 'db> { args: &[PatId], ellipsis: Option<u32>, fields: impl DoubleEndedIterator<Item = PlaceElem> + Clone, - cond_place: &Place, + cond_place: &PlaceRef<'db>, mode: MatchingMode, ) -> Result<'db, (BasicBlockId, Option<BasicBlockId>)> { let (al, ar) = args.split_at(ellipsis.map_or(args.len(), |it| it as usize)); diff --git a/crates/hir-ty/src/mir/pretty.rs b/crates/hir-ty/src/mir/pretty.rs index 777cf170bc..9054987066 100644 --- a/crates/hir-ty/src/mir/pretty.rs +++ b/crates/hir-ty/src/mir/pretty.rs @@ -341,9 +341,7 @@ impl<'a, 'db> MirPrettyCtx<'a, 'db> { w!( this, " as {}).{}", - loc.parent.enum_variants(this.db).variants[loc.index as usize] - .1 - .display(this.db, this.display_target.edition), + loc.name.display(this.db, this.display_target.edition), name.display(this.db, this.display_target.edition) ); } @@ -375,7 +373,7 @@ impl<'a, 'db> MirPrettyCtx<'a, 'db> { } } } - f(self, p.local, p.projection.lookup(&self.body.projection_store)); + f(self, p.local, p.projection.lookup()); } fn operand(&mut self, r: &Operand) { diff --git a/crates/hir-ty/src/next_solver.rs b/crates/hir-ty/src/next_solver.rs index 47b4b1dc4a..f0d33ad2dd 100644 --- a/crates/hir-ty/src/next_solver.rs +++ b/crates/hir-ty/src/next_solver.rs @@ -120,6 +120,7 @@ pub struct DefaultEmpty<'db> { pub clauses: Clauses<'db>, pub region_assumptions: RegionAssumptions<'db>, pub consts: Consts<'db>, + pub projection: crate::mir::Projection<'db>, } pub struct DefaultAny<'db> { @@ -237,6 +238,12 @@ pub fn default_types<'a, 'db>(db: &'db dyn HirDatabase) -> &'a DefaultAny<'db> { let ty = ManuallyDrop::new(ty.store()); ty.as_ref() }; + let create_projection = |slice| { + let it = crate::mir::Projection::new_from_slice(slice); + // We need to increase the refcount (forever), so that the types won't be freed. + let it = ManuallyDrop::new(it.store()); + it.as_ref() + }; let str = create_ty(TyKind::Str); let statik = create_region(RegionKind::ReStatic); @@ -303,6 +310,7 @@ pub fn default_types<'a, 'db>(db: &'db dyn HirDatabase) -> &'a DefaultAny<'db> { clauses: create_clauses(&[]), region_assumptions: create_region_assumptions(&[]), consts: create_consts(&[]), + projection: create_projection(&[]), }, one_invariant: create_variances_of(&[rustc_type_ir::Variance::Invariant]), one_covariant: create_variances_of(&[rustc_type_ir::Variance::Covariant]), diff --git a/crates/hir-ty/src/next_solver/consts/valtree.rs b/crates/hir-ty/src/next_solver/consts/valtree.rs index b856ee5a85..bef2386706 100644 --- a/crates/hir-ty/src/next_solver/consts/valtree.rs +++ b/crates/hir-ty/src/next_solver/consts/valtree.rs @@ -8,6 +8,7 @@ use stdx::never; use crate::{ MemoryMap, ParamEnvAndCrate, consteval, + db::HirDatabase, mir::pad16, next_solver::{Const, Consts, TyKind, WorldExposer}, }; @@ -32,6 +33,34 @@ impl<'db> ValueConst<'db> { let value = ValTree::new(kind); ValueConst { ty, value } } + + /// Attempts to convert to a `ValTreeKind::Leaf` value. + pub fn try_to_leaf(self) -> Option<ScalarInt> { + match self.value.inner() { + ValTreeKind::Leaf(s) => Some(*s), + ValTreeKind::Branch(_) => None, + } + } + + /// Attempts to extract the raw bits from the constant. + /// + /// Fails if the value can't be represented as bits (e.g. because it is a reference + /// or an aggregate). + #[inline] + pub fn try_to_bits( + self, + db: &'db dyn HirDatabase, + param_env: ParamEnvAndCrate<'db>, + ) -> Option<u128> { + let (TyKind::Bool | TyKind::Char | TyKind::Uint(_) | TyKind::Int(_) | TyKind::Float(_)) = + self.ty.kind() + else { + return None; + }; + let scalar = self.try_to_leaf()?; + let size = db.layout_of_ty(self.ty.store(), param_env.store()).ok()?.size; + Some(scalar.to_bits(size)) + } } pub(super) fn allocation_to_const<'db>( diff --git a/crates/hir-ty/src/next_solver/infer/mod.rs b/crates/hir-ty/src/next_solver/infer/mod.rs index 839bdf17e7..6b6dd549b3 100644 --- a/crates/hir-ty/src/next_solver/infer/mod.rs +++ b/crates/hir-ty/src/next_solver/infer/mod.rs @@ -1,7 +1,6 @@ //! Infer context the next-trait-solver. use std::cell::{Cell, RefCell}; -use std::fmt; use std::ops::Range; use std::sync::Arc; @@ -308,32 +307,6 @@ pub enum BoundRegionConversionTime { AssocTypeProjection(SolverDefId), } -#[derive(Copy, Clone, Debug)] -pub struct FixupError { - unresolved: TyOrConstInferVar, -} - -impl fmt::Display for FixupError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - use TyOrConstInferVar::*; - - match self.unresolved { - TyInt(_) => write!( - f, - "cannot determine the type of this integer; \ - add a suffix to specify the type explicitly" - ), - TyFloat(_) => write!( - f, - "cannot determine the type of this number; \ - add a suffix to specify the type explicitly" - ), - Ty(_) => write!(f, "unconstrained type"), - Const(_) => write!(f, "unconstrained const value"), - } - } -} - /// See the `region_obligations` field for more information. #[derive(Clone, Debug)] pub struct TypeOutlivesConstraint<'db> { diff --git a/crates/hir-ty/src/next_solver/infer/traits.rs b/crates/hir-ty/src/next_solver/infer/traits.rs index 4584b35796..362689ab5c 100644 --- a/crates/hir-ty/src/next_solver/infer/traits.rs +++ b/crates/hir-ty/src/next_solver/infer/traits.rs @@ -161,11 +161,11 @@ impl<'db> PredicateObligation<'db> { /// Flips the polarity of the inner predicate. /// /// Given `T: Trait` predicate it returns `T: !Trait` and given `T: !Trait` returns `T: Trait`. - pub fn flip_polarity(&self, _interner: DbInterner<'db>) -> Option<PredicateObligation<'db>> { + pub fn flip_polarity(&self, interner: DbInterner<'db>) -> Option<PredicateObligation<'db>> { Some(PredicateObligation { cause: self.cause, param_env: self.param_env, - predicate: self.predicate.flip_polarity()?, + predicate: self.predicate.flip_polarity(interner)?, recursion_depth: self.recursion_depth, }) } diff --git a/crates/hir-ty/src/next_solver/interner.rs b/crates/hir-ty/src/next_solver/interner.rs index b3d31dd40b..172b1a245f 100644 --- a/crates/hir-ty/src/next_solver/interner.rs +++ b/crates/hir-ty/src/next_solver/interner.rs @@ -330,6 +330,7 @@ unsafe impl Sync for DbInterner<'_> {} impl<'db> DbInterner<'db> { // FIXME(next-solver): remove this method + #[doc(hidden)] pub fn conjure() -> DbInterner<'db> { // Here we can not reinit the cache since we do that when we attach the db. crate::with_attached_db(|db| DbInterner { @@ -607,8 +608,8 @@ impl<'db> inherent::AdtDef<DbInterner<'db>> for AdtDef { hir_def::AdtId::EnumId(id) => id .enum_variants(db) .variants - .iter() - .flat_map(|&(variant_id, _, _)| field_tys(variant_id.into())) + .values() + .flat_map(|&(variant_id, _)| field_tys(variant_id.into())) .collect(), }; @@ -2581,6 +2582,7 @@ pub unsafe fn collect_ty_garbage() { gc.add_slice_storage::<super::predicate::BoundExistentialPredicatesStorage>(); gc.add_slice_storage::<super::region::RegionAssumptionsStorage>(); gc.add_slice_storage::<super::ty::TysStorage>(); + gc.add_slice_storage::<crate::mir::ProjectionStorage>(); // SAFETY: // - By our precondition, there are no unrecorded types. @@ -2645,4 +2647,5 @@ impl_gc_visit_slice!( super::region::RegionAssumptionsStorage, super::ty::TysStorage, super::consts::ConstsStorage, + crate::mir::ProjectionStorage, ); diff --git a/crates/hir-ty/src/next_solver/predicate.rs b/crates/hir-ty/src/next_solver/predicate.rs index 30738dea5c..cf492e65c3 100644 --- a/crates/hir-ty/src/next_solver/predicate.rs +++ b/crates/hir-ty/src/next_solver/predicate.rs @@ -229,7 +229,7 @@ impl<'db> Predicate<'db> { /// Flips the polarity of a Predicate. /// /// Given `T: Trait` predicate it returns `T: !Trait` and given `T: !Trait` returns `T: Trait`. - pub fn flip_polarity(self) -> Option<Predicate<'db>> { + pub fn flip_polarity(self, interner: DbInterner<'db>) -> Option<Predicate<'db>> { let kind = self .kind() .map_bound(|kind| match kind { @@ -245,7 +245,7 @@ impl<'db> Predicate<'db> { }) .transpose()?; - Some(Predicate::new(DbInterner::conjure(), kind)) + Some(Predicate::new(interner, kind)) } } @@ -355,13 +355,6 @@ impl<'db> rustc_type_ir::inherent::SliceLike for Clauses<'db> { } } -impl<'db> Default for Clauses<'db> { - #[inline] - fn default() -> Self { - Clauses::empty(DbInterner::conjure()) - } -} - impl<'db> rustc_type_ir::inherent::Clauses<DbInterner<'db>> for Clauses<'db> {} impl<'db> rustc_type_ir::TypeSuperFoldable<DbInterner<'db>> for Clauses<'db> { @@ -444,8 +437,8 @@ pub struct ParamEnv<'db> { } impl<'db> ParamEnv<'db> { - pub fn empty() -> Self { - ParamEnv { clauses: Clauses::empty(DbInterner::conjure()) } + pub fn empty(interner: DbInterner<'db>) -> Self { + ParamEnv { clauses: Clauses::empty(interner) } } pub fn clauses(self) -> Clauses<'db> { diff --git a/crates/hir-ty/src/opaques.rs b/crates/hir-ty/src/opaques.rs index 4244b1bac4..dfd1fd96c6 100644 --- a/crates/hir-ty/src/opaques.rs +++ b/crates/hir-ty/src/opaques.rs @@ -1,8 +1,8 @@ //! Handling of opaque types, detection of defining scope and hidden type. use hir_def::{ - AssocItemId, AssocItemLoc, DefWithBodyId, ExpressionStoreOwnerId, FunctionId, GenericDefId, - HasModule, ItemContainerId, TypeAliasId, signatures::ImplSignature, + AssocItemId, AssocItemLoc, DefWithBodyId, FunctionId, HasModule, ItemContainerId, TypeAliasId, + signatures::ImplSignature, }; use hir_expand::name::Name; use la_arena::ArenaMap; @@ -129,10 +129,9 @@ pub(crate) fn tait_hidden_types( let infcx = interner.infer_ctxt().build(TypingMode::non_body_analysis()); let mut ocx = ObligationCtxt::new(&infcx); let cause = ObligationCause::dummy(); - let param_env = - db.trait_environment(ExpressionStoreOwnerId::from(GenericDefId::from(type_alias))); + let param_env = db.trait_environment(type_alias.into()); - let defining_bodies = tait_defining_bodies(db, &loc); + let defining_bodies = tait_defining_bodies(db, loc); let mut result = ArenaMap::with_capacity(taits_count); for defining_body in defining_bodies { diff --git a/crates/hir-ty/src/representability.rs b/crates/hir-ty/src/representability.rs index 0b7dc4d309..0ea8003e55 100644 --- a/crates/hir-ty/src/representability.rs +++ b/crates/hir-ty/src/representability.rs @@ -29,7 +29,7 @@ pub(crate) fn representability(db: &dyn HirDatabase, id: AdtId) -> Representabil AdtId::StructId(id) => variant_representability(db, id.into()), AdtId::UnionId(id) => variant_representability(db, id.into()), AdtId::EnumId(id) => { - for &(variant, ..) in &id.enum_variants(db).variants { + for &(variant, ..) in id.enum_variants(db).variants.values() { rtry!(variant_representability(db, variant.into())); } Representability::Representable @@ -105,7 +105,7 @@ fn params_in_repr(db: &dyn HirDatabase, def_id: AdtId) -> Box<[bool]> { AdtId::StructId(def_id) => handle_variant(def_id.into()), AdtId::UnionId(def_id) => handle_variant(def_id.into()), AdtId::EnumId(def_id) => { - for &(variant, ..) in &def_id.enum_variants(db).variants { + for &(variant, ..) in def_id.enum_variants(db).variants.values() { handle_variant(variant.into()); } } diff --git a/crates/hir-ty/src/specialization.rs b/crates/hir-ty/src/specialization.rs index 467b598447..2d206fe380 100644 --- a/crates/hir-ty/src/specialization.rs +++ b/crates/hir-ty/src/specialization.rs @@ -1,9 +1,6 @@ //! Impl specialization related things -use hir_def::{ - ExpressionStoreOwnerId, GenericDefId, HasModule, ImplId, signatures::ImplSignature, - unstable_features::UnstableFeatures, -}; +use hir_def::{HasModule, ImplId, signatures::ImplSignature, unstable_features::UnstableFeatures}; use tracing::debug; use crate::{ @@ -48,9 +45,7 @@ fn specializes_query( specializing_impl_def_id: ImplId, parent_impl_def_id: ImplId, ) -> bool { - let trait_env = db.trait_environment(ExpressionStoreOwnerId::from(GenericDefId::from( - specializing_impl_def_id, - ))); + let trait_env = db.trait_environment(specializing_impl_def_id.into()); let interner = DbInterner::new_with(db, specializing_impl_def_id.krate(db)); let specializing_impl_signature = ImplSignature::of(db, specializing_impl_def_id); diff --git a/crates/hir-ty/src/test_db.rs b/crates/hir-ty/src/test_db.rs index e19e26ebc4..653fbe34f1 100644 --- a/crates/hir-ty/src/test_db.rs +++ b/crates/hir-ty/src/test_db.rs @@ -132,6 +132,10 @@ impl SourceDatabase for TestDB { fn nonce_and_revision(&self) -> (Nonce, salsa::Revision) { (self.nonce, salsa::plumbing::ZalsaDatabase::zalsa(self).current_revision()) } + + fn line_column(&self, _file: FileId, _offset: syntax::TextSize) -> Result<(u32, u32), ()> { + Err(()) + } } #[salsa_macros::db] diff --git a/crates/hir-ty/src/tests.rs b/crates/hir-ty/src/tests.rs index d259ce7963..ce4eff701c 100644 --- a/crates/hir-ty/src/tests.rs +++ b/crates/hir-ty/src/tests.rs @@ -460,7 +460,7 @@ fn infer_with_mismatches(content: &str, include_mismatches: bool) -> String { AdtId::EnumId(id) => variants.extend( id.enum_variants(&db) .variants - .iter() + .values() .map(|&(variant, ..)| (variant.into(), krate)), ), } @@ -600,7 +600,7 @@ pub(crate) fn visit_module( visit_body(db, body, cb); } ModuleDefId::AdtId(AdtId::EnumId(it)) => { - it.enum_variants(db).variants.iter().for_each(|&(it, _, _)| { + it.enum_variants(db).variants.values().for_each(|&(it, _)| { let body = Body::of(db, it.into()); cb(it.into()); visit_body(db, body, cb); diff --git a/crates/hir-ty/src/tests/macros.rs b/crates/hir-ty/src/tests/macros.rs index 28a688d4a3..c0da6cfd30 100644 --- a/crates/hir-ty/src/tests/macros.rs +++ b/crates/hir-ty/src/tests/macros.rs @@ -194,10 +194,8 @@ fn expr_macro_def_expanded_in_various_places() { !0..6 '1isize': isize !0..6 '1isize': isize !0..6 '1isize': isize - 39..442 '{ ...!(); }': {unknown} + 39..442 '{ ...!(); }': ! 73..94 'spam!(...am!())': {unknown} - 100..119 'for _ ...!() {}': fn into_iter<isize>(isize) -> <isize as IntoIterator>::IntoIter - 100..119 'for _ ...!() {}': <isize as IntoIterator>::IntoIter 100..119 'for _ ...!() {}': ! 100..119 'for _ ...!() {}': {unknown} 100..119 'for _ ...!() {}': &'? mut {unknown} @@ -208,6 +206,8 @@ fn expr_macro_def_expanded_in_various_places() { 100..119 'for _ ...!() {}': () 100..119 'for _ ...!() {}': () 104..105 '_': {unknown} + 109..116 'spam!()': fn into_iter<isize>(isize) -> <isize as IntoIterator>::IntoIter + 109..116 'spam!()': <isize as IntoIterator>::IntoIter 117..119 '{}': () 124..134 '|| spam!()': impl Fn() -> isize 140..156 'while ...!() {}': ! @@ -288,10 +288,8 @@ fn expr_macro_rules_expanded_in_various_places() { !0..6 '1isize': isize !0..6 '1isize': isize !0..6 '1isize': isize - 53..456 '{ ...!(); }': {unknown} + 53..456 '{ ...!(); }': ! 87..108 'spam!(...am!())': {unknown} - 114..133 'for _ ...!() {}': fn into_iter<isize>(isize) -> <isize as IntoIterator>::IntoIter - 114..133 'for _ ...!() {}': <isize as IntoIterator>::IntoIter 114..133 'for _ ...!() {}': ! 114..133 'for _ ...!() {}': {unknown} 114..133 'for _ ...!() {}': &'? mut {unknown} @@ -302,6 +300,8 @@ fn expr_macro_rules_expanded_in_various_places() { 114..133 'for _ ...!() {}': () 114..133 'for _ ...!() {}': () 118..119 '_': {unknown} + 123..130 'spam!()': fn into_iter<isize>(isize) -> <isize as IntoIterator>::IntoIter + 123..130 'spam!()': <isize as IntoIterator>::IntoIter 131..133 '{}': () 138..148 '|| spam!()': impl Fn() -> isize 154..170 'while ...!() {}': ! @@ -1493,7 +1493,7 @@ fn main() { !0..136 'builti...tack))': () !0..449 'builti...urn),)': ! 10..1236 '{ ... } }': () - 16..1234 'unsafe... }': () + 16..1234 'unsafe... }': ! 37..40 'foo': i32 43..44 '1': i32 58..63 'mut o': i32 diff --git a/crates/hir-ty/src/tests/method_resolution.rs b/crates/hir-ty/src/tests/method_resolution.rs index 4291c9ba18..2084c01853 100644 --- a/crates/hir-ty/src/tests/method_resolution.rs +++ b/crates/hir-ty/src/tests/method_resolution.rs @@ -1715,8 +1715,8 @@ fn f<S: Sized, T, U: ?Sized>() { 95..103 'u32::foo': fn foo<u32>() -> u8 109..115 'S::foo': fn foo<S>() -> u8 121..127 'T::foo': fn foo<T>() -> u8 - 133..139 'U::foo': fn foo<U>() -> u8 - 145..157 '<[u32]>::foo': fn foo<[u32]>() -> u8 + 133..139 'U::foo': {unknown} + 145..157 '<[u32]>::foo': {unknown} "#]], ); } diff --git a/crates/hir-ty/src/tests/never_type.rs b/crates/hir-ty/src/tests/never_type.rs index 91273cd177..fd21286d50 100644 --- a/crates/hir-ty/src/tests/never_type.rs +++ b/crates/hir-ty/src/tests/never_type.rs @@ -262,42 +262,42 @@ fn diverging_expression_1() { let x: u32 = { let y: u32 = { loop {}; }; }; } ", - expect![[r" - 11..39 '{ ...urn; }': () + expect![[r#" + 11..39 '{ ...urn; }': ! 21..22 'x': u32 30..36 'return': ! - 51..84 '{ ...; }; }': () + 51..84 '{ ...; }; }': ! 61..62 'x': u32 - 70..81 '{ return; }': u32 + 70..81 '{ return; }': ! 72..78 'return': ! - 96..125 '{ ... {}; }': () + 96..125 '{ ... {}; }': ! 106..107 'x': u32 115..122 'loop {}': ! 120..122 '{}': () - 137..170 '{ ...} }; }': () + 137..170 '{ ...} }; }': ! 147..148 'x': u32 156..167 '{ loop {} }': u32 158..165 'loop {}': ! 163..165 '{}': () - 182..246 '{ ...} }; }': () + 182..246 '{ ...} }; }': ! 192..193 'x': u32 201..243 '{ if t...}; } }': u32 203..241 'if tru... {}; }': u32 206..210 'true': bool - 211..223 '{ loop {}; }': u32 + 211..223 '{ loop {}; }': ! 213..220 'loop {}': ! 218..220 '{}': () - 229..241 '{ loop {}; }': u32 + 229..241 '{ loop {}; }': ! 231..238 'loop {}': ! 236..238 '{}': () - 258..310 '{ ...; }; }': () + 258..310 '{ ...; }; }': ! 268..269 'x': u32 - 277..307 '{ let ...; }; }': u32 + 277..307 '{ let ...; }; }': ! 283..284 'y': u32 - 292..304 '{ loop {}; }': u32 + 292..304 '{ loop {}; }': ! 294..301 'loop {}': ! 299..301 '{}': () - "]], + "#]], ); } @@ -312,7 +312,7 @@ fn diverging_expression_2() { } "#, expect![[r#" - 11..84 '{ ..." }; }': () + 11..84 '{ ..." }; }': ! 54..55 'x': u32 63..81 '{ loop...foo" }': u32 65..72 'loop {}': ! @@ -355,14 +355,12 @@ fn diverging_expression_3_break() { 54..55 'x': u32 63..82 '{ loop...k; } }': u32 65..80 'loop { break; }': u32 - 70..80 '{ break; }': () + 70..80 '{ break; }': ! 72..77 'break': ! 72..77: expected u32, got () 97..343 '{ ...; }; }': () 140..141 'x': u32 149..175 '{ for ...; }; }': u32 - 151..172 'for a ...eak; }': fn into_iter<{unknown}>({unknown}) -> <{unknown} as IntoIterator>::IntoIter - 151..172 'for a ...eak; }': <{unknown} as IntoIterator>::IntoIter 151..172 'for a ...eak; }': ! 151..172 'for a ...eak; }': {unknown} 151..172 'for a ...eak; }': &'? mut {unknown} @@ -374,12 +372,12 @@ fn diverging_expression_3_break() { 151..172 'for a ...eak; }': () 155..156 'a': {unknown} 160..161 'b': {unknown} - 162..172 '{ break; }': () + 160..161 'b': fn into_iter<{unknown}>({unknown}) -> <{unknown} as IntoIterator>::IntoIter + 160..161 'b': <{unknown} as IntoIterator>::IntoIter + 162..172 '{ break; }': ! 164..169 'break': ! 226..227 'x': u32 235..253 '{ for ... {}; }': u32 - 237..250 'for a in b {}': fn into_iter<{unknown}>({unknown}) -> <{unknown} as IntoIterator>::IntoIter - 237..250 'for a in b {}': <{unknown} as IntoIterator>::IntoIter 237..250 'for a in b {}': ! 237..250 'for a in b {}': {unknown} 237..250 'for a in b {}': &'? mut {unknown} @@ -391,11 +389,11 @@ fn diverging_expression_3_break() { 237..250 'for a in b {}': () 241..242 'a': {unknown} 246..247 'b': {unknown} + 246..247 'b': fn into_iter<{unknown}>({unknown}) -> <{unknown} as IntoIterator>::IntoIter + 246..247 'b': <{unknown} as IntoIterator>::IntoIter 248..250 '{}': () 304..305 'x': u32 313..340 '{ for ...; }; }': u32 - 315..337 'for a ...urn; }': fn into_iter<{unknown}>({unknown}) -> <{unknown} as IntoIterator>::IntoIter - 315..337 'for a ...urn; }': <{unknown} as IntoIterator>::IntoIter 315..337 'for a ...urn; }': ! 315..337 'for a ...urn; }': {unknown} 315..337 'for a ...urn; }': &'? mut {unknown} @@ -407,7 +405,9 @@ fn diverging_expression_3_break() { 315..337 'for a ...urn; }': () 319..320 'a': {unknown} 324..325 'b': {unknown} - 326..337 '{ return; }': () + 324..325 'b': fn into_iter<{unknown}>({unknown}) -> <{unknown} as IntoIterator>::IntoIter + 324..325 'b': <{unknown} as IntoIterator>::IntoIter + 326..337 '{ return; }': ! 328..334 'return': ! 149..175: expected u32, got () 235..253: expected u32, got () @@ -419,7 +419,7 @@ fn diverging_expression_3_break() { 409..430 'while ...eak; }': () 409..430 'while ...eak; }': () 415..419 'true': bool - 420..430 '{ break; }': () + 420..430 '{ break; }': ! 422..427 'break': ! 537..538 'x': u32 546..564 '{ whil... {}; }': u32 @@ -434,7 +434,7 @@ fn diverging_expression_3_break() { 626..648 'while ...urn; }': () 626..648 'while ...urn; }': () 632..636 'true': bool - 637..648 '{ return; }': () + 637..648 '{ return; }': ! 639..645 'return': ! 407..433: expected u32, got () 546..564: expected u32, got () diff --git a/crates/hir-ty/src/tests/patterns.rs b/crates/hir-ty/src/tests/patterns.rs index e719f43e74..a6e864916f 100644 --- a/crates/hir-ty/src/tests/patterns.rs +++ b/crates/hir-ty/src/tests/patterns.rs @@ -47,8 +47,6 @@ fn infer_pattern() { 82..94 '(1, "hello")': (i32, &'? str) 83..84 '1': i32 86..93 '"hello"': &'static str - 101..150 'for (e... }': fn into_iter<[(i32, i32); 1]>([(i32, i32); 1]) -> <[(i32, i32); 1] as IntoIterator>::IntoIter - 101..150 'for (e... }': IntoIter<(i32, i32), 1> 101..150 'for (e... }': ! 101..150 'for (e... }': IntoIter<(i32, i32), 1> 101..150 'for (e... }': &'? mut IntoIter<(i32, i32), 1> @@ -62,6 +60,8 @@ fn infer_pattern() { 106..107 'e': i32 109..110 'f': i32 115..123 '[(0, 1)]': [(i32, i32); 1] + 115..123 '[(0, 1)]': fn into_iter<[(i32, i32); 1]>([(i32, i32); 1]) -> <[(i32, i32); 1] as IntoIterator>::IntoIter + 115..123 '[(0, 1)]': IntoIter<(i32, i32), 1> 116..122 '(0, 1)': (i32, i32) 117..118 '0': i32 120..121 '1': i32 @@ -606,7 +606,7 @@ fn enum_variant_through_self_in_pattern() { } "#, expect![[r#" - 75..217 '{ ... }': () + 75..217 '{ ... }': ! 85..210 'match ... }': () 92..99 'loop {}': ! 97..99 '{}': () diff --git a/crates/hir-ty/src/tests/regression.rs b/crates/hir-ty/src/tests/regression.rs index 5a90e700ac..22404087bf 100644 --- a/crates/hir-ty/src/tests/regression.rs +++ b/crates/hir-ty/src/tests/regression.rs @@ -270,8 +270,6 @@ fn infer_std_crash_5() { "#, expect![[r#" 26..322 '{ ... } }': () - 32..320 'for co... }': fn into_iter<{unknown}>({unknown}) -> <{unknown} as IntoIterator>::IntoIter - 32..320 'for co... }': <{unknown} as IntoIterator>::IntoIter 32..320 'for co... }': ! 32..320 'for co... }': {unknown} 32..320 'for co... }': &'? mut {unknown} @@ -281,27 +279,29 @@ fn infer_std_crash_5() { 32..320 'for co... }': () 32..320 'for co... }': () 32..320 'for co... }': () - 36..43 'content': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} + 36..43 'content': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} 47..60 'doesnt_matter': {unknown} + 47..60 'doesnt_matter': fn into_iter<{unknown}>({unknown}) -> <{unknown} as IntoIterator>::IntoIter + 47..60 'doesnt_matter': <{unknown} as IntoIterator>::IntoIter 61..320 '{ ... }': () - 75..79 'name': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} - 82..166 'if doe... }': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} + 75..79 'name': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} + 82..166 'if doe... }': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} 85..98 'doesnt_matter': bool 99..128 '{ ... }': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} 113..118 'first': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} - 134..166 '{ ... }': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} - 148..156 '&content': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} - 149..156 'content': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} - 181..188 'content': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} - 191..313 'if ICE... }': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} + 134..166 '{ ... }': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} + 148..156 '&content': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} + 149..156 'content': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} + 181..188 'content': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} + 191..313 'if ICE... }': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} 194..231 'ICE_RE..._VALUE': {unknown} 194..247 'ICE_RE...&name)': bool - 241..246 '&name': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} - 242..246 'name': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} - 248..276 '{ ... }': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} - 262..266 'name': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} - 282..313 '{ ... }': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} - 296..303 'content': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} + 241..246 '&name': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} + 242..246 'name': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} + 248..276 '{ ... }': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} + 262..266 'name': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} + 282..313 '{ ... }': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} + 296..303 'content': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} "#]], ); } @@ -416,7 +416,7 @@ fn issue_2669() { 120..215 '{ ... }': () 130..133 'end': fn end<{unknown}>() 130..135 'end()': () - 164..209 '{ ... }': () + 164..209 '{ ... }': ! 182..184 '_x': ! 191..198 'loop {}': ! 196..198 '{}': () @@ -631,7 +631,7 @@ fn issue_4053_diesel_where_clauses() { 65..69 'self': Self 267..271 'self': Self 466..470 'self': SelectStatement<F, S, D, W, O, LOf, {unknown}, {unknown}> - 488..522 '{ ... }': {unknown} + 488..522 '{ ... }': () 498..502 'self': SelectStatement<F, S, D, W, O, LOf, {unknown}, {unknown}> 498..508 'self.order': O 498..515 'self.o...into()': dyn QueryFragment<DB> + 'static @@ -1059,7 +1059,7 @@ fn cfg_tail() { 216..227 '{ "third" }': () 218..225 '"third"': &'static str 293..357 '{ ...] 15 }': () - 299..311 '{ "fourth" }': &'static str + 299..311 '{ "fourth" }': &'? str 301..309 '"fourth"': &'static str "#]], ) @@ -1261,8 +1261,6 @@ fn test() { "#, expect![[r#" 10..68 '{ ... } }': () - 16..66 'for _ ... }': fn into_iter<()>(()) -> <() as IntoIterator>::IntoIter - 16..66 'for _ ... }': <() as IntoIterator>::IntoIter 16..66 'for _ ... }': ! 16..66 'for _ ... }': {unknown} 16..66 'for _ ... }': &'? mut {unknown} @@ -1274,6 +1272,8 @@ fn test() { 16..66 'for _ ... }': () 20..21 '_': {unknown} 25..39 '{ let x = 0; }': () + 25..39 '{ let x = 0; }': fn into_iter<()>(()) -> <() as IntoIterator>::IntoIter + 25..39 '{ let x = 0; }': <() as IntoIterator>::IntoIter 31..32 'x': i32 35..36 '0': i32 40..66 '{ ... }': () @@ -2238,7 +2238,7 @@ type Bar = impl Foo; async fn f<A, B, C>() -> Bar {} "#, expect![[r#" - 64..66 '{}': impl Foo + ?Sized + 64..66 '{}': () "#]], ); } diff --git a/crates/hir-ty/src/tests/regression/new_solver.rs b/crates/hir-ty/src/tests/regression/new_solver.rs index 33a12fcd1e..c77b20f4b5 100644 --- a/crates/hir-ty/src/tests/regression/new_solver.rs +++ b/crates/hir-ty/src/tests/regression/new_solver.rs @@ -357,7 +357,7 @@ where "#, expect![[r#" 182..183 't': T - 230..280 '{ ... {}; }': () + 230..280 '{ ... {}; }': ! 240..241 't': <T as DimMax<U>>::Output 270..277 'loop {}': ! 275..277 '{}': () diff --git a/crates/hir-ty/src/tests/simple.rs b/crates/hir-ty/src/tests/simple.rs index c0b8d93b47..b54ed08031 100644 --- a/crates/hir-ty/src/tests/simple.rs +++ b/crates/hir-ty/src/tests/simple.rs @@ -135,7 +135,7 @@ fn test(a: u32, b: isize, c: !, d: &str) { 16..17 'b': isize 26..27 'c': ! 32..33 'd': &'? str - 41..120 '{ ...f32; }': () + 41..120 '{ ...f32; }': ! 47..48 'a': u32 54..55 'b': isize 61..62 'c': ! @@ -1018,14 +1018,14 @@ fn foo() { 28..32 'true': bool 33..50 '{ ... }': i32 43..44 '1': i32 - 56..79 '{ ... }': i32 + 56..79 '{ ... }': ! 66..72 'return': ! 89..92 '_x2': i32 95..148 'if tru... }': i32 98..102 'true': bool 103..120 '{ ... }': i32 113..114 '2': i32 - 126..148 '{ ... }': ! + 126..148 '{ ... }': i32 136..142 'return': ! 158..161 '_x3': i32 164..246 'match ... }': i32 @@ -1034,7 +1034,7 @@ fn foo() { 185..189 'true': bool 193..194 '3': i32 204..205 '_': bool - 209..240 '{ ... }': i32 + 209..240 '{ ... }': ! 223..229 'return': ! 256..259 '_x4': i32 262..319 'match ... }': i32 @@ -1939,7 +1939,7 @@ fn closure_return() { 16..58 '{ ...; }; }': u32 26..27 'x': impl Fn() -> usize 30..55 '|| -> ...n 1; }': impl Fn() -> usize - 42..55 '{ return 1; }': usize + 42..55 '{ return 1; }': ! 44..52 'return 1': ! 51..52 '1': usize "#]], @@ -1958,7 +1958,7 @@ fn closure_return_unit() { 16..47 '{ ...; }; }': u32 26..27 'x': impl Fn() 30..44 '|| { return; }': impl Fn() - 33..44 '{ return; }': () + 33..44 '{ return; }': ! 35..41 'return': ! "#]], ); @@ -2434,10 +2434,10 @@ fn infer_loop_break_with_val() { 59..168 '{ ... }; }': () 69..70 'x': Option<bool> 73..165 'loop {... }': Option<bool> - 78..165 '{ ... }': () + 78..165 '{ ... }': ! 88..132 'if fal... }': () 91..96 'false': bool - 97..132 '{ ... }': () + 97..132 '{ ... }': ! 111..121 'break None': ! 117..121 'None': Option<bool> 142..158 'break ...(true)': ! @@ -2470,7 +2470,7 @@ fn infer_loop_break_without_val() { 78..133 '{ ... }': () 88..127 'if fal... }': () 91..96 'false': bool - 97..127 '{ ... }': () + 97..127 '{ ... }': ! 111..116 'break': ! "#]], ); @@ -2500,24 +2500,24 @@ fn infer_labelled_break_with_val() { 19..21 '_x': impl Fn() -> bool 24..332 '|| 'ou... }': impl Fn() -> bool 27..332 ''outer... }': bool - 40..332 '{ ... }': () + 40..332 '{ ... }': ! 54..59 'inner': i8 62..300 ''inner... }': i8 - 75..300 '{ ... }': () + 75..300 '{ ... }': ! 93..94 'i': bool 97..113 'Defaul...efault': {unknown} 97..115 'Defaul...ault()': bool 129..269 'if (br... }': () 133..147 'break 'outer i': ! 146..147 'i': bool - 149..208 '{ ... }': () + 149..208 '{ ... }': ! 167..193 'loop {...5i8; }': ! - 172..193 '{ brea...5i8; }': () + 172..193 '{ brea...5i8; }': ! 174..190 'break ...er 5i8': ! 187..190 '5i8': i8 214..269 'if tru... }': () 217..221 'true': bool - 222..269 '{ ... }': () + 222..269 '{ ... }': ! 240..254 'break 'inner 6': ! 253..254 '6': i8 282..289 'break 7': ! @@ -2566,12 +2566,12 @@ fn foo() { 140..270 'if (br... }': () 144..158 'break 'outer i': ! 157..158 'i': bool - 160..209 '{ ... }': () + 160..209 '{ ... }': ! 178..194 'break ...er 5i8': ! 191..194 '5i8': i8 215..270 'if tru... }': () 218..222 'true': bool - 223..270 '{ ... }': () + 223..270 '{ ... }': ! 241..255 'break 'inner 6': ! 254..255 '6': i8 283..313 'break ... { 0 }': ! @@ -2666,7 +2666,7 @@ fn generic_default_in_struct_literal() { } "#, expect![[r#" - 99..319 '{ ...32); }': () + 99..319 '{ ...32); }': ! 109..110 'x': Thing<!> 113..133 'Thing ...p {} }': Thing<!> 124..131 'loop {}': ! @@ -3254,9 +3254,9 @@ fn main() { expect![[r#" 104..108 'self': &'? Box<T> 188..192 'self': &'a Box<Foo<T>> - 218..220 '{}': &'a T + 218..220 '{}': &'? T 242..246 'self': &'a Box<Foo<T>> - 275..277 '{}': &'a Foo<T> + 275..277 '{}': &'? Foo<T> 297..301 'self': Box<Foo<T>> 322..324 '{}': Foo<T> 338..559 '{ ...r(); }': () @@ -4305,3 +4305,21 @@ enum Enum { "#]], ); } + +#[test] +fn labelled_block_break() { + check_types( + r#" +//- minicore: option +fn foo() { + 'a: { + if false { + break 'a Some(1); + } + None + // ^^^^ Option<i32> + }; +} + "#, + ); +} diff --git a/crates/hir-ty/src/tests/traits.rs b/crates/hir-ty/src/tests/traits.rs index ea978cde58..85c93abcf9 100644 --- a/crates/hir-ty/src/tests/traits.rs +++ b/crates/hir-ty/src/tests/traits.rs @@ -1490,7 +1490,7 @@ fn test(x: Box<dyn Trait<u64>>, y: &dyn Trait<u64>) { expect![[r#" 29..33 'self': &'? Self 54..58 'self': &'? Self - 206..208 '{}': Box<dyn Trait<u64> + 'static> + 206..208 '{}': Box<dyn Trait<u64> + '?> 218..219 'x': Box<dyn Trait<u64> + 'static> 242..243 'y': &'? (dyn Trait<u64> + 'static) 262..379 '{ ...2(); }': () @@ -1571,7 +1571,7 @@ fn test(x: Trait, y: &Trait) -> u64 { }"#, expect![[r#" 26..30 'self': &'? Self - 60..62 '{}': dyn Trait + 'static + 60..62 '{}': dyn Trait + '? 72..73 'x': dyn Trait + 'static 82..83 'y': &'? (dyn Trait + 'static) 100..175 '{ ...o(); }': u64 @@ -1712,7 +1712,7 @@ fn test<T: Trait<Type = u32>>(x: T, y: impl Trait<Type = i64>) { }"#, expect![[r#" 81..82 't': T - 109..111 '{}': <T as Trait>::Type + 109..111 '{}': () 143..144 't': T 154..156 '{}': U 186..187 't': T @@ -3027,13 +3027,13 @@ fn test() { 140..146 'IsCopy': IsCopy 140..153 'IsCopy.test()': bool 159..166 'NotCopy': NotCopy - 159..173 'NotCopy.test()': bool + 159..173 'NotCopy.test()': {unknown} 179..195 '(IsCop...sCopy)': (IsCopy, IsCopy) 179..202 '(IsCop...test()': bool 180..186 'IsCopy': IsCopy 188..194 'IsCopy': IsCopy 208..225 '(IsCop...tCopy)': (IsCopy, NotCopy) - 208..232 '(IsCop...test()': bool + 208..232 '(IsCop...test()': {unknown} 209..215 'IsCopy': IsCopy 217..224 'NotCopy': NotCopy "#]], @@ -3126,7 +3126,7 @@ fn test() { 79..194 '{ ...ized }': () 85..88 '1u8': u8 85..95 '1u8.test()': bool - 101..116 '(*"foo").test()': bool + 101..116 '(*"foo").test()': {unknown} 102..108 '*"foo"': str 103..108 '"foo"': &'static str 135..145 '(1u8, 1u8)': (u8, u8) @@ -3134,7 +3134,7 @@ fn test() { 136..139 '1u8': u8 141..144 '1u8': u8 158..171 '(1u8, *"foo")': (u8, str) - 158..178 '(1u8, ...test()': bool + 158..178 '(1u8, ...test()': {unknown} 159..162 '1u8': u8 164..170 '*"foo"': str 165..170 '"foo"': &'static str @@ -4069,7 +4069,7 @@ fn f<F: Foo>() { 212..295 '{ ...ZED; }': () 218..239 'F::Exp..._SIZED': Yes 245..266 'F::Imp..._SIZED': Yes - 272..292 'F::Rel..._SIZED': Yes + 272..292 'F::Rel..._SIZED': {unknown} "#]], ); } @@ -5018,7 +5018,7 @@ where "#, expect![[r#" 84..86 'de': D - 135..138 '{ }': <D as Deserializer<'de>>::Error + 135..138 '{ }': () "#]], ); } @@ -5083,7 +5083,7 @@ fn main() { let _ = iter.into_iter(); }"#, expect![[r#" - 10..313 '{ ...r(); }': () + 10..313 '{ ...r(); }': ! 223..227 'iter': Box<dyn Iterator<Item = &'? [u8]> + 'static> 273..280 'loop {}': ! 278..280 '{}': () diff --git a/crates/hir-ty/src/traits.rs b/crates/hir-ty/src/traits.rs index 9582f2ceba..f6b5adfb6f 100644 --- a/crates/hir-ty/src/traits.rs +++ b/crates/hir-ty/src/traits.rs @@ -121,7 +121,7 @@ pub fn implements_trait_unique<'db>( env: ParamEnvAndCrate<'db>, trait_: TraitId, ) -> bool { - implements_trait_unique_impl(db, env, trait_, &mut |infcx| { + implements_trait_unique_with_infcx(db, env, trait_, &mut |infcx| { infcx.fill_rest_fresh_args(Span::Dummy, trait_.into(), [ty.into()]) }) } @@ -133,10 +133,10 @@ pub fn implements_trait_unique_with_args<'db>( trait_: TraitId, args: GenericArgs<'db>, ) -> bool { - implements_trait_unique_impl(db, env, trait_, &mut |_| args) + implements_trait_unique_with_infcx(db, env, trait_, &mut |_| args) } -fn implements_trait_unique_impl<'db>( +pub fn implements_trait_unique_with_infcx<'db>( db: &'db dyn HirDatabase, env: ParamEnvAndCrate<'db>, trait_: TraitId, diff --git a/crates/hir-ty/src/variance.rs b/crates/hir-ty/src/variance.rs index 7eee78b8c4..49dacc16eb 100644 --- a/crates/hir-ty/src/variance.rs +++ b/crates/hir-ty/src/variance.rs @@ -25,8 +25,8 @@ use crate::{ db::HirDatabase, generics::{Generics, generics}, next_solver::{ - Const, ConstKind, DbInterner, ExistentialPredicate, GenericArgKind, GenericArgs, Region, - RegionKind, StoredVariancesOf, TermKind, Ty, TyKind, VariancesOf, + Const, ConstKind, DbInterner, ExistentialPredicate, GenericArgKind, GenericArgs, Pattern, + PatternKind, Region, RegionKind, StoredVariancesOf, TermKind, Ty, TyKind, VariancesOf, }, }; @@ -135,7 +135,7 @@ impl<'db> Context<'db> { AdtId::StructId(s) => add_constraints_from_variant(VariantId::StructId(s)), AdtId::UnionId(u) => add_constraints_from_variant(VariantId::UnionId(u)), AdtId::EnumId(e) => { - e.enum_variants(db).variants.iter().for_each(|&(variant, _, _)| { + e.enum_variants(db).variants.values().for_each(|&(variant, _)| { add_constraints_from_variant(VariantId::EnumVariantId(variant)) }); } @@ -249,17 +249,35 @@ impl<'db> Context<'db> { // we encounter this when walking the trait references for object // types, where we use Error as the Self type } + TyKind::Pat(typ, pat) => { + self.add_constraints_from_pat(pat); + self.add_constraints_from_ty(typ, variance); + } TyKind::Bound(..) => {} TyKind::CoroutineWitness(..) | TyKind::Placeholder(..) | TyKind::Infer(..) - | TyKind::UnsafeBinder(..) - | TyKind::Pat(..) => { + | TyKind::UnsafeBinder(..) => { never!("unexpected type encountered in variance inference: {:?}", ty) } } } + fn add_constraints_from_pat(&mut self, pat: Pattern<'db>) { + match pat.kind() { + PatternKind::Range { start, end } => { + self.add_constraints_from_const(start); + self.add_constraints_from_const(end); + } + PatternKind::NotNull => {} + PatternKind::Or(patterns) => { + for pat in patterns { + self.add_constraints_from_pat(pat) + } + } + } + } + fn add_constraints_from_invariant_args(&mut self, args: GenericArgs<'db>) { for k in args.iter() { match k.kind() { diff --git a/crates/hir/src/attrs.rs b/crates/hir/src/attrs.rs index f9cf05e73a..9a61885ccb 100644 --- a/crates/hir/src/attrs.rs +++ b/crates/hir/src/attrs.rs @@ -26,8 +26,8 @@ use stdx::never; use crate::{ Adt, AsAssocItem, AssocItem, BuiltinType, Const, ConstParam, DocLinkDef, Enum, EnumVariant, - ExternCrateDecl, Field, Function, GenericParam, HasCrate, Impl, LangItem, LifetimeParam, Macro, - Module, ModuleDef, Static, Struct, Trait, Type, TypeAlias, TypeParam, Union, Variant, + ExternCrateDecl, Field, Function, GenericParam, Impl, LangItem, LifetimeParam, Macro, Module, + ModuleDef, Static, Struct, Trait, Type, TypeAlias, TypeParam, Union, Variant, }; #[derive(Debug, Clone, Copy)] @@ -487,27 +487,28 @@ fn resolve_impl_trait_item<'db>( ns: Option<Namespace>, ) -> Option<DocLinkDef> { let krate = ty.krate(db); - let environment = crate::param_env_from_resolver(db, &resolver); + let param_env = ty.param_env(db); let traits_in_scope = resolver.traits_in_scope(db); // `ty.iterate_path_candidates()` require a scope, which is not available when resolving // attributes here. Use path resolution directly instead. // // FIXME: resolve type aliases (which are not yielded by iterate_path_candidates) - let interner = DbInterner::new_with(db, environment.krate); + let interner = DbInterner::new_with(db, param_env.krate); let infcx = interner.infer_ctxt().build(TypingMode::PostAnalysis); let features = resolver.top_level_def_map().features(); let ctx = MethodResolutionContext { infcx: &infcx, resolver: &resolver, - param_env: environment.param_env, + param_env: param_env.param_env, traits_in_scope: &traits_in_scope, - edition: krate.edition(db), + edition: krate.data(db).edition, features, call_span: hir_ty::Span::Dummy, receiver_span: hir_ty::Span::Dummy, }; - let resolution = ctx.probe_for_name(method_resolution::Mode::Path, name.clone(), ty.ty); + let resolution = + ctx.probe_for_name(method_resolution::Mode::Path, name.clone(), ty.ty.skip_binder()); let resolution = match resolution { Ok(resolution) => resolution.item, Err(MethodError::PrivateMatch(resolution)) => resolution.item, diff --git a/crates/hir/src/diagnostics.rs b/crates/hir/src/diagnostics.rs index a044f24587..f3188c9aad 100644 --- a/crates/hir/src/diagnostics.rs +++ b/crates/hir/src/diagnostics.rs @@ -15,12 +15,12 @@ use hir_def::{ }; use hir_expand::{HirFileId, InFile, mod_path::ModPath, name::Name}; use hir_ty::{ - CastError, InferenceDiagnostic, InferenceTyDiagnosticSource, ParamEnvAndCrate, + CastError, ExplicitDropMethodUseKind, InferenceDiagnostic, InferenceTyDiagnosticSource, PathGenericsSource, PathLoweringDiagnostic, TyLoweringDiagnostic, TyLoweringDiagnosticKind, db::HirDatabase, diagnostics::{BodyValidationDiagnostic, UnsafetyReason}, display::{DisplayTarget, HirDisplay}, - next_solver::DbInterner, + next_solver::{DbInterner, EarlyBinder}, solver_errors::SolverDiagnosticKind, }; use stdx::{impl_from, never}; @@ -31,7 +31,7 @@ use syntax::{ }; use triomphe::Arc; -use crate::{AssocItem, Field, Function, GenericDef, Local, Trait, Type, Variant}; +use crate::{AssocItem, Field, Function, GenericDef, Local, Trait, Type, TypeOwnerId, Variant}; pub use hir_def::VariantId; pub use hir_ty::{ @@ -100,11 +100,17 @@ macro_rules! diagnostics { } diagnostics![AnyDiagnostic<'db> -> + ArrayPatternWithoutFixedLength, AwaitOutsideOfAsync, BreakOutsideOfLoop, + CannotBeDereferenced<'db>, + CannotImplicitlyDerefTraitObject<'db>, + CannotIndexInto<'db>, CastToUnsized<'db>, ExpectedArrayOrSlicePat<'db>, ExpectedFunction<'db>, + ExplicitDropMethodUse, + FruInDestructuringAssignment, FunctionalRecordUpdateOnNonStruct, GenericDefaultRefersToSelf, InactiveCode, @@ -115,19 +121,23 @@ diagnostics![AnyDiagnostic<'db> -> InvalidCast<'db>, InvalidDeriveTarget, InvalidLhsOfAssignment, + InvalidRangePatType, MacroDefError, MacroError, MacroExpansionParseError, MalformedDerive, + MethodCallIllegalSizedBound, MismatchedArgCount, MismatchedTupleStructPatArgCount, MissingFields, MissingMatchArms, MissingUnsafe, MovedOutOfRef<'db>, + MutableRefBinding, NeedMut, NonExhaustiveLet, NonExhaustiveRecordExpr, + NonExhaustiveRecordPat, NoSuchField, MismatchedArrayPatLen, DuplicateField, @@ -298,18 +308,56 @@ pub struct MismatchedArrayPatLen { } #[derive(Debug)] +pub struct ArrayPatternWithoutFixedLength { + pub pat: InFile<ExprOrPatPtr>, +} + +#[derive(Debug)] pub struct ExpectedArrayOrSlicePat<'db> { pub pat: InFile<ExprOrPatPtr>, pub found: Type<'db>, } #[derive(Debug)] +pub struct InvalidRangePatType { + pub pat: InFile<ExprOrPatPtr>, +} + +#[derive(Debug)] pub struct ExpectedFunction<'db> { pub call: InFile<ExprOrPatPtr>, pub found: Type<'db>, } #[derive(Debug)] +pub struct CannotBeDereferenced<'db> { + pub expr: InFile<ExprOrPatPtr>, + pub found: Type<'db>, +} + +#[derive(Debug)] +pub struct CannotImplicitlyDerefTraitObject<'db> { + pub pat: InFile<ExprOrPatPtr>, + pub found: Type<'db>, +} + +#[derive(Debug)] +pub struct CannotIndexInto<'db> { + pub expr: InFile<ExprOrPatPtr>, + pub found: Type<'db>, +} + +#[derive(Debug)] +pub struct ExplicitDropMethodUse { + pub expr_or_path: Either<InFile<AstPtr<ast::MethodCallExpr>>, InFile<AstPtr<ast::Path>>>, +} + +#[derive(Debug)] +pub struct FruInDestructuringAssignment { + pub node: InFile<AstPtr<ast::Expr>>, +} + +#[derive(Debug)] pub struct FunctionalRecordUpdateOnNonStruct { pub base_expr: InFile<ExprOrPatPtr>, } @@ -401,6 +449,12 @@ pub struct NonExhaustiveRecordExpr { } #[derive(Debug)] +pub struct NonExhaustiveRecordPat { + pub pat: InFile<ExprOrPatPtr>, + pub variant: Variant, +} + +#[derive(Debug)] pub struct TypeMismatch<'db> { pub expr_or_pat: InFile<ExprOrPatPtr>, pub expected: Type<'db>, @@ -583,6 +637,11 @@ pub struct InvalidLhsOfAssignment { } #[derive(Debug)] +pub struct MethodCallIllegalSizedBound { + pub call_expr: InFile<ExprOrPatPtr>, +} + +#[derive(Debug)] pub struct PatternArgInExternFn { pub node: InFile<AstPtr<ast::Pat>>, } @@ -594,6 +653,11 @@ pub struct UnimplementedTrait<'db> { pub root_trait_predicate: Option<crate::TraitPredicate<'db>>, } +#[derive(Debug)] +pub struct MutableRefBinding { + pub pat: InFile<ExprOrPatPtr>, +} + impl<'db> AnyDiagnostic<'db> { pub(crate) fn body_validation_diagnostic( db: &'db dyn HirDatabase, @@ -733,7 +797,7 @@ impl<'db> AnyDiagnostic<'db> { d: &'db InferenceDiagnostic, source_map: &hir_def::expr_store::BodySourceMap, sig_map: &hir_def::expr_store::ExpressionStoreSourceMap, - env: ParamEnvAndCrate<'db>, + type_owner: TypeOwnerId, ) -> Option<AnyDiagnostic<'db>> { let expr_syntax = |expr| { source_map @@ -757,6 +821,7 @@ impl<'db> AnyDiagnostic<'db> { ExprOrPatId::ExprId(expr) => expr_syntax(expr), ExprOrPatId::PatId(pat) => pat_syntax(pat), }; + let new_ty = |ty| Type { owner: type_owner, ty: EarlyBinder::bind(ty) }; let span_syntax = |span| match span { hir_ty::Span::ExprId(idx) => expr_syntax(idx).map(|it| it.upcast()), hir_ty::Span::PatId(idx) => pat_syntax(idx).map(|it| it.upcast()), @@ -784,9 +849,21 @@ impl<'db> AnyDiagnostic<'db> { let pat = pat_syntax(pat)?.map(Into::into); MismatchedArrayPatLen { pat, expected, found, has_rest }.into() } + &InferenceDiagnostic::ArrayPatternWithoutFixedLength { pat } => { + let pat = pat_syntax(pat)?.map(Into::into); + ArrayPatternWithoutFixedLength { pat }.into() + } InferenceDiagnostic::ExpectedArrayOrSlicePat { pat, found } => { let pat = pat_syntax(*pat)?.map(Into::into); - ExpectedArrayOrSlicePat { pat, found: Type::new(db, def, found.as_ref()) }.into() + ExpectedArrayOrSlicePat { + pat, + found: Type { owner: type_owner, ty: EarlyBinder::bind(found.as_ref()) }, + } + .into() + } + &InferenceDiagnostic::InvalidRangePatType { pat } => { + let pat = pat_syntax(pat)?.map(Into::into); + InvalidRangePatType { pat }.into() } &InferenceDiagnostic::DuplicateField { field: expr, variant } => { let expr_or_pat = match expr { @@ -812,8 +889,7 @@ impl<'db> AnyDiagnostic<'db> { } InferenceDiagnostic::ExpectedFunction { call_expr, found } => { let call_expr = expr_syntax(*call_expr)?; - ExpectedFunction { call: call_expr, found: Type::new(db, def, found.as_ref()) } - .into() + ExpectedFunction { call: call_expr, found: new_ty(found.as_ref()) }.into() } InferenceDiagnostic::UnresolvedField { expr, @@ -825,7 +901,7 @@ impl<'db> AnyDiagnostic<'db> { UnresolvedField { expr, name: name.clone(), - receiver: Type::new(db, def, receiver.as_ref()), + receiver: new_ty(receiver.as_ref()), method_with_same_name_exists: *method_with_same_name_exists, } .into() @@ -841,10 +917,10 @@ impl<'db> AnyDiagnostic<'db> { UnresolvedMethodCall { expr, name: name.clone(), - receiver: Type::new(db, def, receiver.as_ref()), + receiver: new_ty(receiver.as_ref()), field_with_same_name: field_with_same_name .as_ref() - .map(|ty| Type::new(db, def, ty.as_ref())), + .map(|ty| new_ty(ty.as_ref())), assoc_func_with_same_name: assoc_func_with_same_name.map(Into::into), } .into() @@ -872,12 +948,16 @@ impl<'db> AnyDiagnostic<'db> { &InferenceDiagnostic::NonExhaustiveRecordExpr { expr } => { NonExhaustiveRecordExpr { expr: expr_syntax(expr)? }.into() } + &InferenceDiagnostic::NonExhaustiveRecordPat { pat, variant } => { + let pat = pat_syntax(pat)?.map(Into::into); + NonExhaustiveRecordPat { pat, variant: variant.into() }.into() + } &InferenceDiagnostic::FunctionalRecordUpdateOnNonStruct { base_expr } => { FunctionalRecordUpdateOnNonStruct { base_expr: expr_syntax(base_expr)? }.into() } InferenceDiagnostic::TypedHole { expr, expected } => { let expr = expr_syntax(*expr)?; - TypedHole { expr, expected: Type::new(db, def, expected.as_ref()) }.into() + TypedHole { expr, expected: new_ty(expected.as_ref()) }.into() } &InferenceDiagnostic::MismatchedTupleStructPatArgCount { pat, expected, found } => { let InFile { file_id, value } = pat_syntax(pat)?; @@ -888,14 +968,26 @@ impl<'db> AnyDiagnostic<'db> { } InferenceDiagnostic::CastToUnsized { expr, cast_ty } => { let expr = expr_syntax(*expr)?; - CastToUnsized { expr, cast_ty: Type::new(db, def, cast_ty.as_ref()) }.into() + CastToUnsized { expr, cast_ty: new_ty(cast_ty.as_ref()) }.into() } InferenceDiagnostic::InvalidCast { expr, error, expr_ty, cast_ty } => { let expr = expr_syntax(*expr)?; - let expr_ty = Type::new(db, def, expr_ty.as_ref()); - let cast_ty = Type::new(db, def, cast_ty.as_ref()); + let expr_ty = new_ty(expr_ty.as_ref()); + let cast_ty = new_ty(cast_ty.as_ref()); InvalidCast { expr, error: *error, expr_ty, cast_ty }.into() } + InferenceDiagnostic::CannotBeDereferenced { expr, found } => { + let expr = expr_syntax(*expr)?; + CannotBeDereferenced { expr, found: new_ty(found.as_ref()) }.into() + } + InferenceDiagnostic::CannotImplicitlyDerefTraitObject { pat, found } => { + let pat = pat_syntax(*pat)?.map(Into::into); + CannotImplicitlyDerefTraitObject { pat, found: new_ty(found.as_ref()) }.into() + } + InferenceDiagnostic::CannotIndexInto { expr, found } => { + let expr = expr_syntax(*expr)?; + CannotIndexInto { expr, found: new_ty(found.as_ref()) }.into() + } InferenceDiagnostic::TyDiagnostic { source, diag } => { let source_map = match source { InferenceTyDiagnosticSource::Body => source_map, @@ -963,13 +1055,13 @@ impl<'db> AnyDiagnostic<'db> { let lhs = expr_syntax(lhs)?; InvalidLhsOfAssignment { lhs }.into() } + &InferenceDiagnostic::MethodCallIllegalSizedBound { call_expr } => { + MethodCallIllegalSizedBound { call_expr: expr_syntax(call_expr)? }.into() + } &InferenceDiagnostic::TypeMustBeKnown { at_point, ref top_term } => { let at_point = span_syntax(at_point)?; let top_term = top_term.as_ref().map(|top_term| match top_term.as_ref().kind() { - rustc_type_ir::GenericArgKind::Type(ty) => Either::Left(Type { - ty, - env: crate::body_param_env_from_has_crate(db, def), - }), + rustc_type_ir::GenericArgKind::Type(ty) => Either::Left(new_ty(ty)), // FIXME: Printing the const to string is definitely not the correct thing to do here. rustc_type_ir::GenericArgKind::Const(konst) => Either::Right( konst.display(db, DisplayTarget::from_crate(db, def.krate(db))).to_string(), @@ -988,14 +1080,37 @@ impl<'db> AnyDiagnostic<'db> { let expr_or_pat = expr_or_pat_syntax(*node)?; TypeMismatch { expr_or_pat, - expected: Type { env, ty: expected.as_ref() }, - actual: Type { env, ty: found.as_ref() }, + expected: Type { owner: type_owner, ty: EarlyBinder::bind(expected.as_ref()) }, + actual: Type { owner: type_owner, ty: EarlyBinder::bind(found.as_ref()) }, } .into() } InferenceDiagnostic::SolverDiagnostic(d) => { let span = span_syntax(d.span)?; - Self::solver_diagnostic(db, &d.kind, span, env)? + Self::solver_diagnostic(db, &d.kind, span, type_owner)? + } + InferenceDiagnostic::ExplicitDropMethodUse { kind } => { + let expr_or_path = match kind { + ExplicitDropMethodUseKind::MethodCall(expr) => { + let expr = expr_syntax(*expr)?; + let expr = expr.with_value(expr.value.cast::<ast::MethodCallExpr>()?); + Either::Left(expr) + } + ExplicitDropMethodUseKind::Path(path_expr_id) => { + let syntax = expr_or_pat_syntax(*path_expr_id)?; + let file_id = syntax.file_id; + let syntax = + syntax.with_value(syntax.value.cast::<ast::PathExpr>()?).to_node(db); + let path = syntax.path()?; + let path = InFile::new(file_id, AstPtr::new(&path)); + Either::Right(path) + } + }; + ExplicitDropMethodUse { expr_or_path }.into() + } + InferenceDiagnostic::MutableRefBinding { pat } => { + let pat = pat_syntax(*pat)?.map(Into::into); + MutableRefBinding { pat }.into() } }) } @@ -1004,16 +1119,21 @@ impl<'db> AnyDiagnostic<'db> { db: &'db dyn HirDatabase, d: &'db SolverDiagnosticKind, span: SpanSyntax, - env: ParamEnvAndCrate<'db>, + type_owner: TypeOwnerId, ) -> Option<AnyDiagnostic<'db>> { let interner = DbInterner::new_no_crate(db); Some(match d { SolverDiagnosticKind::TraitUnimplemented { trait_predicate, root_trait_predicate } => { - let trait_predicate = - crate::TraitPredicate { inner: trait_predicate.get(interner), env }; + let trait_predicate = crate::TraitPredicate { + inner: trait_predicate.get(interner), + owner: type_owner, + }; let root_trait_predicate = root_trait_predicate.as_ref().map(|root_trait_predicate| { - crate::TraitPredicate { inner: root_trait_predicate.get(interner), env } + crate::TraitPredicate { + inner: root_trait_predicate.get(interner), + owner: type_owner, + } }); UnimplementedTrait { span, trait_predicate, root_trait_predicate }.into() } diff --git a/crates/hir/src/display.rs b/crates/hir/src/display.rs index a71851ea8c..ed18482bf3 100644 --- a/crates/hir/src/display.rs +++ b/crates/hir/src/display.rs @@ -2,7 +2,8 @@ use either::Either; use hir_def::{ - AdtId, BuiltinDeriveImplId, FunctionId, GenericDefId, ImplId, ItemContainerId, + AdtId, BuiltinDeriveImplId, DefWithBodyId, ExpressionStoreOwnerId, FunctionId, GenericDefId, + ImplId, ItemContainerId, builtin_derive::BuiltinDeriveImplMethod, expr_store::{Body, ExpressionStore}, hir::generics::{GenericParams, TypeOrConstParamData, TypeParamProvenance, WherePredicate}, @@ -32,7 +33,7 @@ use crate::{ Adt, AnyFunctionId, AsAssocItem, AssocItem, AssocItemContainer, Const, ConstParam, Crate, Enum, EnumVariant, ExternCrateDecl, Field, Function, GenericParam, HasCrate, HasVisibility, Impl, LifetimeParam, Macro, Module, SelfParam, Static, Struct, StructKind, Trait, TraitPredicate, - TraitRef, TupleField, Type, TypeAlias, TypeNs, TypeOrConstParam, TypeParam, Union, + TraitRef, TupleField, Type, TypeAlias, TypeOrConstParam, TypeParam, Union, }; fn write_builtin_derive_impl_method<'db>( @@ -102,8 +103,11 @@ impl<'db> HirDisplay<'db> for Function { if f.show_container_bounds() && !params.is_empty() { write_trait_header(trait_.into(), f)?; f.write_char('\n')?; - has_disaplayable_predicates(f.db, params, params_store) - .then_some((params, params_store)) + has_disaplayable_predicates(f.db, params, params_store).then_some(( + params, + trait_.into(), + params_store, + )) } else { None } @@ -113,8 +117,11 @@ impl<'db> HirDisplay<'db> for Function { if f.show_container_bounds() && !params.is_empty() { write_impl_header(impl_, f)?; f.write_char('\n')?; - has_disaplayable_predicates(f.db, params, params_store) - .then_some((params, params_store)) + has_disaplayable_predicates(f.db, params, params_store).then_some(( + params, + impl_.into(), + params_store, + )) } else { None } @@ -125,7 +132,7 @@ impl<'db> HirDisplay<'db> for Function { // Write signature of the function let has_written_where = write_function(f, id)?; - if let Some((container_params, container_params_store)) = container_params { + if let Some((container_params, owner, container_params_store)) = container_params { if !has_written_where { f.write_str("\nwhere")?; } @@ -135,7 +142,12 @@ impl<'db> HirDisplay<'db> for Function { _ => unreachable!(), }; write!(f, "\n // Bounds from {container_name}:",)?; - write_where_predicates(container_params, container_params_store, f)?; + write_where_predicates( + container_params, + ExpressionStoreOwnerId::Signature(owner), + container_params_store, + f, + )?; } Ok(()) } @@ -197,6 +209,7 @@ fn write_function<'db>(f: &mut HirFormatter<'_, 'db>, func_id: FunctionId) -> Re let comma = if too_long_param { ",\n " } else { ", " }; // FIXME: Use resolved `param.ty` once we no longer discard lifetimes let body = Body::of(db, func_id.into()); + let owner = DefWithBodyId::FunctionId(func_id).into(); for (type_ref, param) in data.params.iter().zip(func.assoc_fn_params(db)).skip(skip_self) { if !first { f.write_str(comma)?; @@ -205,11 +218,11 @@ fn write_function<'db>(f: &mut HirFormatter<'_, 'db>, func_id: FunctionId) -> Re } let pat_id = body.params[param.idx - body.self_param().is_some() as usize]; - let pat_str = body.pretty_print_pat(db, func_id.into(), pat_id, true, f.edition()); + let pat_str = body.pretty_print_pat(db, owner, pat_id, true, f.edition()); f.write_str(&pat_str)?; f.write_str(": ")?; - type_ref.hir_fmt(f, &data.store)?; + type_ref.hir_fmt(f, owner, &data.store)?; } if data.is_varargs() { @@ -258,7 +271,7 @@ fn write_function<'db>(f: &mut HirFormatter<'_, 'db>, func_id: FunctionId) -> Re TypeRef::Tuple(tup) if tup.is_empty() => {} _ => { f.write_str(" -> ")?; - ret_type.hir_fmt(f, &data.store)?; + ret_type.hir_fmt(f, owner, &data.store)?; } } } @@ -278,7 +291,8 @@ fn write_impl_header<'db>(impl_: ImplId, f: &mut HirFormatter<'_, 'db>) -> Resul let impl_data = ImplSignature::of(db, impl_); if let Some(target_trait) = &impl_data.target_trait { f.write_char(' ')?; - hir_display_with_store(&impl_data.store[target_trait.path], &impl_data.store).hir_fmt(f)?; + hir_display_with_store(&impl_data.store[target_trait.path], impl_.into(), &impl_data.store) + .hir_fmt(f)?; f.write_str(" for")?; } @@ -306,13 +320,14 @@ impl<'db> HirDisplay<'db> for SelfParam { }; let data = FunctionSignature::of(f.db, func); let param = *data.params.first().unwrap(); + let owner = ExpressionStoreOwnerId::Body(func.into()); match &data.store[param] { TypeRef::Path(p) if p.is_self_type() => f.write_str("self"), TypeRef::Reference(ref_) if matches!(&data.store[ref_.ty], TypeRef::Path(p) if p.is_self_type()) => { f.write_char('&')?; if let Some(lifetime) = &ref_.lifetime { - lifetime.hir_fmt(f, &data.store)?; + lifetime.hir_fmt(f, owner, &data.store)?; f.write_char(' ')?; } if let hir_def::type_ref::Mutability::Mut = ref_.mutability { @@ -322,7 +337,7 @@ impl<'db> HirDisplay<'db> for SelfParam { } _ => { f.write_str("self: ")?; - param.hir_fmt(f, &data.store) + param.hir_fmt(f, owner, &data.store) } } } @@ -517,7 +532,11 @@ impl<'db> HirDisplay<'db> for EnumVariant { f.write_str(", ")?; } // Enum variant fields must be pub. - field.type_ref.hir_fmt(f, &data.store)?; + field.type_ref.hir_fmt( + f, + ExpressionStoreOwnerId::VariantFields(self.id.into()), + &data.store, + )?; } f.write_char(')')?; } @@ -533,13 +552,7 @@ impl<'db> HirDisplay<'db> for EnumVariant { impl<'db> HirDisplay<'db> for Type<'db> { fn hir_fmt(&self, f: &mut HirFormatter<'_, 'db>) -> Result { - self.ty.hir_fmt(f) - } -} - -impl<'db> HirDisplay<'db> for TypeNs<'db> { - fn hir_fmt(&self, f: &mut HirFormatter<'_, 'db>) -> Result { - self.ty.hir_fmt(f) + self.ty.skip_binder().hir_fmt(f) } } @@ -579,7 +592,7 @@ impl<'db> HirDisplay<'db> for TypeParam { let params = GenericParams::of(f.db, self.id.parent()); let param_data = ¶ms[self.id.local_id()]; let krate = self.id.parent().krate(f.db).id; - let ty = self.ty(f.db).ty; + let ty = self.ty(f.db).ty.skip_binder(); let predicates = GenericPredicates::query_all(f.db, self.id.parent()); let predicates = predicates .iter_identity() @@ -666,6 +679,7 @@ fn write_generic_params_or_args<'db>( include_defaults: bool, ) -> Result { let (params, store) = GenericParams::with_store(f.db, def); + let owner = def.into(); if params.iter_lt().next().is_none() && params.iter_type_or_consts().all(|it| it.1.const_param().is_none()) && params @@ -701,17 +715,17 @@ fn write_generic_params_or_args<'db>( write!(f, "{}", name.display(f.db, f.edition()))?; if include_defaults && let Some(default) = &ty.default { f.write_str(" = ")?; - default.hir_fmt(f, store)?; + default.hir_fmt(f, owner, store)?; } } TypeOrConstParamData::ConstParamData(c) => { delim(f)?; write!(f, "const {}: ", name.display(f.db, f.edition()))?; - c.ty.hir_fmt(f, store)?; + c.ty.hir_fmt(f, owner, store)?; if include_defaults && let Some(default) = &c.default { f.write_str(" = ")?; - default.hir_fmt(f, store)?; + default.hir_fmt(f, owner, store)?; } } } @@ -729,7 +743,7 @@ fn write_where_clause<'db>(def: GenericDefId, f: &mut HirFormatter<'_, 'db>) -> } f.write_str("\nwhere")?; - write_where_predicates(params, store, f)?; + write_where_predicates(params, def.into(), store, f)?; Ok(true) } @@ -752,6 +766,7 @@ fn has_disaplayable_predicates( fn write_where_predicates<'db>( params: &GenericParams, + owner: ExpressionStoreOwnerId, store: &ExpressionStore, f: &mut HirFormatter<'_, 'db>, ) -> Result { @@ -783,29 +798,31 @@ fn write_where_predicates<'db>( f.write_str("\n ")?; match pred { TypeBound { target, bound } => { - target.hir_fmt(f, store)?; + target.hir_fmt(f, owner, store)?; f.write_str(": ")?; - bound.hir_fmt(f, store)?; + bound.hir_fmt(f, owner, store)?; } Lifetime { target, bound } => { - target.hir_fmt(f, store)?; + target.hir_fmt(f, owner, store)?; write!(f, ": ")?; - bound.hir_fmt(f, store)?; + bound.hir_fmt(f, owner, store)?; } ForLifetime { lifetimes, target, bound } => { let lifetimes = lifetimes.iter().map(|it| it.display(f.db, f.edition())).join(", "); write!(f, "for<{lifetimes}> ")?; - target.hir_fmt(f, store)?; + target.hir_fmt(f, owner, store)?; f.write_str(": ")?; - bound.hir_fmt(f, store)?; + bound.hir_fmt(f, owner, store)?; } } while let Some(nxt) = iter.next_if(|nxt| check_same_target(pred, nxt)) { f.write_str(" + ")?; match nxt { - TypeBound { bound, .. } | ForLifetime { bound, .. } => bound.hir_fmt(f, store)?, - Lifetime { bound, .. } => bound.hir_fmt(f, store)?, + TypeBound { bound, .. } | ForLifetime { bound, .. } => { + bound.hir_fmt(f, owner, store)? + } + Lifetime { bound, .. } => bound.hir_fmt(f, owner, store)?, } } f.write_str(",")?; @@ -830,7 +847,7 @@ impl<'db> HirDisplay<'db> for Const { Some(name) => write!(f, "{}: ", name.display(f.db, f.edition()))?, None => f.write_str("_: ")?, } - data.type_ref.hir_fmt(f, &data.store)?; + data.type_ref.hir_fmt(f, ExpressionStoreOwnerId::Signature(self.id.into()), &data.store)?; Ok(()) } } @@ -844,7 +861,7 @@ impl<'db> HirDisplay<'db> for Static { f.write_str("mut ")?; } write!(f, "{}: ", data.name.display(f.db, f.edition()))?; - data.type_ref.hir_fmt(f, &data.store)?; + data.type_ref.hir_fmt(f, ExpressionStoreOwnerId::Signature(self.id.into()), &data.store)?; Ok(()) } } @@ -925,13 +942,19 @@ impl<'db> HirDisplay<'db> for TypeAlias { if !data.bounds.is_empty() { f.write_str(": ")?; f.write_joined( - data.bounds.iter().map(|bound| hir_display_with_store(bound, &data.store)), + data.bounds.iter().map(|bound| { + hir_display_with_store( + bound, + ExpressionStoreOwnerId::Signature(self.id.into()), + &data.store, + ) + }), " + ", )?; } if let Some(ty) = data.ty { f.write_str(" = ")?; - ty.hir_fmt(f, &data.store)?; + ty.hir_fmt(f, ExpressionStoreOwnerId::Signature(self.id.into()), &data.store)?; } write_where_clause(def_id, f)?; Ok(()) diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index 63b834a8d1..d187763151 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -39,7 +39,8 @@ mod display; pub use hir_def::ModuleId; use std::{ - fmt, + borrow::Borrow, + fmt, iter, mem::discriminant, ops::{ControlFlow, Not}, }; @@ -93,19 +94,17 @@ use hir_ty::{ method_resolution::{self, InherentImpls, MethodResolutionContext}, mir::interpret_mir, next_solver::{ - AliasTy, AnyImplId, ClauseKind, ConstKind, DbInterner, EarlyBinder, EarlyParamRegion, - ErrorGuaranteed, GenericArg, GenericArgs, ParamConst, ParamEnv, PolyFnSig, Region, - RegionKind, SolverDefId, Ty, TyKind, TypingMode, + AliasTy, AnyImplId, ClauseKind, DbInterner, EarlyBinder, ErrorGuaranteed, GenericArg, + GenericArgs, ParamEnv, PolyFnSig, Region, SolverDefId, Ty, TyKind, TypingMode, infer::{DbInternerInferExt, InferCtxt}, }, traits::{self, is_inherent_impl_coherent, structurally_normalize_ty}, }; use itertools::Itertools; -use rustc_hash::FxHashSet; +use rustc_hash::{FxHashMap, FxHashSet}; use rustc_type_ir::{ - AliasTyKind, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeSuperVisitable, TypeVisitable, - TypeVisitor, fast_reject, - inherent::{GenericArgs as _, IntoKind, SliceLike, Term as _, Ty as _}, + AliasTyKind, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, fast_reject, + inherent::{AdtDef as _, GenericArgs as _, IntoKind, SliceLike, Term as _, Ty as _}, }; use span::{AstIdNode, Edition, FileId}; use stdx::{format_to, impl_from, never}; @@ -183,6 +182,7 @@ pub use { mir::{MirEvalError, MirLowerError}, next_solver::abi::Safety, next_solver::{clear_tls_solver_cache, collect_ty_garbage}, + setup_tracing, }, // FIXME: These are needed for import assets, properly encapsulate them. hir_ty::{method_resolution::TraitImpls, next_solver::SimplifiedType}, @@ -198,6 +198,7 @@ use { name::AsName, span_map::{ExpansionSpanMap, RealSpanMap, SpanMap}, }, + hir_ty::next_solver, }; /// hir::Crate describes a single crate. It's the main interface with which @@ -781,22 +782,20 @@ impl Module { let (variants, diagnostics) = e.id.enum_variants_with_diagnostics(db); let file = e.id.lookup(db).id.file_id; let ast_id_map = db.ast_id_map(file); - if let Some(diagnostics) = &diagnostics { - for diag in diagnostics.iter() { - acc.push( - InactiveCode { - node: InFile::new( - file, - ast_id_map.get(diag.ast_id).syntax_node_ptr(), - ), - cfg: diag.cfg.clone(), - opts: diag.opts.clone(), - } - .into(), - ); - } + for diag in diagnostics { + acc.push( + InactiveCode { + node: InFile::new( + file, + ast_id_map.get(diag.ast_id).syntax_node_ptr(), + ), + cfg: diag.cfg.clone(), + opts: diag.opts.clone(), + } + .into(), + ); } - for &(v, _, _) in &variants.variants { + for &(v, _) in variants.variants.values() { let source_map = &v.fields_with_source_map(db).1; push_ty_diagnostics( db, @@ -946,14 +945,17 @@ impl Module { .collect(); if !missing.is_empty() { + let env = ParamEnvAndCrate { + param_env: db.trait_environment(GenericDefId::from(impl_id)), + krate: self.id.krate(db), + }; let self_ty = db.impl_self_ty(impl_id).instantiate_identity().skip_norm_wip(); - let self_ty = structurally_normalize_ty( - &infcx, - self_ty, - db.trait_environment(GenericDefId::from(impl_id).into()), - ); + let self_ty = structurally_normalize_ty(&infcx, self_ty, env.param_env); + let tail_ty = struct_tail_raw(db, interner, self_ty, |ty| { + structurally_normalize_ty(&infcx, ty, env.param_env) + }); let self_ty_is_guaranteed_unsized = matches!( - self_ty.kind(), + tail_ty.kind(), TyKind::Dynamic(..) | TyKind::Slice(..) | TyKind::Str ); if self_ty_is_guaranteed_unsized { @@ -1332,24 +1334,6 @@ pub struct Field { pub(crate) id: LocalFieldId, } -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub struct InstantiatedField<'db> { - pub(crate) inner: Field, - pub(crate) args: GenericArgs<'db>, -} - -impl<'db> InstantiatedField<'db> { - /// Returns the type as in the signature of the struct. - pub fn ty(&self, db: &'db dyn HirDatabase) -> TypeNs<'db> { - let interner = DbInterner::new_no_crate(db); - - let var_id = self.inner.parent.into(); - let field = db.field_types(var_id)[self.inner.id].get(); - let ty = field.instantiate(interner, self.args).skip_norm_wip(); - TypeNs::new(db, var_id, ty) - } -} - #[derive(Debug, PartialEq, Eq, Copy, Clone, Hash)] pub struct TupleField { pub owner: InferBodyId, @@ -1370,7 +1354,7 @@ impl TupleField { .get(self.index as usize) .copied() .unwrap_or_else(|| Ty::new_error(interner, ErrorGuaranteed)); - Type { env: body_param_env_from_has_crate(db, self.owner.expression_store_owner(db)), ty } + Type::new_body(db, self.owner.expression_store_owner(db), ty) } } @@ -1420,46 +1404,14 @@ impl Field { /// Returns the type as in the signature of the struct. Only use this in the /// context of the field definition. - pub fn ty<'db>(&self, db: &'db dyn HirDatabase) -> TypeNs<'db> { - let var_id = self.parent.into(); - let ty = db.field_types(var_id)[self.id].get().skip_binder(); - TypeNs::new(db, var_id, ty) - } - - // FIXME: Find better API to also handle const generics - pub fn ty_with_args<'db>( - &self, - db: &'db dyn HirDatabase, - generics: impl Iterator<Item = Type<'db>>, - ) -> Type<'db> { + pub fn ty<'db>(&self, db: &'db dyn HirDatabase) -> Type<'db> { let var_id = self.parent.into(); - let def_id: AdtId = match self.parent { - Variant::Struct(it) => it.id.into(), - Variant::Union(it) => it.id.into(), - Variant::EnumVariant(it) => it.parent_enum(db).id.into(), - }; - let interner = DbInterner::new_no_crate(db); - let args = generic_args_from_tys(interner, def_id.into(), generics.map(|ty| ty.ty)); - let ty = db.field_types(var_id)[self.id].get().instantiate(interner, args).skip_norm_wip(); - Type::new(db, var_id, ty) + let ty = db.field_types(var_id)[self.id].get().instantiate_identity().skip_norm_wip(); + Type::new(var_id.adt_id(db).into(), ty) } pub fn layout<'db>(&self, db: &'db dyn HirDatabase) -> Result<Layout<'db>, LayoutError> { - db.layout_of_ty( - self.ty(db).ty.store(), - param_env_from_has_crate( - db, - match hir_def::VariantId::from(self.parent) { - hir_def::VariantId::EnumVariantId(id) => { - GenericDefId::AdtId(id.lookup(db).parent.into()) - } - hir_def::VariantId::StructId(id) => GenericDefId::AdtId(id.into()), - hir_def::VariantId::UnionId(id) => GenericDefId::AdtId(id.into()), - }, - ) - .store(), - ) - .map(|layout| Layout(layout, db.target_data_layout(self.krate(db).into()).unwrap())) + self.ty(db).layout(db) } pub fn parent_def(&self, _db: &dyn HirDatabase) -> Variant { @@ -1504,10 +1456,6 @@ impl Struct { Type::from_def(db, self.id) } - pub fn ty_params(self, db: &dyn HirDatabase) -> Type<'_> { - Type::from_def_params(db, self.id) - } - pub fn constructor_ty(self, db: &dyn HirDatabase) -> Type<'_> { Type::from_value_def(db, self.id) } @@ -1531,11 +1479,6 @@ impl Struct { pub fn is_unstable(self, db: &dyn HirDatabase) -> bool { AttrFlags::query(db, self.id.into()).contains(AttrFlags::IS_UNSTABLE) } - - pub fn instantiate_infer<'db>(self, infer_ctxt: &InferCtxt<'db>) -> InstantiatedStruct<'db> { - let args = infer_ctxt.fresh_args_for_item(hir_ty::Span::Dummy, self.id.into()); - InstantiatedStruct { inner: self, args } - } } impl HasVisibility for Struct { @@ -1547,34 +1490,6 @@ impl HasVisibility for Struct { } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub struct InstantiatedStruct<'db> { - pub(crate) inner: Struct, - pub(crate) args: GenericArgs<'db>, -} - -impl<'db> InstantiatedStruct<'db> { - pub fn fields(self, db: &dyn HirDatabase) -> Vec<InstantiatedField<'db>> { - self.inner - .id - .fields(db) - .fields() - .iter() - .map(|(id, _)| InstantiatedField { - inner: Field { parent: self.inner.into(), id }, - args: self.args, - }) - .collect() - } - - pub fn ty(self, db: &'db dyn HirDatabase) -> TypeNs<'db> { - let interner = DbInterner::new_no_crate(db); - - let ty = db.ty(self.inner.id.into()); - TypeNs::new(db, self.inner.id, ty.instantiate(interner, self.args).skip_norm_wip()) - } -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct Union { pub(crate) id: UnionId, } @@ -1592,10 +1507,6 @@ impl Union { Type::from_def(db, self.id) } - pub fn ty_params(self, db: &dyn HirDatabase) -> Type<'_> { - Type::from_def_params(db, self.id) - } - pub fn constructor_ty(self, db: &dyn HirDatabase) -> Type<'_> { Type::from_value_def(db, self.id) } @@ -1644,7 +1555,7 @@ impl Enum { } pub fn variants(self, db: &dyn HirDatabase) -> Vec<EnumVariant> { - self.id.enum_variants(db).variants.iter().map(|&(id, _, _)| EnumVariant { id }).collect() + self.id.enum_variants(db).variants.values().map(|&(id, _)| EnumVariant { id }).collect() } pub fn num_variants(self, db: &dyn HirDatabase) -> usize { @@ -1659,15 +1570,11 @@ impl Enum { Type::from_def(db, self.id) } - pub fn ty_params<'db>(self, db: &'db dyn HirDatabase) -> Type<'db> { - Type::from_def_params(db, self.id) - } - /// The type of the enum variant bodies. pub fn variant_body_ty<'db>(self, db: &'db dyn HirDatabase) -> Type<'db> { let interner = DbInterner::new_no_crate(db); - Type::new_for_crate( - self.id.lookup(db).container.krate(db), + Type::no_params( + Type::builtin_type_crate(db), match EnumSignature::variant_body_type(db, self.id) { layout::IntegerType::Pointer(sign) => match sign { true => Ty::new_int(interner, rustc_type_ir::IntTy::Isize), @@ -1722,27 +1629,6 @@ impl HasVisibility for Enum { } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub struct InstantiatedEnum<'db> { - pub(crate) inner: Enum, - pub(crate) args: GenericArgs<'db>, -} - -impl<'db> InstantiatedEnum<'db> { - pub fn ty(self, db: &'db dyn HirDatabase) -> TypeNs<'db> { - let interner = DbInterner::new_no_crate(db); - - let ty = db.ty(self.inner.id.into()); - TypeNs::new(db, self.inner.id, ty.instantiate(interner, self.args).skip_norm_wip()) - } -} - -impl From<&EnumVariant> for DefWithBodyId { - fn from(&v: &EnumVariant) -> Self { - DefWithBodyId::VariantId(v.into()) - } -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct EnumVariant { pub(crate) id: EnumVariantId, } @@ -1761,9 +1647,7 @@ impl EnumVariant { } pub fn name(self, db: &dyn HirDatabase) -> Name { - let lookup = self.id.lookup(db); - let enum_ = lookup.parent; - enum_.enum_variants(db).variants[lookup.index as usize].1.clone() + self.id.lookup(db).name.clone() } pub fn fields(self, db: &dyn HirDatabase) -> Vec<Field> { @@ -1798,7 +1682,7 @@ impl EnumVariant { layout::Variants::Multiple { variants, .. } => Layout( { let lookup = self.id.lookup(db); - let rustc_enum_variant_idx = RustcEnumVariantIdx(lookup.index as usize); + let rustc_enum_variant_idx = RustcEnumVariantIdx(lookup.index(db)); Arc::new(variants[rustc_enum_variant_idx].clone()) }, db.target_data_layout(parent_enum.krate(db).into()).unwrap(), @@ -1810,40 +1694,6 @@ impl EnumVariant { pub fn is_unstable(self, db: &dyn HirDatabase) -> bool { AttrFlags::query(db, self.id.into()).contains(AttrFlags::IS_UNSTABLE) } - - pub fn instantiate_infer<'db>(self, infer_ctxt: &InferCtxt<'db>) -> InstantiatedVariant<'db> { - let args = infer_ctxt.fresh_args_for_item( - hir_ty::Span::Dummy, - self.parent_enum(infer_ctxt.interner.db()).id.into(), - ); - InstantiatedVariant { inner: self, args } - } -} - -// FIXME: Rename to `EnumVariant` -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub struct InstantiatedVariant<'db> { - pub(crate) inner: EnumVariant, - pub(crate) args: GenericArgs<'db>, -} - -impl<'db> InstantiatedVariant<'db> { - pub fn parent_enum(self, db: &dyn HirDatabase) -> InstantiatedEnum<'db> { - InstantiatedEnum { inner: self.inner.id.lookup(db).parent.into(), args: self.args } - } - - pub fn fields(self, db: &dyn HirDatabase) -> Vec<InstantiatedField<'db>> { - self.inner - .id - .fields(db) - .fields() - .iter() - .map(|(id, _)| InstantiatedField { - inner: Field { parent: self.inner.into(), id }, - args: self.args, - }) - .collect() - } } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] @@ -1892,23 +1742,6 @@ impl Adt { Type::from_def(db, id) } - /// Turns this ADT into a type with the given type parameters. This isn't - /// the greatest API, FIXME find a better one. - pub fn ty_with_args<'db>( - self, - db: &'db dyn HirDatabase, - args: impl IntoIterator<Item = Type<'db>>, - ) -> Type<'db> { - let id = AdtId::from(self); - let interner = DbInterner::new_no_crate(db); - let ty = Ty::new_adt( - interner, - id, - generic_args_from_tys(interner, id.into(), args.into_iter().map(|ty| ty.ty)), - ); - Type::new(db, id, ty) - } - pub fn module(self, db: &dyn HirDatabase) -> Module { match self { Adt::Struct(s) => s.module(db), @@ -2016,8 +1849,7 @@ impl AnonConst { pub fn ty<'db>(self, db: &'db dyn HirDatabase) -> Type<'db> { let loc = self.id.loc(db); - let env = body_param_env_from_has_crate(db, loc.owner); - Type { env, ty: loc.ty.get().instantiate_identity().skip_norm_wip() } + Type { owner: self.id.into(), ty: loc.ty.get() } } pub fn eval(self, db: &dyn HirDatabase) -> Result<EvaluatedConst<'_>, ConstEvalError> { @@ -2127,7 +1959,7 @@ impl DefWithBody { }, DefWithBody::Static(it) => it.id.into(), DefWithBody::Const(it) => it.id.into(), - DefWithBody::EnumVariant(it) => it.into(), + DefWithBody::EnumVariant(it) => it.id.into(), }) } @@ -2167,8 +1999,6 @@ impl DefWithBody { let Ok(id) = self.try_into() else { return; }; - let krate = self.module(db).id.krate(db); - let env = body_param_env_from_has_crate(db, id); let (body, source_map) = Body::with_source_map(db, id); let sig_source_map = match self { @@ -2191,6 +2021,7 @@ impl DefWithBody { expr_store_diagnostics(db, acc, source_map); let infer = InferenceResult::of(db, id); + let type_owner = id.generic_def(db).into(); for d in infer.diagnostics() { acc.extend(AnyDiagnostic::inference_diagnostic( db, @@ -2198,7 +2029,7 @@ impl DefWithBody { d, source_map, sig_source_map, - env, + type_owner, )); } @@ -2267,8 +2098,11 @@ impl DefWithBody { mir::MirSpan::Unknown => continue, }; acc.push( - MovedOutOfRef { ty: Type::new_for_crate(krate, moof.ty.as_ref()), span } - .into(), + MovedOutOfRef { + ty: Type { owner: type_owner, ty: EarlyBinder::bind(moof.ty.as_ref()) }, + span, + } + .into(), ) } let mol = &borrowck_result.mutability_of_locals; @@ -2365,9 +2199,9 @@ impl DefWithBody { ) -> impl Iterator<Item = Type<'db>> { self.id().into_iter().flat_map(move |def_id| { let infer = InferenceResult::of(db, def_id); - let resolver = def_id.resolver(db); + let def_id = def_id.generic_def(db); - infer.expression_types().map(move |(_, ty)| Type::new_with_resolver(db, &resolver, ty)) + infer.expression_types().map(move |(_, ty)| Type::new(def_id, ty)) }) } @@ -2375,9 +2209,9 @@ impl DefWithBody { pub fn pattern_types<'db>(self, db: &'db dyn HirDatabase) -> impl Iterator<Item = Type<'db>> { self.id().into_iter().flat_map(move |def_id| { let infer = InferenceResult::of(db, def_id); - let resolver = def_id.resolver(db); + let def_id = def_id.generic_def(db); - infer.pattern_types().map(move |(_, ty)| Type::new_with_resolver(db, &resolver, ty)) + infer.pattern_types().map(move |(_, ty)| Type::new(def_id, ty)) }) } @@ -2385,9 +2219,9 @@ impl DefWithBody { pub fn binding_types<'db>(self, db: &'db dyn HirDatabase) -> impl Iterator<Item = Type<'db>> { self.id().into_iter().flat_map(move |def_id| { let infer = InferenceResult::of(db, def_id); - let resolver = def_id.resolver(db); + let def_id = def_id.generic_def(db); - infer.binding_types().map(move |(_, ty)| Type::new_with_resolver(db, &resolver, ty)) + infer.binding_types().map(move |(_, ty)| Type::new(def_id, ty)) }) } } @@ -2420,6 +2254,9 @@ fn expr_store_diagnostics<'db>( ExpressionStoreDiagnostics::PatternArgInExternFn { node } => { PatternArgInExternFn { node: *node }.into() } + ExpressionStoreDiagnostics::FruInDestructuringAssignment { node } => { + FruInDestructuringAssignment { node: *node }.into() + } }); } @@ -2468,12 +2305,9 @@ impl Function { AnyFunctionId::BuiltinDeriveImplMethod { method, impl_ } => { // Get the type for the trait function, as we can't get the type for the impl function // because it has not `CallableDefId`. - let krate = impl_.module(db).krate(db); - let interner = DbInterner::new_with(db, krate); - let param_env = hir_ty::builtin_derive::param_env(interner, impl_); - let env = ParamEnvAndCrate { param_env, krate }; + // FIXME: This does not account for replacing `Self`. Do we really need that? let Some(trait_method) = method.trait_method(db, impl_) else { - return Type { env, ty: Ty::new_error(interner, ErrorGuaranteed) }; + return Type::unknown(); }; Function::from(trait_method).ty(db) } @@ -2483,160 +2317,36 @@ impl Function { pub fn fn_ptr_type(self, db: &dyn HirDatabase) -> Type<'_> { match self.id { AnyFunctionId::FunctionId(id) => { - let resolver = id.resolver(db); let interner = DbInterner::new_no_crate(db); - // FIXME: This shouldn't be `instantiate_identity()`, we shouldn't leak `TyKind::Param`s. let callable_sig = db.callable_item_signature(id.into()).instantiate_identity().skip_norm_wip(); let ty = Ty::new_fn_ptr(interner, callable_sig); - Type::new_with_resolver_inner(db, &resolver, ty) + Type::new(id.into(), ty) } AnyFunctionId::BuiltinDeriveImplMethod { method, impl_ } => { - struct ParamsShifter<'db> { - interner: DbInterner<'db>, - shift_by: i32, - } - - impl<'db> TypeFolder<DbInterner<'db>> for ParamsShifter<'db> { - fn cx(&self) -> DbInterner<'db> { - self.interner - } - - fn fold_ty(&mut self, ty: Ty<'db>) -> Ty<'db> { - if let TyKind::Param(param) = ty.kind() { - Ty::new_param( - self.interner, - param.id, - param.index.checked_add_signed(self.shift_by).unwrap(), - ) - } else { - ty.super_fold_with(self) - } - } - - fn fold_const( - &mut self, - ct: hir_ty::next_solver::Const<'db>, - ) -> hir_ty::next_solver::Const<'db> { - if let ConstKind::Param(param) = ct.kind() { - hir_ty::next_solver::Const::new_param( - self.interner, - ParamConst { - id: param.id, - index: param.index.checked_add_signed(self.shift_by).unwrap(), - }, - ) - } else { - ct.super_fold_with(self) - } - } - - fn fold_region(&mut self, r: Region<'db>) -> Region<'db> { - if let RegionKind::ReEarlyParam(param) = r.kind() { - Region::new_early_param( - self.interner, - EarlyParamRegion { - id: param.id, - index: param.index.checked_add_signed(self.shift_by).unwrap(), - }, - ) - } else { - r - } - } - } - // Get the type for the trait function, as we can't get the type for the impl function // because it has not `CallableDefId`. - let krate = impl_.module(db).krate(db); - let interner = DbInterner::new_with(db, krate); - let param_env = hir_ty::builtin_derive::param_env(interner, impl_); - let env = ParamEnvAndCrate { param_env, krate }; + // FIXME: This does not account for replacing `Self`. Do we really need that? let Some(trait_method) = method.trait_method(db, impl_) else { - return Type { env, ty: Ty::new_error(interner, ErrorGuaranteed) }; + return Type::unknown(); }; - // The procedure works as follows: the method may have additional generic parameters (e.g. `Hash::hash()`), - // and we want them to be params of the impl method as well. So we start with the trait method identity - // args and extract from them the trait method own args. In parallel, we retrieve the impl trait ref. - // Now we can put our args as [...impl_trait_ref.args, ...trait_method_own_args], but we have one problem: - // the args in `trait_method_own_args` use indices appropriate for the trait method, which are not necessarily - // good for the impl method. So we shift them by `impl_generics_len - trait_generics_len`, which is essentially - // `impl_generics_len - impl_trait_ref.args.len()`. - let trait_method_fn_ptr = Ty::new_fn_ptr( - interner, - db.callable_item_signature(trait_method.into()) - .instantiate_identity() - .skip_norm_wip(), - ); - let impl_trait_ref = hir_ty::builtin_derive::impl_trait(interner, impl_) - .instantiate_identity() - .skip_norm_wip(); - let trait_method_args = - GenericArgs::identity_for_item(interner, trait_method.into()); - let trait_method_own_args = GenericArgs::new_from_iter( - interner, - trait_method_args.iter().skip(impl_trait_ref.args.len()), - ); - let impl_params_count = hir_ty::builtin_derive::generic_params_count(db, impl_); - let shift_args_by = impl_params_count as i32 - impl_trait_ref.args.len() as i32; - let shifted_trait_method_own_args = trait_method_own_args - .fold_with(&mut ParamsShifter { interner, shift_by: shift_args_by }); - let impl_method_args = GenericArgs::new_from_iter( - interner, - impl_trait_ref.args.iter().chain(shifted_trait_method_own_args), - ); - let impl_method_fn_ptr = EarlyBinder::bind(trait_method_fn_ptr) - .instantiate(interner, impl_method_args) - .skip_norm_wip(); - Type { env, ty: impl_method_fn_ptr } + Function::from(trait_method).fn_ptr_type(db) } } } - fn fn_sig<'db>(self, db: &'db dyn HirDatabase) -> (ParamEnvAndCrate<'db>, PolyFnSig<'db>) { + fn fn_sig<'db>(self, db: &'db dyn HirDatabase) -> (TypeOwnerId, PolyFnSig<'db>) { let fn_ptr = self.fn_ptr_type(db); - let TyKind::FnPtr(sig_tys, hdr) = fn_ptr.ty.kind() else { + let TyKind::FnPtr(sig_tys, hdr) = fn_ptr.ty.skip_binder().kind() else { unreachable!(); }; - (fn_ptr.env, sig_tys.with(hdr)) + (fn_ptr.owner, sig_tys.with(hdr)) } - // FIXME: Find a better API to express all combinations here, perhaps we should have `PreInstantiationType`? - /// Get this function's return type pub fn ret_type(self, db: &dyn HirDatabase) -> Type<'_> { - let (env, sig) = self.fn_sig(db); - Type { env, ty: sig.skip_binder().output() } - } - - // FIXME: Find better API to also handle const generics - pub fn ret_type_with_args<'db>( - self, - db: &'db dyn HirDatabase, - generics: impl Iterator<Item = Type<'db>>, - ) -> Type<'db> { - let ret_type = self.ret_type(db); - let interner = DbInterner::new_no_crate(db); - let args = self.adapt_generic_args(interner, generics); - ret_type.derived(EarlyBinder::bind(ret_type.ty).instantiate(interner, args).skip_norm_wip()) - } - - fn adapt_generic_args<'db>( - self, - interner: DbInterner<'db>, - generics: impl Iterator<Item = Type<'db>>, - ) -> GenericArgs<'db> { - let generics = generics.map(|ty| ty.ty); - match self.id { - AnyFunctionId::FunctionId(id) => generic_args_from_tys(interner, id.into(), generics), - AnyFunctionId::BuiltinDeriveImplMethod { impl_, .. } => { - let impl_args = GenericArgs::identity_for_item(interner, impl_.into()); - GenericArgs::new_from_iter( - interner, - impl_args.iter().chain(generics.map(Into::into)), - ) - } - } + let (owner, sig) = self.fn_sig(db); + Type { owner, ty: EarlyBinder::bind(sig.skip_binder().output()) } } pub fn async_ret_type<'db>(self, db: &'db dyn HirDatabase) -> Option<Type<'db>> { @@ -2646,15 +2356,13 @@ impl Function { if !self.is_async(db) { return None; } - let resolver = id.resolver(db); - // FIXME: This shouldn't be `instantiate_identity()`, we shouldn't leak `TyKind::Param`s. let ret_ty = db.callable_item_signature(id.into()).instantiate_identity().skip_binder().output(); for pred in ret_ty.impl_trait_bounds(db).into_iter().flatten() { if let ClauseKind::Projection(projection) = pred.kind().skip_binder() && let Some(output_ty) = projection.term.as_type() { - return Type::new_with_resolver_inner(db, &resolver, output_ty).into(); + return Some(Type::new(id.into(), output_ty)); } } None @@ -2680,7 +2388,7 @@ impl Function { } pub fn assoc_fn_params(self, db: &dyn HirDatabase) -> Vec<Param<'_>> { - let (env, sig) = self.fn_sig(db); + let (owner, sig) = self.fn_sig(db); let func = match self.id { AnyFunctionId::FunctionId(id) => Callee::Def(CallableDefId::FunctionId(id)), AnyFunctionId::BuiltinDeriveImplMethod { method, impl_ } => { @@ -2691,7 +2399,11 @@ impl Function { .inputs() .iter() .enumerate() - .map(|(idx, &ty)| Param { func: func.clone(), ty: Type { env, ty }, idx }) + .map(|(idx, &ty)| Param { + func: func.clone(), + ty: Type { owner, ty: EarlyBinder::bind(ty) }, + idx, + }) .collect() } @@ -2717,28 +2429,6 @@ impl Function { params } - // FIXME: Find better API to also handle const generics - pub fn params_without_self_with_args<'db>( - self, - db: &'db dyn HirDatabase, - generics: impl Iterator<Item = Type<'db>>, - ) -> Vec<Param<'db>> { - let interner = DbInterner::new_no_crate(db); - let args = self.adapt_generic_args(interner, generics); - let params = self.params_without_self(db); - params - .into_iter() - .map(|param| Param { - func: param.func, - idx: param.idx, - ty: Type { - env: param.ty.env, - ty: EarlyBinder::bind(param.ty.ty).instantiate(interner, args).skip_norm_wip(), - }, - }) - .collect() - } - pub fn is_const(self, db: &dyn HirDatabase) -> bool { match self.id { AnyFunctionId::FunctionId(id) => FunctionSignature::of(db, id).is_const(), @@ -2753,6 +2443,13 @@ impl Function { } } + pub fn is_unsafe(self, db: &dyn HirDatabase) -> bool { + match self.id { + AnyFunctionId::FunctionId(id) => FunctionSignature::of(db, id).is_unsafe(), + AnyFunctionId::BuiltinDeriveImplMethod { .. } => false, + } + } + pub fn is_varargs(self, db: &dyn HirDatabase) -> bool { match self.id { AnyFunctionId::FunctionId(id) => FunctionSignature::of(db, id).is_varargs(), @@ -2913,7 +2610,7 @@ impl Function { id.into(), GenericArgs::empty(interner).store(), ParamEnvAndCrate { - param_env: db.trait_environment(GenericDefId::from(id).into()), + param_env: db.trait_environment(id.into()), krate: id.module(db).krate(db), } .store(), @@ -3085,20 +2782,8 @@ impl SelfParam { } pub fn ty<'db>(&self, db: &'db dyn HirDatabase) -> Type<'db> { - let (env, sig) = self.func.fn_sig(db); - Type { env, ty: sig.skip_binder().inputs()[0] } - } - - // FIXME: Find better API to also handle const generics - pub fn ty_with_args<'db>( - &self, - db: &'db dyn HirDatabase, - generics: impl Iterator<Item = Type<'db>>, - ) -> Type<'db> { - let interner = DbInterner::new_no_crate(db); - let args = self.func.adapt_generic_args(interner, generics); - let Type { env, ty } = self.ty(db); - Type { env, ty: EarlyBinder::bind(ty).instantiate(interner, args).skip_norm_wip() } + let (owner, sig) = self.func.fn_sig(db); + Type { owner, ty: EarlyBinder::bind(sig.skip_binder().inputs()[0]) } } } @@ -3430,10 +3115,6 @@ impl TypeAlias { Type::from_def(db, self.id) } - pub fn ty_params(self, db: &dyn HirDatabase) -> Type<'_> { - Type::from_def_params(db, self.id) - } - pub fn name(self, db: &dyn HirDatabase) -> Name { TypeAliasSignature::of(db, self.id).name.clone() } @@ -3482,10 +3163,13 @@ impl BuiltinType { } } + pub fn bool() -> BuiltinType { + BuiltinType { inner: hir_def::builtin_type::BuiltinType::Bool } + } + pub fn ty<'db>(self, db: &'db dyn HirDatabase) -> Type<'db> { - let core = Crate::core(db).map(|core| core.id).unwrap_or_else(|| all_crates(db)[0]); let interner = DbInterner::new_no_crate(db); - Type::new_for_crate(core, Ty::from_builtin_type(interner, self.inner)) + Type::no_params(Type::builtin_type_crate(db), Ty::from_builtin_type(interner, self.inner)) } pub fn name(self) -> Name { @@ -3598,6 +3282,7 @@ impl Macro { } MacroExpander::BuiltInAttr(_) => MacroKind::AttrBuiltIn, MacroExpander::BuiltInDerive(_) => MacroKind::DeriveBuiltIn, + MacroExpander::UnimplementedBuiltIn => MacroKind::Declarative, }, MacroId::MacroRulesId(it) => match it.lookup(db).expander { MacroExpander::Declarative { .. } => MacroKind::Declarative, @@ -3606,6 +3291,7 @@ impl Macro { } MacroExpander::BuiltInAttr(_) => MacroKind::AttrBuiltIn, MacroExpander::BuiltInDerive(_) => MacroKind::DeriveBuiltIn, + MacroExpander::UnimplementedBuiltIn => MacroKind::Declarative, }, MacroId::ProcMacroId(it) => match it.lookup(db).kind { ProcMacroKind::CustomDerive => MacroKind::Derive, @@ -4278,23 +3964,19 @@ impl GenericDef { // We cannot call this `Substitution` unfortunately... #[derive(Debug)] pub struct GenericSubstitution<'db> { + owner: TypeOwnerId, def: GenericDefId, subst: GenericArgs<'db>, - env: ParamEnvAndCrate<'db>, } impl<'db> GenericSubstitution<'db> { - fn new(def: GenericDefId, subst: GenericArgs<'db>, env: ParamEnvAndCrate<'db>) -> Self { - Self { def, subst, env } + fn new(def: GenericDefId, subst: GenericArgs<'db>, owner: TypeOwnerId) -> Self { + Self { owner, def, subst } } - fn new_from_fn( - def: Function, - subst: GenericArgs<'db>, - env: ParamEnvAndCrate<'db>, - ) -> Option<Self> { + fn new_from_fn(def: Function, subst: GenericArgs<'db>, owner: TypeOwnerId) -> Option<Self> { match def.id { - AnyFunctionId::FunctionId(def) => Some(Self::new(def.into(), subst, env)), + AnyFunctionId::FunctionId(def) => Some(Self::new(def.into(), subst, owner)), AnyFunctionId::BuiltinDeriveImplMethod { .. } => None, } } @@ -4341,7 +4023,12 @@ impl<'db> GenericSubstitution<'db> { .zip(type_params); container_params .chain(self_params) - .filter_map(|(ty, name)| Some((name?.symbol().clone(), Type { ty, env: self.env }))) + .filter_map(|(ty, name)| { + Some(( + name?.symbol().clone(), + Type { ty: EarlyBinder::bind(ty), owner: self.owner }, + )) + }) .collect() } } @@ -4454,7 +4141,7 @@ impl Local { let def = self.parent; let infer = InferenceResult::of(db, self.parent_infer); let ty = infer.binding_ty(self.binding_id); - Type::new(db, def, ty) + Type::new_body(db, def, ty) } /// All definitions for this local. Example: `let (a$0, _) | (_, a$0) = it;` @@ -4754,18 +4441,17 @@ impl TypeParam { } pub fn ty(self, db: &dyn HirDatabase) -> Type<'_> { - let resolver = self.id.parent().resolver(db); let interner = DbInterner::new_no_crate(db); let index = hir_ty::type_or_const_param_idx(db, self.id.into()); let ty = Ty::new_param(interner, self.id, index); - Type::new_with_resolver_inner(db, &resolver, ty) + Type::new(self.id.parent(), ty) } /// FIXME: this only lists trait bounds from the item defining the type /// parameter, not additional bounds that might be added e.g. by a method if /// the parameter comes from an impl! pub fn trait_bounds(self, db: &dyn HirDatabase) -> Vec<Trait> { - let self_ty = self.ty(db).ty; + let self_ty = self.ty(db).ty.instantiate_identity().skip_norm_wip(); GenericPredicates::query_explicit(db, self.id.parent()) .iter_identity() .filter_map(|pred| match &pred.kind().skip_binder() { @@ -4779,10 +4465,9 @@ impl TypeParam { pub fn default(self, db: &dyn HirDatabase) -> Option<Type<'_>> { let ty = generic_arg_from_param(db, self.id.into())?; - let resolver = self.id.parent().resolver(db); match ty.kind() { rustc_type_ir::GenericArgKind::Type(it) if !it.is_ty_error() => { - Some(Type::new_with_resolver_inner(db, &resolver, it)) + Some(Type::new(self.id.parent(), it)) } _ => None, } @@ -4843,7 +4528,7 @@ impl ConstParam { } pub fn ty(self, db: &dyn HirDatabase) -> Type<'_> { - Type::new(db, self.id.parent(), db.const_param_ty(self.id)) + Type::new(self.id.parent(), db.const_param_ty(self.id)) } pub fn default(self, db: &dyn HirDatabase, display_target: DisplayTarget) -> Option<String> { @@ -4965,21 +4650,26 @@ impl Impl { /// blanket impls, and only does a shallow type constructor check. In fact, this should've probably been on `Adt` /// etc., and not on `Type`. If you would want to create a precise list of all impls applying to a type, /// you would need to include blanket impls, and try to prove to predicates for each candidate. - pub fn all_for_type<'db>(db: &'db dyn HirDatabase, Type { ty, env }: Type<'db>) -> Vec<Impl> { + pub fn all_for_type<'db>(db: &'db dyn HirDatabase, ty: Type<'db>) -> Vec<Impl> { let mut result = Vec::new(); let interner = DbInterner::new_no_crate(db); - let Some(simplified_ty) = - fast_reject::simplify_type(interner, ty, fast_reject::TreatParams::AsRigid) - else { + let Some(simplified_ty) = fast_reject::simplify_type( + interner, + ty.ty.skip_binder(), + fast_reject::TreatParams::AsRigid, + ) else { return Vec::new(); }; let mut extend_with_impls = |impls: Either<&[ImplId], &[BuiltinDeriveImplId]>| match impls { Either::Left(impls) => result.extend(impls.iter().copied().map(Impl::from)), Either::Right(impls) => result.extend(impls.iter().copied().map(Impl::from)), }; - method_resolution::with_incoherent_inherent_impls(db, env.krate, &simplified_ty, |impls| { - extend_with_impls(Either::Left(impls)) - }); + method_resolution::with_incoherent_inherent_impls( + db, + ty.krate(db), + &simplified_ty, + |impls| extend_with_impls(Either::Left(impls)), + ); if let Some(module) = method_resolution::simplified_type_module(db, &simplified_ty) { InherentImpls::for_each_crate_and_block( db, @@ -4987,7 +4677,7 @@ impl Impl { module.block(db), &mut |impls| extend_with_impls(Either::Left(impls.for_self_ty(&simplified_ty))), ); - std::iter::successors(module.block(db), |block| block.loc(db).module.block(db)) + iter::successors(module.block(db), |block| block.loc(db).module.block(db)) .filter_map(|block| TraitImpls::for_block(db, block).as_deref()) .for_each(|impls| impls.for_self_ty(&simplified_ty, &mut extend_with_impls)); for &krate in &*all_crates(db) { @@ -5042,21 +4732,16 @@ impl Impl { match self.id { AnyImplId::ImplId(id) => { let trait_ref = db.impl_trait(id)?.instantiate_identity().skip_norm_wip(); - let resolver = id.resolver(db); - Some(TraitRef::new_with_resolver(db, &resolver, trait_ref)) + Some(TraitRef::new(id.into(), trait_ref)) } AnyImplId::BuiltinDeriveImplId(id) => { let loc = id.loc(db); let krate = loc.module(db).krate(db); let interner = DbInterner::new_with(db, krate); - let env = ParamEnvAndCrate { - param_env: hir_ty::builtin_derive::param_env(interner, id), - krate, - }; let trait_ref = hir_ty::builtin_derive::impl_trait(interner, id) .instantiate_identity() .skip_norm_wip(); - Some(TraitRef { env, trait_ref }) + Some(TraitRef { owner: TypeOwnerId::BuiltinDeriveImplId(id), trait_ref }) } } } @@ -5064,24 +4749,16 @@ impl Impl { pub fn self_ty(self, db: &dyn HirDatabase) -> Type<'_> { match self.id { AnyImplId::ImplId(id) => { - let resolver = id.resolver(db); - // FIXME: This shouldn't be `instantiate_identity()`, we shouldn't leak `TyKind::Param`s. let ty = db.impl_self_ty(id).instantiate_identity().skip_norm_wip(); - Type::new_with_resolver_inner(db, &resolver, ty) + Type::new(id.into(), ty) } AnyImplId::BuiltinDeriveImplId(id) => { let loc = id.loc(db); let krate = loc.module(db).krate(db); let interner = DbInterner::new_with(db, krate); - let env = ParamEnvAndCrate { - param_env: hir_ty::builtin_derive::param_env(interner, id), - krate, - }; - let ty = hir_ty::builtin_derive::impl_trait(interner, id) - .instantiate_identity() - .skip_norm_wip() - .self_ty(); - Type { env, ty } + let ty = + hir_ty::builtin_derive::impl_trait(interner, id).map_bound(|it| it.self_ty()); + Type { owner: TypeOwnerId::BuiltinDeriveImplId(id), ty } } } } @@ -5143,38 +4820,33 @@ impl Impl { #[derive(Clone, PartialEq, Eq, Debug, Hash)] pub struct TraitRef<'db> { - env: ParamEnvAndCrate<'db>, + owner: TypeOwnerId, trait_ref: hir_ty::next_solver::TraitRef<'db>, } impl<'db> TraitRef<'db> { - pub(crate) fn new_with_resolver( - db: &'db dyn HirDatabase, - resolver: &Resolver<'_>, - trait_ref: hir_ty::next_solver::TraitRef<'db>, - ) -> Self { - let env = param_env_from_resolver(db, resolver); - TraitRef { env, trait_ref } + fn new(owner: GenericDefId, trait_ref: hir_ty::next_solver::TraitRef<'db>) -> Self { + Self { owner: TypeOwnerId::GenericDefId(owner), trait_ref } } pub fn trait_(&self) -> Trait { Trait { id: self.trait_ref.def_id.0 } } - pub fn self_ty(&self) -> TypeNs<'_> { + pub fn self_ty(&self) -> Type<'_> { let ty = self.trait_ref.self_ty(); - TypeNs { env: self.env, ty } + Type { owner: self.owner, ty: EarlyBinder::bind(ty) } } /// Returns `idx`-th argument of this trait reference if it is a type argument. Note that the /// first argument is the `Self` type. - pub fn get_type_argument(&self, idx: usize) -> Option<TypeNs<'db>> { + pub fn get_type_argument(&self, idx: usize) -> Option<Type<'db>> { self.trait_ref .args .as_slice() .get(idx) .and_then(|arg| arg.ty()) - .map(|ty| TypeNs { env: self.env, ty }) + .map(|ty| Type { owner: self.owner, ty: EarlyBinder::bind(ty) }) } } @@ -5186,6 +4858,7 @@ enum AnyClosureId { #[derive(Clone, Debug, PartialEq, Eq, Hash)] pub struct Closure<'db> { + owner: TypeOwnerId, id: AnyClosureId, subst: GenericArgs<'db>, } @@ -5223,12 +4896,11 @@ impl<'db> Closure<'db> { let InternedClosure { owner: infer_owner, expr: closure, .. } = closure; let infer = InferenceResult::of(db, infer_owner); let owner = infer_owner.expression_store_owner(db); - let param_env = body_param_env_from_has_crate(db, owner); infer.closures_data[&closure] .min_captures .values() .flatten() - .map(|capture| ClosureCapture { owner, infer_owner, closure, capture, param_env }) + .map(|capture| ClosureCapture { owner, infer_owner, closure, capture }) .collect() } @@ -5319,7 +4991,6 @@ pub struct ClosureCapture<'db> { infer_owner: InferBodyId, closure: ExprId, capture: &'db hir_ty::closure_analysis::CapturedPlace, - param_env: ParamEnvAndCrate<'db>, } impl<'db> ClosureCapture<'db> { @@ -5427,14 +5098,14 @@ impl<'db> ClosureCapture<'db> { result } - pub fn ty(&self, _db: &'db dyn HirDatabase) -> Type<'db> { - Type { env: self.param_env, ty: self.capture.place.ty() } + pub fn ty(&self, db: &'db dyn HirDatabase) -> Type<'db> { + Type::new_body(db, self.owner, self.capture.place.ty()) } /// The type that is stored in the closure, which is different from [`Self::ty()`], representing /// the place's type, when the capture is by ref. pub fn captured_ty(&self, db: &'db dyn HirDatabase) -> Type<'db> { - Type { env: self.param_env, ty: self.capture.captured_ty(db) } + Type::new_body(db, self.owner, self.capture.captured_ty(db)) } } @@ -5510,69 +5181,143 @@ impl CaptureUsageSource { } } -#[derive(Clone, PartialEq, Eq, Debug, Hash)] +#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)] +enum TypeOwnerId { + GenericDefId(GenericDefId), + BuiltinDeriveImplId(BuiltinDeriveImplId), + AnonConstId(AnonConstId), + // FIXME: What do when we unify two different crates? Currently we just randomly keep one. + NoParams(base_db::Crate), +} +impl_from!(GenericDefId, BuiltinDeriveImplId, AnonConstId for TypeOwnerId); + +impl TypeOwnerId { + fn unify(self, other: Self) -> Option<Self> { + match (self, other) { + (TypeOwnerId::NoParams(_), owner) => Some(owner), + (owner, TypeOwnerId::NoParams(_)) => Some(owner), + (_, _) => { + if self == other { + Some(self) + } else { + None + } + } + } + } + + #[track_caller] + fn must_unify(self, other: Self) -> Self { + self.unify(other).expect("failed to unify type owners") + } + + fn can_rebase_into( + self, + db: &dyn HirDatabase, + rebase_into: Self, + self_ty: EarlyBinder<'_, Ty<'_>>, + ) -> bool { + if self == rebase_into || !self_ty.skip_binder().has_param() { + return true; + } + let self_def = match self { + TypeOwnerId::GenericDefId(def) => def, + TypeOwnerId::BuiltinDeriveImplId(_) | TypeOwnerId::AnonConstId(_) => return false, + TypeOwnerId::NoParams(_) => return true, + }; + let self_def = match self_def { + GenericDefId::ImplId(def) => ItemContainerId::ImplId(def), + GenericDefId::TraitId(def) => ItemContainerId::TraitId(def), + GenericDefId::AdtId(_) + | GenericDefId::ConstId(_) + | GenericDefId::FunctionId(_) + | GenericDefId::StaticId(_) + | GenericDefId::TypeAliasId(_) => return false, + }; + let rebase_into_def = match rebase_into { + TypeOwnerId::GenericDefId(def) => def, + TypeOwnerId::BuiltinDeriveImplId(_) + | TypeOwnerId::AnonConstId(_) + | TypeOwnerId::NoParams(_) => return false, + }; + let rebase_into_parent = match rebase_into_def { + GenericDefId::ConstId(def) => def.loc(db).container, + GenericDefId::FunctionId(def) => def.loc(db).container, + GenericDefId::TypeAliasId(def) => def.loc(db).container, + GenericDefId::AdtId(_) + | GenericDefId::ImplId(_) + | GenericDefId::StaticId(_) + | GenericDefId::TraitId(_) => return false, + }; + self_def == rebase_into_parent + } +} + +/// Note: A [`Type`] remembers its origin. Trying to do anything (except comparing) +/// with types of different origins will cause errors or panics. Instead, use the `instantiate` methods. +#[derive(Clone, Debug)] pub struct Type<'db> { - env: ParamEnvAndCrate<'db>, - ty: Ty<'db>, + owner: TypeOwnerId, + ty: EarlyBinder<'db, Ty<'db>>, } -impl<'db> Type<'db> { - pub(crate) fn new_with_resolver( - db: &'db dyn HirDatabase, - resolver: &Resolver<'_>, - ty: Ty<'db>, - ) -> Self { - Type::new_with_resolver_inner(db, resolver, ty) +impl<'db> std::hash::Hash for Type<'db> { + fn hash<H: std::hash::Hasher>(&self, state: &mut H) { + // Do not hash the owner as different owners can compare the same. + // self.owner.hash(state); + self.ty.hash(state); } +} - pub(crate) fn new_with_resolver_inner( - db: &'db dyn HirDatabase, - resolver: &Resolver<'_>, - ty: Ty<'db>, - ) -> Self { - let environment = param_env_from_resolver(db, resolver); - Type { env: environment, ty } +impl<'db> PartialEq for Type<'db> { + fn eq(&self, other: &Self) -> bool { + if self.ty != other.ty { + return false; + } + hir_ty::with_attached_db(|db| { + self.owner.can_rebase_into(db, other.owner, self.ty) + || other.owner.can_rebase_into(db, self.owner, other.ty) + }) } +} - pub(crate) fn new_for_crate(krate: base_db::Crate, ty: Ty<'db>) -> Self { - Type { env: empty_param_env(krate), ty } +impl<'db> Eq for Type<'db> {} + +impl<'db> Type<'db> { + fn new(owner: GenericDefId, ty: Ty<'db>) -> Self { + Type { owner: TypeOwnerId::GenericDefId(owner), ty: EarlyBinder::bind(ty) } } - fn new(db: &'db dyn HirDatabase, lexical_env: impl HasResolver, ty: Ty<'db>) -> Self { - let resolver = lexical_env.resolver(db); - let environment = param_env_from_resolver(db, &resolver); - Type { env: environment, ty } + fn new_body(db: &dyn HirDatabase, owner: ExpressionStoreOwnerId, ty: Ty<'db>) -> Self { + Self::new(owner.generic_def(db), ty) } - fn from_def(db: &'db dyn HirDatabase, def: impl Into<TyDefId> + HasResolver) -> Self { - let interner = DbInterner::new_no_crate(db); - let ty = db.ty(def.into()); - let def = match def.into() { - TyDefId::AdtId(it) => GenericDefId::AdtId(it), - TyDefId::TypeAliasId(it) => GenericDefId::TypeAliasId(it), - TyDefId::BuiltinType(_) => { - return Type::new(db, def, ty.skip_binder()); - } - }; - let args = GenericArgs::error_for_item(interner, def.into()); - Type::new(db, def, ty.instantiate(interner, args).skip_norm_wip()) + fn no_params(krate: base_db::Crate, ty: Ty<'db>) -> Self { + Type { owner: TypeOwnerId::NoParams(krate), ty: EarlyBinder::bind(ty) } } - // FIXME: We shouldn't leak `TyKind::Param`s. - fn from_def_params(db: &'db dyn HirDatabase, def: impl Into<TyDefId> + HasResolver) -> Self { - let ty = db.ty(def.into()); - Type::new(db, def, ty.instantiate_identity().skip_norm_wip()) + fn builtin_type_crate(db: &'db dyn HirDatabase) -> base_db::Crate { + // It doesn't really matter. + all_crates(db)[0] } - fn from_value_def( - db: &'db dyn HirDatabase, - def: impl Into<ValueTyDefId> + HasResolver, - ) -> Self { - let interner = DbInterner::new_no_crate(db); - let Some(ty) = db.value_ty(def.into()) else { - return Type::new(db, def, Ty::new_error(interner, ErrorGuaranteed)); + fn from_def(db: &'db dyn HirDatabase, def: impl Into<TyDefId>) -> Self { + let def = def.into(); + let ty = db.ty(def); + let owner = match def { + TyDefId::AdtId(it) => TypeOwnerId::GenericDefId(GenericDefId::AdtId(it)), + TyDefId::TypeAliasId(it) => TypeOwnerId::GenericDefId(GenericDefId::TypeAliasId(it)), + TyDefId::BuiltinType(_) => TypeOwnerId::NoParams(Self::builtin_type_crate(db)), + }; + Type { owner, ty } + } + + fn from_value_def(db: &'db dyn HirDatabase, def: impl Into<ValueTyDefId>) -> Self { + let def = def.into(); + let Some(ty) = db.value_ty(def) else { + return Type::unknown(); }; - let def = match def.into() { + let def = match def { ValueTyDefId::ConstId(it) => GenericDefId::ConstId(it), ValueTyDefId::FunctionId(it) => GenericDefId::FunctionId(it), ValueTyDefId::StructId(it) => GenericDefId::AdtId(AdtId::StructId(it)), @@ -5580,52 +5325,192 @@ impl<'db> Type<'db> { ValueTyDefId::EnumVariantId(it) => { GenericDefId::AdtId(AdtId::EnumId(it.lookup(db).parent)) } - ValueTyDefId::StaticId(_) => { - return Type::new(db, def, ty.skip_binder()); + ValueTyDefId::StaticId(it) => { + return Type::no_params(hir_def::HasModule::krate(&it, db), ty.skip_binder()); + } + }; + Type::new(def, ty.instantiate_identity().skip_norm_wip()) + } + + /// Replace any generic parameters with error types. + pub fn instantiate_with_errors(&self) -> Self { + let interner = DbInterner::conjure(); + let krate = self.krate(interner.db()); + let args = match self.owner { + TypeOwnerId::GenericDefId(def) => GenericArgs::error_for_item(interner, def.into()), + TypeOwnerId::BuiltinDeriveImplId(def) => { + GenericArgs::error_for_item(interner, def.into()) } + TypeOwnerId::AnonConstId(def) => GenericArgs::error_for_item(interner, def.into()), + TypeOwnerId::NoParams(_) => GenericArgs::empty(interner), }; - let args = GenericArgs::error_for_item(interner, def.into()); - Type::new(db, def, ty.instantiate(interner, args).skip_norm_wip()) + Type::no_params(krate, self.ty.instantiate(interner, args).skip_norm_wip()) + } + + // FIXME: Find some way with const params, maybe even lifetimes? + pub fn instantiate(&self, args: impl IntoIterator<Item: Borrow<Type<'db>>>) -> Type<'db> { + let interner = DbInterner::conjure(); + let (args, owner) = match self.owner { + TypeOwnerId::GenericDefId(def) => generic_args_from_tys(interner, def.into(), args), + TypeOwnerId::BuiltinDeriveImplId(def) => { + generic_args_from_tys(interner, def.into(), args) + } + TypeOwnerId::AnonConstId(def) => generic_args_from_tys(interner, def.into(), args), + TypeOwnerId::NoParams(krate) => { + (GenericArgs::empty(interner), TypeOwnerId::NoParams(krate)) + } + }; + Type { owner, ty: EarlyBinder::bind(self.ty.instantiate(interner, args).skip_norm_wip()) } + } + + /// Instantiates multiple types with infer vars, keeping the same infer vars for the same owners. + fn instantiate_many_with_infer( + tys: impl IntoIterator<Item: Borrow<Type<'db>>>, + infcx: &InferCtxt<'db>, + ) -> impl Iterator<Item = Ty<'db>> { + let mut var_for_param = FxHashMap::default(); + tys.into_iter().map(move |ty| { + let ty = ty.borrow(); + let owner = match ty.owner { + TypeOwnerId::GenericDefId(def) => def.into(), + TypeOwnerId::BuiltinDeriveImplId(def) => def.into(), + TypeOwnerId::AnonConstId(def) => def.into(), + TypeOwnerId::NoParams(_) => return ty.ty.skip_binder(), + }; + let args = GenericArgs::for_item(infcx.interner, owner, |_, param, _| { + *var_for_param + .entry(param) + .or_insert_with(|| infcx.var_for_def(param, hir_ty::Span::Dummy)) + }); + + ty.ty.instantiate(infcx.interner, args).skip_norm_wip() + }) + } + + /// Tries to put this type as-is in the context of `rebase_into`. This will return `Some(_)` if: + /// + /// - The type does not reference generic parameters, or + /// - `rebase_into` is in the context of a child of our context (for example, a function in an impl). + pub fn try_rebase_into( + &self, + db: &'db dyn HirDatabase, + rebase_into: &Type<'db>, + ) -> Option<Self> { + if self.owner.can_rebase_into(db, rebase_into.owner, self.ty) { + Some(Type { owner: rebase_into.owner, ty: self.ty }) + } else { + None + } + } + + /// If `self` can be rebased into `rebase_into`, returns that. Otherwise, instantiates `self` with errors + /// and returns that. + pub fn rebase_into_or_error( + &self, + db: &'db dyn HirDatabase, + rebase_into: &Type<'db>, + ) -> Type<'db> { + self.try_rebase_into(db, rebase_into).unwrap_or_else(|| self.instantiate_with_errors()) } - pub fn new_slice(ty: Self) -> Self { + pub fn try_rebase_into_owner( + &self, + db: &'db dyn HirDatabase, + new_owner: GenericDef, + ) -> Option<Self> { + let new_owner = new_owner.id()?.into(); + if self.owner.can_rebase_into(db, new_owner, self.ty) { + Some(Type { owner: new_owner, ty: self.ty }) + } else { + None + } + } + + pub fn rebase_into_owner_or_error( + &self, + db: &'db dyn HirDatabase, + new_owner: GenericDef, + ) -> Self { + self.try_rebase_into_owner(db, new_owner).unwrap_or_else(|| self.instantiate_with_errors()) + } + + pub fn unknown() -> Self { let interner = DbInterner::conjure(); - Type { env: ty.env, ty: Ty::new_slice(interner, ty.ty) } + Type::no_params( + Self::builtin_type_crate(interner.db()), + Ty::new_error(interner, ErrorGuaranteed), + ) + } + + pub fn new_slice(db: &'db dyn HirDatabase, ty: Self) -> Self { + let interner = DbInterner::new_no_crate(db); + Type { owner: ty.owner, ty: ty.ty.map_bound(|ty| Ty::new_slice(interner, ty)) } + } + + pub fn new_tuple( + db: &'db dyn HirDatabase, + tys: impl IntoIterator<Item: Borrow<Type<'db>>>, + ) -> Self { + let interner = DbInterner::new_no_crate(db); + let mut owner = None::<TypeOwnerId>; + let ty = EarlyBinder::bind(Ty::new_tup_from_iter( + interner, + tys.into_iter().map(|ty| { + let ty = ty.borrow(); + + match &mut owner { + Some(owner) => *owner = owner.must_unify(ty.owner), + None => owner = Some(ty.owner), + } + + ty.ty.skip_binder() + }), + )); + let owner = + owner.unwrap_or_else(|| TypeOwnerId::NoParams(Self::builtin_type_crate(interner.db()))); + Type { owner, ty } } - pub fn new_tuple(krate: base_db::Crate, tys: &[Self]) -> Self { - let tys = tys.iter().map(|it| it.ty); + pub fn new_unit() -> Self { let interner = DbInterner::conjure(); - Type { env: empty_param_env(krate), ty: Ty::new_tup_from_iter(interner, tys) } + Type::no_params(Self::builtin_type_crate(interner.db()), Ty::new_unit(interner)) } pub fn is_unit(&self) -> bool { - self.ty.is_unit() + self.ty.skip_binder().is_unit() } pub fn is_bool(&self) -> bool { - matches!(self.ty.kind(), TyKind::Bool) + matches!(self.ty.skip_binder().kind(), TyKind::Bool) } pub fn is_str(&self) -> bool { - matches!(self.ty.kind(), TyKind::Str) + matches!(self.ty.skip_binder().kind(), TyKind::Str) } pub fn is_never(&self) -> bool { - matches!(self.ty.kind(), TyKind::Never) + matches!(self.ty.skip_binder().kind(), TyKind::Never) } pub fn is_mutable_reference(&self) -> bool { - matches!(self.ty.kind(), TyKind::Ref(.., hir_ty::next_solver::Mutability::Mut)) + matches!( + self.ty.skip_binder().kind(), + TyKind::Ref(.., hir_ty::next_solver::Mutability::Mut) + ) } pub fn is_reference(&self) -> bool { - matches!(self.ty.kind(), TyKind::Ref(..)) + matches!(self.ty.skip_binder().kind(), TyKind::Ref(..)) } pub fn contains_reference(&self, db: &'db dyn HirDatabase) -> bool { let interner = DbInterner::new_no_crate(db); - return self.ty.visit_with(&mut Visitor { interner }).is_break(); + return self + .ty + .instantiate_identity() + .skip_norm_wip() + .visit_with(&mut Visitor { interner }) + .is_break(); fn is_phantom_data(db: &dyn HirDatabase, adt_id: AdtId) -> bool { match adt_id { @@ -5681,8 +5566,8 @@ impl<'db> Type<'db> { AdtId::EnumId(id) => id .enum_variants(self.interner.db()) .variants - .iter() - .map(|&(variant_id, _, _)| variant_id_to_fields(variant_id.into())) + .values() + .map(|&(variant_id, _)| variant_id_to_fields(variant_id.into())) .collect(), AdtId::UnionId(id) => { vec![variant_id_to_fields(id.into())] @@ -5703,89 +5588,129 @@ impl<'db> Type<'db> { } pub fn as_reference(&self) -> Option<(Type<'db>, Mutability)> { - let TyKind::Ref(_lt, ty, m) = self.ty.kind() else { return None }; + let TyKind::Ref(_lt, ty, m) = self.ty.skip_binder().kind() else { return None }; let m = Mutability::from_mutable(matches!(m, hir_ty::next_solver::Mutability::Mut)); Some((self.derived(ty), m)) } - pub fn add_reference(&self, mutability: Mutability) -> Self { - let interner = DbInterner::conjure(); + pub fn as_reference_inner(&self) -> Option<Type<'db>> { + self.as_reference().map(|(inner, _)| inner) + } + + pub fn add_reference(&self, db: &'db dyn HirDatabase, mutability: Mutability) -> Self { + let interner = DbInterner::new_no_crate(db); let ty_mutability = match mutability { Mutability::Shared => hir_ty::next_solver::Mutability::Not, Mutability::Mut => hir_ty::next_solver::Mutability::Mut, }; - self.derived(Ty::new_ref(interner, Region::error(interner), self.ty, ty_mutability)) + self.derived(Ty::new_ref( + interner, + Region::error(interner), + self.ty.skip_binder(), + ty_mutability, + )) } pub fn is_slice(&self) -> bool { - matches!(self.ty.kind(), TyKind::Slice(..)) + matches!(self.ty.skip_binder().kind(), TyKind::Slice(..)) } pub fn is_usize(&self) -> bool { - matches!(self.ty.kind(), TyKind::Uint(rustc_type_ir::UintTy::Usize)) + matches!(self.ty.skip_binder().kind(), TyKind::Uint(rustc_type_ir::UintTy::Usize)) } pub fn is_float(&self) -> bool { - matches!(self.ty.kind(), TyKind::Float(_)) + matches!(self.ty.skip_binder().kind(), TyKind::Float(_)) } pub fn is_char(&self) -> bool { - matches!(self.ty.kind(), TyKind::Char) + matches!(self.ty.skip_binder().kind(), TyKind::Char) } pub fn is_int_or_uint(&self) -> bool { - matches!(self.ty.kind(), TyKind::Int(_) | TyKind::Uint(_)) + matches!(self.ty.skip_binder().kind(), TyKind::Int(_) | TyKind::Uint(_)) } pub fn is_scalar(&self) -> bool { matches!( - self.ty.kind(), + self.ty.skip_binder().kind(), TyKind::Bool | TyKind::Char | TyKind::Int(_) | TyKind::Uint(_) | TyKind::Float(_) ) } pub fn is_tuple(&self) -> bool { - matches!(self.ty.kind(), TyKind::Tuple(..)) - } - - pub fn remove_ref(&self) -> Option<Type<'db>> { - match self.ty.kind() { - TyKind::Ref(_, ty, _) => Some(self.derived(ty)), - _ => None, - } + matches!(self.ty.skip_binder().kind(), TyKind::Tuple(..)) } pub fn as_slice(&self) -> Option<Type<'db>> { - match self.ty.kind() { + match self.ty.skip_binder().kind() { TyKind::Slice(ty) => Some(self.derived(ty)), _ => None, } } pub fn strip_references(&self) -> Self { - self.derived(self.ty.strip_references()) + self.derived(self.ty.skip_binder().strip_references()) } // FIXME: This is the same as `remove_ref()`, remove one of these methods. pub fn strip_reference(&self) -> Self { - self.derived(self.ty.strip_reference()) + self.derived(self.ty.skip_binder().strip_reference()) } pub fn is_unknown(&self) -> bool { - self.ty.is_ty_error() + self.ty.skip_binder().is_ty_error() + } + + fn krate(&self, db: &'db dyn HirDatabase) -> base_db::Crate { + match self.owner { + TypeOwnerId::GenericDefId(def) => hir_def::HasModule::krate(&def, db), + TypeOwnerId::BuiltinDeriveImplId(def) => { + hir_def::HasModule::krate(&def.loc(db).adt, db) + } + TypeOwnerId::AnonConstId(def) => hir_def::HasModule::krate(&def, db), + TypeOwnerId::NoParams(krate) => krate, + } + } + + fn param_env(&self, db: &'db dyn HirDatabase) -> ParamEnvAndCrate<'db> { + let interner = DbInterner::new_no_crate(db); + let krate = self.krate(db); + match self.owner { + TypeOwnerId::GenericDefId(def) => { + ParamEnvAndCrate { param_env: db.trait_environment(def), krate } + } + TypeOwnerId::BuiltinDeriveImplId(def) => ParamEnvAndCrate { + param_env: hir_ty::builtin_derive::param_env(interner, def), + krate, + }, + TypeOwnerId::AnonConstId(def) => ParamEnvAndCrate { + param_env: db.trait_environment(def.loc(db).owner.generic_def(db)), + krate, + }, + TypeOwnerId::NoParams(_) => { + ParamEnvAndCrate { param_env: ParamEnv::empty(interner), krate } + } + } } /// Checks that particular type `ty` implements `std::future::IntoFuture` or /// `std::future::Future` and returns the `Output` associated type. /// This function is used in `.await` syntax completion. pub fn into_future_output(&self, db: &'db dyn HirDatabase) -> Option<Type<'db>> { - let lang_items = hir_def::lang_item::lang_items(db, self.env.krate); + let env = self.param_env(db); + let lang_items = hir_def::lang_item::lang_items(db, env.krate); let (trait_, output_assoc_type) = lang_items .IntoFuture .zip(lang_items.IntoFutureOutput) .or(lang_items.Future.zip(lang_items.FutureOutput))?; - if !traits::implements_trait_unique(self.ty, db, self.env, trait_) { + if !traits::implements_trait_unique( + self.ty.instantiate_identity().skip_norm_wip(), + db, + env, + trait_, + ) { return None; } @@ -5794,32 +5719,46 @@ impl<'db> Type<'db> { /// This does **not** resolve `IntoFuture`, only `Future`. pub fn future_output(self, db: &'db dyn HirDatabase) -> Option<Type<'db>> { - let lang_items = hir_def::lang_item::lang_items(db, self.env.krate); + let krate = self.krate(db); + let lang_items = hir_def::lang_item::lang_items(db, krate); let future_output = lang_items.FutureOutput?; self.normalize_trait_assoc_type(db, &[], future_output.into()) } /// This does **not** resolve `IntoIterator`, only `Iterator`. pub fn iterator_item(self, db: &'db dyn HirDatabase) -> Option<Type<'db>> { - let lang_items = hir_def::lang_item::lang_items(db, self.env.krate); + let krate = self.krate(db); + let lang_items = hir_def::lang_item::lang_items(db, krate); let iterator_item = lang_items.IteratorItem?; self.normalize_trait_assoc_type(db, &[], iterator_item.into()) } pub fn impls_iterator(self, db: &'db dyn HirDatabase) -> bool { - let lang_items = hir_def::lang_item::lang_items(db, self.env.krate); + let env = self.param_env(db); + let lang_items = hir_def::lang_item::lang_items(db, env.krate); let Some(iterator_trait) = lang_items.Iterator else { return false; }; - traits::implements_trait_unique(self.ty, db, self.env, iterator_trait) + traits::implements_trait_unique( + self.ty.instantiate_identity().skip_norm_wip(), + db, + env, + iterator_trait, + ) } /// Resolves the projection `<Self as IntoIterator>::IntoIter` and returns the resulting type pub fn into_iterator_iter(self, db: &'db dyn HirDatabase) -> Option<Type<'db>> { - let lang_items = hir_def::lang_item::lang_items(db, self.env.krate); + let env = self.param_env(db); + let lang_items = hir_def::lang_item::lang_items(db, env.krate); let trait_ = lang_items.IntoIterator?; - if !traits::implements_trait_unique(self.ty, db, self.env, trait_) { + if !traits::implements_trait_unique( + self.ty.instantiate_identity().skip_norm_wip(), + db, + env, + trait_, + ) { return None; } @@ -5832,24 +5771,63 @@ impl<'db> Type<'db> { /// This function can be used to check if a particular type is callable, since FnOnce is a /// supertrait of Fn and FnMut, so all callable types implements at least FnOnce. pub fn impls_fnonce(&self, db: &'db dyn HirDatabase) -> bool { - let lang_items = hir_def::lang_item::lang_items(db, self.env.krate); + let env = self.param_env(db); + let lang_items = hir_def::lang_item::lang_items(db, env.krate); let fnonce_trait = match lang_items.FnOnce { Some(it) => it, None => return false, }; - traits::implements_trait_unique(self.ty, db, self.env, fnonce_trait) + traits::implements_trait_unique( + self.ty.instantiate_identity().skip_norm_wip(), + db, + env, + fnonce_trait, + ) } // FIXME: Find better API that also handles const generics pub fn impls_trait(&self, db: &'db dyn HirDatabase, trait_: Trait, args: &[Type<'db>]) -> bool { + let env = self.param_env(db); let interner = DbInterner::new_no_crate(db); - let args = generic_args_from_tys( - interner, - trait_.id.into(), - std::iter::once(self.ty).chain(args.iter().map(|ty| ty.ty)), - ); - traits::implements_trait_unique_with_args(db, self.env, trait_.id, args) + let (args, _owner) = + generic_args_from_tys(interner, trait_.id.into(), iter::once(self).chain(args)); + traits::implements_trait_unique_with_args(db, env, trait_.id, args) + } + + /// Unlike [`Type::impls_trait()`], which checks whether the type always implements the trait, + /// this check whether there are any generic args substitution for `args`` that will cause the + /// trait to be implemented. + /// + /// For example, suppose we're there's `struct Foo<T>` and we're checking `Foo<T>: Trait`. + /// `impls_trait()` will return true only if there is `impl<T> Trait for Foo<T>`, while this + /// method will also return true if there is only `impl Trait for Foo<i32>`. + /// + /// Note that you can of course instantiate `Foo<T>` with `<i32>` and then the checks will + /// be the same, but this check for *any* substitution. + /// + /// Unlike almost anything that takes more than one type, you *can* pass types from different origins + /// to this function. + pub fn has_any_impl( + &self, + db: &'db dyn HirDatabase, + trait_: Trait, + args: &[Type<'db>], + ) -> bool { + let interner = DbInterner::new_no_crate(db); + let env = ParamEnvAndCrate { param_env: ParamEnv::empty(interner), krate: self.krate(db) }; + traits::implements_trait_unique_with_infcx(db, env, trait_.id, &mut |infcx| { + let mut args = Self::instantiate_many_with_infer(iter::once(self).chain(args), infcx); + GenericArgs::for_item(infcx.interner, trait_.id.into(), |_, param, _| { + if let GenericParamId::TypeParamId(_) = param + && let Some(arg) = args.next() + { + arg.into() + } else { + infcx.var_for_def(param, hir_ty::Span::Dummy) + } + }) + }) } pub fn normalize_trait_assoc_type( @@ -5858,12 +5836,10 @@ impl<'db> Type<'db> { args: &[Type<'db>], alias: TypeAlias, ) -> Option<Type<'db>> { - let interner = DbInterner::new_with(db, self.env.krate); - let args = generic_args_from_tys( - interner, - alias.id.into(), - std::iter::once(self.ty).chain(args.iter().map(|ty| ty.ty)), - ); + let env = self.param_env(db); + let interner = DbInterner::new_with(db, env.krate); + let (args, owner) = + generic_args_from_tys(interner, alias.id.into(), iter::once(self).chain(args)); // FIXME: We don't handle GATs yet. let projection = Ty::new_alias( interner, @@ -5875,12 +5851,13 @@ impl<'db> Type<'db> { ); let infcx = interner.infer_ctxt().build(TypingMode::PostAnalysis); - let ty = structurally_normalize_ty(&infcx, projection, self.env.param_env); - if ty.is_ty_error() { None } else { Some(self.derived(ty)) } + let ty = structurally_normalize_ty(&infcx, projection, env.param_env); + if ty.is_ty_error() { None } else { Some(Type { owner, ty: EarlyBinder::bind(ty) }) } } pub fn is_copy(&self, db: &'db dyn HirDatabase) -> bool { - let lang_items = hir_def::lang_item::lang_items(db, self.env.krate); + let env = self.param_env(db); + let lang_items = hir_def::lang_item::lang_items(db, env.krate); let Some(copy_trait) = lang_items.Copy else { return false; }; @@ -5889,7 +5866,7 @@ impl<'db> Type<'db> { pub fn as_callable(&self, db: &'db dyn HirDatabase) -> Option<Callable<'db>> { let interner = DbInterner::new_no_crate(db); - let callee = match self.ty.kind() { + let callee = match self.ty.skip_binder().kind() { TyKind::Closure(id, subst) => Callee::Closure(id.0, subst), TyKind::CoroutineClosure(id, subst) => Callee::CoroutineClosure(id.0, subst), TyKind::FnPtr(..) => Callee::FnPtr, @@ -5897,7 +5874,9 @@ impl<'db> Type<'db> { // This will happen when it implements fn or fn mut, since we add an autoborrow adjustment TyKind::Ref(_, inner_ty, _) => return self.derived(inner_ty).as_callable(db), _ => { - let (fn_trait, sig) = hir_ty::callable_sig_from_fn_trait(self.ty, self.env, db)?; + let env = self.param_env(db); + let (fn_trait, sig) = + hir_ty::callable_sig_from_fn_trait(self.ty.skip_binder(), env, db)?; return Some(Callable { ty: self.clone(), sig, @@ -5907,74 +5886,77 @@ impl<'db> Type<'db> { } }; - let sig = self.ty.callable_sig(interner)?; + let sig = self.ty.skip_binder().callable_sig(interner)?; Some(Callable { ty: self.clone(), sig, callee, is_bound_method: false }) } pub fn is_closure(&self) -> bool { - matches!(self.ty.kind(), TyKind::Closure { .. }) + matches!(self.ty.skip_binder().kind(), TyKind::Closure { .. }) } pub fn as_closure(&self) -> Option<Closure<'db>> { - match self.ty.kind() { + match self.ty.skip_binder().kind() { TyKind::Closure(id, subst) => { - Some(Closure { id: AnyClosureId::ClosureId(id.0), subst }) - } - TyKind::CoroutineClosure(id, subst) => { - Some(Closure { id: AnyClosureId::CoroutineClosureId(id.0), subst }) + Some(Closure { id: AnyClosureId::ClosureId(id.0), subst, owner: self.owner }) } + TyKind::CoroutineClosure(id, subst) => Some(Closure { + id: AnyClosureId::CoroutineClosureId(id.0), + subst, + owner: self.owner, + }), _ => None, } } pub fn is_fn(&self) -> bool { - matches!(self.ty.kind(), TyKind::FnDef(..) | TyKind::FnPtr { .. }) + matches!(self.ty.skip_binder().kind(), TyKind::FnDef(..) | TyKind::FnPtr { .. }) } pub fn is_array(&self) -> bool { - matches!(self.ty.kind(), TyKind::Array(..)) + matches!(self.ty.skip_binder().kind(), TyKind::Array(..)) } - pub fn is_packed(&self, db: &'db dyn HirDatabase) -> bool { - let adt_id = match self.ty.kind() { - TyKind::Adt(adt_def, ..) => adt_def.def_id(), - _ => return false, - }; - - let adt = adt_id.into(); - match adt { - Adt::Struct(s) => s.repr(db).unwrap_or_default().pack.is_some(), + pub fn is_packed(&self, _db: &'db dyn HirDatabase) -> bool { + match self.ty.skip_binder().kind() { + TyKind::Adt(adt_def, ..) => adt_def.is_packed(), _ => false, } } pub fn is_raw_ptr(&self) -> bool { - matches!(self.ty.kind(), TyKind::RawPtr(..)) + matches!(self.ty.skip_binder().kind(), TyKind::RawPtr(..)) } pub fn is_mutable_raw_ptr(&self) -> bool { // Used outside of rust-analyzer (e.g. by `ra_ap_hir` consumers). - matches!(self.ty.kind(), TyKind::RawPtr(.., hir_ty::next_solver::Mutability::Mut)) + matches!( + self.ty.skip_binder().kind(), + TyKind::RawPtr(.., hir_ty::next_solver::Mutability::Mut) + ) } pub fn as_raw_ptr(&self) -> Option<(Type<'db>, Mutability)> { // Used outside of rust-analyzer (e.g. by `ra_ap_hir` consumers). - let TyKind::RawPtr(ty, m) = self.ty.kind() else { return None }; + let TyKind::RawPtr(ty, m) = self.ty.skip_binder().kind() else { return None }; let m = Mutability::from_mutable(matches!(m, hir_ty::next_solver::Mutability::Mut)); Some((self.derived(ty), m)) } pub fn remove_raw_ptr(&self) -> Option<Type<'db>> { - if let TyKind::RawPtr(ty, _) = self.ty.kind() { Some(self.derived(ty)) } else { None } + if let TyKind::RawPtr(ty, _) = self.ty.skip_binder().kind() { + Some(self.derived(ty)) + } else { + None + } } pub fn contains_unknown(&self) -> bool { - self.ty.references_non_lt_error() + self.ty.skip_binder().references_non_lt_error() } pub fn fields(&self, db: &'db dyn HirDatabase) -> Vec<(Field, Self)> { let interner = DbInterner::new_no_crate(db); - let (variant_id, substs) = match self.ty.kind() { + let (variant_id, substs) = match self.ty.skip_binder().kind() { TyKind::Adt(adt_def, substs) => { let id = match adt_def.def_id() { AdtId::StructId(id) => id.into(), @@ -5997,7 +5979,7 @@ impl<'db> Type<'db> { } pub fn tuple_fields(&self, _db: &'db dyn HirDatabase) -> Vec<Self> { - if let TyKind::Tuple(substs) = self.ty.kind() { + if let TyKind::Tuple(substs) = self.ty.skip_binder().kind() { substs.iter().map(|ty| self.derived(ty)).collect() } else { Vec::new() @@ -6005,17 +5987,18 @@ impl<'db> Type<'db> { } pub fn as_array(&self, db: &'db dyn HirDatabase) -> Option<(Self, usize)> { - if let TyKind::Array(ty, len) = self.ty.kind() { + if let TyKind::Array(ty, len) = self.ty.skip_binder().kind() { try_const_usize(db, len).map(|it| (self.derived(ty), it as usize)) } else { None } } - pub fn fingerprint_for_trait_impl(&self) -> Option<SimplifiedType> { + // FIXME: We should probably remove this. + pub fn fingerprint_for_trait_impl(&self, db: &'db dyn HirDatabase) -> Option<SimplifiedType> { fast_reject::simplify_type( - DbInterner::conjure(), - self.ty, + DbInterner::new_no_crate(db), + self.ty.skip_binder(), fast_reject::TreatParams::AsRigid, ) } @@ -6031,9 +6014,10 @@ impl<'db> Type<'db> { fn autoderef_(&self, db: &'db dyn HirDatabase) -> impl Iterator<Item = Ty<'db>> { let interner = DbInterner::new_no_crate(db); + let env = self.param_env(db); // There should be no inference vars in types passed here - let canonical = hir_ty::replace_errors_with_variables(interner, &self.ty); - autoderef(db, self.env, canonical) + let canonical = hir_ty::replace_errors_with_variables(interner, &self.ty.skip_binder()); + autoderef(db, env, canonical) } // This would be nicer if it just returned an iterator, but that runs into @@ -6065,17 +6049,20 @@ impl<'db> Type<'db> { } } }; + let krate = self.krate(db); let interner = DbInterner::new_no_crate(db); - let Some(simplified_type) = - fast_reject::simplify_type(interner, self.ty, fast_reject::TreatParams::AsRigid) - else { + let Some(simplified_type) = fast_reject::simplify_type( + interner, + self.ty.skip_binder(), + fast_reject::TreatParams::AsRigid, + ) else { return; }; method_resolution::with_incoherent_inherent_impls( db, - self.env.krate, + krate, &simplified_type, &mut handle_impls, ); @@ -6111,12 +6098,12 @@ impl<'db> Type<'db> { /// - "U" /// ``` pub fn type_arguments(&self) -> impl Iterator<Item = Type<'db>> + '_ { - match self.ty.strip_references().kind() { + match self.ty.skip_binder().strip_references().kind() { TyKind::Adt(_, substs) => Either::Left(substs.types().map(move |ty| self.derived(ty))), TyKind::Tuple(substs) => { Either::Right(Either::Left(substs.iter().map(move |ty| self.derived(ty)))) } - _ => Either::Right(Either::Right(std::iter::empty())), + _ => Either::Right(Either::Right(iter::empty())), } } @@ -6144,6 +6131,7 @@ impl<'db> Type<'db> { display_target: DisplayTarget, ) -> impl Iterator<Item = SmolStr> + 'a { self.ty + .skip_binder() .strip_references() .as_adt() .into_iter() @@ -6234,7 +6222,7 @@ impl<'db> Type<'db> { }; let infcx = interner.infer_ctxt().build(typing_mode); let features = resolver.top_level_def_map().features(); - let environment = param_env_from_resolver(db, resolver); + let environment = self.param_env(db); let ctx = MethodResolutionContext { infcx: &infcx, resolver, @@ -6269,7 +6257,8 @@ impl<'db> Type<'db> { self.with_method_resolution(db, scope.resolver(), traits_in_scope, |ctx| { // There should be no inference vars in types passed here - let canonical = hir_ty::replace_errors_with_variables(ctx.infcx.interner, &self.ty); + let canonical = + hir_ty::replace_errors_with_variables(ctx.infcx.interner, &self.ty.skip_binder()); let (self_ty, _) = ctx.infcx.instantiate_canonical(hir_ty::Span::Dummy, &canonical); match name { @@ -6377,7 +6366,8 @@ impl<'db> Type<'db> { self.with_method_resolution(db, scope.resolver(), traits_in_scope, |ctx| { // There should be no inference vars in types passed here - let canonical = hir_ty::replace_errors_with_variables(ctx.infcx.interner, &self.ty); + let canonical = + hir_ty::replace_errors_with_variables(ctx.infcx.interner, &self.ty.skip_binder()); let (self_ty, _) = ctx.infcx.instantiate_canonical(hir_ty::Span::Dummy, &canonical); match name { @@ -6426,23 +6416,23 @@ impl<'db> Type<'db> { } pub fn as_adt(&self) -> Option<Adt> { - let (adt, _subst) = self.ty.as_adt()?; + let (adt, _subst) = self.ty.skip_binder().as_adt()?; Some(adt.into()) } /// Holes in the args can come from lifetime/const params. pub fn as_adt_with_args(&self) -> Option<(Adt, Vec<Option<Type<'db>>>)> { - let (adt, args) = self.ty.as_adt()?; + let (adt, args) = self.ty.skip_binder().as_adt()?; let args = args.iter().map(|arg| Some(self.derived(arg.ty()?))).collect(); Some((adt.into(), args)) } pub fn as_builtin(&self) -> Option<BuiltinType> { - self.ty.as_builtin().map(|inner| BuiltinType { inner }) + self.ty.skip_binder().as_builtin().map(|inner| BuiltinType { inner }) } pub fn as_dyn_trait(&self) -> Option<Trait> { - self.ty.dyn_trait().map(Into::into) + self.ty.skip_binder().dyn_trait().map(Into::into) } /// If a type can be represented as `dyn Trait`, returns all traits accessible via this type, @@ -6461,11 +6451,11 @@ impl<'db> Type<'db> { pub fn env_traits(&self, db: &'db dyn HirDatabase) -> impl Iterator<Item = Trait> { let _p = tracing::info_span!("env_traits").entered(); + let env = self.param_env(db); self.autoderef_(db) .filter(|ty| matches!(ty.kind(), TyKind::Param(_))) - .flat_map(|ty| { - self.env - .param_env + .flat_map(move |ty| { + env.param_env .clauses() .iter() .filter_map(move |pred| match pred.kind().skip_binder() { @@ -6479,7 +6469,7 @@ impl<'db> Type<'db> { } pub fn as_impl_traits(&self, db: &'db dyn HirDatabase) -> Option<impl Iterator<Item = Trait>> { - self.ty.impl_trait_bounds(db).map(|it| { + self.ty.skip_binder().impl_trait_bounds(db).map(|it| { it.into_iter().filter_map(|pred| match pred.kind().skip_binder() { ClauseKind::Trait(trait_ref) => Some(Trait::from(trait_ref.def_id().0)), _ => None, @@ -6489,7 +6479,7 @@ impl<'db> Type<'db> { pub fn as_associated_type_parent_trait(&self, db: &'db dyn HirDatabase) -> Option<Trait> { let TyKind::Alias(AliasTy { kind: AliasTyKind::Projection { def_id }, .. }) = - self.ty.kind() + self.ty.skip_binder().kind() else { return None; }; @@ -6500,7 +6490,7 @@ impl<'db> Type<'db> { } fn derived(&self, ty: Ty<'db>) -> Self { - Type { env: self.env, ty } + Type { owner: self.owner, ty: EarlyBinder::bind(ty) } } /// Visits every type, including generic arguments, in this type. `callback` is called with type @@ -6508,7 +6498,7 @@ impl<'db> Type<'db> { pub fn walk(&self, db: &'db dyn HirDatabase, callback: impl FnMut(Type<'db>)) { struct Visitor<'db, F> { db: &'db dyn HirDatabase, - env: ParamEnvAndCrate<'db>, + owner: TypeOwnerId, callback: F, visited: FxHashSet<Ty<'db>>, } @@ -6523,7 +6513,7 @@ impl<'db> Type<'db> { return; } - (self.callback)(Type { env: self.env, ty }); + (self.callback)(Type { owner: self.owner, ty: EarlyBinder::bind(ty) }); if let Some(bounds) = ty.impl_trait_bounds(self.db) { bounds.visit_with(self); @@ -6533,17 +6523,23 @@ impl<'db> Type<'db> { } } - let mut visitor = Visitor { db, env: self.env, callback, visited: FxHashSet::default() }; - self.ty.visit_with(&mut visitor); + let mut visitor = + Visitor { db, owner: self.owner, callback, visited: FxHashSet::default() }; + self.ty.skip_binder().visit_with(&mut visitor); } /// Check if type unifies with another type. /// /// Note that we consider placeholder types to unify with everything. /// For example `Option<T>` and `Option<U>` unify although there is unresolved goal `T = U`. pub fn could_unify_with(&self, db: &'db dyn HirDatabase, other: &Type<'db>) -> bool { + self.owner.must_unify(other.owner); + let env = self.param_env(db); let interner = DbInterner::new_no_crate(db); - let tys = hir_ty::replace_errors_with_variables(interner, &(self.ty, other.ty)); - hir_ty::could_unify(db, self.env, &tys) + let tys = hir_ty::replace_errors_with_variables( + interner, + &(self.ty.skip_binder(), other.ty.skip_binder()), + ); + hir_ty::could_unify(db, env, &tys) } /// Check if type unifies with another type eagerly making sure there are no unresolved goals. @@ -6551,19 +6547,29 @@ impl<'db> Type<'db> { /// This means that placeholder types are not considered to unify if there are any bounds set on /// them. For example `Option<T>` and `Option<U>` do not unify as we cannot show that `T = U` pub fn could_unify_with_deeply(&self, db: &'db dyn HirDatabase, other: &Type<'db>) -> bool { + self.owner.must_unify(other.owner); + let env = self.param_env(db); let interner = DbInterner::new_no_crate(db); - let tys = hir_ty::replace_errors_with_variables(interner, &(self.ty, other.ty)); - hir_ty::could_unify_deeply(db, self.env, &tys) + let tys = hir_ty::replace_errors_with_variables( + interner, + &(self.ty.skip_binder(), other.ty.skip_binder()), + ); + hir_ty::could_unify_deeply(db, env, &tys) } pub fn could_coerce_to(&self, db: &'db dyn HirDatabase, to: &Type<'db>) -> bool { + self.owner.must_unify(to.owner); + let env = self.param_env(db); let interner = DbInterner::new_no_crate(db); - let tys = hir_ty::replace_errors_with_variables(interner, &(self.ty, to.ty)); - hir_ty::could_coerce(db, self.env, &tys) + let tys = hir_ty::replace_errors_with_variables( + interner, + &(self.ty.skip_binder(), to.ty.skip_binder()), + ); + hir_ty::could_coerce(db, env, &tys) } pub fn as_type_param(&self, _db: &'db dyn HirDatabase) -> Option<TypeParam> { - match self.ty.kind() { + match self.ty.skip_binder().kind() { TyKind::Param(param) => Some(TypeParam { id: param.id }), _ => None, } @@ -6571,60 +6577,23 @@ impl<'db> Type<'db> { /// Returns unique `GenericParam`s contained in this type. pub fn generic_params(&self, db: &'db dyn HirDatabase) -> FxHashSet<GenericParam> { - hir_ty::collect_params(&self.ty) + hir_ty::collect_params(&self.ty.skip_binder()) .into_iter() .map(|id| TypeOrConstParam { id }.split(db).either_into()) .collect() } pub fn layout(&self, db: &'db dyn HirDatabase) -> Result<Layout<'db>, LayoutError> { - db.layout_of_ty(self.ty.store(), self.env.store()) - .map(|layout| Layout(layout, db.target_data_layout(self.env.krate).unwrap())) + let env = self.param_env(db); + db.layout_of_ty(self.ty.skip_binder().store(), env.store()) + .map(|layout| Layout(layout, db.target_data_layout(env.krate).unwrap())) } pub fn drop_glue(&self, db: &'db dyn HirDatabase) -> DropGlue { - let interner = DbInterner::new_with(db, self.env.krate); + let env = self.param_env(db); + let interner = DbInterner::new_with(db, env.krate); let infcx = interner.infer_ctxt().build(TypingMode::PostAnalysis); - hir_ty::drop::has_drop_glue(&infcx, self.ty, self.env.param_env) - } -} - -#[derive(Clone, PartialEq, Eq, Debug, Hash)] -pub struct TypeNs<'db> { - env: ParamEnvAndCrate<'db>, - ty: Ty<'db>, -} - -impl<'db> TypeNs<'db> { - fn new(db: &'db dyn HirDatabase, lexical_env: impl HasResolver, ty: Ty<'db>) -> Self { - let resolver = lexical_env.resolver(db); - let environment = param_env_from_resolver(db, &resolver); - TypeNs { env: environment, ty } - } - - pub fn to_type(&self, _db: &'db dyn HirDatabase) -> Type<'db> { - Type { env: self.env, ty: self.ty } - } - - // FIXME: Find better API that also handles const generics - pub fn impls_trait(&self, infcx: InferCtxt<'db>, trait_: Trait, args: &[TypeNs<'db>]) -> bool { - let args = GenericArgs::new_from_iter( - infcx.interner, - [self.ty].into_iter().chain(args.iter().map(|t| t.ty)).map(GenericArg::from), - ); - let trait_ref = - hir_ty::next_solver::TraitRef::new_from_args(infcx.interner, trait_.id.into(), args); - let obligation = hir_ty::next_solver::infer::traits::Obligation::new( - infcx.interner, - hir_ty::next_solver::infer::traits::ObligationCause::dummy(), - self.env.param_env, - trait_ref, - ); - infcx.predicate_must_hold_modulo_regions(&obligation) - } - - pub fn is_bool(&self) -> bool { - matches!(self.ty.kind(), rustc_type_ir::TyKind::Bool) + hir_ty::drop::has_drop_glue(&infcx, self.ty.skip_binder(), env.param_env) } } @@ -6689,12 +6658,16 @@ impl<'db> Callable<'db> { Callee::Def(CallableDefId::EnumVariantId(it)) => { CallableKind::TupleEnumVariant(it.into()) } - Callee::Closure(id, subst) => { - CallableKind::Closure(Closure { id: AnyClosureId::ClosureId(id), subst }) - } - Callee::CoroutineClosure(id, subst) => { - CallableKind::Closure(Closure { id: AnyClosureId::CoroutineClosureId(id), subst }) - } + Callee::Closure(id, subst) => CallableKind::Closure(Closure { + id: AnyClosureId::ClosureId(id), + subst, + owner: self.ty.owner, + }), + Callee::CoroutineClosure(id, subst) => CallableKind::Closure(Closure { + id: AnyClosureId::CoroutineClosureId(id), + subst, + owner: self.ty.owner, + }), Callee::FnPtr => CallableKind::FnPtr, Callee::FnImpl(fn_) => CallableKind::FnImpl(fn_.into()), } @@ -6835,7 +6808,7 @@ impl<'db> Layout<'db> { reverse_index .into_iter() .flatten() - .chain(std::iter::once((0, self.0.size.bytes()))) + .chain(iter::once((0, self.0.size.bytes()))) .tuple_windows() .filter_map(|((i, start), (_, end))| { let size = field_size(i)?; @@ -6993,7 +6966,7 @@ pub enum PredicatePolarity { #[derive(Debug, Clone, PartialEq, Eq)] pub struct TraitPredicate<'db> { inner: hir_ty::next_solver::TraitPredicate<'db>, - env: ParamEnvAndCrate<'db>, + owner: TypeOwnerId, } impl<'db> TraitPredicate<'db> { @@ -7005,7 +6978,7 @@ impl<'db> TraitPredicate<'db> { } pub fn trait_ref(&self) -> TraitRef<'db> { - TraitRef { env: self.env, trait_ref: self.inner.trait_ref } + TraitRef { owner: self.owner, trait_ref: self.inner.trait_ref } } } @@ -7075,8 +7048,8 @@ impl HasCrate for TypeAlias { } impl HasCrate for Type<'_> { - fn krate(&self, _db: &dyn HirDatabase) -> Crate { - self.env.krate.into() + fn krate(&self, db: &dyn HirDatabase) -> Crate { + self.krate(db).into() } } @@ -7395,21 +7368,33 @@ fn as_name_opt(name: Option<impl AsName>) -> Name { name.map_or_else(Name::missing, |name| name.as_name()) } +#[track_caller] fn generic_args_from_tys<'db>( interner: DbInterner<'db>, def_id: SolverDefId, - args: impl IntoIterator<Item = Ty<'db>>, -) -> GenericArgs<'db> { + args: impl IntoIterator<Item: Borrow<Type<'db>>>, +) -> (GenericArgs<'db>, TypeOwnerId) { + let mut owner = None::<TypeOwnerId>; let mut args = args.into_iter(); - GenericArgs::for_item(interner, def_id, |_, id, _| { + let args = GenericArgs::for_item(interner, def_id, |_, id, _| { if matches!(id, GenericParamId::TypeParamId(_)) && let Some(arg) = args.next() { - arg.into() + let arg = arg.borrow(); + + match &mut owner { + Some(owner) => *owner = owner.must_unify(arg.owner), + None => owner = Some(arg.owner), + } + + arg.ty.skip_binder().into() } else { next_solver::GenericArg::error_from_id(interner, id) } - }) + }); + let owner = + owner.unwrap_or_else(|| TypeOwnerId::NoParams(Type::builtin_type_crate(interner.db()))); + (args, owner) } fn has_non_default_type_params(db: &dyn HirDatabase, generic_def: GenericDefId) -> bool { @@ -7425,46 +7410,58 @@ fn has_non_default_type_params(db: &dyn HirDatabase, generic_def: GenericDefId) }) } -fn param_env_from_resolver<'db>( - db: &'db dyn HirDatabase, - resolver: &Resolver<'_>, -) -> ParamEnvAndCrate<'db> { - ParamEnvAndCrate { - param_env: resolver - .generic_def() - .map_or_else(ParamEnv::empty, |generic_def| db.trait_environment(generic_def.into())), - krate: resolver.krate(), - } -} - fn param_env_from_has_crate<'db>( db: &'db dyn HirDatabase, id: impl hir_def::HasModule + Into<GenericDefId> + Copy, ) -> ParamEnvAndCrate<'db> { - ParamEnvAndCrate { param_env: db.trait_environment(id.into().into()), krate: id.krate(db) } -} - -fn body_param_env_from_has_crate<'db>( - db: &'db dyn HirDatabase, - id: impl hir_def::HasModule + Into<ExpressionStoreOwnerId> + Copy, -) -> ParamEnvAndCrate<'db> { ParamEnvAndCrate { param_env: db.trait_environment(id.into()), krate: id.krate(db) } } -fn empty_param_env<'db>(krate: base_db::Crate) -> ParamEnvAndCrate<'db> { - ParamEnvAndCrate { param_env: ParamEnv::empty(), krate } -} - // FIXME: We probably don't want to expose this. pub trait MacroCallIdExt { - fn loc(self, db: &dyn HirDatabase) -> hir_expand::MacroCallLoc; + fn loc(self, db: &dyn HirDatabase) -> &hir_expand::MacroCallLoc; } impl MacroCallIdExt for span::MacroCallId { #[inline] - fn loc(self, db: &dyn HirDatabase) -> hir_expand::MacroCallLoc { + fn loc(self, db: &dyn HirDatabase) -> &hir_expand::MacroCallLoc { hir_expand::MacroCallId::from(self).loc(db) } } -pub use hir_ty::next_solver; -pub use hir_ty::setup_tracing; +// Like https://github.com/rust-lang/rust/blob/7c3c88f42ad444f4688b865591d84660be4ece2f/compiler/rustc_middle/src/ty/util.rs#L254-L310 +pub fn struct_tail_raw<'db>( + db: &'db dyn HirDatabase, + interner: DbInterner<'db>, + mut ty: Ty<'db>, + mut normalize: impl FnMut(Ty<'db>) -> Ty<'db>, +) -> Ty<'db> { + let recursion_limit = 16; + for iteration in 0.. { + if iteration >= recursion_limit { + return Ty::new_error(interner, ErrorGuaranteed); + } + match ty.kind() { + TyKind::Adt(def, args) => { + let AdtId::StructId(def_id) = def.def_id() else { break }; + let last_field = db.field_types(def_id.into()).iter().next_back(); + match last_field { + Some((_, field)) => { + ty = normalize(field.get().instantiate(interner, args).skip_norm_wip()) + } + None => break, + } + } + TyKind::Tuple(tys) if let Some((&last_ty, _)) = tys.split_last() => { + ty = last_ty; + } + TyKind::Tuple(_) => break, + TyKind::Pat(inner, _) => { + ty = inner; + } + _ => { + break; + } + } + } + ty +} diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs index a1bbe47188..f633bb063f 100644 --- a/crates/hir/src/semantics.rs +++ b/crates/hir/src/semantics.rs @@ -1088,7 +1088,12 @@ impl<'db> SemanticsImpl<'db> { /// That is, we strictly check if it lies inside the input of a macro call. pub fn is_inside_macro_call(&self, token @ InFile { value, .. }: InFile<&SyntaxToken>) -> bool { value.parent_ancestors().any(|ancestor| { - if ast::MacroCall::can_cast(ancestor.kind()) { + if let Some(macro_call) = ast::MacroCall::cast(ancestor.clone()) + // If this is the *path* of a macro, it's not inside the call. + && macro_call.path().is_none_or(|path| { + !path.syntax().text_range().contains_range(value.text_range()) + }) + { return true; } @@ -1323,7 +1328,7 @@ impl<'db> SemanticsImpl<'db> { .map(|(call_id, item)| { let item_range = item.syntax().text_range(); let loc = call_id.loc(db); - let text_range = match loc.kind { + let text_range = match &loc.kind { hir_expand::MacroCallKind::Attr { censored_attr_ids: attr_ids, .. @@ -1740,11 +1745,7 @@ impl<'db> SemanticsImpl<'db> { analyzer.expr_adjustments(expr).map(|it| { it.iter() .map(|adjust| { - let target = Type::new_with_resolver( - self.db, - &analyzer.resolver, - adjust.target.as_ref(), - ); + let target = analyzer.ty(adjust.target.as_ref()); let kind = match adjust.kind { hir_ty::Adjust::NeverToAny => Adjust::NeverToAny, hir_ty::Adjust::Deref(Some(hir_ty::OverloadedDeref(m))) => { @@ -1771,6 +1772,10 @@ impl<'db> SemanticsImpl<'db> { }) } + pub fn expr_is_diverging(&self, expr: &ast::Expr) -> bool { + (|| self.analyze(expr.syntax())?.expr_is_diverging(self.db, expr))().unwrap_or(false) + } + pub fn type_of_expr(&self, expr: &ast::Expr) -> Option<TypeInfo<'db>> { self.analyze(expr.syntax())? .type_of_expr(self.db, expr) @@ -1835,10 +1840,10 @@ impl<'db> SemanticsImpl<'db> { let substs = hir_ty::next_solver::GenericArgs::for_item(interner, trait_.id.into(), |_, id, _| { assert!(matches!(id, hir_def::GenericParamId::TypeParamId(_)), "expected a type"); - subst.next().expect("too few subst").ty.into() + subst.next().expect("too few subst").ty.skip_binder().into() }); assert!(subst.next().is_none(), "too many subst"); - Some(match self.db.lookup_impl_method(env.env, func, substs).0 { + Some(match self.db.lookup_impl_method(env.param_env(self.db), func, substs).0 { Either::Left(it) => it.into(), Either::Right((impl_, method)) => { Function { id: AnyFunctionId::BuiltinDeriveImplMethod { method, impl_ } } @@ -1946,6 +1951,15 @@ impl<'db> SemanticsImpl<'db> { self.analyze(field.syntax())?.resolve_record_pat_field(self.db, field) } + // FIXME: Remove this from https://github.com/rust-lang/rust-analyzer/pull/22449#discussion_r3299763452 + pub fn resolve_tuple_struct_pat_fields( + &self, + tuple_struct_pat: &ast::TupleStructPat, + ) -> Option<Vec<(Field, Type<'db>)>> { + self.analyze(tuple_struct_pat.syntax())? + .resolve_tuple_struct_pat_fields(self.db, tuple_struct_pat) + } + // FIXME: Replace this with `resolve_macro_call2` pub fn resolve_macro_call(&self, macro_call: &ast::MacroCall) -> Option<Macro> { let macro_call = self.find_file(macro_call.syntax()).with_value(macro_call); @@ -2436,7 +2450,7 @@ impl<'db> SemanticsImpl<'db> { AnyImplId::ImplId(id) => id, AnyImplId::BuiltinDeriveImplId(id) => return Some(id.loc(self.db).adt.into()), }; - let source = hir_def::src::HasSource::ast_ptr(&id.loc(self.db), self.db); + let source = hir_def::src::HasSource::ast_ptr(id.loc(self.db), self.db); let mut file_id = source.file_id; let adt_ast_id = loop { let macro_call = file_id.macro_file()?; diff --git a/crates/hir/src/semantics/child_by_source.rs b/crates/hir/src/semantics/child_by_source.rs index babeb35913..97c5a451ab 100644 --- a/crates/hir/src/semantics/child_by_source.rs +++ b/crates/hir/src/semantics/child_by_source.rs @@ -202,7 +202,7 @@ impl ChildBySource for EnumId { let ast_id_map = db.ast_id_map(loc.id.file_id); - self.enum_variants(db).variants.iter().for_each(|&(variant, _, _)| { + self.enum_variants(db).variants.values().for_each(|&(variant, _)| { res[keys::ENUM_VARIANT].insert(ast_id_map.get(variant.lookup(db).id.value), variant); }); let (_, source_map) = EnumSignature::with_source_map(db, *self); diff --git a/crates/hir/src/source_analyzer.rs b/crates/hir/src/source_analyzer.rs index 06182620c8..1f9520d780 100644 --- a/crates/hir/src/source_analyzer.rs +++ b/crates/hir/src/source_analyzer.rs @@ -41,8 +41,8 @@ use hir_ty::{ lang_items::lang_items_for_bin_op, method_resolution::{self, CandidateId}, next_solver::{ - AliasTy, DbInterner, DefaultAny, ErrorGuaranteed, GenericArgs, ParamEnv, Region, Ty, - TyKind, TypingMode, infer::DbInternerInferExt, + AliasTy, DbInterner, DefaultAny, EarlyBinder, ErrorGuaranteed, GenericArgs, ParamEnv, + Region, Ty, TyKind, TypingMode, infer::DbInternerInferExt, }, traits::structurally_normalize_ty, }; @@ -63,7 +63,7 @@ use syntax::{ use crate::{ Adt, AnyFunctionId, AssocItem, BindingMode, BuiltinAttr, BuiltinType, Callable, Const, DeriveHelper, EnumVariant, Field, Function, GenericSubstitution, Local, Macro, ModuleDef, - SemanticsImpl, Static, Struct, ToolModule, Trait, TupleField, Type, TypeAlias, + SemanticsImpl, Static, Struct, ToolModule, Trait, TupleField, Type, TypeAlias, TypeOwnerId, db::HirDatabase, semantics::{PathResolution, PathResolutionPerNs}, }; @@ -75,6 +75,7 @@ pub(crate) struct SourceAnalyzer<'db> { pub(crate) file_id: HirFileId, pub(crate) resolver: Resolver<'db>, pub(crate) body_or_sig: Option<BodyOrSig<'db>>, + pub(crate) type_owner: TypeOwnerId, pub(crate) infer_body: Option<InferBodyId>, } @@ -148,6 +149,7 @@ impl<'db> SourceAnalyzer<'db> { resolver, body_or_sig: Some(BodyOrSig::Body { def, body, source_map, infer }), file_id, + type_owner: def.generic_def(db).into(), infer_body: Some(def.into()), } } @@ -212,6 +214,7 @@ impl<'db> SourceAnalyzer<'db> { resolver, body_or_sig: Some(BodyOrSig::Sig { def, store, source_map, generics, infer }), file_id, + type_owner: def.into(), infer_body, } } @@ -261,6 +264,7 @@ impl<'db> SourceAnalyzer<'db> { infer, }), file_id, + type_owner: GenericDefId::from(def.adt_id(db)).into(), infer_body, } } @@ -269,7 +273,16 @@ impl<'db> SourceAnalyzer<'db> { resolver: Resolver<'db>, node: InFile<&SyntaxNode>, ) -> SourceAnalyzer<'db> { - SourceAnalyzer { resolver, body_or_sig: None, file_id: node.file_id, infer_body: None } + SourceAnalyzer { + type_owner: resolver + .generic_def() + .map(Into::into) + .unwrap_or_else(|| TypeOwnerId::NoParams(resolver.krate())), + resolver, + body_or_sig: None, + file_id: node.file_id, + infer_body: None, + } } fn owner(&self) -> Option<ExpressionStoreOwnerId> { @@ -288,6 +301,10 @@ impl<'db> SourceAnalyzer<'db> { }) } + pub(crate) fn ty(&self, ty: Ty<'db>) -> Type<'db> { + Type { owner: self.type_owner, ty: EarlyBinder::bind(ty) } + } + pub(crate) fn def( &self, ) -> Option<( @@ -330,13 +347,21 @@ impl<'db> SourceAnalyzer<'db> { } fn trait_environment(&self, db: &'db dyn HirDatabase) -> ParamEnvAndCrate<'db> { - self.param_and(self.body_or_sig.as_ref().map_or_else(ParamEnv::empty, |body_or_sig| { - match *body_or_sig { - BodyOrSig::Body { def, .. } => db.trait_environment(def.into()), - BodyOrSig::VariantFields { def, .. } => db.trait_environment(def.into()), - BodyOrSig::Sig { def, .. } => db.trait_environment(def.into()), - } - })) + self.param_and(self.body_or_sig.as_ref().map_or_else( + || ParamEnv::empty(DbInterner::new_no_crate(db)), + |body_or_sig| { + let def = match *body_or_sig { + BodyOrSig::Body { def, .. } => def.generic_def(db), + BodyOrSig::VariantFields { def, .. } => match def { + VariantId::EnumVariantId(def) => def.loc(db).parent.into(), + VariantId::StructId(def) => def.into(), + VariantId::UnionId(def) => def.into(), + }, + BodyOrSig::Sig { def, .. } => def, + }; + db.trait_environment(def) + }, + )) } pub(crate) fn expr_id(&self, expr: ast::Expr) -> Option<ExprOrPatId> { @@ -418,12 +443,41 @@ impl<'db> SourceAnalyzer<'db> { } } - Some(Type::new_with_resolver(db, &self.resolver, ty)) + Some(self.ty(ty)) + } + + pub(crate) fn expr_is_diverging( + &self, + _db: &'db dyn HirDatabase, + expr: &ast::Expr, + ) -> Option<bool> { + let expr_id = self.expr_id(expr.clone())?; + let store = self.store()?; + let infer = self.infer()?; + Some(self.expr_id_is_diverging(store, infer, expr_id)) + } + + fn expr_id_is_diverging( + &self, + store: &ExpressionStore, + infer: &InferenceResult, + expr_id: ExprOrPatId, + ) -> bool { + // FIXME: This is an approximation, perhaps we need to store a set of diverging exprs in inference? + if infer.type_of_expr_or_pat(expr_id).is_some_and(|ty| ty.is_never()) { + true + } else if let ExprOrPatId::ExprId(expr_id) = expr_id + && let Expr::Block { tail: Some(tail), .. } = store[expr_id] + { + self.expr_id_is_diverging(store, infer, tail.into()) + } else { + false + } } pub(crate) fn type_of_expr( &self, - db: &'db dyn HirDatabase, + _db: &'db dyn HirDatabase, expr: &ast::Expr, ) -> Option<(Type<'db>, Option<Type<'db>>)> { let expr_id = self.expr_id(expr.clone())?; @@ -433,13 +487,13 @@ impl<'db> SourceAnalyzer<'db> { .and_then(|expr_id| infer.expr_adjustment(expr_id)) .and_then(|adjusts| adjusts.last().map(|adjust| adjust.target.as_ref())); let ty = infer.expr_or_pat_ty(expr_id); - let mk_ty = |ty: Ty<'db>| Type::new_with_resolver(db, &self.resolver, ty); + let mk_ty = |ty: Ty<'db>| self.ty(ty); Some((mk_ty(ty), coerced.map(mk_ty))) } pub(crate) fn type_of_pat( &self, - db: &'db dyn HirDatabase, + _db: &'db dyn HirDatabase, pat: &ast::Pat, ) -> Option<(Type<'db>, Option<Type<'db>>)> { let expr_or_pat_id = self.pat_id(pat)?; @@ -456,25 +510,25 @@ impl<'db> SourceAnalyzer<'db> { }; let ty = infer.expr_or_pat_ty(expr_or_pat_id); - let mk_ty = |ty: Ty<'db>| Type::new_with_resolver(db, &self.resolver, ty); + let mk_ty = |ty: Ty<'db>| self.ty(ty); Some((mk_ty(ty), coerced.map(mk_ty))) } pub(crate) fn type_of_binding_in_pat( &self, - db: &'db dyn HirDatabase, + _db: &'db dyn HirDatabase, pat: &ast::IdentPat, ) -> Option<Type<'db>> { let binding_id = self.binding_id_of_pat(pat)?; let infer = self.infer()?; let ty = infer.binding_ty(binding_id); - let mk_ty = |ty: Ty<'db>| Type::new_with_resolver(db, &self.resolver, ty); + let mk_ty = |ty: Ty<'db>| self.ty(ty); Some(mk_ty(ty)) } pub(crate) fn type_of_self( &self, - db: &'db dyn HirDatabase, + _db: &'db dyn HirDatabase, _param: &ast::SelfParam, ) -> Option<Type<'db>> { let binding = match self.body_or_sig.as_ref()? { @@ -482,7 +536,7 @@ impl<'db> SourceAnalyzer<'db> { BodyOrSig::Body { body, .. } => body.self_param()?, }; let ty = self.infer()?.binding_ty(binding); - Some(Type::new_with_resolver(db, &self.resolver, ty)) + Some(self.ty(ty)) } pub(crate) fn binding_mode_of_pat( @@ -504,7 +558,7 @@ impl<'db> SourceAnalyzer<'db> { } pub(crate) fn pattern_adjustments( &self, - db: &'db dyn HirDatabase, + _db: &'db dyn HirDatabase, pat: &ast::Pat, ) -> Option<SmallVec<[Type<'db>; 1]>> { let pat_id = self.pat_id(pat)?; @@ -513,7 +567,7 @@ impl<'db> SourceAnalyzer<'db> { infer .pat_adjustment(pat_id.as_pat()?)? .iter() - .map(|adjust| Type::new_with_resolver(db, &self.resolver, adjust.source.as_ref())) + .map(|adjust| self.ty(adjust.source.as_ref())) .collect(), ) } @@ -527,7 +581,7 @@ impl<'db> SourceAnalyzer<'db> { let (func, args) = self.infer()?.method_resolution(expr_id)?; let interner = DbInterner::new_no_crate(db); let ty = db.value_ty(func.into())?.instantiate(interner, args).skip_norm_wip(); - let ty = Type::new_with_resolver(db, &self.resolver, ty); + let ty = self.ty(ty); let mut res = ty.as_callable(db)?; res.is_bound_method = true; Some(res) @@ -557,7 +611,7 @@ impl<'db> SourceAnalyzer<'db> { self.resolve_impl_method_or_trait_def_with_subst(db, f_in_trait, substs); Some(( Either::Left(fn_), - GenericSubstitution::new_from_fn(fn_, subst, self.trait_environment(db)), + GenericSubstitution::new_from_fn(fn_, subst, self.type_owner), )) } None => { @@ -592,12 +646,12 @@ impl<'db> SourceAnalyzer<'db> { &self, field_expr: ExprId, infer: &InferenceResult, - db: &'db dyn HirDatabase, + _db: &'db dyn HirDatabase, ) -> Option<GenericSubstitution<'db>> { let body = self.store()?; if let Expr::Field { expr: object_expr, name: _ } = body[field_expr] { let (adt, subst) = infer.type_of_expr_with_adjust(object_expr)?.as_adt()?; - return Some(GenericSubstitution::new(adt.into(), subst, self.trait_environment(db))); + return Some(GenericSubstitution::new(adt.into(), subst, self.type_owner)); } None } @@ -628,10 +682,7 @@ impl<'db> SourceAnalyzer<'db> { }, None => inference_result.method_resolution(expr_id).map(|(f, substs)| { let (f, subst) = self.resolve_impl_method_or_trait_def_with_subst(db, f, substs); - ( - Either::Right(f), - GenericSubstitution::new_from_fn(f, subst, self.trait_environment(db)), - ) + (Either::Right(f), GenericSubstitution::new_from_fn(f, subst, self.type_owner)) }), } } @@ -721,7 +772,7 @@ impl<'db> SourceAnalyzer<'db> { .map(Trait::from); if let Some(into_future_trait) = into_future_trait { - let type_ = Type::new_with_resolver(db, &self.resolver, ty); + let type_ = self.ty(ty); if type_.impls_trait(db, into_future_trait, &[]) { let items = into_future_trait.items(db); let into_future_type = items.into_iter().find_map(|item| match item { @@ -733,7 +784,7 @@ impl<'db> SourceAnalyzer<'db> { _ => None, })?; let future_trait = type_.normalize_trait_assoc_type(db, &[], into_future_type)?; - ty = future_trait.ty; + ty = future_trait.ty.skip_binder(); } } @@ -882,8 +933,8 @@ impl<'db> SourceAnalyzer<'db> { Some(( field.into(), local, - Type::new_with_resolver(db, &self.resolver, field_ty), - GenericSubstitution::new(adt.into(), subst, self.trait_environment(db)), + self.ty(field_ty), + GenericSubstitution::new(adt.into(), subst, self.type_owner), )) } @@ -906,11 +957,33 @@ impl<'db> SourceAnalyzer<'db> { .skip_norm_wip(); Some(( field.into(), - Type::new_with_resolver(db, &self.resolver, field_ty), - GenericSubstitution::new(adt.into(), subst, self.trait_environment(db)), + self.ty(field_ty), + GenericSubstitution::new(adt.into(), subst, self.type_owner), )) } + pub(crate) fn resolve_tuple_struct_pat_fields( + &self, + db: &'db dyn HirDatabase, + tuple_struct_pat: &ast::TupleStructPat, + ) -> Option<Vec<(Field, Type<'db>)>> { + let interner = DbInterner::new_no_crate(db); + let pat_id = self.pat_id(&tuple_struct_pat.clone().into())?; + let variant_id = self.infer()?.variant_resolution_for_pat(pat_id.as_pat()?)?; + let (_adt, substs) = self.infer()?.pat_ty(pat_id.as_pat()?).as_adt()?; + + Some( + db.field_types(variant_id) + .iter() + .map(|(local_id, ty)| { + let def = Field { parent: variant_id.into(), id: local_id }; + let ty = ty.get().instantiate(interner, substs).skip_norm_wip(); + (def, self.ty(ty)) + }) + .collect(), + ) + } + pub(crate) fn resolve_bind_pat_to_const( &self, db: &'db dyn HirDatabase, @@ -962,15 +1035,15 @@ impl<'db> SourceAnalyzer<'db> { let container = offset_of_expr.ty()?; let container = self.type_of_type(db, &container)?; - let trait_env = container.env; + let env = self.trait_environment(db); - let interner = DbInterner::new_with(db, trait_env.krate); + let interner = DbInterner::new_with(db, env.krate); let infcx = interner.infer_ctxt().build(TypingMode::PostAnalysis); - let mut container = Either::Right(container.ty); + let mut container = Either::Right(container.ty.skip_binder()); for field_name in offset_of_expr.fields() { if let Either::Right(container) = &mut container { - *container = structurally_normalize_ty(&infcx, *container, trait_env.param_env); + *container = structurally_normalize_ty(&infcx, *container, env.param_env); } let handle_variants = |variant: VariantId, subst: GenericArgs<'db>, container: &mut _| { @@ -1017,7 +1090,10 @@ impl<'db> SourceAnalyzer<'db> { }; if field_name.syntax().text_range() == name_ref.syntax().text_range() { - return Some((field_def, GenericSubstitution::new(generic_def, subst, trait_env))); + return Some(( + field_def, + GenericSubstitution::new(generic_def, subst, self.type_owner), + )); } } never!("the `NameRef` is a child of the `OffsetOfExpr`, we should've visited it"); @@ -1045,7 +1121,7 @@ impl<'db> SourceAnalyzer<'db> { let subst = GenericSubstitution::new( f_in_trait.into(), subs, - self.trait_environment(db), + self.type_owner, ); (AssocItem::Function(f_in_trait.into()), Some(subst)) } @@ -1058,14 +1134,14 @@ impl<'db> SourceAnalyzer<'db> { let subst = GenericSubstitution::new_from_fn( fn_, subst, - self.trait_environment(db), + self.type_owner, ); (AssocItem::Function(fn_), subst) } else { let subst = GenericSubstitution::new( f_in_trait.into(), subs, - self.trait_environment(db), + self.type_owner, ); (AssocItem::Function(f_in_trait.into()), Some(subst)) } @@ -1075,11 +1151,8 @@ impl<'db> SourceAnalyzer<'db> { CandidateId::ConstId(const_id) => { let (konst, subst) = self.resolve_impl_const_or_trait_def_with_subst(db, const_id, subs); - let subst = GenericSubstitution::new( - konst.into(), - subst, - self.trait_environment(db), - ); + let subst = + GenericSubstitution::new(konst.into(), subst, self.type_owner); (AssocItem::Const(konst.into()), Some(subst)) } }; @@ -1103,20 +1176,13 @@ impl<'db> SourceAnalyzer<'db> { CandidateId::ConstId(const_id) => { let (konst, subst) = self.resolve_impl_const_or_trait_def_with_subst(db, const_id, subs); - let subst = GenericSubstitution::new( - konst.into(), - subst, - self.trait_environment(db), - ); + let subst = + GenericSubstitution::new(konst.into(), subst, self.type_owner); (AssocItemId::from(konst), subst) } CandidateId::FunctionId(function_id) => ( function_id.into(), - GenericSubstitution::new( - function_id.into(), - subs, - self.trait_environment(db), - ), + GenericSubstitution::new(function_id.into(), subs, self.type_owner), ), }; return Some((PathResolution::Def(AssocItem::from(assoc).into()), Some(subst))); @@ -1320,12 +1386,11 @@ impl<'db> SourceAnalyzer<'db> { } else { return None; }; - let env = self.trait_environment(db); let (subst, expected_resolution) = match ty.kind() { TyKind::Adt(adt_def, subst) => { let adt_id = adt_def.def_id(); ( - GenericSubstitution::new(adt_id.into(), subst, env), + GenericSubstitution::new(adt_id.into(), subst, self.type_owner), PathResolution::Def(ModuleDef::Adt(adt_id.into())), ) } @@ -1336,7 +1401,7 @@ impl<'db> SourceAnalyzer<'db> { }) => { let assoc_id = def_id.0; ( - GenericSubstitution::new(assoc_id.into(), args, env), + GenericSubstitution::new(assoc_id.into(), args, self.type_owner), PathResolution::Def(ModuleDef::TypeAlias(assoc_id.into())), ) } @@ -1347,7 +1412,7 @@ impl<'db> SourceAnalyzer<'db> { CallableDefId::EnumVariantId(_) => return None, }; ( - GenericSubstitution::new(generic_def_id, subst, env), + GenericSubstitution::new(generic_def_id, subst, self.type_owner), PathResolution::Def(ModuleDefId::from(fn_id.0).into()), ) } @@ -1466,7 +1531,7 @@ impl<'db> SourceAnalyzer<'db> { .map(|local_id| { let field = FieldId { parent: variant, local_id }; let ty = field_types[local_id].get().instantiate(interner, substs).skip_norm_wip(); - (field.into(), Type::new_with_resolver_inner(db, &self.resolver, ty)) + (field.into(), self.ty(ty)) }) .collect() } @@ -1589,7 +1654,7 @@ impl<'db> SourceAnalyzer<'db> { func: FunctionId, substs: GenericArgs<'db>, ) -> (Function, GenericArgs<'db>) { - let owner = match self.resolver.expression_store_owner() { + let owner = match self.resolver.generic_def() { Some(it) => it, None => return (func.into(), substs), }; @@ -1609,7 +1674,7 @@ impl<'db> SourceAnalyzer<'db> { const_id: ConstId, subs: GenericArgs<'db>, ) -> (ConstId, GenericArgs<'db>) { - let owner = match self.resolver.expression_store_owner() { + let owner = match self.resolver.generic_def() { Some(it) => it, None => return (const_id, subs), }; diff --git a/crates/hir/src/symbols.rs b/crates/hir/src/symbols.rs index ff56544d82..c4040c1c00 100644 --- a/crates/hir/src/symbols.rs +++ b/crates/hir/src/symbols.rs @@ -190,8 +190,8 @@ impl<'a> SymbolCollector<'a> { let enum_name = Symbol::intern(EnumSignature::of(this.db, id).name.as_str()); this.with_container_name(Some(enum_name), |this| { let variants = id.enum_variants(this.db); - for (variant_id, variant_name, _) in &variants.variants { - this.push_decl(*variant_id, variant_name, true, None); + for (variant_name, (variant_id, _)) in &variants.variants { + this.push_decl(*variant_id, variant_name, false, None); } }); } @@ -401,7 +401,7 @@ impl<'a> SymbolCollector<'a> { fn collect_from_impl(&mut self, impl_id: ImplId) { let impl_data = ImplSignature::of(self.db, impl_id); let impl_name = Some( - hir_display_with_store(impl_data.self_ty, &impl_data.store) + hir_display_with_store(impl_data.self_ty, impl_id.into(), &impl_data.store) .display( self.db, crate::Impl::from(impl_id).krate(self.db).to_display_target(self.db), diff --git a/crates/hir/src/term_search.rs b/crates/hir/src/term_search.rs index af2371d493..1cc6766bfb 100644 --- a/crates/hir/src/term_search.rs +++ b/crates/hir/src/term_search.rs @@ -145,7 +145,7 @@ impl<'db> LookupTable<'db> { self.data .iter() .find(|(t, _)| { - t.add_reference(Mutability::Shared).could_unify_with_deeply(db, ty) + t.add_reference(db, Mutability::Shared).could_unify_with_deeply(db, ty) }) .map(|(t, it)| { it.exprs(t) diff --git a/crates/hir/src/term_search/expr.rs b/crates/hir/src/term_search/expr.rs index e3d0121e49..a824c8bdca 100644 --- a/crates/hir/src/term_search/expr.rs +++ b/crates/hir/src/term_search/expr.rs @@ -310,21 +310,18 @@ impl<'db> Expr<'db> { Expr::Local(it) => it.ty(db), Expr::ConstParam(it) => it.ty(db), Expr::FamousType { ty, .. } => ty.clone(), - Expr::Function { func, generics, .. } => { - func.ret_type_with_args(db, generics.iter().cloned()) - } - Expr::Method { func, generics, target, .. } => func.ret_type_with_args( - db, - target.ty(db).type_arguments().chain(generics.iter().cloned()), - ), + Expr::Function { func, generics, .. } => func.ret_type(db).instantiate(generics), + Expr::Method { func, generics, target, .. } => func + .ret_type(db) + .instantiate(target.ty(db).type_arguments().chain(generics.iter().cloned())), Expr::Variant { variant, generics, .. } => { - Adt::from(variant.parent_enum(db)).ty_with_args(db, generics.iter().cloned()) + Adt::from(variant.parent_enum(db)).ty(db).instantiate(generics) } Expr::Struct { strukt, generics, .. } => { - Adt::from(*strukt).ty_with_args(db, generics.iter().cloned()) + Adt::from(*strukt).ty(db).instantiate(generics) } Expr::Tuple { ty, .. } => ty.clone(), - Expr::Field { expr, field } => field.ty_with_args(db, expr.ty(db).type_arguments()), + Expr::Field { expr, field } => field.ty(db).instantiate(expr.ty(db).type_arguments()), Expr::Reference(it) => it.ty(db), Expr::Many(ty) => ty.clone(), } diff --git a/crates/hir/src/term_search/tactics.rs b/crates/hir/src/term_search/tactics.rs index 8700326e17..2b7f7da3bf 100644 --- a/crates/hir/src/term_search/tactics.rs +++ b/crates/hir/src/term_search/tactics.rs @@ -10,18 +10,13 @@ use std::iter; -use hir_ty::{ - db::HirDatabase, - mir::BorrowKind, - next_solver::{DbInterner, Ty}, -}; +use hir_ty::{db::HirDatabase, mir::BorrowKind}; use itertools::Itertools; use rustc_hash::FxHashSet; -use rustc_type_ir::inherent::Ty as _; use crate::{ - Adt, AssocItem, GenericDef, GenericParam, HasAttrs, HasVisibility, Impl, ModuleDef, ScopeDef, - Type, TypeParam, term_search::Expr, + Adt, AssocItem, BuiltinType, GenericDef, GenericParam, HasAttrs, HasVisibility, Impl, + ModuleDef, ScopeDef, Type, TypeParam, term_search::Expr, }; use super::{LookupTable, NewTypesKey, TermSearchCtx}; @@ -87,7 +82,7 @@ pub(super) fn trivial<'a, 'lt, 'db, DB: HirDatabase>( return None; } - ty.could_unify_with_deeply(db, &ctx.goal).then_some(expr) + ty.instantiate_with_errors().could_unify_with_deeply(db, &ctx.goal).then_some(expr) }) } @@ -137,7 +132,7 @@ pub(super) fn assoc_const<'a, 'lt, 'db, DB: HirDatabase>( lookup.insert(ty.clone(), std::iter::once(expr.clone())); - ty.could_unify_with_deeply(db, &ctx.goal).then_some(expr) + ty.instantiate_with_errors().could_unify_with_deeply(db, &ctx.goal).then_some(expr) }) } @@ -202,7 +197,9 @@ pub(super) fn data_constructor<'a, 'lt, 'db, DB: HirDatabase>( // Early exit if some param cannot be filled from lookup let param_exprs: Vec<Vec<Expr<'_>>> = fields .into_iter() - .map(|field| lookup.find(db, &field.ty_with_args(db, generics.iter().cloned()))) + .map(|field| { + lookup.find(db, &field.ty(db).instantiate(generics.iter().cloned())) + }) .collect::<Option<_>>()?; // Note that we need special case for 0 param constructors because of multi cartesian @@ -252,7 +249,7 @@ pub(super) fn data_constructor<'a, 'lt, 'db, DB: HirDatabase>( .fields(db) .into_iter() .map(|field| { - lookup.find(db, &field.ty_with_args(db, generics.iter().cloned())) + lookup.find(db, &field.ty(db).instantiate(generics.iter().cloned())) }) .collect::<Option<_>>()?; @@ -285,7 +282,9 @@ pub(super) fn data_constructor<'a, 'lt, 'db, DB: HirDatabase>( } Adt::Union(_) => None, }) - .filter_map(|(ty, exprs)| ty.could_unify_with_deeply(db, &ctx.goal).then_some(exprs)) + .filter_map(|(ty, exprs)| { + ty.instantiate_with_errors().could_unify_with_deeply(db, &ctx.goal).then_some(exprs) + }) .flatten() } @@ -364,7 +363,7 @@ pub(super) fn free_function<'a, 'lt, 'db, DB: HirDatabase>( }) .collect::<Option<_>>()?; - let ret_ty = it.ret_type_with_args(db, generics.iter().cloned()); + let ret_ty = it.ret_type(db).instantiate(generics.iter().cloned()); // Filter out private and unsafe functions if !it.is_visible_from(db, module) || it.is_unsafe_to_call( @@ -381,10 +380,10 @@ pub(super) fn free_function<'a, 'lt, 'db, DB: HirDatabase>( // Early exit if some param cannot be filled from lookup let param_exprs: Vec<Vec<Expr<'_>>> = it - .params_without_self_with_args(db, generics.iter().cloned()) + .params_without_self(db) .into_iter() .map(|field| { - let ty = field.ty(); + let ty = &field.ty().instantiate(&generics); match ty.is_mutable_reference() { true => None, false => lookup.find_autoref(db, ty), @@ -418,7 +417,9 @@ pub(super) fn free_function<'a, 'lt, 'db, DB: HirDatabase>( _ => None, }) .flatten() - .filter_map(|(ty, exprs)| ty.could_unify_with_deeply(db, &ctx.goal).then_some(exprs)) + .filter_map(|(ty, exprs)| { + ty.instantiate_with_errors().could_unify_with_deeply(db, &ctx.goal).then_some(exprs) + }) .flatten() } @@ -493,7 +494,7 @@ pub(super) fn impl_method<'a, 'lt, 'db, DB: HirDatabase>( return None; } - let ret_ty = it.ret_type_with_args(db, ty.type_arguments()); + let ret_ty = it.ret_type(db).instantiate(ty.type_arguments()); // Filter out functions that return references if ctx.config.enable_borrowcheck && ret_ty.contains_reference(db) || ret_ty.is_raw_ptr() { @@ -501,12 +502,12 @@ pub(super) fn impl_method<'a, 'lt, 'db, DB: HirDatabase>( } // Ignore functions that do not change the type - if ty.could_unify_with_deeply(db, &ret_ty) { + if ty.instantiate_with_errors().could_unify_with_deeply(db, &ret_ty) { return None; } let self_ty = - it.self_param(db).expect("No self param").ty_with_args(db, ty.type_arguments()); + it.self_param(db).expect("No self param").ty(db).instantiate(ty.type_arguments()); // Ignore functions that have different self type if !self_ty.autoderef(db).any(|s_ty| ty == s_ty) { @@ -517,9 +518,9 @@ pub(super) fn impl_method<'a, 'lt, 'db, DB: HirDatabase>( // Early exit if some param cannot be filled from lookup let param_exprs: Vec<Vec<Expr<'_>>> = it - .params_without_self_with_args(db, ty.type_arguments()) + .params_without_self(db) .into_iter() - .map(|field| lookup.find_autoref(db, field.ty())) + .map(|field| lookup.find_autoref(db, &field.ty().instantiate(ty.type_arguments()))) .collect::<Option<_>>()?; let generics: Vec<_> = ty.type_arguments().collect(); @@ -540,7 +541,9 @@ pub(super) fn impl_method<'a, 'lt, 'db, DB: HirDatabase>( Some((ret_ty, fn_exprs)) }) - .filter_map(|(ty, exprs)| ty.could_unify_with_deeply(db, &ctx.goal).then_some(exprs)) + .filter_map(|(ty, exprs)| { + ty.instantiate_with_errors().could_unify_with_deeply(db, &ctx.goal).then_some(exprs) + }) .flatten() } @@ -581,7 +584,9 @@ pub(super) fn struct_projection<'a, 'lt, 'db, DB: HirDatabase>( Some((filed_ty, exprs)) }) }) - .filter_map(|(ty, exprs)| ty.could_unify_with_deeply(db, &ctx.goal).then_some(exprs)) + .filter_map(|(ty, exprs)| { + ty.instantiate_with_errors().could_unify_with_deeply(db, &ctx.goal).then_some(exprs) + }) .flatten() } @@ -604,20 +609,18 @@ pub(super) fn famous_types<'a, 'lt, 'db, DB: HirDatabase>( lookup: &'lt mut LookupTable<'db>, ) -> impl Iterator<Item = Expr<'db>> + use<'a, 'db, 'lt, DB> { let db = ctx.sema.db; - let module = ctx.scope.module(); - let interner = DbInterner::new_no_crate(db); - let bool_ty = Ty::new_bool(interner); - let unit_ty = Ty::new_unit(interner); + let bool_ty = BuiltinType::bool().ty(db); + let unit_ty = Type::new_unit(); [ - Expr::FamousType { ty: Type::new(db, module.id, bool_ty), value: "true" }, - Expr::FamousType { ty: Type::new(db, module.id, bool_ty), value: "false" }, - Expr::FamousType { ty: Type::new(db, module.id, unit_ty), value: "()" }, + Expr::FamousType { ty: bool_ty.clone(), value: "true" }, + Expr::FamousType { ty: bool_ty, value: "false" }, + Expr::FamousType { ty: unit_ty, value: "()" }, ] .into_iter() .inspect(|exprs| { lookup.insert(exprs.ty(db), std::iter::once(exprs.clone())); }) - .filter(|expr| expr.ty(db).could_unify_with_deeply(db, &ctx.goal)) + .filter(|expr| expr.ty(db).instantiate_with_errors().could_unify_with_deeply(db, &ctx.goal)) } /// # Impl static method (without self type) tactic @@ -691,7 +694,7 @@ pub(super) fn impl_static_method<'a, 'lt, 'db, DB: HirDatabase>( return None; } - let ret_ty = it.ret_type_with_args(db, ty.type_arguments()); + let ret_ty = it.ret_type(db).instantiate(ty.type_arguments()); // Filter out functions that return references if ctx.config.enable_borrowcheck && ret_ty.contains_reference(db) || ret_ty.is_raw_ptr() { @@ -700,9 +703,9 @@ pub(super) fn impl_static_method<'a, 'lt, 'db, DB: HirDatabase>( // Early exit if some param cannot be filled from lookup let param_exprs: Vec<Vec<Expr<'_>>> = it - .params_without_self_with_args(db, ty.type_arguments()) + .params_without_self(db) .into_iter() - .map(|field| lookup.find_autoref(db, field.ty())) + .map(|field| lookup.find_autoref(db, &field.ty().instantiate(ty.type_arguments()))) .collect::<Option<_>>()?; // Note that we need special case for 0 param constructors because of multi cartesian @@ -722,7 +725,9 @@ pub(super) fn impl_static_method<'a, 'lt, 'db, DB: HirDatabase>( Some((ret_ty, fn_exprs)) }) - .filter_map(|(ty, exprs)| ty.could_unify_with_deeply(db, &ctx.goal).then_some(exprs)) + .filter_map(|(ty, exprs)| { + ty.instantiate_with_errors().could_unify_with_deeply(db, &ctx.goal).then_some(exprs) + }) .flatten() } @@ -745,7 +750,6 @@ pub(super) fn make_tuple<'a, 'lt, 'db, DB: HirDatabase>( should_continue: &'a dyn std::ops::Fn() -> bool, ) -> impl Iterator<Item = Expr<'db>> + use<'a, 'db, 'lt, DB> { let db = ctx.sema.db; - let module = ctx.scope.module(); lookup .types_wishlist() @@ -774,7 +778,7 @@ pub(super) fn make_tuple<'a, 'lt, 'db, DB: HirDatabase>( .filter(|_| should_continue()) .map(|params| { let tys: Vec<Type<'_>> = params.iter().map(|it| it.ty(db)).collect(); - let tuple_ty = Type::new_tuple(module.krate(db).into(), &tys); + let tuple_ty = Type::new_tuple(db, &tys); let expr = Expr::Tuple { ty: tuple_ty.clone(), params }; lookup.insert(tuple_ty, iter::once(expr.clone())); @@ -785,5 +789,10 @@ pub(super) fn make_tuple<'a, 'lt, 'db, DB: HirDatabase>( Some(exprs) }) .flatten() - .filter_map(|expr| expr.ty(db).could_unify_with_deeply(db, &ctx.goal).then_some(expr)) + .filter_map(|expr| { + expr.ty(db) + .instantiate_with_errors() + .could_unify_with_deeply(db, &ctx.goal) + .then_some(expr) + }) } diff --git a/crates/ide-assists/src/handlers/add_label_to_loop.rs b/crates/ide-assists/src/handlers/add_label_to_loop.rs index b2194ab3dc..e7a2b13452 100644 --- a/crates/ide-assists/src/handlers/add_label_to_loop.rs +++ b/crates/ide-assists/src/handlers/add_label_to_loop.rs @@ -1,10 +1,8 @@ -use ide_db::{ - source_change::SourceChangeBuilder, syntax_helpers::node_ext::for_each_break_and_continue_expr, -}; +use ide_db::syntax_helpers::node_ext::for_each_break_and_continue_expr; use syntax::{ SyntaxToken, T, ast::{self, AstNode, HasLoopBody}, - syntax_editor::{Position, SyntaxEditor}, + syntax_editor::{Position, SyntaxAnnotation, SyntaxEditor}, }; use crate::{AssistContext, AssistId, Assists}; @@ -24,8 +22,8 @@ use crate::{AssistContext, AssistId, Assists}; // -> // ``` // fn main() { -// ${1:'l}: loop { -// break ${2:'l}; +// ${0:'l}: loop { +// break ${0:'l}; // continue ${0:'l}; // } // } @@ -53,8 +51,11 @@ pub(crate) fn add_label_to_loop(acc: &mut Assists, ctx: &AssistContext<'_, '_>) ]; editor.insert_all(Position::before(&loop_kw), elements); - if let Some(cap) = ctx.config.snippet_cap { - editor.add_annotation(label.syntax(), builder.make_placeholder_snippet(cap)); + let annotation = + ctx.config.snippet_cap.map(|cap| builder.make_placeholder_snippet(cap)); + + if let Some(annotation) = annotation { + editor.add_annotation(label.syntax(), annotation); } let loop_body = loop_expr.loop_body().and_then(|it| it.stmt_list()); @@ -64,13 +65,14 @@ pub(crate) fn add_label_to_loop(acc: &mut Assists, ctx: &AssistContext<'_, '_>) ast::Expr::ContinueExpr(continue_expr) => continue_expr.continue_token(), _ => return, }; - if let Some(token) = token { - insert_label_after_token(&editor, &token, ctx, builder); + if let Some(token) = token + && let Some(annotation) = annotation + { + insert_label_after_token(&editor, &token, annotation); } }); builder.add_file_edits(ctx.vfs_file_id(), editor); - builder.rename(); }, ) } @@ -86,17 +88,14 @@ fn loop_token(loop_expr: &ast::AnyHasLoopBody) -> Option<syntax::SyntaxToken> { fn insert_label_after_token( editor: &SyntaxEditor, token: &SyntaxToken, - ctx: &AssistContext<'_, '_>, - builder: &mut SourceChangeBuilder, + annotation: SyntaxAnnotation, ) { let make = editor.make(); let label = make.lifetime("'l"); let elements = vec![make.whitespace(" ").into(), label.syntax().clone().into()]; editor.insert_all(Position::after(token), elements); - if let Some(cap) = ctx.config.snippet_cap { - editor.add_annotation(label.syntax(), builder.make_placeholder_snippet(cap)); - } + editor.add_annotation(label.syntax(), annotation); } #[cfg(test)] @@ -118,8 +117,8 @@ fn main() { }"#, r#" fn main() { - ${1:'l}: loop { - break ${2:'l}; + ${0:'l}: loop { + break ${0:'l}; continue ${0:'l}; } }"#, @@ -139,8 +138,8 @@ fn main() { }"#, r#" fn main() { - ${1:'l}: while true { - break ${2:'l}; + ${0:'l}: while true { + break ${0:'l}; continue ${0:'l}; } }"#, @@ -160,8 +159,8 @@ fn main() { }"#, r#" fn main() { - ${1:'l}: for _ in 0..5 { - break ${2:'l}; + ${0:'l}: for _ in 0..5 { + break ${0:'l}; continue ${0:'l}; } }"#, @@ -185,8 +184,8 @@ fn main() { }"#, r#" fn main() { - ${1:'l}: loop { - break ${2:'l}; + ${0:'l}: loop { + break ${0:'l}; continue ${0:'l}; loop { break; @@ -217,8 +216,8 @@ fn main() { loop { break; continue; - ${1:'l}: loop { - break ${2:'l}; + ${0:'l}: loop { + break ${0:'l}; continue ${0:'l}; } } diff --git a/crates/ide-assists/src/handlers/add_missing_match_arms.rs b/crates/ide-assists/src/handlers/add_missing_match_arms.rs index 667a1d7813..632fe0d72c 100644 --- a/crates/ide-assists/src/handlers/add_missing_match_arms.rs +++ b/crates/ide-assists/src/handlers/add_missing_match_arms.rs @@ -80,16 +80,25 @@ pub(crate) fn add_missing_match_arms(acc: &mut Assists, ctx: &AssistContext<'_, let module = scope.module(); let cfg = ctx.config.find_path_config(ctx.sema.is_nightly(scope.krate())); let self_ty = if ctx.config.prefer_self_ty { - scope.expression_store_owner().and_then(|def| { - match def { - hir::ExpressionStoreOwner::Body(def_with_body) => { - def_with_body.as_assoc_item(ctx.db()) + scope + .expression_store_owner() + .and_then(|def| { + match def { + hir::ExpressionStoreOwner::Body(def_with_body) => { + def_with_body.as_assoc_item(ctx.db()) + } + hir::ExpressionStoreOwner::Signature(def) => def.as_assoc_item(ctx.db()), + hir::ExpressionStoreOwner::VariantFields(_) => None, + }? + .implementing_ty(ctx.db()) + }) + .map(|self_ty| { + if let Some(owner) = scope.generic_def() { + self_ty.try_rebase_into_owner(ctx.db(), owner).unwrap() + } else { + self_ty } - hir::ExpressionStoreOwner::Signature(def) => def.as_assoc_item(ctx.db()), - hir::ExpressionStoreOwner::VariantFields(_) => None, - }? - .implementing_ty(ctx.db()) - }) + }) } else { None }; @@ -613,7 +622,7 @@ fn build_pat( hir::StructKind::Tuple => { let mut name_generator = suggest_name::NameGenerator::default(); let pats = fields.into_iter().map(|f| { - let name = name_generator.for_type(&f.ty(db).to_type(db), db, edition); + let name = name_generator.for_type(&f.ty(db), db, edition); match name { Some(name) => make.ident_pat(false, false, make.name(&name)).into(), None => make.wildcard_pat().into(), diff --git a/crates/ide-assists/src/handlers/auto_import.rs b/crates/ide-assists/src/handlers/auto_import.rs index ac0bae7cd9..dd082476d2 100644 --- a/crates/ide-assists/src/handlers/auto_import.rs +++ b/crates/ide-assists/src/handlers/auto_import.rs @@ -293,7 +293,7 @@ pub(crate) fn relevance_score( if let Some(ty) = ty { if ty == *expected { score = 100000; - } else if ty.could_unify_with(ctx.db(), expected) { + } else if ty.could_unify_with(ctx.db(), &expected.instantiate_with_errors()) { score = 10000; } } @@ -2037,4 +2037,60 @@ fn baz() { "Import `foo::Ext` without `as _`", ); } + + #[test] + fn local_enum_variant() { + check_assist( + auto_import, + r#" +mod foo { + pub enum ControlFlow { + Continue, + } +} + +fn main() { + Continue$0; +} + "#, + r#" +use foo::ControlFlow::Continue; + +mod foo { + pub enum ControlFlow { + Continue, + } +} + +fn main() { + Continue; +} + "#, + ); + } + + #[test] + fn foreign_enum_variant() { + check_assist( + auto_import, + r#" +//- /foo.rs crate:foo +pub enum ControlFlow { + Continue, +} + +//- /main.rs crate:main deps:foo +fn main() { + Continue$0; +} + "#, + r#" +use foo::ControlFlow::Continue; + +fn main() { + Continue; +} + "#, + ); + } } diff --git a/crates/ide-assists/src/handlers/convert_let_else_to_match.rs b/crates/ide-assists/src/handlers/convert_let_else_to_match.rs index 07e12f0320..65c1c7cb70 100644 --- a/crates/ide-assists/src/handlers/convert_let_else_to_match.rs +++ b/crates/ide-assists/src/handlers/convert_let_else_to_match.rs @@ -229,6 +229,7 @@ fn remove_mut_and_collect_idents( | ast::Pat::LiteralPat(_) | ast::Pat::PathPat(_) | ast::Pat::WildcardPat(_) + | ast::Pat::NotNull(_) | ast::Pat::ConstBlockPat(_) => pat.clone(), // don't support macro pat yet ast::Pat::MacroPat(_) => return None, diff --git a/crates/ide-assists/src/handlers/convert_match_to_let_else.rs b/crates/ide-assists/src/handlers/convert_match_to_let_else.rs index a93ab138e5..9dffdf3f36 100644 --- a/crates/ide-assists/src/handlers/convert_match_to_let_else.rs +++ b/crates/ide-assists/src/handlers/convert_match_to_let_else.rs @@ -93,7 +93,7 @@ fn find_arms( let mut extracting = None; let mut diverging = None; for arm in arms { - if ctx.sema.type_of_expr(&arm.expr()?)?.original().is_never() { + if ctx.sema.expr_is_diverging(&arm.expr()?) { diverging = Some(arm); } else { extracting = Some(arm); diff --git a/crates/ide-assists/src/handlers/expand_rest_pattern.rs b/crates/ide-assists/src/handlers/expand_rest_pattern.rs index 4aa11b4e03..8ae322c020 100644 --- a/crates/ide-assists/src/handlers/expand_rest_pattern.rs +++ b/crates/ide-assists/src/handlers/expand_rest_pattern.rs @@ -137,13 +137,7 @@ fn expand_tuple_struct_rest_pattern( pat.fields() .take(prefix_count) .chain(fields[prefix_count..fields.len() - suffix_count].iter().map(|f| { - gen_unnamed_pat( - ctx, - make, - &mut name_gen, - &f.ty(ctx.db()).to_type(ctx.sema.db), - f.index(), - ) + gen_unnamed_pat(ctx, make, &mut name_gen, &f.ty(ctx.db()), f.index()) })) .chain(pat.fields().skip(prefix_count + 1)), ); diff --git a/crates/ide-assists/src/handlers/extract_function.rs b/crates/ide-assists/src/handlers/extract_function.rs index 44123dc20d..67bb7fc03e 100644 --- a/crates/ide-assists/src/handlers/extract_function.rs +++ b/crates/ide-assists/src/handlers/extract_function.rs @@ -21,7 +21,7 @@ use itertools::Itertools; use syntax::{ Edition, SyntaxElement, SyntaxKind::{self, COMMENT}, - SyntaxNode, SyntaxToken, T, TextRange, TextSize, TokenAtOffset, WalkEvent, + SyntaxNode, T, TextRange, WalkEvent, ast::{ self, AstNode, AstToken, HasAttrs, HasGenericParams, HasName, edit::{AstNodeEdit, IndentLevel}, @@ -103,7 +103,7 @@ pub(crate) fn extract_function(acc: &mut Assists, ctx: &AssistContext<'_, '_>) - let ret_ty = body.return_ty(ctx)?; let control_flow = body.external_control_flow(ctx, &container_info)?; - let ret_values = body.ret_values(ctx, node.parent().as_ref().unwrap_or(&node)); + let ret_values = body.ret_values(ctx); let target_range = body.text_range(); @@ -968,13 +968,11 @@ impl FunctionBody { fn ret_values<'a>( &self, ctx: &'a AssistContext<'_, '_>, - parent: &SyntaxNode, ) -> impl Iterator<Item = OutlivedLocal> + 'a { - let parent = parent.clone(); let range = self.text_range(); locals_defined_in_body(&ctx.sema, self) .into_iter() - .filter_map(move |local| local_outlives_body(ctx, range, local, &parent)) + .filter_map(move |local| local_outlives_body(ctx, range, local)) } /// Analyses the function body for external control flow. @@ -1174,15 +1172,11 @@ fn has_exclusive_usages( usages .iter() .filter(|reference| body.contains_range(reference.range)) - .any(|reference| reference_is_exclusive(reference, body, ctx)) + .any(|reference| reference_is_exclusive(reference, ctx)) } /// checks if this reference requires `&mut` access inside node -fn reference_is_exclusive( - reference: &FileReference, - node: &dyn HasTokenAtOffset, - ctx: &AssistContext<'_, '_>, -) -> bool { +fn reference_is_exclusive(reference: &FileReference, ctx: &AssistContext<'_, '_>) -> bool { // FIXME: this quite an incorrect way to go about doing this :-) // `FileReference` is an IDE-type --- it encapsulates data communicated to the human, // but doesn't necessary fully reflect all the intricacies of the underlying language semantics @@ -1195,7 +1189,7 @@ fn reference_is_exclusive( } // we take `&mut` reference to variable: `&mut v` - let path = match path_element_of_reference(node, reference.range) { + let path = match path_element_of(reference) { Some(path) => path, None => return false, }; @@ -1205,11 +1199,6 @@ fn reference_is_exclusive( /// checks if this expr requires `&mut` access, recurses on field access fn expr_require_exclusive_access(ctx: &AssistContext<'_, '_>, expr: &ast::Expr) -> Option<bool> { - if let ast::Expr::MacroExpr(_) = expr { - // FIXME: expand macro and check output for mutable usages of the variable? - return None; - } - let parent = expr.syntax().parent()?; if let Some(bin_expr) = ast::BinExpr::cast(parent.clone()) { @@ -1238,66 +1227,19 @@ fn expr_require_exclusive_access(ctx: &AssistContext<'_, '_>, expr: &ast::Expr) Some(false) } -trait HasTokenAtOffset { - fn token_at_offset(&self, offset: TextSize) -> TokenAtOffset<SyntaxToken>; -} - -impl HasTokenAtOffset for SyntaxNode { - fn token_at_offset(&self, offset: TextSize) -> TokenAtOffset<SyntaxToken> { - SyntaxNode::token_at_offset(self, offset) - } -} - -impl HasTokenAtOffset for FunctionBody { - fn token_at_offset(&self, offset: TextSize) -> TokenAtOffset<SyntaxToken> { - match self { - FunctionBody::Expr(expr) => expr.syntax().token_at_offset(offset), - FunctionBody::Span { parent, text_range, .. } => { - match parent.syntax().token_at_offset(offset) { - TokenAtOffset::None => TokenAtOffset::None, - TokenAtOffset::Single(t) => { - if text_range.contains_range(t.text_range()) { - TokenAtOffset::Single(t) - } else { - TokenAtOffset::None - } - } - TokenAtOffset::Between(a, b) => { - match ( - text_range.contains_range(a.text_range()), - text_range.contains_range(b.text_range()), - ) { - (true, true) => TokenAtOffset::Between(a, b), - (true, false) => TokenAtOffset::Single(a), - (false, true) => TokenAtOffset::Single(b), - (false, false) => TokenAtOffset::None, - } - } - } - } - } - } -} - /// find relevant `ast::Expr` for reference /// /// # Preconditions /// /// `node` must cover `reference`, that is `node.text_range().contains_range(reference.range)` -fn path_element_of_reference( - node: &dyn HasTokenAtOffset, - reference_range: TextRange, -) -> Option<ast::Expr> { - let token = node.token_at_offset(reference_range.start()).right_biased().or_else(|| { - stdx::never!(false, "cannot find token at variable usage: {:?}", reference_range); - None - })?; - let path = token.parent_ancestors().find_map(ast::Expr::cast).or_else(|| { - stdx::never!(false, "cannot find path parent of variable usage: {:?}", token); +fn path_element_of(reference: &FileReference) -> Option<ast::Expr> { + let path = reference.name.syntax().ancestors().find_map(ast::Expr::cast).or_else(|| { + stdx::never!(false, "cannot find path parent of variable usage: {:?}", reference.name); None })?; stdx::always!( - matches!(path, ast::Expr::PathExpr(_) | ast::Expr::MacroExpr(_)), + matches!(path, ast::Expr::PathExpr(_)) + || path.syntax().parent().and_then(ast::FormatArgsExpr::cast).is_some(), "unexpected expression type for variable usage: {:?}", path ); @@ -1327,14 +1269,13 @@ fn local_outlives_body( ctx: &AssistContext<'_, '_>, body_range: TextRange, local: Local, - parent: &SyntaxNode, ) -> Option<OutlivedLocal> { let usages = LocalUsages::find_local_usages(ctx, local); let mut has_mut_usages = false; let mut any_outlives = false; for usage in usages.iter() { if body_range.end() <= usage.range.start() { - has_mut_usages |= reference_is_exclusive(usage, parent, ctx); + has_mut_usages |= reference_is_exclusive(usage, ctx); any_outlives |= true; if has_mut_usages { break; // no need to check more elements we have all the info we wanted @@ -2093,12 +2034,11 @@ fn fix_param_usages( let mut usages_for_param: Vec<(&Param<'_>, Vec<ast::Expr>)> = Vec::new(); let mut usages_for_self_param: Vec<ast::Expr> = Vec::new(); let source_range = source_syntax.text_range(); - let source_start = source_range.start(); + let syntax_offset = source_range.start() - syntax.text_range().start(); let reference_filter = |reference: &FileReference| { source_range.contains_range(reference.range).then_some(())?; - let local_range = reference.range - source_start; - path_element_of_reference(syntax, local_range) + path_element_of(reference) }; if let Some(self_param) = to_this_param { @@ -2119,10 +2059,20 @@ fn fix_param_usages( } let make = editor.make(); + let to_original = |old: &SyntaxNode| { + ctx.sema + .original_range_opt(old) + .map(|orig| crate::utils::cover_edit_range(syntax, orig.range - syntax_offset)) + }; + let replace = |range, new: &SyntaxNode| { + editor.replace_all(range, vec![new.clone().into()]); + }; for self_usage in usages_for_self_param { - let this_expr = make.expr_path(make.ident_path("this")); - editor.replace(self_usage.syntax(), this_expr.syntax()); + if let Some(original) = to_original(self_usage.syntax()) { + let this_expr = make.expr_path(make.ident_path("this")); + replace(original, this_expr.syntax()) + } } for (param, usages) in usages_for_param { for usage in usages { @@ -2135,24 +2085,33 @@ fn fix_param_usages( // do nothing } Some(ast::Expr::RefExpr(node)) - if param.kind() == ParamKind::MutRef && node.mut_token().is_some() => + if param.kind() == ParamKind::MutRef + && node.mut_token().is_some() + && let Some(original) = to_original(node.syntax()) => { - editor.replace( - node.syntax(), + replace( + original, node.expr().expect("RefExpr::expr() cannot be None").syntax(), ); } Some(ast::Expr::RefExpr(node)) - if param.kind() == ParamKind::SharedRef && node.mut_token().is_none() => + if param.kind() == ParamKind::SharedRef + && node.mut_token().is_none() + && let Some(original) = to_original(node.syntax()) => { - editor.replace( - node.syntax(), + replace( + original, node.expr().expect("RefExpr::expr() cannot be None").syntax(), ); } Some(_) | None => { - let p = make.expr_prefix(T![*], usage.clone()); - editor.replace(usage.syntax(), p.syntax()) + if let Some(original) = to_original(usage.syntax()) + // ignore variable in format string + && !matches!(usage, ast::Expr::Literal(_)) + { + let p = make.expr_prefix(T![*], usage.clone()); + replace(original, p.syntax()) + } } } } @@ -3520,6 +3479,61 @@ fn $0fun_name(n: &mut i32) { } #[test] + fn mut_param_because_of_mut_ref_in_macro() { + check_assist( + extract_function, + r#" +macro_rules! refmut { ($e:expr) => { &mut $e }; } +fn foo() { + let mut n = 1; + $0let v = refmut!(n); + *v += 1;$0 + let k = n; +} +"#, + r#" +macro_rules! refmut { ($e:expr) => { &mut $e }; } +fn foo() { + let mut n = 1; + fun_name(&mut n); + let k = n; +} + +fn $0fun_name(n: &mut i32) { + let v = refmut!(*n); + *v += 1; +} +"#, + ); + + check_assist( + extract_function, + r#" +macro_rules! id { ($e:expr) => { $e }; } +fn foo() { + let mut n = 1; + $0let v = id!(&mut n); + *v += 1;$0 + let k = n; +} +"#, + r#" +macro_rules! id { ($e:expr) => { $e }; } +fn foo() { + let mut n = 1; + fun_name(&mut n); + let k = n; +} + +fn $0fun_name(n: &mut i32) { + let v = id!(n); + *v += 1; +} +"#, + ); + } + + #[test] fn mut_param_by_value_because_of_mut_ref() { check_assist( extract_function, @@ -6396,7 +6410,42 @@ fn main() { } fn $0fun_name(s: &Foo) { - *print!("{}{}", s, s); + print!("{}{}", *s, *s); +}"#, + ); + + check_assist( + extract_function, + r#" +//- minicore: fmt +#[derive(Debug)] +struct Foo(&'static str); + +impl Foo { + fn text(&self) -> &str { self.0 } +} + +fn main() { + let s = Foo(""); + $0print!("{s}{s}");$0 + let _ = s.text() == ""; +}"#, + r#" +#[derive(Debug)] +struct Foo(&'static str); + +impl Foo { + fn text(&self) -> &str { self.0 } +} + +fn main() { + let s = Foo(""); + fun_name(&s); + let _ = s.text() == ""; +} + +fn $0fun_name(s: &Foo) { + print!("{s}{s}"); }"#, ); } diff --git a/crates/ide-assists/src/handlers/extract_module.rs b/crates/ide-assists/src/handlers/extract_module.rs index 9e06a17337..40eaed0080 100644 --- a/crates/ide-assists/src/handlers/extract_module.rs +++ b/crates/ide-assists/src/handlers/extract_module.rs @@ -733,6 +733,7 @@ fn check_def_in_mod_and_out_sel( Definition::Static(x) => check_item!(x), Definition::Trait(x) => check_item!(x), Definition::TypeAlias(x) => check_item!(x), + Definition::Macro(x) => check_item!(x), _ => {} } @@ -1813,4 +1814,33 @@ mod foo { "#, ) } + + #[test] + fn test_extract_module_macro_call_import() { + check_assist( + extract_module, + r" +macro_rules! my_macro { + () => {}; +} + +$0fn bar() { + my_macro!(); +}$0 + ", + r" +macro_rules! my_macro { + () => {}; +} + +mod modname { + use super::my_macro; + + pub(crate) fn bar() { + my_macro!(); + } +} + ", + ) + } } diff --git a/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs b/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs index 50ce8d9b4d..5e6e74bc94 100644 --- a/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs +++ b/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs @@ -249,10 +249,6 @@ fn tag_generics_in_variant(ty: &ast::Type, generics: &mut [(ast::GenericParam, b } } param if matches!(token.kind(), T![ident]) => { - #[expect( - clippy::collapsible_match, - reason = "it won't compile since in the guard, `param` is immutable" - )] if match param { ast::GenericParam::ConstParam(konst) => konst .name() diff --git a/crates/ide-assists/src/handlers/extract_variable.rs b/crates/ide-assists/src/handlers/extract_variable.rs index d4a0490f16..e1b91c9e89 100644 --- a/crates/ide-assists/src/handlers/extract_variable.rs +++ b/crates/ide-assists/src/handlers/extract_variable.rs @@ -13,7 +13,10 @@ use syntax::{ syntax_editor::{Element, Position}, }; -use crate::{AssistContext, AssistId, Assists, utils::is_body_const}; +use crate::{ + AssistContext, AssistId, Assists, + utils::{cover_edit_range, is_body_const}, +}; // Assist: extract_variable // @@ -107,9 +110,7 @@ pub(crate) fn extract_variable(acc: &mut Assists, ctx: &AssistContext<'_, '_>) - .skip_while(|it| !it.text_range().contains_range(range)) .find_map(valid_target_expr(ctx))?; let original_range = ctx.sema.original_range(expr.syntax()); - let (first, last) = extract_token_range_of(&node, original_range.range)?; - let to_extract = first.syntax_element()..=last.syntax_element(); - (to_extract, expr) + (cover_edit_range(&node, original_range.range), expr) } else { let expr = node .descendants() @@ -3004,4 +3005,26 @@ fn main() { "Extract into variable", ); } + + #[test] + fn regression_22441() { + check_assist_by_label( + extract_variable, + r#" +//- minicore: option, write +fn main() { + let maybe_str = Some("world"); + write!((), "Hello, {}!", $0maybe_str.unwrap()$0); +} +"#, + r#" +fn main() { + let maybe_str = Some("world"); + let $0var_name = maybe_str.unwrap(); + write!((), "Hello, {}!", var_name); +} +"#, + "Extract into variable", + ); + } } diff --git a/crates/ide-assists/src/handlers/generate_from_impl_for_enum.rs b/crates/ide-assists/src/handlers/generate_from_impl_for_enum.rs index 52df6182ac..1617016172 100644 --- a/crates/ide-assists/src/handlers/generate_from_impl_for_enum.rs +++ b/crates/ide-assists/src/handlers/generate_from_impl_for_enum.rs @@ -1,4 +1,3 @@ -use hir::next_solver::{DbInterner, TypingMode}; use ide_db::{RootDatabase, famous_defs::FamousDefs}; use syntax::ast::{self, AstNode, HasName, edit::AstNodeEdit, syntax_factory::SyntaxFactory}; use syntax::syntax_editor::Position; @@ -159,16 +158,12 @@ fn existing_from_impl( let variant = sema.to_def(variant)?; let krate = variant.module(db).krate(db); let from_trait = FamousDefs(sema, krate).core_convert_From()?; - let interner = DbInterner::new_with(db, krate.base()); - use hir::next_solver::infer::DbInternerInferExt; - let infcx = interner.infer_ctxt().build(TypingMode::non_body_analysis()); - let variant = variant.instantiate_infer(&infcx); let enum_ = variant.parent_enum(sema.db); let field_ty = variant.fields(sema.db).first()?.ty(sema.db); let enum_ty = enum_.ty(sema.db); tracing::debug!(?enum_, ?field_ty, ?enum_ty); - enum_ty.impls_trait(infcx, from_trait, &[field_ty]).then_some(()) + enum_ty.has_any_impl(db, from_trait, &[field_ty]).then_some(()) } #[cfg(test)] diff --git a/crates/ide-assists/src/handlers/generate_single_field_struct_from.rs b/crates/ide-assists/src/handlers/generate_single_field_struct_from.rs index 4348dfa212..d5629e2e7e 100644 --- a/crates/ide-assists/src/handlers/generate_single_field_struct_from.rs +++ b/crates/ide-assists/src/handlers/generate_single_field_struct_from.rs @@ -1,4 +1,3 @@ -use hir::next_solver::{DbInterner, TypingMode}; use hir::{HasCrate, ModuleDef, Semantics}; use ide_db::use_trivial_constructor::use_trivial_constructor_with_factory; use ide_db::{ @@ -233,15 +232,11 @@ fn from_impl_exists( let strukt = sema.to_def(strukt)?; let krate = strukt.krate(db); let from_trait = FamousDefs(sema, krate).core_convert_From()?; - let interner = DbInterner::new_with(db, krate.base()); - use hir::next_solver::infer::DbInternerInferExt; - let infcx = interner.infer_ctxt().build(TypingMode::non_body_analysis()); - let strukt = strukt.instantiate_infer(&infcx); let field_ty = strukt.fields(db).get(main_field_i)?.ty(db); let struct_ty = strukt.ty(db); tracing::debug!(?strukt, ?field_ty, ?struct_ty); - struct_ty.impls_trait(infcx, from_trait, &[field_ty]).then_some(()) + struct_ty.has_any_impl(db, from_trait, &[field_ty]).then_some(()) } #[cfg(test)] diff --git a/crates/ide-assists/src/handlers/inline_call.rs b/crates/ide-assists/src/handlers/inline_call.rs index af048c6ae0..8897e59355 100644 --- a/crates/ide-assists/src/handlers/inline_call.rs +++ b/crates/ide-assists/src/handlers/inline_call.rs @@ -7,7 +7,7 @@ use hir::{ sym, }; use ide_db::{ - EditionedFileId, FileId, FxHashMap, RootDatabase, + EditionedFileId, FxHashMap, RootDatabase, base_db::Crate, defs::Definition, imports::insert_use::remove_use_tree_if_simple, @@ -21,7 +21,7 @@ use syntax::{ ast::{ self, HasArgList, HasGenericArgs, Pat, PathExpr, edit::{AstNodeEdit, IndentLevel}, - make, + syntax_factory::SyntaxFactory, }, syntax_editor::SyntaxEditor, }; @@ -79,7 +79,11 @@ pub(crate) fn inline_into_callers(acc: &mut Assists, ctx: &AssistContext<'_, '_> let function = ctx.sema.to_def(&ast_func)?; - let params = get_fn_params(ctx.sema.db, function, ¶m_list)?; + let def_file_editor = SyntaxEditor::new(ast_func.syntax().ancestors().last().unwrap()).0; + let params = get_fn_params(ctx.sema.db, function, ¶m_list, def_file_editor.make())?; + + let mut file_editors = FxHashMap::default(); + file_editors.insert(vfs_def_file, def_file_editor); let usages = Definition::Function(function).usages(&ctx.sema); if !usages.at_least_one() { @@ -106,7 +110,6 @@ pub(crate) fn inline_into_callers(acc: &mut Assists, ctx: &AssistContext<'_, '_> let mut usages = usages.all(); let current_file_usage = usages.references.remove(&def_file); - let mut file_editors: FxHashMap<FileId, SyntaxEditor> = FxHashMap::default(); let mut remove_def = true; let mut inline_refs_for_file = |file_id: EditionedFileId, refs: Vec<FileReference>| { let file_id = file_id.file_id(ctx.db()); @@ -170,10 +173,10 @@ pub(crate) fn inline_into_callers(acc: &mut Assists, ctx: &AssistContext<'_, '_> None => builder.edit_file(vfs_def_file), } if remove_def { - let editor = file_editors + file_editors .entry(vfs_def_file) - .or_insert_with(|| builder.make_editor(ast_func.syntax())); - editor.delete(ast_func.syntax()); + .or_insert_with(|| builder.make_editor(ast_func.syntax())) + .delete(ast_func.syntax()); } for (file_id, editor) in file_editors { builder.add_file_edits(file_id, editor); @@ -251,7 +254,9 @@ pub(crate) fn inline_call(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Opt cov_mark::hit!(inline_call_recursive); return None; } - let params = get_fn_params(ctx.sema.db, function, ¶m_list)?; + let syntax = call_info.node.syntax().clone(); + let editor = SyntaxEditor::new(syntax.ancestors().last().unwrap()).0; + let params = get_fn_params(ctx.sema.db, function, ¶m_list, editor.make())?; if call_info.arguments.len() != params.len() { // Can't inline the function because they've passed the wrong number of @@ -260,9 +265,7 @@ pub(crate) fn inline_call(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Opt return None; } - let syntax = call_info.node.syntax().clone(); acc.add(AssistId::refactor_inline("inline_call"), label, syntax.text_range(), |builder| { - let editor = builder.make_editor(call_info.node.syntax()); let replacement = inline(&ctx.sema, file_id, function, &fn_body, ¶ms, &call_info, &editor); editor.replace(call_info.node.syntax(), replacement.syntax()); @@ -311,6 +314,7 @@ fn get_fn_params<'db>( db: &'db dyn HirDatabase, function: hir::Function, param_list: &ast::ParamList, + make: &SyntaxFactory, ) -> Option<Vec<(ast::Pat, Option<ast::Type>, hir::Param<'db>)>> { let mut assoc_fn_params = function.assoc_fn_params(db).into_iter(); @@ -318,10 +322,10 @@ fn get_fn_params<'db>( if let Some(self_param) = param_list.self_param() { // Keep `ref` and `mut` and transform them into `&` and `mut` later params.push(( - make::ident_pat( + make.ident_pat( self_param.amp_token().is_some(), self_param.mut_token().is_some(), - make::name("this"), + make.name("this"), ) .into(), None, diff --git a/crates/ide-assists/src/handlers/qualify_path.rs b/crates/ide-assists/src/handlers/qualify_path.rs index cb48554083..5e07d85db9 100644 --- a/crates/ide-assists/src/handlers/qualify_path.rs +++ b/crates/ide-assists/src/handlers/qualify_path.rs @@ -52,8 +52,8 @@ pub(crate) fn qualify_path(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Op ImportCandidate::Path(candidate) if !candidate.qualifier.is_empty() => { cov_mark::hit!(qualify_path_qualifier_start); let path = ast::Path::cast(syntax_under_caret.clone())?; - let (prev_segment, segment) = (path.qualifier()?.segment()?, path.segment()?); - QualifyCandidate::QualifierStart(segment, prev_segment.generic_arg_list()) + let first_seg_generics = path.segments().next()?.generic_arg_list(); + QualifyCandidate::QualifierStart(path, first_seg_generics) } ImportCandidate::Path(_) => { cov_mark::hit!(qualify_path_unqualified_name); @@ -113,7 +113,7 @@ pub(crate) fn qualify_path(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Op Some(()) } pub(crate) enum QualifyCandidate<'db> { - QualifierStart(ast::PathSegment, Option<ast::GenericArgList>), + QualifierStart(ast::Path, Option<ast::GenericArgList>), UnqualifiedName(Option<ast::GenericArgList>), TraitAssocItem(ast::Path, ast::PathSegment), TraitMethod(&'db RootDatabase, ast::MethodCallExpr), @@ -131,9 +131,11 @@ impl QualifyCandidate<'_> { ) { let import = mod_path_to_ast_with_factory(editor.make(), import, edition); match self { - QualifyCandidate::QualifierStart(segment, generics) => { + QualifyCandidate::QualifierStart(path, generics) => { let generics = generics.as_ref().map_or_else(String::new, ToString::to_string); - replacer(format!("{import}{generics}::{segment}")); + let suffix = + path.segments().skip(1).map(|s| s.to_string()).collect::<Vec<_>>().join("::"); + replacer(format!("{import}{generics}::{suffix}")); } QualifyCandidate::UnqualifiedName(generics) => { let generics = generics.as_ref().map_or_else(String::new, ToString::to_string); @@ -285,6 +287,76 @@ fmt::Formatter } #[test] + fn applicable_when_multiple_segments() { + check_assist( + qualify_path, + r#" + mod a { pub mod b { pub mod c { pub fn foo() {} } } } +b::c::foo$0 +"#, + r#" + mod a { pub mod b { pub mod c { pub fn foo() {} } } } +a::b::c::foo +"#, + ); + check_assist( + qualify_path, + r#" + mod a { pub mod b { pub mod c { pub fn foo() {} } } } +b::c$0::foo +"#, + r#" + mod a { pub mod b { pub mod c { pub fn foo() {} } } } +a::b::c::foo +"#, + ); + check_assist( + qualify_path, + r#" + mod a { pub mod b { pub mod c { pub fn foo() {} } } } +b$0::c::foo +"#, + r#" + mod a { pub mod b { pub mod c { pub fn foo() {} } } } +a::b::c::foo +"#, + ); + } + + #[test] + fn applicable_when_multiple_segments_with_generics() { + check_assist( + qualify_path, + r#" +mod a { + pub mod b { + pub struct TestStruct<T>(T); + impl<T> TestStruct<T> { + pub const TEST_CONST: u8 = 42; + } + } +} +fn main() { + b::TestStruct::<()>::TEST_CONST$0; +} +"#, + r#" +mod a { + pub mod b { + pub struct TestStruct<T>(T); + impl<T> TestStruct<T> { + pub const TEST_CONST: u8 = 42; + } + } +} +fn main() { + a::b::TestStruct::<()>::TEST_CONST; +} +"#, + ); + } + + #[test] fn applicable_when_found_an_import() { check_assist( qualify_path, diff --git a/crates/ide-assists/src/handlers/replace_if_let_with_match.rs b/crates/ide-assists/src/handlers/replace_if_let_with_match.rs index aa3f917f12..ff2d0544b2 100644 --- a/crates/ide-assists/src/handlers/replace_if_let_with_match.rs +++ b/crates/ide-assists/src/handlers/replace_if_let_with_match.rs @@ -171,7 +171,7 @@ fn make_else_arm( let (pattern, expr) = if let Some(else_expr) = else_expr { let pattern = match conditionals { [(None, Some(_), _)] => make.literal_pat("false").into(), - [(Some(pat), _, _)] => match ctx + [(Some(pat), None, _)] => match ctx .sema .type_of_pat(pat) .and_then(|ty| TryEnum::from_ty(&ctx.sema, &ty.adjusted())) @@ -956,6 +956,31 @@ fn foo(x: Option<i32>) { } #[test] + fn special_case_option_with_guard() { + check_assist( + replace_if_let_with_match, + r#" +//- minicore: option +fn foo(x: Option<i32>) { + $0if let Some(x) = x && x != 4 { + println!("{}", x) + } else { + println!("none") + } +} +"#, + r#" +fn foo(x: Option<i32>) { + match x { + Some(x) if x != 4 => println!("{}", x), + _ => println!("none"), + } +} +"#, + ); + } + + #[test] fn special_case_option_ref() { check_assist( replace_if_let_with_match, @@ -1006,6 +1031,31 @@ fn foo(x: Option<i32>) { } #[test] + fn special_case_inverted_option_with_guard() { + check_assist( + replace_if_let_with_match, + r#" +//- minicore: option +fn foo(x: Option<i32>) { + $0if let None = x && other_cond { + println!("none") + } else { + println!("some") + } +} +"#, + r#" +fn foo(x: Option<i32>) { + match x { + None if other_cond => println!("none"), + _ => println!("some"), + } +} +"#, + ); + } + + #[test] fn special_case_result() { check_assist( replace_if_let_with_match, diff --git a/crates/ide-assists/src/handlers/replace_method_eager_lazy.rs b/crates/ide-assists/src/handlers/replace_method_eager_lazy.rs index 7aa9a82109..17ee8597c1 100644 --- a/crates/ide-assists/src/handlers/replace_method_eager_lazy.rs +++ b/crates/ide-assists/src/handlers/replace_method_eager_lazy.rs @@ -1,8 +1,8 @@ use hir::Semantics; -use ide_db::{RootDatabase, assists::AssistId, defs::Definition}; +use ide_db::{RootDatabase, assists::AssistId, defs::Definition, famous_defs::FamousDefs}; use syntax::{ AstNode, - ast::{self, Expr, HasArgList, make, syntax_factory::SyntaxFactory}, + ast::{self, Expr, HasArgList, syntax_factory::SyntaxFactory}, }; use crate::{AssistContext, Assists, utils::wrap_paren_in_call}; @@ -64,9 +64,29 @@ pub(crate) fn replace_with_lazy_method( format!("Replace {method_name} with {method_name_lazy}"), call.syntax().text_range(), |builder| { - let closured = into_closure(&last_arg, &method_name_lazy); - builder.replace(method_name.syntax().text_range(), method_name_lazy); - builder.replace_ast(last_arg, closured); + let editor = builder.make_editor(call.syntax()); + let add_param = match &*method_name_lazy { + "and_then" => true, + "or_else" | "unwrap_or_else" => FamousDefs(&ctx.sema, scope.krate()) + .core_result_Result() + .is_some_and(|result| { + result + .ty(ctx.db()) + .instantiate_with_errors() + .could_unify_with(ctx.db(), &receiver_ty) + }), + _ => false, + }; + let closured = into_closure(&last_arg, add_param, editor.make()); + editor.replace(method_name.syntax(), editor.make().name(&method_name_lazy).syntax()); + editor.replace(last_arg.syntax(), closured.syntax()); + if let Some(cap) = ctx.config.snippet_cap + && let ast::Expr::ClosureExpr(closured) = closured + && let Some(param) = closured.param_list().and_then(|it| it.params().next()) + { + editor.add_annotation(param.syntax(), builder.make_placeholder_snippet(cap)); + } + builder.add_file_edits(ctx.vfs_file_id(), editor); }, ) } @@ -83,7 +103,7 @@ fn lazy_method_name(name: &str) -> String { } } -fn into_closure(param: &Expr, name_lazy: &str) -> Expr { +fn into_closure(param: &Expr, add_param: bool, make: &SyntaxFactory) -> Expr { (|| { if let ast::Expr::CallExpr(call) = param { if call.arg_list()?.args().count() == 0 { Some(call.expr()?) } else { None } @@ -92,9 +112,8 @@ fn into_closure(param: &Expr, name_lazy: &str) -> Expr { } })() .unwrap_or_else(|| { - let pats = (name_lazy == "and_then") - .then(|| make::untyped_param(make::ext::simple_ident_pat(make::name("it")).into())); - make::expr_closure(pats, param.clone()).into() + let pats = add_param.then(|| make.untyped_param(make.wildcard_pat().into())); + make.expr_closure(pats, param.clone()).into() }) } @@ -156,14 +175,16 @@ pub(crate) fn replace_with_eager_method( format!("Replace {method_name} with {method_name_eager}"), call.syntax().text_range(), |builder| { - builder.replace(method_name.syntax().text_range(), method_name_eager); - let called = into_call(&last_arg, &ctx.sema); - builder.replace_ast(last_arg, called); + let editor = builder.make_editor(call.syntax()); + let called = into_call(&last_arg, &ctx.sema, editor.make()); + editor.replace(method_name.syntax(), editor.make().name(method_name_eager).syntax()); + editor.replace(last_arg.syntax(), called.syntax()); + builder.add_file_edits(ctx.vfs_file_id(), editor); }, ) } -fn into_call(param: &Expr, sema: &Semantics<'_, RootDatabase>) -> Expr { +fn into_call(param: &Expr, sema: &Semantics<'_, RootDatabase>, make: &SyntaxFactory) -> Expr { (|| { if let ast::Expr::ClosureExpr(closure) = param { let mut params = closure.param_list()?.params(); @@ -183,8 +204,8 @@ fn into_call(param: &Expr, sema: &Semantics<'_, RootDatabase>) -> Expr { } })() .unwrap_or_else(|| { - let callable = wrap_paren_in_call(param.clone(), &SyntaxFactory::without_mappings()); - make::expr_call(callable, make::arg_list(Vec::new())).into() + let callable = wrap_paren_in_call(param.clone(), make); + make.expr_call(callable, make.arg_list(Vec::new())).into() }) } @@ -213,7 +234,7 @@ mod tests { check_assist( replace_with_lazy_method, r#" -//- minicore: option, fn +//- minicore: option, result, fn fn foo() { let foo = Some(1); return foo.unwrap_$0or(2); @@ -229,6 +250,26 @@ fn foo() { } #[test] + fn replace_or_with_or_else_with_parameter() { + check_assist( + replace_with_lazy_method, + r#" +//- minicore: option, result, fn +fn foo() { + let foo = Ok(1); + return foo.unwrap_$0or(2); +} +"#, + r#" +fn foo() { + let foo = Ok(1); + return foo.unwrap_or_else(|${0:_}| 2); +} +"#, + ) + } + + #[test] fn replace_or_with_or_else_call() { check_assist( replace_with_lazy_method, @@ -358,7 +399,7 @@ fn foo() { r#" fn foo() { let foo = Some("foo"); - return foo.and_then(|it| Some("bar")); + return foo.and_then(|${0:_}| Some("bar")); } "#, ) diff --git a/crates/ide-assists/src/handlers/wrap_return_type.rs b/crates/ide-assists/src/handlers/wrap_return_type.rs index f9c103aab8..032cc28858 100644 --- a/crates/ide-assists/src/handlers/wrap_return_type.rs +++ b/crates/ide-assists/src/handlers/wrap_return_type.rs @@ -92,7 +92,7 @@ pub(crate) fn wrap_return_type(acc: &mut Assists, ctx: &AssistContext<'_, '_>) - }; let semantic_ty = ty_constructor .map(|ty_constructor| { - hir::Adt::from(ty_constructor).ty_with_args(ctx.db(), [ty.clone()]) + hir::Adt::from(ty_constructor).ty(ctx.db()).instantiate([ty.clone()]) }) .unwrap_or_else(|| ty.clone()); (ast_ty, semantic_ty) @@ -256,7 +256,7 @@ fn wrapper_alias<'db>( ); let new_ty = - hir::Adt::from(enum_ty).ty_with_args(ctx.db(), [semantic_ret_type.clone()]); + hir::Adt::from(enum_ty).ty(ctx.db()).instantiate([semantic_ret_type.clone()]); Some((make.ty_path(path), new_ty)) }) diff --git a/crates/ide-assists/src/tests/generated.rs b/crates/ide-assists/src/tests/generated.rs index d3ee35aa86..15572e5529 100644 --- a/crates/ide-assists/src/tests/generated.rs +++ b/crates/ide-assists/src/tests/generated.rs @@ -204,8 +204,8 @@ fn main() { "#####, r#####" fn main() { - ${1:'l}: loop { - break ${2:'l}; + ${0:'l}: loop { + break ${0:'l}; continue ${0:'l}; } } diff --git a/crates/ide-assists/src/utils.rs b/crates/ide-assists/src/utils.rs index 096f6678a5..1b6c9a579a 100644 --- a/crates/ide-assists/src/utils.rs +++ b/crates/ide-assists/src/utils.rs @@ -417,6 +417,7 @@ fn check_pat_variant_nested_or_literal_with_depth( | ast::Pat::PathPat(_) | ast::Pat::BoxPat(_) | ast::Pat::DerefPat(_) + | ast::Pat::NotNull(_) | ast::Pat::ConstBlockPat(_) => true, ast::Pat::IdentPat(ident_pat) => ident_pat.pat().is_some_and(|pat| { @@ -841,8 +842,8 @@ pub(crate) fn convert_reference_type<'db>( } fn could_deref_to_target(ty: &hir::Type<'_>, target: &hir::Type<'_>, db: &dyn HirDatabase) -> bool { - let ty_ref = ty.add_reference(hir::Mutability::Shared); - let target_ref = target.add_reference(hir::Mutability::Shared); + let ty_ref = ty.add_reference(db, hir::Mutability::Shared); + let target_ref = target.add_reference(db, hir::Mutability::Shared); ty_ref.could_coerce_to(db, &target_ref) } @@ -870,7 +871,7 @@ fn handle_as_ref_slice( famous_defs: &FamousDefs<'_, '_>, ) -> Option<(ReferenceConversionType, bool)> { let type_argument = ty.type_arguments().next()?; - let slice_type = hir::Type::new_slice(type_argument); + let slice_type = hir::Type::new_slice(db, type_argument); ty.impls_trait(db, famous_defs.core_convert_AsRef()?, slice::from_ref(&slice_type)).then_some(( ReferenceConversionType::AsRefSlice, @@ -1181,13 +1182,5 @@ pub(crate) fn is_never_block( sema: &Semantics<'_, RootDatabase>, block_expr: &ast::BlockExpr, ) -> bool { - if let Some(tail_expr) = block_expr.tail_expr() { - sema.type_of_expr(&tail_expr).is_some_and(|ty| ty.original.is_never()) - } else if let Some(ast::Stmt::ExprStmt(expr_stmt)) = block_expr.statements().last() - && let Some(expr) = expr_stmt.expr() - { - sema.type_of_expr(&expr).is_some_and(|ty| ty.original.is_never()) - } else { - false - } + sema.expr_is_diverging(&block_expr.clone().into()) } diff --git a/crates/ide-completion/src/completions.rs b/crates/ide-completion/src/completions.rs index f3190bbbc8..20048ea97b 100644 --- a/crates/ide-completion/src/completions.rs +++ b/crates/ide-completion/src/completions.rs @@ -199,10 +199,10 @@ impl Completions { item.add_to(self, ctx.db); } - pub(crate) fn add_expr( + pub(crate) fn add_expr<'db>( &mut self, - ctx: &CompletionContext<'_, '_>, - expr: &hir::term_search::Expr<'_>, + ctx: &CompletionContext<'_, 'db>, + expr: &hir::term_search::Expr<'db>, ) { if let Some(item) = render_expr(ctx, expr) { item.add_to(self, ctx.db) diff --git a/crates/ide-completion/src/completions/dot.rs b/crates/ide-completion/src/completions/dot.rs index 2cc2200df9..59c6c55c22 100644 --- a/crates/ide-completion/src/completions/dot.rs +++ b/crates/ide-completion/src/completions/dot.rs @@ -1,9 +1,9 @@ //! Completes references after dot (fields and method calls). -use std::ops::ControlFlow; +use std::{collections::hash_map, ops::ControlFlow}; -use hir::{Complete, Function, HasContainer, ItemContainer, MethodCandidateCallback}; -use ide_db::FxHashSet; +use hir::{Complete, Function, HasContainer, ItemContainer, MethodCandidateCallback, Name}; +use ide_db::{FxHashMap, FxHashSet}; use itertools::Either; use syntax::SmolStr; @@ -93,7 +93,7 @@ pub(crate) fn complete_dot( // Does <&receiver_ty as IntoIterator>::IntoIter` exist? Assume `iter` is valid let iter = receiver_ty .autoderef(ctx.db) - .map(|ty| ty.strip_references().add_reference(hir::Mutability::Shared)) + .map(|ty| ty.strip_references().add_reference(ctx.db, hir::Mutability::Shared)) .find_map(|ty| ty.into_iterator_iter(ctx.db)) .map(|ty| (ty, SmolStr::new_static("iter()"))); // Does <receiver_ty as IntoIterator>::IntoIter` exist? @@ -239,6 +239,9 @@ fn complete_methods( // duplicated, trait methods can. And it is still useful to show all of them (even when there // is also an inherent method, especially considering that it may be private, and filtered later). seen_methods: FxHashSet<Function>, + // However, duplicate inherent methods is usually meaningless + // https://github.com/rust-lang/rust-analyzer/issues/20773#issuecomment-4302781553 + seen_inherent_methods: FxHashMap<Name, Function>, } impl<F> MethodCandidateCallback for Callback<'_, '_, F> @@ -249,7 +252,21 @@ fn complete_methods( // `where` clauses or `dyn Trait`. fn on_inherent_method(&mut self, func: hir::Function) -> ControlFlow<()> { if func.self_param(self.ctx.db).is_some() && self.seen_methods.insert(func) { - (self.f)(func); + let same_name = self.seen_inherent_methods.entry(func.name(self.ctx.db)); + let do_complete = match &same_name { + hash_map::Entry::Vacant(_) => true, + hash_map::Entry::Occupied(same_func) => { + match self.ctx.is_visible(same_func.get()) { + crate::context::Visible::Yes => false, + crate::context::Visible::Editable => true, + crate::context::Visible::No => true, + } + } + }; + if do_complete { + same_name.insert_entry(func); + (self.f)(func); + } } ControlFlow::Continue(()) } @@ -277,7 +294,12 @@ fn complete_methods( &ctx.scope, traits_in_scope, None, - Callback { ctx, f, seen_methods: FxHashSet::default() }, + Callback { + ctx, + f, + seen_methods: FxHashSet::default(), + seen_inherent_methods: FxHashMap::default(), + }, ); } @@ -870,6 +892,86 @@ fn test(a: A) { } #[test] + fn test_inherent_method_no_same_name() { + check_no_kw( + r#" +//- minicore: deref +struct A {} +struct B {} +impl core::ops::Deref for A { + type Target = B; + fn deref(&self) -> &Self::Target { loop {} } +} +trait Foo { fn foo(&self) -> u32 {} } +impl Foo for A {} +impl Foo for B {} +impl A { fn foo(&self) -> u8 {} } +impl B { fn foo(&self) -> u16 {} } +fn test(a: A) { + a.$0 +} +"#, + expect![[r#" + me deref() (use core::ops::Deref) fn(&self) -> &<Self as Deref>::Target + me foo() fn(&self) -> u8 + me foo() (as Foo) fn(&self) -> u32 + "#]], + ); + + check_no_kw( + r#" +//- minicore: deref +//- /dep.rs crate:dep +pub struct A {} +pub struct B {} +pub struct C {} +pub struct D {} +pub struct E {} +pub struct F {} +impl core::ops::Deref for A { + type Target = B; + fn deref(&self) -> &Self::Target { loop {} } +} +impl core::ops::Deref for B { + type Target = C; + fn deref(&self) -> &Self::Target { loop {} } +} +impl core::ops::Deref for C { + type Target = D; + fn deref(&self) -> &Self::Target { loop {} } +} +impl core::ops::Deref for D { + type Target = E; + fn deref(&self) -> &Self::Target { loop {} } +} +impl core::ops::Deref for E { + type Target = F; + fn deref(&self) -> &Self::Target { loop {} } +} +pub trait Foo { fn foo(&self) -> u32 {} } +impl Foo for A {} +impl Foo for B {} +impl A { fn foo(&self) -> u8 {} } +impl B { pub fn foo(&self) -> u16 {} } +impl C { fn foo(&self) -> i8 {} } +impl D { fn foo(&self) -> i16 {} } +impl E { pub fn foo(&self) -> i32 {} } +impl F { pub fn foo(&self) -> f32 {} } +//- /main.rs crate:main deps:dep +use dep::*; +fn test(a: A) { + a.$0 +} +"#, + expect![[r#" + me deref() (use core::ops::Deref) fn(&self) -> &<Self as Deref>::Target + me foo() fn(&self) -> u16 + me foo() (as Foo) fn(&self) -> u32 + "#]], + ); + } + + #[test] fn test_completion_works_in_consts() { check_no_kw( r#" diff --git a/crates/ide-completion/src/completions/expr.rs b/crates/ide-completion/src/completions/expr.rs index a2a4cbac21..8d906064c0 100644 --- a/crates/ide-completion/src/completions/expr.rs +++ b/crates/ide-completion/src/completions/expr.rs @@ -182,6 +182,9 @@ pub(crate) fn complete_expr_path( } _ => return, }; + // Note: this is not *required* here, we do it to also find methods that require + // the type to be instantiated with specific types. + let ty = ty.instantiate_with_errors(); if let Some(hir::Adt::Enum(e)) = ty.as_adt() { cov_mark::hit!(completes_variant_through_alias); @@ -317,12 +320,7 @@ pub(crate) fn complete_expr_path( } // synthetic names currently leak out as we lack synthetic hygiene, so filter them // out here - ScopeDef::Local(_) => - { - #[expect( - clippy::collapsible_match, - reason = "this changes meaning, causing the next arm to be selected" - )] + ScopeDef::Local(_) => { if !name.as_str().starts_with('<') { acc.add_path_resolution(ctx, path_ctx, name, def, doc_aliases) } diff --git a/crates/ide-completion/src/completions/postfix.rs b/crates/ide-completion/src/completions/postfix.rs index 0cb39dd108..540089cf91 100644 --- a/crates/ide-completion/src/completions/postfix.rs +++ b/crates/ide-completion/src/completions/postfix.rs @@ -259,7 +259,7 @@ pub(crate) fn complete_postfix( postfix_snippet( "lete", "let Ok else {}", - format!("let Ok({placeholder}) = {receiver_text} else {{\n $2\n}};\n$0"), + format!("let Ok({placeholder}) = {receiver_text} else {{\n $2\n}};$0"), ) .add_to(acc, ctx.db); @@ -281,9 +281,7 @@ pub(crate) fn complete_postfix( postfix_snippet( "lete", "let Some else {}", - format!( - "let Some({placeholder}) = {receiver_text} else {{\n $2\n}};\n$0" - ), + format!("let Some({placeholder}) = {receiver_text} else {{\n $2\n}};$0"), ) .add_to(acc, ctx.db); @@ -1101,8 +1099,28 @@ fn main() { let bar = Some(true); let Some(${1:bar}) = bar else { $2 -}; -$0 +};$0 +} +"#, + ); + + check_edit( + "lete", + r#" +//- minicore: option +fn main() { + let bar = Some(true); + bar.$0 + other(); +} +"#, + r#" +fn main() { + let bar = Some(true); + let Some(${1:bar}) = bar else { + $2 +};$0 + other(); } "#, ); diff --git a/crates/ide-completion/src/completions/record.rs b/crates/ide-completion/src/completions/record.rs index 1238a91dad..16e89bd0de 100644 --- a/crates/ide-completion/src/completions/record.rs +++ b/crates/ide-completion/src/completions/record.rs @@ -1,7 +1,8 @@ //! Complete fields in record literals and patterns. use ide_db::SymbolKind; use syntax::{ - SmolStr, + SmolStr, T, + algo::next_non_trivia_token, ast::{self, Expr}, }; @@ -28,11 +29,7 @@ pub(crate) fn complete_record_pattern_fields( record_pat.record_pat_field_list().and_then(|fl| fl.fields().next()).is_some(); match were_fields_specified { - false => un - .fields(ctx.db) - .into_iter() - .map(|f| (f, f.ty(ctx.db).to_type(ctx.db))) - .collect(), + false => un.fields(ctx.db).into_iter().map(|f| (f, f.ty(ctx.db))).collect(), true => return, } } @@ -60,11 +57,7 @@ pub(crate) fn complete_record_expr_fields( record_expr.record_expr_field_list().and_then(|fl| fl.fields().next()).is_some(); match were_fields_specified { - false => un - .fields(ctx.db) - .into_iter() - .map(|f| (f, f.ty(ctx.db).to_type(ctx.db))) - .collect(), + false => un.fields(ctx.db).into_iter().map(|f| (f, f.ty(ctx.db))).collect(), true => return, } } @@ -105,7 +98,9 @@ pub(crate) fn add_default_update( let impls_default_trait = default_trait .zip(ty) .is_some_and(|(default_trait, ty)| ty.original.impls_trait(ctx.db, default_trait, &[])); - if impls_default_trait { + let ends_of_record_list = + next_non_trivia_token(ctx.token.clone()).is_none_or(|it| it.kind() == T!['}']); + if impls_default_trait && ends_of_record_list { // FIXME: This should make use of scope_def like completions so we get all the other goodies // that is we should handle this like actually completing the default function let completion_text = "..Default::default()"; diff --git a/crates/ide-completion/src/config.rs b/crates/ide-completion/src/config.rs index 21f624be2c..ac52c816e5 100644 --- a/crates/ide-completion/src/config.rs +++ b/crates/ide-completion/src/config.rs @@ -44,6 +44,7 @@ pub struct CompletionConfig<'a> { pub enum AutoImportExclusionType { Always, Methods, + SubItems, } #[derive(Clone, Debug, PartialEq, Eq)] diff --git a/crates/ide-completion/src/context.rs b/crates/ide-completion/src/context.rs index f7fced3f06..f7bb14391b 100644 --- a/crates/ide-completion/src/context.rs +++ b/crates/ide-completion/src/context.rs @@ -611,7 +611,7 @@ impl<'db> CompletionContext<'_, 'db> { let Some(unstable_feature) = attrs.unstable_feature(self.db) else { return true; }; - !INTERNAL_FEATURES.contains(&unstable_feature) + !is_internal_feature(&unstable_feature) || self.krate.is_unstable_feature_enabled(self.db, &unstable_feature) } @@ -729,6 +729,13 @@ impl<'db> CompletionContext<'_, 'db> { vec![] } } + + pub(crate) fn rebase_ty(&self, ty: &hir::Type<'db>) -> hir::Type<'db> { + self.scope + .generic_def() + .and_then(|def| ty.try_rebase_into_owner(self.db, def)) + .unwrap_or_else(|| ty.instantiate_with_errors()) + } } // CompletionContext construction @@ -848,8 +855,23 @@ impl<'a, 'db> CompletionContext<'a, 'db> { .map(|it| (it.into_module_def(), *kind)) }) .collect(); + let exclude_subitems = exclude_flyimport + .iter() + .flat_map(|it| match it { + (ModuleDef::Module(module), AutoImportExclusionType::SubItems) => { + module.scope(db, None) + } + _ => vec![], + }) + .filter_map(|(_, def)| match def { + ScopeDef::ModuleDef(module_def) => Some(module_def), + _ => None, + }) + .collect::<Vec<_>>(); exclude_flyimport .extend(exclude_traits.iter().map(|&t| (t.into(), AutoImportExclusionType::Always))); + exclude_flyimport + .extend(exclude_subitems.into_iter().map(|it| (it, AutoImportExclusionType::Always))); // FIXME: This should be part of `CompletionAnalysis` / `expand_and_analyze` let complete_semicolon = if !config.add_semicolon_to_unit { @@ -858,7 +880,13 @@ impl<'a, 'db> CompletionContext<'a, 'db> { sema.token_ancestors_with_macros(token.clone()).find(|node| { matches!( node.kind(), - BLOCK_EXPR | MATCH_ARM | CLOSURE_EXPR | ARG_LIST | PAREN_EXPR | ARRAY_EXPR + BLOCK_EXPR + | MATCH_ARM + | CLOSURE_EXPR + | ARG_LIST + | PAREN_EXPR + | ARRAY_EXPR + | MATCH_EXPR ) }) { @@ -949,6 +977,7 @@ const INTERNAL_FEATURES_LIST: &[Symbol] = &[ sym::eii_internals, sym::field_representing_type_raw, sym::intrinsics, + sym::core_intrinsics, sym::lang_items, sym::link_cfg, sym::more_maybe_bounds, @@ -972,3 +1001,12 @@ const INTERNAL_FEATURES_LIST: &[Symbol] = &[ static INTERNAL_FEATURES: LazyLock<FxHashSet<Symbol>> = LazyLock::new(|| INTERNAL_FEATURES_LIST.iter().cloned().collect()); + +fn is_internal_feature(feature: &Symbol) -> bool { + if INTERNAL_FEATURES.contains(feature) { + return true; + } + // Libs features are internal if they end in `_internal` or `_internals`. + let feature = feature.as_str(); + feature.ends_with("_internal") || feature.ends_with("_internals") +} diff --git a/crates/ide-completion/src/context/analysis.rs b/crates/ide-completion/src/context/analysis.rs index faeb97f93f..b2ee94d49c 100644 --- a/crates/ide-completion/src/context/analysis.rs +++ b/crates/ide-completion/src/context/analysis.rs @@ -641,13 +641,23 @@ fn expected_type_and_name<'db>( } for _ in refs_level..0 { cov_mark::hit!(expected_type_fn_param_deref); - ty = ty.add_reference(hir::Mutability::Shared); + ty = ty.add_reference(sema.db, hir::Mutability::Shared); } ty } _ => ty, }; + let mut generic_def = None; + let mut rebase_ty = { + let node = node.clone(); + move |ty: hir::Type<'db>| { + let def = *generic_def + .get_or_insert_with(|| sema.scope(&node).and_then(|scope| scope.generic_def())); + def.and_then(|def| ty.try_rebase_into_owner(sema.db, def)) + .unwrap_or_else(|| ty.instantiate_with_errors()) + } + }; let (ty, name) = loop { break match_ast! { match node { @@ -791,26 +801,22 @@ fn expected_type_and_name<'db>( (ty, None) }, ast::TupleStructPat(it) => { - let fields = it.path().and_then(|path| match sema.resolve_path(&path)? { - hir::PathResolution::Def(hir::ModuleDef::Adt(adt)) => Some(adt.as_struct()?.fields(sema.db)), - hir::PathResolution::Def(hir::ModuleDef::EnumVariant(variant)) => Some(variant.fields(sema.db)), - _ => None, - }); + let fields = sema.resolve_tuple_struct_pat_fields(&it); let nr = it.fields().take_while(|it| it.syntax().text_range().end() <= token.text_range().start()).count(); - let ty = fields.and_then(|fields| Some(fields.get(nr)?.ty(sema.db).to_type(sema.db))); + let ty = fields.and_then(|fields| Some(rebase_ty(fields.get(nr)?.1.clone()))); (ty, None) }, ast::Fn(it) => { cov_mark::hit!(expected_type_fn_ret_with_leading_char); cov_mark::hit!(expected_type_fn_ret_without_leading_char); let def = sema.to_def(&it); - (def.map(|def| def.ret_type(sema.db)), None) + (def.map(|def| rebase_ty(def.ret_type(sema.db))), None) }, ast::ReturnExpr(it) => { let fn_ = sema.ancestors_with_macros(it.syntax().clone()) .find_map(Either::<ast::Fn, ast::ClosureExpr>::cast); let ty = fn_.and_then(|f| match f { - Either::Left(f) => Some(sema.to_def(&f)?.ret_type(sema.db)), + Either::Left(f) => Some(rebase_ty(sema.to_def(&f)?.ret_type(sema.db))), Either::Right(f) => { let ty = sema.type_of_expr(&f.into())?.original.as_callable(sema.db)?; Some(ty.return_type()) diff --git a/crates/ide-completion/src/context/tests.rs b/crates/ide-completion/src/context/tests.rs index 94d904932a..1d1a55c6fe 100644 --- a/crates/ide-completion/src/context/tests.rs +++ b/crates/ide-completion/src/context/tests.rs @@ -329,6 +329,27 @@ fn foo(x: Foo) -> Foo { "#, expect![[r#"ty: u32, name: ?"#]], ); + + check_expected_type_and_name( + r#" +//- minicore: option +struct Foo<T>(T); +fn foo(x: Foo<Option<i32>>) -> Foo { + match x { Foo($0) => () } +} +"#, + expect![[r#"ty: Option<i32>, name: ?"#]], + ); + check_expected_type_and_name( + r#" +//- minicore: option +enum Foo<T> { Var(T) }; +fn foo(x: Foo<Option<i32>>) -> Foo { + match x { Foo::Var($0) => () } +} +"#, + expect![[r#"ty: Option<i32>, name: ?"#]], + ); } #[test] diff --git a/crates/ide-completion/src/item.rs b/crates/ide-completion/src/item.rs index cfadec6287..61281f8cfb 100644 --- a/crates/ide-completion/src/item.rs +++ b/crates/ide-completion/src/item.rs @@ -181,6 +181,9 @@ pub struct CompletionRelevance { /// } /// ``` pub is_local: bool, + /// This is missing variant in the patterns. + /// Maybe this can also be used for struct fields. + pub is_missing: bool, /// Populated when the completion item comes from a trait (impl). pub trait_: Option<CompletionRelevanceTraitInfo>, /// This is set when an import is suggested in a use item whose name is already imported. @@ -286,6 +289,7 @@ impl CompletionRelevance { exact_name_match, type_match, is_local, + is_missing, is_name_already_imported, requires_import, is_private_editable, @@ -300,16 +304,19 @@ impl CompletionRelevance { // only applicable for completions within use items // lower rank for conflicting import names if is_name_already_imported { - score -= 1; + score -= 15; } // slightly prefer locals if is_local { - score += 1; + score += 2; + } + if is_missing { + score += 2; } // lower rank private things if !is_private_editable { - score += 1; + score += 10; } if let Some(trait_) = trait_ { @@ -330,10 +337,10 @@ impl CompletionRelevance { // lower rank for items that need an import if requires_import { - score -= 1; + score -= 12; } if exact_name_match { - score += 20; + score += 40; } match postfix_match { Some(CompletionRelevancePostfixMatch::Exact) => score += 100, @@ -341,16 +348,26 @@ impl CompletionRelevance { None => (), }; score += match type_match { - Some(CompletionRelevanceTypeMatch::Exact) => 18, - Some(CompletionRelevanceTypeMatch::CouldUnify) => 5, + Some(CompletionRelevanceTypeMatch::Exact) => 35, + Some(CompletionRelevanceTypeMatch::CouldUnify) => 15, None => 0, }; if let Some(function) = function { - let mut fn_score = match function.return_type { - CompletionRelevanceReturnType::DirectConstructor => 15, - CompletionRelevanceReturnType::Builder => 10, - CompletionRelevanceReturnType::Constructor => 5, - CompletionRelevanceReturnType::Other => 0u32, + let mut fn_score = if requires_import { + // Rank constructors that require imports lower than those who don't. + match function.return_type { + CompletionRelevanceReturnType::DirectConstructor => 8, + CompletionRelevanceReturnType::Builder => 5, + CompletionRelevanceReturnType::Constructor => 3, + CompletionRelevanceReturnType::Other => 0u32, + } + } else { + match function.return_type { + CompletionRelevanceReturnType::DirectConstructor => 15, + CompletionRelevanceReturnType::Builder => 10, + CompletionRelevanceReturnType::Constructor => 5, + CompletionRelevanceReturnType::Other => 0u32, + } }; // When a fn is bumped due to return type: @@ -368,12 +385,12 @@ impl CompletionRelevance { }; if has_local_inherent_impl { - score -= 5; + score -= 8; } // lower rank for deprecated items if is_deprecated { - score -= 5; + score -= 15; } score @@ -824,15 +841,17 @@ mod tests { is_private_editable: true, ..default }], - vec![Cr { - trait_: Some(crate::item::CompletionRelevanceTraitInfo { - notable_trait: false, - is_op_method: true, - }), - ..default - }], + vec![ + Cr { + trait_: Some(crate::item::CompletionRelevanceTraitInfo { + notable_trait: false, + is_op_method: true, + }), + ..default + }, + Cr { is_private_editable: true, ..default }, + ], vec![Cr { postfix_match: Some(CompletionRelevancePostfixMatch::NonExact), ..default }], - vec![Cr { is_private_editable: true, ..default }], vec![default], vec![Cr { is_local: true, ..default }], vec![Cr { type_match: Some(CompletionRelevanceTypeMatch::CouldUnify), ..default }], diff --git a/crates/ide-completion/src/render.rs b/crates/ide-completion/src/render.rs index fbbdffefe3..e48847c983 100644 --- a/crates/ide-completion/src/render.rs +++ b/crates/ide-completion/src/render.rs @@ -71,7 +71,7 @@ impl<'a, 'db> RenderContext<'a, 'db> { self.completion.config.snippet_cap } - fn db(&self) -> &'a RootDatabase { + fn db(&self) -> &'db RootDatabase { self.completion.db } @@ -181,9 +181,9 @@ pub(crate) fn render_field( if !expected_fn_type && let Some(receiver) = &dot_access.receiver - && let Some(receiver) = ctx.completion.sema.original_ast_node(receiver.clone()) + && let Some(receiver) = ctx.completion.sema.original_range_opt(receiver.syntax()) { - builder.insert(receiver.syntax().text_range().start(), "(".to_owned()); + builder.insert(receiver.range.start(), "(".to_owned()); builder.insert(ctx.source_range().end(), ")".to_owned()); let is_parens_needed = !matches!(dot_access.kind, DotAccessKind::Method); @@ -198,10 +198,10 @@ pub(crate) fn render_field( item.insert_text(field_with_receiver(receiver.as_deref(), &escaped_name)); } if let Some(receiver) = &dot_access.receiver - && let Some(original) = ctx.completion.sema.original_ast_node(receiver.clone()) + && let Some(original) = ctx.completion.sema.original_range_opt(receiver.syntax()) && let Some(ref_mode) = compute_ref_match(ctx.completion, ty) { - item.ref_match(ref_mode, original.syntax().text_range().start()); + item.ref_match(ref_mode, original.range.start()); } item.doc_aliases(ctx.doc_aliases); item.build(db) @@ -294,9 +294,9 @@ pub(crate) fn render_resolution_with_import_pat( Some(render_resolution_pat(ctx, pattern_ctx, local_name, Some(import_edit), resolution)) } -pub(crate) fn render_expr( - ctx: &CompletionContext<'_, '_>, - expr: &hir::term_search::Expr<'_>, +pub(crate) fn render_expr<'db>( + ctx: &CompletionContext<'_, 'db>, + expr: &hir::term_search::Expr<'db>, ) -> Option<Builder> { let mut i = 1; let mut snippet_formatter = |ty: &hir::Type<'_>| { @@ -341,7 +341,7 @@ pub(crate) fn render_expr( "Autogenerated expression by term search", ))); item.set_relevance(crate::CompletionRelevance { - type_match: compute_type_match(ctx, &expr.ty(ctx.db)), + type_match: compute_type_match(ctx, &ctx.rebase_ty(&expr.ty(ctx.db))), ..Default::default() }); for trait_ in expr.traits_used(ctx.db) { @@ -405,8 +405,8 @@ fn render_resolution_pat( } } -fn render_resolution_path( - ctx: RenderContext<'_, '_>, +fn render_resolution_path<'db>( + ctx: RenderContext<'_, 'db>, path_ctx: &PathCompletionCtx<'_>, local_name: hir::Name, import_to_add: Option<LocatedImport>, @@ -471,18 +471,21 @@ fn render_resolution_path( .insert_snippet(cap, ""); // set is snippet } } - let allow_module_path = matches!(path_ctx.kind, PathKind::Use) || !config.add_colons_to_module; + let allow_module_path = matches!(path_ctx.kind, PathKind::Use) + || completion.token.next_token().is_some_and(|it| it.kind() == syntax::T![::]) + || !config.add_colons_to_module; if !allow_module_path && matches!(resolution, ScopeDef::ModuleDef(Module(_))) { insert_text = format_smolstr!("{insert_text}::"); item.lookup_by(name.clone()).label(insert_text.clone()); } adds_ret_type_arrow(completion, path_ctx, &mut item, insert_text.into()); - let mut set_item_relevance = |ty: Type<'_>| { + let mut set_item_relevance = |ty: Type<'db>| { if !ty.is_unknown() { item.detail(ty.display(db, krate).to_string()); } + let ty = completion.rebase_ty(&ty); item.set_relevance(CompletionRelevance { type_match: compute_type_match(completion, &ty), exact_name_match: compute_exact_name_match(completion, &name), @@ -492,7 +495,13 @@ fn render_resolution_path( ..CompletionRelevance::default() }); - path_ref_match(completion, path_ctx, &ty, &mut item); + match resolution { + ScopeDef::Local(_) + | ScopeDef::ModuleDef(ModuleDef::Const(_) | ModuleDef::Static(_)) => { + path_ref_match(completion, path_ctx, &ty, &mut item) + } + _ => (), + } }; match resolution { @@ -680,8 +689,8 @@ fn compute_type_match( // &mut ty -> &ty if completion_ty.is_mutable_reference() - && let Some(expected_type) = expected_type.remove_ref() - && let Some(completion_ty) = completion_ty.remove_ref() + && let Some((expected_type, _)) = expected_type.as_reference() + && let Some((completion_ty, _)) = completion_ty.as_reference() { return match_types(ctx, &expected_type, &completion_ty); } @@ -709,18 +718,19 @@ fn compute_ref_match( ctx: &CompletionContext<'_, '_>, completion_ty: &hir::Type<'_>, ) -> Option<CompletionItemRefMode> { - let expected_type = ctx.expected_type.as_ref()?; - let expected_without_ref = expected_type.remove_ref(); - let completion_without_ref = completion_ty.remove_ref(); - if expected_type.could_unify_with(ctx.db, completion_ty) { + if compute_type_match(ctx, completion_ty).is_some() || completion_ty.is_unit() { return None; } - if let Some(expected_without_ref) = &expected_without_ref + let expected_type = ctx.expected_type.as_ref()?; + let expected_without_ref = expected_type.as_reference(); + let completion_without_ref = completion_ty.as_reference(); + + if let Some((expected_without_ref, _)) = &expected_without_ref && (completion_without_ref.is_none() || completion_ty.could_unify_with(ctx.db, expected_without_ref)) && completion_ty .autoderef(ctx.db) - .any(|ty| ty.could_unify_with(ctx.db, expected_without_ref)) + .any(|ty| !ty.is_unknown() && ty.could_unify_with(ctx.db, expected_without_ref)) { cov_mark::hit!(suggest_ref); let mutability = if expected_type.is_mutable_reference() { @@ -731,7 +741,7 @@ fn compute_ref_match( return Some(CompletionItemRefMode::Reference(mutability)); } - if let Some(completion_without_ref) = completion_without_ref + if let Some((completion_without_ref, _)) = completion_without_ref && completion_without_ref == *expected_type && completion_without_ref.is_copy(ctx.db) { @@ -750,10 +760,10 @@ fn path_ref_match( ) { if let Some(original_path) = &path_ctx.original_path { // At least one char was typed by the user already, in that case look for the original path - if let Some(original_path) = completion.sema.original_ast_node(original_path.clone()) + if let Some(original_path) = completion.sema.original_range_opt(original_path.syntax()) && let Some(ref_mode) = compute_ref_match(completion, ty) { - item.ref_match(ref_mode, original_path.syntax().text_range().start()); + item.ref_match(ref_mode, original_path.range.start()); } } else { // completion requested on an empty identifier, there is no path here yet. @@ -865,6 +875,7 @@ mod tests { exact_name_match, type_match, is_local, + is_missing, trait_, is_name_already_imported: _, requires_import, @@ -880,6 +891,7 @@ mod tests { (type_match == Some(CompletionRelevanceTypeMatch::CouldUnify), "type_could_unify"), (exact_name_match, "name"), (is_local, "local"), + (is_missing, "missing"), (postfix_match == Some(CompletionRelevancePostfixMatch::Exact), "snippet"), (trait_.is_some_and(|it| it.is_op_method), "op_method"), (requires_import, "requires_import"), @@ -944,9 +956,9 @@ fn main() { } "#, expect![[r#" - st dep::test_mod_b::Struct {…} dep::test_mod_b::Struct { } [type_could_unify] - ex dep::test_mod_b::Struct { } [type_could_unify] - st Struct Struct [type_could_unify+requires_import] + st dep::test_mod_b::Struct {…} dep::test_mod_b::Struct { } [type] + ex dep::test_mod_b::Struct { } [type] + st Struct Struct [type+requires_import] md dep:: [] fn main() fn() [] fn test(…) fn(Struct) [] @@ -984,7 +996,7 @@ fn main() { } "#, expect![[r#" - un Union Union [type_could_unify+requires_import] + un Union Union [type+requires_import] md dep:: [] fn main() fn() [] fn test(…) fn(Union) [] @@ -1020,9 +1032,9 @@ fn main() { } "#, expect![[r#" - ev dep::test_mod_b::Enum::variant dep::test_mod_b::Enum::variant [type_could_unify] - ex dep::test_mod_b::Enum::variant [type_could_unify] - en Enum Enum [type_could_unify+requires_import] + ev dep::test_mod_b::Enum::variant dep::test_mod_b::Enum::variant [type] + ex dep::test_mod_b::Enum::variant [type] + en Enum Enum [type+requires_import] md dep:: [] fn main() fn() [] fn test(…) fn(Enum) [] @@ -1058,11 +1070,13 @@ fn main() { } "#, expect![[r#" - ev dep::test_mod_b::Enum::Variant dep::test_mod_b::Enum::Variant [type_could_unify] - ex dep::test_mod_b::Enum::Variant [type_could_unify] + ev dep::test_mod_b::Enum::Variant dep::test_mod_b::Enum::Variant [type] + ex dep::test_mod_b::Enum::Variant [type] + ev Variant Variant [type+requires_import] md dep:: [] fn main() fn() [] fn test(…) fn(Enum) [] + ev Variant Variant [requires_import] "#]], ); } @@ -1122,7 +1136,7 @@ fn main() { } "#, expect![[r#" - ct CONST i32 [type_could_unify+requires_import] + ct CONST i32 [type+requires_import] md dep:: [] fn main() fn() [] fn test(…) fn(i32) [] @@ -1154,7 +1168,7 @@ fn main() { } "#, expect![[r#" - sc STATIC i32 [type_could_unify+requires_import] + sc STATIC i32 [type+requires_import] md dep:: [] fn main() fn() [] fn test(…) fn(i32) [] @@ -1281,6 +1295,7 @@ fn main() { Foo::Fo$0 } exact_name_match: false, type_match: None, is_local: false, + is_missing: false, trait_: None, is_name_already_imported: false, requires_import: false, @@ -1333,6 +1348,7 @@ fn main() { Foo::Fo$0 } exact_name_match: false, type_match: None, is_local: false, + is_missing: false, trait_: None, is_name_already_imported: false, requires_import: false, @@ -1478,6 +1494,7 @@ fn main() { Foo::Fo$0 } exact_name_match: false, type_match: None, is_local: false, + is_missing: false, trait_: None, is_name_already_imported: false, requires_import: false, @@ -1564,6 +1581,7 @@ fn main() { let _: m::Spam = S$0 } Exact, ), is_local: false, + is_missing: false, trait_: None, is_name_already_imported: false, requires_import: false, @@ -1602,6 +1620,7 @@ fn main() { let _: m::Spam = S$0 } Exact, ), is_local: false, + is_missing: false, trait_: None, is_name_already_imported: false, requires_import: false, @@ -1653,6 +1672,7 @@ fn main() { som$0 } exact_name_match: false, type_match: None, is_local: false, + is_missing: false, trait_: None, is_name_already_imported: false, requires_import: false, @@ -1712,6 +1732,7 @@ fn main() { som$0 } exact_name_match: false, type_match: None, is_local: false, + is_missing: false, trait_: None, is_name_already_imported: false, requires_import: false, @@ -1755,6 +1776,7 @@ fn main() { A$0 } exact_name_match: false, type_match: None, is_local: false, + is_missing: false, trait_: None, is_name_already_imported: false, requires_import: false, @@ -1798,6 +1820,7 @@ fn main() { A$0 } exact_name_match: false, type_match: None, is_local: false, + is_missing: false, trait_: None, is_name_already_imported: false, requires_import: false, @@ -1843,6 +1866,7 @@ fn main() { A::$0 } exact_name_match: false, type_match: None, is_local: false, + is_missing: false, trait_: None, is_name_already_imported: false, requires_import: false, @@ -1879,6 +1903,7 @@ fn main() { A::$0 } exact_name_match: false, type_match: None, is_local: false, + is_missing: false, trait_: None, is_name_already_imported: false, requires_import: false, @@ -1929,6 +1954,7 @@ fn main() { A$0 } exact_name_match: false, type_match: None, is_local: false, + is_missing: false, trait_: None, is_name_already_imported: false, requires_import: false, @@ -1972,6 +1998,7 @@ fn main() { A$0 } exact_name_match: false, type_match: None, is_local: false, + is_missing: false, trait_: None, is_name_already_imported: false, requires_import: false, @@ -2012,6 +2039,7 @@ impl A$0 exact_name_match: false, type_match: None, is_local: false, + is_missing: false, trait_: None, is_name_already_imported: false, requires_import: false, @@ -2052,6 +2080,7 @@ fn main() { A$0 } exact_name_match: false, type_match: None, is_local: false, + is_missing: false, trait_: None, is_name_already_imported: false, requires_import: false, @@ -2096,6 +2125,7 @@ fn main() { a$0 } exact_name_match: false, type_match: None, is_local: false, + is_missing: false, trait_: None, is_name_already_imported: false, requires_import: false, @@ -2140,6 +2170,7 @@ fn main() { A { the$0 } } CouldUnify, ), is_local: false, + is_missing: false, trait_: None, is_name_already_imported: false, requires_import: false, @@ -2195,6 +2226,7 @@ impl S { exact_name_match: false, type_match: None, is_local: false, + is_missing: false, trait_: None, is_name_already_imported: false, requires_import: false, @@ -2288,6 +2320,7 @@ use self::E::*; exact_name_match: false, type_match: None, is_local: false, + is_missing: false, trait_: None, is_name_already_imported: false, requires_import: false, @@ -2360,6 +2393,7 @@ fn foo(s: S) { s.$0 } exact_name_match: false, type_match: None, is_local: false, + is_missing: false, trait_: None, is_name_already_imported: false, requires_import: false, @@ -2580,6 +2614,7 @@ fn f() -> i32 { Exact, ), is_local: false, + is_missing: false, trait_: None, is_name_already_imported: false, requires_import: false, @@ -2639,7 +2674,6 @@ fn go(world: &WorldSnapshot) { go(w$0) } st WorldSnapshot {…} WorldSnapshot { _f: () } [] st &WorldSnapshot {…} [type] st WorldSnapshot WorldSnapshot [] - st &WorldSnapshot [type] fn go(…) fn(&WorldSnapshot) [] "#]], ); @@ -2687,6 +2721,7 @@ fn main() { exact_name_match: false, type_match: None, is_local: true, + is_missing: false, trait_: None, is_name_already_imported: false, requires_import: false, @@ -2705,6 +2740,204 @@ fn main() { } #[test] + fn complete_ref_match_in_macro() { + check_kinds( + r#" +macro_rules! id { ($($t:tt)*) => ($($t)*); } +fn foo(data: &i32) {} +fn main() { + let indent = 2i32; + id!(foo(i$0)) +} +"#, + &[CompletionItemKind::SymbolKind(SymbolKind::Local)], + expect![[r#" + [ + CompletionItem { + label: "indent", + detail_left: None, + detail_right: Some( + "i32", + ), + source_range: 114..115, + delete: 114..115, + insert: "indent", + kind: SymbolKind( + Local, + ), + detail: "i32", + relevance: CompletionRelevance { + exact_name_match: false, + type_match: None, + is_local: true, + is_missing: false, + trait_: None, + is_name_already_imported: false, + requires_import: false, + is_private_editable: false, + postfix_match: None, + function: None, + is_skipping_completion: false, + has_local_inherent_impl: false, + is_deprecated: false, + }, + ref_match: "&@114", + }, + ] + "#]], + ); + + check_kinds( + r#" +macro_rules! id { ($($t:tt)*) => ($($t)*); } +fn foo(data: &i32) {} +fn indent() -> i32 { i32 } +fn main() { + id!(foo(i$0)) +} +"#, + &[CompletionItemKind::SymbolKind(SymbolKind::Function)], + expect![[r#" + [ + CompletionItem { + label: "foo(…)", + detail_left: None, + detail_right: Some( + "fn(&i32)", + ), + source_range: 118..119, + delete: 118..119, + insert: "foo(${1:data})$0", + kind: SymbolKind( + Function, + ), + lookup: "foo", + detail: "fn(&i32)", + trigger_call_info: true, + }, + CompletionItem { + label: "indent()", + detail_left: None, + detail_right: Some( + "fn() -> i32", + ), + source_range: 118..119, + delete: 118..119, + insert: "indent()$0", + kind: SymbolKind( + Function, + ), + lookup: "indent", + detail: "fn() -> i32", + ref_match: "&@118", + }, + CompletionItem { + label: "main()", + detail_left: None, + detail_right: Some( + "fn()", + ), + source_range: 118..119, + delete: 118..119, + insert: "main()$0", + kind: SymbolKind( + Function, + ), + lookup: "main", + detail: "fn()", + }, + ] + "#]], + ); + + // FIXME: It is best to test `S.in` if speculative execution is implemented + check_kinds( + r#" +macro_rules! id { ($($t:tt)*) => ($($t)*); } +fn foo(data: &i32) {} +struct S; +impl S {fn indent(&self) -> i32 { i32 }} +fn main() { + id!(foo(S.i$0)) +} +"#, + &[CompletionItemKind::SymbolKind(SymbolKind::Method)], + expect![[r#" + [ + CompletionItem { + label: "indent()", + detail_left: None, + detail_right: Some( + "fn(&self) -> i32", + ), + source_range: 144..145, + delete: 144..145, + insert: "indent()$0", + kind: SymbolKind( + Method, + ), + lookup: "indent", + detail: "fn(&self) -> i32", + relevance: CompletionRelevance { + exact_name_match: false, + type_match: None, + is_local: false, + is_missing: false, + trait_: None, + is_name_already_imported: false, + requires_import: false, + is_private_editable: false, + postfix_match: None, + function: Some( + CompletionRelevanceFn { + has_params: true, + has_self_param: true, + return_type: Other, + }, + ), + is_skipping_completion: false, + has_local_inherent_impl: false, + is_deprecated: false, + }, + ref_match: "&@142", + }, + ] + "#]], + ); + + check_kinds( + r#" +macro_rules! id { ($($t:tt)*) => ($($t)*); } +fn foo(data: &i32) {} +struct S { indent: i32 } +fn main(s: S) { + id!(foo(s.i$0)) +} +"#, + &[CompletionItemKind::SymbolKind(SymbolKind::Field)], + expect![[r#" + [ + CompletionItem { + label: "indent", + detail_left: None, + detail_right: Some( + "i32", + ), + source_range: 122..123, + delete: 122..123, + insert: "indent", + kind: SymbolKind( + Field, + ), + detail: "i32", + ref_match: "&@120", + }, + ] + "#]], + ); + } + + #[test] fn too_many_arguments() { cov_mark::check!(too_many_arguments); check_relevance( @@ -2825,6 +3058,37 @@ mod b { } #[test] + fn score_patterns() { + check_relevance( + r#" +struct Foo(Bar); +struct Bar { field: i32 } +fn go(Foo($0): Foo) {} +"#, + expect![[r#" + bn Bar {…} Bar { field$1 }$0 [type] + st Bar [] + st Foo [] + bn Foo(…) Foo($1)$0 [] + "#]], + ); + + check_relevance( + r#" +struct Foo(Bar); +enum Bar { Variant { field: i32 } } +fn go(foo: Foo) { match foo { Foo($0) } } +"#, + expect![[r#" + bn Bar::Variant {…} Bar::Variant { field$1 }$0 [type] + en Bar [] + st Foo [] + bn Foo(…) Foo($1)$0 [] + "#]], + ); + } + + #[test] fn test_avoid_redundant_suggestion() { check_relevance( r#" @@ -2865,7 +3129,6 @@ fn main() { st S S [] st &mut S [type] st S S [] - st &mut S [type] fn foo(…) fn(&mut S) [] fn main() fn() [] "#]], @@ -2879,6 +3142,7 @@ fn main() { foo(&mut $0); } "#, + // FIXME: There are many `S` here expect![[r#" lc s S [type+name+local] st S S [type] @@ -2940,12 +3204,51 @@ fn main() { st &mut S(…) [type] lc ssss S<u32> [local] lc &mut ssss [type+local] - st S S<{unknown}> [] - st &mut S [type] + st S S<T> [] fn foo(…) fn(&mut S<T>) [] fn main() fn() [] "#]], ); + // Regression test https://github.com/rust-lang/rust-analyzer/issues/22324 + check_relevance( + r#" +//- minicore: deref +struct S<T>(T); +impl<T> core::ops::Deref for S<T> { + type Target = T; +} +fn foo<T>(s: &u32) {} +fn main() { + let ssss = S(); + foo($0); +} + "#, + // FIXME: term_search exclude ssss.0 (field.ty().is_unknown()) + expect![[r#" + ex ssss.0 [type_could_unify] + lc ssss S<{unknown}> [local] + st S S<T> [] + md core:: [] + fn foo(…) fn(&u32) [] + fn main() fn() [] + "#]], + ); + check_relevance( + r#" +//- minicore: deref +fn foo<T>(s: &T) {} +fn main() { + let ssss = &mut 2i32; + foo($0); +} + "#, + expect![[r#" + lc ssss &mut i32 [type_could_unify+local] + md core:: [] + fn foo(…) fn(&T) [] + fn main() fn() [] + "#]], + ); } #[test] @@ -3016,9 +3319,7 @@ fn main() { lc t T [local] lc &t [type+local] st S S [] - st &S [type] st T T [] - st &T [type] md core:: [] fn foo(…) fn(&S) [] fn main() fn() [] @@ -3065,9 +3366,7 @@ fn main() { lc t T [local] lc &mut t [type+local] st S S [] - st &mut S [type] st T T [] - st &mut T [type] md core:: [] fn foo(…) fn(&mut S) [] fn main() fn() [] @@ -3131,7 +3430,6 @@ fn bar(t: &Foo) {} ev Foo::B Foo::B [] ev &Foo::B [type] en Foo Foo [] - en &Foo [type] fn bar(…) fn(&Foo) [] fn foo() fn() [] "#]], @@ -3166,9 +3464,7 @@ fn main() { st &S [type] ex core::ops::Deref::deref(&bar()) [type_could_unify] st S S [] - st &S [type] st T T [] - st &T [type] fn bar() fn() -> T [] fn &bar() [type] md core:: [] @@ -3499,6 +3795,7 @@ fn foo(f: Foo) { let _: &u32 = f.b$0 } exact_name_match: false, type_match: None, is_local: false, + is_missing: false, trait_: None, is_name_already_imported: false, requires_import: false, @@ -3594,6 +3891,7 @@ fn foo() { Exact, ), is_local: false, + is_missing: false, trait_: None, is_name_already_imported: false, requires_import: false, @@ -3648,6 +3946,7 @@ fn main() { exact_name_match: false, type_match: None, is_local: false, + is_missing: false, trait_: None, is_name_already_imported: false, requires_import: false, @@ -3690,14 +3989,14 @@ fn foo() { } "#, expect![[r#" - ev Foo::B Foo::B [type_could_unify] - ev Foo::A(…) Foo::A(T) [type_could_unify] lc foo Foo<u32> [type+local] ex Foo::B [type] ex foo [type] - en Foo Foo<{unknown}> [type_could_unify] + ev Foo::B Foo::B [type_could_unify] + ev Foo::A(…) Foo::A(T) [type_could_unify] + en Foo Foo<T> [type_could_unify] + fn baz() fn() -> Foo<T> [type_could_unify] fn bar() fn() -> Foo<u8> [] - fn baz() fn() -> Foo<T> [] fn foo() fn() [] "#]], ); @@ -3727,6 +4026,7 @@ fn main() { &[CompletionItemKind::Snippet, CompletionItemKind::SymbolKind(SymbolKind::Method)], expect![[r#" sn not !expr [snippet] + me not() fn(self) -> <Self as Not>::Output [type_could_unify+requires_import] sn box Box::new(expr) [] sn call function(expr) [] sn const const {} [] @@ -3740,7 +4040,6 @@ fn main() { sn return return expr [] sn unsafe unsafe {} [] sn while while expr {} [] - me not() fn(self) -> <Self as Not>::Output [requires_import] "#]], ); } @@ -3791,7 +4090,7 @@ enum Foo { en Foo Foo [] st Other Other [] sp Self Foo [] - st Vec<…> Vec<{unknown}> [] + st Vec<…> Vec<T> [] "#]], ); } @@ -4140,6 +4439,7 @@ fn main() { exact_name_match: false, type_match: None, is_local: false, + is_missing: false, trait_: Some( CompletionRelevanceTraitInfo { notable_trait: true, @@ -4176,6 +4476,7 @@ fn main() { exact_name_match: false, type_match: None, is_local: false, + is_missing: false, trait_: Some( CompletionRelevanceTraitInfo { notable_trait: true, diff --git a/crates/ide-completion/src/render/function.rs b/crates/ide-completion/src/render/function.rs index 97d5a25f49..4f70a90aff 100644 --- a/crates/ide-completion/src/render/function.rs +++ b/crates/ide-completion/src/render/function.rs @@ -76,7 +76,7 @@ fn render( completion.edition, ); - let ret_type = func.ret_type(db); + let ret_type = ctx.completion.rebase_ty(&func.ret_type(db)); let assoc_item = func.as_assoc_item(db); let trait_info = @@ -107,6 +107,7 @@ fn render( let function = assoc_item .and_then(|assoc_item| assoc_item.implementing_ty(db)) + .map(|self_type| ctx.completion.rebase_ty(&self_type)) .map(|self_type| compute_return_type_match(db, &ctx, self_type, &ret_type)) .map(|return_type| CompletionRelevanceFn { has_params: has_self_param || func.num_params(db) > 0, @@ -118,7 +119,7 @@ fn render( type_match: if has_call_parens || complete_call_parens.is_some() { compute_type_match(completion, &ret_type) } else { - compute_type_match(completion, &func.ty(db)) + compute_type_match(completion, &ctx.completion.rebase_ty(&func.ty(db))) }, exact_name_match: compute_exact_name_match(completion, &call), function, @@ -132,10 +133,10 @@ fn render( super::path_ref_match(completion, path_ctx, &ret_type, &mut item); } FuncKind::Method(DotAccess { receiver: Some(receiver), .. }, _) => { - if let Some(original_expr) = completion.sema.original_ast_node(receiver.clone()) + if let Some(original_expr) = completion.sema.original_range_opt(receiver.syntax()) && let Some(ref_mode) = compute_ref_match(completion, &ret_type) { - item.ref_match(ref_mode, original_expr.syntax().text_range().start()); + item.ref_match(ref_mode, original_expr.range.start()); } } _ => (), @@ -287,7 +288,7 @@ pub(super) fn add_call_parens<'b>( } fn ref_of_param(ctx: &CompletionContext<'_, '_>, arg: &str, ty: &hir::Type<'_>) -> &'static str { - if let Some(derefed_ty) = ty.remove_ref() { + if let Some(derefed_ty) = ty.as_reference_inner() { for (name, local) in ctx.locals.iter().sorted_by_key(|&(k, _)| k.clone()) { if name.as_str() == arg { return if local.ty(ctx.db) == derefed_ty { @@ -952,4 +953,23 @@ fn bar() { "#, ); } + + #[test] + fn no_semicolon_in_match() { + check_edit( + r#"foo"#, + r#" +fn foo() {} +fn bar() { + match fo$0 {} +} +"#, + r#" +fn foo() {} +fn bar() { + match foo()$0 {} +} +"#, + ); + } } diff --git a/crates/ide-completion/src/render/literal.rs b/crates/ide-completion/src/render/literal.rs index 9e0cec62e6..943ff58219 100644 --- a/crates/ide-completion/src/render/literal.rs +++ b/crates/ide-completion/src/render/literal.rs @@ -127,7 +127,7 @@ fn render( item.set_documentation(thing.docs(db)).set_deprecated(thing.is_deprecated(&ctx)); - let ty = thing.ty(db); + let ty = ctx.completion.rebase_ty(&thing.ty(db)); item.set_relevance(CompletionRelevance { type_match: compute_type_match(ctx.completion, &ty), // function is a misnomer here, this is more about constructor information diff --git a/crates/ide-completion/src/render/pattern.rs b/crates/ide-completion/src/render/pattern.rs index 392ecbc302..13a83eb216 100644 --- a/crates/ide-completion/src/render/pattern.rs +++ b/crates/ide-completion/src/render/pattern.rs @@ -103,21 +103,21 @@ pub(crate) fn render_variant_pat( )) } -fn build_completion( - ctx: RenderContext<'_, '_>, +fn build_completion<'db>( + ctx: RenderContext<'_, 'db>, label: SmolStr, lookup: SmolStr, pat: String, def: impl HasDocs, - adt_ty: hir::Type<'_>, + adt_ty: hir::Type<'db>, // Missing in context of match statement completions is_variant_missing: bool, ) -> CompletionItem { let mut relevance = ctx.completion_relevance(); + let adt_ty = ctx.completion.rebase_ty(&adt_ty); - if is_variant_missing { - relevance.type_match = super::compute_type_match(ctx.completion, &adt_ty); - } + relevance.type_match = super::compute_type_match(ctx.completion, &adt_ty); + relevance.is_missing = is_variant_missing; let mut item = CompletionItem::new( CompletionItemKind::Binding, diff --git a/crates/ide-completion/src/render/variant.rs b/crates/ide-completion/src/render/variant.rs index f86af6cdcb..8d1b885969 100644 --- a/crates/ide-completion/src/render/variant.rs +++ b/crates/ide-completion/src/render/variant.rs @@ -32,7 +32,7 @@ pub(crate) fn render_record_lit( if let Some(local) = ctx.locals.get(&field_name) && local .ty(ctx.db) - .could_unify_with_deeply(ctx.db, &field.ty(ctx.db).to_type(ctx.db)) + .could_unify_with_deeply(ctx.db, &ctx.rebase_ty(&field.ty(ctx.db))) { f(&format_args!("{}{tab}", field_name.display(ctx.db, ctx.edition))) } else { diff --git a/crates/ide-completion/src/tests/expression.rs b/crates/ide-completion/src/tests/expression.rs index c1205f9e18..e49afa66ad 100644 --- a/crates/ide-completion/src/tests/expression.rs +++ b/crates/ide-completion/src/tests/expression.rs @@ -1055,6 +1055,7 @@ fn brr() { fn brr() fn() st YoloVariant YoloVariant st YoloVariant {…} YoloVariant { f: usize } + ev Yolo(…) (use HH::Yolo) Yolo(YoloVariant) bt u32 u32 kw const kw crate:: @@ -1157,6 +1158,12 @@ fn complete_module_colons() { r#"mod module {} fn foo() { module:: }"#, ); + check_edit( + "module", + r#"mod module {} fn foo() { $0foo::bar }"#, + r#"mod module {} fn foo() { module::foo::bar }"#, + ); + check_edit_with_config( CompletionConfig { add_colons_to_module: false, ..TEST_CONFIG }, "module", @@ -1166,6 +1173,27 @@ fn complete_module_colons() { } #[test] +fn complete_module_exists_colons() { + check_edit( + "module", + r#"mod module {} fn foo() { $0::bar }"#, + r#"mod module {} fn foo() { module::bar }"#, + ); + + check_edit( + "module", + r#" +macro_rules! i { ($i:ident) => { $i::bar } } +mod module {} +fn foo() { i!($0) }"#, + r#" +macro_rules! i { ($i:ident) => { $i::bar } } +mod module {} +fn foo() { i!(module) }"#, + ); +} + +#[test] fn else_completion_after_if() { check( r#" @@ -2344,7 +2372,7 @@ fn main() { $0 } //- /std.rs crate:std -#[unstable(feature = "intrinsics")] +#[unstable(feature = "core_intrinsics")] pub mod intrinsics {} "#, expect![[r#" @@ -2936,6 +2964,84 @@ fn foo() { } #[test] +fn flyimport_excluded_mod_items_from_flyimport() { + check_with_config( + CompletionConfig { + exclude_flyimport: vec![( + "ra_test_fixture::xpack::xmodule2".to_owned(), + AutoImportExclusionType::SubItems, + )], + ..TEST_CONFIG + }, + r#" +mod xpack { + mod xmodule1 { + pub struct XOther; + } + pub mod xmodule2 { + pub use super::xmodule1::*; + pub struct XStruct; + pub fn xfn() {} + } +} + +fn foo() { + x$0 +} + "#, + expect![[r#" + ct CONST Unit + en Enum Enum + fn foo() fn() + fn function() fn() + ma makro!(…) macro_rules! makro + md module:: + md xmodule2:: (use xpack::xmodule2) + md xpack:: + sc STATIC Unit + st Record Record + st Tuple Tuple + st Unit Unit + un Union Union + ev TupleV(…) TupleV(u32) + bt u32 u32 + kw async + kw const + kw crate:: + kw enum + kw extern + kw false + kw fn + kw for + kw if + kw if let + kw impl + kw impl for + kw let + kw letm + kw loop + kw match + kw mod + kw return + kw self:: + kw static + kw struct + kw trait + kw true + kw type + kw union + kw unsafe + kw use + kw while + kw while let + sn macro_rules + sn pd + sn ppd + "#]], + ); +} + +#[test] fn excluded_trait_method_is_excluded_from_path_completion() { check_with_config( CompletionConfig { @@ -3092,8 +3198,8 @@ fn bar() { } "#, expect![[r#" - en Option Option<{unknown}> - en Result Result<{unknown}, {unknown}> + en Option Option<T> + en Result Result<T, E> fn bar() fn() lc i i32 ma const_format_args!(…) macro_rules! const_format_args @@ -3901,3 +4007,105 @@ fn tryme(param: impl SubTrait) { "#]], ); } + +#[test] +fn can_complete_macro_path_inside_expansion() { + check( + r#" +macro_rules! bar { () => (); } +macro_rules! foo { ($i:ident) => { $i!() }; } +fn main() { + foo!(ba$0); +} + "#, + expect![[r#" + fn main() fn() + ma bar macro_rules! bar + ma foo macro_rules! foo + bt u32 u32 + kw const + kw crate:: + kw false + kw for + kw if + kw if let + kw loop + kw match + kw return + kw self:: + kw true + kw unsafe + kw while + kw while let + "#]], + ); +} + +#[test] +fn no_completion_for_autorefd_traits_in_path_mode() { + check( + r#" +//- minicore: clone +trait Test1 {} + +fn test<H: Test1>(test: H) { + H::$0 +} + "#, + expect![""], + ); +} + +#[test] +fn imported_enum_variant_has_lower_priority() { + check( + r#" +pub struct String {} +mod foo { + pub enum Foo { String } +} +fn main() { + Strin$0 +} + "#, + expect![[r#" + fn main() fn() + md foo:: + st String String + ev String (use foo::Foo::String) String + bt u32 u32 + kw async + kw const + kw crate:: + kw enum + kw extern + kw false + kw fn + kw for + kw if + kw if let + kw impl + kw impl for + kw let + kw letm + kw loop + kw match + kw mod + kw return + kw self:: + kw static + kw struct + kw trait + kw true + kw type + kw union + kw unsafe + kw use + kw while + kw while let + sn macro_rules + sn pd + sn ppd + "#]], + ); +} diff --git a/crates/ide-completion/src/tests/flyimport.rs b/crates/ide-completion/src/tests/flyimport.rs index 231623a42f..45db8ecfc6 100644 --- a/crates/ide-completion/src/tests/flyimport.rs +++ b/crates/ide-completion/src/tests/flyimport.rs @@ -781,9 +781,9 @@ fn main() { } "#, expect![[r#" - me random_method(…) (use dep::test_mod::TestTrait) fn(&self) DEPRECATED ct SPECIAL_CONST (use dep::test_mod::TestTrait) u8 DEPRECATED fn weird_function() (use dep::test_mod::TestTrait) fn() DEPRECATED + me random_method(…) (use dep::test_mod::TestTrait) fn(&self) DEPRECATED "#]], ); } diff --git a/crates/ide-completion/src/tests/predicate.rs b/crates/ide-completion/src/tests/predicate.rs index 9826a8ed7b..3f3a6f4cf5 100644 --- a/crates/ide-completion/src/tests/predicate.rs +++ b/crates/ide-completion/src/tests/predicate.rs @@ -14,7 +14,7 @@ struct Foo<'lt, T, const C: usize> where $0 {} en Enum Enum ma makro!(…) macro_rules! makro md module:: - st Foo<…> Foo<'_, {unknown}, _> + st Foo<…> Foo<'lt, T, C> st Record Record st Tuple Tuple st Unit Unit @@ -91,7 +91,7 @@ struct Foo<'lt, T, const C: usize> where for<'a> $0 {} en Enum Enum ma makro!(…) macro_rules! makro md module:: - st Foo<…> Foo<'_, {unknown}, _> + st Foo<…> Foo<'lt, T, C> st Record Record st Tuple Tuple st Unit Unit diff --git a/crates/ide-completion/src/tests/record.rs b/crates/ide-completion/src/tests/record.rs index ddb9294469..aecf8eb651 100644 --- a/crates/ide-completion/src/tests/record.rs +++ b/crates/ide-completion/src/tests/record.rs @@ -157,6 +157,8 @@ fn foo(f: Struct) { fn in_functional_update() { cov_mark::check!(functional_update); + // FIXME: This should filter out all completions that do not have the type `Foo` + // I think maybe ranking by type match is enough check( r#" //- minicore:default @@ -210,7 +212,6 @@ fn main() { #[test] fn functional_update_no_dot() { cov_mark::check!(functional_update_field); - // FIXME: This should filter out all completions that do not have the type `Foo` check( r#" //- minicore:default @@ -281,6 +282,48 @@ fn main() { } #[test] +fn functional_update_non_last() { + check( + r#" +//- minicore:default +struct Foo { foo1: u32, foo2: u32 } +impl Default for Foo { + fn default() -> Self { loop {} } +} + +fn main() { + let thing = 1; + let foo = Foo { foo1: 0, foo2: 0 }; + let foo2 = Foo { $0 thing } +} +"#, + expect![[r#" + fd foo1 u32 + fd foo2 u32 + "#]], + ); + check( + r#" +//- minicore:default +struct Foo { foo1: u32, foo2: u32 } +impl Default for Foo { + fn default() -> Self { loop {} } +} + +fn main() { + let thing = 1; + let foo = Foo { foo1: 0, foo2: 0 }; + let foo2 = Foo { $0thing } +} +"#, + expect![[r#" + fd foo1 u32 + fd foo2 u32 + "#]], + ); +} + +#[test] fn functional_update_fields_completion() { // Complete fields before functional update `..` check( diff --git a/crates/ide-completion/src/tests/type_pos.rs b/crates/ide-completion/src/tests/type_pos.rs index 24080334ae..ad058901c0 100644 --- a/crates/ide-completion/src/tests/type_pos.rs +++ b/crates/ide-completion/src/tests/type_pos.rs @@ -15,8 +15,8 @@ struct Foo<'lt, T, const C: usize> { en Enum Enum ma makro!(…) macro_rules! makro md module:: - sp Self Foo<'_, {unknown}, _> - st Foo<…> Foo<'_, {unknown}, _> + sp Self Foo<'lt, T, C> + st Foo<…> Foo<'lt, T, C> st Record Record st Tuple Tuple st Unit Unit @@ -44,8 +44,8 @@ struct Foo<'lt, T, const C: usize>(f$0); en Enum Enum ma makro!(…) macro_rules! makro md module:: - sp Self Foo<'_, {unknown}, _> - st Foo<…> Foo<'_, {unknown}, _> + sp Self Foo<'lt, T, C> + st Foo<…> Foo<'lt, T, C> st Record Record st Tuple Tuple st Unit Unit @@ -409,7 +409,7 @@ const FOO: $0 = Foo(2); en Enum Enum ma makro!(…) macro_rules! makro md module:: - st Foo<…> Foo<{unknown}> + st Foo<…> Foo<T> st Record Record st Tuple Tuple st Unit Unit @@ -438,7 +438,7 @@ static FOO: $0 = Foo(2); en Enum Enum ma makro!(…) macro_rules! makro md module:: - st Foo<…> Foo<{unknown}> + st Foo<…> Foo<T> st Record Record st Tuple Tuple st Unit Unit @@ -629,7 +629,7 @@ fn foo<'lt, T, const C: usize>() { en Enum Enum ma makro!(…) macro_rules! makro md module:: - st Foo<…> Foo<{unknown}> + st Foo<…> Foo<T> st Record Record st Tuple Tuple st Unit Unit diff --git a/crates/ide-db/src/generated/lints.rs b/crates/ide-db/src/generated/lints.rs index 264bb4fa81..683adff615 100644 --- a/crates/ide-db/src/generated/lints.rs +++ b/crates/ide-db/src/generated/lints.rs @@ -573,13 +573,6 @@ pub const DEFAULT_LINTS: &[Lint] = &[ deny_since: None, }, Lint { - label: "inline_always_mismatching_target_features", - description: r##"detects when a function annotated with `#[inline(always)]` and `#[target_feature(enable = "..")]` is inlined into a caller without the required target feature"##, - default_severity: Severity::Warning, - warn_since: None, - deny_since: None, - }, - Lint { label: "inline_no_sanitize", description: r##"detects incompatible use of `#[inline(always)]` and `#[sanitize(... = "off")]`"##, default_severity: Severity::Warning, @@ -2458,6 +2451,22 @@ The tracking issue for this feature is: [#111889] deny_since: None, }, Lint { + label: "abi_swift", + description: r##"# `abi_swift` + +Allows `extern "Swift" fn()`. + +The tracking issue for this feature is: [#156481] + +[#156481]: https://github.com/rust-lang/rust/issues/156481 + +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { label: "abi_unadjusted", description: r##"# `abi_unadjusted` @@ -3932,22 +3941,6 @@ The tracking issue for this feature is: [#151828] deny_since: None, }, Lint { - label: "bool_to_result", - description: r##"# `bool_to_result` - - - -The tracking issue for this feature is: [#142748] - -[#142748]: https://github.com/rust-lang/rust/issues/142748 - ------------------------- -"##, - default_severity: Severity::Allow, - warn_since: None, - deny_since: None, - }, - Lint { label: "borrowed_buf_init", description: r##"# `borrowed_buf_init` @@ -4645,22 +4638,6 @@ The tracking issue for this feature is: [#94039] deny_since: None, }, Lint { - label: "cfg_target_has_atomic_equal_alignment", - description: r##"# `cfg_target_has_atomic_equal_alignment` - -Allows `cfg(target_has_atomic_equal_alignment = "...")`. - -The tracking issue for this feature is: [#93822] - -[#93822]: https://github.com/rust-lang/rust/issues/93822 - ------------------------- -"##, - default_severity: Severity::Allow, - warn_since: None, - deny_since: None, - }, - Lint { label: "cfg_target_has_reliable_f16_f128", description: r##"# `cfg_target_has_reliable_f16_f128` @@ -4852,6 +4829,22 @@ The tracking issue for this feature is: [#148519] deny_since: None, }, Lint { + label: "clflushopt_target_feature", + description: r##"# `clflushopt_target_feature` + +The `clflushopt` target feature on x86. + +The tracking issue for this feature is: [#157096] + +[#157096]: https://github.com/rust-lang/rust/issues/157096 + +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { label: "clone_from_ref", description: r##"# `clone_from_ref` @@ -11167,6 +11160,22 @@ The tracking issue for this feature is: [#147456] deny_since: None, }, Lint { + label: "move_expr", + description: r##"# `move_expr` + +Allows `move(expr)` in closures. + +The tracking issue for this feature is: [#155050] + +[#155050]: https://github.com/rust-lang/rust/issues/155050 + +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { label: "movrs_target_feature", description: r##"# `movrs_target_feature` @@ -11263,6 +11272,22 @@ The tracking issue for this feature is: [#123076] deny_since: None, }, Lint { + label: "mut_restriction", + description: r##"# `mut_restriction` + +Allows `mut(crate) field: Type` restrictions. + +The tracking issue for this feature is: [#105077] + +[#105077]: https://github.com/rust-lang/rust/issues/105077 + +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { label: "mutex_data_ptr", description: r##"# `mutex_data_ptr` @@ -12069,6 +12094,20 @@ The tracking issue for this feature is: [#118485] deny_since: None, }, Lint { + label: "os_str_split_at", + description: r##"# `os_str_split_at` + + + +This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. + +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { label: "os_string_truncate", description: r##"# `os_string_truncate` @@ -12257,22 +12296,6 @@ The tracking issue for this feature is: [#153328] deny_since: None, }, Lint { - label: "path_is_empty", - description: r##"# `path_is_empty` - - - -The tracking issue for this feature is: [#148494] - -[#148494]: https://github.com/rust-lang/rust/issues/148494 - ------------------------- -"##, - default_severity: Severity::Allow, - warn_since: None, - deny_since: None, - }, - Lint { label: "path_trailing_sep", description: r##"# `path_trailing_sep` @@ -13467,6 +13490,22 @@ The tracking issue for this feature is: [#138099] deny_since: None, }, Lint { + label: "return_address", + description: r##"# `return_address` + + + +The tracking issue for this feature is: [#154966] + +[#154966]: https://github.com/rust-lang/rust/issues/154966 + +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { label: "return_type_notation", description: r##"# `return_type_notation` @@ -13941,6 +13980,22 @@ The tracking issue for this feature is: [#56975] deny_since: None, }, Lint { + label: "share_trait", + description: r##"# `share_trait` + + + +The tracking issue for this feature is: [#156756] + +[#156756]: https://github.com/rust-lang/rust/issues/156756 + +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { label: "signed_bigint_helpers", description: r##"# `signed_bigint_helpers` @@ -14927,22 +14982,6 @@ The tracking issue for this feature is: [#95439] deny_since: None, }, Lint { - label: "target_feature_inline_always", - description: r##"# `target_feature_inline_always` - -Allows the use of target_feature when a function is marked inline(always). - -The tracking issue for this feature is: [#145574] - -[#145574]: https://github.com/rust-lang/rust/issues/145574 - ------------------------- -"##, - default_severity: Severity::Allow, - warn_since: None, - deny_since: None, - }, - Lint { label: "tcp_deferaccept", description: r##"# `tcp_deferaccept` @@ -15687,22 +15726,6 @@ The tracking issue for this feature is: [#37572] deny_since: None, }, Lint { - label: "trusted_len_next_unchecked", - description: r##"# `trusted_len_next_unchecked` - - - -The tracking issue for this feature is: [#37572] - -[#37572]: https://github.com/rust-lang/rust/issues/37572 - ------------------------- -"##, - default_severity: Severity::Allow, - warn_since: None, - deny_since: None, - }, - Lint { label: "trusted_random_access", description: r##"# `trusted_random_access` @@ -16306,6 +16329,22 @@ The tracking issue for this feature is: [#89517] deny_since: None, }, Lint { + label: "unix_kill_process_group", + description: r##"# `unix_kill_process_group` + + + +The tracking issue for this feature is: [#156537] + +[#156537]: https://github.com/rust-lang/rust/issues/156537 + +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { label: "unix_mkfifo", description: r##"# `unix_mkfifo` diff --git a/crates/ide-db/src/imports/import_assets.rs b/crates/ide-db/src/imports/import_assets.rs index e0501b5e44..6edc550b33 100644 --- a/crates/ide-db/src/imports/import_assets.rs +++ b/crates/ide-db/src/imports/import_assets.rs @@ -814,13 +814,13 @@ fn trait_applicable_items<'db>( let mut deref_chain = trait_candidate.receiver_ty.autoderef(db).collect::<Vec<_>>(); // As a last step, we can do array unsizing (that's the only unsizing that rustc does for method receivers!) if let Some((ty, _len)) = deref_chain.last().and_then(|ty| ty.as_array(db)) { - let slice = Type::new_slice(ty); + let slice = Type::new_slice(db, ty); deref_chain.push(slice); } deref_chain .into_iter() .flat_map(|ty| { - let fingerprint = ty.fingerprint_for_trait_impl()?; + let fingerprint = ty.fingerprint_for_trait_impl(db)?; let mut crates = vec![]; if let Some(adt) = ty.as_adt() { diff --git a/crates/ide-db/src/lib.rs b/crates/ide-db/src/lib.rs index 6180e3186c..2b1525e2b2 100644 --- a/crates/ide-db/src/lib.rs +++ b/crates/ide-db/src/lib.rs @@ -180,6 +180,10 @@ impl SourceDatabase for RootDatabase { fn nonce_and_revision(&self) -> (Nonce, salsa::Revision) { (self.nonce, salsa::plumbing::ZalsaDatabase::zalsa(self).current_revision()) } + + fn line_column(&self, file: FileId, offset: syntax::TextSize) -> Result<(u32, u32), ()> { + line_index(self, file).try_line_col(offset).map(|lc| (lc.line, lc.col)).ok_or(()) + } } impl Default for RootDatabase { diff --git a/crates/ide-db/src/syntax_helpers/node_ext.rs b/crates/ide-db/src/syntax_helpers/node_ext.rs index 11ba815dab..e30b21c139 100644 --- a/crates/ide-db/src/syntax_helpers/node_ext.rs +++ b/crates/ide-db/src/syntax_helpers/node_ext.rs @@ -216,12 +216,7 @@ pub fn walk_ty(ty: &ast::Type, cb: &mut dyn FnMut(ast::Type) -> bool) { preorder.skip_subtree(); cb(ty); } - Some(ty) => - { - #[expect( - clippy::collapsible_match, - reason = "it won't compile due to exhaustiveness" - )] + Some(ty) => { if cb(ty) { preorder.skip_subtree(); } diff --git a/crates/ide-db/src/syntax_helpers/suggest_name.rs b/crates/ide-db/src/syntax_helpers/suggest_name.rs index 76fea5c262..15920595a8 100644 --- a/crates/ide-db/src/syntax_helpers/suggest_name.rs +++ b/crates/ide-db/src/syntax_helpers/suggest_name.rs @@ -412,7 +412,7 @@ fn from_type( edition: Edition, ) -> Option<SmolStr> { let ty = sema.type_of_expr(expr)?.adjusted(); - let ty = ty.remove_ref().unwrap_or(ty); + let ty = ty.strip_reference(); name_of_type(&ty, sema.db, edition) } @@ -445,7 +445,7 @@ fn name_of_type<'db>( return None; } name - } else if let Some(inner_ty) = ty.remove_ref() { + } else if let Some((inner_ty, _)) = ty.as_reference() { return name_of_type(&inner_ty, db, edition); } else if let Some(inner_ty) = ty.as_slice() { return Some(sequence_name(Some(&inner_ty), db, edition)); diff --git a/crates/ide-db/src/test_data/test_symbol_index_collection.txt b/crates/ide-db/src/test_data/test_symbol_index_collection.txt index 1b20a574bd..a84f75cb4e 100644 --- a/crates/ide-db/src/test_data/test_symbol_index_collection.txt +++ b/crates/ide-db/src/test_data/test_symbol_index_collection.txt @@ -38,7 +38,7 @@ "Enum", ), is_alias: false, - is_assoc: true, + is_assoc: false, is_import: false, do_not_complete: Yes, _marker: PhantomData<&()>, @@ -110,7 +110,7 @@ "Enum", ), is_alias: false, - is_assoc: true, + is_assoc: false, is_import: false, do_not_complete: Yes, _marker: PhantomData<&()>, diff --git a/crates/ide-diagnostics/src/handlers/array_pattern_without_fixed_length.rs b/crates/ide-diagnostics/src/handlers/array_pattern_without_fixed_length.rs new file mode 100644 index 0000000000..e7d0868350 --- /dev/null +++ b/crates/ide-diagnostics/src/handlers/array_pattern_without_fixed_length.rs @@ -0,0 +1,46 @@ +use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; + +// Diagnostic: array-pattern-without-fixed-length +// +// This diagnostic is triggered when a rest array pattern is matched against an +// array with a non-constant length. +pub(crate) fn array_pattern_without_fixed_length( + ctx: &DiagnosticsContext<'_, '_>, + d: &hir::ArrayPatternWithoutFixedLength, +) -> Diagnostic { + Diagnostic::new_with_syntax_node_ptr( + ctx, + DiagnosticCode::RustcHardError("E0730"), + "cannot pattern-match on an array without a fixed length", + d.pat.map(Into::into), + ) + .stable() +} + +#[cfg(test)] +mod tests { + use crate::tests::check_diagnostics; + + #[test] + fn array_pattern_without_fixed_length() { + check_diagnostics( + r#" +fn f<const N: usize>(arr: [u8; N]) { + let [_head, _tail @ ..] = arr; + //^^^^^^^^^^^^^^^^^^^ error: cannot pattern-match on an array without a fixed length +} +"#, + ); + } + + #[test] + fn fixed_length_array_pattern_is_ok() { + check_diagnostics( + r#" +fn f(arr: [u8; 3]) { + let [_head, _tail @ ..] = arr; +} +"#, + ); + } +} diff --git a/crates/ide-diagnostics/src/handlers/break_outside_of_loop.rs b/crates/ide-diagnostics/src/handlers/break_outside_of_loop.rs index b7265c47b6..43eb4ab8e0 100644 --- a/crates/ide-diagnostics/src/handlers/break_outside_of_loop.rs +++ b/crates/ide-diagnostics/src/handlers/break_outside_of_loop.rs @@ -126,14 +126,13 @@ fn foo() { #[test] fn value_break_in_for_loop() { - // FIXME: the error is correct, but the message is terrible check_diagnostics( r#" //- minicore: iterator fn test() { for _ in [()] { break 3; - // ^ error: expected (), found i32 + // ^^^^^^^ error: can't break with a value in this position } } "#, diff --git a/crates/ide-diagnostics/src/handlers/cannot_be_dereferenced.rs b/crates/ide-diagnostics/src/handlers/cannot_be_dereferenced.rs new file mode 100644 index 0000000000..6c9726f8af --- /dev/null +++ b/crates/ide-diagnostics/src/handlers/cannot_be_dereferenced.rs @@ -0,0 +1,74 @@ +use hir::HirDisplay; + +use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; + +// Diagnostic: cannot-be-dereferenced +// +// This diagnostic is triggered if the operand of a dereference expression +// cannot be dereferenced. +pub(crate) fn cannot_be_dereferenced( + ctx: &DiagnosticsContext<'_, '_>, + d: &hir::CannotBeDereferenced<'_>, +) -> Diagnostic { + Diagnostic::new_with_syntax_node_ptr( + ctx, + DiagnosticCode::RustcHardError("E0614"), + format!( + "type `{}` cannot be dereferenced", + d.found.display(ctx.sema.db, ctx.display_target) + ), + d.expr.map(Into::into), + ) + .stable() +} + +#[cfg(test)] +mod tests { + use crate::tests::check_diagnostics; + + #[test] + fn cannot_be_dereferenced() { + check_diagnostics( + r#" +fn f() { + let x = 1i32; + let _ = *x; + //^^ error: type `i32` cannot be dereferenced +} +"#, + ); + } + + #[test] + fn allows_reference_deref() { + check_diagnostics( + r#" +fn f(x: &i32) { + let _ = *x; +} +"#, + ); + } + + #[test] + fn allows_overloaded_deref() { + check_diagnostics( + r#" +//- minicore: deref +struct Wrapper(i32); + +impl core::ops::Deref for Wrapper { + type Target = i32; + + fn deref(&self) -> &i32 { + &self.0 + } +} + +fn f(x: Wrapper) { + let _ = *x; +} +"#, + ); + } +} diff --git a/crates/ide-diagnostics/src/handlers/cannot_implicitly_deref_trait_object.rs b/crates/ide-diagnostics/src/handlers/cannot_implicitly_deref_trait_object.rs new file mode 100644 index 0000000000..eca14144dd --- /dev/null +++ b/crates/ide-diagnostics/src/handlers/cannot_implicitly_deref_trait_object.rs @@ -0,0 +1,53 @@ +use hir::HirDisplay; + +use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; + +// Diagnostic: cannot-implicitly-deref-trait-object +// +// This diagnostic is triggered when a pointer to a trait object is implicitly +// dereferenced by a pattern. +pub(crate) fn cannot_implicitly_deref_trait_object( + ctx: &DiagnosticsContext<'_, '_>, + d: &hir::CannotImplicitlyDerefTraitObject<'_>, +) -> Diagnostic { + Diagnostic::new_with_syntax_node_ptr( + ctx, + DiagnosticCode::RustcHardError("E0033"), + format!( + "type `{}` cannot be dereferenced", + d.found.display(ctx.sema.db, ctx.display_target) + ), + d.pat.map(Into::into), + ) + .stable() +} + +#[cfg(test)] +mod tests { + use crate::tests::check_diagnostics; + + #[test] + fn trait_object_pattern_deref() { + check_diagnostics( + r#" +trait Trait {} + +fn f(x: &dyn Trait) { + let &ref _y = x; + //^^^^^^^ error: type `&(dyn Trait + 'static)` cannot be dereferenced +} +"#, + ); + } + + #[test] + fn allows_sized_ref_pattern_deref() { + check_diagnostics( + r#" +fn f(x: &i32) { + let &ref _y = x; +} +"#, + ); + } +} diff --git a/crates/ide-diagnostics/src/handlers/cannot_index_into.rs b/crates/ide-diagnostics/src/handlers/cannot_index_into.rs new file mode 100644 index 0000000000..1e42313b4a --- /dev/null +++ b/crates/ide-diagnostics/src/handlers/cannot_index_into.rs @@ -0,0 +1,77 @@ +use hir::HirDisplay; + +use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; + +// Diagnostic: cannot-index-into +// +// This diagnostic is triggered if indexing is used on a type that cannot be +// indexed. +pub(crate) fn cannot_index_into( + ctx: &DiagnosticsContext<'_, '_>, + d: &hir::CannotIndexInto<'_>, +) -> Diagnostic { + Diagnostic::new_with_syntax_node_ptr( + ctx, + DiagnosticCode::RustcHardError("E0608"), + format!( + "cannot index into a value of type `{}`", + d.found.display(ctx.sema.db, ctx.display_target) + ), + d.expr.map(Into::into), + ) + .stable() +} + +#[cfg(test)] +mod tests { + use crate::tests::check_diagnostics; + + #[test] + fn cannot_index_into() { + check_diagnostics( + r#" +//- minicore: index +fn f() { + let x = 1i32; + let _ = x[0]; + //^^^^ error: cannot index into a value of type `i32` +} +"#, + ); + } + + #[test] + fn allows_array_indexing() { + check_diagnostics( + r#" +//- minicore: index, slice +fn f() { + let x = [1i32, 2]; + let _ = x[0]; +} +"#, + ); + } + + #[test] + fn allows_overloaded_indexing() { + check_diagnostics( + r#" +//- minicore: index +struct Bag(i32); + +impl core::ops::Index<usize> for Bag { + type Output = i32; + + fn index(&self, _: usize) -> &Self::Output { + &self.0 + } +} + +fn f(x: Bag) { + let _ = x[0]; +} +"#, + ); + } +} diff --git a/crates/ide-diagnostics/src/handlers/explicit_drop_method_use.rs b/crates/ide-diagnostics/src/handlers/explicit_drop_method_use.rs new file mode 100644 index 0000000000..b02bccf5a5 --- /dev/null +++ b/crates/ide-diagnostics/src/handlers/explicit_drop_method_use.rs @@ -0,0 +1,426 @@ +use either::Either; +use hir::InFile; +use ide_db::assists::Assist; +use ide_db::source_change::{SourceChange, SourceChangeBuilder}; +use ide_db::text_edit::TextEdit; +use itertools::Itertools; +use syntax::{ + AstNode, AstPtr, + ast::{self, HasArgList}, +}; + +use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext, adjusted_display_range, fix}; + +// Diagnostic: explicit-drop-method-use +// +// This diagnostic is triggered when the `Drop::drop` method is called (or named) explicitly. +pub(crate) fn explicit_drop_method_use( + ctx: &DiagnosticsContext<'_, '_>, + d: &hir::ExplicitDropMethodUse, +) -> Diagnostic { + match d.expr_or_path { + Either::Left(expr) => { + let display_range = adjusted_display_range(ctx, expr, &|node| { + Some(node.name_ref()?.syntax().text_range()) + }); + Diagnostic::new( + DiagnosticCode::RustcHardError("E0040"), + "explicit use of destructor method", + display_range, + ) + .stable() + .with_main_node(expr.map(Into::into)) + .with_fixes(fix_method_call(ctx, expr)) + } + Either::Right(path) => Diagnostic::new_with_syntax_node_ptr( + ctx, + DiagnosticCode::RustcHardError("E0040"), + "explicit use of destructor method", + path.map(Into::into), + ) + .stable() + .with_fixes(fix_path(ctx, path)), + } +} + +fn fix_method_call( + ctx: &DiagnosticsContext<'_, '_>, + mcall_ptr: InFile<AstPtr<ast::MethodCallExpr>>, +) -> Option<Vec<Assist>> { + if mcall_ptr.file_id.is_macro() { + // TODO: handle macro calls. Rough plan: + // 1. upmap the range of the receiver and the range of the whole call + // 2. delete everything outside the receiver and replace it with `drop(...)`, using range edits only. + return None; + } + + let db = ctx.db(); + + let file_id = mcall_ptr.file_id; + let mcall = mcall_ptr.to_node(db); + let range = mcall.syntax().text_range(); + + // `mcall` is `foo.drop()` -- extract the receiver, and wrap it in `drop()` + // NOTE: it could theoretically be `(&mut foo).drop()` instead, in which case the fix + // below would be incorrect, as it'd result in `drop((&mut foo))` instead of `drop(foo)` + // -- but we don't bother to deal with that case. + let recv = mcall.receiver()?; + + let mut builder = SourceChangeBuilder::new(file_id.original_file(db).file_id(db)); + let editor = builder.make_editor(mcall.syntax()); + let make = editor.make(); + let new_call = + make.expr_call(make.expr_path(make.path_from_text("drop")), make.arg_list([recv])); + builder.replace_ast(ast::Expr::MethodCallExpr(mcall), ast::Expr::CallExpr(new_call)); + let source_change = builder.finish(); + Some(vec![fix("use-drop-function", "Use `drop` function", source_change, range)]) +} + +fn fix_path( + ctx: &DiagnosticsContext<'_, '_>, + path_ptr: InFile<AstPtr<ast::Path>>, +) -> Option<Vec<Assist>> { + let db = ctx.db(); + + let file_id = path_ptr.file_id; + let path = path_ptr.to_node(db); + + if let Some(call) = + path.syntax().parent().and_then(|it| it.parent()).and_then(ast::CallExpr::cast) + { + if file_id.is_macro() { + // TODO: make this work in macros? Might not be worth it, as this is a niche way to trigger this + // already niche error + return None; + } + + // `call` is `Drop::drop(&mut foo)` -- extract the arg, and wrap it in `drop()` + let arg_list = call.arg_list()?; + let ref_recv = arg_list.args().exactly_one().ok()?; + let ast::Expr::RefExpr(ref_recv) = ref_recv else { + return None; + }; + let recv = ref_recv.expr()?; + + let range = call.syntax().text_range(); + + let mut builder = SourceChangeBuilder::new(file_id.original_file(db).file_id(db)); + let editor = builder.make_editor(call.syntax()); + let make = editor.make(); + let new_call = + make.expr_call(make.expr_path(make.path_from_text("drop")), make.arg_list([recv])); + builder.replace_ast(call, new_call); + let source_change = builder.finish(); + Some(vec![fix("use-drop-function", "Use `drop` function", source_change, range)]) + } else { + // `path` could be the `Foo::drop` in `let d = Foo::drop;` + // -- replace the path with `drop` + + let range = InFile::new(file_id, path.syntax().text_range()) + .original_node_file_range_rooted_opt(db)?; + + let edit = TextEdit::replace(range.range, "drop".to_owned()); + let source_change = SourceChange::from_text_edit(range.file_id.file_id(db), edit); + Some(vec![fix("use-drop-function", "Use `drop` function", source_change, range.range)]) + } +} + +#[cfg(test)] +mod tests { + use crate::tests::{ + check_diagnostics, check_diagnostics_with_disabled, check_fix, check_fix_with_disabled, + }; + + #[test] + fn method_call_diagnostic() { + check_diagnostics( + r#" +//- minicore: drop +struct A; +impl Drop for A { fn drop(&mut self) {} } + +fn main(mut a: A) { + a.drop(); + // ^^^^ 💡 error: explicit use of destructor method +} +"#, + ); + } + + #[test] + fn method_call_fix() { + check_fix( + r#" +//- minicore: drop +struct A; +impl Drop for A { fn drop(&mut self) {} } + +fn main(mut a: A) { + a.drop$0(); +} +"#, + r#" +struct A; +impl Drop for A { fn drop(&mut self) {} } + +fn main(mut a: A) { + drop(a); +} +"#, + ); + } + + #[test] + fn qualified_call_1_diagnostic() { + check_diagnostics( + r#" +//- minicore: drop +struct A; +impl Drop for A { fn drop(&mut self) {} } + +fn main(mut a: A) { + A::drop(&mut a); + // ^^^^^^^ 💡 error: explicit use of destructor method +} +"#, + ); + } + + #[test] + fn qualified_call_1_fix() { + check_fix( + r#" +//- minicore: drop +struct A; +impl Drop for A { fn drop(&mut self) {} } + +fn main(mut a: A) { + A::drop(&mut a$0); +} +"#, + r#" +struct A; +impl Drop for A { fn drop(&mut self) {} } + +fn main(mut a: A) { + drop(a); +} +"#, + ) + } + + #[test] + fn qualified_call_2_diagnostic() { + check_diagnostics( + r#" +//- minicore: drop +struct A; +impl Drop for A { fn drop(&mut self) {} } + +fn main(mut a: A) { + Drop::drop(&mut a); + // ^^^^^^^^^^ 💡 error: explicit use of destructor method +} +"#, + ); + } + + #[test] + fn qualified_call_2_fix() { + check_fix( + r#" +//- minicore: drop +struct A; +impl Drop for A { fn drop(&mut self) {} } + +fn main(mut a: A) { + Drop::drop(&mut a$0); +} +"#, + r#" +struct A; +impl Drop for A { fn drop(&mut self) {} } + +fn main(mut a: A) { + drop(a); +} +"#, + ) + } + + #[test] + fn fully_qualified_call_diagnostic() { + check_diagnostics( + r#" +//- minicore: drop +struct A; +impl Drop for A { fn drop(&mut self) {} } + +fn main(mut a: A) { + <A as Drop>::drop(&mut a); + // ^^^^^^^^^^^^^^^^^ 💡 error: explicit use of destructor method +} +"#, + ); + } + + #[test] + fn fully_qualified_call_fix() { + check_fix( + r#" +//- minicore: drop +struct A; +impl Drop for A { fn drop(&mut self) {} } + +fn main(mut a: A) { + <A as Drop>::drop(&mut a$0); +} +"#, + r#" +struct A; +impl Drop for A { fn drop(&mut self) {} } + +fn main(mut a: A) { + drop(a); +} +"#, + ) + } + + #[test] + fn path_diagnostic() { + check_diagnostics_with_disabled( + r#" +//- minicore: drop +struct A; +impl Drop for A { fn drop(&mut self) {} } + +fn main(mut a: A) { + let d = A::drop; + // ^^^^^^^ 💡 error: explicit use of destructor method + d(&mut a); +} +"#, + // Because of the error, the code isn't analyzed further (?), and so `d` is warned on as unused. + // Arguably a bug in r-a (rustc doesn't emit a warning in this case) + // FIXME: remove this once r-a no longer warns + &["unused_variables"], + ); + } + + #[test] + // NOTE: Here, the fix is not completely correct, as it doesn't replace `d(&mut a)` with `d(a)`. + // Oh well, rustc doesn't either + fn path_fix() { + check_fix_with_disabled( + r#" +//- minicore: drop +struct A; +impl Drop for A { fn drop(&mut self) {} } + +fn main(mut a: A) { + let d = A::drop$0; + d(&mut a); +} +"#, + r#" +struct A; +impl Drop for A { fn drop(&mut self) {} } + +fn main(mut a: A) { + let d = drop; + d(&mut a); +} +"#, + // Because of the error, the code isn't analyzed further (?), and so `d` is warned on as unused. + // Arguably a bug in r-a (rustc doesn't emit a warning in this case) + // FIXME: remove this once r-a no longer warns + &["unused_variables"], + ); + } + + #[test] + // NOTE: Here, the fix is not completely correct, as it doesn't replace `d(&mut a)` with `d(a)`. + // Oh well, rustc doesn't either + fn path_fix_in_macro() { + check_fix( + r#" +//- minicore: drop +struct A; +impl Drop for A { fn drop(&mut self) {} } + +macro_rules! main { + ($e:expr) => { + fn main() { $e } + } +} + +main!{{ + let mut a = A; + let d = A::drop$0; + d(&mut a); +}}; +"#, + r#" +struct A; +impl Drop for A { fn drop(&mut self) {} } + +macro_rules! main { + ($e:expr) => { + fn main() { $e } + } +} + +main!{{ + let mut a = A; + let d = drop; + d(&mut a); +}}; +"#, + ); + } + + #[test] + fn std_mem_drop() { + check_diagnostics( + r#" +//- minicore: drop +struct A; +impl Drop for A { fn drop(&mut self) {} } + +fn main(a: A) { + drop(a); +} +"#, + ); + } + + #[test] + fn inherent_drop_method() { + check_diagnostics( + r#" +struct A; +impl A { fn drop(&mut self) {} } + +fn main(mut a: A) { + a.drop(); +} +"#, + ); + } + + #[test] + fn custom_trait_drop_method() { + check_diagnostics( + r#" +struct A; +trait MyDrop { fn drop(&mut self); } +impl MyDrop for A { fn drop(&mut self) {} } + +fn main(mut a: A) { + a.drop(); +} +"#, + ); + } +} diff --git a/crates/ide-diagnostics/src/handlers/fru_in_destructuring_assignment.rs b/crates/ide-diagnostics/src/handlers/fru_in_destructuring_assignment.rs new file mode 100644 index 0000000000..f8d3d80d62 --- /dev/null +++ b/crates/ide-diagnostics/src/handlers/fru_in_destructuring_assignment.rs @@ -0,0 +1,67 @@ +use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; + +// Diagnostic: fru-in-destructuring-assignment +// +// This diagnostic is triggered when a destructuring assignment contains functional record update +pub(crate) fn fru_in_destructuring_assignment( + ctx: &DiagnosticsContext<'_, '_>, + d: &hir::FruInDestructuringAssignment, +) -> Diagnostic { + Diagnostic::new_with_syntax_node_ptr( + ctx, + DiagnosticCode::SyntaxError, + "functional record updates are not allowed in destructuring assignments", + d.node.map(Into::into), + ) + .stable() +} + +#[cfg(test)] +mod tests { + use crate::tests::{check_diagnostics, check_diagnostics_with_disabled}; + + #[test] + fn spread_variable() { + check_diagnostics_with_disabled( + r#" +struct Foo { bar: u32, baz: u32 } +fn test(f: Foo, g: Foo, mut bar: u32, mut baz: u32) { + Foo { ..g } = f; + // ^ error: functional record updates are not allowed in destructuring assignments + Foo { bar, ..g } = f; + // ^ error: functional record updates are not allowed in destructuring assignments + Foo { bar, baz, ..g } = f; + // ^ error: functional record updates are not allowed in destructuring assignments +} + "#, + // We don't end up using neither `bar` nor `baz` + &["unused_variables"], + ); + } + + #[test] + fn spread_default() { + check_diagnostics( + r#" +struct Foo { bar: u32, baz: u32 } +fn test(f: Foo) { + Foo { ..Default::default() } = f; + // ^^^^^^^^^^^^^^^^^^ error: functional record updates are not allowed in destructuring assignments +} + "#, + ); + } + + #[test] + fn spread_struct() { + check_diagnostics( + r#" +struct Foo { bar: u32, baz: u32 } +fn test(f: Foo) { + Foo { ..Foo { bar: 0, baz: 0 } } = f; + // ^^^^^^^^^^^^^^^^^^^^^^ error: functional record updates are not allowed in destructuring assignments +} + "#, + ); + } +} diff --git a/crates/ide-diagnostics/src/handlers/invalid_range_pat_type.rs b/crates/ide-diagnostics/src/handlers/invalid_range_pat_type.rs new file mode 100644 index 0000000000..465f59559d --- /dev/null +++ b/crates/ide-diagnostics/src/handlers/invalid_range_pat_type.rs @@ -0,0 +1,55 @@ +use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; + +// Diagnostic: invalid-range-pat-type +// +// This diagnostic is triggered when a range pattern is used with a type that +// is neither `char` nor numeric. +pub(crate) fn invalid_range_pat_type( + ctx: &DiagnosticsContext<'_, '_>, + d: &hir::InvalidRangePatType, +) -> Diagnostic { + Diagnostic::new_with_syntax_node_ptr( + ctx, + DiagnosticCode::RustcHardError("E0029"), + "only `char` and numeric types are allowed in range patterns", + d.pat.map(Into::into), + ) + .stable() +} + +#[cfg(test)] +mod tests { + use crate::tests::check_diagnostics; + + #[test] + fn bool_range_pattern() { + check_diagnostics( + r#" +fn f(x: bool) { + match x { + false..=true => {} + //^^^^^^^^^^^^ error: only `char` and numeric types are allowed in range patterns + } +} +"#, + ); + } + + #[test] + fn numeric_and_char_range_patterns() { + check_diagnostics( + r#" +fn f(x: u8, c: char) { + match x { + 0..=9 => {} + _ => {} + } + match c { + 'a'..='z' => {} + _ => {} + } +} +"#, + ); + } +} diff --git a/crates/ide-diagnostics/src/handlers/macro_error.rs b/crates/ide-diagnostics/src/handlers/macro_error.rs index b6571e02ef..7acbcad22c 100644 --- a/crates/ide-diagnostics/src/handlers/macro_error.rs +++ b/crates/ide-diagnostics/src/handlers/macro_error.rs @@ -327,4 +327,21 @@ fn it_works() { "#, ); } + + #[test] + fn unimplemented_builtin_macro() { + check_diagnostics( + r#" +#[rustc_builtin_macro] +macro_rules! unimplemented_builtin_macro { + // ^^^^^^^^^^^^^^^^^^^^^^^^^^^ weak: unimplemented built-in macro + () => {}; +} + + #[unimplemented_builtin_macro] +// ^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: this built-in macro is not implemented +struct Foo; + "#, + ); + } } diff --git a/crates/ide-diagnostics/src/handlers/method_call_illegal_sized_bound.rs b/crates/ide-diagnostics/src/handlers/method_call_illegal_sized_bound.rs new file mode 100644 index 0000000000..0a0ac8627c --- /dev/null +++ b/crates/ide-diagnostics/src/handlers/method_call_illegal_sized_bound.rs @@ -0,0 +1,78 @@ +use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; + +// Diagnostic: method-call-illegal-sized-bound +// +// This diagnostic is triggered when a method is called on a trait-object +// receiver but the method's predicates require `Self: Sized`, which the +// trait object cannot satisfy. +pub(crate) fn method_call_illegal_sized_bound( + ctx: &DiagnosticsContext<'_, '_>, + d: &hir::MethodCallIllegalSizedBound, +) -> Diagnostic { + Diagnostic::new_with_syntax_node_ptr( + ctx, + DiagnosticCode::RustcHardError("E0277"), + "the method cannot be invoked on a trait object because its `Self: Sized` bound is not satisfied", + d.call_expr.map(|it| it.into()), + ) + .stable() +} + +#[cfg(test)] +mod tests { + use crate::tests::check_diagnostics; + + #[test] + fn sized_bound_method_on_trait_object_errors() { + check_diagnostics( + r#" +//- minicore: sized +trait Foo { + fn cant_call(&self) where Self: Sized; +} + +fn f(x: &dyn Foo) { + x.cant_call(); + //^^^^^^^^^^^^^ error: the method cannot be invoked on a trait object because its `Self: Sized` bound is not satisfied +} +"#, + ); + } + + #[test] + fn method_without_sized_bound_on_trait_object_does_not_error() { + check_diagnostics( + r#" +//- minicore: sized +trait Foo { + fn dyn_safe(&self); +} + +fn f(x: &dyn Foo) { + x.dyn_safe(); +} +"#, + ); + } + + #[test] + fn sized_bound_method_on_concrete_type_does_not_error() { + check_diagnostics( + r#" +//- minicore: sized +trait Foo { + fn cant_dispatch(&self) where Self: Sized; +} + +struct S; +impl Foo for S { + fn cant_dispatch(&self) {} +} + +fn f(s: &S) { + s.cant_dispatch(); +} +"#, + ); + } +} diff --git a/crates/ide-diagnostics/src/handlers/missing_unsafe.rs b/crates/ide-diagnostics/src/handlers/missing_unsafe.rs index b4ddb239c8..d279e9b4e8 100644 --- a/crates/ide-diagnostics/src/handlers/missing_unsafe.rs +++ b/crates/ide-diagnostics/src/handlers/missing_unsafe.rs @@ -425,17 +425,20 @@ fn main() { fn raw_deref_on_union_field() { check_diagnostics( r#" +//- minicore: index, slice +#![allow(unused_variables)] + fn main() { union U { a: u8 } - let x = U { a: 3 }; + let mut x = U { a: 3 }; let a = &raw mut x.a; union U1 { - a: u8 + a: usize } let x = U1 { a: 3 }; diff --git a/crates/ide-diagnostics/src/handlers/mutability_errors.rs b/crates/ide-diagnostics/src/handlers/mutability_errors.rs index 31becd1d74..49aa6d7bd9 100644 --- a/crates/ide-diagnostics/src/handlers/mutability_errors.rs +++ b/crates/ide-diagnostics/src/handlers/mutability_errors.rs @@ -1313,7 +1313,7 @@ fn main() { fn regression_20662() { check_diagnostics( r#" -//- minicore: index +//- minicore: index, slice pub trait A: core::ops::IndexMut<usize> { type T: A; } diff --git a/crates/ide-diagnostics/src/handlers/mutable_ref.rs b/crates/ide-diagnostics/src/handlers/mutable_ref.rs new file mode 100644 index 0000000000..b56619612d --- /dev/null +++ b/crates/ide-diagnostics/src/handlers/mutable_ref.rs @@ -0,0 +1,64 @@ +use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; + +// Diagnostic: mutable-ref +// +// This diagnostic is triggered when binding is taken that is both mutable and by-reference. +pub(crate) fn mutable_ref_binding( + ctx: &DiagnosticsContext<'_, '_>, + d: &hir::MutableRefBinding, +) -> Diagnostic { + Diagnostic::new_with_syntax_node_ptr( + ctx, + DiagnosticCode::RustcHardError("E0658"), + "bindings cannot be both mutable and by-reference by default in 2024 edition. add experimental #![feature(mut_ref)] for this functionality", + d.pat.map(Into::into), + ) + .stable() +} + +#[cfg(test)] +mod tests { + use crate::tests::check_diagnostics; + + #[test] + fn mutable_ref_binding_missing_feature() { + check_diagnostics( + r#" +//- minicore: option +#![feature(ref_pat_eat_one_layer_2024)] +struct TestStruct { + val: i32 +} +fn main() { + let opt_ref = &Some(TestStruct {val: 1}); + + if let Some(mut x) = opt_ref { + //^^^^^ error: bindings cannot be both mutable and by-reference by default in 2024 edition. add experimental #![feature(mut_ref)] for this functionality + x = &TestStruct{val: 5}; + } +} +"#, + ); + } + + #[test] + fn mutable_ref_binding_with_feature() { + check_diagnostics( + r#" +//- minicore: option +#![feature(ref_pat_eat_one_layer_2024)] +#![feature(mut_ref)] +struct TestStruct { + val: i32 +} +fn main() { + let opt_ref = &Some(TestStruct{val: 1}); + + if let Some(mut x) = opt_ref { + x = &TestStruct{val: 5}; + } +} +"#, + ); + } +} diff --git a/crates/ide-diagnostics/src/handlers/no_such_field.rs b/crates/ide-diagnostics/src/handlers/no_such_field.rs index 7959fddc75..3dd6744b05 100644 --- a/crates/ide-diagnostics/src/handlers/no_such_field.rs +++ b/crates/ide-diagnostics/src/handlers/no_such_field.rs @@ -116,14 +116,13 @@ fn missing_record_expr_field_fixes( let mut new_field = new_field.to_string(); // FIXME: check submodule instead of FileId - if usage_file_id != def_file_id && !matches!(def_id, hir::Variant::EnumVariant(_)) { - new_field = format!("pub(crate) {new_field}"); - } - new_field = format!("\n{indent}{new_field}{postfix}"); - - if needs_comma { - new_field = format!(",{new_field}"); - } + let vis = if usage_file_id != def_file_id && !matches!(def_id, hir::Variant::EnumVariant(_)) { + "pub(crate) " + } else { + "" + }; + let comma = if needs_comma { "," } else { "" }; + new_field = format!("{comma}\n{indent}{vis}{new_field}{postfix}"); let source_change = SourceChange::from_text_edit( def_file_id.file_id(sema.db), diff --git a/crates/ide-diagnostics/src/handlers/non_exhaustive_record_pat.rs b/crates/ide-diagnostics/src/handlers/non_exhaustive_record_pat.rs new file mode 100644 index 0000000000..ea587e6037 --- /dev/null +++ b/crates/ide-diagnostics/src/handlers/non_exhaustive_record_pat.rs @@ -0,0 +1,79 @@ +use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; + +// Diagnostic: non-exhaustive-record-pat +// +// This diagnostic is triggered if a record pattern destructures a `#[non_exhaustive]` +// struct or enum variant from another crate without `..`. +pub(crate) fn non_exhaustive_record_pat( + ctx: &DiagnosticsContext<'_, '_>, + d: &hir::NonExhaustiveRecordPat, +) -> Diagnostic { + let item = match d.variant { + hir::Variant::Struct(_) => "struct", + hir::Variant::Union(_) => "union", + hir::Variant::EnumVariant(_) => "variant", + }; + + Diagnostic::new_with_syntax_node_ptr( + ctx, + DiagnosticCode::RustcHardError("E0638"), + format!("`..` required with {item} marked as non-exhaustive"), + d.pat.map(Into::into), + ) + .stable() +} + +#[cfg(test)] +mod tests { + use crate::tests::check_diagnostics; + + #[test] + fn reports_external_non_exhaustive_struct_pattern_without_rest() { + check_diagnostics( + r#" +//- /lib.rs crate:lib +#[non_exhaustive] +pub struct S { + pub field: u32, +} + +fn local_ok(s: S) { + let S { field } = s; + let _ = field; +} + +//- /main.rs crate:main deps:lib +fn main(s: lib::S) { + let lib::S { field } = s; + //^^^^^^^^^^^^^^^^ error: `..` required with struct marked as non-exhaustive + let _ = field; +} +"#, + ); + } + + #[test] + fn reports_external_non_exhaustive_variant_pattern_without_rest() { + check_diagnostics( + r#" +//- /lib.rs crate:lib +pub enum E { + #[non_exhaustive] + V { field: u32 }, +} + +fn local_ok(e: E) { + let E::V { field } = e; + let _ = field; +} + +//- /main.rs crate:main deps:lib +fn main(e: lib::E) { + let lib::E::V { field } = e; + //^^^^^^^^^^^^^^^^^^^ error: `..` required with variant marked as non-exhaustive + let _ = field; +} +"#, + ); + } +} diff --git a/crates/ide-diagnostics/src/handlers/pattern_arg_in_extern_fn.rs b/crates/ide-diagnostics/src/handlers/pattern_arg_in_extern_fn.rs index 459ec175b1..8a54834361 100644 --- a/crates/ide-diagnostics/src/handlers/pattern_arg_in_extern_fn.rs +++ b/crates/ide-diagnostics/src/handlers/pattern_arg_in_extern_fn.rs @@ -21,6 +21,24 @@ mod tests { use crate::tests::check_diagnostics; #[test] + fn ident_pattern_allowed() { + check_diagnostics( + r#" +unsafe extern { fn foo(a: i32); } + "#, + ); + } + + #[test] + fn wildcard_pattern_allowed() { + check_diagnostics( + r#" +unsafe extern { fn foo(_: i32); } + "#, + ); + } + + #[test] fn tuple_pattern() { check_diagnostics( r#" diff --git a/crates/ide-diagnostics/src/handlers/trait_impl_missing_assoc_item.rs b/crates/ide-diagnostics/src/handlers/trait_impl_missing_assoc_item.rs index 5f5e155bd7..1ffef25b50 100644 --- a/crates/ide-diagnostics/src/handlers/trait_impl_missing_assoc_item.rs +++ b/crates/ide-diagnostics/src/handlers/trait_impl_missing_assoc_item.rs @@ -149,10 +149,18 @@ impl Trait for () { type Item = (); fn item() {} } +impl Trait for Adt<i32> {} + //^^^^^ error: not all trait items implemented, missing: `type Item`, `fn item` // Items with Self: Sized bound not required to be implemented for unsized types. impl Trait for str {} impl Trait for dyn OtherTrait {} +impl Trait for Adt<[i32]> {} +impl Trait for Slice<i32> {} +impl Trait for (str,) {} + +struct Adt<T>(i32, T); +struct Slice<T>([T]); "#, ) } diff --git a/crates/ide-diagnostics/src/handlers/type_mismatch.rs b/crates/ide-diagnostics/src/handlers/type_mismatch.rs index 250c692d16..5a1856c89d 100644 --- a/crates/ide-diagnostics/src/handlers/type_mismatch.rs +++ b/crates/ide-diagnostics/src/handlers/type_mismatch.rs @@ -8,7 +8,7 @@ use ide_db::{ use syntax::{ AstNode, AstPtr, TextSize, ast::{ - self, BlockExpr, Expr, ExprStmt, HasArgList, + self, BlockExpr, Expr, ExprStmt, HasArgList, RefExpr, edit::{AstNodeEdit, IndentLevel}, }, }; @@ -51,10 +51,10 @@ pub(crate) fn type_mismatch( format!( "expected {}, found {}", d.expected - .display(ctx.sema.db, ctx.display_target) + .display(ctx.db(), ctx.display_target) .with_closure_style(ClosureStyle::ClosureWithId), d.actual - .display(ctx.sema.db, ctx.display_target) + .display(ctx.db(), ctx.display_target) .with_closure_style(ClosureStyle::ClosureWithId), ), display_range, @@ -69,7 +69,7 @@ fn fixes(ctx: &DiagnosticsContext<'_, '_>, d: &hir::TypeMismatch<'_>) -> Option< if let Some(expr_ptr) = d.expr_or_pat.value.cast::<ast::Expr>() { let expr_ptr = &InFile { file_id: d.expr_or_pat.file_id, value: expr_ptr }; - add_reference(ctx, d, expr_ptr, &mut fixes); + add_or_fix_reference(ctx, d, expr_ptr, &mut fixes); add_missing_ok_or_some(ctx, d, expr_ptr, &mut fixes); remove_unnecessary_wrapper(ctx, d, expr_ptr, &mut fixes); remove_semicolon(ctx, d, expr_ptr, &mut fixes); @@ -79,7 +79,7 @@ fn fixes(ctx: &DiagnosticsContext<'_, '_>, d: &hir::TypeMismatch<'_>) -> Option< if fixes.is_empty() { None } else { Some(fixes) } } -fn add_reference( +fn add_or_fix_reference( ctx: &DiagnosticsContext<'_, '_>, d: &hir::TypeMismatch<'_>, expr_ptr: &InFile<AstPtr<ast::Expr>>, @@ -87,13 +87,56 @@ fn add_reference( ) -> Option<()> { let range = ctx.sema.diagnostics_display_range((*expr_ptr).map(|it| it.into())); - let (_, mutability) = d.expected.as_reference()?; - let actual_with_ref = d.actual.add_reference(mutability); - if !actual_with_ref.could_coerce_to(ctx.sema.db, &d.expected) { + let (expected_with_ref_removed, expected_mutability) = d.expected.as_reference()?; + + if let Some((actual_with_ref_removed, hir::Mutability::Shared)) = d.actual.as_reference() + && expected_mutability == hir::Mutability::Mut + && actual_with_ref_removed.could_coerce_to(ctx.db(), &expected_with_ref_removed) + { + // The actual type is `&T`, and the expected type is `&mut T`, (or `U` that `T` can be coerced to). + // It's likely that, instead of adding a reference, we should just change the mutability of + // the existing one. + + let expr = expr_ptr.to_node(ctx.db()); + // If the node comes from a macro expansion, then we shouldn't assist, + // as the suggestion would overwrite the macro _definition_ position + let expr = ctx.sema.original_ast_node(expr)?; + let expr_without_ref = RefExpr::cast(expr.syntax().clone())?.expr()?; + + let pos = expr_without_ref.syntax().text_range().start(); + let edit = TextEdit::insert(pos, expected_mutability.as_keyword_for_ref().to_owned()); + let source_change = SourceChange::from_text_edit(range.file_id, edit); + acc.push(fix( + "make_reference_mutable", + "Make reference mutable", + source_change, + range.range, + )); + return Some(()); + } + + let actual_with_ref = d.actual.add_reference(ctx.db(), expected_mutability); + if !actual_with_ref.could_coerce_to(ctx.db(), &d.expected) { return None; } - let ampersands = format!("&{}", mutability.as_keyword_for_ref()); + let expr = expr_ptr.to_node(ctx.db()); + let assign = expr + .syntax() + .parent() + .and_then(ast::BinExpr::cast) + .filter(|it| it.op_kind() == Some(ast::BinaryOp::Assignment { op: None })); + if let Some(assign) = assign + && expected_mutability.is_mut() + && let Some(range) = ctx.sema.original_range_opt(assign.syntax()) + { + let edit = TextEdit::insert(range.range.start(), "*".to_owned()); + let source_change = SourceChange::from_text_edit(range.file_id.file_id(ctx.db()), edit); + acc.push(fix("add_deref_here", "Add deref here", source_change, range.range)); + return Some(()); + } + + let ampersands = format!("&{}", expected_mutability.as_keyword_for_ref()); let edit = TextEdit::insert(range.range.start(), ampersands); let source_change = SourceChange::from_text_edit(range.file_id, edit); @@ -107,7 +150,7 @@ fn add_missing_ok_or_some( expr_ptr: &InFile<AstPtr<ast::Expr>>, acc: &mut Vec<Assist>, ) -> Option<()> { - let root = ctx.sema.db.parse_or_expand(expr_ptr.file_id); + let root = ctx.db().parse_or_expand(expr_ptr.file_id); let expr = expr_ptr.value.to_node(&root); let hir::FileRange { file_id, range: expr_range } = ctx.sema.original_range_opt(expr.syntax())?; @@ -126,14 +169,13 @@ fn add_missing_ok_or_some( let variant_name = if Some(expected_enum) == core_result { "Ok" } else { "Some" }; - let wrapped_actual_ty = - expected_adt.ty_with_args(ctx.sema.db, std::iter::once(d.actual.clone())); + let wrapped_actual_ty = expected_adt.ty(ctx.db()).instantiate([d.actual.clone()]); - if !d.expected.could_unify_with(ctx.sema.db, &wrapped_actual_ty) { + if !d.expected.could_unify_with(ctx.db(), &wrapped_actual_ty) { return None; } - let file_id = file_id.file_id(ctx.sema.db); + let file_id = file_id.file_id(ctx.db()); if d.actual.is_unit() { if let Expr::BlockExpr(block) = &expr { @@ -202,7 +244,7 @@ fn remove_unnecessary_wrapper( expr_ptr: &InFile<AstPtr<ast::Expr>>, acc: &mut Vec<Assist>, ) -> Option<()> { - let db = ctx.sema.db; + let db = ctx.db(); let root = db.parse_or_expand(expr_ptr.file_id); let expr = expr_ptr.value.to_node(&root); // FIXME: support inside MacroCall? @@ -225,7 +267,7 @@ fn remove_unnecessary_wrapper( return None; } - let inner_type = variant.fields(db).first()?.ty_with_args(db, d.actual.type_arguments()); + let inner_type = variant.fields(db).first()?.ty(db).instantiate(d.actual.type_arguments()); if !d.expected.could_unify_with(db, &inner_type) { return None; } @@ -233,7 +275,7 @@ fn remove_unnecessary_wrapper( let inner_arg = call_expr.arg_list()?.args().next()?; let file_id = expr_ptr.file_id.original_file(db); - let mut builder = SourceChangeBuilder::new(file_id.file_id(ctx.sema.db)); + let mut builder = SourceChangeBuilder::new(file_id.file_id(ctx.db())); let editor; match inner_arg { // We're returning `()` @@ -267,7 +309,7 @@ fn remove_unnecessary_wrapper( } } - builder.add_file_edits(file_id.file_id(ctx.sema.db), editor); + builder.add_file_edits(file_id.file_id(ctx.db()), editor); let name = format!("Remove unnecessary {}() wrapper", variant.name(db).as_str()); acc.push(fix( "remove_unnecessary_wrapper", @@ -284,7 +326,7 @@ fn remove_semicolon( expr_ptr: &InFile<AstPtr<ast::Expr>>, acc: &mut Vec<Assist>, ) -> Option<()> { - let root = ctx.sema.db.parse_or_expand(expr_ptr.file_id); + let root = ctx.db().parse_or_expand(expr_ptr.file_id); let expr = expr_ptr.value.to_node(&root); if !d.actual.is_unit() { return None; @@ -294,14 +336,14 @@ fn remove_semicolon( let expr_before_semi = block.statements().last().and_then(|s| ExprStmt::cast(s.syntax().clone()))?; let type_before_semi = ctx.sema.type_of_expr(&expr_before_semi.expr()?)?.original(); - if !type_before_semi.could_coerce_to(ctx.sema.db, &d.expected) { + if !type_before_semi.could_coerce_to(ctx.db(), &d.expected) { return None; } let semicolon_range = expr_before_semi.semicolon_token()?.text_range(); let edit = TextEdit::delete(semicolon_range); let source_change = SourceChange::from_text_edit( - expr_ptr.file_id.original_file(ctx.sema.db).file_id(ctx.sema.db), + expr_ptr.file_id.original_file(ctx.db()).file_id(ctx.db()), edit, ); @@ -315,21 +357,21 @@ fn str_ref_to_owned( expr_ptr: &InFile<AstPtr<ast::Expr>>, acc: &mut Vec<Assist>, ) -> Option<()> { - let expected = d.expected.display(ctx.sema.db, ctx.display_target); + let expected = d.expected.display(ctx.db(), ctx.display_target); // FIXME do this properly let is_applicable = d.actual.strip_reference().is_str() && expected.to_string() == "String"; if !is_applicable { return None; } - let root = ctx.sema.db.parse_or_expand(expr_ptr.file_id); + let root = ctx.db().parse_or_expand(expr_ptr.file_id); let expr = expr_ptr.value.to_node(&root); let hir::FileRange { file_id, range } = ctx.sema.original_range_opt(expr.syntax())?; let to_owned = ".to_owned()".to_owned(); let edit = TextEdit::insert(range.end(), to_owned); - let source_change = SourceChange::from_text_edit(file_id.file_id(ctx.sema.db), edit); + let source_change = SourceChange::from_text_edit(file_id.file_id(ctx.db()), edit); acc.push(fix("str_ref_to_owned", "Add .to_owned() here", source_change, range)); Some(()) @@ -392,6 +434,112 @@ fn test(_arg: &mut i32) {} } #[test] + fn fix_reference_to_int() { + check_fix( + r#" +fn main() { + test($0&123); +} +fn test(_arg: &mut i32) {} + "#, + r#" +fn main() { + test(&mut 123); +} +fn test(_arg: &mut i32) {} + "#, + ); + } + + #[test] + fn add_reference_to_parenthesized_int() { + check_fix( + r#" +fn main() { + test(($0123)); +} +fn test(_arg: &i32) {} + "#, + r#" +fn main() { + test((&123)); +} +fn test(_arg: &i32) {} + "#, + ); + } + + #[test] + fn add_mutable_reference_to_parenthesized_int() { + check_fix( + r#" +fn main() { + test(($0123)); +} +fn test(_arg: &mut i32) {} + "#, + r#" +fn main() { + test((&mut 123)); +} +fn test(_arg: &mut i32) {} + "#, + ); + } + + #[test] + fn fix_reference_to_parenthesized_int_paren_inside_ref() { + check_fix( + r#" +fn main() { + test(&$0(123)); +} +fn test(_arg: &mut i32) {} + "#, + r#" +fn main() { + test(&mut (123)); +} +fn test(_arg: &mut i32) {} + "#, + ); + } + + #[test] + fn fix_reference_to_parenthesized_int_ref_inside_paren() { + check_fix( + r#" +fn main() { + test(($0&123)); +} +fn test(_arg: &mut i32) {} + "#, + r#" +fn main() { + test((&mut 123)); +} +fn test(_arg: &mut i32) {} + "#, + ); + } + + #[test] + fn add_deref_in_assign() { + check_fix( + r#" +fn test(arg: &mut i32) { + arg = $02; +} + "#, + r#" +fn test(arg: &mut i32) { + *arg = 2; +} + "#, + ); + } + + #[test] fn add_reference_to_array() { check_fix( r#" @@ -411,6 +559,19 @@ fn test(_arg: &[i32]) {} } #[test] + fn fix_reference_to_array() { + check_no_fix( + r#" +//- minicore: coerce_unsized +fn main() { + test($0&[1, 2, 3]); +} +fn test(_arg: &mut [i32]) {} + "#, + ); + } + + #[test] fn add_reference_with_autoderef() { check_fix( r#" @@ -444,6 +605,49 @@ fn test(_arg: &Bar) {} } #[test] + // FIXME: this should suggest making the reference mutable instead: `&Foo -> &mut Foo`. + // Currently it doesn't, as the logic for that assist strips away references, and thus checks + // whether `Foo` can be coerced to `Bar` (which it can't), instead of checking `&mut Foo` to + // `&mut Bar` (which it can) + fn fix_reference_with_autoderef() { + check_fix( + r#" +//- minicore: coerce_unsized, deref_mut +struct Foo; +struct Bar; +impl core::ops::Deref for Foo { + type Target = Bar; + fn deref(&self) -> &Self::Target { loop {} } +} +impl core::ops::DerefMut for Foo { + fn deref_mut(&mut self) -> &mut Self::Target { loop {} } +} + +fn main() { + test($0&Foo); +} +fn test(_arg: &mut Bar) {} + "#, + r#" +struct Foo; +struct Bar; +impl core::ops::Deref for Foo { + type Target = Bar; + fn deref(&self) -> &Self::Target { loop {} } +} +impl core::ops::DerefMut for Foo { + fn deref_mut(&mut self) -> &mut Self::Target { loop {} } +} + +fn main() { + test(&mut &Foo); +} +fn test(_arg: &mut Bar) {} + "#, + ); + } + + #[test] fn add_reference_to_method_call() { check_fix( r#" @@ -484,6 +688,38 @@ fn main() { } #[test] + fn add_mutable_reference_to_let_stmt() { + check_fix( + r#" +fn main() { + let _test: &mut i32 = $0123; +} + "#, + r#" +fn main() { + let _test: &mut i32 = &mut 123; +} + "#, + ); + } + + #[test] + fn fix_reference_to_let_stmt() { + check_fix( + r#" +fn main() { + let _test: &mut i32 = $0&123; +} + "#, + r#" +fn main() { + let _test: &mut i32 = &mut 123; +} + "#, + ); + } + + #[test] fn add_reference_to_macro_call() { check_fix( r#" @@ -512,16 +748,50 @@ fn main() { } #[test] - fn add_mutable_reference_to_let_stmt() { + fn fix_reference_to_macro_call() { check_fix( r#" +macro_rules! thousand { + () => { + 1000_u64 + }; +} + +fn test(_foo: &mut u64) {} fn main() { - let _test: &mut i32 = $0123; + test($0&thousand!()); } "#, r#" +macro_rules! thousand { + () => { + 1000_u64 + }; +} + +fn test(_foo: &mut u64) {} fn main() { - let _test: &mut i32 = &mut 123; + test(&mut thousand!()); +} + "#, + ); + } + + #[test] + // If the immutable reference comes from a macro expansion, + // we can't do anything to change it to a mutable one. + fn dont_fix_reference_inside_macro_call() { + check_no_fix( + r#" +macro_rules! thousand { + () => { + &1000_u64 + }; +} + +fn test(_foo: &mut u64) {} +fn main() { + test($0thousand!()); } "#, ); @@ -1207,6 +1477,20 @@ fn f() { } #[test] + fn type_mismatch_in_condition() { + check_diagnostics( + r#" +fn f() { + if 1 {} + //^ error: expected bool, found i32 + match () { _ if 1 => (), _ => () } + //^ error: expected bool, found i32 +} +"#, + ); + } + + #[test] fn regression_14768() { check_diagnostics( r#" diff --git a/crates/ide-diagnostics/src/handlers/unimplemented_trait.rs b/crates/ide-diagnostics/src/handlers/unimplemented_trait.rs index d94ceef642..4a253bc831 100644 --- a/crates/ide-diagnostics/src/handlers/unimplemented_trait.rs +++ b/crates/ide-diagnostics/src/handlers/unimplemented_trait.rs @@ -69,4 +69,20 @@ fn foo() { "#, ); } + + #[test] + fn for_iterable() { + check_diagnostics( + r#" +//- minicore: iterator +fn foo() { + for _ in () {} + // ^^ error: the trait bound `(): Iterator` is not satisfied + // ^^ error: the trait bound `(): Iterator` is not satisfied + // | required by the bound `(): IntoIterator` +} + + "#, + ); + } } diff --git a/crates/ide-diagnostics/src/handlers/unresolved_method.rs b/crates/ide-diagnostics/src/handlers/unresolved_method.rs index 93caf281f0..01929a5144 100644 --- a/crates/ide-diagnostics/src/handlers/unresolved_method.rs +++ b/crates/ide-diagnostics/src/handlers/unresolved_method.rs @@ -129,25 +129,17 @@ fn assoc_func_fix( let receiver_type = &ctx.sema.type_of_expr(&receiver)?.original; let assoc_fn_params = f.assoc_fn_params(db); - let need_to_take_receiver_as_first_arg = if assoc_fn_params.is_empty() { - false - } else { - assoc_fn_params - .first() - .map(|first_arg| { - // For generic type, say `Box`, take `Box::into_raw(b: Self)` as example, - // type of `b` is `Self`, which is `Box<T, A>`, containing unspecified generics. - // However, type of `receiver` is specified, it could be `Box<i32, Global>` or something like that, - // so `first_arg.ty() == receiver_type` evaluate to `false` here. - // Here add `first_arg.ty().as_adt() == receiver_type.as_adt()` as guard, - // apply `.as_adt()` over `Box<T, A>` or `Box<i32, Global>` gets `Box`, so we get `true` here. - - // FIXME: it fails when type of `b` is `Box` with other generic param different from `receiver` - first_arg.ty() == receiver_type - || first_arg.ty().as_adt() == receiver_type.as_adt() - }) - .unwrap_or(false) - }; + let need_to_take_receiver_as_first_arg = assoc_fn_params.first().is_some_and(|first_arg| { + // For generic type, say `Box`, take `Box::into_raw(b: Self)` as example, + // type of `b` is `Self`, which is `Box<T, A>`, containing unspecified generics. + // However, type of `receiver` is specified, it could be `Box<i32, Global>` or something like that, + // so `first_arg.ty() == receiver_type` evaluate to `false` here. + // Here add `first_arg.ty().as_adt() == receiver_type.as_adt()` as guard, + // apply `.as_adt()` over `Box<T, A>` or `Box<i32, Global>` gets `Box`, so we get `true` here. + + // FIXME: it fails when type of `b` is `Box` with other generic param different from `receiver` + first_arg.ty() == receiver_type || first_arg.ty().as_adt() == receiver_type.as_adt() + }); let mut receiver_type_adt_name = receiver_type.as_adt()?.name(db).display_no_db(ctx.edition).to_smolstr(); diff --git a/crates/ide-diagnostics/src/lib.rs b/crates/ide-diagnostics/src/lib.rs index 49b3234a11..aec68b55c7 100644 --- a/crates/ide-diagnostics/src/lib.rs +++ b/crates/ide-diagnostics/src/lib.rs @@ -29,13 +29,19 @@ extern crate rustc_driver as _; mod handlers { + pub(crate) mod array_pattern_without_fixed_length; pub(crate) mod await_outside_of_async; pub(crate) mod bad_rtn; pub(crate) mod break_outside_of_loop; + pub(crate) mod cannot_be_dereferenced; + pub(crate) mod cannot_implicitly_deref_trait_object; + pub(crate) mod cannot_index_into; pub(crate) mod duplicate_field; pub(crate) mod elided_lifetimes_in_path; pub(crate) mod expected_array_or_slice_pat; pub(crate) mod expected_function; + pub(crate) mod explicit_drop_method_use; + pub(crate) mod fru_in_destructuring_assignment; pub(crate) mod functional_record_update_on_non_struct; pub(crate) mod generic_args_prohibited; pub(crate) mod generic_default_refers_to_self; @@ -47,8 +53,10 @@ mod handlers { pub(crate) mod invalid_cast; pub(crate) mod invalid_derive_target; pub(crate) mod invalid_lhs_of_assignment; + pub(crate) mod invalid_range_pat_type; pub(crate) mod macro_error; pub(crate) mod malformed_derive; + pub(crate) mod method_call_illegal_sized_bound; pub(crate) mod mismatched_arg_count; pub(crate) mod mismatched_array_pat_len; pub(crate) mod missing_fields; @@ -57,9 +65,11 @@ mod handlers { pub(crate) mod missing_unsafe; pub(crate) mod moved_out_of_ref; pub(crate) mod mutability_errors; + pub(crate) mod mutable_ref; pub(crate) mod no_such_field; pub(crate) mod non_exhaustive_let; pub(crate) mod non_exhaustive_record_expr; + pub(crate) mod non_exhaustive_record_pat; pub(crate) mod parenthesized_generic_args_without_fn_trait; pub(crate) mod pattern_arg_in_extern_fn; pub(crate) mod private_assoc_item; @@ -426,7 +436,15 @@ pub fn semantic_diagnostics( for diag in diags { let d = match diag { AnyDiagnostic::AwaitOutsideOfAsync(d) => handlers::await_outside_of_async::await_outside_of_async(&ctx, &d), + AnyDiagnostic::CannotBeDereferenced(d) => handlers::cannot_be_dereferenced::cannot_be_dereferenced(&ctx, &d), + AnyDiagnostic::CannotImplicitlyDerefTraitObject(d) => handlers::cannot_implicitly_deref_trait_object::cannot_implicitly_deref_trait_object(&ctx, &d), + AnyDiagnostic::CannotIndexInto(d) => handlers::cannot_index_into::cannot_index_into(&ctx, &d), AnyDiagnostic::CastToUnsized(d) => handlers::invalid_cast::cast_to_unsized(&ctx, &d), + AnyDiagnostic::ArrayPatternWithoutFixedLength(d) => { + handlers::array_pattern_without_fixed_length::array_pattern_without_fixed_length( + &ctx, &d, + ) + } AnyDiagnostic::ExpectedArrayOrSlicePat(d) => handlers::expected_array_or_slice_pat::expected_array_or_slice_pat(&ctx, &d), AnyDiagnostic::ExpectedFunction(d) => handlers::expected_function::expected_function(&ctx, &d), AnyDiagnostic::FunctionalRecordUpdateOnNonStruct(d) => handlers::functional_record_update_on_non_struct::functional_record_update_on_non_struct(&ctx, &d), @@ -452,12 +470,14 @@ pub fn semantic_diagnostics( continue; }, AnyDiagnostic::MalformedDerive(d) => handlers::malformed_derive::malformed_derive(&ctx, &d), + AnyDiagnostic::MethodCallIllegalSizedBound(d) => handlers::method_call_illegal_sized_bound::method_call_illegal_sized_bound(&ctx, &d), AnyDiagnostic::MismatchedArgCount(d) => handlers::mismatched_arg_count::mismatched_arg_count(&ctx, &d), AnyDiagnostic::MismatchedArrayPatLen(d) => handlers::mismatched_array_pat_len::mismatched_array_pat_len(&ctx, &d), AnyDiagnostic::MissingFields(d) => handlers::missing_fields::missing_fields(&ctx, &d), AnyDiagnostic::MissingMatchArms(d) => handlers::missing_match_arms::missing_match_arms(&ctx, &d), AnyDiagnostic::MissingUnsafe(d) => handlers::missing_unsafe::missing_unsafe(&ctx, &d), AnyDiagnostic::MovedOutOfRef(d) => handlers::moved_out_of_ref::moved_out_of_ref(&ctx, &d), + AnyDiagnostic::MutableRefBinding(d) => handlers::mutable_ref::mutable_ref_binding(&ctx, &d), AnyDiagnostic::NeedMut(d) => match handlers::mutability_errors::need_mut(&ctx, &d) { Some(it) => it, None => continue, @@ -466,6 +486,9 @@ pub fn semantic_diagnostics( AnyDiagnostic::NonExhaustiveRecordExpr(d) => { handlers::non_exhaustive_record_expr::non_exhaustive_record_expr(&ctx, &d) } + AnyDiagnostic::NonExhaustiveRecordPat(d) => { + handlers::non_exhaustive_record_pat::non_exhaustive_record_pat(&ctx, &d) + } AnyDiagnostic::NoSuchField(d) => handlers::no_such_field::no_such_field(&ctx, &d), AnyDiagnostic::DuplicateField(d) => handlers::duplicate_field::duplicate_field(&ctx, &d), AnyDiagnostic::PrivateAssocItem(d) => handlers::private_assoc_item::private_assoc_item(&ctx, &d), @@ -519,10 +542,13 @@ pub fn semantic_diagnostics( AnyDiagnostic::ElidedLifetimesInPath(d) => handlers::elided_lifetimes_in_path::elided_lifetimes_in_path(&ctx, &d), AnyDiagnostic::GenericDefaultRefersToSelf(d) => handlers::generic_default_refers_to_self::generic_default_refers_to_self(&ctx, &d), AnyDiagnostic::InvalidLhsOfAssignment(d) => handlers::invalid_lhs_of_assignment::invalid_lhs_of_assignment(&ctx, &d), + AnyDiagnostic::InvalidRangePatType(d) => handlers::invalid_range_pat_type::invalid_range_pat_type(&ctx, &d), AnyDiagnostic::TypeMustBeKnown(d) => handlers::type_must_be_known::type_must_be_known(&ctx, &d), AnyDiagnostic::PatternArgInExternFn(d) => handlers::pattern_arg_in_extern_fn::pattern_arg_in_extern_fn(&ctx, &d), AnyDiagnostic::UnionExprMustHaveExactlyOneField(d) => handlers::union_expr_must_have_exactly_one_field::union_expr_must_have_exactly_one_field(&ctx, &d), AnyDiagnostic::UnimplementedTrait(d) => handlers::unimplemented_trait::unimplemented_trait(&ctx, &d), + AnyDiagnostic::FruInDestructuringAssignment(d) => handlers::fru_in_destructuring_assignment::fru_in_destructuring_assignment(&ctx, &d), + AnyDiagnostic::ExplicitDropMethodUse(d) => handlers::explicit_drop_method_use::explicit_drop_method_use(&ctx, &d), }; res.push(d) } diff --git a/crates/ide/src/goto_type_definition.rs b/crates/ide/src/goto_type_definition.rs index ae208fe1b5..ffd144a827 100644 --- a/crates/ide/src/goto_type_definition.rs +++ b/crates/ide/src/goto_type_definition.rs @@ -88,7 +88,7 @@ pub(crate) fn goto_type_definition( ast::Pat(it) => sema.type_of_pat(&it)?.original, ast::SelfParam(it) => sema.type_of_self(&it)?, ast::Type(it) => sema.resolve_type(&it)?, - ast::RecordField(it) => sema.to_def(&it)?.ty(db).to_type(db), + ast::RecordField(it) => sema.to_def(&it)?.ty(db), // can't match on RecordExprField directly as `ast::Expr` will match an iteration too early otherwise ast::NameRef(it) => { if let Some(record_field) = ast::RecordExprField::for_name_ref(&it) { diff --git a/crates/ide/src/hover.rs b/crates/ide/src/hover.rs index df1fcecc99..c3a8e0362f 100644 --- a/crates/ide/src/hover.rs +++ b/crates/ide/src/hover.rs @@ -469,7 +469,7 @@ pub(crate) fn hover_for_definition( Definition::Local(it) => Some(it.ty(db)), Definition::GenericParam(hir::GenericParam::ConstParam(it)) => Some(it.ty(db)), Definition::GenericParam(hir::GenericParam::TypeParam(it)) => Some(it.ty(db)), - Definition::Field(field) => Some(field.ty(db).to_type(db)), + Definition::Field(field) => Some(field.ty(db)), Definition::TupleField(it) => Some(it.ty(db)), Definition::Function(it) => Some(it.ty(db)), Definition::Adt(it) => Some(it.ty(db)), @@ -630,7 +630,7 @@ fn goto_type_action_for_def( let ty = match def { Definition::Local(it) => Some(it.ty(db)), - Definition::Field(field) => Some(field.ty(db).to_type(db)), + Definition::Field(field) => Some(field.ty(db)), Definition::TupleField(field) => Some(field.ty(db)), Definition::Const(it) => Some(it.ty(db)), Definition::Static(it) => Some(it.ty(db)), diff --git a/crates/ide/src/hover/render.rs b/crates/ide/src/hover/render.rs index e08bbc5c21..da4f185d75 100644 --- a/crates/ide/src/hover/render.rs +++ b/crates/ide/src/hover/render.rs @@ -664,14 +664,14 @@ pub(super) fn definition( } let drop_info = match def { Definition::Field(field) => { - DropInfo { drop_glue: field.ty(db).to_type(db).drop_glue(db), has_dtor: None } + DropInfo { drop_glue: field.ty(db).drop_glue(db), has_dtor: None } } Definition::Adt(Adt::Struct(strukt)) => { - let struct_drop_glue = strukt.ty_params(db).drop_glue(db); + let struct_drop_glue = strukt.ty(db).drop_glue(db); let mut fields_drop_glue = strukt .fields(db) .iter() - .map(|field| field.ty(db).to_type(db).drop_glue(db)) + .map(|field| field.ty(db).drop_glue(db)) .max() .unwrap_or(DropGlue::None); let has_dtor = match (fields_drop_glue, struct_drop_glue) { @@ -688,10 +688,10 @@ pub(super) fn definition( // Unions cannot have fields with drop glue. Definition::Adt(Adt::Union(union)) => DropInfo { drop_glue: DropGlue::None, - has_dtor: Some(union.ty_params(db).drop_glue(db) != DropGlue::None), + has_dtor: Some(union.ty(db).drop_glue(db) != DropGlue::None), }, Definition::Adt(Adt::Enum(enum_)) => { - let enum_drop_glue = enum_.ty_params(db).drop_glue(db); + let enum_drop_glue = enum_.ty(db).drop_glue(db); let fields_drop_glue = enum_ .variants(db) .iter() @@ -699,7 +699,7 @@ pub(super) fn definition( variant .fields(db) .iter() - .map(|field| field.ty(db).to_type(db).drop_glue(db)) + .map(|field| field.ty(db).drop_glue(db)) .max() .unwrap_or(DropGlue::None) }) @@ -714,13 +714,13 @@ pub(super) fn definition( let fields_drop_glue = variant .fields(db) .iter() - .map(|field| field.ty(db).to_type(db).drop_glue(db)) + .map(|field| field.ty(db).drop_glue(db)) .max() .unwrap_or(DropGlue::None); DropInfo { drop_glue: fields_drop_glue, has_dtor: None } } Definition::TypeAlias(type_alias) => { - DropInfo { drop_glue: type_alias.ty_params(db).drop_glue(db), has_dtor: None } + DropInfo { drop_glue: type_alias.ty(db).drop_glue(db), has_dtor: None } } Definition::Local(local) => { DropInfo { drop_glue: local.ty(db).drop_glue(db), has_dtor: None } diff --git a/crates/ide/src/hover/tests.rs b/crates/ide/src/hover/tests.rs index bf5e0be374..30644fe2db 100644 --- a/crates/ide/src/hover/tests.rs +++ b/crates/ide/src/hover/tests.rs @@ -3153,6 +3153,35 @@ fn test_hover_layout_of_enum() { } #[test] +fn test_hover_layout_nonzero_type_alias() { + check( + r#"//- minicore: non_zero +use core::num; +trait Trait { type Inner; } +impl Trait for u8 { type Inner = num::NonZeroU8; } +#[repr(transparent)] +struct NonZero<T: Trait>(T::Inner); +type NonZeroU8$0 = NonZero<u8>; +"#, + expect![[r#" + *NonZeroU8* + + ```rust + ra_test_fixture + ``` + + ```rust + type NonZeroU8 = NonZero<u8> + ``` + + --- + + size = 1, align = 1, niches = 1, no Drop + "#]], + ); +} + +#[test] fn test_hover_layout_padding_info() { check( r#"struct $0Foo { diff --git a/crates/ide/src/inlay_hints/implicit_drop.rs b/crates/ide/src/inlay_hints/implicit_drop.rs index 57aba51b4e..f6b13cbe0b 100644 --- a/crates/ide/src/inlay_hints/implicit_drop.rs +++ b/crates/ide/src/inlay_hints/implicit_drop.rs @@ -43,7 +43,7 @@ pub(super) fn hints( for (_, bb) in mir.basic_blocks.iter() { let terminator = bb.terminator.as_ref()?; - if let TerminatorKind::Drop { place, .. } = terminator.kind { + if let TerminatorKind::Drop { place, .. } = &terminator.kind { if !place.projection.is_empty() { continue; // Ignore complex cases for now } diff --git a/crates/ide/src/navigation_target.rs b/crates/ide/src/navigation_target.rs index f70bb3353f..b8c14dc09f 100644 --- a/crates/ide/src/navigation_target.rs +++ b/crates/ide/src/navigation_target.rs @@ -964,7 +964,7 @@ pub(crate) fn orig_range_with_focus_r( // *should* contain the name _ => { let call = call(); - let kind = call.kind; + let kind = &call.kind; let range = kind.clone().original_call_range_with_input(db); //If the focus range is in the attribute/derive body, we // need to point the call site to the entire body, if not, fall back diff --git a/crates/ide/src/rename.rs b/crates/ide/src/rename.rs index b664187932..26c6776107 100644 --- a/crates/ide/src/rename.rs +++ b/crates/ide/src/rename.rs @@ -16,7 +16,7 @@ use std::fmt::Write; use stdx::{always, format_to, never}; use syntax::{ AstNode, SyntaxKind, SyntaxNode, TextRange, TextSize, - ast::{self, HasArgList, make, prec::ExprPrecedence}, + ast::{self, HasArgList, prec::ExprPrecedence}, }; use ide_db::text_edit::TextEdit; @@ -522,11 +522,11 @@ fn rename_to_self( }; let first_param_ty = first_param.ty(); let impl_ty = impl_.self_ty(sema.db); - let (ty, self_param) = if impl_ty.remove_ref().is_some() { + let (ty, self_param) = if impl_ty.is_reference() { // if the impl is a ref to the type we can just match the `&T` with self directly (first_param_ty.clone(), "self") } else { - first_param_ty.remove_ref().map_or((first_param_ty.clone(), "self"), |ty| { + first_param_ty.as_reference_inner().map_or((first_param_ty.clone(), "self"), |ty| { (ty, if first_param_ty.is_mutable_reference() { "&mut self" } else { "&self" }) }) }; @@ -818,11 +818,12 @@ fn rename_elided_lifetime( let mut builder = SourceChangeBuilder::new(position.file_id); let editor = builder.make_editor(&root); + let make = editor.make(); - editor.replace(lifetime_token, make::lifetime(new_name).syntax().clone()); + editor.replace(lifetime_token, make.lifetime(new_name).syntax().clone()); if let Some(has_generic_params) = parent.ancestors().find_map(ast::AnyHasGenericParams::cast) { - let lifetime_param = make::lifetime_param(make::lifetime(new_name)); + let lifetime_param = make.lifetime_param(make.lifetime(new_name)); editor.add_generic_param(&has_generic_params, lifetime_param.into()); } diff --git a/crates/ide/src/signature_help.rs b/crates/ide/src/signature_help.rs index edcf0dc22b..0022c1148a 100644 --- a/crates/ide/src/signature_help.rs +++ b/crates/ide/src/signature_help.rs @@ -174,9 +174,15 @@ fn signature_help_for_call( match callable.kind() { hir::CallableKind::Function(func) => { res.doc = func.docs(db).map(Documentation::into_owned); + if func.is_const(db) { + format_to!(res.signature, "const "); + } if func.is_async(db) { format_to!(res.signature, "async "); } + if func.is_unsafe(db) { + format_to!(res.signature, "unsafe "); + } format_to!(res.signature, "fn {}", func.name(db).display(db, edition)); let generic_params = GenericDef::Function(func) @@ -529,7 +535,7 @@ fn signature_help_for_tuple_struct_pat( pat.syntax(), token, pat.fields(), - fields.into_iter().map(|it| it.ty(db).to_type(db)), + fields.into_iter().map(|it| it.ty(db)), display_target, )) } @@ -2664,4 +2670,76 @@ fn main() { "#]], ); } + + #[test] + fn test_const_function() { + check( + r#" +//- minicore: sized, fn +pub const fn foo(x: u32, y: u32) -> u32 { x + y } + +fn main() { + foo($0) +} + "#, + expect![[r#" + const fn foo(x: u32, y: u32) -> u32 + ^^^^^^ ------ + "#]], + ); + } + + #[test] + fn test_unsafe_function() { + check( + r#" +//- minicore: sized, fn +pub unsafe fn foo(x: u32, y: u32) -> u32 { x + y } + +fn main() { + unsafe { foo($0) } +} + "#, + expect![[r#" + unsafe fn foo(x: u32, y: u32) -> u32 + ^^^^^^ ------ + "#]], + ); + } + + #[test] + fn test_const_unsafe_function() { + check( + r#" +//- minicore: sized, fn +pub const unsafe fn foo(x: u32, y: u32) -> u32 { x + y } + +fn main() { + unsafe { foo($0) } +} + "#, + expect![[r#" + const unsafe fn foo(x: u32, y: u32) -> u32 + ^^^^^^ ------ + "#]], + ); + } + + #[test] + fn test_async_unsafe_function() { + check( + r#" +//- minicore: sized, fn, future +pub async unsafe fn foo(x: u32, y: u32) -> u32 { x + y } + +fn main() { + unsafe { foo($0) } +} + "#, + expect![[r#" + async unsafe fn foo(x: u32, y: u32) -> u32 + ^^^^^^ ------ + "#]], + ); + } } diff --git a/crates/ide/src/syntax_highlighting/inject.rs b/crates/ide/src/syntax_highlighting/inject.rs index 76bb06328b..9af6d67b59 100644 --- a/crates/ide/src/syntax_highlighting/inject.rs +++ b/crates/ide/src/syntax_highlighting/inject.rs @@ -177,7 +177,7 @@ pub(super) fn doc_comment( match sema.first_crate(vfs_file_id) { Some(krate) => krate.base().data(sema.db).proc_macro_cwd.clone(), None => { - // Arbitrarily pick /, since from_single_file treats this file as as /main.rs anyway. + // Arbitrarily pick /, since from_single_file treats this file as /main.rs anyway. Arc::new(ide_db::base_db::AbsPathBuf::try_from("/").unwrap()) } } diff --git a/crates/ide/src/view_memory_layout.rs b/crates/ide/src/view_memory_layout.rs index 1b9df9722b..03bde6f3e5 100644 --- a/crates/ide/src/view_memory_layout.rs +++ b/crates/ide/src/view_memory_layout.rs @@ -98,7 +98,7 @@ pub(crate) fn view_memory_layout( Definition::BuiltinType(it) => it.ty(db), Definition::SelfType(it) => it.self_ty(db), Definition::Local(it) => it.ty(db), - Definition::Field(it) => it.ty(db).to_type(db), + Definition::Field(it) => it.ty(db), Definition::Const(it) => it.ty(db), Definition::Static(it) => it.ty(db), _ => return None, diff --git a/crates/intern/src/symbol/symbols.rs b/crates/intern/src/symbol/symbols.rs index ac6daaf006..e5f66a202e 100644 --- a/crates/intern/src/symbol/symbols.rs +++ b/crates/intern/src/symbol/symbols.rs @@ -475,6 +475,7 @@ define_symbols! { PartialOrd, CoercePointee, path, + pattern_type, Pending, phantom_data, pieces, @@ -645,6 +646,7 @@ define_symbols! { eii_internals, field_representing_type_raw, intrinsics, + core_intrinsics, link_cfg, more_maybe_bounds, negative_bounds, @@ -670,4 +672,7 @@ define_symbols! { deref_patterns, mut_ref, type_changing_struct_update, + RangeMin, + RangeMax, + RangeSub, } diff --git a/crates/load-cargo/Cargo.toml b/crates/load-cargo/Cargo.toml index 91b012e050..7b5e51fba5 100644 --- a/crates/load-cargo/Cargo.toml +++ b/crates/load-cargo/Cargo.toml @@ -30,7 +30,7 @@ vfs.workspace = true intern.workspace = true [features] -in-rust-tree = ["hir-expand/in-rust-tree"] +in-rust-tree = ["hir-expand/in-rust-tree", "proc-macro-api/in-rust-tree"] [lints] workspace = true diff --git a/crates/load-cargo/src/lib.rs b/crates/load-cargo/src/lib.rs index 801eaeaea9..fd90bc404a 100644 --- a/crates/load-cargo/src/lib.rs +++ b/crates/load-cargo/src/lib.rs @@ -609,11 +609,9 @@ impl ProcMacroExpander for Expander { SubRequest::LineColumn { file_id, ast_id, offset } => { let range = resolve_sub_span(db, file_id, ast_id, TextRange::empty(TextSize::from(offset))); - let source = db.file_text(range.file_id.file_id(db)).text(db); - let line_index = ide_db::line_index::LineIndex::new(source); - let (line, column) = line_index - .try_line_col(range.range.start()) - .map(|lc| (lc.line + 1, lc.col + 1)) + let (line, column) = db + .line_column(range.file_id.file_id(db), range.range.start()) + .map(|(line, col)| (line + 1, col + 1)) .unwrap_or((1, 1)); // proc_macro::Span line/column are 1-based Ok(SubResponse::LineColumnResult { line, column }) @@ -726,6 +724,8 @@ impl ProcMacroExpander for Expander { Ok(SubResponse::SpanParentResult { parent_span: None }) } + // FIXME: implement this + SubRequest::SpanJoin { .. } => Ok(SubResponse::SpanJoinResult { span: None }), }; match self.0.expand( subtree.view(), diff --git a/crates/mbe/src/tests.rs b/crates/mbe/src/tests.rs index 271dfc877b..f3ebb663d9 100644 --- a/crates/mbe/src/tests.rs +++ b/crates/mbe/src/tests.rs @@ -74,7 +74,7 @@ fn check_( "{}", syntax_bridge::prettify_macro_expansion::prettify_macro_expansion( node.syntax_node(), - &mut |_| None, + &mut |_, _| None, |_| () ) ); diff --git a/crates/parser/src/grammar/patterns.rs b/crates/parser/src/grammar/patterns.rs index 5726f085a0..29fa11720a 100644 --- a/crates/parser/src/grammar/patterns.rs +++ b/crates/parser/src/grammar/patterns.rs @@ -12,6 +12,7 @@ pub(super) const PATTERN_FIRST: TokenSet = T![_], T![-], T![.], + T![!], ])); const PAT_TOP_FIRST: TokenSet = PATTERN_FIRST.union(TokenSet::new(&[T![|]])); @@ -256,6 +257,7 @@ fn atom_pat(p: &mut Parser<'_>, recovery_set: TokenSet) -> Option<CompletedMarke T![&] => ref_pat(p), T!['('] => tuple_pat(p), T!['['] => slice_pat(p), + T![!] => not_null_pat(p), _ => { p.err_recover("expected pattern", recovery_set); @@ -435,6 +437,18 @@ fn ref_pat(p: &mut Parser<'_>) -> CompletedMarker { m.complete(p, REF_PAT) } +// test not_null_pat +// fn main() { +// let (!a | !&0) = (); +// } +fn not_null_pat(p: &mut Parser<'_>) -> CompletedMarker { + assert!(p.at(T![!])); + let m = p.start(); + p.bump(T![!]); + pattern_single(p); + m.complete(p, NOT_NULL) +} + // test tuple_pat // fn main() { // let (a, b, ..) = (); diff --git a/crates/parser/src/grammar/types.rs b/crates/parser/src/grammar/types.rs index 667bb68c64..f92afde60d 100644 --- a/crates/parser/src/grammar/types.rs +++ b/crates/parser/src/grammar/types.rs @@ -1,3 +1,5 @@ +use crate::grammar::entry::prefix::pat; + use super::*; pub(super) const TYPE_FIRST: TokenSet = paths::PATH_FIRST.union(TokenSet::new(&[ @@ -341,6 +343,10 @@ fn bare_dyn_trait_type(p: &mut Parser<'_>) { // type B = crate::foo!(); fn path_or_macro_type(p: &mut Parser<'_>, allow_bounds: bool) { assert!(paths::is_path_start(p)); + if p.at_contextual_kw(T![builtin]) && p.nth_at(1, T![#]) { + pattern_type(p); + return; + } let r = p.start(); let m = p.start(); @@ -411,3 +417,23 @@ pub(super) fn opt_type_bounds_as_dyn_trait_type( // Finally precede everything with DYN_TRAIT_TYPE m.precede(p).complete(p, DYN_TRAIT_TYPE) } + +// test pattern_type +// type T = builtin#pattern_type (u8 is 0..10); +fn pattern_type(p: &mut Parser<'_>) { + let m = p.start(); + p.bump_remap(T![builtin]); + p.bump(T![#]); + if p.eat_contextual_kw(T![pattern_type]) { + p.expect(T!['(']); + type_(p); + if !p.eat_contextual_kw(T![is]) { + p.error("expected `is`") + } + pat(p); + p.expect(T![')']); + m.complete(p, PATTERN_TYPE); + } else { + m.abandon(p); + } +} diff --git a/crates/parser/src/syntax_kind/generated.rs b/crates/parser/src/syntax_kind/generated.rs index b1867275ce..dd675e0834 100644 --- a/crates/parser/src/syntax_kind/generated.rs +++ b/crates/parser/src/syntax_kind/generated.rs @@ -127,6 +127,7 @@ pub enum SyntaxKind { GLOBAL_ASM_KW, INLATEOUT_KW, INOUT_KW, + IS_KW, LABEL_KW, LATEOUT_KW, MACRO_RULES_KW, @@ -135,9 +136,11 @@ pub enum SyntaxKind { NOMEM_KW, NORETURN_KW, NOSTACK_KW, + NULL_KW, OFFSET_OF_KW, OPTIONS_KW, OUT_KW, + PATTERN_TYPE_KW, PRESERVES_FLAGS_KW, PURE_KW, RAW_KW, @@ -254,6 +257,7 @@ pub enum SyntaxKind { NAME, NAME_REF, NEVER_TYPE, + NOT_NULL, OFFSET_OF_EXPR, OR_PAT, PARAM, @@ -268,6 +272,7 @@ pub enum SyntaxKind { PATH_PAT, PATH_SEGMENT, PATH_TYPE, + PATTERN_TYPE, PREFIX_EXPR, PTR_TYPE, RANGE_EXPR, @@ -439,6 +444,7 @@ impl SyntaxKind { | NAME | NAME_REF | NEVER_TYPE + | NOT_NULL | OFFSET_OF_EXPR | OR_PAT | PARAM @@ -453,6 +459,7 @@ impl SyntaxKind { | PATH_PAT | PATH_SEGMENT | PATH_TYPE + | PATTERN_TYPE | PREFIX_EXPR | PTR_TYPE | RANGE_EXPR @@ -636,6 +643,7 @@ impl SyntaxKind { GLOBAL_ASM_KW => "global_asm", INLATEOUT_KW => "inlateout", INOUT_KW => "inout", + IS_KW => "is", LABEL_KW => "label", LATEOUT_KW => "lateout", MACRO_RULES_KW => "macro_rules", @@ -644,9 +652,11 @@ impl SyntaxKind { NOMEM_KW => "nomem", NORETURN_KW => "noreturn", NOSTACK_KW => "nostack", + NULL_KW => "null", OFFSET_OF_KW => "offset_of", OPTIONS_KW => "options", OUT_KW => "out", + PATTERN_TYPE_KW => "pattern_type", PRESERVES_FLAGS_KW => "preserves_flags", PURE_KW => "pure", RAW_KW => "raw", @@ -742,6 +752,7 @@ impl SyntaxKind { GLOBAL_ASM_KW => true, INLATEOUT_KW => true, INOUT_KW => true, + IS_KW => true, LABEL_KW => true, LATEOUT_KW => true, MACRO_RULES_KW => true, @@ -750,9 +761,11 @@ impl SyntaxKind { NOMEM_KW => true, NORETURN_KW => true, NOSTACK_KW => true, + NULL_KW => true, OFFSET_OF_KW => true, OPTIONS_KW => true, OUT_KW => true, + PATTERN_TYPE_KW => true, PRESERVES_FLAGS_KW => true, PURE_KW => true, RAW_KW => true, @@ -836,6 +849,7 @@ impl SyntaxKind { GLOBAL_ASM_KW => true, INLATEOUT_KW => true, INOUT_KW => true, + IS_KW => true, LABEL_KW => true, LATEOUT_KW => true, MACRO_RULES_KW => true, @@ -844,9 +858,11 @@ impl SyntaxKind { NOMEM_KW => true, NORETURN_KW => true, NOSTACK_KW => true, + NULL_KW => true, OFFSET_OF_KW => true, OPTIONS_KW => true, OUT_KW => true, + PATTERN_TYPE_KW => true, PRESERVES_FLAGS_KW => true, PURE_KW => true, RAW_KW => true, @@ -993,6 +1009,7 @@ impl SyntaxKind { "global_asm" => GLOBAL_ASM_KW, "inlateout" => INLATEOUT_KW, "inout" => INOUT_KW, + "is" => IS_KW, "label" => LABEL_KW, "lateout" => LATEOUT_KW, "macro_rules" => MACRO_RULES_KW, @@ -1001,9 +1018,11 @@ impl SyntaxKind { "nomem" => NOMEM_KW, "noreturn" => NORETURN_KW, "nostack" => NOSTACK_KW, + "null" => NULL_KW, "offset_of" => OFFSET_OF_KW, "options" => OPTIONS_KW, "out" => OUT_KW, + "pattern_type" => PATTERN_TYPE_KW, "preserves_flags" => PRESERVES_FLAGS_KW, "pure" => PURE_KW, "raw" => RAW_KW, @@ -1168,6 +1187,7 @@ macro_rules ! T_ { [global_asm] => { $ crate :: SyntaxKind :: GLOBAL_ASM_KW }; [inlateout] => { $ crate :: SyntaxKind :: INLATEOUT_KW }; [inout] => { $ crate :: SyntaxKind :: INOUT_KW }; + [is] => { $ crate :: SyntaxKind :: IS_KW }; [label] => { $ crate :: SyntaxKind :: LABEL_KW }; [lateout] => { $ crate :: SyntaxKind :: LATEOUT_KW }; [macro_rules] => { $ crate :: SyntaxKind :: MACRO_RULES_KW }; @@ -1176,9 +1196,11 @@ macro_rules ! T_ { [nomem] => { $ crate :: SyntaxKind :: NOMEM_KW }; [noreturn] => { $ crate :: SyntaxKind :: NORETURN_KW }; [nostack] => { $ crate :: SyntaxKind :: NOSTACK_KW }; + [null] => { $ crate :: SyntaxKind :: NULL_KW }; [offset_of] => { $ crate :: SyntaxKind :: OFFSET_OF_KW }; [options] => { $ crate :: SyntaxKind :: OPTIONS_KW }; [out] => { $ crate :: SyntaxKind :: OUT_KW }; + [pattern_type] => { $ crate :: SyntaxKind :: PATTERN_TYPE_KW }; [preserves_flags] => { $ crate :: SyntaxKind :: PRESERVES_FLAGS_KW }; [pure] => { $ crate :: SyntaxKind :: PURE_KW }; [raw] => { $ crate :: SyntaxKind :: RAW_KW }; diff --git a/crates/parser/test_data/generated/runner.rs b/crates/parser/test_data/generated/runner.rs index 7aaf270a77..ccf8b89be7 100644 --- a/crates/parser/test_data/generated/runner.rs +++ b/crates/parser/test_data/generated/runner.rs @@ -474,6 +474,8 @@ mod ok { run_and_expect_no_errors("test_data/parser/inline/ok/nocontentexpr_after_item.rs"); } #[test] + fn not_null_pat() { run_and_expect_no_errors("test_data/parser/inline/ok/not_null_pat.rs"); } + #[test] fn offset_of_parens() { run_and_expect_no_errors("test_data/parser/inline/ok/offset_of_parens.rs"); } @@ -506,6 +508,8 @@ mod ok { run_and_expect_no_errors("test_data/parser/inline/ok/path_type_with_bounds.rs"); } #[test] + fn pattern_type() { run_and_expect_no_errors("test_data/parser/inline/ok/pattern_type.rs"); } + #[test] fn placeholder_pat() { run_and_expect_no_errors("test_data/parser/inline/ok/placeholder_pat.rs"); } diff --git a/crates/parser/test_data/parser/err/0032_match_arms_inner_attrs.rast b/crates/parser/test_data/parser/err/0032_match_arms_inner_attrs.rast index 2334b730e4..234070bcb1 100644 --- a/crates/parser/test_data/parser/err/0032_match_arms_inner_attrs.rast +++ b/crates/parser/test_data/parser/err/0032_match_arms_inner_attrs.rast @@ -37,27 +37,26 @@ SOURCE_FILE MATCH_ARM ATTR POUND "#" - ERROR + NOT_NULL BANG "!" - ARRAY_EXPR - L_BRACK "[" - CALL_EXPR - PATH_EXPR + SLICE_PAT + L_BRACK "[" + TUPLE_STRUCT_PAT PATH PATH_SEGMENT NAME_REF IDENT "doc" - ARG_LIST L_PAREN "(" - LITERAL - STRING "\"Not allowed here\"" + LITERAL_PAT + LITERAL + STRING "\"Not allowed here\"" R_PAREN ")" - R_BRACK "]" - WHITESPACE "\n " - MATCH_ARM - WILDCARD_PAT + R_BRACK "]" + WHITESPACE "\n " + UNDERSCORE_EXPR UNDERSCORE "_" - WHITESPACE " " + WHITESPACE " " + MATCH_ARM FAT_ARROW "=>" WHITESPACE " " TUPLE_EXPR @@ -103,22 +102,21 @@ SOURCE_FILE MATCH_ARM ATTR POUND "#" - ERROR + NOT_NULL BANG "!" - ARRAY_EXPR - L_BRACK "[" - CALL_EXPR - PATH_EXPR + SLICE_PAT + L_BRACK "[" + TUPLE_STRUCT_PAT PATH PATH_SEGMENT NAME_REF IDENT "doc" - ARG_LIST L_PAREN "(" - LITERAL - STRING "\"Nor here\"" + LITERAL_PAT + LITERAL + STRING "\"Nor here\"" R_PAREN ")" - R_BRACK "]" + R_BRACK "]" WHITESPACE "\n " R_CURLY "}" WHITESPACE "\n\n " @@ -146,27 +144,26 @@ SOURCE_FILE WHITESPACE "\n " ATTR POUND "#" - ERROR + NOT_NULL BANG "!" - ARRAY_EXPR - L_BRACK "[" - CALL_EXPR - PATH_EXPR + SLICE_PAT + L_BRACK "[" + TUPLE_STRUCT_PAT PATH PATH_SEGMENT NAME_REF IDENT "doc" - ARG_LIST L_PAREN "(" - LITERAL - STRING "\"Nor here\"" + LITERAL_PAT + LITERAL + STRING "\"Nor here\"" R_PAREN ")" - R_BRACK "]" - WHITESPACE "\n " - MATCH_ARM - WILDCARD_PAT + R_BRACK "]" + WHITESPACE "\n " + UNDERSCORE_EXPR UNDERSCORE "_" - WHITESPACE " " + WHITESPACE " " + MATCH_ARM FAT_ARROW "=>" WHITESPACE " " TUPLE_EXPR @@ -190,13 +187,13 @@ SOURCE_FILE R_CURLY "}" WHITESPACE "\n" error 52: expected L_BRACK -error 52: expected pattern -error 53: expected FAT_ARROW -error 78: expected `,` +error 78: expected FAT_ARROW +error 88: expected `,` +error 89: expected pattern error 161: expected L_BRACK -error 161: expected pattern -error 162: expected FAT_ARROW +error 179: expected FAT_ARROW +error 179: expected expression error 232: expected L_BRACK -error 232: expected pattern -error 233: expected FAT_ARROW -error 250: expected `,` +error 250: expected FAT_ARROW +error 260: expected `,` +error 261: expected pattern diff --git a/crates/parser/test_data/parser/inline/ok/not_null_pat.rast b/crates/parser/test_data/parser/inline/ok/not_null_pat.rast new file mode 100644 index 0000000000..7a2e655201 --- /dev/null +++ b/crates/parser/test_data/parser/inline/ok/not_null_pat.rast @@ -0,0 +1,46 @@ +SOURCE_FILE + FN + FN_KW "fn" + WHITESPACE " " + NAME + IDENT "main" + PARAM_LIST + L_PAREN "(" + R_PAREN ")" + WHITESPACE " " + BLOCK_EXPR + STMT_LIST + L_CURLY "{" + WHITESPACE "\n " + LET_STMT + LET_KW "let" + WHITESPACE " " + PAREN_PAT + L_PAREN "(" + OR_PAT + NOT_NULL + BANG "!" + IDENT_PAT + NAME + IDENT "a" + WHITESPACE " " + PIPE "|" + WHITESPACE " " + NOT_NULL + BANG "!" + REF_PAT + AMP "&" + LITERAL_PAT + LITERAL + INT_NUMBER "0" + R_PAREN ")" + WHITESPACE " " + EQ "=" + WHITESPACE " " + TUPLE_EXPR + L_PAREN "(" + R_PAREN ")" + SEMICOLON ";" + WHITESPACE "\n" + R_CURLY "}" + WHITESPACE "\n" diff --git a/crates/parser/test_data/parser/inline/ok/not_null_pat.rs b/crates/parser/test_data/parser/inline/ok/not_null_pat.rs new file mode 100644 index 0000000000..f44fae5a77 --- /dev/null +++ b/crates/parser/test_data/parser/inline/ok/not_null_pat.rs @@ -0,0 +1,3 @@ +fn main() { + let (!a | !&0) = (); +} diff --git a/crates/parser/test_data/parser/inline/ok/pattern_type.rast b/crates/parser/test_data/parser/inline/ok/pattern_type.rast new file mode 100644 index 0000000000..c9caeb1f9b --- /dev/null +++ b/crates/parser/test_data/parser/inline/ok/pattern_type.rast @@ -0,0 +1,34 @@ +SOURCE_FILE + TYPE_ALIAS + TYPE_KW "type" + WHITESPACE " " + NAME + IDENT "T" + WHITESPACE " " + EQ "=" + WHITESPACE " " + PATTERN_TYPE + BUILTIN_KW "builtin" + POUND "#" + PATTERN_TYPE_KW "pattern_type" + WHITESPACE " " + L_PAREN "(" + PATH_TYPE + PATH + PATH_SEGMENT + NAME_REF + IDENT "u8" + WHITESPACE " " + IS_KW "is" + WHITESPACE " " + RANGE_PAT + LITERAL_PAT + LITERAL + INT_NUMBER "0" + DOT2 ".." + LITERAL_PAT + LITERAL + INT_NUMBER "10" + R_PAREN ")" + SEMICOLON ";" + WHITESPACE "\n" diff --git a/crates/parser/test_data/parser/inline/ok/pattern_type.rs b/crates/parser/test_data/parser/inline/ok/pattern_type.rs new file mode 100644 index 0000000000..c909c7b708 --- /dev/null +++ b/crates/parser/test_data/parser/inline/ok/pattern_type.rs @@ -0,0 +1 @@ +type T = builtin#pattern_type (u8 is 0..10); diff --git a/crates/proc-macro-api/Cargo.toml b/crates/proc-macro-api/Cargo.toml index a135a469e8..7342e0ecdc 100644 --- a/crates/proc-macro-api/Cargo.toml +++ b/crates/proc-macro-api/Cargo.toml @@ -34,9 +34,8 @@ semver.workspace = true rayon.workspace = true [features] -sysroot-abi = ["proc-macro-srv", "proc-macro-srv/sysroot-abi"] +in-rust-tree = ["proc-macro-srv", "proc-macro-srv/in-rust-tree"] default = [] -in-rust-tree = [] [lints] workspace = true diff --git a/crates/proc-macro-api/src/bidirectional_protocol.rs b/crates/proc-macro-api/src/bidirectional_protocol.rs index ba59cb219b..75c3bf8d35 100644 --- a/crates/proc-macro-api/src/bidirectional_protocol.rs +++ b/crates/proc-macro-api/src/bidirectional_protocol.rs @@ -12,8 +12,8 @@ use span::Span; use crate::{ ProcMacro, ProcMacroKind, ServerError, bidirectional_protocol::msg::{ - BidirectionalMessage, ExpandMacro, ExpandMacroData, ExpnGlobals, Request, Response, - SubRequest, SubResponse, + ApiVersionCheck, BidirectionalMessage, ExpandMacro, ExpandMacroData, ExpnGlobals, + ListMacros, Request, Response, SubRequest, SubResponse, }, legacy_protocol::{ SpanMode, @@ -98,7 +98,7 @@ pub(crate) fn version_check( srv: &ProcMacroServerProcess, callback: SubCallback<'_>, ) -> Result<u32, ServerError> { - let request = BidirectionalMessage::Request(Request::ApiVersionCheck {}); + let request = BidirectionalMessage::Request(Request::ApiVersionCheck(ApiVersionCheck {})); let response_payload = run_request(srv, request, callback)?; @@ -135,9 +135,9 @@ pub(crate) fn find_proc_macros( dylib_path: &AbsPath, callback: SubCallback<'_>, ) -> Result<Result<Vec<(String, ProcMacroKind)>, String>, ServerError> { - let request = BidirectionalMessage::Request(Request::ListMacros { + let request = BidirectionalMessage::Request(Request::ListMacros(ListMacros { dylib_path: dylib_path.to_path_buf().into(), - }); + })); let response_payload = run_request(srv, request, callback)?; @@ -186,25 +186,12 @@ pub(crate) fn expand( match response_payload { BidirectionalMessage::Response(Response::ExpandMacro(it)) => Ok(it - .map(|tree| { - let mut expanded = FlatTree::to_subtree_resolved(tree, version, &span_data_table); - if proc_macro.needs_fixup_change() { - proc_macro.change_fixup_to_match_old_server(&mut expanded); - } - expanded - }) - .map_err(|msg| msg.0)), - BidirectionalMessage::Response(Response::ExpandMacroExtended(it)) => Ok(it .map(|resp| { - let mut expanded = FlatTree::to_subtree_resolved( + FlatTree::to_subtree_resolved( resp.tree, version, &deserialize_span_data_index_map(&resp.span_data_table), - ); - if proc_macro.needs_fixup_change() { - proc_macro.change_fixup_to_match_old_server(&mut expanded); - } - expanded + ) }) .map_err(|msg| msg.0)), _ => Err(ServerError { message: "unexpected response".to_owned(), io: None }), diff --git a/crates/proc-macro-api/src/bidirectional_protocol/msg.rs b/crates/proc-macro-api/src/bidirectional_protocol/msg.rs index ab4bed81e6..e516297f06 100644 --- a/crates/proc-macro-api/src/bidirectional_protocol/msg.rs +++ b/crates/proc-macro-api/src/bidirectional_protocol/msg.rs @@ -1,6 +1,8 @@ //! Bidirectional protocol messages +#![expect(clippy::disallowed_types)] use std::{ + collections::{HashMap, HashSet}, io::{self, BufRead, Write}, ops::Range, }; @@ -16,13 +18,54 @@ use crate::{ #[derive(Debug, Serialize, Deserialize)] pub enum SubRequest { - FilePath { file_id: u32 }, - SourceText { file_id: u32, ast_id: u32, start: u32, end: u32 }, - LocalFilePath { file_id: u32 }, - LineColumn { file_id: u32, ast_id: u32, offset: u32 }, - ByteRange { file_id: u32, ast_id: u32, start: u32, end: u32 }, - SpanSource { file_id: u32, ast_id: u32, start: u32, end: u32, ctx: u32 }, - SpanParent { file_id: u32, ast_id: u32, start: u32, end: u32, ctx: u32 }, + FilePath { + file_id: u32, + }, + SourceText { + file_id: u32, + ast_id: u32, + start: u32, + end: u32, + }, + LocalFilePath { + file_id: u32, + }, + LineColumn { + file_id: u32, + ast_id: u32, + offset: u32, + }, + ByteRange { + file_id: u32, + ast_id: u32, + start: u32, + end: u32, + }, + SpanSource { + file_id: u32, + ast_id: u32, + start: u32, + end: u32, + ctx: u32, + }, + SpanParent { + file_id: u32, + ast_id: u32, + start: u32, + end: u32, + ctx: u32, + }, + SpanJoin { + file_id: u32, + ast_id_first: u32, + start_first: u32, + end_first: u32, + ctx_first: u32, + ast_id_second: u32, + start_second: u32, + end_second: u32, + ctx_second: u32, + }, } #[derive(Debug, Serialize, Deserialize)] @@ -54,6 +97,9 @@ pub enum SubResponse { SpanParentResult { parent_span: Option<ParentSpan>, }, + SpanJoinResult { + span: Option<SpanJoin>, + }, Cancel { reason: String, }, @@ -69,6 +115,15 @@ pub struct ParentSpan { } #[derive(Debug, Serialize, Deserialize)] +pub struct SpanJoin { + pub ast_id: u32, + pub start: u32, + pub end: u32, + pub ctx: u32, +} + +#[expect(clippy::large_enum_variant)] +#[derive(Debug, Serialize, Deserialize)] pub enum BidirectionalMessage { Request(Request), Response(Response), @@ -78,22 +133,30 @@ pub enum BidirectionalMessage { #[derive(Debug, Serialize, Deserialize)] pub enum Request { - ListMacros { dylib_path: Utf8PathBuf }, + ListMacros(ListMacros), ExpandMacro(Box<ExpandMacro>), - ApiVersionCheck {}, + ApiVersionCheck(ApiVersionCheck), SetConfig(ServerConfig), } +#[expect(clippy::large_enum_variant)] #[derive(Debug, Serialize, Deserialize)] pub enum Response { ListMacros(Result<Vec<(String, ProcMacroKind)>, String>), - ExpandMacro(Result<FlatTree, PanicMessage>), ApiVersionCheck(u32), SetConfig(ServerConfig), - ExpandMacroExtended(Result<ExpandMacroExtended, PanicMessage>), + ExpandMacro(Result<ExpandMacroResponse, PanicMessage>), } #[derive(Debug, Serialize, Deserialize)] +pub struct ListMacros { + pub dylib_path: Utf8PathBuf, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct ApiVersionCheck {} + +#[derive(Debug, Serialize, Deserialize)] pub struct ExpandMacro { pub lib: Utf8PathBuf, pub env: Vec<(String, String)>, @@ -102,9 +165,11 @@ pub struct ExpandMacro { } #[derive(Debug, Serialize, Deserialize)] -pub struct ExpandMacroExtended { +pub struct ExpandMacroResponse { pub tree: FlatTree, pub span_data_table: Vec<u32>, + pub tracked_env_vars: HashMap<Box<str>, Option<Box<str>>>, + pub tracked_paths: HashSet<Box<str>>, } #[derive(Debug, Serialize, Deserialize)] diff --git a/crates/proc-macro-api/src/legacy_protocol/msg.rs b/crates/proc-macro-api/src/legacy_protocol/msg.rs index bb0dde4728..9b71a8b70c 100644 --- a/crates/proc-macro-api/src/legacy_protocol/msg.rs +++ b/crates/proc-macro-api/src/legacy_protocol/msg.rs @@ -193,9 +193,7 @@ impl Message for Response { #[cfg(test)] mod tests { use intern::Symbol; - use span::{ - Edition, ROOT_ERASED_FILE_AST_ID, Span, SpanAnchor, SyntaxContext, TextRange, TextSize, - }; + use span::{ROOT_ERASED_FILE_AST_ID, Span, SpanAnchor, SyntaxContext, TextRange, TextSize}; use tt::{ Delimiter, DelimiterKind, Ident, Leaf, Literal, Punct, Spacing, TopSubtree, TopSubtreeBuilder, @@ -205,6 +203,11 @@ mod tests { use super::*; + fn make_ctx() -> SyntaxContext { + // SAFETY: Tests do not use a Database, so this won't ever be used within salsa. + unsafe { SyntaxContext::from_u32(0) } + } + fn fixture_token_tree_top_many_none() -> TopSubtree { let anchor = SpanAnchor { file_id: span::EditionedFileId::new( @@ -215,16 +218,8 @@ mod tests { }; let mut builder = TopSubtreeBuilder::new(Delimiter { - open: Span { - range: TextRange::empty(TextSize::new(0)), - anchor, - ctx: SyntaxContext::root(Edition::CURRENT), - }, - close: Span { - range: TextRange::empty(TextSize::new(0)), - anchor, - ctx: SyntaxContext::root(Edition::CURRENT), - }, + open: Span { range: TextRange::empty(TextSize::new(0)), anchor, ctx: make_ctx() }, + close: Span { range: TextRange::empty(TextSize::new(0)), anchor, ctx: make_ctx() }, kind: DelimiterKind::Invisible, }); @@ -234,7 +229,7 @@ mod tests { span: Span { range: TextRange::at(TextSize::new(0), TextSize::of("struct")), anchor, - ctx: SyntaxContext::root(Edition::CURRENT), + ctx: make_ctx(), }, is_raw: tt::IdentIsRaw::No, } @@ -246,7 +241,7 @@ mod tests { span: Span { range: TextRange::at(TextSize::new(5), TextSize::of("r#Foo")), anchor, - ctx: SyntaxContext::root(Edition::CURRENT), + ctx: make_ctx(), }, is_raw: tt::IdentIsRaw::Yes, } @@ -257,7 +252,7 @@ mod tests { Span { range: TextRange::at(TextSize::new(10), TextSize::of("\"Foo\"")), anchor, - ctx: SyntaxContext::root(Edition::CURRENT), + ctx: make_ctx(), }, tt::LitKind::Str, ))); @@ -266,7 +261,7 @@ mod tests { span: Span { range: TextRange::at(TextSize::new(13), TextSize::of('@')), anchor, - ctx: SyntaxContext::root(Edition::CURRENT), + ctx: make_ctx(), }, spacing: Spacing::Joint, })); @@ -275,7 +270,7 @@ mod tests { Span { range: TextRange::at(TextSize::new(14), TextSize::of('{')), anchor, - ctx: SyntaxContext::root(Edition::CURRENT), + ctx: make_ctx(), }, ); builder.open( @@ -283,7 +278,7 @@ mod tests { Span { range: TextRange::at(TextSize::new(15), TextSize::of('[')), anchor, - ctx: SyntaxContext::root(Edition::CURRENT), + ctx: make_ctx(), }, ); builder.push(Leaf::Literal(Literal::new( @@ -291,7 +286,7 @@ mod tests { Span { range: TextRange::at(TextSize::new(16), TextSize::of("0u32")), anchor, - ctx: SyntaxContext::root(Edition::CURRENT), + ctx: make_ctx(), }, tt::LitKind::Integer, "u32", @@ -299,13 +294,13 @@ mod tests { builder.close(Span { range: TextRange::at(TextSize::new(20), TextSize::of(']')), anchor, - ctx: SyntaxContext::root(Edition::CURRENT), + ctx: make_ctx(), }); builder.close(Span { range: TextRange::at(TextSize::new(21), TextSize::of('}')), anchor, - ctx: SyntaxContext::root(Edition::CURRENT), + ctx: make_ctx(), }); builder.build() @@ -321,16 +316,8 @@ mod tests { }; let builder = TopSubtreeBuilder::new(Delimiter { - open: Span { - range: TextRange::empty(TextSize::new(0)), - anchor, - ctx: SyntaxContext::root(Edition::CURRENT), - }, - close: Span { - range: TextRange::empty(TextSize::new(0)), - anchor, - ctx: SyntaxContext::root(Edition::CURRENT), - }, + open: Span { range: TextRange::empty(TextSize::new(0)), anchor, ctx: make_ctx() }, + close: Span { range: TextRange::empty(TextSize::new(0)), anchor, ctx: make_ctx() }, kind: DelimiterKind::Invisible, }); @@ -347,16 +334,8 @@ mod tests { }; let builder = TopSubtreeBuilder::new(Delimiter { - open: Span { - range: TextRange::empty(TextSize::new(0)), - anchor, - ctx: SyntaxContext::root(Edition::CURRENT), - }, - close: Span { - range: TextRange::empty(TextSize::new(0)), - anchor, - ctx: SyntaxContext::root(Edition::CURRENT), - }, + open: Span { range: TextRange::empty(TextSize::new(0)), anchor, ctx: make_ctx() }, + close: Span { range: TextRange::empty(TextSize::new(0)), anchor, ctx: make_ctx() }, kind: DelimiterKind::Brace, }); @@ -405,7 +384,7 @@ mod tests { } #[test] - #[cfg(feature = "sysroot-abi")] + #[cfg(feature = "in-rust-tree")] fn test_proc_macro_rpc_works_ts() { for tt in [ fixture_token_tree_top_many_none, diff --git a/crates/proc-macro-api/src/legacy_protocol/msg/flat.rs b/crates/proc-macro-api/src/legacy_protocol/msg/flat.rs index 248de70f0e..3015bd0c0e 100644 --- a/crates/proc-macro-api/src/legacy_protocol/msg/flat.rs +++ b/crates/proc-macro-api/src/legacy_protocol/msg/flat.rs @@ -34,7 +34,7 @@ //! as we don't have bincode in Cargo.toml yet, lets stick with serde_json for //! the time being. -#[cfg(feature = "sysroot-abi")] +#[cfg(feature = "in-rust-tree")] use proc_macro_srv::TokenStream; use std::collections::VecDeque; @@ -195,7 +195,7 @@ impl FlatTree { } } -#[cfg(feature = "sysroot-abi")] +#[cfg(feature = "in-rust-tree")] impl FlatTree { pub fn from_tokenstream( tokenstream: proc_macro_srv::TokenStream<Span>, @@ -591,7 +591,7 @@ impl<'a, T: SpanTransformer, U> Writer<'a, '_, T, U> { T::token_id_of(self.span_data_table, span) } - #[cfg(feature = "sysroot-abi")] + #[cfg(feature = "in-rust-tree")] pub(crate) fn intern(&mut self, text: &'a str) -> u32 { let table = &mut self.text; *self.string_table.entry(text.into()).or_insert_with(|| { @@ -611,7 +611,7 @@ impl<'a, T: SpanTransformer, U> Writer<'a, '_, T, U> { } } -#[cfg(feature = "sysroot-abi")] +#[cfg(feature = "in-rust-tree")] impl<'a, T: SpanTransformer> Writer<'a, '_, T, Option<proc_macro_srv::TokenStreamIter<'a, T::Span>>> { @@ -852,7 +852,7 @@ impl<T: SpanTransformer<Span = span::Span>> Reader<'_, T> { } } -#[cfg(feature = "sysroot-abi")] +#[cfg(feature = "in-rust-tree")] impl<T: SpanTransformer> Reader<'_, T> { pub(crate) fn read_tokenstream( self, diff --git a/crates/proc-macro-api/src/lib.rs b/crates/proc-macro-api/src/lib.rs index e83ddb8594..149f6e44f9 100644 --- a/crates/proc-macro-api/src/lib.rs +++ b/crates/proc-macro-api/src/lib.rs @@ -5,13 +5,12 @@ //! is used to provide basic infrastructure for communication between two //! processes: Client (RA itself), Server (the external program) -#![cfg_attr(not(feature = "sysroot-abi"), allow(unused_crate_dependencies))] +#![cfg_attr(not(feature = "in-rust-tree"), allow(unused_crate_dependencies))] #![cfg_attr( - feature = "sysroot-abi", - feature(proc_macro_internals, proc_macro_diagnostic, proc_macro_span) + feature = "in-rust-tree", + feature(proc_macro_internals, proc_macro_diagnostic, proc_macro_span, rustc_private) )] #![allow(internal_features, unused_features)] -#![cfg_attr(feature = "in-rust-tree", feature(rustc_private))] #[cfg(feature = "in-rust-tree")] extern crate rustc_driver as _; diff --git a/crates/proc-macro-api/src/process.rs b/crates/proc-macro-api/src/process.rs index 80e4ed05c3..035c12669c 100644 --- a/crates/proc-macro-api/src/process.rs +++ b/crates/proc-macro-api/src/process.rs @@ -240,8 +240,9 @@ impl ProcMacroServerProcess { /// Enable support for rust-analyzer span mode if the server supports it. pub(crate) fn rust_analyzer_spans(&self) -> bool { match self.protocol { - Protocol::LegacyJson { mode } => mode == SpanMode::RustAnalyzer, - Protocol::BidirectionalPostcardPrototype { mode } => mode == SpanMode::RustAnalyzer, + Protocol::LegacyJson { mode } | Protocol::BidirectionalPostcardPrototype { mode } => { + mode == SpanMode::RustAnalyzer + } } } diff --git a/crates/proc-macro-srv-cli/Cargo.toml b/crates/proc-macro-srv-cli/Cargo.toml index f586fe7644..44e19f2d3c 100644 --- a/crates/proc-macro-srv-cli/Cargo.toml +++ b/crates/proc-macro-srv-cli/Cargo.toml @@ -31,9 +31,8 @@ proc-macro-test.path = "../proc-macro-srv/proc-macro-test" [features] default = [] -# default = ["sysroot-abi"] -sysroot-abi = ["proc-macro-srv/sysroot-abi", "proc-macro-api/sysroot-abi"] -in-rust-tree = ["proc-macro-srv/in-rust-tree", "sysroot-abi"] +# default = ["in-rust-tree"] +in-rust-tree = ["proc-macro-srv/in-rust-tree", "proc-macro-api/in-rust-tree"] [[bin]] diff --git a/crates/proc-macro-srv-cli/README.md b/crates/proc-macro-srv-cli/README.md index 02a67ac3ec..93a3c7ffc2 100644 --- a/crates/proc-macro-srv-cli/README.md +++ b/crates/proc-macro-srv-cli/README.md @@ -9,12 +9,12 @@ rust-analyzer uses a RPC (via stdio) client-server architecture for procedural m 1. Proc macros are dynamic libraries that can segfault, bringing down the entire process, so running them out of process allows rust-analyzer to recover from fatal errors.
2. Proc macro dylibs are compiled against a specific rustc version and require matching internal APIs to load and execute, as such having this binary shipped as a rustup component allows us to always match the rustc version irrespective of the rust-analyzer version used.
-## The `sysroot-abi` Feature
+## The `in-rust-tree` Feature
-**The `sysroot-abi` feature is required for the binary to actually function.** Without it, the binary will return an error:
+**The `in-rust-tree` feature is required for the binary to actually function.** Without it, the binary will return an error:
```
-proc-macro-srv-cli needs to be compiled with the `sysroot-abi` feature to function
+proc-macro-srv-cli needs to be compiled with the `in-rust-tree` feature to function
```
This feature is necessary because the proc-macro server needs access to unstable rustc internals (`proc_macro_internals`, `proc_macro_diagnostic`, `proc_macro_span`) which are only available on nightly or with `RUSTC_BOOTSTRAP=1`.
@@ -24,10 +24,10 @@ rust-analyzer is a stable toolchain project though, so the feature flag is used ```bash
# Using nightly toolchain
-cargo build -p proc-macro-srv-cli --features sysroot-abi
+cargo build -p proc-macro-srv-cli --features in-rust-tree
# Or with RUSTC_BOOTSTRAP on stable
-RUSTC_BOOTSTRAP=1 cargo build -p proc-macro-srv-cli --features sysroot-abi
+RUSTC_BOOTSTRAP=1 cargo build -p proc-macro-srv-cli --features in-rust-tree
```
### Installing the proc-macro server
@@ -42,7 +42,7 @@ cargo xtask install --proc-macro-server ## Testing
```bash
-cargo test --features sysroot-abi -p proc-macro-srv -p proc-macro-srv-cli -p proc-macro-api
+cargo test --features in-rust-tree -p proc-macro-srv -p proc-macro-srv-cli -p proc-macro-api
```
The tests use a test proc macro dylib built by the `proc-macro-test` crate, which compiles a small proc macro implementation during build time.
diff --git a/crates/proc-macro-srv-cli/src/lib.rs b/crates/proc-macro-srv-cli/src/lib.rs index 8475c05ae8..c330928fbc 100644 --- a/crates/proc-macro-srv-cli/src/lib.rs +++ b/crates/proc-macro-srv-cli/src/lib.rs @@ -1,11 +1,10 @@ -//! Library interface for `proc-macro-srv-cli`.
-//!
-//! This module exposes the server main loop and protocol format for integration testing.
-
-#![cfg_attr(feature = "in-rust-tree", feature(rustc_private))]
-
-#[cfg(feature = "in-rust-tree")]
-extern crate rustc_driver as _;
-
-#[cfg(feature = "sysroot-abi")]
-pub mod main_loop;
+//! Library interface for `proc-macro-srv-cli`. +//! +//! This module exposes the server main loop and protocol format for integration testing. + +#![cfg(feature = "in-rust-tree")] +#![feature(rustc_private)] + +extern crate rustc_driver as _; + +pub mod main_loop; diff --git a/crates/proc-macro-srv-cli/src/main.rs b/crates/proc-macro-srv-cli/src/main.rs index 928753659f..926633df62 100644 --- a/crates/proc-macro-srv-cli/src/main.rs +++ b/crates/proc-macro-srv-cli/src/main.rs @@ -1,7 +1,7 @@ //! A standalone binary for `proc-macro-srv`. //! Driver for proc macro server #![cfg_attr(feature = "in-rust-tree", feature(rustc_private))] -#![cfg_attr(not(feature = "sysroot-abi"), allow(unused_crate_dependencies))] +#![cfg_attr(not(feature = "in-rust-tree"), allow(unused_crate_dependencies))] #![allow(clippy::print_stdout, clippy::print_stderr)] #[cfg(feature = "in-rust-tree")] @@ -12,7 +12,7 @@ mod version; use clap::{Command, ValueEnum}; use proc_macro_api::ProtocolFormat; -#[cfg(feature = "sysroot-abi")] +#[cfg(feature = "in-rust-tree")] use proc_macro_srv_cli::main_loop::run; fn main() -> std::io::Result<()> { @@ -91,7 +91,7 @@ impl ValueEnum for ProtocolFormatArg { } } -#[cfg(not(feature = "sysroot-abi"))] +#[cfg(not(feature = "in-rust-tree"))] fn run( _: &mut std::io::BufReader<std::io::Stdin>, _: &mut std::io::Stdout, @@ -99,7 +99,7 @@ fn run( ) -> std::io::Result<()> { Err(std::io::Error::new( std::io::ErrorKind::Unsupported, - "proc-macro-srv-cli needs to be compiled with the `sysroot-abi` feature to function" + "proc-macro-srv-cli needs to be compiled with the `in-rust-tree` feature to function" .to_owned(), )) } diff --git a/crates/proc-macro-srv-cli/src/main_loop.rs b/crates/proc-macro-srv-cli/src/main_loop.rs index c525ed848b..6697b6380d 100644 --- a/crates/proc-macro-srv-cli/src/main_loop.rs +++ b/crates/proc-macro-srv-cli/src/main_loop.rs @@ -1,4 +1,5 @@ //! The main loop of the proc-macro server. +use proc_macro_api::bidirectional_protocol::msg::{ApiVersionCheck, ListMacros}; use proc_macro_api::{ ProtocolFormat, bidirectional_protocol::msg as bidirectional, legacy_protocol::msg as legacy, version::CURRENT_API_VERSION, @@ -72,7 +73,7 @@ fn run_new( match req { bidirectional::BidirectionalMessage::Request(request) => match request { - bidirectional::Request::ListMacros { dylib_path } => { + bidirectional::Request::ListMacros(ListMacros { dylib_path }) => { let res = srv.list_macros(&dylib_path).map(|macros| { macros .into_iter() @@ -83,7 +84,7 @@ fn run_new( send_response(stdout, bidirectional::Response::ListMacros(res))?; } - bidirectional::Request::ApiVersionCheck {} => { + bidirectional::Request::ApiVersionCheck(ApiVersionCheck {}) => { send_response( stdout, bidirectional::Response::ApiVersionCheck(CURRENT_API_VERSION), @@ -142,6 +143,7 @@ fn handle_expand_id( let attributes = attributes .map(|it| it.to_tokenstream_unresolved::<SpanTrans>(CURRENT_API_VERSION, |_, b| b)); + let mut tracked_env = Default::default(); let res = srv .expand( lib, @@ -153,11 +155,18 @@ fn handle_expand_id( def_site, call_site, mixed_site, + &mut tracked_env, None, ) .map(|it| { legacy::FlatTree::from_tokenstream_raw::<SpanTrans>(it, call_site, CURRENT_API_VERSION) }) + .map(|tree| bidirectional::ExpandMacroResponse { + tree, + span_data_table: vec![], + tracked_env_vars: tracked_env.env_vars, + tracked_paths: tracked_env.paths, + }) .map_err(|e| legacy::PanicMessage(e.into_string().unwrap_or_default())); send_response(stdout, bidirectional::Response::ExpandMacro(res)) @@ -343,6 +352,46 @@ impl proc_macro_srv::ProcMacroClientInterface for ProcMacroClientHandle<'_> { other => handle_failure(other), } } + + fn span_join( + &mut self, + first: proc_macro_srv::span::Span, + second: proc_macro_srv::span::Span, + ) -> Option<proc_macro_srv::span::Span> { + assert_eq!(first.anchor.file_id, second.anchor.file_id); + let response = self.roundtrip(bidirectional::SubRequest::SpanJoin { + file_id: first.anchor.file_id.as_u32(), + ast_id_first: first.anchor.ast_id.into_raw(), + start_first: first.range.start().into(), + end_first: first.range.end().into(), + ctx_first: first.ctx.into_u32(), + ast_id_second: second.anchor.ast_id.into_raw(), + start_second: second.range.start().into(), + end_second: second.range.end().into(), + ctx_second: second.ctx.into_u32(), + }); + + match response { + Ok(bidirectional::SubResponse::SpanJoinResult { span }) => { + span.map(|bidirectional::SpanJoin { ast_id, start, end, ctx }| { + proc_macro_srv::span::Span { + range: proc_macro_srv::span::TextRange::new( + proc_macro_srv::span::TextSize::new(start), + proc_macro_srv::span::TextSize::new(end), + ), + anchor: proc_macro_srv::span::SpanAnchor { + file_id: first.anchor.file_id, + ast_id: proc_macro_srv::span::ErasedFileAstId::from_raw(ast_id), + }, + // SAFETY: spans originate from the server. If the protocol is violated, + // undefined behavior is the caller’s responsibility. + ctx: unsafe { proc_macro_srv::span::SyntaxContext::from_u32(ctx) }, + } + }) + } + other => handle_failure(other), + } + } } fn handle_expand_ra( @@ -383,6 +432,8 @@ fn handle_expand_ra( }) }); + let mut tracked_env = Default::default(); + let res = srv .expand( lib, @@ -394,6 +445,7 @@ fn handle_expand_ra( def_site, call_site, mixed_site, + &mut tracked_env, Some(&mut ProcMacroClientHandle { stdin, stdout, buf }), ) .map(|it| { @@ -407,10 +459,15 @@ fn handle_expand_ra( legacy::serialize_span_data_index_map(&span_data_table), ) }) - .map(|(tree, span_data_table)| bidirectional::ExpandMacroExtended { tree, span_data_table }) + .map(|(tree, span_data_table)| bidirectional::ExpandMacroResponse { + tree, + span_data_table, + tracked_env_vars: tracked_env.env_vars, + tracked_paths: tracked_env.paths, + }) .map_err(|e| legacy::PanicMessage(e.into_string().unwrap_or_default())); - send_response(stdout, bidirectional::Response::ExpandMacroExtended(res)) + send_response(stdout, bidirectional::Response::ExpandMacro(res)) } fn run_old( @@ -480,6 +537,7 @@ fn run_old( def_site, call_site, mixed_site, + &mut Default::default(), None, ) .map(|it| { @@ -522,6 +580,7 @@ fn run_old( def_site, call_site, mixed_site, + &mut Default::default(), None, ) .map(|it| { diff --git a/crates/proc-macro-srv-cli/tests/bidirectional_postcard.rs b/crates/proc-macro-srv-cli/tests/bidirectional_postcard.rs index ba9657a9bb..9c55eed08e 100644 --- a/crates/proc-macro-srv-cli/tests/bidirectional_postcard.rs +++ b/crates/proc-macro-srv-cli/tests/bidirectional_postcard.rs @@ -1,7 +1,6 @@ -#![cfg(feature = "sysroot-abi")] -#![cfg_attr(feature = "in-rust-tree", feature(rustc_private))] +#![cfg(feature = "in-rust-tree")] +#![feature(rustc_private)] -#[cfg(feature = "in-rust-tree")] extern crate rustc_driver as _; mod common { @@ -15,7 +14,10 @@ use expect_test::expect; use proc_macro_api::{ ProtocolFormat::BidirectionalPostcardPrototype, bidirectional_protocol::{ - msg::{ExpandMacro, ExpandMacroData, ExpnGlobals, Request, Response}, + msg::{ + ApiVersionCheck, ExpandMacro, ExpandMacroData, ExpnGlobals, ListMacros, Request, + Response, + }, reject_subrequests, }, legacy_protocol::msg::{PanicMessage, ServerConfig, SpanDataIndexMap, SpanMode}, @@ -25,8 +27,12 @@ use proc_macro_api::{ #[test] fn test_bidi_version_check_bidirectional() { with_server(BidirectionalPostcardPrototype, |writer, reader| { - let response = - request_bidirectional(writer, reader, Request::ApiVersionCheck {}, reject_subrequests); + let response = request_bidirectional( + writer, + reader, + Request::ApiVersionCheck(ApiVersionCheck {}), + reject_subrequests, + ); match response { Response::ApiVersionCheck(version) => { @@ -44,7 +50,7 @@ fn test_bidi_list_macros() { let response = request_bidirectional( writer, reader, - Request::ListMacros { dylib_path }, + Request::ListMacros(ListMacros { dylib_path }), &reject_subrequests, ); @@ -84,7 +90,7 @@ fn test_bidi_list_macros_invalid_path() { let response = request_bidirectional( writer, reader, - Request::ListMacros { dylib_path: "/nonexistent/path/to/dylib.so".into() }, + Request::ListMacros(ListMacros { dylib_path: "/nonexistent/path/to/dylib.so".into() }), reject_subrequests, ); @@ -168,8 +174,12 @@ fn test_bidi_basic_call_flow() { with_server(BidirectionalPostcardPrototype, |writer, reader| { let dylib_path = proc_macro_test_dylib_path(); - let response1 = - request_bidirectional(writer, reader, Request::ApiVersionCheck {}, reject_subrequests); + let response1 = request_bidirectional( + writer, + reader, + Request::ApiVersionCheck(ApiVersionCheck {}), + reject_subrequests, + ); assert!(matches!(response1, Response::ApiVersionCheck(_))); let response2 = request_bidirectional( @@ -183,7 +193,7 @@ fn test_bidi_basic_call_flow() { let response3 = request_bidirectional( writer, reader, - Request::ListMacros { dylib_path: dylib_path.clone() }, + Request::ListMacros(ListMacros { dylib_path: dylib_path.clone() }), reject_subrequests, ); assert!(matches!(response3, Response::ListMacros(Ok(_)))); @@ -195,8 +205,12 @@ fn test_bidi_expand_nonexistent_macro() { with_server(BidirectionalPostcardPrototype, |writer, reader| { let dylib_path = proc_macro_test_dylib_path(); - let version_response = - request_bidirectional(writer, reader, Request::ApiVersionCheck {}, reject_subrequests); + let version_response = request_bidirectional( + writer, + reader, + Request::ApiVersionCheck(ApiVersionCheck {}), + reject_subrequests, + ); let Response::ApiVersionCheck(version) = version_response else { panic!("expected version check response"); }; diff --git a/crates/proc-macro-srv-cli/tests/common/utils.rs b/crates/proc-macro-srv-cli/tests/common/utils.rs index 3049e98004..b78e107452 100644 --- a/crates/proc-macro-srv-cli/tests/common/utils.rs +++ b/crates/proc-macro-srv-cli/tests/common/utils.rs @@ -135,6 +135,11 @@ pub(crate) fn proc_macro_test_dylib_path() -> Utf8PathBuf { path.into() } +fn make_ctx() -> SyntaxContext { + // SAFETY: Tests do not use a Database, so this won't ever be used within salsa. + unsafe { SyntaxContext::from_u32(0) } +} + /// Creates a simple empty token tree suitable for testing. pub(crate) fn create_empty_token_tree( version: u32, @@ -144,11 +149,7 @@ pub(crate) fn create_empty_token_tree( file_id: EditionedFileId::new(FileId::from_raw(0), Edition::CURRENT), ast_id: span::ROOT_ERASED_FILE_AST_ID, }; - let span = Span { - range: TextRange::empty(0.into()), - anchor, - ctx: SyntaxContext::root(Edition::CURRENT), - }; + let span = Span { range: TextRange::empty(0.into()), anchor, ctx: make_ctx() }; let builder = TopSubtreeBuilder::new(Delimiter { open: span, diff --git a/crates/proc-macro-srv-cli/tests/legacy_json.rs b/crates/proc-macro-srv-cli/tests/legacy_json.rs index 562cf0c251..f5cbaa7421 100644 --- a/crates/proc-macro-srv-cli/tests/legacy_json.rs +++ b/crates/proc-macro-srv-cli/tests/legacy_json.rs @@ -3,10 +3,9 @@ //! These tests exercise the full client-server RPC procedure using in-memory //! channels without needing to spawn the actual server and client processes. -#![cfg(feature = "sysroot-abi")] -#![cfg_attr(feature = "in-rust-tree", feature(rustc_private))] +#![cfg(feature = "in-rust-tree")] +#![feature(rustc_private)] -#[cfg(feature = "in-rust-tree")] extern crate rustc_driver as _; mod common { diff --git a/crates/proc-macro-srv/Cargo.toml b/crates/proc-macro-srv/Cargo.toml index 8e5617f8a2..a161d79693 100644 --- a/crates/proc-macro-srv/Cargo.toml +++ b/crates/proc-macro-srv/Cargo.toml @@ -23,8 +23,6 @@ paths.workspace = true span = { path = "../span", version = "0.0.0", default-features = false} intern.workspace = true -ra-ap-rustc_lexer.workspace = true - [target.'cfg(unix)'.dependencies] libc.workspace = true @@ -38,8 +36,11 @@ proc-macro-test.path = "./proc-macro-test" [features] default = [] -sysroot-abi = [] -in-rust-tree = ["sysroot-abi"] +# default = ["in-rust-tree"] +in-rust-tree = [] [lints] workspace = true + +[package.metadata.rust-analyzer] +rustc_private=true diff --git a/crates/proc-macro-srv/src/dylib.rs b/crates/proc-macro-srv/src/dylib.rs index 9a65538675..96daa2c462 100644 --- a/crates/proc-macro-srv/src/dylib.rs +++ b/crates/proc-macro-srv/src/dylib.rs @@ -12,7 +12,7 @@ use object::Object; use paths::{Utf8Path, Utf8PathBuf}; use crate::{ - PanicMessage, ProcMacroClientHandle, ProcMacroKind, ProcMacroSrvSpan, + PanicMessage, ProcMacroClientHandle, ProcMacroKind, ProcMacroSrvSpan, TrackedEnv, dylib::proc_macros::ProcMacros, token_stream::TokenStream, }; @@ -45,14 +45,22 @@ impl Expander { def_site: S, call_site: S, mixed_site: S, - callback: Option<ProcMacroClientHandle<'_>>, + tracked_env: &'a mut TrackedEnv, + callback: Option<ProcMacroClientHandle<'a>>, ) -> Result<TokenStream<S>, PanicMessage> where <S::Server<'a> as bridge::server::Server>::TokenStream: Default, { - self.inner - .proc_macros - .expand(macro_name, macro_body, attribute, def_site, call_site, mixed_site, callback) + self.inner.proc_macros.expand( + macro_name, + macro_body, + attribute, + def_site, + call_site, + mixed_site, + tracked_env, + callback, + ) } pub(crate) fn list_macros(&self) -> impl Iterator<Item = (&str, ProcMacroKind)> { diff --git a/crates/proc-macro-srv/src/dylib/proc_macros.rs b/crates/proc-macro-srv/src/dylib/proc_macros.rs index cf00be0327..4ed32f8e6c 100644 --- a/crates/proc-macro-srv/src/dylib/proc_macros.rs +++ b/crates/proc-macro-srv/src/dylib/proc_macros.rs @@ -1,5 +1,7 @@ //! Proc macro ABI -use crate::{ProcMacroClientHandle, ProcMacroKind, ProcMacroSrvSpan, token_stream::TokenStream}; +use crate::{ + ProcMacroClientHandle, ProcMacroKind, ProcMacroSrvSpan, TrackedEnv, token_stream::TokenStream, +}; use rustc_proc_macro::bridge; #[repr(transparent)] @@ -12,7 +14,7 @@ impl From<bridge::PanicMessage> for crate::PanicMessage { } impl ProcMacros { - pub(crate) fn expand<S: ProcMacroSrvSpan>( + pub(crate) fn expand<'a, S: ProcMacroSrvSpan>( &self, macro_name: &str, macro_body: TokenStream<S>, @@ -20,7 +22,8 @@ impl ProcMacros { def_site: S, call_site: S, mixed_site: S, - callback: Option<ProcMacroClientHandle<'_>>, + tracked_env: &'a mut TrackedEnv, + callback: Option<ProcMacroClientHandle<'a>>, ) -> Result<TokenStream<S>, crate::PanicMessage> { let parsed_attributes = attribute.unwrap_or_default(); @@ -31,7 +34,7 @@ impl ProcMacros { { let res = client.run( &bridge::server::SAME_THREAD, - S::make_server(call_site, def_site, mixed_site, callback), + S::make_server(call_site, def_site, mixed_site, tracked_env, callback), macro_body, cfg!(debug_assertions), ); @@ -40,7 +43,7 @@ impl ProcMacros { bridge::client::ProcMacro::Bang { name, client } if *name == macro_name => { let res = client.run( &bridge::server::SAME_THREAD, - S::make_server(call_site, def_site, mixed_site, callback), + S::make_server(call_site, def_site, mixed_site, tracked_env, callback), macro_body, cfg!(debug_assertions), ); @@ -49,7 +52,7 @@ impl ProcMacros { bridge::client::ProcMacro::Attr { name, client } if *name == macro_name => { let res = client.run( &bridge::server::SAME_THREAD, - S::make_server(call_site, def_site, mixed_site, callback), + S::make_server(call_site, def_site, mixed_site, tracked_env, callback), parsed_attributes, macro_body, cfg!(debug_assertions), diff --git a/crates/proc-macro-srv/src/lib.rs b/crates/proc-macro-srv/src/lib.rs index 0bdc379cb6..7562a2e664 100644 --- a/crates/proc-macro-srv/src/lib.rs +++ b/crates/proc-macro-srv/src/lib.rs @@ -7,33 +7,16 @@ //! //! * We use `tt` for proc-macro `TokenStream` server, it is easier to manipulate and interact with //! RA than `proc-macro2` token stream. -//! * By **copying** the whole rustc `lib_proc_macro` code, we are able to build this with `stable` -//! rustc rather than `unstable`. (Although in general ABI compatibility is still an issue)… - -#![cfg(feature = "sysroot-abi")] -#![cfg_attr(feature = "in-rust-tree", feature(rustc_private))] -#![feature(proc_macro_internals, proc_macro_diagnostic, proc_macro_span)] -#![allow( - unreachable_pub, - internal_features, - clippy::disallowed_types, - clippy::print_stderr, - unused_crate_dependencies, - unused_features -)] + +#![cfg(feature = "in-rust-tree")] +#![feature(proc_macro_internals, proc_macro_diagnostic, proc_macro_span, rustc_private)] +#![expect(unreachable_pub, internal_features, clippy::disallowed_types, clippy::print_stderr)] +#![allow(unused_features, unused_crate_dependencies)] #![deny(deprecated_safe, clippy::undocumented_unsafe_blocks)] -#[cfg(not(feature = "in-rust-tree"))] -extern crate proc_macro as rustc_proc_macro; -#[cfg(feature = "in-rust-tree")] extern crate rustc_driver as _; -#[cfg(feature = "in-rust-tree")] -extern crate rustc_proc_macro; - -#[cfg(not(feature = "in-rust-tree"))] -extern crate ra_ap_rustc_lexer as rustc_lexer; -#[cfg(feature = "in-rust-tree")] extern crate rustc_lexer; +extern crate rustc_proc_macro; mod bridge; mod dylib; @@ -41,7 +24,7 @@ mod server_impl; mod token_stream; use std::{ - collections::{HashMap, hash_map::Entry}, + collections::{HashMap, HashSet, hash_map::Entry}, env, ffi::OsString, fs, @@ -52,7 +35,7 @@ use std::{ }; use paths::{Utf8Path, Utf8PathBuf}; -use span::Span; +use span::{FIXUP_ERASED_FILE_AST_ID_MARKER, Span}; use temp_dir::TempDir; pub use crate::server_impl::token_id::SpanId; @@ -123,6 +106,7 @@ pub trait ProcMacroClientInterface { fn byte_range(&mut self, span: Span) -> Range<usize>; fn span_source(&mut self, span: Span) -> Span; fn span_parent(&mut self, span: Span) -> Option<Span>; + fn span_join(&mut self, first: Span, second: Span) -> Option<Span>; } const EXPANDER_STACK_SIZE: usize = 8 * 1024 * 1024; @@ -144,7 +128,7 @@ impl ExpandError { } impl ProcMacroSrv<'_> { - pub fn expand<S: ProcMacroSrvSpan>( + pub fn expand<'a, S: ProcMacroSrvSpan + 'a>( &self, lib: impl AsRef<Utf8Path>, env: &[(String, String)], @@ -155,7 +139,8 @@ impl ProcMacroSrv<'_> { def_site: S, call_site: S, mixed_site: S, - callback: Option<ProcMacroClientHandle<'_>>, + tracked_env: &'a mut TrackedEnv, + callback: Option<ProcMacroClientHandle<'a>>, ) -> Result<token_stream::TokenStream<S>, ExpandError> { let snapped_env = self.env; let expander = self.expander(lib.as_ref()).map_err(|err| ExpandError::Internal { @@ -172,13 +157,18 @@ impl ProcMacroSrv<'_> { .name(macro_name.to_owned()) .spawn_scoped(s, move || { expander.expand( - macro_name, macro_body, attribute, def_site, call_site, mixed_site, + macro_name, + macro_body, + attribute, + def_site, + call_site, + mixed_site, + tracked_env, callback, ) }); match thread.unwrap().join() { Ok(res) => res.map_err(ExpandError::Panic), - Err(payload) => { if let Some(marker) = payload.downcast_ref::<ProcMacroPanicMarker>() { return match marker { @@ -235,6 +225,12 @@ impl ProcMacroSrv<'_> { } } +#[derive(Default)] +pub struct TrackedEnv { + pub env_vars: HashMap<Box<str>, Option<Box<str>>>, + pub paths: HashSet<Box<str>>, +} + pub trait ProcMacroSrvSpan: Copy + Send + Sync { type Server<'a>: rustc_proc_macro::bridge::server::Server< TokenStream = crate::token_stream::TokenStream<Self>, @@ -243,6 +239,7 @@ pub trait ProcMacroSrvSpan: Copy + Send + Sync { call_site: Self, def_site: Self, mixed_site: Self, + tracked_env: &'a mut TrackedEnv, callback: Option<ProcMacroClientHandle<'a>>, ) -> Self::Server<'a>; } @@ -254,16 +251,10 @@ impl ProcMacroSrvSpan for SpanId { call_site: Self, def_site: Self, mixed_site: Self, + _: &'a mut TrackedEnv, callback: Option<ProcMacroClientHandle<'a>>, ) -> Self::Server<'a> { - Self::Server { - call_site, - def_site, - mixed_site, - callback, - tracked_env_vars: Default::default(), - tracked_paths: Default::default(), - } + Self::Server { call_site, def_site, mixed_site, callback } } } @@ -273,6 +264,7 @@ impl ProcMacroSrvSpan for Span { call_site: Self, def_site: Self, mixed_site: Self, + tracked_env: &'a mut TrackedEnv, callback: Option<ProcMacroClientHandle<'a>>, ) -> Self::Server<'a> { Self::Server { @@ -280,8 +272,8 @@ impl ProcMacroSrvSpan for Span { def_site, mixed_site, callback, - tracked_env_vars: Default::default(), - tracked_paths: Default::default(), + tracked_env, + fixup_id: FIXUP_ERASED_FILE_AST_ID_MARKER, } } } diff --git a/crates/proc-macro-srv/src/server_impl/rust_analyzer_span.rs b/crates/proc-macro-srv/src/server_impl/rust_analyzer_span.rs index 6b6bfcc934..bdac9c920c 100644 --- a/crates/proc-macro-srv/src/server_impl/rust_analyzer_span.rs +++ b/crates/proc-macro-srv/src/server_impl/rust_analyzer_span.rs @@ -4,30 +4,25 @@ //! It is an unfortunate result of how the proc-macro API works that we need to look into the //! concrete representation of the spans, and as such, RustRover cannot make use of this unless they //! change their representation to be compatible with rust-analyzer's. -use std::{ - collections::{HashMap, HashSet}, - ops::{Bound, Range}, -}; +use std::ops::{Bound, Range}; use intern::Symbol; use rustc_proc_macro::bridge::server; -use span::{FIXUP_ERASED_FILE_AST_ID_MARKER, Span, TextRange, TextSize}; +use span::{ErasedFileAstId, Span, TextRange, TextSize}; use crate::{ - ProcMacroClientHandle, + ProcMacroClientHandle, TrackedEnv, bridge::{Diagnostic, ExpnGlobals, Literal, TokenTree}, server_impl::literal_from_str, }; pub struct RaSpanServer<'a> { - // FIXME: Report this back to the caller to track as dependencies - pub tracked_env_vars: HashMap<Box<str>, Option<Box<str>>>, - // FIXME: Report this back to the caller to track as dependencies - pub tracked_paths: HashSet<Box<str>>, + pub tracked_env: &'a mut TrackedEnv, pub call_site: Span, pub def_site: Span, pub mixed_site: Span, pub callback: Option<ProcMacroClientHandle<'a>>, + pub fixup_id: ErasedFileAstId, } impl server::Server for RaSpanServer<'_> { @@ -56,10 +51,10 @@ impl server::Server for RaSpanServer<'_> { } fn track_env_var(&mut self, var: &str, value: Option<&str>) { - self.tracked_env_vars.insert(var.into(), value.map(Into::into)); + self.tracked_env.env_vars.insert(var.into(), value.map(Into::into)); } fn track_path(&mut self, path: &str) { - self.tracked_paths.insert(path.into()); + self.tracked_env.paths.insert(path.into()); } fn literal_from_str(&mut self, s: &str) -> Result<Literal<Self::Span>, String> { @@ -185,24 +180,18 @@ impl server::Server for RaSpanServer<'_> { fn span_join(&mut self, first: Self::Span, second: Self::Span) -> Option<Self::Span> { // We can't modify the span range for fixup spans, those are meaningful to fixup, so just // prefer the non-fixup span. - if first.anchor.ast_id == FIXUP_ERASED_FILE_AST_ID_MARKER { + if first.anchor.ast_id == self.fixup_id { return Some(second); } - if second.anchor.ast_id == FIXUP_ERASED_FILE_AST_ID_MARKER { + if second.anchor.ast_id == self.fixup_id { return Some(first); } - // FIXME: Once we can talk back to the client, implement a "long join" request for anchors - // that differ in [AstId]s as joining those spans requires resolving the AstIds. if first.anchor != second.anchor { - return None; + return self.callback.as_mut()?.span_join(first, second); } - // Differing context, we can't merge these so prefer the one that's root + // Differing context, we can't merge these if first.ctx != second.ctx { - if first.ctx.is_root() { - return Some(second); - } else if second.ctx.is_root() { - return Some(first); - } + return Some(first); } Some(Span { range: first.range.cover(second.range), @@ -217,7 +206,7 @@ impl server::Server for RaSpanServer<'_> { end: Bound<usize>, ) -> Option<Self::Span> { // We can't modify the span range for fixup spans, those are meaningful to fixup. - if span.anchor.ast_id == FIXUP_ERASED_FILE_AST_ID_MARKER { + if span.anchor.ast_id == self.fixup_id { return Some(span); } let length = span.range.len().into(); @@ -260,7 +249,7 @@ impl server::Server for RaSpanServer<'_> { fn span_end(&mut self, span: Self::Span) -> Self::Span { // We can't modify the span range for fixup spans, those are meaningful to fixup. - if span.anchor.ast_id == FIXUP_ERASED_FILE_AST_ID_MARKER { + if span.anchor.ast_id == self.fixup_id { return span; } Span { range: TextRange::empty(span.range.end()), ..span } @@ -268,7 +257,7 @@ impl server::Server for RaSpanServer<'_> { fn span_start(&mut self, span: Self::Span) -> Self::Span { // We can't modify the span range for fixup spans, those are meaningful to fixup. - if span.anchor.ast_id == FIXUP_ERASED_FILE_AST_ID_MARKER { + if span.anchor.ast_id == self.fixup_id { return span; } Span { range: TextRange::empty(span.range.start()), ..span } diff --git a/crates/proc-macro-srv/src/server_impl/token_id.rs b/crates/proc-macro-srv/src/server_impl/token_id.rs index e1c96095c8..6c393b8bef 100644 --- a/crates/proc-macro-srv/src/server_impl/token_id.rs +++ b/crates/proc-macro-srv/src/server_impl/token_id.rs @@ -1,9 +1,6 @@ //! proc-macro server backend based on [`proc_macro_api::msg::SpanId`] as the backing span. //! This backend is rather inflexible, used by RustRover and older rust-analyzer versions. -use std::{ - collections::{HashMap, HashSet}, - ops::{Bound, Range}, -}; +use std::ops::{Bound, Range}; use intern::Symbol; use rustc_proc_macro::bridge::server; @@ -26,10 +23,6 @@ impl std::fmt::Debug for SpanId { type Span = SpanId; pub struct SpanIdServer<'a> { - // FIXME: Report this back to the caller to track as dependencies - pub tracked_env_vars: HashMap<Box<str>, Option<Box<str>>>, - // FIXME: Report this back to the caller to track as dependencies - pub tracked_paths: HashSet<Box<str>>, pub call_site: Span, pub def_site: Span, pub mixed_site: Span, @@ -60,12 +53,9 @@ impl server::Server for SpanIdServer<'_> { fn injected_env_var(&mut self, _: &str) -> Option<std::string::String> { None } - fn track_env_var(&mut self, var: &str, value: Option<&str>) { - self.tracked_env_vars.insert(var.into(), value.map(Into::into)); - } - fn track_path(&mut self, path: &str) { - self.tracked_paths.insert(path.into()); - } + fn track_env_var(&mut self, _: &str, _: Option<&str>) {} + + fn track_path(&mut self, _: &str) {} fn literal_from_str(&mut self, s: &str) -> Result<Literal<Self::Span>, String> { literal_from_str(s, self.call_site) diff --git a/crates/proc-macro-srv/src/tests/mod.rs b/crates/proc-macro-srv/src/tests/mod.rs index ebef9a9a51..a8ddb216f0 100644 --- a/crates/proc-macro-srv/src/tests/mod.rs +++ b/crates/proc-macro-srv/src/tests/mod.rs @@ -31,23 +31,23 @@ fn test_derive_empty() { IDENT 1 r#u32 "#]], expect![[r#" - IDENT 42:Root[0000, 0]@0..6#ROOT2024 struct - IDENT 42:Root[0000, 0]@7..8#ROOT2024 S - GROUP {} 42:Root[0000, 0]@9..10#ROOT2024 42:Root[0000, 0]@46..47#ROOT2024 42:Root[0000, 0]@9..47#ROOT2024 - IDENT 42:Root[0000, 0]@11..16#ROOT2024 field - PUNCT 42:Root[0000, 0]@16..17#ROOT2024 : [alone] - PUNCT 42:Root[0000, 0]@18..19#ROOT2024 & [joint] - PUNCT 42:Root[0000, 0]@22..23#ROOT2024 ' [joint] - IDENT 42:Root[0000, 0]@22..24#ROOT2024 r#lt - IDENT 42:Root[0000, 0]@25..27#ROOT2024 fn - GROUP () 42:Root[0000, 0]@27..28#ROOT2024 42:Root[0000, 0]@31..32#ROOT2024 42:Root[0000, 0]@27..32#ROOT2024 - IDENT 42:Root[0000, 0]@28..31#ROOT2024 u32 - PUNCT 42:Root[0000, 0]@33..34#ROOT2024 - [joint] - PUNCT 42:Root[0000, 0]@34..35#ROOT2024 > [alone] - PUNCT 42:Root[0000, 0]@36..37#ROOT2024 & [joint] - PUNCT 42:Root[0000, 0]@38..39#ROOT2024 ' [joint] - IDENT 42:Root[0000, 0]@38..39#ROOT2024 a - IDENT 42:Root[0000, 0]@42..45#ROOT2024 r#u32 + IDENT 42:Root[0000, 0]@0..6#0 struct + IDENT 42:Root[0000, 0]@7..8#0 S + GROUP {} 42:Root[0000, 0]@9..10#0 42:Root[0000, 0]@46..47#0 42:Root[0000, 0]@9..47#0 + IDENT 42:Root[0000, 0]@11..16#0 field + PUNCT 42:Root[0000, 0]@16..17#0 : [alone] + PUNCT 42:Root[0000, 0]@18..19#0 & [joint] + PUNCT 42:Root[0000, 0]@22..23#0 ' [joint] + IDENT 42:Root[0000, 0]@22..24#0 r#lt + IDENT 42:Root[0000, 0]@25..27#0 fn + GROUP () 42:Root[0000, 0]@27..28#0 42:Root[0000, 0]@31..32#0 42:Root[0000, 0]@27..32#0 + IDENT 42:Root[0000, 0]@28..31#0 u32 + PUNCT 42:Root[0000, 0]@33..34#0 - [joint] + PUNCT 42:Root[0000, 0]@34..35#0 > [alone] + PUNCT 42:Root[0000, 0]@36..37#0 & [joint] + PUNCT 42:Root[0000, 0]@38..39#0 ' [joint] + IDENT 42:Root[0000, 0]@38..39#0 a + IDENT 42:Root[0000, 0]@42..45#0 r#u32 "#]], ); } @@ -137,76 +137,76 @@ pub struct Foo { PUNCT 1 , [alone] "#]], expect![[r#" - PUNCT 42:Root[0000, 0]@1..2#ROOT2024 # [joint] - GROUP [] 42:Root[0000, 0]@2..3#ROOT2024 42:Root[0000, 0]@52..53#ROOT2024 42:Root[0000, 0]@2..53#ROOT2024 - IDENT 42:Root[0000, 0]@3..9#ROOT2024 helper - GROUP () 42:Root[0000, 0]@9..10#ROOT2024 42:Root[0000, 0]@51..52#ROOT2024 42:Root[0000, 0]@9..52#ROOT2024 - IDENT 42:Root[0000, 0]@10..18#ROOT2024 build_fn - GROUP () 42:Root[0000, 0]@18..19#ROOT2024 42:Root[0000, 0]@50..51#ROOT2024 42:Root[0000, 0]@18..51#ROOT2024 - IDENT 42:Root[0000, 0]@19..26#ROOT2024 private - PUNCT 42:Root[0000, 0]@26..27#ROOT2024 , [alone] - IDENT 42:Root[0000, 0]@28..32#ROOT2024 name - PUNCT 42:Root[0000, 0]@33..34#ROOT2024 = [alone] - LITER 42:Root[0000, 0]@35..50#ROOT2024 Str partial_build - IDENT 42:Root[0000, 0]@54..57#ROOT2024 pub - IDENT 42:Root[0000, 0]@58..64#ROOT2024 struct - IDENT 42:Root[0000, 0]@65..68#ROOT2024 Foo - GROUP {} 42:Root[0000, 0]@69..70#ROOT2024 42:Root[0000, 0]@190..191#ROOT2024 42:Root[0000, 0]@69..191#ROOT2024 - PUNCT 42:Root[0000, 0]@0..0#ROOT2024 # [alone] - GROUP [] 42:Root[0000, 0]@0..0#ROOT2024 42:Root[0000, 0]@0..0#ROOT2024 42:Root[0000, 0]@0..0#ROOT2024 - IDENT 42:Root[0000, 0]@0..0#ROOT2024 doc - PUNCT 42:Root[0000, 0]@0..0#ROOT2024 = [alone] - LITER 42:Root[0000, 0]@75..130#ROOT2024 Str / The domain where this federated instance is running - PUNCT 42:Root[0000, 0]@135..136#ROOT2024 # [joint] - GROUP [] 42:Root[0000, 0]@136..137#ROOT2024 42:Root[0000, 0]@157..158#ROOT2024 42:Root[0000, 0]@136..158#ROOT2024 - IDENT 42:Root[0000, 0]@137..143#ROOT2024 helper - GROUP () 42:Root[0000, 0]@143..144#ROOT2024 42:Root[0000, 0]@156..157#ROOT2024 42:Root[0000, 0]@143..157#ROOT2024 - IDENT 42:Root[0000, 0]@144..150#ROOT2024 setter - GROUP () 42:Root[0000, 0]@150..151#ROOT2024 42:Root[0000, 0]@155..156#ROOT2024 42:Root[0000, 0]@150..156#ROOT2024 - IDENT 42:Root[0000, 0]@151..155#ROOT2024 into - IDENT 42:Root[0000, 0]@163..166#ROOT2024 pub - GROUP () 42:Root[0000, 0]@166..167#ROOT2024 42:Root[0000, 0]@172..173#ROOT2024 42:Root[0000, 0]@166..173#ROOT2024 - IDENT 42:Root[0000, 0]@167..172#ROOT2024 crate - IDENT 42:Root[0000, 0]@174..180#ROOT2024 domain - PUNCT 42:Root[0000, 0]@180..181#ROOT2024 : [alone] - IDENT 42:Root[0000, 0]@182..188#ROOT2024 String - PUNCT 42:Root[0000, 0]@188..189#ROOT2024 , [alone] - - - PUNCT 42:Root[0000, 0]@1..2#ROOT2024 # [joint] - GROUP [] 42:Root[0000, 0]@2..3#ROOT2024 42:Root[0000, 0]@52..53#ROOT2024 42:Root[0000, 0]@2..53#ROOT2024 - IDENT 42:Root[0000, 0]@3..9#ROOT2024 helper - GROUP () 42:Root[0000, 0]@9..10#ROOT2024 42:Root[0000, 0]@51..52#ROOT2024 42:Root[0000, 0]@9..52#ROOT2024 - IDENT 42:Root[0000, 0]@10..18#ROOT2024 build_fn - GROUP () 42:Root[0000, 0]@18..19#ROOT2024 42:Root[0000, 0]@50..51#ROOT2024 42:Root[0000, 0]@18..51#ROOT2024 - IDENT 42:Root[0000, 0]@19..26#ROOT2024 private - PUNCT 42:Root[0000, 0]@26..27#ROOT2024 , [alone] - IDENT 42:Root[0000, 0]@28..32#ROOT2024 name - PUNCT 42:Root[0000, 0]@33..34#ROOT2024 = [alone] - LITER 42:Root[0000, 0]@35..50#ROOT2024 Str partial_build - IDENT 42:Root[0000, 0]@54..57#ROOT2024 pub - IDENT 42:Root[0000, 0]@58..64#ROOT2024 struct - IDENT 42:Root[0000, 0]@65..68#ROOT2024 Foo - GROUP {} 42:Root[0000, 0]@69..70#ROOT2024 42:Root[0000, 0]@190..191#ROOT2024 42:Root[0000, 0]@69..191#ROOT2024 - PUNCT 42:Root[0000, 0]@0..0#ROOT2024 # [alone] - GROUP [] 42:Root[0000, 0]@0..0#ROOT2024 42:Root[0000, 0]@0..0#ROOT2024 42:Root[0000, 0]@0..0#ROOT2024 - IDENT 42:Root[0000, 0]@0..0#ROOT2024 doc - PUNCT 42:Root[0000, 0]@0..0#ROOT2024 = [alone] - LITER 42:Root[0000, 0]@75..130#ROOT2024 Str / The domain where this federated instance is running - PUNCT 42:Root[0000, 0]@135..136#ROOT2024 # [joint] - GROUP [] 42:Root[0000, 0]@136..137#ROOT2024 42:Root[0000, 0]@157..158#ROOT2024 42:Root[0000, 0]@136..158#ROOT2024 - IDENT 42:Root[0000, 0]@137..143#ROOT2024 helper - GROUP () 42:Root[0000, 0]@143..144#ROOT2024 42:Root[0000, 0]@156..157#ROOT2024 42:Root[0000, 0]@143..157#ROOT2024 - IDENT 42:Root[0000, 0]@144..150#ROOT2024 setter - GROUP () 42:Root[0000, 0]@150..151#ROOT2024 42:Root[0000, 0]@155..156#ROOT2024 42:Root[0000, 0]@150..156#ROOT2024 - IDENT 42:Root[0000, 0]@151..155#ROOT2024 into - IDENT 42:Root[0000, 0]@163..166#ROOT2024 pub - GROUP () 42:Root[0000, 0]@166..167#ROOT2024 42:Root[0000, 0]@172..173#ROOT2024 42:Root[0000, 0]@166..173#ROOT2024 - IDENT 42:Root[0000, 0]@167..172#ROOT2024 crate - IDENT 42:Root[0000, 0]@174..180#ROOT2024 domain - PUNCT 42:Root[0000, 0]@180..181#ROOT2024 : [alone] - IDENT 42:Root[0000, 0]@182..188#ROOT2024 String - PUNCT 42:Root[0000, 0]@188..189#ROOT2024 , [alone] + PUNCT 42:Root[0000, 0]@1..2#0 # [joint] + GROUP [] 42:Root[0000, 0]@2..3#0 42:Root[0000, 0]@52..53#0 42:Root[0000, 0]@2..53#0 + IDENT 42:Root[0000, 0]@3..9#0 helper + GROUP () 42:Root[0000, 0]@9..10#0 42:Root[0000, 0]@51..52#0 42:Root[0000, 0]@9..52#0 + IDENT 42:Root[0000, 0]@10..18#0 build_fn + GROUP () 42:Root[0000, 0]@18..19#0 42:Root[0000, 0]@50..51#0 42:Root[0000, 0]@18..51#0 + IDENT 42:Root[0000, 0]@19..26#0 private + PUNCT 42:Root[0000, 0]@26..27#0 , [alone] + IDENT 42:Root[0000, 0]@28..32#0 name + PUNCT 42:Root[0000, 0]@33..34#0 = [alone] + LITER 42:Root[0000, 0]@35..50#0 Str partial_build + IDENT 42:Root[0000, 0]@54..57#0 pub + IDENT 42:Root[0000, 0]@58..64#0 struct + IDENT 42:Root[0000, 0]@65..68#0 Foo + GROUP {} 42:Root[0000, 0]@69..70#0 42:Root[0000, 0]@190..191#0 42:Root[0000, 0]@69..191#0 + PUNCT 42:Root[0000, 0]@0..0#0 # [alone] + GROUP [] 42:Root[0000, 0]@0..0#0 42:Root[0000, 0]@0..0#0 42:Root[0000, 0]@0..0#0 + IDENT 42:Root[0000, 0]@0..0#0 doc + PUNCT 42:Root[0000, 0]@0..0#0 = [alone] + LITER 42:Root[0000, 0]@75..130#0 Str / The domain where this federated instance is running + PUNCT 42:Root[0000, 0]@135..136#0 # [joint] + GROUP [] 42:Root[0000, 0]@136..137#0 42:Root[0000, 0]@157..158#0 42:Root[0000, 0]@136..158#0 + IDENT 42:Root[0000, 0]@137..143#0 helper + GROUP () 42:Root[0000, 0]@143..144#0 42:Root[0000, 0]@156..157#0 42:Root[0000, 0]@143..157#0 + IDENT 42:Root[0000, 0]@144..150#0 setter + GROUP () 42:Root[0000, 0]@150..151#0 42:Root[0000, 0]@155..156#0 42:Root[0000, 0]@150..156#0 + IDENT 42:Root[0000, 0]@151..155#0 into + IDENT 42:Root[0000, 0]@163..166#0 pub + GROUP () 42:Root[0000, 0]@166..167#0 42:Root[0000, 0]@172..173#0 42:Root[0000, 0]@166..173#0 + IDENT 42:Root[0000, 0]@167..172#0 crate + IDENT 42:Root[0000, 0]@174..180#0 domain + PUNCT 42:Root[0000, 0]@180..181#0 : [alone] + IDENT 42:Root[0000, 0]@182..188#0 String + PUNCT 42:Root[0000, 0]@188..189#0 , [alone] + + + PUNCT 42:Root[0000, 0]@1..2#0 # [joint] + GROUP [] 42:Root[0000, 0]@2..3#0 42:Root[0000, 0]@52..53#0 42:Root[0000, 0]@2..53#0 + IDENT 42:Root[0000, 0]@3..9#0 helper + GROUP () 42:Root[0000, 0]@9..10#0 42:Root[0000, 0]@51..52#0 42:Root[0000, 0]@9..52#0 + IDENT 42:Root[0000, 0]@10..18#0 build_fn + GROUP () 42:Root[0000, 0]@18..19#0 42:Root[0000, 0]@50..51#0 42:Root[0000, 0]@18..51#0 + IDENT 42:Root[0000, 0]@19..26#0 private + PUNCT 42:Root[0000, 0]@26..27#0 , [alone] + IDENT 42:Root[0000, 0]@28..32#0 name + PUNCT 42:Root[0000, 0]@33..34#0 = [alone] + LITER 42:Root[0000, 0]@35..50#0 Str partial_build + IDENT 42:Root[0000, 0]@54..57#0 pub + IDENT 42:Root[0000, 0]@58..64#0 struct + IDENT 42:Root[0000, 0]@65..68#0 Foo + GROUP {} 42:Root[0000, 0]@69..70#0 42:Root[0000, 0]@190..191#0 42:Root[0000, 0]@69..191#0 + PUNCT 42:Root[0000, 0]@0..0#0 # [alone] + GROUP [] 42:Root[0000, 0]@0..0#0 42:Root[0000, 0]@0..0#0 42:Root[0000, 0]@0..0#0 + IDENT 42:Root[0000, 0]@0..0#0 doc + PUNCT 42:Root[0000, 0]@0..0#0 = [alone] + LITER 42:Root[0000, 0]@75..130#0 Str / The domain where this federated instance is running + PUNCT 42:Root[0000, 0]@135..136#0 # [joint] + GROUP [] 42:Root[0000, 0]@136..137#0 42:Root[0000, 0]@157..158#0 42:Root[0000, 0]@136..158#0 + IDENT 42:Root[0000, 0]@137..143#0 helper + GROUP () 42:Root[0000, 0]@143..144#0 42:Root[0000, 0]@156..157#0 42:Root[0000, 0]@143..157#0 + IDENT 42:Root[0000, 0]@144..150#0 setter + GROUP () 42:Root[0000, 0]@150..151#0 42:Root[0000, 0]@155..156#0 42:Root[0000, 0]@150..156#0 + IDENT 42:Root[0000, 0]@151..155#0 into + IDENT 42:Root[0000, 0]@163..166#0 pub + GROUP () 42:Root[0000, 0]@166..167#0 42:Root[0000, 0]@172..173#0 42:Root[0000, 0]@166..173#0 + IDENT 42:Root[0000, 0]@167..172#0 crate + IDENT 42:Root[0000, 0]@174..180#0 domain + PUNCT 42:Root[0000, 0]@180..181#0 : [alone] + IDENT 42:Root[0000, 0]@182..188#0 String + PUNCT 42:Root[0000, 0]@188..189#0 , [alone] "#]], ); } @@ -232,19 +232,19 @@ fn test_derive_error() { PUNCT 1 ; [alone] "#]], expect![[r#" - IDENT 42:Root[0000, 0]@0..6#ROOT2024 struct - IDENT 42:Root[0000, 0]@7..8#ROOT2024 S - GROUP {} 42:Root[0000, 0]@9..10#ROOT2024 42:Root[0000, 0]@22..23#ROOT2024 42:Root[0000, 0]@9..23#ROOT2024 - IDENT 42:Root[0000, 0]@11..16#ROOT2024 field - PUNCT 42:Root[0000, 0]@16..17#ROOT2024 : [alone] - IDENT 42:Root[0000, 0]@18..21#ROOT2024 u32 - - - IDENT 42:Root[0000, 0]@0..13#ROOT2024 compile_error - PUNCT 42:Root[0000, 0]@13..14#ROOT2024 ! [joint] - GROUP () 42:Root[0000, 0]@14..15#ROOT2024 42:Root[0000, 0]@62..63#ROOT2024 42:Root[0000, 0]@14..63#ROOT2024 - LITER 42:Root[0000, 0]@15..62#ROOT2024 Str #[derive(DeriveError)] struct S {field : u32} - PUNCT 42:Root[0000, 0]@63..64#ROOT2024 ; [alone] + IDENT 42:Root[0000, 0]@0..6#0 struct + IDENT 42:Root[0000, 0]@7..8#0 S + GROUP {} 42:Root[0000, 0]@9..10#0 42:Root[0000, 0]@22..23#0 42:Root[0000, 0]@9..23#0 + IDENT 42:Root[0000, 0]@11..16#0 field + PUNCT 42:Root[0000, 0]@16..17#0 : [alone] + IDENT 42:Root[0000, 0]@18..21#0 u32 + + + IDENT 42:Root[0000, 0]@0..13#0 compile_error + PUNCT 42:Root[0000, 0]@13..14#0 ! [joint] + GROUP () 42:Root[0000, 0]@14..15#0 42:Root[0000, 0]@62..63#0 42:Root[0000, 0]@14..63#0 + LITER 42:Root[0000, 0]@15..62#0 Str #[derive(DeriveError)] struct S {field : u32} + PUNCT 42:Root[0000, 0]@63..64#0 ; [alone] "#]], ); } @@ -273,22 +273,22 @@ fn test_fn_like_macro_noop() { GROUP [] 1 1 1 "#]], expect![[r#" - IDENT 42:Root[0000, 0]@0..5#ROOT2024 ident - PUNCT 42:Root[0000, 0]@5..6#ROOT2024 , [alone] - LITER 42:Root[0000, 0]@7..8#ROOT2024 Integer 0 - PUNCT 42:Root[0000, 0]@8..9#ROOT2024 , [alone] - LITER 42:Root[0000, 0]@10..11#ROOT2024 Integer 1 - PUNCT 42:Root[0000, 0]@11..12#ROOT2024 , [alone] - GROUP [] 42:Root[0000, 0]@13..14#ROOT2024 42:Root[0000, 0]@14..15#ROOT2024 42:Root[0000, 0]@13..15#ROOT2024 - - - IDENT 42:Root[0000, 0]@0..5#ROOT2024 ident - PUNCT 42:Root[0000, 0]@5..6#ROOT2024 , [alone] - LITER 42:Root[0000, 0]@7..8#ROOT2024 Integer 0 - PUNCT 42:Root[0000, 0]@8..9#ROOT2024 , [alone] - LITER 42:Root[0000, 0]@10..11#ROOT2024 Integer 1 - PUNCT 42:Root[0000, 0]@11..12#ROOT2024 , [alone] - GROUP [] 42:Root[0000, 0]@13..14#ROOT2024 42:Root[0000, 0]@14..15#ROOT2024 42:Root[0000, 0]@13..15#ROOT2024 + IDENT 42:Root[0000, 0]@0..5#0 ident + PUNCT 42:Root[0000, 0]@5..6#0 , [alone] + LITER 42:Root[0000, 0]@7..8#0 Integer 0 + PUNCT 42:Root[0000, 0]@8..9#0 , [alone] + LITER 42:Root[0000, 0]@10..11#0 Integer 1 + PUNCT 42:Root[0000, 0]@11..12#0 , [alone] + GROUP [] 42:Root[0000, 0]@13..14#0 42:Root[0000, 0]@14..15#0 42:Root[0000, 0]@13..15#0 + + + IDENT 42:Root[0000, 0]@0..5#0 ident + PUNCT 42:Root[0000, 0]@5..6#0 , [alone] + LITER 42:Root[0000, 0]@7..8#0 Integer 0 + PUNCT 42:Root[0000, 0]@8..9#0 , [alone] + LITER 42:Root[0000, 0]@10..11#0 Integer 1 + PUNCT 42:Root[0000, 0]@11..12#0 , [alone] + GROUP [] 42:Root[0000, 0]@13..14#0 42:Root[0000, 0]@14..15#0 42:Root[0000, 0]@13..15#0 "#]], ); } @@ -315,20 +315,20 @@ fn test_fn_like_macro_clone_ident_subtree() { IDENT 1 ident3 "#]], expect![[r#" - IDENT 42:Root[0000, 0]@0..5#ROOT2024 ident - PUNCT 42:Root[0000, 0]@5..6#ROOT2024 , [alone] - GROUP [] 42:Root[0000, 0]@7..8#ROOT2024 42:Root[0000, 0]@22..23#ROOT2024 42:Root[0000, 0]@7..23#ROOT2024 - IDENT 42:Root[0000, 0]@8..14#ROOT2024 ident2 - PUNCT 42:Root[0000, 0]@14..15#ROOT2024 , [alone] - IDENT 42:Root[0000, 0]@16..22#ROOT2024 ident3 - - - IDENT 42:Root[0000, 0]@0..5#ROOT2024 ident - PUNCT 42:Root[0000, 0]@5..6#ROOT2024 , [alone] - GROUP [] 42:Root[0000, 0]@7..23#ROOT2024 42:Root[0000, 0]@7..23#ROOT2024 42:Root[0000, 0]@7..23#ROOT2024 - IDENT 42:Root[0000, 0]@8..14#ROOT2024 ident2 - PUNCT 42:Root[0000, 0]@14..15#ROOT2024 , [alone] - IDENT 42:Root[0000, 0]@16..22#ROOT2024 ident3 + IDENT 42:Root[0000, 0]@0..5#0 ident + PUNCT 42:Root[0000, 0]@5..6#0 , [alone] + GROUP [] 42:Root[0000, 0]@7..8#0 42:Root[0000, 0]@22..23#0 42:Root[0000, 0]@7..23#0 + IDENT 42:Root[0000, 0]@8..14#0 ident2 + PUNCT 42:Root[0000, 0]@14..15#0 , [alone] + IDENT 42:Root[0000, 0]@16..22#0 ident3 + + + IDENT 42:Root[0000, 0]@0..5#0 ident + PUNCT 42:Root[0000, 0]@5..6#0 , [alone] + GROUP [] 42:Root[0000, 0]@7..23#0 42:Root[0000, 0]@7..23#0 42:Root[0000, 0]@7..23#0 + IDENT 42:Root[0000, 0]@8..14#0 ident2 + PUNCT 42:Root[0000, 0]@14..15#0 , [alone] + IDENT 42:Root[0000, 0]@16..22#0 ident3 "#]], ); } @@ -345,10 +345,10 @@ fn test_fn_like_macro_clone_raw_ident() { IDENT 1 r#async "#]], expect![[r#" - IDENT 42:Root[0000, 0]@2..7#ROOT2024 r#async + IDENT 42:Root[0000, 0]@2..7#0 r#async - IDENT 42:Root[0000, 0]@2..7#ROOT2024 r#async + IDENT 42:Root[0000, 0]@2..7#0 r#async "#]], ); } @@ -366,11 +366,11 @@ fn test_fn_like_fn_like_span_join() { IDENT 1 r#joined "#]], expect![[r#" - IDENT 42:Root[0000, 0]@0..3#ROOT2024 foo - IDENT 42:Root[0000, 0]@8..11#ROOT2024 bar + IDENT 42:Root[0000, 0]@0..3#0 foo + IDENT 42:Root[0000, 0]@8..11#0 bar - IDENT 42:Root[0000, 0]@0..11#ROOT2024 r#joined + IDENT 42:Root[0000, 0]@0..11#0 r#joined "#]], ); } @@ -391,14 +391,14 @@ fn test_fn_like_fn_like_span_ops() { IDENT 1 start_span "#]], expect![[r#" - IDENT 42:Root[0000, 0]@0..12#ROOT2024 set_def_site - IDENT 42:Root[0000, 0]@13..33#ROOT2024 resolved_at_def_site - IDENT 42:Root[0000, 0]@34..44#ROOT2024 start_span + IDENT 42:Root[0000, 0]@0..12#0 set_def_site + IDENT 42:Root[0000, 0]@13..33#0 resolved_at_def_site + IDENT 42:Root[0000, 0]@34..44#0 start_span - IDENT 41:Root[0000, 0]@0..150#ROOT2024 set_def_site - IDENT 42:Root[0000, 0]@13..33#ROOT2024 resolved_at_def_site - IDENT 42:Root[0000, 0]@34..34#ROOT2024 start_span + IDENT 41:Root[0000, 0]@0..150#0 set_def_site + IDENT 42:Root[0000, 0]@13..33#0 resolved_at_def_site + IDENT 42:Root[0000, 0]@34..34#0 start_span "#]], ); } @@ -428,19 +428,19 @@ fn test_fn_like_mk_literals() { expect![[r#" - LITER 42:Root[0000, 0]@0..100#ROOT2024 ByteStr byte_string - LITER 42:Root[0000, 0]@0..100#ROOT2024 Char c - LITER 42:Root[0000, 0]@0..100#ROOT2024 Str string - LITER 42:Root[0000, 0]@0..100#ROOT2024 Str -string - LITER 42:Root[0000, 0]@0..100#ROOT2024 CStr cstring - LITER 42:Root[0000, 0]@0..100#ROOT2024 Float 3.14f64 - LITER 42:Root[0000, 0]@0..100#ROOT2024 Float -3.14f64 - LITER 42:Root[0000, 0]@0..100#ROOT2024 Float 3.14 - LITER 42:Root[0000, 0]@0..100#ROOT2024 Float -3.14 - LITER 42:Root[0000, 0]@0..100#ROOT2024 Integer 123i64 - LITER 42:Root[0000, 0]@0..100#ROOT2024 Integer -123i64 - LITER 42:Root[0000, 0]@0..100#ROOT2024 Integer 123 - LITER 42:Root[0000, 0]@0..100#ROOT2024 Integer -123 + LITER 42:Root[0000, 0]@0..100#0 ByteStr byte_string + LITER 42:Root[0000, 0]@0..100#0 Char c + LITER 42:Root[0000, 0]@0..100#0 Str string + LITER 42:Root[0000, 0]@0..100#0 Str -string + LITER 42:Root[0000, 0]@0..100#0 CStr cstring + LITER 42:Root[0000, 0]@0..100#0 Float 3.14f64 + LITER 42:Root[0000, 0]@0..100#0 Float -3.14f64 + LITER 42:Root[0000, 0]@0..100#0 Float 3.14 + LITER 42:Root[0000, 0]@0..100#0 Float -3.14 + LITER 42:Root[0000, 0]@0..100#0 Integer 123i64 + LITER 42:Root[0000, 0]@0..100#0 Integer -123i64 + LITER 42:Root[0000, 0]@0..100#0 Integer 123 + LITER 42:Root[0000, 0]@0..100#0 Integer -123 "#]], ); } @@ -459,8 +459,8 @@ fn test_fn_like_mk_idents() { expect![[r#" - IDENT 42:Root[0000, 0]@0..100#ROOT2024 standard - IDENT 42:Root[0000, 0]@0..100#ROOT2024 r#raw + IDENT 42:Root[0000, 0]@0..100#0 standard + IDENT 42:Root[0000, 0]@0..100#0 r#raw "#]], ); } @@ -515,48 +515,48 @@ fn test_fn_like_macro_clone_literals() { LITER 1 CStr null "#]], expect![[r#" - LITER 42:Root[0000, 0]@0..4#ROOT2024 Integer 1u16 - PUNCT 42:Root[0000, 0]@4..5#ROOT2024 , [alone] - LITER 42:Root[0000, 0]@6..11#ROOT2024 Integer 2_u32 - PUNCT 42:Root[0000, 0]@11..12#ROOT2024 , [alone] - PUNCT 42:Root[0000, 0]@13..14#ROOT2024 - [alone] - LITER 42:Root[0000, 0]@14..18#ROOT2024 Integer 4i64 - PUNCT 42:Root[0000, 0]@18..19#ROOT2024 , [alone] - LITER 42:Root[0000, 0]@20..27#ROOT2024 Float 3.14f32 - PUNCT 42:Root[0000, 0]@27..28#ROOT2024 , [alone] - LITER 42:Root[0000, 0]@29..43#ROOT2024 Str hello bridge - PUNCT 42:Root[0000, 0]@43..44#ROOT2024 , [alone] - LITER 42:Root[0000, 0]@45..61#ROOT2024 Str suffixedsuffix - PUNCT 42:Root[0000, 0]@61..62#ROOT2024 , [alone] - LITER 42:Root[0000, 0]@63..73#ROOT2024 StrRaw(2) raw - PUNCT 42:Root[0000, 0]@73..74#ROOT2024 , [alone] - LITER 42:Root[0000, 0]@75..78#ROOT2024 Char a - PUNCT 42:Root[0000, 0]@78..79#ROOT2024 , [alone] - LITER 42:Root[0000, 0]@80..84#ROOT2024 Byte b - PUNCT 42:Root[0000, 0]@84..85#ROOT2024 , [alone] - LITER 42:Root[0000, 0]@86..93#ROOT2024 CStr null - - - LITER 42:Root[0000, 0]@0..4#ROOT2024 Integer 1u16 - PUNCT 42:Root[0000, 0]@4..5#ROOT2024 , [alone] - LITER 42:Root[0000, 0]@6..11#ROOT2024 Integer 2_u32 - PUNCT 42:Root[0000, 0]@11..12#ROOT2024 , [alone] - PUNCT 42:Root[0000, 0]@13..14#ROOT2024 - [alone] - LITER 42:Root[0000, 0]@14..18#ROOT2024 Integer 4i64 - PUNCT 42:Root[0000, 0]@18..19#ROOT2024 , [alone] - LITER 42:Root[0000, 0]@20..27#ROOT2024 Float 3.14f32 - PUNCT 42:Root[0000, 0]@27..28#ROOT2024 , [alone] - LITER 42:Root[0000, 0]@29..43#ROOT2024 Str hello bridge - PUNCT 42:Root[0000, 0]@43..44#ROOT2024 , [alone] - LITER 42:Root[0000, 0]@45..61#ROOT2024 Str suffixedsuffix - PUNCT 42:Root[0000, 0]@61..62#ROOT2024 , [alone] - LITER 42:Root[0000, 0]@63..73#ROOT2024 StrRaw(2) raw - PUNCT 42:Root[0000, 0]@73..74#ROOT2024 , [alone] - LITER 42:Root[0000, 0]@75..78#ROOT2024 Char a - PUNCT 42:Root[0000, 0]@78..79#ROOT2024 , [alone] - LITER 42:Root[0000, 0]@80..84#ROOT2024 Byte b - PUNCT 42:Root[0000, 0]@84..85#ROOT2024 , [alone] - LITER 42:Root[0000, 0]@86..93#ROOT2024 CStr null + LITER 42:Root[0000, 0]@0..4#0 Integer 1u16 + PUNCT 42:Root[0000, 0]@4..5#0 , [alone] + LITER 42:Root[0000, 0]@6..11#0 Integer 2_u32 + PUNCT 42:Root[0000, 0]@11..12#0 , [alone] + PUNCT 42:Root[0000, 0]@13..14#0 - [alone] + LITER 42:Root[0000, 0]@14..18#0 Integer 4i64 + PUNCT 42:Root[0000, 0]@18..19#0 , [alone] + LITER 42:Root[0000, 0]@20..27#0 Float 3.14f32 + PUNCT 42:Root[0000, 0]@27..28#0 , [alone] + LITER 42:Root[0000, 0]@29..43#0 Str hello bridge + PUNCT 42:Root[0000, 0]@43..44#0 , [alone] + LITER 42:Root[0000, 0]@45..61#0 Str suffixedsuffix + PUNCT 42:Root[0000, 0]@61..62#0 , [alone] + LITER 42:Root[0000, 0]@63..73#0 StrRaw(2) raw + PUNCT 42:Root[0000, 0]@73..74#0 , [alone] + LITER 42:Root[0000, 0]@75..78#0 Char a + PUNCT 42:Root[0000, 0]@78..79#0 , [alone] + LITER 42:Root[0000, 0]@80..84#0 Byte b + PUNCT 42:Root[0000, 0]@84..85#0 , [alone] + LITER 42:Root[0000, 0]@86..93#0 CStr null + + + LITER 42:Root[0000, 0]@0..4#0 Integer 1u16 + PUNCT 42:Root[0000, 0]@4..5#0 , [alone] + LITER 42:Root[0000, 0]@6..11#0 Integer 2_u32 + PUNCT 42:Root[0000, 0]@11..12#0 , [alone] + PUNCT 42:Root[0000, 0]@13..14#0 - [alone] + LITER 42:Root[0000, 0]@14..18#0 Integer 4i64 + PUNCT 42:Root[0000, 0]@18..19#0 , [alone] + LITER 42:Root[0000, 0]@20..27#0 Float 3.14f32 + PUNCT 42:Root[0000, 0]@27..28#0 , [alone] + LITER 42:Root[0000, 0]@29..43#0 Str hello bridge + PUNCT 42:Root[0000, 0]@43..44#0 , [alone] + LITER 42:Root[0000, 0]@45..61#0 Str suffixedsuffix + PUNCT 42:Root[0000, 0]@61..62#0 , [alone] + LITER 42:Root[0000, 0]@63..73#0 StrRaw(2) raw + PUNCT 42:Root[0000, 0]@73..74#0 , [alone] + LITER 42:Root[0000, 0]@75..78#0 Char a + PUNCT 42:Root[0000, 0]@78..79#0 , [alone] + LITER 42:Root[0000, 0]@80..84#0 Byte b + PUNCT 42:Root[0000, 0]@84..85#0 , [alone] + LITER 42:Root[0000, 0]@86..93#0 CStr null "#]], ); } @@ -593,30 +593,30 @@ fn test_fn_like_macro_negative_literals() { LITER 1 Float 2.7 "#]], expect![[r#" - PUNCT 42:Root[0000, 0]@0..1#ROOT2024 - [alone] - LITER 42:Root[0000, 0]@1..5#ROOT2024 Integer 1u16 - PUNCT 42:Root[0000, 0]@5..6#ROOT2024 , [alone] - PUNCT 42:Root[0000, 0]@7..8#ROOT2024 - [alone] - LITER 42:Root[0000, 0]@9..14#ROOT2024 Integer 2_u32 - PUNCT 42:Root[0000, 0]@14..15#ROOT2024 , [alone] - PUNCT 42:Root[0000, 0]@16..17#ROOT2024 - [alone] - LITER 42:Root[0000, 0]@17..24#ROOT2024 Float 3.14f32 - PUNCT 42:Root[0000, 0]@24..25#ROOT2024 , [alone] - PUNCT 42:Root[0000, 0]@26..27#ROOT2024 - [alone] - LITER 42:Root[0000, 0]@28..31#ROOT2024 Float 2.7 - - - PUNCT 42:Root[0000, 0]@0..1#ROOT2024 - [alone] - LITER 42:Root[0000, 0]@1..5#ROOT2024 Integer 1u16 - PUNCT 42:Root[0000, 0]@5..6#ROOT2024 , [alone] - PUNCT 42:Root[0000, 0]@7..8#ROOT2024 - [alone] - LITER 42:Root[0000, 0]@9..14#ROOT2024 Integer 2_u32 - PUNCT 42:Root[0000, 0]@14..15#ROOT2024 , [alone] - PUNCT 42:Root[0000, 0]@16..17#ROOT2024 - [alone] - LITER 42:Root[0000, 0]@17..24#ROOT2024 Float 3.14f32 - PUNCT 42:Root[0000, 0]@24..25#ROOT2024 , [alone] - PUNCT 42:Root[0000, 0]@26..27#ROOT2024 - [alone] - LITER 42:Root[0000, 0]@28..31#ROOT2024 Float 2.7 + PUNCT 42:Root[0000, 0]@0..1#0 - [alone] + LITER 42:Root[0000, 0]@1..5#0 Integer 1u16 + PUNCT 42:Root[0000, 0]@5..6#0 , [alone] + PUNCT 42:Root[0000, 0]@7..8#0 - [alone] + LITER 42:Root[0000, 0]@9..14#0 Integer 2_u32 + PUNCT 42:Root[0000, 0]@14..15#0 , [alone] + PUNCT 42:Root[0000, 0]@16..17#0 - [alone] + LITER 42:Root[0000, 0]@17..24#0 Float 3.14f32 + PUNCT 42:Root[0000, 0]@24..25#0 , [alone] + PUNCT 42:Root[0000, 0]@26..27#0 - [alone] + LITER 42:Root[0000, 0]@28..31#0 Float 2.7 + + + PUNCT 42:Root[0000, 0]@0..1#0 - [alone] + LITER 42:Root[0000, 0]@1..5#0 Integer 1u16 + PUNCT 42:Root[0000, 0]@5..6#0 , [alone] + PUNCT 42:Root[0000, 0]@7..8#0 - [alone] + LITER 42:Root[0000, 0]@9..14#0 Integer 2_u32 + PUNCT 42:Root[0000, 0]@14..15#0 , [alone] + PUNCT 42:Root[0000, 0]@16..17#0 - [alone] + LITER 42:Root[0000, 0]@17..24#0 Float 3.14f32 + PUNCT 42:Root[0000, 0]@24..25#0 , [alone] + PUNCT 42:Root[0000, 0]@26..27#0 - [alone] + LITER 42:Root[0000, 0]@28..31#0 Float 2.7 "#]], ); } @@ -647,20 +647,20 @@ fn test_attr_macro() { PUNCT 1 ; [alone] "#]], expect![[r#" - IDENT 42:Root[0000, 0]@0..3#ROOT2024 mod - IDENT 42:Root[0000, 0]@4..5#ROOT2024 m - GROUP {} 42:Root[0000, 0]@6..7#ROOT2024 42:Root[0000, 0]@7..8#ROOT2024 42:Root[0000, 0]@6..8#ROOT2024 + IDENT 42:Root[0000, 0]@0..3#0 mod + IDENT 42:Root[0000, 0]@4..5#0 m + GROUP {} 42:Root[0000, 0]@6..7#0 42:Root[0000, 0]@7..8#0 42:Root[0000, 0]@6..8#0 - IDENT 42:Root[0000, 0]@0..4#ROOT2024 some - IDENT 42:Root[0000, 0]@5..14#ROOT2024 arguments + IDENT 42:Root[0000, 0]@0..4#0 some + IDENT 42:Root[0000, 0]@5..14#0 arguments - IDENT 42:Root[0000, 0]@0..13#ROOT2024 compile_error - PUNCT 42:Root[0000, 0]@13..14#ROOT2024 ! [joint] - GROUP () 42:Root[0000, 0]@14..15#ROOT2024 42:Root[0000, 0]@55..56#ROOT2024 42:Root[0000, 0]@14..56#ROOT2024 - LITER 42:Root[0000, 0]@15..55#ROOT2024 Str #[attr_error(some arguments)] mod m {} - PUNCT 42:Root[0000, 0]@56..57#ROOT2024 ; [alone] + IDENT 42:Root[0000, 0]@0..13#0 compile_error + PUNCT 42:Root[0000, 0]@13..14#0 ! [joint] + GROUP () 42:Root[0000, 0]@14..15#0 42:Root[0000, 0]@55..56#0 42:Root[0000, 0]@14..56#0 + LITER 42:Root[0000, 0]@15..55#0 Str #[attr_error(some arguments)] mod m {} + PUNCT 42:Root[0000, 0]@56..57#0 ; [alone] "#]], ); } @@ -722,8 +722,8 @@ fn test_fn_like_span_line_column() { " hello", expect![[r#" - LITER 42:Root[0000, 0]@0..100#ROOT2024 Integer 2 - LITER 42:Root[0000, 0]@0..100#ROOT2024 Integer 1 + LITER 42:Root[0000, 0]@0..100#0 Integer 2 + LITER 42:Root[0000, 0]@0..100#0 Integer 1 "#]], ); } diff --git a/crates/proc-macro-srv/src/tests/utils.rs b/crates/proc-macro-srv/src/tests/utils.rs index 31beca20d6..9780bcf348 100644 --- a/crates/proc-macro-srv/src/tests/utils.rs +++ b/crates/proc-macro-srv/src/tests/utils.rs @@ -11,6 +11,11 @@ use crate::{ token_stream::TokenStream, }; +fn make_ctx() -> SyntaxContext { + // SAFETY: Tests do not use a Database, so this won't ever be used within salsa. + unsafe { SyntaxContext::from_u32(0) } +} + fn parse_string(call_site: SpanId, src: &str) -> TokenStream<SpanId> { TokenStream::from_str(src, call_site).unwrap() } @@ -62,7 +67,16 @@ fn assert_expand_impl( let attr_ts_string = attr_ts.as_ref().map(|it| format!("{it:?}")); let res = expander - .expand(macro_name, input_ts, attr_ts, def_site, call_site, mixed_site, None) + .expand( + macro_name, + input_ts, + attr_ts, + def_site, + call_site, + mixed_site, + &mut Default::default(), + None, + ) .unwrap(); expect.assert_eq(&format!( "{input_ts_string}{}{}{}", @@ -77,7 +91,7 @@ fn assert_expand_impl( file_id: EditionedFileId::current_edition(FileId::from_raw(41)), ast_id: ROOT_ERASED_FILE_AST_ID, }, - ctx: SyntaxContext::root(span::Edition::CURRENT), + ctx: make_ctx(), }; let call_site = Span { range: TextRange::new(0.into(), 100.into()), @@ -85,7 +99,7 @@ fn assert_expand_impl( file_id: EditionedFileId::current_edition(FileId::from_raw(42)), ast_id: ROOT_ERASED_FILE_AST_ID, }, - ctx: SyntaxContext::root(span::Edition::CURRENT), + ctx: make_ctx(), }; let mixed_site = call_site; @@ -94,8 +108,18 @@ fn assert_expand_impl( let fixture_string = format!("{fixture:?}"); let attr_string = attr.as_ref().map(|it| format!("{it:?}")); - let res = - expander.expand(macro_name, fixture, attr, def_site, call_site, mixed_site, None).unwrap(); + let res = expander + .expand( + macro_name, + fixture, + attr, + def_site, + call_site, + mixed_site, + &mut Default::default(), + None, + ) + .unwrap(); expect_spanned.assert_eq(&format!( "{fixture_string}{}{}{}", if attr_string.is_some() { "\n\n" } else { "" }, @@ -150,6 +174,10 @@ impl ProcMacroClientInterface for MockCallback<'_> { fn span_parent(&mut self, _span: Span) -> Option<Span> { None } + + fn span_join(&mut self, _: Span, _: Span) -> Option<Span> { + None + } } pub fn assert_expand_with_callback( @@ -166,7 +194,7 @@ pub fn assert_expand_with_callback( file_id: EditionedFileId::current_edition(FileId::from_raw(41)), ast_id: ROOT_ERASED_FILE_AST_ID, }, - ctx: SyntaxContext::root(span::Edition::CURRENT), + ctx: make_ctx(), }; let call_site = Span { range: TextRange::new(0.into(), 100.into()), @@ -174,7 +202,7 @@ pub fn assert_expand_with_callback( file_id: EditionedFileId::current_edition(FileId::from_raw(42)), ast_id: ROOT_ERASED_FILE_AST_ID, }, - ctx: SyntaxContext::root(span::Edition::CURRENT), + ctx: make_ctx(), }; let mixed_site = call_site; @@ -182,7 +210,16 @@ pub fn assert_expand_with_callback( let mut callback = MockCallback { text: ra_fixture }; let res = expander - .expand(macro_name, fixture, None, def_site, call_site, mixed_site, Some(&mut callback)) + .expand( + macro_name, + fixture, + None, + def_site, + call_site, + mixed_site, + &mut Default::default(), + Some(&mut callback), + ) .unwrap(); expect_spanned.assert_eq(&format!("{res:?}")); } diff --git a/crates/profile/Cargo.toml b/crates/profile/Cargo.toml index 048d3776ca..2cffb269fd 100644 --- a/crates/profile/Cargo.toml +++ b/crates/profile/Cargo.toml @@ -13,7 +13,6 @@ rust-version.workspace = true doctest = false [dependencies] -cfg-if = "1.0.1" jemalloc-ctl = { version = "0.5.4", package = "tikv-jemalloc-ctl", optional = true } [target.'cfg(all(target_os = "linux", not(target_env = "ohos"), any(target_arch = "x86", target_arch = "x86_64", target_arch = "aarch64")))'.dependencies] diff --git a/crates/profile/src/memory_usage.rs b/crates/profile/src/memory_usage.rs index 1462259d62..a8a409bd46 100644 --- a/crates/profile/src/memory_usage.rs +++ b/crates/profile/src/memory_usage.rs @@ -3,8 +3,6 @@ //! Measures the total size of all currently allocated objects. use std::fmt; -use cfg_if::cfg_if; - #[derive(Copy, Clone)] pub struct MemoryUsage { pub allocated: Bytes, @@ -25,15 +23,17 @@ impl std::ops::Sub for MemoryUsage { impl MemoryUsage { pub fn now() -> MemoryUsage { - cfg_if! { - if #[cfg(all(feature = "jemalloc", not(target_env = "msvc")))] { + cfg_select! { + all(feature = "jemalloc", not(target_env = "msvc")) => { jemalloc_ctl::epoch::advance().unwrap(); MemoryUsage { allocated: Bytes(jemalloc_ctl::stats::allocated::read().unwrap() as isize), } - } else if #[cfg(all(target_os = "linux", target_env = "gnu"))] { + } + all(target_os = "linux", target_env = "gnu") => { memusage_linux() - } else if #[cfg(windows)] { + } + windows => { // There doesn't seem to be an API for determining heap usage, so we try to // approximate that by using the Commit Charge value. @@ -48,7 +48,8 @@ impl MemoryUsage { let usage = unsafe { mem_counters.assume_init().PagefileUsage }; MemoryUsage { allocated: Bytes(usage as isize) } - } else { + } + _ => { MemoryUsage { allocated: Bytes(0) } } } diff --git a/crates/rust-analyzer/src/cli/analysis_stats.rs b/crates/rust-analyzer/src/cli/analysis_stats.rs index 61e0b1e611..1a036c3b99 100644 --- a/crates/rust-analyzer/src/cli/analysis_stats.rs +++ b/crates/rust-analyzer/src/cli/analysis_stats.rs @@ -14,14 +14,16 @@ use hir::{ Adt, AssocItem, Crate, DefWithBody, FindPathConfig, GenericDef, HasCrate, HasSource, HirDisplay, ModuleDef, Name, Variant, crate_lang_items, db::{DefDatabase, ExpandDatabase, HirDatabase}, - next_solver::{DbInterner, GenericArgs}, }; use hir_def::{ DefWithBodyId, ExpressionStoreOwnerId, GenericDefId, SyntheticSyntax, expr_store::{Body, BodySourceMap, ExpressionStore}, hir::{ExprId, PatId, generics::GenericParams}, }; -use hir_ty::InferenceResult; +use hir_ty::{ + InferenceResult, + next_solver::{DbInterner, GenericArgs}, +}; use ide::{ Analysis, AnalysisHost, AnnotationConfig, DiagnosticsConfig, Edition, InlayFieldsToResolve, InlayHintsConfig, LineCol, RaFixtureConfig, RootDatabase, @@ -415,7 +417,7 @@ impl flags::AnalysisStats { hir_def::AdtId::from(a), GenericArgs::empty(interner).store(), hir_ty::ParamEnvAndCrate { - param_env: db.trait_environment(GenericDefId::from(a).into()), + param_env: db.trait_environment(a.into()), krate: a.krate(db).into(), } .store(), diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs index f4c3f24ce6..6f532e4224 100644 --- a/crates/rust-analyzer/src/config.rs +++ b/crates/rust-analyzer/src/config.rs @@ -662,6 +662,9 @@ config_data! { /// For traits the type "methods" can be used to only exclude the methods but not the trait /// itself. /// + /// For modules the type "subItems" can be used to only exclude the all items in it but not the module + /// itself. This does not include items defined in nested modules. + /// /// This setting also inherits `#rust-analyzer.completion.excludeTraits#`. completion_autoimport_exclude: Vec<AutoImportExclusion> = vec![ AutoImportExclusion::Verbose { path: "core::borrow::Borrow".to_owned(), r#type: AutoImportExclusionType::Methods }, @@ -1938,6 +1941,9 @@ impl Config { AutoImportExclusionType::Methods => { ide_completion::AutoImportExclusionType::Methods } + AutoImportExclusionType::SubItems => { + ide_completion::AutoImportExclusionType::SubItems + } }, ), }) @@ -2997,6 +3003,7 @@ pub enum AutoImportExclusion { pub enum AutoImportExclusionType { Always, Methods, + SubItems, } #[derive(Serialize, Deserialize, Debug, Clone)] @@ -4128,10 +4135,11 @@ fn field_props(field: &str, ty: &str, doc: &[&str], default: &str) -> serde_json }, "type": { "type": "string", - "enum": ["always", "methods"], + "enum": ["always", "methods", "subItems"], "enumDescriptions": [ "Do not show this item or its methods (if it is a trait) in auto-import completions.", - "Do not show this traits methods in auto-import completions." + "Do not show this trait's methods in auto-import completions.", + "Do not show this module's all items in it in auto-import completions." ], }, } diff --git a/crates/rust-analyzer/src/flycheck.rs b/crates/rust-analyzer/src/flycheck.rs index aad8bece95..3c9a8c147d 100644 --- a/crates/rust-analyzer/src/flycheck.rs +++ b/crates/rust-analyzer/src/flycheck.rs @@ -63,7 +63,12 @@ pub(crate) enum Target { } impl CargoOptions { - pub(crate) fn apply_on_command(&self, cmd: &mut Command, ws_target_dir: Option<&Utf8Path>) { + pub(crate) fn apply_on_command( + &self, + cmd: &mut Command, + ws_target_dir: Option<&Utf8Path>, + package_repr: Option<&str>, + ) { for target in &self.target_tuples { cmd.args(["--target", target.as_str()]); } @@ -83,8 +88,24 @@ impl CargoOptions { cmd.arg("--no-default-features"); } if !self.features.is_empty() { + // If we are scoped to a particular package, filter any features of the form + // `crate/feature` which target other packages. + let features = if let Some(name) = package_repr { + let filtered = self + .features + .iter() + .filter(|f| match f.split_once('/') { + Some((c, _)) => c == name, + None => true, + }) + .map(|s| s.as_str()) + .collect::<Vec<_>>(); + filtered.join(" ") + } else { + self.features.join(" ") + }; cmd.arg("--features"); - cmd.arg(self.features.join(" ")); + cmd.arg(features); } } if let Some(target_dir) = self.target_dir_config.target_dir(ws_target_dir) { @@ -890,12 +911,18 @@ impl FlycheckActor { cmd.env("CARGO_LOG", "cargo::core::compiler::fingerprint=info"); cmd.arg(&cargo_options.subcommand); - match scope { - FlycheckScope::Workspace => cmd.arg("--workspace"), + let package_repr = match scope { + FlycheckScope::Workspace => { + cmd.arg("--workspace"); + None + } FlycheckScope::Package { package: PackageSpecifier::Cargo { package_id }, .. - } => cmd.arg("-p").arg(&package_id.repr), + } => { + cmd.arg("-p").arg(&package_id.repr); + Some(package_id.repr.as_str()) + } FlycheckScope::Package { package: PackageSpecifier::BuildInfo { .. }, .. } => { @@ -935,6 +962,7 @@ impl FlycheckActor { cargo_options.apply_on_command( &mut cmd, self.ws_target_dir.as_ref().map(Utf8PathBuf::as_path), + package_repr, ); cmd.args(&cargo_options.extra_args); Some((cmd, FlycheckCommandOrigin::Cargo)) diff --git a/crates/rust-analyzer/src/handlers/request.rs b/crates/rust-analyzer/src/handlers/request.rs index ca6a2e70b0..5bc0f5f0a7 100644 --- a/crates/rust-analyzer/src/handlers/request.rs +++ b/crates/rust-analyzer/src/handlers/request.rs @@ -1662,7 +1662,10 @@ pub(crate) fn handle_code_lens( .map(|spec| { matches!( spec.target_kind(), - TargetKind::Bin | TargetKind::Example | TargetKind::Test + TargetKind::Bin + | TargetKind::Example + | TargetKind::Test + | TargetKind::Bench ) }) .unwrap_or(false), @@ -2345,7 +2348,7 @@ fn should_skip_target(runnable: &Runnable, cargo_spec: Option<&TargetSpec>) -> b match &cargo_spec { Some(spec) => !matches!( spec.target_kind(), - TargetKind::Bin | TargetKind::Example | TargetKind::Test + TargetKind::Bin | TargetKind::Example | TargetKind::Test | TargetKind::Bench ), None => true, } diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs index a9ce6f728b..92d5323e96 100644 --- a/crates/rust-analyzer/src/main_loop.rs +++ b/crates/rust-analyzer/src/main_loop.rs @@ -478,6 +478,7 @@ impl GlobalState { (false, false) }; + let mut gc_elapsed = None; if self.is_quiescent() { let became_quiescent = !was_quiescent; if became_quiescent { @@ -541,8 +542,10 @@ impl GlobalState { && self.fmt_pool.handle.is_empty() && current_revision != self.last_gc_revision { + let gc_start = Instant::now(); self.analysis_host.trigger_garbage_collection(); - self.last_gc_revision = current_revision; + self.last_gc_revision = self.analysis_host.raw_database().nonce_and_revision().1; + gc_elapsed = Some(gc_start.elapsed()); } } @@ -588,10 +591,14 @@ impl GlobalState { let loop_duration = loop_start.elapsed(); if loop_duration > Duration::from_millis(100) && was_quiescent { tracing::warn!( - "overly long loop turn took {loop_duration:?} (event handling took {event_handling_duration:?}): {event_dbg_msg}" + "overly long loop turn took {loop_duration:?}:\n\ + (event handling took {event_handling_duration:?}): {event_dbg_msg}\n\ + (garbage collection took {gc_elapsed:?})" ); self.poke_rust_analyzer_developer(format!( - "overly long loop turn took {loop_duration:?} (event handling took {event_handling_duration:?}): {event_dbg_msg}" + "overly long loop turn took {loop_duration:?}:\n\ + (event handling took {event_handling_duration:?}): {event_dbg_msg}\n\ + (garbage collection took {gc_elapsed:?})" )); } } @@ -1260,6 +1267,10 @@ impl GlobalState { let mut dispatcher = RequestDispatcher { req: Some(req), global_state: self }; dispatcher.on_sync_mut::<lsp_types::request::Shutdown>(|s, ()| { s.shutdown_requested = true; + s.proc_macro_clients = + std::iter::repeat_with(|| None).take(s.proc_macro_clients.len()).collect(); + s.flycheck.iter().for_each(|handle| handle.cancel()); + s.discover_handles.clear(); Ok(()) }); diff --git a/crates/rust-analyzer/src/mem_docs.rs b/crates/rust-analyzer/src/mem_docs.rs index 6a93a0ebb4..05fc7a8dd6 100644 --- a/crates/rust-analyzer/src/mem_docs.rs +++ b/crates/rust-analyzer/src/mem_docs.rs @@ -3,6 +3,7 @@ use std::mem; use rustc_hash::FxHashMap; +use triomphe::Arc; use vfs::VfsPath; /// Holds the set of in-memory documents. @@ -11,7 +12,7 @@ use vfs::VfsPath; /// might be different from what's on disk. #[derive(Default, Clone)] pub(crate) struct MemDocs { - mem_docs: FxHashMap<VfsPath, DocumentData>, + mem_docs: Arc<FxHashMap<VfsPath, Arc<DocumentData>>>, added_or_removed: bool, } @@ -22,7 +23,7 @@ impl MemDocs { pub(crate) fn insert(&mut self, path: VfsPath, data: DocumentData) -> Result<(), ()> { self.added_or_removed = true; - match self.mem_docs.insert(path, data) { + match Arc::make_mut(&mut self.mem_docs).insert(path, Arc::new(data)) { Some(_) => Err(()), None => Ok(()), } @@ -30,20 +31,20 @@ impl MemDocs { pub(crate) fn remove(&mut self, path: &VfsPath) -> Result<(), ()> { self.added_or_removed = true; - match self.mem_docs.remove(path) { + match Arc::make_mut(&mut self.mem_docs).remove(path) { Some(_) => Ok(()), None => Err(()), } } pub(crate) fn get(&self, path: &VfsPath) -> Option<&DocumentData> { - self.mem_docs.get(path) + self.mem_docs.get(path).map(Arc::as_ref) } pub(crate) fn get_mut(&mut self, path: &VfsPath) -> Option<&mut DocumentData> { // NB: don't set `self.added_or_removed` here, as that purposefully only // tracks changes to the key set. - self.mem_docs.get_mut(path) + Arc::make_mut(&mut self.mem_docs).get_mut(path).map(Arc::make_mut) } pub(crate) fn iter(&self) -> impl Iterator<Item = &VfsPath> { diff --git a/crates/rust-analyzer/src/test_runner.rs b/crates/rust-analyzer/src/test_runner.rs index 0d9c8310d8..31f35df5c7 100644 --- a/crates/rust-analyzer/src/test_runner.rs +++ b/crates/rust-analyzer/src/test_runner.rs @@ -124,7 +124,7 @@ impl CargoTestHandle { cmd.arg("--no-fail-fast"); cmd.arg("--manifest-path"); cmd.arg(root.join("Cargo.toml")); - options.apply_on_command(&mut cmd, ws_target_dir); + options.apply_on_command(&mut cmd, ws_target_dir, Some(&test_target.package)); cmd.arg("--"); if let Some(path) = path { cmd.arg(path); diff --git a/crates/rust-analyzer/tests/slow-tests/flycheck.rs b/crates/rust-analyzer/tests/slow-tests/flycheck.rs index 386edb82f4..c6f1f81139 100644 --- a/crates/rust-analyzer/tests/slow-tests/flycheck.rs +++ b/crates/rust-analyzer/tests/slow-tests/flycheck.rs @@ -76,7 +76,6 @@ fn main() { } #[test] -#[ignore = "this test tends to stuck, FIXME: investigate that"] fn test_flycheck_diagnostic_with_override_command() { if skip_slow_tests() { return; @@ -113,7 +112,6 @@ fn main() {} } #[test] -#[ignore = "this test tends to stuck, FIXME: investigate that"] fn test_flycheck_diagnostics_with_override_command_cleared_after_fix() { if skip_slow_tests() { return; diff --git a/crates/rust-analyzer/tests/slow-tests/support.rs b/crates/rust-analyzer/tests/slow-tests/support.rs index 7390403673..2c4b978a4d 100644 --- a/crates/rust-analyzer/tests/slow-tests/support.rs +++ b/crates/rust-analyzer/tests/slow-tests/support.rs @@ -378,8 +378,9 @@ impl Server { { continue; } + } else if req.method != "workspace/diagnostic/refresh" { + panic!("unexpected request: {req:?}") } - panic!("unexpected request: {req:?}") } Message::Notification(_) => (), Message::Response(res) => { diff --git a/crates/span/src/hygiene.rs b/crates/span/src/hygiene.rs index f475de93e0..70b0447569 100644 --- a/crates/span/src/hygiene.rs +++ b/crates/span/src/hygiene.rs @@ -19,7 +19,9 @@ //! # The Call-site Hierarchy //! //! `ExpnData::call_site` in rustc, `MacroCallLoc::call_site` in rust-analyzer. +#[cfg(feature = "salsa")] use crate::Edition; + use std::fmt; /// A syntax context describes a hierarchy tracking order of macro definitions. @@ -282,7 +284,7 @@ const _: () = { let fields = SyntaxContext::ingredient(zalsa).data(zalsa, id); fields.edition } - None => Edition::from_u32(SyntaxContext::MAX_ID - self.into_u32()), + None => Edition::from_u32(SyntaxContext::MAX_ROOT_ID - self.into_u32()), } } @@ -332,32 +334,9 @@ const _: () = { } }; -impl SyntaxContext { - #[inline] - pub fn is_root(self) -> bool { - (SyntaxContext::MAX_ID - Edition::LATEST as u32) <= self.into_u32() - && self.into_u32() <= (SyntaxContext::MAX_ID - Edition::Edition2015 as u32) - } - - #[inline] - pub fn remove_root_edition(&mut self) { - if self.is_root() { - *self = Self::root(Edition::Edition2015); - } - } - - /// The root context, which is the parent of all other contexts. All `FileId`s have this context. - #[inline] - pub const fn root(edition: Edition) -> Self { - let edition = edition as u32; - // SAFETY: Roots are valid `SyntaxContext`s - unsafe { SyntaxContext::from_u32(SyntaxContext::MAX_ID - edition) } - } -} - #[cfg(feature = "salsa")] impl<'db> SyntaxContext { - const MAX_ID: u32 = salsa::Id::MAX_U32 - 1; + const MAX_ROOT_ID: u32 = salsa::Id::MAX_U32 + Edition::LATEST as u32; #[inline] pub const fn into_u32(self) -> u32 { @@ -378,7 +357,8 @@ impl<'db> SyntaxContext { if self.is_root() { None } else { - // SAFETY: By our invariant, this is either a root (which we verified it's not) or a valid `salsa::Id`. + // SAFETY: By our invariant, this is either a root (which we verified it's not) or a + // valid `salsa::Id` index. unsafe { Some(salsa::Id::from_index(self.0)) } } } @@ -390,6 +370,27 @@ impl<'db> SyntaxContext { } #[inline] + pub fn is_root(self) -> bool { + (SyntaxContext::MAX_ROOT_ID - Edition::LATEST as u32) <= self.into_u32() + && self.into_u32() <= (SyntaxContext::MAX_ROOT_ID - Edition::Edition2015 as u32) + } + + #[inline] + pub fn remove_root_edition(&mut self) { + if self.is_root() { + *self = Self::root(Edition::Edition2015); + } + } + + /// The root context, which is the parent of all other contexts. All `FileId`s have this context. + #[inline] + pub const fn root(edition: Edition) -> Self { + let edition = edition as u32; + // SAFETY: Roots are valid `SyntaxContext`s + unsafe { SyntaxContext::from_u32(SyntaxContext::MAX_ROOT_ID - edition) } + } + + #[inline] pub fn outer_mark( self, db: &'db dyn salsa::Database, @@ -447,15 +448,8 @@ impl<'db> SyntaxContext { #[derive(Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)] pub struct SyntaxContext(u32); -#[allow(dead_code)] -const SALSA_MAX_ID_MIRROR: u32 = u32::MAX - 0xFF; -#[cfg(feature = "salsa")] -const _: () = assert!(salsa::Id::MAX_U32 == SALSA_MAX_ID_MIRROR); - #[cfg(not(feature = "salsa"))] impl SyntaxContext { - const MAX_ID: u32 = SALSA_MAX_ID_MIRROR - 1; - pub const fn into_u32(self) -> u32 { self.0 } @@ -496,16 +490,28 @@ impl Transparency { } } +#[cfg(feature = "salsa")] impl fmt::Display for SyntaxContext { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { if self.is_root() { - write!(f, "ROOT{}", Edition::from_u32(SyntaxContext::MAX_ID - self.into_u32()).number()) + write!( + f, + "ROOT{}", + Edition::from_u32(SyntaxContext::MAX_ROOT_ID - self.into_u32()).number() + ) } else { write!(f, "{}", self.into_u32()) } } } +#[cfg(not(feature = "salsa"))] +impl fmt::Display for SyntaxContext { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.into_u32()) + } +} + impl std::fmt::Debug for SyntaxContext { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { if f.alternate() { @@ -515,3 +521,69 @@ impl std::fmt::Debug for SyntaxContext { } } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_root_edition_is_root() { + for edition in Edition::iter() { + let ctx = SyntaxContext::root(edition); + assert!(ctx.is_root(), "{edition} root should be identified as root"); + } + } + + #[test] + fn test_root_edition_editions() { + let db = salsa::DatabaseImpl::new(); + for edition in Edition::iter() { + let ctx = SyntaxContext::root(edition); + assert_eq!(edition, ctx.edition(&db), "{edition} root should have edition {edition}"); + } + } + + #[test] + fn test_roots_do_not_overlap_with_salsa_ids() { + for edition in Edition::iter() { + let root = SyntaxContext::root(edition); + let root_u32 = root.into_u32(); + assert!( + root_u32 >= salsa::Id::MAX_U32, + "Root context for {:?} (value {}) must be >= salsa::Id::MAX_U32 ({}) to avoid collision", + edition, + root_u32, + salsa::Id::MAX_U32 + ); + } + } + + #[test] + fn test_non_root_value_is_not_root() { + for edition in Edition::iter() { + // SAFETY: This is just for testing purposes + let ctx = unsafe { SyntaxContext::from_u32(edition as u32 + 1) }; + assert!(!ctx.is_root(), "{edition} root should be identified as root"); + } + } + + #[test] + fn test_interned_context_round_trips_through_u32() { + let db = salsa::DatabaseImpl::new(); + let root = SyntaxContext::root(Edition::Edition2015); + let ctx = SyntaxContext::new( + &db, + None, + Transparency::Opaque, + Edition::Edition2021, + root, + |_| root, + |_| root, + ); + + // SAFETY: The value was produced by `SyntaxContext::into_u32` above. + let round_tripped = unsafe { SyntaxContext::from_u32(ctx.into_u32()) }; + assert_eq!(round_tripped.edition(&db), Edition::Edition2021); + assert_eq!(round_tripped.parent(&db), root); + } +} diff --git a/crates/span/src/lib.rs b/crates/span/src/lib.rs index bfe7b2620d..8274a94edb 100644 --- a/crates/span/src/lib.rs +++ b/crates/span/src/lib.rs @@ -51,13 +51,20 @@ impl Span { } // Differing context, we can't merge these so prefer the one that's root if self.ctx != other.ctx { + #[cfg(feature = "salsa")] if self.ctx.is_root() { return Some(other); } else if other.ctx.is_root() { return Some(self); } + None + } else { + Some(Span { + range: self.range.cover(other.range), + anchor: other.anchor, + ctx: other.ctx, + }) } - Some(Span { range: self.range.cover(other.range), anchor: other.anchor, ctx: other.ctx }) } pub fn eq_ignoring_ctx(self, other: Self) -> bool { diff --git a/crates/span/src/map.rs b/crates/span/src/map.rs index dc7d471aa0..d8309b04d4 100644 --- a/crates/span/src/map.rs +++ b/crates/span/src/map.rs @@ -6,8 +6,8 @@ use std::{fmt, hash::Hash}; use stdx::{always, itertools::Itertools}; use crate::{ - EditionedFileId, ErasedFileAstId, ROOT_ERASED_FILE_AST_ID, Span, SpanAnchor, SyntaxContext, - TextRange, TextSize, + EditionedFileId, ErasedFileAstId, ROOT_ERASED_FILE_AST_ID, Span, SyntaxContext, TextRange, + TextSize, }; /// Maps absolute text ranges for the corresponding file to the relevant span data. @@ -220,6 +220,7 @@ impl RealSpanMap { Self { file_id, pairs, end } } + #[cfg(feature = "salsa")] pub fn span_for_range(&self, range: TextRange) -> Span { assert!( range.end() <= self.end, @@ -234,7 +235,7 @@ impl RealSpanMap { let (offset, ast_id) = self.pairs[idx - 1]; Span { range: range - offset, - anchor: SpanAnchor { file_id: self.file_id, ast_id }, + anchor: crate::SpanAnchor { file_id: self.file_id, ast_id }, ctx: SyntaxContext::root(self.file_id.edition()), } } diff --git a/crates/stdx/src/assert.rs b/crates/stdx/src/assert.rs index 91c279798c..6032395a4d 100644 --- a/crates/stdx/src/assert.rs +++ b/crates/stdx/src/assert.rs @@ -43,7 +43,7 @@ /// Asserts that the condition is always true and returns its actual value. /// -/// If the condition is true does nothing and and evaluates to true. +/// If the condition is true does nothing and evaluates to true. /// /// If the condition is false: /// * panics if `force` feature or `debug_assertions` are enabled, @@ -71,7 +71,7 @@ macro_rules! always { /// Asserts that the condition is never true and returns its actual value. /// -/// If the condition is false does nothing and and evaluates to false. +/// If the condition is false does nothing and evaluates to false. /// /// If the condition is true: /// * panics if `force` feature or `debug_assertions` are enabled, diff --git a/crates/syntax-bridge/Cargo.toml b/crates/syntax-bridge/Cargo.toml index c928f23e02..a539cea67f 100644 --- a/crates/syntax-bridge/Cargo.toml +++ b/crates/syntax-bridge/Cargo.toml @@ -20,8 +20,7 @@ syntax.workspace = true parser.workspace = true tt.workspace = true stdx.workspace = true -# span = {workspace = true, default-features = false} does not work -span = { path = "../span", version = "0.0.0", default-features = false} +span.workspace = true intern.workspace = true [dev-dependencies] diff --git a/crates/syntax-bridge/src/prettify_macro_expansion.rs b/crates/syntax-bridge/src/prettify_macro_expansion.rs index 648119ed70..001c920c9b 100644 --- a/crates/syntax-bridge/src/prettify_macro_expansion.rs +++ b/crates/syntax-bridge/src/prettify_macro_expansion.rs @@ -3,6 +3,7 @@ use syntax::{ NodeOrToken, SyntaxKind::{self, *}, SyntaxNode, SyntaxToken, T, WalkEvent, + ast::syntax_factory::SyntaxFactory, syntax_editor::{Position, SyntaxEditor}, }; @@ -21,7 +22,7 @@ pub enum PrettifyWsKind { #[deprecated = "use `hir_expand::prettify_macro_expansion()` instead"] pub fn prettify_macro_expansion( syn: SyntaxNode, - dollar_crate_replacement: &mut dyn FnMut(&SyntaxToken) -> Option<SyntaxToken>, + dollar_crate_replacement: &mut dyn FnMut(&SyntaxToken, &SyntaxFactory) -> Option<SyntaxToken>, inspect_mods: impl FnOnce(&[(Position, PrettifyWsKind)]), ) -> SyntaxNode { let mut indent = 0; @@ -66,7 +67,7 @@ pub fn prettify_macro_expansion( }; if token.kind() == SyntaxKind::IDENT && token.text() == "$crate" - && let Some(replacement) = dollar_crate_replacement(&token) + && let Some(replacement) = dollar_crate_replacement(&token, editor.make()) { dollar_crate_replacements.push((token.clone(), replacement)); } @@ -191,7 +192,7 @@ mod tests { let source_file = syntax::ast::SourceFile::parse(&ra_fixture, span::Edition::CURRENT); let syn = remove_whitespaces(&source_file.syntax_node()); - let pretty = prettify_macro_expansion(syn, &mut |_| None, |_| ()); + let pretty = prettify_macro_expansion(syn, &mut |_, _| None, |_| ()); let mut pretty = pretty.to_string(); if pretty.contains('\n') { pretty.push('\n'); diff --git a/crates/syntax/rust.ungram b/crates/syntax/rust.ungram index 6bcf8ba743..408f2f4b32 100644 --- a/crates/syntax/rust.ungram +++ b/crates/syntax/rust.ungram @@ -665,6 +665,7 @@ Type = | NeverType | ParenType | PathType +| PatternType | PtrType | RefType | SliceType @@ -706,6 +707,9 @@ FnPtrType = ForType = ForBinder Type +PatternType = + 'builtin' '#' 'pattern_type' '(' Type 'is' Pat ')' + ImplTraitType = 'impl' TypeBoundList @@ -749,6 +753,10 @@ Pat = | TupleStructPat | ConstBlockPat | DerefPat +| NotNull + +NotNull = + '!' 'null' DerefPat = 'builtin' '#' 'deref' '(' Pat ')' diff --git a/crates/syntax/src/ast/generated/nodes.rs b/crates/syntax/src/ast/generated/nodes.rs index e3e5c499d4..c18311bfa3 100644 --- a/crates/syntax/src/ast/generated/nodes.rs +++ b/crates/syntax/src/ast/generated/nodes.rs @@ -1186,6 +1186,15 @@ impl NeverType { #[inline] pub fn excl_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![!]) } } +pub struct NotNull { + pub(crate) syntax: SyntaxNode, +} +impl NotNull { + #[inline] + pub fn excl_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![!]) } + #[inline] + pub fn null_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![null]) } +} pub struct OffsetOfExpr { pub(crate) syntax: SyntaxNode, } @@ -1357,6 +1366,29 @@ impl PathType { #[inline] pub fn path(&self) -> Option<Path> { support::child(&self.syntax) } } +pub struct PatternType { + pub(crate) syntax: SyntaxNode, +} +impl PatternType { + #[inline] + pub fn pat(&self) -> Option<Pat> { support::child(&self.syntax) } + #[inline] + pub fn ty(&self) -> Option<Type> { support::child(&self.syntax) } + #[inline] + pub fn pound_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![#]) } + #[inline] + pub fn l_paren_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T!['(']) } + #[inline] + pub fn r_paren_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![')']) } + #[inline] + pub fn builtin_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![builtin]) } + #[inline] + pub fn is_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![is]) } + #[inline] + pub fn pattern_type_token(&self) -> Option<SyntaxToken> { + support::token(&self.syntax, T![pattern_type]) + } +} pub struct PrefixExpr { pub(crate) syntax: SyntaxNode, } @@ -2275,6 +2307,7 @@ pub enum Pat { IdentPat(IdentPat), LiteralPat(LiteralPat), MacroPat(MacroPat), + NotNull(NotNull), OrPat(OrPat), ParenPat(ParenPat), PathPat(PathPat), @@ -2307,6 +2340,7 @@ pub enum Type { NeverType(NeverType), ParenType(ParenType), PathType(PathType), + PatternType(PatternType), PtrType(PtrType), RefType(RefType), SliceType(SliceType), @@ -5363,6 +5397,38 @@ impl fmt::Debug for NeverType { f.debug_struct("NeverType").field("syntax", &self.syntax).finish() } } +impl AstNode for NotNull { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + NOT_NULL + } + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { kind == NOT_NULL } + #[inline] + fn cast(syntax: SyntaxNode) -> Option<Self> { + if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { &self.syntax } +} +impl hash::Hash for NotNull { + fn hash<H: hash::Hasher>(&self, state: &mut H) { self.syntax.hash(state); } +} +impl Eq for NotNull {} +impl PartialEq for NotNull { + fn eq(&self, other: &Self) -> bool { self.syntax == other.syntax } +} +impl Clone for NotNull { + fn clone(&self) -> Self { Self { syntax: self.syntax.clone() } } +} +impl fmt::Debug for NotNull { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("NotNull").field("syntax", &self.syntax).finish() + } +} impl AstNode for OffsetOfExpr { #[inline] fn kind() -> SyntaxKind @@ -5811,6 +5877,38 @@ impl fmt::Debug for PathType { f.debug_struct("PathType").field("syntax", &self.syntax).finish() } } +impl AstNode for PatternType { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + PATTERN_TYPE + } + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { kind == PATTERN_TYPE } + #[inline] + fn cast(syntax: SyntaxNode) -> Option<Self> { + if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { &self.syntax } +} +impl hash::Hash for PatternType { + fn hash<H: hash::Hasher>(&self, state: &mut H) { self.syntax.hash(state); } +} +impl Eq for PatternType {} +impl PartialEq for PatternType { + fn eq(&self, other: &Self) -> bool { self.syntax == other.syntax } +} +impl Clone for PatternType { + fn clone(&self) -> Self { Self { syntax: self.syntax.clone() } } +} +impl fmt::Debug for PatternType { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("PatternType").field("syntax", &self.syntax).finish() + } +} impl AstNode for PrefixExpr { #[inline] fn kind() -> SyntaxKind @@ -8581,6 +8679,10 @@ impl From<MacroPat> for Pat { #[inline] fn from(node: MacroPat) -> Pat { Pat::MacroPat(node) } } +impl From<NotNull> for Pat { + #[inline] + fn from(node: NotNull) -> Pat { Pat::NotNull(node) } +} impl From<OrPat> for Pat { #[inline] fn from(node: OrPat) -> Pat { Pat::OrPat(node) } @@ -8636,6 +8738,7 @@ impl AstNode for Pat { | IDENT_PAT | LITERAL_PAT | MACRO_PAT + | NOT_NULL | OR_PAT | PAREN_PAT | PATH_PAT @@ -8658,6 +8761,7 @@ impl AstNode for Pat { IDENT_PAT => Pat::IdentPat(IdentPat { syntax }), LITERAL_PAT => Pat::LiteralPat(LiteralPat { syntax }), MACRO_PAT => Pat::MacroPat(MacroPat { syntax }), + NOT_NULL => Pat::NotNull(NotNull { syntax }), OR_PAT => Pat::OrPat(OrPat { syntax }), PAREN_PAT => Pat::ParenPat(ParenPat { syntax }), PATH_PAT => Pat::PathPat(PathPat { syntax }), @@ -8682,6 +8786,7 @@ impl AstNode for Pat { Pat::IdentPat(it) => &it.syntax, Pat::LiteralPat(it) => &it.syntax, Pat::MacroPat(it) => &it.syntax, + Pat::NotNull(it) => &it.syntax, Pat::OrPat(it) => &it.syntax, Pat::ParenPat(it) => &it.syntax, Pat::PathPat(it) => &it.syntax, @@ -8748,6 +8853,10 @@ impl From<PathType> for Type { #[inline] fn from(node: PathType) -> Type { Type::PathType(node) } } +impl From<PatternType> for Type { + #[inline] + fn from(node: PatternType) -> Type { Type::PatternType(node) } +} impl From<PtrType> for Type { #[inline] fn from(node: PtrType) -> Type { Type::PtrType(node) } @@ -8779,6 +8888,7 @@ impl AstNode for Type { | NEVER_TYPE | PAREN_TYPE | PATH_TYPE + | PATTERN_TYPE | PTR_TYPE | REF_TYPE | SLICE_TYPE @@ -8798,6 +8908,7 @@ impl AstNode for Type { NEVER_TYPE => Type::NeverType(NeverType { syntax }), PAREN_TYPE => Type::ParenType(ParenType { syntax }), PATH_TYPE => Type::PathType(PathType { syntax }), + PATTERN_TYPE => Type::PatternType(PatternType { syntax }), PTR_TYPE => Type::PtrType(PtrType { syntax }), REF_TYPE => Type::RefType(RefType { syntax }), SLICE_TYPE => Type::SliceType(SliceType { syntax }), @@ -8819,6 +8930,7 @@ impl AstNode for Type { Type::NeverType(it) => &it.syntax, Type::ParenType(it) => &it.syntax, Type::PathType(it) => &it.syntax, + Type::PatternType(it) => &it.syntax, Type::PtrType(it) => &it.syntax, Type::RefType(it) => &it.syntax, Type::SliceType(it) => &it.syntax, @@ -10453,6 +10565,11 @@ impl std::fmt::Display for NeverType { std::fmt::Display::fmt(self.syntax(), f) } } +impl std::fmt::Display for NotNull { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(self.syntax(), f) + } +} impl std::fmt::Display for OffsetOfExpr { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) @@ -10523,6 +10640,11 @@ impl std::fmt::Display for PathType { std::fmt::Display::fmt(self.syntax(), f) } } +impl std::fmt::Display for PatternType { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(self.syntax(), f) + } +} impl std::fmt::Display for PrefixExpr { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) diff --git a/crates/syntax/src/ast/syntax_factory/constructors.rs b/crates/syntax/src/ast/syntax_factory/constructors.rs index 1070af65e7..2f7eab2423 100644 --- a/crates/syntax/src/ast/syntax_factory/constructors.rs +++ b/crates/syntax/src/ast/syntax_factory/constructors.rs @@ -479,6 +479,18 @@ impl SyntaxFactory { ast } + pub fn lifetime_param(&self, lifetime: ast::Lifetime) -> ast::LifetimeParam { + let ast = make::lifetime_param(lifetime.clone()).clone_for_update(); + + if let Some(mut mapping) = self.mappings() { + let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); + builder.map_node(lifetime.syntax().clone(), ast.lifetime().unwrap().syntax().clone()); + builder.finish(&mut mapping); + } + + ast + } + pub fn generic_param_list( &self, params: impl IntoIterator<Item = ast::GenericParam>, diff --git a/crates/syntax/src/syntax_editor/edits.rs b/crates/syntax/src/syntax_editor/edits.rs index 0338d976b0..f2b979eb9d 100644 --- a/crates/syntax/src/syntax_editor/edits.rs +++ b/crates/syntax/src/syntax_editor/edits.rs @@ -3,7 +3,7 @@ use crate::{ AstToken, Direction, SyntaxElement, SyntaxKind, SyntaxNode, SyntaxToken, T, algo::neighbor, - ast::{self, AstNode, HasGenericParams, HasName, edit::IndentLevel, make}, + ast::{self, AstNode, HasGenericParams, HasName, edit::IndentLevel}, syntax_editor::{Position, SyntaxEditor}, }; @@ -207,6 +207,7 @@ impl ast::AssocItemList { /// Attention! This function does align the first line of `item` with respect to `self`, /// but it does _not_ change indentation of other lines (if any). pub fn add_items(&self, editor: &SyntaxEditor, items: Vec<ast::AssocItem>) { + let make = editor.make(); let (indent, position, whitespace) = match self.assoc_items().last() { Some(last_item) => ( IndentLevel::from_node(last_item.syntax()), @@ -228,7 +229,7 @@ impl ast::AssocItemList { .flat_map(|(i, item)| { let whitespace = if i != 0 { "\n\n" } else { whitespace }; vec![ - make::tokens::whitespace(&format!("{whitespace}{indent}")).into(), + make.whitespace(&format!("{whitespace}{indent}")).into(), item.syntax().clone().into(), ] }) @@ -390,10 +391,11 @@ impl ast::VariantList { impl ast::Fn { pub fn replace_or_insert_body(&self, editor: &SyntaxEditor, body: ast::BlockExpr) { + let make = editor.make(); if let Some(old_body) = self.body() { editor.replace(old_body.syntax(), body.syntax()); } else { - let single_space = make::tokens::single_space(); + let single_space = make.whitespace(" "); let elements = vec![single_space.into(), body.syntax().clone().into()]; if let Some(semicolon) = self.semicolon_token() { diff --git a/crates/test-utils/src/fixture.rs b/crates/test-utils/src/fixture.rs index 1f6262c897..730e9aeb52 100644 --- a/crates/test-utils/src/fixture.rs +++ b/crates/test-utils/src/fixture.rs @@ -507,7 +507,7 @@ impl MiniCore { active_regions.drain(active_regions.len() - active_line_region..); } if inactive_line_region > 0 { - inactive_regions.drain(inactive_regions.len() - active_line_region..); + inactive_regions.drain(inactive_regions.len() - inactive_line_region..); } } diff --git a/crates/test-utils/src/lib.rs b/crates/test-utils/src/lib.rs index 62867fd5b5..4eab7f4b18 100644 --- a/crates/test-utils/src/lib.rs +++ b/crates/test-utils/src/lib.rs @@ -260,7 +260,7 @@ pub fn extract_annotations(text: &str) -> Vec<(TextRange, String)> { let &(_, idx) = prev_line_annotations .iter() .find(|&&(off, _idx)| off == offset) - .unwrap(); + .expect("annotation continuation not found"); res[idx].1.push('\n'); res[idx].1.push_str(&content); res[idx].1.push('\n'); diff --git a/crates/test-utils/src/minicore.rs b/crates/test-utils/src/minicore.rs index 8975fa56d7..bd15dca609 100644 --- a/crates/test-utils/src/minicore.rs +++ b/crates/test-utils/src/minicore.rs @@ -54,11 +54,12 @@ //! iterators: iterator, fn //! manually_drop: drop //! matches: -//! non_null: -//! non_zero: +//! non_null: pat +//! non_zero: pat, transmute, option //! option: panic //! ord: eq, option //! panic: fmt +//! pat: panic //! phantom_data: //! pin: //! pointee: copy, send, sync, ord, hash, unpin, phantom_data @@ -538,10 +539,10 @@ pub mod ptr { // endregion:pointee // region:non_null - #[rustc_layout_scalar_valid_range_start(1)] #[rustc_nonnull_optimization_guaranteed] + #[repr(transparent)] pub struct NonNull<T: crate::marker::PointeeSized> { - pointer: *const T, + pointer: crate::pattern_type!(*const T is !null), } // region:coerce_unsized impl<T: crate::marker::PointeeSized, U: crate::marker::PointeeSized> @@ -1713,6 +1714,43 @@ pub mod result { #[lang = "Err"] Err(E), } + impl<T, E> Result<T, E> { + pub const fn or<F>(self, res: Result<T, F>) -> Result<T, F> { + match self { + Ok(v) => Ok(v), + Err(_) => res, + } + } + + pub const fn unwrap_or(self, default: T) -> T { + match self { + Ok(t) => t, + Err(_) => default, + } + } + + // region:fn + pub const fn or_else<F, O>(self, op: O) -> Result<T, F> + where + O: FnOnce(E) -> Result<T, F>, + { + match self { + Ok(t) => Ok(t), + Err(e) => op(e), + } + } + + pub const fn unwrap_or_else<F>(self, op: F) -> T + where + F: FnOnce(E) -> T, + { + match self { + Ok(t) => t, + Err(e) => op(e), + } + } + // endregion:fn + } } // endregion:result @@ -2278,12 +2316,61 @@ mod macros { // endregion:deref_pat } +// region:pat +pub mod pat { + #[macro_export] + #[rustc_builtin_macro(pattern_type)] + macro_rules! pattern_type { + ($($arg:tt)*) => { + /* compiler built-in */ + }; + } + + pub const trait RangePattern { + #[lang = "RangeMin"] + const MIN: Self; + + #[lang = "RangeMax"] + const MAX: Self; + + #[lang = "RangeSub"] + fn sub_one(self) -> Self; + } + + impl const RangePattern for u8 { + const MIN: u8 = 0; + const MAX: u8 = 0xFF; + fn sub_one(self) -> Self { + if self == Self::MIN { + panic!("exclusive range end at minimum value of type") + } else { + self - 1 + } + } + } + + // region:coerce_unsized + impl<T: crate::marker::PointeeSized, U: crate::marker::PointeeSized> + crate::ops::CoerceUnsized<pattern_type!(*const U is !null)> for pattern_type!(*const T is !null) + where + T: crate::marker::Unsize<U>, + { + } + // endregion:coerce_unsized + + // region:dispatch_from_dyn + impl<T: crate::ops::DispatchFromDyn<U>, U> + crate::ops::DispatchFromDyn<pattern_type!(U is !null)> for pattern_type!(T is !null) + { + } + // endregion:dispatch_from_dyn +} +// endregion:pat + // region:non_zero pub mod num { #[repr(transparent)] - #[rustc_layout_scalar_valid_range_start(1)] - #[rustc_nonnull_optimization_guaranteed] - pub struct NonZeroU8(u8); + pub struct NonZeroU8(crate::pattern_type!(u8 is 1..)); } // endregion:non_zero @@ -2392,6 +2479,7 @@ pub mod prelude { hash::derive::Hash, // :hash, derive iter::{FromIterator, IntoIterator, Iterator}, // :iterator macros::builtin::{derive, derive_const}, // :derive + macros::deref, // :deref_pat marker::Copy, // :copy marker::Send, // :send marker::Sized, // :sized @@ -2405,7 +2493,6 @@ pub mod prelude { panic, // :panic result::Result::{self, Err, Ok}, // :result str::FromStr, // :str - macros::deref, // :deref_pat }; } diff --git a/crates/tt/Cargo.toml b/crates/tt/Cargo.toml index 6cfb76400e..9a798b592d 100644 --- a/crates/tt/Cargo.toml +++ b/crates/tt/Cargo.toml @@ -24,7 +24,6 @@ intern.workspace = true ra-ap-rustc_lexer.workspace = true [features] -default = [] in-rust-tree = [] [lints] diff --git a/crates/tt/src/lib.rs b/crates/tt/src/lib.rs index 72b0d762ef..7b46c33596 100644 --- a/crates/tt/src/lib.rs +++ b/crates/tt/src/lib.rs @@ -765,27 +765,24 @@ impl Subtree { pub fn pretty(tkns: TokenTreesView<'_>) -> String { return dispatch_ref! { - match tkns.repr => tt => pretty_impl(tt) + match tkns.repr => tt => pretty_impl(tkns, tt) }; use crate::storage::TokenTree; - fn tokentree_to_text<S: SpanStorage>(tkn: &TokenTree<S>, tkns: &mut &[TokenTree<S>]) -> String { + fn tokentree_to_text<S: SpanStorage>( + tkns_view: TokenTreesView<'_>, + tkn: &TokenTree<S>, + tkns: &mut &[TokenTree<S>], + ) -> String { match tkn { TokenTree::Ident { sym, is_raw, .. } => format!("{}{}", is_raw.as_str(), sym), - &TokenTree::Literal { ref text_and_suffix, kind, suffix_len, span: _ } => { + &TokenTree::Literal { ref text_and_suffix, kind, suffix_len, span } => { format!( "{}", Literal { text_and_suffix: text_and_suffix.clone(), - span: Span { - range: TextRange::empty(TextSize::new(0)), - anchor: span::SpanAnchor { - file_id: span::EditionedFileId::from_raw(0), - ast_id: span::FIXUP_ERASED_FILE_AST_ID_MARKER - }, - ctx: span::SyntaxContext::root(span::Edition::Edition2015) - }, + span: span.span(tkns_view.span_parts), kind, suffix_len } @@ -794,7 +791,7 @@ pub fn pretty(tkns: TokenTreesView<'_>) -> String { TokenTree::Punct { char, .. } => format!("{}", char), TokenTree::Subtree { len, delim_kind, .. } => { let (subtree_content, rest) = tkns.split_at(*len as usize); - let content = pretty_impl(subtree_content); + let content = pretty_impl(tkns_view, subtree_content); *tkns = rest; let (open, close) = match *delim_kind { DelimiterKind::Brace => ("{", "}"), @@ -807,13 +804,16 @@ pub fn pretty(tkns: TokenTreesView<'_>) -> String { } } - fn pretty_impl<S: SpanStorage>(mut tkns: &[TokenTree<S>]) -> String { + fn pretty_impl<S: SpanStorage>( + tkns_view: TokenTreesView<'_>, + mut tkns: &[TokenTree<S>], + ) -> String { let mut last = String::new(); let mut last_to_joint = true; while let Some((tkn, rest)) = tkns.split_first() { tkns = rest; - last = [last, tokentree_to_text(tkn, &mut tkns)].join(if last_to_joint { + last = [last, tokentree_to_text(tkns_view, tkn, &mut tkns)].join(if last_to_joint { "" } else { " " diff --git a/docs/book/src/configuration_generated.md b/docs/book/src/configuration_generated.md index 069c8211db..7bbb9e0258 100644 --- a/docs/book/src/configuration_generated.md +++ b/docs/book/src/configuration_generated.md @@ -446,6 +446,9 @@ verbose form `{ "path": "path::to::item", type: "always" }`. For traits the type "methods" can be used to only exclude the methods but not the trait itself. +For modules the type "subItems" can be used to only exclude the all items in it but not the module +itself. This does not include items defined in nested modules. + This setting also inherits `#rust-analyzer.completion.excludeTraits#`. diff --git a/editors/code/package.json b/editors/code/package.json index 14369e6f33..8df606d4c4 100644 --- a/editors/code/package.json +++ b/editors/code/package.json @@ -1328,7 +1328,7 @@ "title": "Completion", "properties": { "rust-analyzer.completion.autoimport.exclude": { - "markdownDescription": "A list of full paths to items to exclude from auto-importing completions.\n\nTraits in this list won't have their methods suggested in completions unless the trait\nis in scope.\n\nYou can either specify a string path which defaults to type \"always\" or use the more\nverbose form `{ \"path\": \"path::to::item\", type: \"always\" }`.\n\nFor traits the type \"methods\" can be used to only exclude the methods but not the trait\nitself.\n\nThis setting also inherits `#rust-analyzer.completion.excludeTraits#`.", + "markdownDescription": "A list of full paths to items to exclude from auto-importing completions.\n\nTraits in this list won't have their methods suggested in completions unless the trait\nis in scope.\n\nYou can either specify a string path which defaults to type \"always\" or use the more\nverbose form `{ \"path\": \"path::to::item\", type: \"always\" }`.\n\nFor traits the type \"methods\" can be used to only exclude the methods but not the trait\nitself.\n\nFor modules the type \"subItems\" can be used to only exclude the all items in it but not the module\nitself. This does not include items defined in nested modules.\n\nThis setting also inherits `#rust-analyzer.completion.excludeTraits#`.", "default": [ { "path": "core::borrow::Borrow", @@ -1355,11 +1355,13 @@ "type": "string", "enum": [ "always", - "methods" + "methods", + "subItems" ], "enumDescriptions": [ "Do not show this item or its methods (if it is a trait) in auto-import completions.", - "Do not show this traits methods in auto-import completions." + "Do not show this trait's methods in auto-import completions.", + "Do not show this module's all items in it in auto-import completions." ] } } diff --git a/rust-version b/rust-version index 1e8579c045..4b1ba970b7 100644 --- a/rust-version +++ b/rust-version @@ -1 +1 @@ -8afb6a8b1b32fce2f8aa7520517833338dc36c5e +ba0949ab745985a442e274ba52e8fb348cb0c662 diff --git a/xtask/src/codegen/grammar/ast_src.rs b/xtask/src/codegen/grammar/ast_src.rs index 43462d1c6e..49a625bdbd 100644 --- a/xtask/src/codegen/grammar/ast_src.rs +++ b/xtask/src/codegen/grammar/ast_src.rs @@ -123,6 +123,7 @@ const CONTEXTUAL_KEYWORDS: &[&str] = &[ "bikeshed", "cfg_attr", "cfg", + "null", ]; // keywords we use for special macro expansions const CONTEXTUAL_BUILTIN_KEYWORDS: &[&str] = &[ @@ -151,6 +152,8 @@ const CONTEXTUAL_BUILTIN_KEYWORDS: &[&str] = &[ "readonly", "sym", "deref", + "pattern_type", + "is", ]; // keywords that are keywords depending on the edition diff --git a/xtask/src/dist.rs b/xtask/src/dist.rs index 57a6a0eae1..e8bedbe79e 100644 --- a/xtask/src/dist.rs +++ b/xtask/src/dist.rs @@ -107,7 +107,9 @@ fn dist_server( ) -> anyhow::Result<()> { let _e = sh.push_env("CFG_RELEASE", release); let _e = sh.push_env("CARGO_PROFILE_RELEASE_LTO", "thin"); + let _e = sh.push_env("CARGO_PROFILE_RELEASE_CODEGEN_UNITS", "1"); let _e = sh.push_env("CARGO_PROFILE_DEV_REL_LTO", "thin"); + let _e = sh.push_env("CARGO_PROFILE_DEV_REL_CODEGEN_UNITS", "1"); // Uncomment to enable debug info for releases. Note that: // * debug info is split on windows and macs, so it does nothing for those platforms, diff --git a/xtask/src/install.rs b/xtask/src/install.rs index a803b4e943..bbb6d9aeac 100644 --- a/xtask/src/install.rs +++ b/xtask/src/install.rs @@ -179,7 +179,7 @@ fn install_proc_macro_server(sh: &Shell, opts: ProcMacroServerOpt) -> anyhow::Re let mut cmd = cmd!( sh, - "cargo install --path crates/proc-macro-srv-cli --profile={profile} --locked --force --features sysroot-abi" + "cargo install --path crates/proc-macro-srv-cli --profile={profile} --locked --force --features in-rust-tree" ); if std::env::var_os("RUSTUP_TOOLCHAIN").is_none() { cmd = cmd.env("RUSTUP_TOOLCHAIN", "nightly"); diff --git a/xtask/src/pgo.rs b/xtask/src/pgo.rs index 0c6f499811..ca6dace940 100644 --- a/xtask/src/pgo.rs +++ b/xtask/src/pgo.rs @@ -91,7 +91,7 @@ fn download_crate_for_training(sh: &Shell, pgo_dir: &Path, repo: &str) -> anyhow let target_path = pgo_dir.join(normalized_path); cmd!(sh, "git clone --depth 1 https://github.com/{repo} {revision...} {target_path}") .run() - .with_context(|| "cannot download PGO training crate from {repo}")?; + .with_context(|| format!("cannot download PGO training crate from {repo}"))?; Ok(target_path) } |