Allow IDE layer to "see" fake builtin derive impls

It sees them as regular impls; the details are abstracted. It's beautiful for the IDE layer, and less beautiful for `hir`, so this is a big change.

Some small differences still exist:

 - We show builtin derives impl (to the IDE layer) as if they have had no generic parameters. It is possible to show the parameters, but that means also having to handle fake impls in `TypeParam` etc., and the benefit is questionable.
 - Getting the fn *def* type of a method of a builtin derive impl is not supported, as there is no real `FunctionId`, therefore no `CallableDefId`. The trait method is returned instead. Note: getting the fn *ptr* type of the method is supported well.
 - Builtin derive impls and their methods do not fully support `HasSource`, because, well, they have no source (at least, not in the form of `ast::Impl` and `ast::Fn`). To support them, we use the derive's `TextRange` where possible, and the trait method's source when not.

 It's important to note that the def map still records the `MacroCallId`. I have doubts over this, as this means it's very easy to create the queries we don't want to create, but it does make things more convenient. In particular, a nicety of this setup is that even "Expand macro recursively" works (it creates the macro input/output query, but given that they will only be created when the user invokes the command, that does not seem to be a problem).
This commit is contained in:
Chayim Refael Friedman 2025-12-03 19:09:42 +02:00
parent 5fbff1d7fb
commit bd934c08cf
34 changed files with 2451 additions and 1535 deletions

View File

@ -3,40 +3,102 @@
//! To save time and memory, builtin derives are not really expanded. Instead, we record them
//! and create their impls based on lowered data, see crates/hir-ty/src/builtin_derive.rs.
use hir_expand::builtin::BuiltinDeriveExpander;
use hir_expand::{InFile, builtin::BuiltinDeriveExpander, name::Name};
use intern::{Symbol, sym};
use tt::TextRange;
use crate::{
AdtId, BuiltinDeriveImplId, BuiltinDeriveImplLoc, FunctionId, HasModule, db::DefDatabase,
};
macro_rules! declare_enum {
( $( $trait:ident ),* $(,)? ) => {
( $( $trait:ident => [ $( $method:ident ),* ] ),* $(,)? ) => {
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum BuiltinDeriveImplTrait {
$( $trait, )*
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[allow(non_camel_case_types)]
pub enum BuiltinDeriveImplMethod {
$( $( $method, )* )*
}
impl BuiltinDeriveImplTrait {
#[inline]
pub fn name(self) -> Symbol {
match self {
$( Self::$trait => sym::$trait, )*
}
}
#[inline]
pub fn get_id(self, lang_items: &crate::lang_item::LangItems) -> Option<crate::TraitId> {
match self {
$( Self::$trait => lang_items.$trait, )*
}
}
#[inline]
pub fn get_method(self, method_name: &Symbol) -> Option<BuiltinDeriveImplMethod> {
match self {
$(
Self::$trait => {
match method_name {
$( _ if *method_name == sym::$method => Some(BuiltinDeriveImplMethod::$method), )*
_ => None,
}
}
)*
}
}
#[inline]
pub fn all_methods(self) -> &'static [BuiltinDeriveImplMethod] {
match self {
$( Self::$trait => &[ $(BuiltinDeriveImplMethod::$method),* ], )*
}
}
}
impl BuiltinDeriveImplMethod {
#[inline]
pub fn name(self) -> Symbol {
match self {
$( $( BuiltinDeriveImplMethod::$method => sym::$method, )* )*
}
}
}
};
}
declare_enum!(
Copy,
Clone,
Default,
Debug,
Hash,
Ord,
PartialOrd,
Eq,
PartialEq,
CoerceUnsized,
DispatchFromDyn,
Copy => [],
Clone => [clone],
Default => [default],
Debug => [fmt],
Hash => [hash],
Ord => [cmp],
PartialOrd => [partial_cmp],
Eq => [],
PartialEq => [eq],
CoerceUnsized => [],
DispatchFromDyn => [],
);
impl BuiltinDeriveImplMethod {
pub fn trait_method(
self,
db: &dyn DefDatabase,
impl_: BuiltinDeriveImplId,
) -> Option<FunctionId> {
let loc = impl_.loc(db);
let lang_items = crate::lang_item::lang_items(db, loc.krate(db));
let trait_ = impl_.loc(db).trait_.get_id(lang_items)?;
trait_.trait_items(db).method_by_name(&Name::new_symbol_root(self.name()))
}
}
pub(crate) fn with_derive_traits(
derive: BuiltinDeriveExpander,
mut f: impl FnMut(BuiltinDeriveImplTrait),
@ -59,3 +121,29 @@ pub(crate) fn with_derive_traits(
};
f(trait_);
}
impl BuiltinDeriveImplLoc {
pub fn source(&self, db: &dyn DefDatabase) -> InFile<TextRange> {
let (adt_ast_id, module) = match self.adt {
AdtId::StructId(adt) => {
let adt_loc = adt.loc(db);
(adt_loc.id.upcast(), adt_loc.container)
}
AdtId::UnionId(adt) => {
let adt_loc = adt.loc(db);
(adt_loc.id.upcast(), adt_loc.container)
}
AdtId::EnumId(adt) => {
let adt_loc = adt.loc(db);
(adt_loc.id.upcast(), adt_loc.container)
}
};
let derive_range = self.derive_attr_id.find_derive_range(
db,
module.krate(db),
adt_ast_id,
self.derive_index,
);
adt_ast_id.with_value(derive_range)
}
}

View File

@ -9,7 +9,7 @@ use indexmap::map::Entry;
use itertools::Itertools;
use la_arena::Idx;
use rustc_hash::{FxHashMap, FxHashSet};
use smallvec::{SmallVec, smallvec};
use smallvec::SmallVec;
use span::Edition;
use stdx::format_to;
use syntax::ast;
@ -531,12 +531,13 @@ impl ItemScope {
adt: AstId<ast::Adt>,
attr_id: AttrId,
attr_call_id: MacroCallId,
len: usize,
mut derive_call_ids: SmallVec<[Option<MacroCallId>; 4]>,
) {
derive_call_ids.shrink_to_fit();
self.derive_macros.entry(adt).or_default().push(DeriveMacroInvocation {
attr_id,
attr_call_id,
derive_call_ids: smallvec![None; len],
derive_call_ids,
});
}

View File

@ -64,6 +64,7 @@ use base_db::{Crate, impl_intern_key};
use hir_expand::{
AstId, ExpandResult, ExpandTo, HirFileId, InFile, MacroCallId, MacroCallKind, MacroCallStyles,
MacroDefId, MacroDefKind,
attrs::AttrId,
builtin::{BuiltinAttrExpander, BuiltinDeriveExpander, BuiltinFnLikeExpander, EagerExpander},
db::ExpandDatabase,
eager::expand_eager_macro_input,
@ -337,6 +338,8 @@ impl ImplId {
pub struct BuiltinDeriveImplLoc {
pub adt: AdtId,
pub trait_: BuiltinDeriveImplTrait,
pub derive_attr_id: AttrId,
pub derive_index: u32,
}
#[salsa::interned(debug, no_lifetime)]
@ -675,6 +678,18 @@ impl_from!(
for ModuleDefId
);
impl From<DefWithBodyId> for ModuleDefId {
#[inline]
fn from(value: DefWithBodyId) -> Self {
match value {
DefWithBodyId::FunctionId(id) => id.into(),
DefWithBodyId::StaticId(id) => id.into(),
DefWithBodyId::ConstId(id) => id.into(),
DefWithBodyId::VariantId(id) => id.into(),
}
}
}
/// A constant, which might appears as a const item, an anonymous const block in expressions
/// or patterns, or as a constant in types with const generics.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, salsa_macros::Supertype)]

View File

@ -122,16 +122,16 @@ struct Foo {
v4: bool // No comma here
}
#[attr1]
#[derive(Bar)]
#[attr2] struct S;
#[attr1]
#[my_cool_derive()] struct Foo {
v1: i32, #[attr3]v2: fn(#[attr4]param2: u32), v3: Foo< {
456
}
>,
}
#[attr1]
#[derive(Bar)]
#[attr2] struct S;"#]],
}"#]],
);
}

View File

