fix: Fix impl Trait<Self>
causing stackoverflows
This commit is contained in:
parent
2cbc2841d8
commit
1915980031
@ -22,6 +22,7 @@ smol_str.opt-level = 3
|
|||||||
text-size.opt-level = 3
|
text-size.opt-level = 3
|
||||||
# This speeds up `cargo xtask dist`.
|
# This speeds up `cargo xtask dist`.
|
||||||
miniz_oxide.opt-level = 3
|
miniz_oxide.opt-level = 3
|
||||||
|
salsa.opt-level = 3
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
incremental = true
|
incremental = true
|
||||||
|
@ -28,7 +28,7 @@
|
|||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use la_arena::ArenaMap;
|
use la_arena::ArenaMap;
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
use stdx::never;
|
use stdx::{never, IsNoneOr};
|
||||||
use triomphe::Arc;
|
use triomphe::Arc;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
@ -41,9 +41,9 @@
|
|||||||
mir::pad16,
|
mir::pad16,
|
||||||
primitive, to_assoc_type_id,
|
primitive, to_assoc_type_id,
|
||||||
utils::{self, detect_variant_from_bytes, generics, ClosureSubst},
|
utils::{self, detect_variant_from_bytes, generics, ClosureSubst},
|
||||||
AdtId, AliasEq, AliasTy, Binders, CallableDefId, CallableSig, Const, ConstScalar, ConstValue,
|
AdtId, AliasEq, AliasTy, Binders, CallableDefId, CallableSig, ConcreteConst, Const,
|
||||||
DomainGoal, FnAbi, GenericArg, GenericArgData, ImplTraitId, Interner, Lifetime, LifetimeData,
|
ConstScalar, ConstValue, DomainGoal, FnAbi, GenericArg, ImplTraitId, Interner, Lifetime,
|
||||||
LifetimeOutlives, MemoryMap, Mutability, OpaqueTy, ProjectionTy, ProjectionTyExt,
|
LifetimeData, LifetimeOutlives, MemoryMap, Mutability, OpaqueTy, ProjectionTy, ProjectionTyExt,
|
||||||
QuantifiedWhereClause, Scalar, Substitution, TraitEnvironment, TraitRef, TraitRefExt, Ty,
|
QuantifiedWhereClause, Scalar, Substitution, TraitEnvironment, TraitRef, TraitRefExt, Ty,
|
||||||
TyExt, WhereClause,
|
TyExt, WhereClause,
|
||||||
};
|
};
|
||||||
@ -60,11 +60,18 @@ impl HirWrite for String {}
|
|||||||
impl HirWrite for fmt::Formatter<'_> {}
|
impl HirWrite for fmt::Formatter<'_> {}
|
||||||
|
|
||||||
pub struct HirFormatter<'a> {
|
pub struct HirFormatter<'a> {
|
||||||
|
/// The database handle
|
||||||
pub db: &'a dyn HirDatabase,
|
pub db: &'a dyn HirDatabase,
|
||||||
|
/// The sink to write into
|
||||||
fmt: &'a mut dyn HirWrite,
|
fmt: &'a mut dyn HirWrite,
|
||||||
|
/// A buffer to intercept writes with, this allows us to track the overall size of the formatted output.
|
||||||
buf: String,
|
buf: String,
|
||||||
|
/// The current size of the formatted output.
|
||||||
curr_size: usize,
|
curr_size: usize,
|
||||||
pub(crate) max_size: Option<usize>,
|
/// Size from which we should truncate the output.
|
||||||
|
max_size: Option<usize>,
|
||||||
|
/// When rendering something that has a concept of "children" (like fields in a struct), this limits
|
||||||
|
/// how many should be rendered.
|
||||||
pub entity_limit: Option<usize>,
|
pub entity_limit: Option<usize>,
|
||||||
omit_verbose_types: bool,
|
omit_verbose_types: bool,
|
||||||
closure_style: ClosureStyle,
|
closure_style: ClosureStyle,
|
||||||
@ -304,7 +311,6 @@ fn allows_opaque(self) -> bool {
|
|||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum DisplaySourceCodeError {
|
pub enum DisplaySourceCodeError {
|
||||||
PathNotFound,
|
PathNotFound,
|
||||||
UnknownType,
|
|
||||||
Coroutine,
|
Coroutine,
|
||||||
OpaqueType,
|
OpaqueType,
|
||||||
}
|
}
|
||||||
@ -418,6 +424,7 @@ fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> {
|
|||||||
let proj_params = &self.substitution.as_slice(Interner)[..proj_params_count];
|
let proj_params = &self.substitution.as_slice(Interner)[..proj_params_count];
|
||||||
if !proj_params.is_empty() {
|
if !proj_params.is_empty() {
|
||||||
write!(f, "<")?;
|
write!(f, "<")?;
|
||||||
|
// FIXME use `hir_fmt_generics` here
|
||||||
f.write_joined(proj_params, ", ")?;
|
f.write_joined(proj_params, ", ")?;
|
||||||
write!(f, ">")?;
|
write!(f, ">")?;
|
||||||
}
|
}
|
||||||
@ -967,6 +974,7 @@ fn hir_fmt(
|
|||||||
.chain(fn_params)
|
.chain(fn_params)
|
||||||
.flatten();
|
.flatten();
|
||||||
write!(f, "<")?;
|
write!(f, "<")?;
|
||||||
|
// FIXME use `hir_fmt_generics` here
|
||||||
f.write_joined(params, ", ")?;
|
f.write_joined(params, ", ")?;
|
||||||
write!(f, ">")?;
|
write!(f, ">")?;
|
||||||
}
|
}
|
||||||
@ -1037,6 +1045,7 @@ fn hir_fmt(
|
|||||||
// FIXME: reconsider the generic args order upon formatting?
|
// FIXME: reconsider the generic args order upon formatting?
|
||||||
if parameters.len(Interner) > 0 {
|
if parameters.len(Interner) > 0 {
|
||||||
write!(f, "<")?;
|
write!(f, "<")?;
|
||||||
|
// FIXME use `hir_fmt_generics` here
|
||||||
f.write_joined(parameters.as_slice(Interner), ", ")?;
|
f.write_joined(parameters.as_slice(Interner), ", ")?;
|
||||||
write!(f, ">")?;
|
write!(f, ">")?;
|
||||||
}
|
}
|
||||||
@ -1287,12 +1296,11 @@ fn hir_fmt(
|
|||||||
}
|
}
|
||||||
TyKind::Error => {
|
TyKind::Error => {
|
||||||
if f.display_target.is_source_code() {
|
if f.display_target.is_source_code() {
|
||||||
return Err(HirDisplayError::DisplaySourceCodeError(
|
f.write_char('_')?;
|
||||||
DisplaySourceCodeError::UnknownType,
|
} else {
|
||||||
));
|
|
||||||
}
|
|
||||||
write!(f, "{{unknown}}")?;
|
write!(f, "{{unknown}}")?;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
TyKind::InferenceVar(..) => write!(f, "_")?,
|
TyKind::InferenceVar(..) => write!(f, "_")?,
|
||||||
TyKind::Coroutine(_, subst) => {
|
TyKind::Coroutine(_, subst) => {
|
||||||
if f.display_target.is_source_code() {
|
if f.display_target.is_source_code() {
|
||||||
@ -1331,99 +1339,91 @@ fn hir_fmt_generics(
|
|||||||
parameters: &Substitution,
|
parameters: &Substitution,
|
||||||
generic_def: Option<hir_def::GenericDefId>,
|
generic_def: Option<hir_def::GenericDefId>,
|
||||||
) -> Result<(), HirDisplayError> {
|
) -> Result<(), HirDisplayError> {
|
||||||
let db = f.db;
|
if parameters.is_empty(Interner) {
|
||||||
if parameters.len(Interner) > 0 {
|
return Ok(());
|
||||||
use std::cmp::Ordering;
|
|
||||||
let param_compare =
|
|
||||||
|a: &GenericArg, b: &GenericArg| match (a.data(Interner), b.data(Interner)) {
|
|
||||||
(crate::GenericArgData::Lifetime(_), crate::GenericArgData::Lifetime(_)) => {
|
|
||||||
Ordering::Equal
|
|
||||||
}
|
}
|
||||||
(crate::GenericArgData::Lifetime(_), _) => Ordering::Less,
|
|
||||||
(_, crate::GenericArgData::Lifetime(_)) => Ordering::Less,
|
let parameters_to_write =
|
||||||
(_, _) => Ordering::Equal,
|
generic_args_sans_defaults(f, generic_def, parameters.as_slice(Interner));
|
||||||
};
|
if !parameters_to_write.is_empty() {
|
||||||
let parameters_to_write = if f.display_target.is_source_code() || f.omit_verbose_types() {
|
write!(f, "<")?;
|
||||||
|
hir_fmt_generic_arguments(f, parameters_to_write)?;
|
||||||
|
write!(f, ">")?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn generic_args_sans_defaults<'ga>(
|
||||||
|
f: &mut HirFormatter<'_>,
|
||||||
|
generic_def: Option<hir_def::GenericDefId>,
|
||||||
|
parameters: &'ga [GenericArg],
|
||||||
|
) -> &'ga [GenericArg] {
|
||||||
|
if f.display_target.is_source_code() || f.omit_verbose_types() {
|
||||||
match generic_def
|
match generic_def
|
||||||
.map(|generic_def_id| db.generic_defaults(generic_def_id))
|
.map(|generic_def_id| f.db.generic_defaults(generic_def_id))
|
||||||
.filter(|defaults| !defaults.is_empty())
|
.filter(|it| !it.is_empty())
|
||||||
{
|
{
|
||||||
None => parameters.as_slice(Interner),
|
None => parameters,
|
||||||
Some(default_parameters) => {
|
Some(default_parameters) => {
|
||||||
fn should_show(
|
let should_show = |arg: &GenericArg, i: usize| {
|
||||||
parameter: &GenericArg,
|
let is_err = |arg: &GenericArg| match arg.data(Interner) {
|
||||||
default_parameters: &[Binders<GenericArg>],
|
chalk_ir::GenericArgData::Lifetime(it) => {
|
||||||
i: usize,
|
*it.data(Interner) == LifetimeData::Error
|
||||||
parameters: &Substitution,
|
|
||||||
) -> bool {
|
|
||||||
if parameter.ty(Interner).map(|it| it.kind(Interner))
|
|
||||||
== Some(&TyKind::Error)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
if let Some(ConstValue::Concrete(c)) =
|
chalk_ir::GenericArgData::Ty(it) => *it.kind(Interner) == TyKind::Error,
|
||||||
parameter.constant(Interner).map(|it| &it.data(Interner).value)
|
chalk_ir::GenericArgData::Const(it) => matches!(
|
||||||
{
|
it.data(Interner).value,
|
||||||
if c.interned == ConstScalar::Unknown {
|
ConstValue::Concrete(ConcreteConst {
|
||||||
return true;
|
interned: ConstScalar::Unknown,
|
||||||
}
|
..
|
||||||
}
|
})
|
||||||
if let Some(crate::LifetimeData::Static | crate::LifetimeData::Error) =
|
),
|
||||||
parameter.lifetime(Interner).map(|it| it.data(Interner))
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
let default_parameter = match default_parameters.get(i) {
|
|
||||||
Some(it) => it,
|
|
||||||
None => return true,
|
|
||||||
};
|
};
|
||||||
let actual_default =
|
// if the arg is error like, render it to inform the user
|
||||||
default_parameter.clone().substitute(Interner, ¶meters);
|
if is_err(arg) {
|
||||||
parameter != &actual_default
|
return true;
|
||||||
}
|
}
|
||||||
|
// otherwise, if the arg is equal to the param default, hide it (unless the
|
||||||
|
// default is an error which can happen for the trait Self type)
|
||||||
|
default_parameters.get(i).is_none_or(|default_parameter| {
|
||||||
|
// !is_err(default_parameter.skip_binders())
|
||||||
|
// &&
|
||||||
|
arg != &default_parameter.clone().substitute(Interner, ¶meters)
|
||||||
|
})
|
||||||
|
};
|
||||||
let mut default_from = 0;
|
let mut default_from = 0;
|
||||||
for (i, parameter) in parameters.iter(Interner).enumerate() {
|
for (i, parameter) in parameters.iter().enumerate() {
|
||||||
if should_show(parameter, &default_parameters, i, parameters) {
|
if should_show(parameter, i) {
|
||||||
default_from = i + 1;
|
default_from = i + 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
¶meters.as_slice(Interner)[0..default_from]
|
¶meters[0..default_from]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
parameters.as_slice(Interner)
|
parameters
|
||||||
};
|
}
|
||||||
//FIXME: Should handle the ordering of lifetimes when creating substitutions
|
}
|
||||||
let mut parameters_to_write = parameters_to_write.to_vec();
|
|
||||||
parameters_to_write.sort_by(param_compare);
|
fn hir_fmt_generic_arguments(
|
||||||
if !parameters_to_write.is_empty() {
|
f: &mut HirFormatter<'_>,
|
||||||
write!(f, "<")?;
|
parameters: &[GenericArg],
|
||||||
|
) -> Result<(), HirDisplayError> {
|
||||||
let mut first = true;
|
let mut first = true;
|
||||||
for generic_arg in parameters_to_write {
|
let lifetime_offset = parameters.iter().position(|arg| arg.lifetime(Interner).is_some());
|
||||||
|
|
||||||
|
let (ty_or_const, lifetimes) = match lifetime_offset {
|
||||||
|
Some(offset) => parameters.split_at(offset),
|
||||||
|
None => (parameters, &[][..]),
|
||||||
|
};
|
||||||
|
for generic_arg in lifetimes.iter().chain(ty_or_const) {
|
||||||
if !first {
|
if !first {
|
||||||
write!(f, ", ")?;
|
write!(f, ", ")?;
|
||||||
}
|
}
|
||||||
first = false;
|
first = false;
|
||||||
if f.display_target.is_source_code() {
|
|
||||||
match generic_arg.data(Interner) {
|
|
||||||
GenericArgData::Lifetime(l)
|
|
||||||
if matches!(l.data(Interner), LifetimeData::Error) =>
|
|
||||||
{
|
|
||||||
write!(f, "'_")
|
|
||||||
}
|
|
||||||
GenericArgData::Ty(t) if matches!(t.kind(Interner), TyKind::Error) => {
|
|
||||||
write!(f, "_")
|
|
||||||
}
|
|
||||||
_ => generic_arg.hir_fmt(f),
|
|
||||||
}?
|
|
||||||
} else {
|
|
||||||
generic_arg.hir_fmt(f)?;
|
generic_arg.hir_fmt(f)?;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
write!(f, ">")?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1544,23 +1544,32 @@ fn write_bounds_like_dyn_trait(
|
|||||||
f.start_location_link(trait_.into());
|
f.start_location_link(trait_.into());
|
||||||
write!(f, "{}", f.db.trait_data(trait_).name.display(f.db.upcast()))?;
|
write!(f, "{}", f.db.trait_data(trait_).name.display(f.db.upcast()))?;
|
||||||
f.end_location_link();
|
f.end_location_link();
|
||||||
if let [_, params @ ..] = trait_ref.substitution.as_slice(Interner) {
|
|
||||||
if is_fn_trait {
|
if is_fn_trait {
|
||||||
|
if let [_self, params @ ..] = trait_ref.substitution.as_slice(Interner) {
|
||||||
if let Some(args) =
|
if let Some(args) =
|
||||||
params.first().and_then(|it| it.assert_ty_ref(Interner).as_tuple())
|
params.first().and_then(|it| it.assert_ty_ref(Interner).as_tuple())
|
||||||
{
|
{
|
||||||
write!(f, "(")?;
|
write!(f, "(")?;
|
||||||
f.write_joined(args.as_slice(Interner), ", ")?;
|
hir_fmt_generic_arguments(f, args.as_slice(Interner))?;
|
||||||
write!(f, ")")?;
|
write!(f, ")")?;
|
||||||
}
|
}
|
||||||
} else if !params.is_empty() {
|
}
|
||||||
|
} else {
|
||||||
|
let params = generic_args_sans_defaults(
|
||||||
|
f,
|
||||||
|
Some(trait_.into()),
|
||||||
|
trait_ref.substitution.as_slice(Interner),
|
||||||
|
);
|
||||||
|
if let [_self, params @ ..] = params {
|
||||||
|
if !params.is_empty() {
|
||||||
write!(f, "<")?;
|
write!(f, "<")?;
|
||||||
f.write_joined(params, ", ")?;
|
hir_fmt_generic_arguments(f, params)?;
|
||||||
// there might be assoc type bindings, so we leave the angle brackets open
|
// there might be assoc type bindings, so we leave the angle brackets open
|
||||||
angle_open = true;
|
angle_open = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
WhereClause::TypeOutlives(to) if Either::Left(&to.ty) == this => {
|
WhereClause::TypeOutlives(to) if Either::Left(&to.ty) == this => {
|
||||||
if !is_fn_trait && angle_open {
|
if !is_fn_trait && angle_open {
|
||||||
write!(f, ">")?;
|
write!(f, ">")?;
|
||||||
@ -1609,9 +1618,9 @@ fn write_bounds_like_dyn_trait(
|
|||||||
let proj_arg_count = generics(f.db.upcast(), assoc_ty_id.into()).len_self();
|
let proj_arg_count = generics(f.db.upcast(), assoc_ty_id.into()).len_self();
|
||||||
if proj_arg_count > 0 {
|
if proj_arg_count > 0 {
|
||||||
write!(f, "<")?;
|
write!(f, "<")?;
|
||||||
f.write_joined(
|
hir_fmt_generic_arguments(
|
||||||
|
f,
|
||||||
&proj.substitution.as_slice(Interner)[..proj_arg_count],
|
&proj.substitution.as_slice(Interner)[..proj_arg_count],
|
||||||
", ",
|
|
||||||
)?;
|
)?;
|
||||||
write!(f, ">")?;
|
write!(f, ">")?;
|
||||||
}
|
}
|
||||||
@ -1670,6 +1679,7 @@ fn fmt_trait_ref(
|
|||||||
f.end_location_link();
|
f.end_location_link();
|
||||||
if tr.substitution.len(Interner) > 1 {
|
if tr.substitution.len(Interner) > 1 {
|
||||||
write!(f, "<")?;
|
write!(f, "<")?;
|
||||||
|
// FIXME use `hir_fmt_generics` here
|
||||||
f.write_joined(&tr.substitution.as_slice(Interner)[1..], ", ")?;
|
f.write_joined(&tr.substitution.as_slice(Interner)[1..], ", ")?;
|
||||||
write!(f, ">")?;
|
write!(f, ">")?;
|
||||||
}
|
}
|
||||||
@ -1728,8 +1738,6 @@ fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> {
|
|||||||
impl HirDisplay for LifetimeData {
|
impl HirDisplay for LifetimeData {
|
||||||
fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> {
|
fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> {
|
||||||
match self {
|
match self {
|
||||||
LifetimeData::BoundVar(idx) => idx.hir_fmt(f),
|
|
||||||
LifetimeData::InferenceVar(_) => write!(f, "_"),
|
|
||||||
LifetimeData::Placeholder(idx) => {
|
LifetimeData::Placeholder(idx) => {
|
||||||
let id = lt_from_placeholder_idx(f.db, *idx);
|
let id = lt_from_placeholder_idx(f.db, *idx);
|
||||||
let generics = generics(f.db.upcast(), id.parent);
|
let generics = generics(f.db.upcast(), id.parent);
|
||||||
@ -1737,6 +1745,9 @@ fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> {
|
|||||||
write!(f, "{}", param_data.name.display(f.db.upcast()))?;
|
write!(f, "{}", param_data.name.display(f.db.upcast()))?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
_ if f.display_target.is_source_code() => write!(f, "'_"),
|
||||||
|
LifetimeData::BoundVar(idx) => idx.hir_fmt(f),
|
||||||
|
LifetimeData::InferenceVar(_) => write!(f, "_"),
|
||||||
LifetimeData::Static => write!(f, "'static"),
|
LifetimeData::Static => write!(f, "'static"),
|
||||||
LifetimeData::Error => write!(f, "'{{error}}"),
|
LifetimeData::Error => write!(f, "'{{error}}"),
|
||||||
LifetimeData::Erased => Ok(()),
|
LifetimeData::Erased => Ok(()),
|
||||||
|
@ -56,7 +56,6 @@
|
|||||||
use chalk_ir::{
|
use chalk_ir::{
|
||||||
fold::{Shift, TypeFoldable},
|
fold::{Shift, TypeFoldable},
|
||||||
interner::HasInterner,
|
interner::HasInterner,
|
||||||
visit::{TypeSuperVisitable, TypeVisitable, TypeVisitor},
|
|
||||||
NoSolution,
|
NoSolution,
|
||||||
};
|
};
|
||||||
use either::Either;
|
use either::Either;
|
||||||
@ -98,7 +97,9 @@
|
|||||||
pub use utils::{all_super_traits, is_fn_unsafe_to_call};
|
pub use utils::{all_super_traits, is_fn_unsafe_to_call};
|
||||||
|
|
||||||
pub use chalk_ir::{
|
pub use chalk_ir::{
|
||||||
cast::Cast, AdtId, BoundVar, DebruijnIndex, Mutability, Safety, Scalar, TyVariableKind,
|
cast::Cast,
|
||||||
|
visit::{TypeSuperVisitable, TypeVisitable, TypeVisitor},
|
||||||
|
AdtId, BoundVar, DebruijnIndex, Mutability, Safety, Scalar, TyVariableKind,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub type ForeignDefId = chalk_ir::ForeignDefId<Interner>;
|
pub type ForeignDefId = chalk_ir::ForeignDefId<Interner>;
|
||||||
|
@ -85,7 +85,7 @@ fn render_dyn_for_ty() {
|
|||||||
trait Foo<'a> {}
|
trait Foo<'a> {}
|
||||||
|
|
||||||
fn foo(foo: &dyn for<'a> Foo<'a>) {}
|
fn foo(foo: &dyn for<'a> Foo<'a>) {}
|
||||||
// ^^^ &dyn Foo<'{error}>
|
// ^^^ &dyn Foo<'_>
|
||||||
"#,
|
"#,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -4711,14 +4711,16 @@ fn walk_bounds(
|
|||||||
if let WhereClause::Implemented(trait_ref) = pred.skip_binders() {
|
if let WhereClause::Implemented(trait_ref) = pred.skip_binders() {
|
||||||
cb(type_.clone());
|
cb(type_.clone());
|
||||||
// skip the self type. it's likely the type we just got the bounds from
|
// skip the self type. it's likely the type we just got the bounds from
|
||||||
|
if let [self_ty, params @ ..] = trait_ref.substitution.as_slice(Interner) {
|
||||||
for ty in
|
for ty in
|
||||||
trait_ref.substitution.iter(Interner).skip(1).filter_map(|a| a.ty(Interner))
|
params.iter().filter(|&ty| ty != self_ty).filter_map(|a| a.ty(Interner))
|
||||||
{
|
{
|
||||||
walk_type(db, &type_.derived(ty.clone()), cb);
|
walk_type(db, &type_.derived(ty.clone()), cb);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn walk_type(db: &dyn HirDatabase, type_: &Type, cb: &mut impl FnMut(Type)) {
|
fn walk_type(db: &dyn HirDatabase, type_: &Type, cb: &mut impl FnMut(Type)) {
|
||||||
let ty = type_.ty.strip_references();
|
let ty = type_.ty.strip_references();
|
||||||
|
@ -59,10 +59,7 @@ pub(crate) fn promote_local_to_const(acc: &mut Assists, ctx: &AssistContext<'_>)
|
|||||||
|
|
||||||
let ty = match ty.display_source_code(ctx.db(), module.into(), false) {
|
let ty = match ty.display_source_code(ctx.db(), module.into(), false) {
|
||||||
Ok(ty) => ty,
|
Ok(ty) => ty,
|
||||||
Err(_) => {
|
Err(_) => return None,
|
||||||
cov_mark::hit!(promote_local_not_applicable_if_ty_not_inferred);
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let initializer = let_stmt.initializer()?;
|
let initializer = let_stmt.initializer()?;
|
||||||
@ -315,13 +312,17 @@ fn foo() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn not_applicable_unknown_ty() {
|
fn not_applicable_unknown_ty() {
|
||||||
cov_mark::check!(promote_local_not_applicable_if_ty_not_inferred);
|
check_assist(
|
||||||
check_assist_not_applicable(
|
|
||||||
promote_local_to_const,
|
promote_local_to_const,
|
||||||
r"
|
r"
|
||||||
fn foo() {
|
fn foo() {
|
||||||
let x$0 = bar();
|
let x$0 = bar();
|
||||||
}
|
}
|
||||||
|
",
|
||||||
|
r"
|
||||||
|
fn foo() {
|
||||||
|
const $0X: _ = bar();
|
||||||
|
}
|
||||||
",
|
",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -226,7 +226,7 @@ pub(crate) fn complete_ascribed_type(
|
|||||||
if !path_ctx.is_trivial_path() {
|
if !path_ctx.is_trivial_path() {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
let x = match ascription {
|
let ty = match ascription {
|
||||||
TypeAscriptionTarget::Let(pat) | TypeAscriptionTarget::FnParam(pat) => {
|
TypeAscriptionTarget::Let(pat) | TypeAscriptionTarget::FnParam(pat) => {
|
||||||
ctx.sema.type_of_pat(pat.as_ref()?)
|
ctx.sema.type_of_pat(pat.as_ref()?)
|
||||||
}
|
}
|
||||||
@ -235,7 +235,9 @@ pub(crate) fn complete_ascribed_type(
|
|||||||
}
|
}
|
||||||
}?
|
}?
|
||||||
.adjusted();
|
.adjusted();
|
||||||
let ty_string = x.display_source_code(ctx.db, ctx.module.into(), true).ok()?;
|
if !ty.is_unknown() {
|
||||||
|
let ty_string = ty.display_source_code(ctx.db, ctx.module.into(), true).ok()?;
|
||||||
acc.add(render_type_inference(ty_string, ctx));
|
acc.add(render_type_inference(ty_string, ctx));
|
||||||
|
}
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
//! See [`Label`]
|
//! See [`Label`]
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
|
use stdx::always;
|
||||||
|
|
||||||
/// A type to specify UI label, like an entry in the list of assists. Enforces
|
/// A type to specify UI label, like an entry in the list of assists. Enforces
|
||||||
/// proper casing:
|
/// proper casing:
|
||||||
///
|
///
|
||||||
@ -30,7 +32,7 @@ fn from(label: Label) -> String {
|
|||||||
|
|
||||||
impl Label {
|
impl Label {
|
||||||
pub fn new(label: String) -> Label {
|
pub fn new(label: String) -> Label {
|
||||||
assert!(label.starts_with(char::is_uppercase) && !label.ends_with('.'));
|
always!(label.starts_with(char::is_uppercase) && !label.ends_with('.'));
|
||||||
Label(label)
|
Label(label)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -81,8 +81,9 @@ fn field_fix(ctx: &DiagnosticsContext<'_>, d: &hir::UnresolvedField) -> Option<A
|
|||||||
let adt = d.receiver.strip_references().as_adt()?;
|
let adt = d.receiver.strip_references().as_adt()?;
|
||||||
let target_module = adt.module(ctx.sema.db);
|
let target_module = adt.module(ctx.sema.db);
|
||||||
|
|
||||||
let suggested_type =
|
let suggested_type = if let Some(new_field_type) =
|
||||||
if let Some(new_field_type) = ctx.sema.type_of_expr(&expr).map(|v| v.adjusted()) {
|
ctx.sema.type_of_expr(&expr).map(|v| v.adjusted()).filter(|it| !it.is_unknown())
|
||||||
|
{
|
||||||
let display =
|
let display =
|
||||||
new_field_type.display_source_code(ctx.sema.db, target_module.into(), false).ok();
|
new_field_type.display_source_code(ctx.sema.db, target_module.into(), false).ok();
|
||||||
make::ty(display.as_deref().unwrap_or("()"))
|
make::ty(display.as_deref().unwrap_or("()"))
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
|
use hir::Name;
|
||||||
use ide_db::{
|
use ide_db::{
|
||||||
assists::{Assist, AssistId, AssistKind},
|
assists::{Assist, AssistId, AssistKind},
|
||||||
base_db::FileRange,
|
base_db::FileRange,
|
||||||
label::Label,
|
label::Label,
|
||||||
source_change::SourceChange,
|
source_change::SourceChange,
|
||||||
|
RootDatabase,
|
||||||
};
|
};
|
||||||
use text_edit::TextEdit;
|
use text_edit::TextEdit;
|
||||||
|
|
||||||
@ -21,7 +23,7 @@ pub(crate) fn unused_variables(
|
|||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
let diagnostic_range = ctx.sema.diagnostics_display_range(ast);
|
let diagnostic_range = ctx.sema.diagnostics_display_range(ast);
|
||||||
let var_name = d.local.primary_source(ctx.sema.db).syntax().to_string();
|
let var_name = d.local.name(ctx.sema.db);
|
||||||
Some(
|
Some(
|
||||||
Diagnostic::new_with_syntax_node_ptr(
|
Diagnostic::new_with_syntax_node_ptr(
|
||||||
ctx,
|
ctx,
|
||||||
@ -29,23 +31,32 @@ pub(crate) fn unused_variables(
|
|||||||
"unused variable",
|
"unused variable",
|
||||||
ast,
|
ast,
|
||||||
)
|
)
|
||||||
.with_fixes(fixes(&var_name, diagnostic_range, ast.file_id.is_macro()))
|
.with_fixes(fixes(ctx.sema.db, var_name, diagnostic_range, ast.file_id.is_macro()))
|
||||||
.experimental(),
|
.experimental(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fixes(var_name: &String, diagnostic_range: FileRange, is_in_marco: bool) -> Option<Vec<Assist>> {
|
fn fixes(
|
||||||
|
db: &RootDatabase,
|
||||||
|
var_name: Name,
|
||||||
|
diagnostic_range: FileRange,
|
||||||
|
is_in_marco: bool,
|
||||||
|
) -> Option<Vec<Assist>> {
|
||||||
if is_in_marco {
|
if is_in_marco {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
Some(vec![Assist {
|
Some(vec![Assist {
|
||||||
id: AssistId("unscore_unused_variable_name", AssistKind::QuickFix),
|
id: AssistId("unscore_unused_variable_name", AssistKind::QuickFix),
|
||||||
label: Label::new(format!("Rename unused {} to _{}", var_name, var_name)),
|
label: Label::new(format!(
|
||||||
|
"Rename unused {} to _{}",
|
||||||
|
var_name.display(db),
|
||||||
|
var_name.display(db)
|
||||||
|
)),
|
||||||
group: None,
|
group: None,
|
||||||
target: diagnostic_range.range,
|
target: diagnostic_range.range,
|
||||||
source_change: Some(SourceChange::from_text_edit(
|
source_change: Some(SourceChange::from_text_edit(
|
||||||
diagnostic_range.file_id,
|
diagnostic_range.file_id,
|
||||||
TextEdit::replace(diagnostic_range.range, format!("_{}", var_name)),
|
TextEdit::replace(diagnostic_range.range, format!("_{}", var_name.display(db))),
|
||||||
)),
|
)),
|
||||||
trigger_signature_help: false,
|
trigger_signature_help: false,
|
||||||
}])
|
}])
|
||||||
|
@ -3301,12 +3301,12 @@ fn foo(ar$0g: &impl Foo<S>) {}
|
|||||||
fn test_hover_dyn_return_has_goto_type_action() {
|
fn test_hover_dyn_return_has_goto_type_action() {
|
||||||
check_actions(
|
check_actions(
|
||||||
r#"
|
r#"
|
||||||
trait Foo {}
|
trait Foo<T> {}
|
||||||
struct S;
|
struct S;
|
||||||
impl Foo for S {}
|
impl Foo<S> for S {}
|
||||||
|
|
||||||
struct B<T>{}
|
struct B<T>{}
|
||||||
fn foo() -> B<dyn Foo> {}
|
fn foo() -> B<dyn Foo<S>> {}
|
||||||
|
|
||||||
fn main() { let s$0t = foo(); }
|
fn main() { let s$0t = foo(); }
|
||||||
"#,
|
"#,
|
||||||
@ -3320,8 +3320,8 @@ fn foo() -> B<dyn Foo> {}
|
|||||||
file_id: FileId(
|
file_id: FileId(
|
||||||
0,
|
0,
|
||||||
),
|
),
|
||||||
full_range: 42..55,
|
full_range: 48..61,
|
||||||
focus_range: 49..50,
|
focus_range: 55..56,
|
||||||
name: "B",
|
name: "B",
|
||||||
kind: Struct,
|
kind: Struct,
|
||||||
description: "struct B<T>",
|
description: "struct B<T>",
|
||||||
@ -3333,11 +3333,24 @@ fn foo() -> B<dyn Foo> {}
|
|||||||
file_id: FileId(
|
file_id: FileId(
|
||||||
0,
|
0,
|
||||||
),
|
),
|
||||||
full_range: 0..12,
|
full_range: 0..15,
|
||||||
focus_range: 6..9,
|
focus_range: 6..9,
|
||||||
name: "Foo",
|
name: "Foo",
|
||||||
kind: Trait,
|
kind: Trait,
|
||||||
description: "trait Foo",
|
description: "trait Foo<T>",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
HoverGotoTypeData {
|
||||||
|
mod_path: "test::S",
|
||||||
|
nav: NavigationTarget {
|
||||||
|
file_id: FileId(
|
||||||
|
0,
|
||||||
|
),
|
||||||
|
full_range: 16..25,
|
||||||
|
focus_range: 23..24,
|
||||||
|
name: "S",
|
||||||
|
kind: Struct,
|
||||||
|
description: "struct S",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@ -4260,6 +4273,10 @@ fn foo<T$0: ?Sized + Sized + Sized>() {}
|
|||||||
```
|
```
|
||||||
"#]],
|
"#]],
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn mixed2() {
|
||||||
check(
|
check(
|
||||||
r#"
|
r#"
|
||||||
//- minicore: sized
|
//- minicore: sized
|
||||||
@ -7924,3 +7941,38 @@ struct Pedro<'a>
|
|||||||
"#]],
|
"#]],
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn hover_impl_trait_arg_self() {
|
||||||
|
check(
|
||||||
|
r#"
|
||||||
|
trait T<Rhs = Self> {}
|
||||||
|
fn main(a$0: impl T) {}
|
||||||
|
"#,
|
||||||
|
expect![[r#"
|
||||||
|
*a*
|
||||||
|
|
||||||
|
```rust
|
||||||
|
a: impl T + ?Sized
|
||||||
|
```
|
||||||
|
"#]],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn hover_struct_default_arg_self() {
|
||||||
|
check(
|
||||||
|
r#"
|
||||||
|
struct T<Rhs = Self> {}
|
||||||
|
fn main(a$0: T) {}
|
||||||
|
"#,
|
||||||
|
expect![[r#"
|
||||||
|
*a*
|
||||||
|
|
||||||
|
```rust
|
||||||
|
// size = 0, align = 1
|
||||||
|
a: T
|
||||||
|
```
|
||||||
|
"#]],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user