Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/hir/src/lib.rs')
| -rw-r--r-- | crates/hir/src/lib.rs | 1053 |
1 files changed, 775 insertions, 278 deletions
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index 9fc29de4a1..78be5a7e8f 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -48,12 +48,13 @@ use arrayvec::ArrayVec; use base_db::{CrateDisplayName, CrateOrigin, LangCrateOrigin}; use either::Either; use hir_def::{ - AdtId, AssocItemId, AssocItemLoc, CallableDefId, ConstId, ConstParamId, DefWithBodyId, EnumId, - EnumVariantId, ExternBlockId, ExternCrateId, FunctionId, GenericDefId, GenericParamId, + AdtId, AssocItemId, AssocItemLoc, BuiltinDeriveImplId, CallableDefId, ConstId, ConstParamId, + DefWithBodyId, EnumId, EnumVariantId, ExternBlockId, ExternCrateId, FunctionId, GenericDefId, HasModule, ImplId, ItemContainerId, LifetimeParamId, LocalFieldId, Lookup, MacroExpander, MacroId, StaticId, StructId, SyntheticSyntax, TupleId, TypeAliasId, TypeOrConstParamId, TypeParamId, UnionId, attrs::AttrFlags, + builtin_derive::BuiltinDeriveImplMethod, expr_store::{ExpressionStoreDiagnostics, ExpressionStoreSourceMap}, hir::{ BindingAnnotation, BindingId, Expr, ExprId, ExprOrPatId, LabelId, Pat, @@ -73,7 +74,8 @@ use hir_def::{ visibility::visibility_from_ast, }; use hir_expand::{ - AstId, MacroCallKind, RenderedExpandError, ValueResult, proc_macro::ProcMacroKind, + AstId, MacroCallKind, RenderedExpandError, ValueResult, builtin::BuiltinDeriveExpander, + proc_macro::ProcMacroKind, }; use hir_ty::{ GenericPredicates, InferenceResult, ParamEnvAndCrate, TyDefId, TyLoweringDiagnostic, @@ -88,8 +90,9 @@ use hir_ty::{ }, mir::{MutBorrowKind, interpret_mir}, next_solver::{ - AliasTy, ClauseKind, ConstKind, DbInterner, ErrorGuaranteed, GenericArg, GenericArgs, - ParamEnv, PolyFnSig, Region, SolverDefId, Ty, TyKind, TypingMode, + AliasTy, AnyImplId, ClauseKind, ConstKind, DbInterner, EarlyBinder, EarlyParamRegion, + ErrorGuaranteed, GenericArg, GenericArgs, ParamConst, ParamEnv, PolyFnSig, Region, + RegionKind, SolverDefId, Ty, TyKind, TypingMode, infer::{DbInternerInferExt, InferCtxt}, }, traits::{self, is_inherent_impl_coherent, structurally_normalize_ty}, @@ -97,7 +100,8 @@ use hir_ty::{ use itertools::Itertools; use rustc_hash::FxHashSet; use rustc_type_ir::{ - AliasTyKind, TypeSuperVisitable, TypeVisitable, TypeVisitor, fast_reject, + AliasTyKind, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeSuperVisitable, TypeVisitable, + TypeVisitor, fast_reject, inherent::{AdtDef, GenericArgs as _, IntoKind, SliceLike, Term as _, Ty as _}, }; use smallvec::SmallVec; @@ -105,7 +109,7 @@ use span::{AstIdNode, Edition, FileId}; use stdx::{format_to, impl_from, never, variance::PhantomCovariantLifetime}; use syntax::{ AstNode, AstPtr, SmolStr, SyntaxNode, SyntaxNodePtr, TextRange, ToSmolStr, - ast::{self, HasName, HasVisibility as _}, + ast::{self, HasName as _, HasVisibility as _}, format_smolstr, }; use triomphe::{Arc, ThinArc}; @@ -146,7 +150,7 @@ pub use { visibility::Visibility, // FIXME: This is here since some queries take it as input that are used // outside of hir. - {ModuleDefId, TraitId}, + {GenericParamId, ModuleDefId, TraitId}, }, hir_expand::{ EditionedFileId, ExpandResult, HirFileId, MacroCallId, MacroKind, @@ -440,7 +444,10 @@ impl ModuleDef { Adt::Union(it) => it.id.into(), }, ModuleDef::Trait(it) => it.id.into(), - ModuleDef::Function(it) => it.id.into(), + ModuleDef::Function(it) => match it.id { + AnyFunctionId::FunctionId(it) => it.into(), + AnyFunctionId::BuiltinDeriveImplMethod { .. } => return Vec::new(), + }, ModuleDef::TypeAlias(it) => it.id.into(), ModuleDef::Module(it) => it.id.into(), ModuleDef::Const(it) => it.id.into(), @@ -504,7 +511,7 @@ impl ModuleDef { pub fn attrs(&self, db: &dyn HirDatabase) -> Option<AttrsWithOwner> { Some(match self { ModuleDef::Module(it) => it.attrs(db), - ModuleDef::Function(it) => it.attrs(db), + ModuleDef::Function(it) => HasAttrs::attrs(*it, db), ModuleDef::Adt(it) => it.attrs(db), ModuleDef::Variant(it) => it.attrs(db), ModuleDef::Const(it) => it.attrs(db), @@ -772,8 +779,11 @@ impl Module { for impl_def in self.impl_defs(db) { GenericDef::Impl(impl_def).diagnostics(db, acc); - let loc = impl_def.id.lookup(db); - let (impl_signature, source_map) = db.impl_signature_with_source_map(impl_def.id); + let AnyImplId::ImplId(impl_id) = impl_def.id else { + continue; + }; + let loc = impl_id.lookup(db); + let (impl_signature, source_map) = db.impl_signature_with_source_map(impl_id); expr_store_diagnostics(db, acc, &source_map); let file_id = loc.id.file_id; @@ -789,26 +799,25 @@ impl Module { let ast_id_map = db.ast_id_map(file_id); - for diag in impl_def.id.impl_items_with_diagnostics(db).1.iter() { + for diag in impl_id.impl_items_with_diagnostics(db).1.iter() { emit_def_diagnostic(db, acc, diag, edition, loc.container.krate(db)); } - if impl_signature.target_trait.is_none() - && !is_inherent_impl_coherent(db, def_map, impl_def.id) - { + let trait_impl = impl_signature.target_trait.is_some(); + if !trait_impl && !is_inherent_impl_coherent(db, def_map, impl_id) { acc.push(IncoherentImpl { impl_: ast_id_map.get(loc.id.value), file_id }.into()) } - if !impl_def.check_orphan_rules(db) { + if trait_impl && !impl_def.check_orphan_rules(db) { acc.push(TraitImplOrphan { impl_: ast_id_map.get(loc.id.value), file_id }.into()) } - let trait_ = impl_def.trait_(db); + let trait_ = trait_impl.then(|| impl_def.trait_(db)).flatten(); let mut trait_is_unsafe = trait_.is_some_and(|t| t.is_unsafe(db)); let impl_is_negative = impl_def.is_negative(db); let impl_is_unsafe = impl_def.is_unsafe(db); - let trait_is_unresolved = trait_.is_none() && impl_signature.target_trait.is_some(); + let trait_is_unresolved = trait_.is_none() && trait_impl; if trait_is_unresolved { // Ignore trait safety errors when the trait is unresolved, as otherwise we'll treat it as safe, // which may not be correct. @@ -822,7 +831,7 @@ impl Module { if drop_trait != trait_.into() { return None; } - let parent = impl_def.id.into(); + let parent = impl_id.into(); let (lifetimes_attrs, type_and_consts_attrs) = AttrFlags::query_generic_params(db, parent); let res = lifetimes_attrs.values().any(|it| it.contains(AttrFlags::MAY_DANGLE)) @@ -851,7 +860,7 @@ impl Module { AssocItemId::ConstId(id) => !db.const_signature(id).has_body(), AssocItemId::TypeAliasId(it) => db.type_alias_signature(it).ty.is_none(), }); - impl_assoc_items_scratch.extend(impl_def.id.impl_items(db).items.iter().cloned()); + impl_assoc_items_scratch.extend(impl_id.impl_items(db).items.iter().cloned()); let redundant = impl_assoc_items_scratch .iter() @@ -883,11 +892,11 @@ impl Module { .collect(); if !missing.is_empty() { - let self_ty = db.impl_self_ty(impl_def.id).instantiate_identity(); + let self_ty = db.impl_self_ty(impl_id).instantiate_identity(); let self_ty = structurally_normalize_ty( &infcx, self_ty, - db.trait_environment(impl_def.id.into()), + db.trait_environment(impl_id.into()), ); let self_ty_is_guaranteed_unsized = matches!( self_ty.kind(), @@ -896,7 +905,13 @@ impl Module { if self_ty_is_guaranteed_unsized { missing.retain(|(_, assoc_item)| { let assoc_item = match *assoc_item { - AssocItem::Function(it) => it.id.into(), + AssocItem::Function(it) => match it.id { + AnyFunctionId::FunctionId(id) => id.into(), + AnyFunctionId::BuiltinDeriveImplMethod { .. } => { + never!("should not have an `AnyFunctionId::BuiltinDeriveImplMethod` here"); + return false; + }, + }, AssocItem::Const(it) => it.id.into(), AssocItem::TypeAlias(it) => it.id.into(), }; @@ -905,6 +920,48 @@ impl Module { } } + // HACK: When specialization is enabled in the current crate, and there exists + // *any* blanket impl that provides a default implementation for the missing item, + // suppress the missing associated item diagnostic. + // This can lead to false negatives when the impl in question does not actually + // specialize that blanket impl, but determining the exact specialization + // relationship here would be significantly more expensive. + if !missing.is_empty() { + let krate = self.krate(db).id; + let def_map = crate_def_map(db, krate); + if def_map.is_unstable_feature_enabled(&sym::specialization) + || def_map.is_unstable_feature_enabled(&sym::min_specialization) + { + missing.retain(|(assoc_name, assoc_item)| { + let AssocItem::Function(_) = assoc_item else { + return true; + }; + + for &impl_ in TraitImpls::for_crate(db, krate).blanket_impls(trait_.id) + { + if impl_ == impl_id { + continue; + } + + for (name, item) in &impl_.impl_items(db).items { + let AssocItemId::FunctionId(fn_) = item else { + continue; + }; + if name != assoc_name { + continue; + } + + if db.function_signature(*fn_).is_default() { + return false; + } + } + } + + true + }); + } + } + if !missing.is_empty() { acc.push( TraitImplMissingAssocItems { @@ -918,20 +975,15 @@ impl Module { impl_assoc_items_scratch.clear(); } + push_ty_diagnostics(db, acc, db.impl_self_ty_with_diagnostics(impl_id).1, &source_map); push_ty_diagnostics( db, acc, - db.impl_self_ty_with_diagnostics(impl_def.id).1, - &source_map, - ); - push_ty_diagnostics( - db, - acc, - db.impl_trait_with_diagnostics(impl_def.id).and_then(|it| it.1), + db.impl_trait_with_diagnostics(impl_id).and_then(|it| it.1), &source_map, ); - for &(_, item) in impl_def.id.impl_items(db).items.iter() { + for &(_, item) in impl_id.impl_items(db).items.iter() { AssocItem::from(item).diagnostics(db, acc, style_lints); } } @@ -955,7 +1007,8 @@ impl Module { pub fn impl_defs(self, db: &dyn HirDatabase) -> Vec<Impl> { let def_map = self.id.def_map(db); - def_map[self.id].scope.impls().map(Impl::from).collect() + let scope = &def_map[self.id].scope; + scope.impls().map(Impl::from).chain(scope.builtin_derive_impls().map(Impl::from)).collect() } /// Finds a path that can be used to refer to the given item from within @@ -968,7 +1021,7 @@ impl Module { ) -> Option<ModPath> { hir_def::find_path::find_path( db, - item.into().into(), + item.into().try_into().ok()?, self.into(), PrefixKind::Plain, false, @@ -985,7 +1038,14 @@ impl Module { prefix_kind: PrefixKind, cfg: FindPathConfig, ) -> Option<ModPath> { - hir_def::find_path::find_path(db, item.into().into(), self.into(), prefix_kind, true, cfg) + hir_def::find_path::find_path( + db, + item.into().try_into().ok()?, + self.into(), + prefix_kind, + true, + cfg, + ) } #[inline] @@ -1863,9 +1923,9 @@ impl VariantDef { pub fn name(&self, db: &dyn HirDatabase) -> Name { match self { - VariantDef::Struct(s) => s.name(db), - VariantDef::Union(u) => u.name(db), - VariantDef::Variant(e) => e.name(db), + VariantDef::Struct(s) => (*s).name(db), + VariantDef::Union(u) => (*u).name(db), + VariantDef::Variant(e) => (*e).name(db), } } } @@ -1909,24 +1969,33 @@ impl DefWithBody { } } - fn id(&self) -> DefWithBodyId { - match self { - DefWithBody::Function(it) => it.id.into(), + fn id(&self) -> Option<DefWithBodyId> { + Some(match self { + DefWithBody::Function(it) => match it.id { + AnyFunctionId::FunctionId(id) => id.into(), + AnyFunctionId::BuiltinDeriveImplMethod { .. } => return None, + }, DefWithBody::Static(it) => it.id.into(), DefWithBody::Const(it) => it.id.into(), DefWithBody::Variant(it) => it.into(), - } + }) } /// A textual representation of the HIR of this def's body for debugging purposes. pub fn debug_hir(self, db: &dyn HirDatabase) -> String { - let body = db.body(self.id()); - body.pretty_print(db, self.id(), Edition::CURRENT) + let Some(id) = self.id() else { + return String::new(); + }; + let body = db.body(id); + body.pretty_print(db, id, Edition::CURRENT) } /// A textual representation of the MIR of this def's body for debugging purposes. pub fn debug_mir(self, db: &dyn HirDatabase) -> String { - let body = db.mir_body(self.id()); + let Some(id) = self.id() else { + return String::new(); + }; + let body = db.mir_body(id); match body { Ok(body) => body.pretty_print(db, self.module(db).krate(db).to_display_target(db)), Err(e) => format!("error:\n{e:?}"), @@ -1939,11 +2008,17 @@ impl DefWithBody { acc: &mut Vec<AnyDiagnostic<'db>>, style_lints: bool, ) { + let Ok(id) = self.try_into() else { + return; + }; let krate = self.module(db).id.krate(db); - let (body, source_map) = db.body_with_source_map(self.into()); + let (body, source_map) = db.body_with_source_map(id); let sig_source_map = match self { - DefWithBody::Function(id) => db.function_signature_with_source_map(id.into()).1, + DefWithBody::Function(id) => match id.id { + AnyFunctionId::FunctionId(id) => db.function_signature_with_source_map(id).1, + AnyFunctionId::BuiltinDeriveImplMethod { .. } => return, + }, DefWithBody::Static(id) => db.static_signature_with_source_map(id.into()).1, DefWithBody::Const(id) => db.const_signature_with_source_map(id.into()).1, DefWithBody::Variant(variant) => { @@ -1958,11 +2033,11 @@ impl DefWithBody { expr_store_diagnostics(db, acc, &source_map); - let infer = InferenceResult::for_body(db, self.into()); + let infer = InferenceResult::for_body(db, id); for d in infer.diagnostics() { acc.extend(AnyDiagnostic::inference_diagnostic( db, - self.into(), + id, d, &source_map, &sig_source_map, @@ -1989,14 +2064,14 @@ impl DefWithBody { acc.push( TypeMismatch { expr_or_pat, - expected: Type::new(db, DefWithBodyId::from(self), mismatch.expected.as_ref()), - actual: Type::new(db, DefWithBodyId::from(self), mismatch.actual.as_ref()), + expected: Type::new(db, id, mismatch.expected.as_ref()), + actual: Type::new(db, id, mismatch.actual.as_ref()), } .into(), ); } - let missing_unsafe = hir_ty::diagnostics::missing_unsafe(db, self.into()); + let missing_unsafe = hir_ty::diagnostics::missing_unsafe(db, id); for (node, reason) in missing_unsafe.unsafe_exprs { match source_map.expr_or_pat_syntax(node) { Ok(node) => acc.push( @@ -2031,7 +2106,7 @@ impl DefWithBody { } } - if let Ok(borrowck_results) = db.borrowck(self.into()) { + if let Ok(borrowck_results) = db.borrowck(id) { for borrowck_result in borrowck_results.iter() { let mir_body = &borrowck_result.mir_body; for moof in &borrowck_result.moved_out_of_ref { @@ -2088,7 +2163,7 @@ impl DefWithBody { { need_mut = &mir::MutabilityReason::Not; } - let local = Local { parent: self.into(), binding_id }; + let local = Local { parent: id, binding_id }; let is_mut = body[binding_id].mode == BindingAnnotation::Mutable; match (need_mut, is_mut) { @@ -2144,17 +2219,11 @@ impl DefWithBody { } } - for diagnostic in BodyValidationDiagnostic::collect(db, self.into(), style_lints) { + for diagnostic in BodyValidationDiagnostic::collect(db, id, style_lints) { acc.extend(AnyDiagnostic::body_validation_diagnostic(db, diagnostic, &source_map)); } - let def: ModuleDef = match self { - DefWithBody::Function(it) => it.into(), - DefWithBody::Static(it) => it.into(), - DefWithBody::Const(it) => it.into(), - DefWithBody::Variant(it) => it.into(), - }; - for diag in hir_ty::diagnostics::incorrect_case(db, def.into()) { + for diag in hir_ty::diagnostics::incorrect_case(db, id.into()) { acc.push(diag.into()) } } @@ -2192,45 +2261,181 @@ fn expr_store_diagnostics<'db>( .macro_calls() .for_each(|(_ast_id, call_id)| macro_call_diagnostics(db, call_id, acc)); } + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +enum AnyFunctionId { + FunctionId(FunctionId), + BuiltinDeriveImplMethod { method: BuiltinDeriveImplMethod, impl_: BuiltinDeriveImplId }, +} + +#[derive(Clone, Copy, PartialEq, Eq, Hash)] pub struct Function { - pub(crate) id: FunctionId, + pub(crate) id: AnyFunctionId, +} + +impl fmt::Debug for Function { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Debug::fmt(&self.id, f) + } } impl Function { pub fn module(self, db: &dyn HirDatabase) -> Module { - self.id.module(db).into() + match self.id { + AnyFunctionId::FunctionId(id) => id.module(db).into(), + AnyFunctionId::BuiltinDeriveImplMethod { impl_, .. } => impl_.module(db).into(), + } } pub fn name(self, db: &dyn HirDatabase) -> Name { - db.function_signature(self.id).name.clone() + match self.id { + AnyFunctionId::FunctionId(id) => db.function_signature(id).name.clone(), + AnyFunctionId::BuiltinDeriveImplMethod { method, .. } => { + Name::new_symbol_root(method.name()) + } + } } pub fn ty(self, db: &dyn HirDatabase) -> Type<'_> { - Type::from_value_def(db, self.id) + match self.id { + AnyFunctionId::FunctionId(id) => Type::from_value_def(db, id), + 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 }; + let Some(trait_method) = method.trait_method(db, impl_) else { + return Type { env, ty: Ty::new_error(interner, ErrorGuaranteed) }; + }; + Function::from(trait_method).ty(db) + } + } } pub fn fn_ptr_type(self, db: &dyn HirDatabase) -> Type<'_> { - let resolver = self.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(self.id.into()).instantiate_identity(); - let ty = Ty::new_fn_ptr(interner, callable_sig); - Type::new_with_resolver_inner(db, &resolver, ty) + 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(); + let ty = Ty::new_fn_ptr(interner, callable_sig); + Type::new_with_resolver_inner(db, &resolver, 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 }; + let Some(trait_method) = method.trait_method(db, impl_) else { + return Type { env, ty: Ty::new_error(interner, ErrorGuaranteed) }; + }; + // 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(), + ); + let impl_trait_ref = + hir_ty::builtin_derive::impl_trait(interner, impl_).instantiate_identity(); + 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); + Type { env, ty: impl_method_fn_ptr } + } + } + } + + fn fn_sig<'db>(self, db: &'db dyn HirDatabase) -> (ParamEnvAndCrate<'db>, PolyFnSig<'db>) { + let fn_ptr = self.fn_ptr_type(db); + let TyKind::FnPtr(sig_tys, hdr) = fn_ptr.ty.kind() else { + unreachable!(); + }; + (fn_ptr.env, 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 resolver = self.id.resolver(db); - // FIXME: This shouldn't be `instantiate_identity()`, we shouldn't leak `TyKind::Param`s. - let ty = db - .callable_item_signature(self.id.into()) - .instantiate_identity() - .skip_binder() - .output(); - Type::new_with_resolver_inner(db, &resolver, ty) + let (env, sig) = self.fn_sig(db); + Type { env, ty: sig.skip_binder().output() } } // FIXME: Find better API to also handle const generics @@ -2239,30 +2444,41 @@ impl Function { db: &'db dyn HirDatabase, generics: impl Iterator<Item = Type<'db>>, ) -> Type<'db> { - let resolver = self.id.resolver(db); + let ret_type = self.ret_type(db); let interner = DbInterner::new_no_crate(db); - let args = generic_args_from_tys(interner, self.id.into(), generics.map(|ty| ty.ty)); + let args = self.adapt_generic_args(interner, generics); + ret_type.derived(EarlyBinder::bind(ret_type.ty).instantiate(interner, args)) + } - let interner = DbInterner::new_no_crate(db); - let ty = db - .callable_item_signature(self.id.into()) - .instantiate(interner, args) - .skip_binder() - .output(); - Type::new_with_resolver_inner(db, &resolver, ty) + 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)), + ) + } + } } pub fn async_ret_type<'db>(self, db: &'db dyn HirDatabase) -> Option<Type<'db>> { + let AnyFunctionId::FunctionId(id) = self.id else { + return None; + }; if !self.is_async(db) { return None; } - let resolver = self.id.resolver(db); + 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(self.id.into()) - .instantiate_identity() - .skip_binder() - .output(); + 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() @@ -2274,31 +2490,47 @@ impl Function { } pub fn has_self_param(self, db: &dyn HirDatabase) -> bool { - db.function_signature(self.id).has_self_param() + match self.id { + AnyFunctionId::FunctionId(id) => db.function_signature(id).has_self_param(), + AnyFunctionId::BuiltinDeriveImplMethod { method, .. } => match method { + BuiltinDeriveImplMethod::clone + | BuiltinDeriveImplMethod::fmt + | BuiltinDeriveImplMethod::hash + | BuiltinDeriveImplMethod::cmp + | BuiltinDeriveImplMethod::partial_cmp + | BuiltinDeriveImplMethod::eq => true, + BuiltinDeriveImplMethod::default => false, + }, + } } pub fn self_param(self, db: &dyn HirDatabase) -> Option<SelfParam> { - self.has_self_param(db).then_some(SelfParam { func: self.id }) + self.has_self_param(db).then_some(SelfParam { func: self }) } pub fn assoc_fn_params(self, db: &dyn HirDatabase) -> Vec<Param<'_>> { - let environment = param_env_from_has_crate(db, self.id); - // FIXME: This shouldn't be `instantiate_identity()`, we shouldn't leak `TyKind::Param`s. - let callable_sig = - db.callable_item_signature(self.id.into()).instantiate_identity().skip_binder(); - callable_sig + let (env, sig) = self.fn_sig(db); + let func = match self.id { + AnyFunctionId::FunctionId(id) => Callee::Def(CallableDefId::FunctionId(id)), + AnyFunctionId::BuiltinDeriveImplMethod { method, impl_ } => { + Callee::BuiltinDeriveImplMethod { method, impl_ } + } + }; + sig.skip_binder() .inputs() .iter() .enumerate() - .map(|(idx, &ty)| { - let ty = Type { env: environment, ty }; - Param { func: Callee::Def(CallableDefId::FunctionId(self.id)), ty, idx } - }) + .map(|(idx, &ty)| Param { func: func.clone(), ty: Type { env, ty }, idx }) .collect() } pub fn num_params(self, db: &dyn HirDatabase) -> usize { - db.function_signature(self.id).params.len() + match self.id { + AnyFunctionId::FunctionId(id) => db.function_signature(id).params.len(), + AnyFunctionId::BuiltinDeriveImplMethod { .. } => { + self.fn_sig(db).1.skip_binder().inputs().len() + } + } } pub fn method_params(self, db: &dyn HirDatabase) -> Option<Vec<Param<'_>>> { @@ -2307,21 +2539,11 @@ impl Function { } pub fn params_without_self(self, db: &dyn HirDatabase) -> Vec<Param<'_>> { - let environment = param_env_from_has_crate(db, self.id); - // FIXME: This shouldn't be `instantiate_identity()`, we shouldn't leak `TyKind::Param`s. - let callable_sig = - db.callable_item_signature(self.id.into()).instantiate_identity().skip_binder(); - let skip = if db.function_signature(self.id).has_self_param() { 1 } else { 0 }; - callable_sig - .inputs() - .iter() - .enumerate() - .skip(skip) - .map(|(idx, &ty)| { - let ty = Type { env: environment, ty }; - Param { func: Callee::Def(CallableDefId::FunctionId(self.id)), ty, idx } - }) - .collect() + let mut params = self.assoc_fn_params(db); + if self.has_self_param(db) { + params.remove(0); + } + params } // FIXME: Find better API to also handle const generics @@ -2330,40 +2552,50 @@ impl Function { db: &'db dyn HirDatabase, generics: impl Iterator<Item = Type<'db>>, ) -> Vec<Param<'db>> { - let environment = param_env_from_has_crate(db, self.id); let interner = DbInterner::new_no_crate(db); - let args = generic_args_from_tys(interner, self.id.into(), generics.map(|ty| ty.ty)); - let callable_sig = - db.callable_item_signature(self.id.into()).instantiate(interner, args).skip_binder(); - let skip = if db.function_signature(self.id).has_self_param() { 1 } else { 0 }; - callable_sig - .inputs() - .iter() - .enumerate() - .skip(skip) - .map(|(idx, &ty)| { - let ty = Type { env: environment, ty }; - Param { func: Callee::Def(CallableDefId::FunctionId(self.id)), ty, idx } + 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), + }, }) .collect() } pub fn is_const(self, db: &dyn HirDatabase) -> bool { - db.function_signature(self.id).is_const() + match self.id { + AnyFunctionId::FunctionId(id) => db.function_signature(id).is_const(), + AnyFunctionId::BuiltinDeriveImplMethod { .. } => false, + } } pub fn is_async(self, db: &dyn HirDatabase) -> bool { - db.function_signature(self.id).is_async() + match self.id { + AnyFunctionId::FunctionId(id) => db.function_signature(id).is_async(), + AnyFunctionId::BuiltinDeriveImplMethod { .. } => false, + } } pub fn is_varargs(self, db: &dyn HirDatabase) -> bool { - db.function_signature(self.id).is_varargs() + match self.id { + AnyFunctionId::FunctionId(id) => db.function_signature(id).is_varargs(), + AnyFunctionId::BuiltinDeriveImplMethod { .. } => false, + } } pub fn extern_block(self, db: &dyn HirDatabase) -> Option<ExternBlock> { - match self.id.lookup(db).container { - ItemContainerId::ExternBlockId(id) => Some(ExternBlock { id }), - _ => None, + match self.id { + AnyFunctionId::FunctionId(id) => match id.lookup(db).container { + ItemContainerId::ExternBlockId(id) => Some(ExternBlock { id }), + _ => None, + }, + AnyFunctionId::BuiltinDeriveImplMethod { .. } => None, } } @@ -2396,33 +2628,46 @@ impl Function { /// Does this function have `#[test]` attribute? pub fn is_test(self, db: &dyn HirDatabase) -> bool { - self.attrs(db).is_test() + self.attrs(db).contains(AttrFlags::IS_TEST) } /// is this a `fn main` or a function with an `export_name` of `main`? pub fn is_main(self, db: &dyn HirDatabase) -> bool { - self.exported_main(db) - || self.module(db).is_crate_root(db) && db.function_signature(self.id).name == sym::main + match self.id { + AnyFunctionId::FunctionId(id) => { + self.exported_main(db) + || self.module(db).is_crate_root(db) + && db.function_signature(id).name == sym::main + } + AnyFunctionId::BuiltinDeriveImplMethod { .. } => false, + } + } + + fn attrs(self, db: &dyn HirDatabase) -> AttrFlags { + match self.id { + AnyFunctionId::FunctionId(id) => AttrFlags::query(db, id.into()), + AnyFunctionId::BuiltinDeriveImplMethod { .. } => AttrFlags::empty(), + } } /// Is this a function with an `export_name` of `main`? pub fn exported_main(self, db: &dyn HirDatabase) -> bool { - AttrFlags::query(db, self.id.into()).contains(AttrFlags::IS_EXPORT_NAME_MAIN) + self.attrs(db).contains(AttrFlags::IS_EXPORT_NAME_MAIN) } /// Does this function have the ignore attribute? pub fn is_ignore(self, db: &dyn HirDatabase) -> bool { - AttrFlags::query(db, self.id.into()).contains(AttrFlags::IS_IGNORE) + self.attrs(db).contains(AttrFlags::IS_IGNORE) } /// Does this function have `#[bench]` attribute? pub fn is_bench(self, db: &dyn HirDatabase) -> bool { - AttrFlags::query(db, self.id.into()).contains(AttrFlags::IS_BENCH) + self.attrs(db).contains(AttrFlags::IS_BENCH) } /// Is this function marked as unstable with `#[feature]` attribute? pub fn is_unstable(self, db: &dyn HirDatabase) -> bool { - AttrFlags::query(db, self.id.into()).contains(AttrFlags::IS_UNSTABLE) + self.attrs(db).contains(AttrFlags::IS_UNSTABLE) } pub fn is_unsafe_to_call( @@ -2431,9 +2676,17 @@ impl Function { caller: Option<Function>, call_edition: Edition, ) -> bool { + let AnyFunctionId::FunctionId(id) = self.id else { + return false; + }; let (target_features, target_feature_is_safe_in_target) = caller .map(|caller| { - let target_features = hir_ty::TargetFeatures::from_fn(db, caller.id); + let target_features = match caller.id { + AnyFunctionId::FunctionId(id) => hir_ty::TargetFeatures::from_fn(db, id), + AnyFunctionId::BuiltinDeriveImplMethod { .. } => { + hir_ty::TargetFeatures::default() + } + }; let target_feature_is_safe_in_target = match &caller.krate(db).id.workspace_data(db).target { Ok(target) => hir_ty::target_feature_is_safe_in_target(target), @@ -2447,7 +2700,7 @@ impl Function { matches!( hir_ty::is_fn_unsafe_to_call( db, - self.id, + id, &target_features, call_edition, target_feature_is_safe_in_target @@ -2460,12 +2713,18 @@ impl Function { /// /// This is false in the case of required (not provided) trait methods. pub fn has_body(self, db: &dyn HirDatabase) -> bool { - db.function_signature(self.id).has_body() + match self.id { + AnyFunctionId::FunctionId(id) => db.function_signature(id).has_body(), + AnyFunctionId::BuiltinDeriveImplMethod { .. } => true, + } } pub fn as_proc_macro(self, db: &dyn HirDatabase) -> Option<Macro> { - let def_map = crate_def_map(db, HasModule::krate(&self.id, db)); - def_map.fn_as_proc_macro(self.id).map(|id| Macro { id: id.into() }) + let AnyFunctionId::FunctionId(id) = self.id else { + return None; + }; + let def_map = crate_def_map(db, HasModule::krate(&id, db)); + def_map.fn_as_proc_macro(id).map(|id| Macro { id: id.into() }) } pub fn eval( @@ -2473,13 +2732,18 @@ impl Function { db: &dyn HirDatabase, span_formatter: impl Fn(FileId, TextRange) -> String, ) -> Result<String, ConstEvalError> { + let AnyFunctionId::FunctionId(id) = self.id else { + return Err(ConstEvalError::MirEvalError(MirEvalError::NotSupported( + "evaluation of builtin derive impl methods is not supported".to_owned(), + ))); + }; let interner = DbInterner::new_no_crate(db); let body = db.monomorphized_mir_body( - self.id.into(), + id.into(), GenericArgs::empty(interner).store(), ParamEnvAndCrate { - param_env: db.trait_environment(self.id.into()), - krate: self.id.module(db).krate(db), + param_env: db.trait_environment(id.into()), + krate: id.module(db).krate(db), } .store(), )?; @@ -2596,36 +2860,47 @@ impl<'db> Param<'db> { #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct SelfParam { - func: FunctionId, + func: Function, } impl SelfParam { pub fn access(self, db: &dyn HirDatabase) -> Access { - let func_data = db.function_signature(self.func); - func_data - .params - .first() - .map(|¶m| match &func_data.store[param] { - TypeRef::Reference(ref_) => match ref_.mutability { - hir_def::type_ref::Mutability::Shared => Access::Shared, - hir_def::type_ref::Mutability::Mut => Access::Exclusive, - }, - _ => Access::Owned, - }) - .unwrap_or(Access::Owned) + match self.func.id { + AnyFunctionId::FunctionId(id) => { + let func_data = db.function_signature(id); + func_data + .params + .first() + .map(|¶m| match &func_data.store[param] { + TypeRef::Reference(ref_) => match ref_.mutability { + hir_def::type_ref::Mutability::Shared => Access::Shared, + hir_def::type_ref::Mutability::Mut => Access::Exclusive, + }, + _ => Access::Owned, + }) + .unwrap_or(Access::Owned) + } + AnyFunctionId::BuiltinDeriveImplMethod { method, .. } => match method { + BuiltinDeriveImplMethod::clone + | BuiltinDeriveImplMethod::fmt + | BuiltinDeriveImplMethod::hash + | BuiltinDeriveImplMethod::cmp + | BuiltinDeriveImplMethod::partial_cmp + | BuiltinDeriveImplMethod::eq => Access::Shared, + BuiltinDeriveImplMethod::default => { + unreachable!("this function does not have a self param") + } + }, + } } pub fn parent_fn(&self) -> Function { - Function::from(self.func) + self.func } pub fn ty<'db>(&self, db: &'db dyn HirDatabase) -> Type<'db> { - // FIXME: This shouldn't be `instantiate_identity()`, we shouldn't leak `TyKind::Param`s. - let callable_sig = - db.callable_item_signature(self.func.into()).instantiate_identity().skip_binder(); - let environment = param_env_from_has_crate(db, self.func); - let ty = rustc_type_ir::inherent::SliceLike::as_slice(&callable_sig.inputs())[0]; - Type { env: environment, ty } + 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 @@ -2635,18 +2910,18 @@ impl SelfParam { generics: impl Iterator<Item = Type<'db>>, ) -> Type<'db> { let interner = DbInterner::new_no_crate(db); - let args = generic_args_from_tys(interner, self.func.into(), generics.map(|ty| ty.ty)); - let callable_sig = - db.callable_item_signature(self.func.into()).instantiate(interner, args).skip_binder(); - let environment = param_env_from_has_crate(db, self.func); - let ty = rustc_type_ir::inherent::SliceLike::as_slice(&callable_sig.inputs())[0]; - Type { env: environment, ty } + 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) } } } impl HasVisibility for Function { fn visibility(&self, db: &dyn HirDatabase) -> Visibility { - db.assoc_visibility(self.id.into()) + match self.id { + AnyFunctionId::FunctionId(id) => db.assoc_visibility(id.into()), + AnyFunctionId::BuiltinDeriveImplMethod { .. } => Visibility::Public, + } } } @@ -2870,7 +3145,7 @@ impl Trait { pub fn function(self, db: &dyn HirDatabase, name: impl PartialEq<Name>) -> Option<Function> { self.id.trait_items(db).items.iter().find(|(n, _)| name == *n).and_then(|&(_, it)| match it { - AssocItemId::FunctionId(id) => Some(Function { id }), + AssocItemId::FunctionId(id) => Some(id.into()), _ => None, }) } @@ -3151,15 +3426,15 @@ impl Macro { ) } - pub fn is_builtin_derive(&self, db: &dyn HirDatabase) -> bool { - match self.id { - MacroId::Macro2Id(it) => { - matches!(it.lookup(db).expander, MacroExpander::BuiltInDerive(_)) - } - MacroId::MacroRulesId(it) => { - matches!(it.lookup(db).expander, MacroExpander::BuiltInDerive(_)) - } - MacroId::ProcMacroId(_) => false, + pub fn builtin_derive_kind(&self, db: &dyn HirDatabase) -> Option<BuiltinDeriveMacroKind> { + let expander = match self.id { + MacroId::Macro2Id(it) => it.lookup(db).expander, + MacroId::MacroRulesId(it) => it.lookup(db).expander, + MacroId::ProcMacroId(_) => return None, + }; + match expander { + MacroExpander::BuiltInDerive(kind) => Some(BuiltinDeriveMacroKind(kind)), + _ => None, } } @@ -3195,8 +3470,55 @@ impl Macro { pub fn is_derive(&self, db: &dyn HirDatabase) -> bool { matches!(self.kind(db), MacroKind::Derive | MacroKind::DeriveBuiltIn) } + + pub fn preferred_brace_style(&self, db: &dyn HirDatabase) -> Option<MacroBraces> { + let attrs = self.attrs(db); + MacroBraces::extract(attrs.attrs) + } +} + +// Feature: Macro Brace Style Attribute +// Crate authors can declare the preferred brace style for their macro. This will affect how completion +// insert calls to it. +// +// This is only supported on function-like macros. +// +// To do that, insert the `#[rust_analyzer::macro_style(style)]` attribute on the macro (for proc macros, +// insert it for the macro's function). `style` can be one of: +// +// - `braces` for `{...}` style. +// - `brackets` for `[...]` style. +// - `parentheses` for `(...)` style. +// +// Malformed attributes will be ignored without warnings. +// +// Note that users have no way to override this attribute, so be careful and only include things +// users definitely do not want to be completed! + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub enum MacroBraces { + Braces, + Brackets, + Parentheses, +} + +impl MacroBraces { + fn extract(attrs: AttrFlags) -> Option<Self> { + if attrs.contains(AttrFlags::MACRO_STYLE_BRACES) { + Some(Self::Braces) + } else if attrs.contains(AttrFlags::MACRO_STYLE_BRACKETS) { + Some(Self::Brackets) + } else if attrs.contains(AttrFlags::MACRO_STYLE_PARENTHESES) { + Some(Self::Parentheses) + } else { + None + } + } } +#[derive(Clone, Copy, PartialEq, Eq, Hash)] +pub struct BuiltinDeriveMacroKind(BuiltinDeriveExpander); + impl HasVisibility for Macro { fn visibility(&self, db: &dyn HirDatabase) -> Visibility { match self.id { @@ -3275,7 +3597,10 @@ pub trait AsExternAssocItem { impl AsExternAssocItem for Function { fn as_extern_assoc_item(self, db: &dyn HirDatabase) -> Option<ExternAssocItem> { - as_extern_assoc_item(db, ExternAssocItem::Function, self.id) + let AnyFunctionId::FunctionId(id) = self.id else { + return None; + }; + as_extern_assoc_item(db, ExternAssocItem::Function, id) } } @@ -3303,7 +3628,7 @@ pub enum AssocItem { impl From<method_resolution::CandidateId> for AssocItem { fn from(value: method_resolution::CandidateId) -> Self { match value { - method_resolution::CandidateId::FunctionId(id) => AssocItem::Function(Function { id }), + method_resolution::CandidateId::FunctionId(id) => AssocItem::Function(id.into()), method_resolution::CandidateId::ConstId(id) => AssocItem::Const(Const { id }), } } @@ -3321,7 +3646,10 @@ pub trait AsAssocItem { impl AsAssocItem for Function { fn as_assoc_item(self, db: &dyn HirDatabase) -> Option<AssocItem> { - as_assoc_item(db, AssocItem::Function, self.id) + match self.id { + AnyFunctionId::FunctionId(id) => as_assoc_item(db, AssocItem::Function, id), + AnyFunctionId::BuiltinDeriveImplMethod { .. } => Some(AssocItem::Function(self)), + } } } @@ -3450,7 +3778,14 @@ impl AssocItem { pub fn container(self, db: &dyn HirDatabase) -> AssocItemContainer { let container = match self { - AssocItem::Function(it) => it.id.lookup(db).container, + AssocItem::Function(it) => match it.id { + AnyFunctionId::FunctionId(id) => id.lookup(db).container, + AnyFunctionId::BuiltinDeriveImplMethod { impl_, .. } => { + return AssocItemContainer::Impl(Impl { + id: AnyImplId::BuiltinDeriveImplId(impl_), + }); + } + }, AssocItem::Const(it) => it.id.lookup(db).container, AssocItem::TypeAlias(it) => it.id.lookup(db).container, }; @@ -3587,9 +3922,13 @@ impl_from!( impl GenericDef { pub fn params(self, db: &dyn HirDatabase) -> Vec<GenericParam> { - let generics = db.generic_params(self.into()); + let Ok(id) = self.try_into() else { + // Let's pretend builtin derive impls don't have generic parameters. + return Vec::new(); + }; + let generics = db.generic_params(id); let ty_params = generics.iter_type_or_consts().map(|(local_id, _)| { - let toc = TypeOrConstParam { id: TypeOrConstParamId { parent: self.into(), local_id } }; + let toc = TypeOrConstParam { id: TypeOrConstParamId { parent: id, local_id } }; match toc.split(db) { Either::Left(it) => GenericParam::ConstParam(it), Either::Right(it) => GenericParam::TypeParam(it), @@ -3603,39 +3942,51 @@ impl GenericDef { } pub fn lifetime_params(self, db: &dyn HirDatabase) -> Vec<LifetimeParam> { - let generics = db.generic_params(self.into()); + let Ok(id) = self.try_into() else { + // Let's pretend builtin derive impls don't have generic parameters. + return Vec::new(); + }; + let generics = db.generic_params(id); generics .iter_lt() - .map(|(local_id, _)| LifetimeParam { - id: LifetimeParamId { parent: self.into(), local_id }, - }) + .map(|(local_id, _)| LifetimeParam { id: LifetimeParamId { parent: id, local_id } }) .collect() } pub fn type_or_const_params(self, db: &dyn HirDatabase) -> Vec<TypeOrConstParam> { - let generics = db.generic_params(self.into()); + let Ok(id) = self.try_into() else { + // Let's pretend builtin derive impls don't have generic parameters. + return Vec::new(); + }; + let generics = db.generic_params(id); generics .iter_type_or_consts() .map(|(local_id, _)| TypeOrConstParam { - id: TypeOrConstParamId { parent: self.into(), local_id }, + id: TypeOrConstParamId { parent: id, local_id }, }) .collect() } - fn id(self) -> GenericDefId { - match self { - GenericDef::Function(it) => it.id.into(), + fn id(self) -> Option<GenericDefId> { + Some(match self { + GenericDef::Function(it) => match it.id { + AnyFunctionId::FunctionId(it) => it.into(), + AnyFunctionId::BuiltinDeriveImplMethod { .. } => return None, + }, GenericDef::Adt(it) => it.into(), GenericDef::Trait(it) => it.id.into(), GenericDef::TypeAlias(it) => it.id.into(), - GenericDef::Impl(it) => it.id.into(), + GenericDef::Impl(it) => match it.id { + AnyImplId::ImplId(it) => it.into(), + AnyImplId::BuiltinDeriveImplId(_) => return None, + }, GenericDef::Const(it) => it.id.into(), GenericDef::Static(it) => it.id.into(), - } + }) } pub fn diagnostics<'db>(self, db: &'db dyn HirDatabase, acc: &mut Vec<AnyDiagnostic<'db>>) { - let def = self.id(); + let Some(def) = self.id() else { return }; let generics = db.generic_params(def); @@ -3708,6 +4059,17 @@ impl<'db> GenericSubstitution<'db> { Self { def, subst, env } } + fn new_from_fn( + def: Function, + subst: GenericArgs<'db>, + env: ParamEnvAndCrate<'db>, + ) -> Option<Self> { + match def.id { + AnyFunctionId::FunctionId(def) => Some(Self::new(def.into(), subst, env)), + AnyFunctionId::BuiltinDeriveImplMethod { .. } => None, + } + } + pub fn types(&self, db: &'db dyn HirDatabase) -> Vec<(Symbol, Type<'db>)> { let container = match self.def { GenericDefId::ConstId(id) => Some(id.lookup(db).container), @@ -3820,7 +4182,9 @@ impl Local { pub fn as_self_param(self, db: &dyn HirDatabase) -> Option<SelfParam> { match self.parent { - DefWithBodyId::FunctionId(func) if self.is_self(db) => Some(SelfParam { func }), + DefWithBodyId::FunctionId(func) if self.is_self(db) => { + Some(SelfParam { func: func.into() }) + } _ => None, } } @@ -4308,7 +4672,7 @@ impl TypeOrConstParam { #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct Impl { - pub(crate) id: ImplId, + pub(crate) id: AnyImplId, } impl Impl { @@ -4320,6 +4684,7 @@ impl Impl { fn extend_with_def_map(db: &dyn HirDatabase, def_map: &DefMap, result: &mut Vec<Impl>) { for (_, module) in def_map.modules() { result.extend(module.scope.impls().map(Impl::from)); + result.extend(module.scope.builtin_derive_impls().map(Impl::from)); for unnamed_const in module.scope.unnamed_consts() { for (_, block_def_map) in db.body(unnamed_const.into()).blocks(db) { @@ -4331,7 +4696,7 @@ impl Impl { } pub fn all_in_module(db: &dyn HirDatabase, module: Module) -> Vec<Impl> { - module.id.def_map(db)[module.id].scope.impls().map(Into::into).collect() + module.impl_defs(db) } /// **Note:** This is an **approximation** that strives to give the *human-perceived notion* of an "impl for type", @@ -4347,20 +4712,19 @@ impl Impl { else { return Vec::new(); }; - let mut extend_with_impls = - |impls: &[ImplId]| result.extend(impls.iter().copied().map(Impl::from)); - method_resolution::with_incoherent_inherent_impls( - db, - env.krate, - &simplified_ty, - &mut extend_with_impls, - ); + 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)) + }); if let Some(module) = method_resolution::simplified_type_module(db, &simplified_ty) { InherentImpls::for_each_crate_and_block( db, module.krate(db), module.block(db), - &mut |impls| extend_with_impls(impls.for_self_ty(&simplified_ty)), + &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)) .filter_map(|block| TraitImpls::for_block(db, block).as_deref()) @@ -4382,7 +4746,10 @@ impl Impl { let module = trait_.module(db).id; let mut all = Vec::new(); let mut handle_impls = |impls: &TraitImpls| { - impls.for_trait(trait_.id, |impls| all.extend(impls.iter().copied().map(Impl::from))); + impls.for_trait(trait_.id, |impls| match impls { + Either::Left(impls) => all.extend(impls.iter().copied().map(Impl::from)), + Either::Right(impls) => all.extend(impls.iter().copied().map(Impl::from)), + }); }; for krate in module.krate(db).transitive_rev_deps(db) { handle_impls(TraitImpls::for_crate(db, krate)); @@ -4396,75 +4763,118 @@ impl Impl { } pub fn trait_(self, db: &dyn HirDatabase) -> Option<Trait> { - let trait_ref = db.impl_trait(self.id)?; - let id = trait_ref.skip_binder().def_id; - Some(Trait { id: id.0 }) + match self.id { + AnyImplId::ImplId(id) => { + let trait_ref = db.impl_trait(id)?; + let id = trait_ref.skip_binder().def_id; + Some(Trait { id: id.0 }) + } + AnyImplId::BuiltinDeriveImplId(id) => { + let loc = id.loc(db); + let lang_items = hir_def::lang_item::lang_items(db, loc.adt.module(db).krate(db)); + loc.trait_.get_id(lang_items).map(Trait::from) + } + } } pub fn trait_ref(self, db: &dyn HirDatabase) -> Option<TraitRef<'_>> { - let trait_ref = db.impl_trait(self.id)?.instantiate_identity(); - let resolver = self.id.resolver(db); - Some(TraitRef::new_with_resolver(db, &resolver, trait_ref)) + match self.id { + AnyImplId::ImplId(id) => { + let trait_ref = db.impl_trait(id)?.instantiate_identity(); + let resolver = id.resolver(db); + Some(TraitRef::new_with_resolver(db, &resolver, 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(); + Some(TraitRef { env, trait_ref }) + } + } } pub fn self_ty(self, db: &dyn HirDatabase) -> Type<'_> { - let resolver = self.id.resolver(db); - // FIXME: This shouldn't be `instantiate_identity()`, we shouldn't leak `TyKind::Param`s. - let ty = db.impl_self_ty(self.id).instantiate_identity(); - Type::new_with_resolver_inner(db, &resolver, ty) + 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(); + Type::new_with_resolver_inner(db, &resolver, 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() + .self_ty(); + Type { env, ty } + } + } } pub fn items(self, db: &dyn HirDatabase) -> Vec<AssocItem> { - self.id.impl_items(db).items.iter().map(|&(_, it)| it.into()).collect() + match self.id { + AnyImplId::ImplId(id) => { + id.impl_items(db).items.iter().map(|&(_, it)| it.into()).collect() + } + AnyImplId::BuiltinDeriveImplId(impl_) => impl_ + .loc(db) + .trait_ + .all_methods() + .iter() + .map(|&method| { + AssocItem::Function(Function { + id: AnyFunctionId::BuiltinDeriveImplMethod { method, impl_ }, + }) + }) + .collect(), + } } pub fn is_negative(self, db: &dyn HirDatabase) -> bool { - db.impl_signature(self.id).flags.contains(ImplFlags::NEGATIVE) + match self.id { + AnyImplId::ImplId(id) => db.impl_signature(id).flags.contains(ImplFlags::NEGATIVE), + AnyImplId::BuiltinDeriveImplId(_) => false, + } } pub fn is_unsafe(self, db: &dyn HirDatabase) -> bool { - db.impl_signature(self.id).flags.contains(ImplFlags::UNSAFE) + match self.id { + AnyImplId::ImplId(id) => db.impl_signature(id).flags.contains(ImplFlags::UNSAFE), + AnyImplId::BuiltinDeriveImplId(_) => false, + } } pub fn module(self, db: &dyn HirDatabase) -> Module { - self.id.lookup(db).container.into() - } - - pub fn as_builtin_derive_path(self, db: &dyn HirDatabase) -> Option<InMacroFile<ast::Path>> { - let src = self.source(db)?; - - let macro_file = src.file_id.macro_file()?; - let loc = macro_file.lookup(db); - let (derive_attr, derive_index) = match loc.kind { - MacroCallKind::Derive { ast_id, derive_attr_index, derive_index, .. } => { - let module_id = self.id.lookup(db).container; - ( - module_id.def_map(db)[module_id] - .scope - .derive_macro_invoc(ast_id, derive_attr_index)?, - derive_index, - ) - } - _ => return None, - }; - let path = db - .parse_macro_expansion(derive_attr) - .value - .0 - .syntax_node() - .children() - .nth(derive_index as usize) - .and_then(<ast::Attr as AstNode>::cast) - .and_then(|it| it.path())?; - Some(InMacroFile { file_id: derive_attr, value: path }) + match self.id { + AnyImplId::ImplId(id) => id.module(db).into(), + AnyImplId::BuiltinDeriveImplId(id) => id.module(db).into(), + } } pub fn check_orphan_rules(self, db: &dyn HirDatabase) -> bool { - check_orphan_rules(db, self.id) + match self.id { + AnyImplId::ImplId(id) => check_orphan_rules(db, id), + AnyImplId::BuiltinDeriveImplId(_) => true, + } } fn all_macro_calls(&self, db: &dyn HirDatabase) -> Box<[(AstId<ast::Item>, MacroCallId)]> { - self.id.impl_items(db).macro_calls.to_vec().into_boxed_slice() + match self.id { + AnyImplId::ImplId(id) => id.impl_items(db).macro_calls.to_vec().into_boxed_slice(), + AnyImplId::BuiltinDeriveImplId(_) => Box::default(), + } } } @@ -5540,7 +5950,7 @@ impl<'db> Type<'db> { else { unreachable!("`Mode::MethodCall` can only return functions"); }; - let id = Function { id }; + let id = Function { id: AnyFunctionId::FunctionId(id) }; match candidate.kind { method_resolution::PickKind::InherentImplPick(_) | method_resolution::PickKind::ObjectPick(..) @@ -5564,7 +5974,7 @@ impl<'db> Type<'db> { else { unreachable!("`Mode::MethodCall` can only return functions"); }; - let id = Function { id }; + let id = Function { id: AnyFunctionId::FunctionId(id) }; match candidate.candidate.kind { method_resolution::CandidateKind::InherentImplCandidate { .. @@ -5919,6 +6329,7 @@ enum Callee<'db> { CoroutineClosure(InternedCoroutineId, GenericArgs<'db>), FnPtr, FnImpl(traits::FnTrait), + BuiltinDeriveImplMethod { method: BuiltinDeriveImplMethod, impl_: BuiltinDeriveImplId }, } pub enum CallableKind<'db> { @@ -5934,6 +6345,9 @@ impl<'db> Callable<'db> { pub fn kind(&self) -> CallableKind<'db> { match self.callee { Callee::Def(CallableDefId::FunctionId(it)) => CallableKind::Function(it.into()), + Callee::BuiltinDeriveImplMethod { method, impl_ } => CallableKind::Function(Function { + id: AnyFunctionId::BuiltinDeriveImplMethod { method, impl_ }, + }), Callee::Def(CallableDefId::StructId(it)) => CallableKind::TupleStruct(it.into()), Callee::Def(CallableDefId::EnumVariantId(it)) => { CallableKind::TupleEnumVariant(it.into()) @@ -5948,12 +6362,22 @@ impl<'db> Callable<'db> { Callee::FnImpl(fn_) => CallableKind::FnImpl(fn_.into()), } } + + fn as_function(&self) -> Option<Function> { + match self.callee { + Callee::Def(CallableDefId::FunctionId(it)) => Some(it.into()), + Callee::BuiltinDeriveImplMethod { method, impl_ } => { + Some(Function { id: AnyFunctionId::BuiltinDeriveImplMethod { method, impl_ } }) + } + _ => None, + } + } + pub fn receiver_param(&self, db: &'db dyn HirDatabase) -> Option<(SelfParam, Type<'db>)> { - let func = match self.callee { - Callee::Def(CallableDefId::FunctionId(it)) if self.is_bound_method => it, - _ => return None, - }; - let func = Function { id: func }; + if !self.is_bound_method { + return None; + } + let func = self.as_function()?; Some(( func.self_param(db)?, self.ty.derived(self.sig.skip_binder().inputs_and_output.inputs()[0]), @@ -6350,7 +6774,12 @@ impl HasContainer for Module { impl HasContainer for Function { fn container(&self, db: &dyn HirDatabase) -> ItemContainer { - container_id_to_hir(self.id.lookup(db).container) + match self.id { + AnyFunctionId::FunctionId(id) => container_id_to_hir(id.lookup(db).container), + AnyFunctionId::BuiltinDeriveImplMethod { impl_, .. } => { + ItemContainer::Impl(Impl { id: AnyImplId::BuiltinDeriveImplId(impl_) }) + } + } } } @@ -6402,11 +6831,79 @@ impl HasContainer for ExternBlock { } } +pub trait HasName { + fn name(&self, db: &dyn HirDatabase) -> Option<Name>; +} + +macro_rules! impl_has_name { + ( $( $ty:ident ),* $(,)? ) => { + $( + impl HasName for $ty { + fn name(&self, db: &dyn HirDatabase) -> Option<Name> { + (*self).name(db).into() + } + } + )* + }; +} + +impl_has_name!( + ModuleDef, + Module, + Field, + Struct, + Union, + Enum, + Variant, + Adt, + VariantDef, + DefWithBody, + Function, + ExternCrateDecl, + Const, + Static, + Trait, + TypeAlias, + Macro, + ExternAssocItem, + AssocItem, + Local, + DeriveHelper, + ToolModule, + Label, + GenericParam, + TypeParam, + LifetimeParam, + ConstParam, + TypeOrConstParam, + InlineAsmOperand, +); + +macro_rules! impl_has_name_no_db { + ( $( $ty:ident ),* $(,)? ) => { + $( + impl HasName for $ty { + fn name(&self, _db: &dyn HirDatabase) -> Option<Name> { + (*self).name().into() + } + } + )* + }; +} + +impl_has_name_no_db!(TupleField, StaticLifetime, BuiltinType, BuiltinAttr); + +impl HasName for Param<'_> { + fn name(&self, db: &dyn HirDatabase) -> Option<Name> { + self.name(db) + } +} + fn container_id_to_hir(c: ItemContainerId) -> ItemContainer { match c { ItemContainerId::ExternBlockId(id) => ItemContainer::ExternBlock(ExternBlock { id }), ItemContainerId::ModuleId(id) => ItemContainer::Module(Module { id }), - ItemContainerId::ImplId(id) => ItemContainer::Impl(Impl { id }), + ItemContainerId::ImplId(id) => ItemContainer::Impl(id.into()), ItemContainerId::TraitId(id) => ItemContainer::Trait(Trait { id }), } } |