@ -224,6 +224,8 @@ struct DeferredBuiltinDerive {
module_id: ModuleId,
depth: usize,
container: ItemContainerId,
derive_attr_id: AttrId,
derive_index: u32,
}
/// Walks the tree of module recursively
@ -1479,10 +1481,10 @@ impl<'db> DefCollector<'db> {
let ast_id = ast_id.with_value(ast_adt_id);
let mut derive_call_ids = SmallVec::new();
match attr.parse_path_comma_token_tree(self.db) {
Some(derive_macros) => {
let call_id = call_id();
let mut len = 0;
for (idx, (path, call_site, _)) in derive_macros.enumerate() {
let ast_id = AstIdWithPath::new(
file_id,
@ -1505,14 +1507,7 @@ impl<'db> DefCollector<'db> {
);
if let Ok((macro_id, def_id, call_id)) = id {
self.def_map.modules[directive.module_id]
.scope
.set_derive_macro_invoc(
ast_id.ast_id,
call_id,
*attr_id,
idx,
);
derive_call_ids.push(Some(call_id));
// Record its helper attributes.
if def_id.krate != self.def_map.krate {
let def_map = crate_def_map(self.db, def_id.krate);
@ -1543,11 +1538,14 @@ impl<'db> DefCollector<'db> {
module_id: directive.module_id,
container: directive.container,
depth: directive.depth,
derive_attr_id: *attr_id,
derive_index: idx as u32,
});
} else {
push_resolved(&mut resolved, directive, call_id);
}
} else {
derive_call_ids.push(None);
self.unresolved_macros.push(MacroDirective {
module_id: directive.module_id,
depth: directive.depth + 1,
@ -1561,7 +1559,6 @@ impl<'db> DefCollector<'db> {
container: directive.container,
});
}
len = idx;
}
// We treat the #[derive] macro as an attribute call, but we do not resolve it for nameres collection.
@ -1570,7 +1567,12 @@ impl<'db> DefCollector<'db> {
// Check the comment in [`builtin_attr_macro`].
self.def_map.modules[directive.module_id]
.scope
.init_derive_attribute(ast_id, *attr_id, call_id, len + 1);
.init_derive_attribute(
ast_id,
*attr_id,
call_id,
derive_call_ids,
);
}
None => {
let diag = DefDiagnostic::malformed_derive(
@ -1863,8 +1865,15 @@ impl ModCollector<'_, '_> {
let module = &mut def_map.modules[module_id];
for deferred_derive in deferred_derives {
crate::builtin_derive::with_derive_traits(deferred_derive.derive, |trait_| {
let impl_id =
BuiltinDeriveImplId::new(db, BuiltinDeriveImplLoc { adt: id, trait_ });
let impl_id = BuiltinDeriveImplId::new(
db,
BuiltinDeriveImplLoc {
adt: id,
trait_,
derive_attr_id: deferred_derive.derive_attr_id,
derive_index: deferred_derive.derive_index,
},
);
module.scope.define_builtin_derive_impl(impl_id);
});
}

View File

@ -20,16 +20,18 @@ use crate::{
GenericPredicates,
db::HirDatabase,
next_solver::{
Clause, Clauses, DbInterner, EarlyBinder, GenericArgs, StoredEarlyBinder, StoredTy,
TraitRef, Ty, TyKind, fold::fold_tys, generics::Generics,
Clause, Clauses, DbInterner, EarlyBinder, GenericArgs, ParamEnv, StoredEarlyBinder,
StoredTy, TraitRef, Ty, TyKind, fold::fold_tys, generics::Generics,
},
};
fn fake_type_param(adt: AdtId) -> TypeParamId {
fn coerce_pointee_new_type_param(trait_id: TraitId) -> TypeParamId {
// HACK: Fake the param.
// We cannot use a dummy param here, because it can leak into the IDE layer and that'll cause panics
// when e.g. trying to display it. So we use an existing param.
TypeParamId::from_unchecked(TypeOrConstParamId {
parent: adt.into(),
local_id: la_arena::Idx::from_raw(la_arena::RawIdx::from_u32(u32::MAX)),
parent: trait_id.into(),
local_id: la_arena::Idx::from_raw(la_arena::RawIdx::from_u32(1)),
})
}
@ -48,13 +50,35 @@ pub(crate) fn generics_of<'db>(interner: DbInterner<'db>, id: BuiltinDeriveImplI
| BuiltinDeriveImplTrait::PartialEq => interner.generics_of(loc.adt.into()),
BuiltinDeriveImplTrait::CoerceUnsized | BuiltinDeriveImplTrait::DispatchFromDyn => {
let mut generics = interner.generics_of(loc.adt.into());
generics.push_param(fake_type_param(loc.adt).into());
let trait_id = loc
.trait_
.get_id(interner.lang_items())
.expect("we don't pass the impl to the solver if we can't resolve the trait");
generics.push_param(coerce_pointee_new_type_param(trait_id).into());
generics
}
}
}
pub(crate) fn impl_trait<'db>(
pub fn generic_params_count(db: &dyn HirDatabase, id: BuiltinDeriveImplId) -> usize {
let loc = id.loc(db);
let adt_params = GenericParams::new(db, loc.adt.into());
let extra_params_count = match loc.trait_ {
BuiltinDeriveImplTrait::Copy
| BuiltinDeriveImplTrait::Clone
| BuiltinDeriveImplTrait::Default
| BuiltinDeriveImplTrait::Debug
| BuiltinDeriveImplTrait::Hash
| BuiltinDeriveImplTrait::Ord
| BuiltinDeriveImplTrait::PartialOrd
| BuiltinDeriveImplTrait::Eq
| BuiltinDeriveImplTrait::PartialEq => 0,
BuiltinDeriveImplTrait::CoerceUnsized | BuiltinDeriveImplTrait::DispatchFromDyn => 1,
};
adt_params.len() + extra_params_count
}
pub fn impl_trait<'db>(
interner: DbInterner<'db>,
id: BuiltinDeriveImplId,
) -> EarlyBinder<'db, TraitRef<'db>> {
@ -93,7 +117,7 @@ pub(crate) fn impl_trait<'db>(
let args = GenericArgs::identity_for_item(interner, loc.adt.into());
let self_ty = Ty::new_adt(interner, loc.adt, args);
let Some((pointee_param_idx, _, new_param_ty)) =
coerce_pointee_params(interner, loc, &generic_params)
coerce_pointee_params(interner, loc, &generic_params, trait_id)
else {
// Malformed derive.
return EarlyBinder::bind(TraitRef::new(
@ -110,14 +134,15 @@ pub(crate) fn impl_trait<'db>(
}
#[salsa::tracked(returns(ref), unsafe(non_update_types))]
pub(crate) fn builtin_derive_predicates<'db>(
db: &'db dyn HirDatabase,
impl_: BuiltinDeriveImplId,
) -> GenericPredicates {
pub fn predicates<'db>(db: &'db dyn HirDatabase, impl_: BuiltinDeriveImplId) -> GenericPredicates {
let loc = impl_.loc(db);
let generic_params = GenericParams::new(db, loc.adt.into());
let interner = DbInterner::new_with(db, loc.module(db).krate(db));
let adt_predicates = GenericPredicates::query(db, loc.adt.into());
let trait_id = loc
.trait_
.get_id(interner.lang_items())
.expect("we don't pass the impl to the solver if we can't resolve the trait");
match loc.trait_ {
BuiltinDeriveImplTrait::Copy
| BuiltinDeriveImplTrait::Clone
@ -127,7 +152,7 @@ pub(crate) fn builtin_derive_predicates<'db>(
| BuiltinDeriveImplTrait::PartialOrd
| BuiltinDeriveImplTrait::Eq
| BuiltinDeriveImplTrait::PartialEq => {
simple_trait_predicates(interner, loc, &generic_params, adt_predicates)
simple_trait_predicates(interner, loc, &generic_params, adt_predicates, trait_id)
}
BuiltinDeriveImplTrait::Default => {
if matches!(loc.adt, AdtId::EnumId(_)) {
@ -137,12 +162,12 @@ pub(crate) fn builtin_derive_predicates<'db>(
.store(),
))
} else {
simple_trait_predicates(interner, loc, &generic_params, adt_predicates)
simple_trait_predicates(interner, loc, &generic_params, adt_predicates, trait_id)
}
}
BuiltinDeriveImplTrait::CoerceUnsized | BuiltinDeriveImplTrait::DispatchFromDyn => {
let Some((pointee_param_idx, pointee_param_id, new_param_ty)) =
coerce_pointee_params(interner, loc, &generic_params)
coerce_pointee_params(interner, loc, &generic_params, trait_id)
else {
// Malformed derive.
return GenericPredicates::from_explicit_own_predicates(StoredEarlyBinder::bind(
@ -181,6 +206,12 @@ pub(crate) fn builtin_derive_predicates<'db>(
}
}
/// Not cached in a query, currently used in `hir` only. If you need this in `hir-ty` consider introducing a query.
pub fn param_env<'db>(interner: DbInterner<'db>, id: BuiltinDeriveImplId) -> ParamEnv<'db> {
let predicates = predicates(interner.db, id);
crate::lower::param_env_from_predicates(interner, predicates)
}
struct MentionsPointee {
pointee_param_idx: u32,
}
@ -216,11 +247,8 @@ fn simple_trait_predicates<'db>(
loc: &BuiltinDeriveImplLoc,
generic_params: &GenericParams,
adt_predicates: &GenericPredicates,
trait_id: TraitId,
) -> GenericPredicates {
let trait_id = loc
.trait_
.get_id(interner.lang_items())
.expect("we don't pass the impl to the solver if we can't resolve the trait");
let extra_predicates = generic_params
.iter_type_or_consts()
.filter(|(_, data)| matches!(data, TypeOrConstParamData::TypeParamData(_)))
@ -309,6 +337,7 @@ fn coerce_pointee_params<'db>(
interner: DbInterner<'db>,
loc: &BuiltinDeriveImplLoc,
generic_params: &GenericParams,
trait_id: TraitId,
) -> Option<(u32, TypeParamId, Ty<'db>)> {
let pointee_param = {
if let Ok((pointee_param, _)) = generic_params
@ -339,7 +368,7 @@ fn coerce_pointee_params<'db>(
let pointee_param_idx =
pointee_param.into_raw().into_u32() + (generic_params.len_lifetimes() as u32);
let new_param_idx = generic_params.len() as u32;
let new_param_id = fake_type_param(loc.adt);
let new_param_id = coerce_pointee_new_type_param(trait_id);
let new_param_ty = Ty::new_param(interner, new_param_id, new_param_idx);
Some((pointee_param_idx, pointee_param_id, new_param_ty))
}
@ -352,11 +381,7 @@ mod tests {
use stdx::format_to;
use test_fixture::WithFixture;
use crate::{
builtin_derive::{builtin_derive_predicates, impl_trait},
next_solver::DbInterner,
test_db::TestDB,
};
use crate::{builtin_derive::impl_trait, next_solver::DbInterner, test_db::TestDB};
fn check_trait_refs(#[rust_analyzer::rust_fixture] ra_fixture: &str, expectation: Expect) {
let db = TestDB::with_files(ra_fixture);
@ -384,8 +409,7 @@ mod tests {
let mut predicates = String::new();
for (_, module) in def_map.modules() {
for derive in module.scope.builtin_derive_impls() {
let preds =
builtin_derive_predicates(&db, derive).all_predicates().skip_binder();
let preds = super::predicates(&db, derive).all_predicates().skip_binder();
format_to!(
predicates,
"{}\n\n",

View File

@ -1568,6 +1568,7 @@ const GOAL: u8 = {
}
#[test]
#[ignore = "builtin derive macros are currently not working with MIR eval"]
fn builtin_derive_macro() {
check_number(
r#"

View File

@ -2,10 +2,12 @@
//! type inference-related queries.
use base_db::{Crate, target::TargetLoadError};
use either::Either;
use hir_def::{
AdtId, CallableDefId, ConstId, ConstParamId, DefWithBodyId, EnumVariantId, FunctionId,
GenericDefId, ImplId, LifetimeParamId, LocalFieldId, StaticId, TraitId, TypeAliasId, VariantId,
db::DefDatabase, hir::ExprId, layout::TargetDataLayout,
AdtId, BuiltinDeriveImplId, CallableDefId, ConstId, ConstParamId, DefWithBodyId, EnumVariantId,
FunctionId, GenericDefId, ImplId, LifetimeParamId, LocalFieldId, StaticId, TraitId,
TypeAliasId, VariantId, builtin_derive::BuiltinDeriveImplMethod, db::DefDatabase, hir::ExprId,
layout::TargetDataLayout,
};
use la_arena::ArenaMap;
use salsa::plumbing::AsId;
@ -83,7 +85,7 @@ pub trait HirDatabase: DefDatabase + std::fmt::Debug {
env: ParamEnvAndCrate<'db>,
func: FunctionId,
fn_subst: GenericArgs<'db>,
) -> (FunctionId, GenericArgs<'db>);
) -> (Either<FunctionId, (BuiltinDeriveImplId, BuiltinDeriveImplMethod)>, GenericArgs<'db>);
// endregion:mir

View File

@ -7,7 +7,7 @@ use std::{
mem,
};
use base_db::Crate;
use base_db::{Crate, FxIndexMap};
use either::Either;
use hir_def::{
FindPathConfig, GenericDefId, HasModule, LocalFieldId, Lookup, ModuleDefId, ModuleId, TraitId,
@ -143,11 +143,11 @@ impl<'db> BoundsFormattingCtx<'db> {
}
impl<'db> HirFormatter<'_, 'db> {
fn start_location_link(&mut self, location: ModuleDefId) {
pub fn start_location_link(&mut self, location: ModuleDefId) {
self.fmt.start_location_link(location);
}
fn end_location_link(&mut self) {
pub fn end_location_link(&mut self) {
self.fmt.end_location_link();
}
@ -1978,6 +1978,49 @@ fn write_bounds_like_dyn_trait<'db>(
Ok(())
}
pub fn write_params_bounds<'db>(
f: &mut HirFormatter<'_, 'db>,
predicates: &[Clause<'db>],
) -> Result {
// Use an FxIndexMap to keep user's order, as far as possible.
let mut per_type = FxIndexMap::<_, Vec<_>>::default();
for &predicate in predicates {
let base_ty = match predicate.kind().skip_binder() {
ClauseKind::Trait(clause) => Either::Left(clause.self_ty()),
ClauseKind::RegionOutlives(clause) => Either::Right(clause.0),
ClauseKind::TypeOutlives(clause) => Either::Left(clause.0),
ClauseKind::Projection(clause) => Either::Left(clause.self_ty()),
ClauseKind::ConstArgHasType(..)
| ClauseKind::WellFormed(_)
| ClauseKind::ConstEvaluatable(_)
| ClauseKind::HostEffect(..)
| ClauseKind::UnstableFeature(_) => continue,
};
per_type.entry(base_ty).or_default().push(predicate);
}
for (base_ty, clauses) in per_type {
f.write_str(" ")?;
match base_ty {
Either::Left(it) => it.hir_fmt(f)?,
Either::Right(it) => it.hir_fmt(f)?,
}
f.write_str(": ")?;
// Rudimentary approximation: type params are `Sized` by default, everything else not.
// FIXME: This is not correct, really. But I'm not sure how we can from the ty representation
// to extract the default sizedness, and if it's possible at all.
let default_sized = match base_ty {
Either::Left(ty) if matches!(ty.kind(), TyKind::Param(_)) => {
SizedByDefault::Sized { anchor: f.krate() }
}
_ => SizedByDefault::NotSized,
};
write_bounds_like_dyn_trait(f, base_ty, &clauses, default_sized)?;
f.write_str(",\n")?;
}
Ok(())
}
impl<'db> HirDisplay<'db> for TraitRef<'db> {
fn hir_fmt(&self, f: &mut HirFormatter<'_, 'db>) -> Result {
let trait_ = self.def_id.0;

View File

@ -25,7 +25,7 @@ extern crate ra_ap_rustc_next_trait_solver as rustc_next_trait_solver;
extern crate self as hir_ty;
mod builtin_derive;
pub mod builtin_derive;
mod infer;
mod inhabitedness;
mod lower;

View File

@ -1855,6 +1855,20 @@ pub(crate) fn trait_environment_for_body_query(
db.trait_environment(def)
}
pub(crate) fn param_env_from_predicates<'db>(
interner: DbInterner<'db>,
predicates: &'db GenericPredicates,
) -> ParamEnv<'db> {
let clauses = rustc_type_ir::elaborate::elaborate(
interner,
predicates.all_predicates().iter_identity_copied(),
);
let clauses = Clauses::new_from_iter(interner, clauses);
// FIXME: We should normalize projections here, like rustc does.
ParamEnv { clauses }
}
pub(crate) fn trait_environment<'db>(db: &'db dyn HirDatabase, def: GenericDefId) -> ParamEnv<'db> {
return ParamEnv { clauses: trait_environment_query(db, def).as_ref() };
@ -1865,13 +1879,8 @@ pub(crate) fn trait_environment<'db>(db: &'db dyn HirDatabase, def: GenericDefId
) -> StoredClauses {
let module = def.module(db);
let interner = DbInterner::new_with(db, module.krate(db));
let predicates = GenericPredicates::query_all(db, def);
let clauses =
rustc_type_ir::elaborate::elaborate(interner, predicates.iter_identity_copied());
let clauses = Clauses::new_from_iter(interner, clauses);
// FIXME: We should normalize projections here, like rustc does.
clauses.store()
let predicates = GenericPredicates::query(db, def);
param_env_from_predicates(interner, predicates).clauses.store()
}
}

View File

@ -16,6 +16,7 @@ use hir_def::{
AssocItemId, BlockId, BuiltinDeriveImplId, ConstId, FunctionId, GenericParamId, HasModule,
ImplId, ItemContainerId, ModuleId, TraitId,
attrs::AttrFlags,
builtin_derive::BuiltinDeriveImplMethod,
expr_store::path::GenericArgs as HirGenericArgs,
hir::ExprId,
lang_item::LangItems,
@ -372,9 +373,13 @@ pub fn lookup_impl_const<'db>(
};
lookup_impl_assoc_item_for_trait_ref(infcx, trait_ref, env, name)
.and_then(
|assoc| if let (AssocItemId::ConstId(id), s) = assoc { Some((id, s)) } else { None },
)
.and_then(|assoc| {
if let (Either::Left(AssocItemId::ConstId(id)), s) = assoc {
Some((id, s))
} else {
None
}
})
.unwrap_or((const_id, subs))
}
@ -420,12 +425,12 @@ pub(crate) fn lookup_impl_method_query<'db>(
env: ParamEnvAndCrate<'db>,
func: FunctionId,
fn_subst: GenericArgs<'db>,
) -> (FunctionId, GenericArgs<'db>) {
) -> (Either<FunctionId, (BuiltinDeriveImplId, BuiltinDeriveImplMethod)>, GenericArgs<'db>) {
let interner = DbInterner::new_with(db, env.krate);
let infcx = interner.infer_ctxt().build(TypingMode::PostAnalysis);
let ItemContainerId::TraitId(trait_id) = func.loc(db).container else {
return (func, fn_subst);
return (Either::Left(func), fn_subst);
};
let trait_params = db.generic_params(trait_id.into()).len();
let trait_ref = TraitRef::new_from_args(
@ -435,16 +440,19 @@ pub(crate) fn lookup_impl_method_query<'db>(
);
let name = &db.function_signature(func).name;
let Some((impl_fn, impl_subst)) = lookup_impl_assoc_item_for_trait_ref(
&infcx,
trait_ref,
env.param_env,
name,
)
.and_then(|assoc| {
if let (AssocItemId::FunctionId(id), subst) = assoc { Some((id, subst)) } else { None }
}) else {
return (func, fn_subst);
let Some((impl_fn, impl_subst)) =
lookup_impl_assoc_item_for_trait_ref(&infcx, trait_ref, env.param_env, name).and_then(
|(assoc, impl_args)| {
let assoc = match assoc {
Either::Left(AssocItemId::FunctionId(id)) => Either::Left(id),
Either::Right(it) => Either::Right(it),
_ => return None,
};
Some((assoc, impl_args))
},
)
else {
return (Either::Left(func), fn_subst);
};
(
@ -461,11 +469,18 @@ fn lookup_impl_assoc_item_for_trait_ref<'db>(
trait_ref: TraitRef<'db>,
env: ParamEnv<'db>,
name: &Name,
) -> Option<(AssocItemId, GenericArgs<'db>)> {
) -> Option<(Either<AssocItemId, (BuiltinDeriveImplId, BuiltinDeriveImplMethod)>, GenericArgs<'db>)>
{
let (impl_id, impl_subst) = find_matching_impl(infcx, env, trait_ref)?;
let AnyImplId::ImplId(impl_id) = impl_id else {
// FIXME: Handle resolution to builtin derive.
return None;
let impl_id = match impl_id {
AnyImplId::ImplId(it) => it,
AnyImplId::BuiltinDeriveImplId(impl_) => {
return impl_
.loc(infcx.interner.db)
.trait_
.get_method(name.symbol())
.map(|method| (Either::Right((impl_, method)), impl_subst));
}
};
let item =
impl_id.impl_items(infcx.interner.db).items.iter().find_map(|(n, it)| match *it {
@ -473,7 +488,7 @@ fn lookup_impl_assoc_item_for_trait_ref<'db>(
AssocItemId::ConstId(c) => (n == name).then_some(AssocItemId::ConstId(c)),
AssocItemId::TypeAliasId(_) => None,
})?;
Some((item, impl_subst))
Some((Either::Left(item), impl_subst))
}
pub(crate) fn find_matching_impl<'db>(
@ -793,19 +808,29 @@ impl TraitImpls {
.unwrap_or_default()
}
pub fn for_trait(&self, trait_: TraitId, mut callback: impl FnMut(&[ImplId])) {
pub fn for_trait(
&self,
trait_: TraitId,
mut callback: impl FnMut(Either<&[ImplId], &[BuiltinDeriveImplId]>),
) {
if let Some(impls) = self.map.get(&trait_) {
callback(&impls.blanket_impls);
callback(Either::Left(&impls.blanket_impls));
for impls in impls.non_blanket_impls.values() {
callback(&impls.0);
callback(Either::Left(&impls.0));
callback(Either::Right(&impls.1));
}
}
}
pub fn for_self_ty(&self, self_ty: &SimplifiedType, mut callback: impl FnMut(&[ImplId])) {
pub fn for_self_ty(
&self,
self_ty: &SimplifiedType,
mut callback: impl FnMut(Either<&[ImplId], &[BuiltinDeriveImplId]>),
) {
for for_trait in self.map.values() {
if let Some(for_ty) = for_trait.non_blanket_impls.get(self_ty) {
callback(&for_ty.0);
callback(Either::Left(&for_ty.0));
callback(Either::Right(&for_ty.1));
}
}
}

View File

@ -77,12 +77,14 @@ macro_rules! from_bytes {
}).into())
};
}
use from_bytes;
macro_rules! not_supported {
($it: expr) => {
return Err(MirEvalError::NotSupported(format!($it)))
return Err($crate::mir::eval::MirEvalError::NotSupported(format!($it)))
};
}
use not_supported;
#[derive(Debug, Default, Clone, PartialEq, Eq, GenericTypeVisitable)]
pub struct VTableMap<'db> {
@ -2622,6 +2624,9 @@ impl<'db> Evaluator<'db> {
def,
generic_args,
);
let Either::Left(imp) = imp else {
not_supported!("evaluating builtin derive impls is not supported")
};
let mir_body = self
.db

View File

@ -16,29 +16,14 @@ use crate::{
mir::eval::{
Address, AdtId, Arc, Evaluator, FunctionId, GenericArgs, HasModule, HirDisplay,
InternedClosure, Interval, IntervalAndTy, IntervalOrOwned, ItemContainerId, Layout, Locals,
Lookup, MirEvalError, MirSpan, Mutability, Result, Ty, TyKind, pad16,
Lookup, MirEvalError, MirSpan, Mutability, Result, Ty, TyKind, from_bytes, not_supported,
pad16,
},
next_solver::Region,
};
mod simd;
macro_rules! from_bytes {
($ty:tt, $value:expr) => {
($ty::from_le_bytes(match ($value).try_into() {
Ok(it) => it,
#[allow(unreachable_patterns)]
Err(_) => return Err(MirEvalError::InternalError("mismatched size".into())),
}))
};
}
macro_rules! not_supported {
($it: expr) => {
return Err(MirEvalError::NotSupported(format!($it)))
};
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum EvalLangItem {
BeginPanic,

View File

@ -6,21 +6,6 @@ use crate::consteval::try_const_usize;
use super::*;
macro_rules! from_bytes {
($ty:tt, $value:expr) => {
($ty::from_le_bytes(match ($value).try_into() {
Ok(it) => it,
Err(_) => return Err(MirEvalError::InternalError("mismatched size".into())),
}))
};
}
macro_rules! not_supported {
($it: expr) => {
return Err(MirEvalError::NotSupported(format!($it)))
};
}
impl<'db> Evaluator<'db> {
fn detect_simd_ty(&self, ty: Ty<'db>) -> Result<'db, (usize, Ty<'db>)> {
match ty.kind() {

View File

@ -1,8 +1,8 @@
use rustc_type_ir::{solve::GoalSource, solve::inspect::GoalEvaluation};
use serde_derive::{Deserialize, Serialize};
use crate::next_solver::infer::InferCtxt;
use crate::next_solver::inspect::{InspectCandidate, InspectGoal};
use crate::next_solver::{AnyImplId, infer::InferCtxt};
use crate::next_solver::{DbInterner, Span};
#[derive(Debug, Clone, Serialize, Deserialize)]
@ -76,14 +76,31 @@ impl<'a, 'db> ProofTreeSerializer<'a, 'db> {
use rustc_type_ir::solve::inspect::ProbeKind;
match candidate.kind() {
ProbeKind::TraitCandidate { source, .. } => {
use hir_def::{Lookup, src::HasSource};
use rustc_type_ir::solve::CandidateSource;
let db = self.infcx.interner.db;
match source {
CandidateSource::Impl(impl_def_id) => {
use hir_def::{Lookup, src::HasSource};
let db = self.infcx.interner.db;
let impl_src = impl_def_id.0.lookup(db).source(db);
Some(impl_src.value.to_string())
}
CandidateSource::Impl(impl_def_id) => match impl_def_id {
AnyImplId::ImplId(impl_def_id) => {
let impl_src = impl_def_id.lookup(db).source(db);
Some(impl_src.value.to_string())
}
AnyImplId::BuiltinDeriveImplId(impl_id) => {
let impl_loc = impl_id.loc(db);
let adt_src = match impl_loc.adt {
hir_def::AdtId::StructId(adt) => {
adt.loc(db).source(db).value.to_string()
}
hir_def::AdtId::UnionId(adt) => {
adt.loc(db).source(db).value.to_string()
}
hir_def::AdtId::EnumId(adt) => {
adt.loc(db).source(db).value.to_string()
}
};
Some(format!("#[derive(${})]\n{}", impl_loc.trait_.name(), adt_src))
}
},
_ => None,
}
}

View File

@ -2370,7 +2370,7 @@ impl<'db> DbInterner<'db> {
fn predicates_of(db: &dyn HirDatabase, def_id: SolverDefId) -> &GenericPredicates {
if let SolverDefId::BuiltinDeriveImplId(impl_) = def_id {
crate::builtin_derive::builtin_derive_predicates(db, impl_)
crate::builtin_derive::predicates(db, impl_)
} else {
GenericPredicates::query(db, def_id.try_into().unwrap())
}

View File

@ -539,7 +539,7 @@ impl SomeStruct {
"AttrFlags::query_",
"AttrFlags::query_",
"AttrFlags::query_",
"impl_trait_with_diagnostics_shim",
"impl_trait_with_diagnostics_query",
"impl_signature_shim",
"impl_signature_with_source_map_shim",
"impl_self_ty_with_diagnostics_query",

View File

@ -35,6 +35,8 @@ pub enum AttrsOwner {
Field(FieldId),
LifetimeParam(LifetimeParamId),
TypeOrConstParam(TypeOrConstParamId),
/// Things that do not have attributes. Used for builtin derives.
Dummy,
}
impl AttrsOwner {
@ -123,7 +125,9 @@ impl AttrsWithOwner {
let owner = match self.owner {
AttrsOwner::AttrDef(it) => Either::Left(it),
AttrsOwner::Field(it) => Either::Right(it),
AttrsOwner::LifetimeParam(_) | AttrsOwner::TypeOrConstParam(_) => return &[],
AttrsOwner::LifetimeParam(_) | AttrsOwner::TypeOrConstParam(_) | AttrsOwner::Dummy => {
return &[];
}
};
self.attrs.doc_aliases(db, owner)
}
@ -133,7 +137,9 @@ impl AttrsWithOwner {
let owner = match self.owner {
AttrsOwner::AttrDef(it) => Either::Left(it),
AttrsOwner::Field(it) => Either::Right(it),
AttrsOwner::LifetimeParam(_) | AttrsOwner::TypeOrConstParam(_) => return None,
AttrsOwner::LifetimeParam(_) | AttrsOwner::TypeOrConstParam(_) | AttrsOwner::Dummy => {
return None;
}
};
self.attrs.cfgs(db, owner)
}
@ -143,7 +149,9 @@ impl AttrsWithOwner {
match self.owner {
AttrsOwner::AttrDef(it) => AttrFlags::docs(db, it).as_deref(),
AttrsOwner::Field(it) => AttrFlags::field_docs(db, it),
AttrsOwner::LifetimeParam(_) | AttrsOwner::TypeOrConstParam(_) => None,
AttrsOwner::LifetimeParam(_) | AttrsOwner::TypeOrConstParam(_) | AttrsOwner::Dummy => {
None
}
}
}
}
@ -156,6 +164,9 @@ pub trait HasAttrs: Sized {
AttrsOwner::Field(it) => AttrsWithOwner::new_field(db, it),
AttrsOwner::LifetimeParam(it) => AttrsWithOwner::new_lifetime_param(db, it),
AttrsOwner::TypeOrConstParam(it) => AttrsWithOwner::new_type_or_const_param(db, it),
AttrsOwner::Dummy => {
AttrsWithOwner { attrs: AttrFlags::empty(), owner: AttrsOwner::Dummy }
}
}
}
@ -167,7 +178,9 @@ pub trait HasAttrs: Sized {
match self.attr_id(db) {
AttrsOwner::AttrDef(it) => AttrFlags::docs(db, it).as_deref(),
AttrsOwner::Field(it) => AttrFlags::field_docs(db, it),
AttrsOwner::LifetimeParam(_) | AttrsOwner::TypeOrConstParam(_) => None,
AttrsOwner::LifetimeParam(_) | AttrsOwner::TypeOrConstParam(_) | AttrsOwner::Dummy => {
None
}
}
}
}
@ -190,12 +203,28 @@ impl_has_attrs![
(Trait, TraitId),
(TypeAlias, TypeAliasId),
(Macro, MacroId),
(Function, FunctionId),
(Adt, AdtId),
(Impl, ImplId),
(ExternCrateDecl, ExternCrateId),
];
impl HasAttrs for Function {
fn attr_id(self, _db: &dyn HirDatabase) -> AttrsOwner {
match self.id {
crate::AnyFunctionId::FunctionId(id) => AttrsOwner::AttrDef(id.into()),
crate::AnyFunctionId::BuiltinDeriveImplMethod { .. } => AttrsOwner::Dummy,
}
}
}
impl HasAttrs for Impl {
fn attr_id(self, _db: &dyn HirDatabase) -> AttrsOwner {
match self.id {
hir_ty::next_solver::AnyImplId::ImplId(id) => AttrsOwner::AttrDef(id.into()),
hir_ty::next_solver::AnyImplId::BuiltinDeriveImplId(..) => AttrsOwner::Dummy,
}
}
}
macro_rules! impl_has_attrs_enum {
($($variant:ident),* for $enum:ident) => {$(
impl HasAttrs for $variant {
@ -294,7 +323,9 @@ fn resolve_doc_path_on_(
AttrsOwner::AttrDef(AttrDefId::MacroId(it)) => it.resolver(db),
AttrsOwner::AttrDef(AttrDefId::ExternCrateId(it)) => it.resolver(db),
AttrsOwner::Field(it) => it.parent.resolver(db),
AttrsOwner::LifetimeParam(_) | AttrsOwner::TypeOrConstParam(_) => return None,
AttrsOwner::LifetimeParam(_) | AttrsOwner::TypeOrConstParam(_) | AttrsOwner::Dummy => {
return None;
}
};
let mut modpath = doc_modpath_from_str(link)?;

View File

@ -2,19 +2,22 @@
use either::Either;
use hir_def::{
AdtId, GenericDefId,
AdtId, BuiltinDeriveImplId, FunctionId, GenericDefId, ImplId, ItemContainerId,
builtin_derive::BuiltinDeriveImplMethod,
expr_store::ExpressionStore,
hir::generics::{GenericParams, TypeOrConstParamData, TypeParamProvenance, WherePredicate},
item_tree::FieldsShape,
signatures::{StaticFlags, TraitFlags},
type_ref::{TypeBound, TypeRef, TypeRefId},
};
use hir_expand::name::Name;
use hir_ty::{
GenericPredicates,
db::HirDatabase,
display::{
HirDisplay, HirDisplayWithExpressionStore, HirFormatter, Result, SizedByDefault,
hir_display_with_store, write_bounds_like_dyn_trait_with_prefix, write_visibility,
hir_display_with_store, write_bounds_like_dyn_trait_with_prefix, write_params_bounds,
write_visibility,
},
next_solver::ClauseKind,
};
@ -22,25 +25,78 @@ use itertools::Itertools;
use rustc_type_ir::inherent::IntoKind;
use crate::{
Adt, AsAssocItem, AssocItem, AssocItemContainer, Const, ConstParam, Crate, Enum,
Adt, AnyFunctionId, AsAssocItem, AssocItem, AssocItemContainer, Const, ConstParam, Crate, Enum,
ExternCrateDecl, Field, Function, GenericParam, HasCrate, HasVisibility, Impl, LifetimeParam,
Macro, Module, SelfParam, Static, Struct, StructKind, Trait, TraitRef, TupleField, Type,
TypeAlias, TypeNs, TypeOrConstParam, TypeParam, Union, Variant,
};
fn write_builtin_derive_impl_method<'db>(
f: &mut HirFormatter<'_, 'db>,
impl_: BuiltinDeriveImplId,
method: BuiltinDeriveImplMethod,
) -> Result {
let db = f.db;
let loc = impl_.loc(db);
let (adt_params, _adt_params_store) = db.generic_params_and_store(loc.adt.into());
if f.show_container_bounds() && !adt_params.is_empty() {
f.write_str("impl")?;
write_generic_params(loc.adt.into(), f)?;
f.write_char(' ')?;
let trait_id = loc.trait_.get_id(f.lang_items());
if let Some(trait_id) = trait_id {
f.start_location_link(trait_id.into());
}
write!(f, "{}", Name::new_symbol_root(loc.trait_.name()).display(db, f.edition()))?;
if trait_id.is_some() {
f.end_location_link();
}
f.write_str(" for ")?;
f.start_location_link(loc.adt.into());
write!(f, "{}", Adt::from(loc.adt).name(db).display(db, f.edition()))?;
f.end_location_link();
write_generic_args(loc.adt.into(), f)?;
f.write_char('\n')?;
}
let Some(trait_method) = method.trait_method(db, impl_) else {
return write!(f, "fn {}(…)", method.name());
};
let has_written_where = write_function(f, trait_method)?;
if f.show_container_bounds() && !adt_params.is_empty() {
if !has_written_where {
f.write_str("\nwhere")?
}
write!(f, "\n // Bounds from impl:")?;
let predicates =
hir_ty::builtin_derive::predicates(db, impl_).explicit_predicates().skip_binder();
write_params_bounds(f, predicates)?;
}
Ok(())
}
impl<'db> HirDisplay<'db> for Function {
fn hir_fmt(&self, f: &mut HirFormatter<'_, 'db>) -> Result {
let id = match self.id {
AnyFunctionId::FunctionId(id) => id,
AnyFunctionId::BuiltinDeriveImplMethod { method, impl_ } => {
return write_builtin_derive_impl_method(f, impl_, method);
}
};
let db = f.db;
let data = db.function_signature(self.id);
let container = self.as_assoc_item(db).map(|it| it.container(db));
let mut module = self.module(db);
let container = id.loc(db).container;
// Write container (trait or impl)
let container_params = match container {
Some(AssocItemContainer::Trait(trait_)) => {
let (params, params_store) = f.db.generic_params_and_store(trait_.id.into());
ItemContainerId::TraitId(trait_) => {
let (params, params_store) = f.db.generic_params_and_store(trait_.into());
if f.show_container_bounds() && !params.is_empty() {
write_trait_header(&trait_, f)?;
write_trait_header(trait_.into(), f)?;
f.write_char('\n')?;
has_disaplayable_predicates(f.db, &params, &params_store)
.then_some((params, params_store))
@ -48,10 +104,10 @@ impl<'db> HirDisplay<'db> for Function {
None
}
}
Some(AssocItemContainer::Impl(impl_)) => {
let (params, params_store) = f.db.generic_params_and_store(impl_.id.into());
ItemContainerId::ImplId(impl_) => {
let (params, params_store) = f.db.generic_params_and_store(impl_.into());
if f.show_container_bounds() && !params.is_empty() {
write_impl_header(&impl_, f)?;
write_impl_header(impl_, f)?;
f.write_char('\n')?;
has_disaplayable_predicates(f.db, &params, &params_store)
.then_some((params, params_store))
@ -59,124 +115,20 @@ impl<'db> HirDisplay<'db> for Function {
None
}
}
None => None,
_ => None,
};
// Write signature of the function
// Block-local impls are "hoisted" to the nearest (non-block) module.
if let Some(AssocItemContainer::Impl(_)) = container {
module = module.nearest_non_block_module(db);
}
let module_id = module.id;
write_visibility(module_id, self.visibility(db), f)?;
if data.is_default() {
f.write_str("default ")?;
}
if data.is_const() {
f.write_str("const ")?;
}
if data.is_async() {
f.write_str("async ")?;
}
// FIXME: This will show `unsafe` for functions that are `#[target_feature]` but not unsafe
// (they are conditionally unsafe to call). We probably should show something else.
if self.is_unsafe_to_call(db, None, f.edition()) {
f.write_str("unsafe ")?;
}
if let Some(abi) = &data.abi {
write!(f, "extern \"{}\" ", abi.as_str())?;
}
write!(f, "fn {}", data.name.display(f.db, f.edition()))?;
write_generic_params(GenericDefId::FunctionId(self.id), f)?;
f.write_char('(')?;
let mut first = true;
let mut skip_self = 0;
if let Some(self_param) = self.self_param(db) {
self_param.hir_fmt(f)?;
first = false;
skip_self = 1;
}
// FIXME: Use resolved `param.ty` once we no longer discard lifetimes
let body = db.body(self.id.into());
for (type_ref, param) in data.params.iter().zip(self.assoc_fn_params(db)).skip(skip_self) {
if !first {
f.write_str(", ")?;
} else {
first = false;
}
let pat_id = body.params[param.idx - body.self_param.is_some() as usize];
let pat_str = body.pretty_print_pat(db, self.id.into(), pat_id, true, f.edition());
f.write_str(&pat_str)?;
f.write_str(": ")?;
type_ref.hir_fmt(f, &data.store)?;
}
if data.is_varargs() {
if !first {
f.write_str(", ")?;
}
f.write_str("...")?;
}
f.write_char(')')?;
// `FunctionData::ret_type` will be `::core::future::Future<Output = ...>` for async fns.
// Use ugly pattern match to strip the Future trait.
// Better way?
let ret_type = if !data.is_async() {
data.ret_type
} else if let Some(ret_type) = data.ret_type {
match &data.store[ret_type] {
TypeRef::ImplTrait(bounds) => match &bounds[0] {
&TypeBound::Path(path, _) => Some(
*data.store[path]
.segments()
.iter()
.last()
.unwrap()
.args_and_bindings
.unwrap()
.bindings[0]
.type_ref
.as_ref()
.unwrap(),
),
_ => None,
},
_ => None,
}
} else {
None
};
if let Some(ret_type) = ret_type {
match &data.store[ret_type] {
TypeRef::Tuple(tup) if tup.is_empty() => {}
_ => {
f.write_str(" -> ")?;
ret_type.hir_fmt(f, &data.store)?;
}
}
}
// Write where clauses
let has_written_where = write_where_clause(GenericDefId::FunctionId(self.id), f)?;
let has_written_where = write_function(f, id)?;
if let Some((container_params, container_params_store)) = container_params {
if !has_written_where {
f.write_str("\nwhere")?;
}
let container_name = match container.unwrap() {
AssocItemContainer::Trait(_) => "trait",
AssocItemContainer::Impl(_) => "impl",
let container_name = match container {
ItemContainerId::TraitId(_) => "trait",
ItemContainerId::ImplId(_) => "impl",
_ => unreachable!(),
};
write!(f, "\n // Bounds from {container_name}:",)?;
write_where_predicates(&container_params, &container_params_store, f)?;
@ -185,14 +137,129 @@ impl<'db> HirDisplay<'db> for Function {
}
}
fn write_impl_header<'db>(impl_: &Impl, f: &mut HirFormatter<'_, 'db>) -> Result {
fn write_function<'db>(f: &mut HirFormatter<'_, 'db>, func_id: FunctionId) -> Result<bool> {
let db = f.db;
let func = Function::from(func_id);
let data = db.function_signature(func_id);
let mut module = func.module(db);
// Block-local impls are "hoisted" to the nearest (non-block) module.
if let ItemContainerId::ImplId(_) = func_id.loc(db).container {
module = module.nearest_non_block_module(db);
}
let module_id = module.id;
write_visibility(module_id, func.visibility(db), f)?;
if data.is_default() {
f.write_str("default ")?;
}
if data.is_const() {
f.write_str("const ")?;
}
if data.is_async() {
f.write_str("async ")?;
}
// FIXME: This will show `unsafe` for functions that are `#[target_feature]` but not unsafe
// (they are conditionally unsafe to call). We probably should show something else.
if func.is_unsafe_to_call(db, None, f.edition()) {
f.write_str("unsafe ")?;
}
if let Some(abi) = &data.abi {
write!(f, "extern \"{}\" ", abi.as_str())?;
}
write!(f, "fn {}", data.name.display(f.db, f.edition()))?;
write_generic_params(GenericDefId::FunctionId(func_id), f)?;
f.write_char('(')?;
let mut first = true;
let mut skip_self = 0;
if let Some(self_param) = func.self_param(db) {
self_param.hir_fmt(f)?;
first = false;
skip_self = 1;
}
// FIXME: Use resolved `param.ty` once we no longer discard lifetimes
let body = db.body(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(", ")?;
} else {
first = false;
}
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());
f.write_str(&pat_str)?;
f.write_str(": ")?;
type_ref.hir_fmt(f, &data.store)?;
}
if data.is_varargs() {
if !first {
f.write_str(", ")?;
}
f.write_str("...")?;
}
f.write_char(')')?;
// `FunctionData::ret_type` will be `::core::future::Future<Output = ...>` for async fns.
// Use ugly pattern match to strip the Future trait.
// Better way?
let ret_type = if !data.is_async() {
data.ret_type
} else if let Some(ret_type) = data.ret_type {
match &data.store[ret_type] {
TypeRef::ImplTrait(bounds) => match &bounds[0] {
&TypeBound::Path(path, _) => Some(
*data.store[path]
.segments()
.iter()
.last()
.unwrap()
.args_and_bindings
.unwrap()
.bindings[0]
.type_ref
.as_ref()
.unwrap(),
),
_ => None,
},
_ => None,
}
} else {
None
};
if let Some(ret_type) = ret_type {
match &data.store[ret_type] {
TypeRef::Tuple(tup) if tup.is_empty() => {}
_ => {
f.write_str(" -> ")?;
ret_type.hir_fmt(f, &data.store)?;
}
}
}
// Write where clauses
let has_written_where = write_where_clause(GenericDefId::FunctionId(func_id), f)?;
Ok(has_written_where)
}
fn write_impl_header<'db>(impl_: ImplId, f: &mut HirFormatter<'_, 'db>) -> Result {
let db = f.db;
f.write_str("impl")?;
let def_id = GenericDefId::ImplId(impl_.id);
let def_id = GenericDefId::ImplId(impl_);
write_generic_params(def_id, f)?;
let impl_data = db.impl_signature(impl_.id);
let impl_data = db.impl_signature(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)?;
@ -200,14 +267,28 @@ fn write_impl_header<'db>(impl_: &Impl, f: &mut HirFormatter<'_, 'db>) -> Result
}
f.write_char(' ')?;
impl_.self_ty(db).hir_fmt(f)?;
Impl::from(impl_).self_ty(db).hir_fmt(f)?;
Ok(())
}
impl<'db> HirDisplay<'db> for SelfParam {
fn hir_fmt(&self, f: &mut HirFormatter<'_, 'db>) -> Result {
let data = f.db.function_signature(self.func);
let func = match self.func.id {
AnyFunctionId::FunctionId(id) => id,
AnyFunctionId::BuiltinDeriveImplMethod { method, .. } => match method {
BuiltinDeriveImplMethod::clone
| BuiltinDeriveImplMethod::fmt
| BuiltinDeriveImplMethod::hash
| BuiltinDeriveImplMethod::cmp
| BuiltinDeriveImplMethod::partial_cmp
| BuiltinDeriveImplMethod::eq => return f.write_str("&self"),
BuiltinDeriveImplMethod::default => {
unreachable!("this trait method does not have a self param")
}
},
};
let data = f.db.function_signature(func);
let param = *data.params.first().unwrap();
match &data.store[param] {
TypeRef::Path(p) if p.is_self_type() => f.write_str("self"),
@ -553,6 +634,18 @@ impl<'db> HirDisplay<'db> for ConstParam {
}
fn write_generic_params<'db>(def: GenericDefId, f: &mut HirFormatter<'_, 'db>) -> Result {
write_generic_params_or_args(def, f, true)
}
fn write_generic_args<'db>(def: GenericDefId, f: &mut HirFormatter<'_, 'db>) -> Result {
write_generic_params_or_args(def, f, false)
}
fn write_generic_params_or_args<'db>(
def: GenericDefId,
f: &mut HirFormatter<'_, 'db>,
include_defaults: bool,
) -> Result {
let (params, store) = f.db.generic_params_and_store(def);
if params.iter_lt().next().is_none()
&& params.iter_type_or_consts().all(|it| it.1.const_param().is_none())
@ -587,7 +680,7 @@ fn write_generic_params<'db>(def: GenericDefId, f: &mut HirFormatter<'_, 'db>) -
}
delim(f)?;
write!(f, "{}", name.display(f.db, f.edition()))?;
if let Some(default) = &ty.default {
if include_defaults && let Some(default) = &ty.default {
f.write_str(" = ")?;
default.hir_fmt(f, &store)?;
}
@ -597,7 +690,7 @@ fn write_generic_params<'db>(def: GenericDefId, f: &mut HirFormatter<'_, 'db>) -
write!(f, "const {}: ", name.display(f.db, f.edition()))?;
c.ty.hir_fmt(f, &store)?;
if let Some(default) = &c.default {
if include_defaults && let Some(default) = &c.default {
f.write_str(" = ")?;
default.hir_fmt(f, &store)?;
}
@ -746,7 +839,7 @@ impl<'db> HirDisplay<'db> for TraitRef<'db> {
impl<'db> HirDisplay<'db> for Trait {
fn hir_fmt(&self, f: &mut HirFormatter<'_, 'db>) -> Result {
// FIXME(trait-alias) needs special handling to print the equal sign
write_trait_header(self, f)?;
write_trait_header(*self, f)?;
let def_id = GenericDefId::TraitId(self.id);
let has_where_clause = write_where_clause(def_id, f)?;
@ -783,7 +876,7 @@ impl<'db> HirDisplay<'db> for Trait {
}
}
fn write_trait_header<'db>(trait_: &Trait, f: &mut HirFormatter<'_, 'db>) -> Result {
fn write_trait_header<'db>(trait_: Trait, f: &mut HirFormatter<'_, 'db>) -> Result {
write_visibility(trait_.module(f.db).id, trait_.visibility(f.db), f)?;
let data = f.db.trait_signature(trait_.id);
if data.flags.contains(TraitFlags::UNSAFE) {

View File

@ -4,14 +4,15 @@
//! are splitting the hir.
use hir_def::{
AdtId, AssocItemId, DefWithBodyId, EnumVariantId, FieldId, GenericDefId, GenericParamId,
ModuleDefId, VariantId,
AdtId, AssocItemId, BuiltinDeriveImplId, DefWithBodyId, EnumVariantId, FieldId, GenericDefId,
GenericParamId, ModuleDefId, VariantId,
hir::{BindingId, LabelId},
};
use hir_ty::next_solver::AnyImplId;
use crate::{
Adt, AssocItem, BuiltinType, DefWithBody, Field, GenericDef, GenericParam, ItemInNs, Label,
Local, ModuleDef, Variant, VariantDef,
Adt, AnyFunctionId, AssocItem, BuiltinType, DefWithBody, Field, GenericDef, GenericParam,
ItemInNs, Label, Local, ModuleDef, Variant, VariantDef,
};
macro_rules! from_id {
@ -39,8 +40,8 @@ from_id![
(hir_def::TraitId, crate::Trait),
(hir_def::StaticId, crate::Static),
(hir_def::ConstId, crate::Const),
(hir_def::FunctionId, crate::Function),
(hir_def::ImplId, crate::Impl),
(crate::AnyFunctionId, crate::Function),
(hir_ty::next_solver::AnyImplId, crate::Impl),
(hir_def::TypeOrConstParamId, crate::TypeOrConstParam),
(hir_def::TypeParamId, crate::TypeParam),
(hir_def::ConstParamId, crate::ConstParam),
@ -119,11 +120,15 @@ impl From<ModuleDefId> for ModuleDef {
}
}
impl From<ModuleDef> for ModuleDefId {
fn from(id: ModuleDef) -> Self {
match id {
impl TryFrom<ModuleDef> for ModuleDefId {
type Error = ();
fn try_from(id: ModuleDef) -> Result<Self, Self::Error> {
Ok(match id {
ModuleDef::Module(it) => ModuleDefId::ModuleId(it.into()),
ModuleDef::Function(it) => ModuleDefId::FunctionId(it.into()),
ModuleDef::Function(it) => match it.id {
AnyFunctionId::FunctionId(it) => it.into(),
AnyFunctionId::BuiltinDeriveImplMethod { .. } => return Err(()),
},
ModuleDef::Adt(it) => ModuleDefId::AdtId(it.into()),
ModuleDef::Variant(it) => ModuleDefId::EnumVariantId(it.into()),
ModuleDef::Const(it) => ModuleDefId::ConstId(it.into()),
@ -132,18 +137,22 @@ impl From<ModuleDef> for ModuleDefId {
ModuleDef::TypeAlias(it) => ModuleDefId::TypeAliasId(it.into()),
ModuleDef::BuiltinType(it) => ModuleDefId::BuiltinType(it.into()),
ModuleDef::Macro(it) => ModuleDefId::MacroId(it.into()),
}
})
}
}
impl From<DefWithBody> for DefWithBodyId {
fn from(def: DefWithBody) -> Self {
match def {
DefWithBody::Function(it) => DefWithBodyId::FunctionId(it.id),
impl TryFrom<DefWithBody> for DefWithBodyId {
type Error = ();
fn try_from(def: DefWithBody) -> Result<Self, ()> {
Ok(match def {
DefWithBody::Function(it) => match it.id {
AnyFunctionId::FunctionId(it) => it.into(),
AnyFunctionId::BuiltinDeriveImplMethod { .. } => return Err(()),
},
DefWithBody::Static(it) => DefWithBodyId::StaticId(it.id),
DefWithBody::Const(it) => DefWithBodyId::ConstId(it.id),
DefWithBody::Variant(it) => DefWithBodyId::VariantId(it.into()),
}
})
}
}
@ -168,17 +177,11 @@ impl From<AssocItemId> for AssocItem {
}
}
impl From<GenericDef> for GenericDefId {
fn from(def: GenericDef) -> Self {
match def {
GenericDef::Function(it) => GenericDefId::FunctionId(it.id),
GenericDef::Adt(it) => GenericDefId::AdtId(it.into()),
GenericDef::Trait(it) => GenericDefId::TraitId(it.id),
GenericDef::TypeAlias(it) => GenericDefId::TypeAliasId(it.id),
GenericDef::Impl(it) => GenericDefId::ImplId(it.id),
GenericDef::Const(it) => GenericDefId::ConstId(it.id),
GenericDef::Static(it) => GenericDefId::StaticId(it.id),
}
impl TryFrom<GenericDef> for GenericDefId {
type Error = ();
fn try_from(def: GenericDef) -> Result<Self, Self::Error> {
def.id().ok_or(())
}
}
@ -238,13 +241,17 @@ impl From<FieldId> for Field {
}
}
impl From<AssocItem> for GenericDefId {
fn from(item: AssocItem) -> Self {
match item {
AssocItem::Function(f) => f.id.into(),
impl TryFrom<AssocItem> for GenericDefId {
type Error = ();
fn try_from(item: AssocItem) -> Result<Self, Self::Error> {
Ok(match item {
AssocItem::Function(f) => match f.id {
AnyFunctionId::FunctionId(it) => it.into(),
AnyFunctionId::BuiltinDeriveImplMethod { .. } => return Err(()),
},
AssocItem::Const(c) => c.id.into(),
AssocItem::TypeAlias(t) => t.id.into(),
}
})
}
}
@ -270,13 +277,14 @@ impl From<hir_def::item_scope::ItemInNs> for ItemInNs {
}
}
impl From<ItemInNs> for hir_def::item_scope::ItemInNs {
fn from(it: ItemInNs) -> Self {
match it {
ItemInNs::Types(it) => Self::Types(it.into()),
ItemInNs::Values(it) => Self::Values(it.into()),
impl TryFrom<ItemInNs> for hir_def::item_scope::ItemInNs {
type Error = ();
fn try_from(it: ItemInNs) -> Result<Self, Self::Error> {
Ok(match it {
ItemInNs::Types(it) => Self::Types(it.try_into()?),
ItemInNs::Values(it) => Self::Values(it.try_into()?),
ItemInNs::Macros(it) => Self::Macros(it.into()),
}
})
}
}
@ -291,3 +299,21 @@ impl From<BuiltinType> for hir_def::builtin_type::BuiltinType {
it.inner
}
}
impl From<hir_def::ImplId> for crate::Impl {
fn from(value: hir_def::ImplId) -> Self {
crate::Impl { id: AnyImplId::ImplId(value) }
}
}
impl From<BuiltinDeriveImplId> for crate::Impl {
fn from(value: BuiltinDeriveImplId) -> Self {
crate::Impl { id: AnyImplId::BuiltinDeriveImplId(value) }
}
}
impl From<hir_def::FunctionId> for crate::Function {
fn from(value: hir_def::FunctionId) -> Self {
crate::Function { id: AnyFunctionId::FunctionId(value) }
}
}

View File

@ -7,18 +7,18 @@ use hir_def::{
src::{HasChildSource, HasSource as _},
};
use hir_expand::{EditionedFileId, HirFileId, InFile};
use hir_ty::db::InternedClosure;
use syntax::ast;
use hir_ty::{db::InternedClosure, next_solver::AnyImplId};
use syntax::{AstNode, ast};
use tt::TextRange;
use crate::{
Adt, Callee, Const, Enum, ExternCrateDecl, Field, FieldSource, Function, Impl,
Adt, AnyFunctionId, Callee, Const, Enum, ExternCrateDecl, Field, FieldSource, Function, Impl,
InlineAsmOperand, Label, LifetimeParam, LocalSource, Macro, Module, Param, SelfParam, Static,
Struct, Trait, TypeAlias, TypeOrConstParam, Union, Variant, VariantDef, db::HirDatabase,
};
pub trait HasSource {
type Ast;
pub trait HasSource: Sized {
type Ast: AstNode;
/// Fetches the definition's source node.
/// Using [`crate::SemanticsImpl::source`] is preferred when working with [`crate::Semantics`],
/// as that caches the parsed file in the semantics' cache.
@ -27,6 +27,20 @@ pub trait HasSource {
/// But we made this method `Option` to support rlib in the future
/// by <https://github.com/rust-lang/rust-analyzer/issues/6913>
fn source(self, db: &dyn HirDatabase) -> Option<InFile<Self::Ast>>;
/// Fetches the source node, along with its full range.
///
/// The reason for the separate existence of this method is that some things, notably builtin derive impls,
/// do not really have a source node, at least not of the correct type. But we still can trace them
/// to source code (the derive producing them). So this method will return the range if it is supported,
/// and if the node is supported too it will return it as well.
fn source_with_range(
self,
db: &dyn HirDatabase,
) -> Option<InFile<(TextRange, Option<Self::Ast>)>> {
let source = self.source(db)?;
Some(source.map(|node| (node.syntax().text_range(), Some(node))))
}
}
/// NB: Module is !HasSource, because it has two source nodes at the same time:
@ -146,7 +160,30 @@ impl HasSource for Variant {
impl HasSource for Function {
type Ast = ast::Fn;
fn source(self, db: &dyn HirDatabase) -> Option<InFile<Self::Ast>> {
Some(self.id.lookup(db).source(db))
match self.id {
AnyFunctionId::FunctionId(id) => Some(id.loc(db).source(db)),
// When calling `source()`, we use the trait method source, but when calling `source_with_range()`,
// we return `None` as the syntax node source. This is relying on the assumption that if you are calling
// `source_with_range()` (e.g. in navigation) you're prepared to deal with no source node, while if
// you call `source()` maybe you don't - therefore we fall back to the trait method, to not lose features.
AnyFunctionId::BuiltinDeriveImplMethod { method, impl_ } => method
.trait_method(db, impl_)
.and_then(|trait_method| Function::from(trait_method).source(db)),
}
}
fn source_with_range(
self,
db: &dyn HirDatabase,
) -> Option<InFile<(TextRange, Option<Self::Ast>)>> {
match self.id {
AnyFunctionId::FunctionId(id) => Some(
id.loc(db).source(db).map(|source| (source.syntax().text_range(), Some(source))),
),
AnyFunctionId::BuiltinDeriveImplMethod { impl_, .. } => {
Some(impl_.loc(db).source(db).map(|range| (range, None)))
}
}
}
}
impl HasSource for Const {
@ -190,7 +227,24 @@ impl HasSource for Macro {
impl HasSource for Impl {
type Ast = ast::Impl;
fn source(self, db: &dyn HirDatabase) -> Option<InFile<Self::Ast>> {
Some(self.id.lookup(db).source(db))
match self.id {
AnyImplId::ImplId(id) => Some(id.loc(db).source(db)),
AnyImplId::BuiltinDeriveImplId(_) => None,
}
}
fn source_with_range(
self,
db: &dyn HirDatabase,
) -> Option<InFile<(TextRange, Option<Self::Ast>)>> {
match self.id {
AnyImplId::ImplId(id) => Some(
id.loc(db).source(db).map(|source| (source.syntax().text_range(), Some(source))),
),
AnyImplId::BuiltinDeriveImplId(impl_) => {
Some(impl_.loc(db).source(db).map(|range| (range, None)))
}
}
}
}
@ -224,7 +278,7 @@ impl HasSource for Param<'_> {
fn source(self, db: &dyn HirDatabase) -> Option<InFile<Self::Ast>> {
match self.func {
Callee::Def(CallableDefId::FunctionId(func)) => {
let InFile { file_id, value } = Function { id: func }.source(db)?;
let InFile { file_id, value } = Function::from(func).source(db)?;
let params = value.param_list()?;
if let Some(self_param) = params.self_param() {
if let Some(idx) = self.idx.checked_sub(1) {
@ -261,7 +315,7 @@ impl HasSource for SelfParam {
type Ast = ast::SelfParam;
fn source(self, db: &dyn HirDatabase) -> Option<InFile<Self::Ast>> {
let InFile { file_id, value } = Function::from(self.func).source(db)?;
let InFile { file_id, value } = self.func.source(db)?;
value
.param_list()
.and_then(|params| params.self_param())

File diff suppressed because it is too large Load Diff

View File

@ -13,7 +13,7 @@ use std::{
use base_db::FxIndexSet;
use either::Either;
use hir_def::{
DefWithBodyId, FunctionId, MacroId, StructId, TraitId, VariantId,
DefWithBodyId, MacroId, StructId, TraitId, VariantId,
attrs::parse_extra_crate_attrs,
expr_store::{Body, ExprOrPatSource, HygieneId, path::Path},
hir::{BindingId, Expr, ExprId, ExprOrPatId, Pat},
@ -34,7 +34,7 @@ use hir_ty::{
diagnostics::{unsafe_operations, unsafe_operations_for_body},
infer_query_with_inspect,
next_solver::{
DbInterner, Span,
AnyImplId, DbInterner, Span,
format_proof_tree::{ProofTreeData, dump_proof_tree_structured},
},
};
@ -53,11 +53,11 @@ use syntax::{
};
use crate::{
Adjust, Adjustment, Adt, AutoBorrow, BindingMode, BuiltinAttr, Callable, Const, ConstParam,
Crate, DefWithBody, DeriveHelper, Enum, Field, Function, GenericSubstitution, HasSource, Impl,
InFile, InlineAsmOperand, ItemInNs, Label, LifetimeParam, Local, Macro, Module, ModuleDef,
Name, OverloadedDeref, ScopeDef, Static, Struct, ToolModule, Trait, TupleField, Type,
TypeAlias, TypeParam, Union, Variant, VariantDef,
Adjust, Adjustment, Adt, AnyFunctionId, AutoBorrow, BindingMode, BuiltinAttr, Callable, Const,
ConstParam, Crate, DefWithBody, DeriveHelper, Enum, Field, Function, GenericSubstitution,
HasSource, Impl, InFile, InlineAsmOperand, ItemInNs, Label, LifetimeParam, Local, Macro,
Module, ModuleDef, Name, OverloadedDeref, ScopeDef, Static, Struct, ToolModule, Trait,
TupleField, Type, TypeAlias, TypeParam, Union, Variant, VariantDef,
db::HirDatabase,
semantics::source_to_def::{ChildContainer, SourceToDefCache, SourceToDefCtx},
source_analyzer::{SourceAnalyzer, resolve_hir_path},
@ -106,7 +106,10 @@ impl PathResolution {
| PathResolution::DeriveHelper(_)
| PathResolution::ConstParam(_) => None,
PathResolution::TypeParam(param) => Some(TypeNs::GenericParam((*param).into())),
PathResolution::SelfType(impl_def) => Some(TypeNs::SelfType((*impl_def).into())),
PathResolution::SelfType(impl_def) => match impl_def.id {
AnyImplId::ImplId(id) => Some(TypeNs::SelfType(id)),
AnyImplId::BuiltinDeriveImplId(_) => None,
},
}
}
}
@ -345,23 +348,23 @@ impl<DB: HirDatabase + ?Sized> Semantics<'_, DB> {
}
pub fn resolve_await_to_poll(&self, await_expr: &ast::AwaitExpr) -> Option<Function> {
self.imp.resolve_await_to_poll(await_expr).map(Function::from)
self.imp.resolve_await_to_poll(await_expr)
}
pub fn resolve_prefix_expr(&self, prefix_expr: &ast::PrefixExpr) -> Option<Function> {
self.imp.resolve_prefix_expr(prefix_expr).map(Function::from)
self.imp.resolve_prefix_expr(prefix_expr)
}
pub fn resolve_index_expr(&self, index_expr: &ast::IndexExpr) -> Option<Function> {
self.imp.resolve_index_expr(index_expr).map(Function::from)
self.imp.resolve_index_expr(index_expr)
}
pub fn resolve_bin_expr(&self, bin_expr: &ast::BinExpr) -> Option<Function> {
self.imp.resolve_bin_expr(bin_expr).map(Function::from)
self.imp.resolve_bin_expr(bin_expr)
}
pub fn resolve_try_expr(&self, try_expr: &ast::TryExpr) -> Option<Function> {
self.imp.resolve_try_expr(try_expr).map(Function::from)
self.imp.resolve_try_expr(try_expr)
}
pub fn resolve_variant(&self, record_lit: ast::RecordExpr) -> Option<VariantDef> {
@ -1749,6 +1752,7 @@ impl<'db> SemanticsImpl<'db> {
func: Function,
subst: impl IntoIterator<Item = Type<'db>>,
) -> Option<Function> {
let AnyFunctionId::FunctionId(func) = func.id else { return Some(func) };
let interner = DbInterner::new_no_crate(self.db);
let mut subst = subst.into_iter();
let substs =
@ -1757,7 +1761,12 @@ impl<'db> SemanticsImpl<'db> {
subst.next().expect("too few subst").ty.into()
});
assert!(subst.next().is_none(), "too many subst");
Some(self.db.lookup_impl_method(env.env, func.into(), substs).0.into())
Some(match self.db.lookup_impl_method(env.env, func, substs).0 {
Either::Left(it) => it.into(),
Either::Right((impl_, method)) => {
Function { id: AnyFunctionId::BuiltinDeriveImplMethod { method, impl_ } }
}
})
}
fn resolve_range_pat(&self, range_pat: &ast::RangePat) -> Option<StructId> {
@ -1768,23 +1777,23 @@ impl<'db> SemanticsImpl<'db> {
self.analyze(range_expr.syntax())?.resolve_range_expr(self.db, range_expr)
}
fn resolve_await_to_poll(&self, await_expr: &ast::AwaitExpr) -> Option<FunctionId> {
fn resolve_await_to_poll(&self, await_expr: &ast::AwaitExpr) -> Option<Function> {
self.analyze(await_expr.syntax())?.resolve_await_to_poll(self.db, await_expr)
}
fn resolve_prefix_expr(&self, prefix_expr: &ast::PrefixExpr) -> Option<FunctionId> {
fn resolve_prefix_expr(&self, prefix_expr: &ast::PrefixExpr) -> Option<Function> {
self.analyze(prefix_expr.syntax())?.resolve_prefix_expr(self.db, prefix_expr)
}
fn resolve_index_expr(&self, index_expr: &ast::IndexExpr) -> Option<FunctionId> {
fn resolve_index_expr(&self, index_expr: &ast::IndexExpr) -> Option<Function> {
self.analyze(index_expr.syntax())?.resolve_index_expr(self.db, index_expr)
}
fn resolve_bin_expr(&self, bin_expr: &ast::BinExpr) -> Option<FunctionId> {
fn resolve_bin_expr(&self, bin_expr: &ast::BinExpr) -> Option<Function> {
self.analyze(bin_expr.syntax())?.resolve_bin_expr(self.db, bin_expr)
}
fn resolve_try_expr(&self, try_expr: &ast::TryExpr) -> Option<FunctionId> {
fn resolve_try_expr(&self, try_expr: &ast::TryExpr) -> Option<Function> {
self.analyze(try_expr.syntax())?.resolve_try_expr(self.db, try_expr)
}
@ -1861,7 +1870,9 @@ impl<'db> SemanticsImpl<'db> {
}
pub fn get_unsafe_ops(&self, def: DefWithBody) -> FxHashSet<ExprOrPatSource> {
let def = DefWithBodyId::from(def);
let Ok(def) = DefWithBodyId::try_from(def) else {
return FxHashSet::default();
};
let (body, source_map) = self.db.body_with_source_map(def);
let infer = InferenceResult::for_body(self.db, def);
let mut res = FxHashSet::default();
@ -1877,7 +1888,9 @@ impl<'db> SemanticsImpl<'db> {
always!(block.unsafe_token().is_some());
let block = self.wrap_node_infile(ast::Expr::from(block));
let Some(def) = self.body_for(block.syntax()) else { return Vec::new() };
let def = def.into();
let Ok(def) = def.try_into() else {
return Vec::new();
};
let (body, source_map) = self.db.body_with_source_map(def);
let infer = InferenceResult::for_body(self.db, def);
let Some(ExprOrPatId::ExprId(block)) = source_map.node_expr(block.as_ref()) else {
@ -2023,16 +2036,22 @@ impl<'db> SemanticsImpl<'db> {
}
/// Search for a definition's source and cache its syntax tree
pub fn source<Def: HasSource>(&self, def: Def) -> Option<InFile<Def::Ast>>
where
Def::Ast: AstNode,
{
pub fn source<Def: HasSource>(&self, def: Def) -> Option<InFile<Def::Ast>> {
// FIXME: source call should go through the parse cache
let res = def.source(self.db)?;
self.cache(find_root(res.value.syntax()), res.file_id);
Some(res)
}
pub fn source_with_range<Def: HasSource>(
&self,
def: Def,
) -> Option<InFile<(TextRange, Option<Def::Ast>)>> {
let res = def.source_with_range(self.db)?;
self.parse_or_expand(res.file_id);
Some(res)
}
pub fn body_for(&self, node: InFile<&SyntaxNode>) -> Option<DefWithBody> {
let container = self.with_ctx(|ctx| ctx.find_container(node))?;
@ -2162,9 +2181,10 @@ impl<'db> SemanticsImpl<'db> {
let def = match &enclosing_item {
Either::Left(ast::Item::Fn(it)) if it.unsafe_token().is_some() => return true,
Either::Left(ast::Item::Fn(it)) => {
self.to_def(it).map(<_>::into).map(DefWithBodyId::FunctionId)
}
Either::Left(ast::Item::Fn(it)) => (|| match self.to_def(it)?.id {
AnyFunctionId::FunctionId(id) => Some(DefWithBodyId::FunctionId(id)),
AnyFunctionId::BuiltinDeriveImplMethod { .. } => None,
})(),
Either::Left(ast::Item::Const(it)) => {
self.to_def(it).map(<_>::into).map(DefWithBodyId::ConstId)
}
@ -2201,7 +2221,11 @@ impl<'db> SemanticsImpl<'db> {
}
pub fn impl_generated_from_derive(&self, impl_: Impl) -> Option<Adt> {
let source = hir_def::src::HasSource::ast_ptr(&impl_.id.loc(self.db), self.db);
let id = match impl_.id {
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 mut file_id = source.file_id;
let adt_ast_id = loop {
let macro_call = file_id.macro_file()?;

View File

@ -57,9 +57,9 @@ use syntax::{
use triomphe::Arc;
use crate::{
Adt, AssocItem, BindingMode, BuiltinAttr, BuiltinType, Callable, Const, DeriveHelper, Field,
Function, GenericSubstitution, Local, Macro, ModuleDef, Static, Struct, ToolModule, Trait,
TupleField, Type, TypeAlias, Variant,
Adt, AnyFunctionId, AssocItem, BindingMode, BuiltinAttr, BuiltinType, Callable, Const,
DeriveHelper, Field, Function, GenericSubstitution, Local, Macro, ModuleDef, Static, Struct,
ToolModule, Trait, TupleField, Type, TypeAlias, Variant,
db::HirDatabase,
semantics::{PathResolution, PathResolutionPerNs},
};
@ -431,7 +431,7 @@ impl<'db> SourceAnalyzer<'db> {
let expr_id = self.expr_id(call.clone().into())?.as_expr()?;
let (f_in_trait, substs) = self.infer()?.method_resolution(expr_id)?;
Some(self.resolve_impl_method_or_trait_def(db, f_in_trait, substs).into())
Some(self.resolve_impl_method_or_trait_def(db, f_in_trait, substs))
}
pub(crate) fn resolve_method_call_fallback(
@ -446,8 +446,8 @@ impl<'db> SourceAnalyzer<'db> {
let (fn_, subst) =
self.resolve_impl_method_or_trait_def_with_subst(db, f_in_trait, substs);
Some((
Either::Left(fn_.into()),
Some(GenericSubstitution::new(fn_.into(), subst, self.trait_environment(db))),
Either::Left(fn_),
GenericSubstitution::new_from_fn(fn_, subst, self.trait_environment(db)),
))
}
None => {
@ -519,8 +519,8 @@ 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.into()),
Some(GenericSubstitution::new(f.into(), subst, self.trait_environment(db))),
Either::Right(f),
GenericSubstitution::new_from_fn(f, subst, self.trait_environment(db)),
)
}),
}
@ -569,7 +569,7 @@ impl<'db> SourceAnalyzer<'db> {
&self,
db: &'db dyn HirDatabase,
await_expr: &ast::AwaitExpr,
) -> Option<FunctionId> {
) -> Option<Function> {
let mut ty = self.ty_of_expr(await_expr.expr()?)?;
let into_future_trait = self
@ -605,7 +605,7 @@ impl<'db> SourceAnalyzer<'db> {
&self,
db: &'db dyn HirDatabase,
prefix_expr: &ast::PrefixExpr,
) -> Option<FunctionId> {
) -> Option<Function> {
let (_op_trait, op_fn) = match prefix_expr.op_kind()? {
ast::UnaryOp::Deref => {
// This can be either `Deref::deref` or `DerefMut::deref_mut`.
@ -650,7 +650,7 @@ impl<'db> SourceAnalyzer<'db> {
&self,
db: &'db dyn HirDatabase,
index_expr: &ast::IndexExpr,
) -> Option<FunctionId> {
) -> Option<Function> {
let base_ty = self.ty_of_expr(index_expr.base()?)?;
let index_ty = self.ty_of_expr(index_expr.index()?)?;
@ -679,7 +679,7 @@ impl<'db> SourceAnalyzer<'db> {
&self,
db: &'db dyn HirDatabase,
binop_expr: &ast::BinExpr,
) -> Option<FunctionId> {
) -> Option<Function> {
let op = binop_expr.op_kind()?;
let lhs = self.ty_of_expr(binop_expr.lhs()?)?;
let rhs = self.ty_of_expr(binop_expr.rhs()?)?;
@ -699,7 +699,7 @@ impl<'db> SourceAnalyzer<'db> {
&self,
db: &'db dyn HirDatabase,
try_expr: &ast::TryExpr,
) -> Option<FunctionId> {
) -> Option<Function> {
let ty = self.ty_of_expr(try_expr.expr()?)?;
let op_fn = self.lang_items(db).TryTraitBranch?;
@ -905,7 +905,7 @@ impl<'db> SourceAnalyzer<'db> {
subs,
self.trait_environment(db),
);
(AssocItemId::from(f_in_trait), subst)
(AssocItem::Function(f_in_trait.into()), Some(subst))
}
Some(func_ty) => {
if let TyKind::FnDef(_fn_def, subs) = func_ty.kind() {
@ -913,19 +913,19 @@ impl<'db> SourceAnalyzer<'db> {
.resolve_impl_method_or_trait_def_with_subst(
db, f_in_trait, subs,
);
let subst = GenericSubstitution::new(
fn_.into(),
let subst = GenericSubstitution::new_from_fn(
fn_,
subst,
self.trait_environment(db),
);
(fn_.into(), subst)
(AssocItem::Function(fn_), subst)
} else {
let subst = GenericSubstitution::new(
f_in_trait.into(),
subs,
self.trait_environment(db),
);
(f_in_trait.into(), subst)
(AssocItem::Function(f_in_trait.into()), Some(subst))
}
}
}
@ -938,11 +938,11 @@ impl<'db> SourceAnalyzer<'db> {
subst,
self.trait_environment(db),
);
(konst.into(), subst)
(AssocItem::Const(konst.into()), Some(subst))
}
};
return Some((PathResolution::Def(AssocItem::from(assoc).into()), Some(subst)));
return Some((PathResolution::Def(assoc.into()), subst));
}
if let Some(VariantId::EnumVariantId(variant)) =
infer.variant_resolution_for_expr_or_pat(expr_id)
@ -1401,7 +1401,7 @@ impl<'db> SourceAnalyzer<'db> {
db: &'db dyn HirDatabase,
func: FunctionId,
substs: GenericArgs<'db>,
) -> FunctionId {
) -> Function {
self.resolve_impl_method_or_trait_def_with_subst(db, func, substs).0
}
@ -1410,13 +1410,19 @@ impl<'db> SourceAnalyzer<'db> {
db: &'db dyn HirDatabase,
func: FunctionId,
substs: GenericArgs<'db>,
) -> (FunctionId, GenericArgs<'db>) {
) -> (Function, GenericArgs<'db>) {
let owner = match self.resolver.body_owner() {
Some(it) => it,
None => return (func, substs),
None => return (func.into(), substs),
};
let env = self.param_and(db.trait_environment_for_body(owner));
db.lookup_impl_method(env, func, substs)
let (func, args) = db.lookup_impl_method(env, func, substs);
match func {
Either::Left(func) => (func.into(), args),
Either::Right((impl_, method)) => {
(Function { id: AnyFunctionId::BuiltinDeriveImplMethod { method, impl_ } }, args)
}
}
}
fn resolve_impl_const_or_trait_def_with_subst(

View File

@ -734,11 +734,11 @@
FileSymbol {
name: "generic_impl_fn",
def: Function(
Function {
id: FunctionId(
FunctionId(
FunctionId(
6402,
),
},
),
),
loc: DeclarationLocation {
hir_file_id: FileId(
@ -769,11 +769,11 @@
FileSymbol {
name: "impl_fn",
def: Function(
Function {
id: FunctionId(
FunctionId(
FunctionId(
6401,
),
},
),
),
loc: DeclarationLocation {
hir_file_id: FileId(
@ -839,11 +839,11 @@
FileSymbol {
name: "main",
def: Function(
Function {
id: FunctionId(
FunctionId(
FunctionId(
6400,
),
},
),
),
loc: DeclarationLocation {
hir_file_id: FileId(
@ -907,11 +907,11 @@
FileSymbol {
name: "trait_fn",
def: Function(
Function {
id: FunctionId(
FunctionId(
FunctionId(
6403,
),
},
),
),
loc: DeclarationLocation {
hir_file_id: FileId(

View File

@ -767,14 +767,30 @@ fn label_of_ty(
)
});
let module_def_location = |label_builder: &mut InlayHintLabelBuilder<'_>,
def: ModuleDef,
name| {
let def = def.try_into();
if let Ok(def) = def {
label_builder.start_location_link(def);
}
#[expect(
clippy::question_mark,
reason = "false positive; replacing with `?` leads to 'type annotations needed' error"
)]
if let Err(err) = label_builder.write_str(name) {
return Err(err);
}
if def.is_ok() {
label_builder.end_location_link();
}
Ok(())
};
label_builder.write_str(LABEL_START)?;
label_builder.start_location_link(ModuleDef::from(iter_trait).into());
label_builder.write_str(LABEL_ITERATOR)?;
label_builder.end_location_link();
module_def_location(label_builder, ModuleDef::from(iter_trait), LABEL_ITERATOR)?;
label_builder.write_str(LABEL_MIDDLE)?;
label_builder.start_location_link(ModuleDef::from(item).into());
label_builder.write_str(LABEL_ITEM)?;
label_builder.end_location_link();
module_def_location(label_builder, ModuleDef::from(item), LABEL_ITEM)?;
label_builder.write_str(LABEL_MIDDLE2)?;
rec(sema, famous_defs, max_length, &ty, label_builder, config, display_target)?;
label_builder.write_str(LABEL_END)?;

View File

@ -34,9 +34,10 @@ pub(super) fn hints(
let def = sema.to_def(node)?;
let def: DefWithBody = def.into();
let (hir, source_map) = sema.db.body_with_source_map(def.into());
let def = def.try_into().ok()?;
let (hir, source_map) = sema.db.body_with_source_map(def);
let mir = sema.db.mir_body(def.into()).ok()?;
let mir = sema.db.mir_body(def).ok()?;
let local_to_binding = mir.local_to_binding_map();

View File

@ -6,7 +6,7 @@ use arrayvec::ArrayVec;
use either::Either;
use hir::{
AssocItem, Crate, FieldSource, HasContainer, HasCrate, HasSource, HirDisplay, HirFileId,
InFile, LocalSource, ModuleSource, Semantics, Symbol, db::ExpandDatabase, sym,
InFile, LocalSource, ModuleSource, Name, Semantics, Symbol, db::ExpandDatabase, sym,
symbols::FileSymbol,
};
use ide_db::{
@ -204,6 +204,22 @@ impl NavigationTarget {
)
}
pub(crate) fn from_named_with_range(
db: &RootDatabase,
ranges: InFile<(TextRange, Option<TextRange>)>,
name: Option<Name>,
kind: SymbolKind,
) -> UpmappingResult<NavigationTarget> {
let InFile { file_id, value: (full_range, focus_range) } = ranges;
let name = name.map(|name| name.symbol().clone()).unwrap_or_else(|| sym::underscore);
orig_range_with_focus_r(db, file_id, full_range, focus_range).map(
|(FileRange { file_id, range: full_range }, focus_range)| {
NavigationTarget::from_syntax(file_id, name.clone(), focus_range, full_range, kind)
},
)
}
pub(crate) fn from_syntax(
file_id: FileId,
name: Symbol,
@ -414,7 +430,13 @@ impl ToNavFromAst for hir::Trait {
impl<D> TryToNav for D
where
D: HasSource + ToNavFromAst + Copy + HasDocs + for<'db> HirDisplay<'db> + HasCrate,
D: HasSource
+ ToNavFromAst
+ Copy
+ HasDocs
+ for<'db> HirDisplay<'db>
+ HasCrate
+ hir::HasName,
D::Ast: ast::HasName,
{
fn try_to_nav(
@ -422,11 +444,19 @@ where
sema: &Semantics<'_, RootDatabase>,
) -> Option<UpmappingResult<NavigationTarget>> {
let db = sema.db;
let src = self.source(db)?;
let src = self.source_with_range(db)?;
Some(
NavigationTarget::from_named(
NavigationTarget::from_named_with_range(
db,
src.as_ref().map(|it| it as &dyn ast::HasName),
src.map(|(full_range, node)| {
(
full_range,
node.and_then(|node| {
Some(ast::HasName::name(&node)?.syntax().text_range())
}),
)
}),
self.name(db),
D::KIND,
)
.map(|mut res| {
@ -477,16 +507,16 @@ impl TryToNav for hir::Impl {
sema: &Semantics<'_, RootDatabase>,
) -> Option<UpmappingResult<NavigationTarget>> {
let db = sema.db;
let InFile { file_id, value } = self.source(db)?;
let derive_path = self.as_builtin_derive_path(db);
let InFile { file_id, value: (full_range, source) } = self.source_with_range(db)?;
let (file_id, focus, syntax) = match &derive_path {
Some(attr) => (attr.file_id.into(), None, attr.value.syntax()),
None => (file_id, value.self_ty(), value.syntax()),
};
Some(orig_range_with_focus(db, file_id, syntax, focus).map(
|(FileRange { file_id, range: full_range }, focus_range)| {
Some(
orig_range_with_focus_r(
db,
file_id,
full_range,
source.and_then(|source| Some(source.self_ty()?.syntax().text_range())),
)
.map(|(FileRange { file_id, range: full_range }, focus_range)| {
NavigationTarget::from_syntax(
file_id,
sym::kw_impl,
@ -494,8 +524,8 @@ impl TryToNav for hir::Impl {
full_range,
SymbolKind::Impl,
)
},
))
}),
)
}
}

View File

@ -2503,7 +2503,7 @@ fn r#fn$0() {}
fn main() { r#fn(); }
"#,
expect![[r#"
r#fn Function FileId(0) 0..12 3..7
fn Function FileId(0) 0..12 3..7
FileId(0) 25..29
"#]],

View File

@ -1679,11 +1679,11 @@ mod r#mod {
[
"(TestMod, NavigationTarget { file_id: FileId(0), full_range: 1..461, focus_range: 5..10, name: \"mod\", kind: Module, description: \"mod r#mod\" })",
"(Test, NavigationTarget { file_id: FileId(0), full_range: 17..41, focus_range: 32..36, name: \"r#fn\", kind: Function })",
"(DocTest, NavigationTarget { file_id: FileId(0), full_range: 47..84, name: \"r#for\", container_name: \"mod\" })",
"(DocTest, NavigationTarget { file_id: FileId(0), full_range: 90..146, name: \"r#struct\", container_name: \"mod\" })",
"(DocTest, NavigationTarget { file_id: FileId(0), full_range: 47..84, name: \"for\", container_name: \"mod\" })",
"(DocTest, NavigationTarget { file_id: FileId(0), full_range: 90..146, name: \"struct\", container_name: \"mod\" })",
"(DocTest, NavigationTarget { file_id: FileId(0), full_range: 152..266, focus_range: 189..205, name: \"impl\", kind: Impl })",
"(DocTest, NavigationTarget { file_id: FileId(0), full_range: 216..260, name: \"r#fn\" })",
"(DocTest, NavigationTarget { file_id: FileId(0), full_range: 323..367, name: \"r#fn\" })",
"(DocTest, NavigationTarget { file_id: FileId(0), full_range: 216..260, name: \"fn\" })",
"(DocTest, NavigationTarget { file_id: FileId(0), full_range: 323..367, name: \"fn\" })",
"(DocTest, NavigationTarget { file_id: FileId(0), full_range: 401..459, focus_range: 445..456, name: \"impl\", kind: Impl })",
]
"#]],

View File

@ -526,6 +526,9 @@ define_symbols! {
arbitrary_self_types_pointers,
supertrait_item_shadowing,
hash,
partial_cmp,
cmp,
CoerceUnsized,
DispatchFromDyn,
define_opaque,
}

View File

@ -693,21 +693,24 @@ impl flags::AnalysisStats {
let mut sw = self.stop_watch();
let mut all = 0;
let mut fail = 0;
for &body_id in bodies {
for &body in bodies {
bar.set_message(move || {
format!("mir lowering: {}", full_name(db, body_id, body_id.module(db)))
format!("mir lowering: {}", full_name(db, body, body.module(db)))
});
bar.inc(1);
if matches!(body_id, DefWithBody::Variant(_)) {
if matches!(body, DefWithBody::Variant(_)) {
continue;
}
let module = body_id.module(db);
if !self.should_process(db, body_id, module) {
let module = body.module(db);
if !self.should_process(db, body, module) {
continue;
}
all += 1;
let Err(e) = db.mir_body(body_id.into()) else {
let Ok(body_id) = body.try_into() else {
continue;
};
let Err(e) = db.mir_body(body_id) else {
continue;
};
if verbosity.is_spammy() {
@ -716,7 +719,7 @@ impl flags::AnalysisStats {
.into_iter()
.rev()
.filter_map(|it| it.name(db))
.chain(Some(body_id.name(db).unwrap_or_else(Name::missing)))
.chain(Some(body.name(db).unwrap_or_else(Name::missing)))
.map(|it| it.display(db, Edition::LATEST).to_string())
.join("::");
bar.println(format!("Mir body for {full_name} failed due {e:?}"));
@ -747,11 +750,12 @@ impl flags::AnalysisStats {
if self.parallel {
let mut inference_sw = self.stop_watch();
let bodies = bodies.iter().filter_map(|&body| body.try_into().ok()).collect::<Vec<_>>();
bodies
.par_iter()
.map_with(db.clone(), |snap, &body| {
snap.body(body.into());
InferenceResult::for_body(snap, body.into());
snap.body(body);
InferenceResult::for_body(snap, body);
})
.count();
eprintln!("{:<20} {}", "Parallel Inference:", inference_sw.elapsed());
@ -769,6 +773,7 @@ impl flags::AnalysisStats {
let mut num_pat_type_mismatches = 0;
let mut panics = 0;
for &body_id in bodies {
let Ok(body_def_id) = body_id.try_into() else { continue };
let name = body_id.name(db).unwrap_or_else(Name::missing);
let module = body_id.module(db);
let display_target = module.krate(db).to_display_target(db);
@ -807,9 +812,9 @@ impl flags::AnalysisStats {
bar.println(msg());
}
bar.set_message(msg);
let body = db.body(body_id.into());
let body = db.body(body_def_id);
let inference_result =
catch_unwind(AssertUnwindSafe(|| InferenceResult::for_body(db, body_id.into())));
catch_unwind(AssertUnwindSafe(|| InferenceResult::for_body(db, body_def_id)));
let inference_result = match inference_result {
Ok(inference_result) => inference_result,
Err(p) => {
@ -826,7 +831,7 @@ impl flags::AnalysisStats {
}
};
// This query is LRU'd, so actually calling it will skew the timing results.
let sm = || db.body_with_source_map(body_id.into()).1;
let sm = || db.body_with_source_map(body_def_id).1;
// region:expressions
let (previous_exprs, previous_unknown, previous_partially_unknown) =
@ -1081,6 +1086,7 @@ impl flags::AnalysisStats {
let mut sw = self.stop_watch();
bar.tick();
for &body_id in bodies {
let Ok(body_def_id) = body_id.try_into() else { continue };
let module = body_id.module(db);
if !self.should_process(db, body_id, module) {
continue;
@ -1114,7 +1120,7 @@ impl flags::AnalysisStats {
bar.println(msg());
}
bar.set_message(msg);
db.body(body_id.into());
db.body(body_def_id);
bar.inc(1);
}