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
|
||||
# This speeds up `cargo xtask dist`.
|
||||
miniz_oxide.opt-level = 3
|
||||
salsa.opt-level = 3
|
||||
|
||||
[profile.release]
|
||||
incremental = true
|
||||
|
@ -28,7 +28,7 @@
|
||||
use itertools::Itertools;
|
||||
use la_arena::ArenaMap;
|
||||
use smallvec::SmallVec;
|
||||
use stdx::never;
|
||||
use stdx::{never, IsNoneOr};
|
||||
use triomphe::Arc;
|
||||
|
||||
use crate::{
|
||||
@ -41,9 +41,9 @@
|
||||
mir::pad16,
|
||||
primitive, to_assoc_type_id,
|
||||
utils::{self, detect_variant_from_bytes, generics, ClosureSubst},
|
||||
AdtId, AliasEq, AliasTy, Binders, CallableDefId, CallableSig, Const, ConstScalar, ConstValue,
|
||||
DomainGoal, FnAbi, GenericArg, GenericArgData, ImplTraitId, Interner, Lifetime, LifetimeData,
|
||||
LifetimeOutlives, MemoryMap, Mutability, OpaqueTy, ProjectionTy, ProjectionTyExt,
|
||||
AdtId, AliasEq, AliasTy, Binders, CallableDefId, CallableSig, ConcreteConst, Const,
|
||||
ConstScalar, ConstValue, DomainGoal, FnAbi, GenericArg, ImplTraitId, Interner, Lifetime,
|
||||
LifetimeData, LifetimeOutlives, MemoryMap, Mutability, OpaqueTy, ProjectionTy, ProjectionTyExt,
|
||||
QuantifiedWhereClause, Scalar, Substitution, TraitEnvironment, TraitRef, TraitRefExt, Ty,
|
||||
TyExt, WhereClause,
|
||||
};
|
||||
@ -60,11 +60,18 @@ impl HirWrite for String {}
|
||||
impl HirWrite for fmt::Formatter<'_> {}
|
||||
|
||||
pub struct HirFormatter<'a> {
|
||||
/// The database handle
|
||||
pub db: &'a dyn HirDatabase,
|
||||
/// The sink to write into
|
||||
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,
|
||||
/// The current size of the formatted output.
|
||||
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>,
|
||||
omit_verbose_types: bool,
|
||||
closure_style: ClosureStyle,
|
||||
@ -304,7 +311,6 @@ fn allows_opaque(self) -> bool {
|
||||
#[derive(Debug)]
|
||||
pub enum DisplaySourceCodeError {
|
||||
PathNotFound,
|
||||
UnknownType,
|
||||
Coroutine,
|
||||
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];
|
||||
if !proj_params.is_empty() {
|
||||
write!(f, "<")?;
|
||||
// FIXME use `hir_fmt_generics` here
|
||||
f.write_joined(proj_params, ", ")?;
|
||||
write!(f, ">")?;
|
||||
}
|
||||
@ -967,6 +974,7 @@ fn hir_fmt(
|
||||
.chain(fn_params)
|
||||
.flatten();
|
||||
write!(f, "<")?;
|
||||
// FIXME use `hir_fmt_generics` here
|
||||
f.write_joined(params, ", ")?;
|
||||
write!(f, ">")?;
|
||||
}
|
||||
@ -1037,6 +1045,7 @@ fn hir_fmt(
|
||||
// FIXME: reconsider the generic args order upon formatting?
|
||||
if parameters.len(Interner) > 0 {
|
||||
write!(f, "<")?;
|
||||
// FIXME use `hir_fmt_generics` here
|
||||
f.write_joined(parameters.as_slice(Interner), ", ")?;
|
||||
write!(f, ">")?;
|
||||
}
|
||||
@ -1287,11 +1296,10 @@ fn hir_fmt(
|
||||
}
|
||||
TyKind::Error => {
|
||||
if f.display_target.is_source_code() {
|
||||
return Err(HirDisplayError::DisplaySourceCodeError(
|
||||
DisplaySourceCodeError::UnknownType,
|
||||
));
|
||||
f.write_char('_')?;
|
||||
} else {
|
||||
write!(f, "{{unknown}}")?;
|
||||
}
|
||||
write!(f, "{{unknown}}")?;
|
||||
}
|
||||
TyKind::InferenceVar(..) => write!(f, "_")?,
|
||||
TyKind::Coroutine(_, subst) => {
|
||||
@ -1331,98 +1339,90 @@ fn hir_fmt_generics(
|
||||
parameters: &Substitution,
|
||||
generic_def: Option<hir_def::GenericDefId>,
|
||||
) -> Result<(), HirDisplayError> {
|
||||
let db = f.db;
|
||||
if parameters.len(Interner) > 0 {
|
||||
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,
|
||||
(_, _) => Ordering::Equal,
|
||||
};
|
||||
let parameters_to_write = if f.display_target.is_source_code() || f.omit_verbose_types() {
|
||||
match generic_def
|
||||
.map(|generic_def_id| db.generic_defaults(generic_def_id))
|
||||
.filter(|defaults| !defaults.is_empty())
|
||||
{
|
||||
None => parameters.as_slice(Interner),
|
||||
Some(default_parameters) => {
|
||||
fn should_show(
|
||||
parameter: &GenericArg,
|
||||
default_parameters: &[Binders<GenericArg>],
|
||||
i: usize,
|
||||
parameters: &Substitution,
|
||||
) -> bool {
|
||||
if parameter.ty(Interner).map(|it| it.kind(Interner))
|
||||
== Some(&TyKind::Error)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if let Some(ConstValue::Concrete(c)) =
|
||||
parameter.constant(Interner).map(|it| &it.data(Interner).value)
|
||||
{
|
||||
if c.interned == ConstScalar::Unknown {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
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 =
|
||||
default_parameter.clone().substitute(Interner, ¶meters);
|
||||
parameter != &actual_default
|
||||
}
|
||||
let mut default_from = 0;
|
||||
for (i, parameter) in parameters.iter(Interner).enumerate() {
|
||||
if should_show(parameter, &default_parameters, i, parameters) {
|
||||
default_from = i + 1;
|
||||
}
|
||||
}
|
||||
¶meters.as_slice(Interner)[0..default_from]
|
||||
}
|
||||
}
|
||||
} else {
|
||||
parameters.as_slice(Interner)
|
||||
};
|
||||
//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);
|
||||
if !parameters_to_write.is_empty() {
|
||||
write!(f, "<")?;
|
||||
let mut first = true;
|
||||
for generic_arg in parameters_to_write {
|
||||
if !first {
|
||||
write!(f, ", ")?;
|
||||
}
|
||||
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)?;
|
||||
}
|
||||
}
|
||||
if parameters.is_empty(Interner) {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
write!(f, ">")?;
|
||||
let parameters_to_write =
|
||||
generic_args_sans_defaults(f, generic_def, parameters.as_slice(Interner));
|
||||
if !parameters_to_write.is_empty() {
|
||||
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
|
||||
.map(|generic_def_id| f.db.generic_defaults(generic_def_id))
|
||||
.filter(|it| !it.is_empty())
|
||||
{
|
||||
None => parameters,
|
||||
Some(default_parameters) => {
|
||||
let should_show = |arg: &GenericArg, i: usize| {
|
||||
let is_err = |arg: &GenericArg| match arg.data(Interner) {
|
||||
chalk_ir::GenericArgData::Lifetime(it) => {
|
||||
*it.data(Interner) == LifetimeData::Error
|
||||
}
|
||||
chalk_ir::GenericArgData::Ty(it) => *it.kind(Interner) == TyKind::Error,
|
||||
chalk_ir::GenericArgData::Const(it) => matches!(
|
||||
it.data(Interner).value,
|
||||
ConstValue::Concrete(ConcreteConst {
|
||||
interned: ConstScalar::Unknown,
|
||||
..
|
||||
})
|
||||
),
|
||||
};
|
||||
// if the arg is error like, render it to inform the user
|
||||
if is_err(arg) {
|
||||
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;
|
||||
for (i, parameter) in parameters.iter().enumerate() {
|
||||
if should_show(parameter, i) {
|
||||
default_from = i + 1;
|
||||
}
|
||||
}
|
||||
¶meters[0..default_from]
|
||||
}
|
||||
}
|
||||
} else {
|
||||
parameters
|
||||
}
|
||||
}
|
||||
|
||||
fn hir_fmt_generic_arguments(
|
||||
f: &mut HirFormatter<'_>,
|
||||
parameters: &[GenericArg],
|
||||
) -> Result<(), HirDisplayError> {
|
||||
let mut first = true;
|
||||
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 {
|
||||
write!(f, ", ")?;
|
||||
}
|
||||
first = false;
|
||||
generic_arg.hir_fmt(f)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@ -1544,20 +1544,29 @@ fn write_bounds_like_dyn_trait(
|
||||
f.start_location_link(trait_.into());
|
||||
write!(f, "{}", f.db.trait_data(trait_).name.display(f.db.upcast()))?;
|
||||
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) =
|
||||
params.first().and_then(|it| it.assert_ty_ref(Interner).as_tuple())
|
||||
{
|
||||
write!(f, "(")?;
|
||||
f.write_joined(args.as_slice(Interner), ", ")?;
|
||||
hir_fmt_generic_arguments(f, args.as_slice(Interner))?;
|
||||
write!(f, ")")?;
|
||||
}
|
||||
} else if !params.is_empty() {
|
||||
write!(f, "<")?;
|
||||
f.write_joined(params, ", ")?;
|
||||
// there might be assoc type bindings, so we leave the angle brackets open
|
||||
angle_open = true;
|
||||
}
|
||||
} 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, "<")?;
|
||||
hir_fmt_generic_arguments(f, params)?;
|
||||
// there might be assoc type bindings, so we leave the angle brackets open
|
||||
angle_open = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1609,9 +1618,9 @@ fn write_bounds_like_dyn_trait(
|
||||
let proj_arg_count = generics(f.db.upcast(), assoc_ty_id.into()).len_self();
|
||||
if proj_arg_count > 0 {
|
||||
write!(f, "<")?;
|
||||
f.write_joined(
|
||||
hir_fmt_generic_arguments(
|
||||
f,
|
||||
&proj.substitution.as_slice(Interner)[..proj_arg_count],
|
||||
", ",
|
||||
)?;
|
||||
write!(f, ">")?;
|
||||
}
|
||||
@ -1670,6 +1679,7 @@ fn fmt_trait_ref(
|
||||
f.end_location_link();
|
||||
if tr.substitution.len(Interner) > 1 {
|
||||
write!(f, "<")?;
|
||||
// FIXME use `hir_fmt_generics` here
|
||||
f.write_joined(&tr.substitution.as_slice(Interner)[1..], ", ")?;
|
||||
write!(f, ">")?;
|
||||
}
|
||||
@ -1728,8 +1738,6 @@ fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> {
|
||||
impl HirDisplay for LifetimeData {
|
||||
fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> {
|
||||
match self {
|
||||
LifetimeData::BoundVar(idx) => idx.hir_fmt(f),
|
||||
LifetimeData::InferenceVar(_) => write!(f, "_"),
|
||||
LifetimeData::Placeholder(idx) => {
|
||||
let id = lt_from_placeholder_idx(f.db, *idx);
|
||||
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()))?;
|
||||
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::Error => write!(f, "'{{error}}"),
|
||||
LifetimeData::Erased => Ok(()),
|
||||
|
@ -56,7 +56,6 @@
|
||||
use chalk_ir::{
|
||||
fold::{Shift, TypeFoldable},
|
||||
interner::HasInterner,
|
||||
visit::{TypeSuperVisitable, TypeVisitable, TypeVisitor},
|
||||
NoSolution,
|
||||
};
|
||||
use either::Either;
|
||||
@ -98,7 +97,9 @@
|
||||
pub use utils::{all_super_traits, is_fn_unsafe_to_call};
|
||||
|
||||
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>;
|
||||
|
@ -85,7 +85,7 @@ fn render_dyn_for_ty() {
|
||||
trait Foo<'a> {}
|
||||
|
||||
fn foo(foo: &dyn for<'a> Foo<'a>) {}
|
||||
// ^^^ &dyn Foo<'{error}>
|
||||
// ^^^ &dyn Foo<'_>
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
@ -4711,10 +4711,12 @@ fn walk_bounds(
|
||||
if let WhereClause::Implemented(trait_ref) = pred.skip_binders() {
|
||||
cb(type_.clone());
|
||||
// skip the self type. it's likely the type we just got the bounds from
|
||||
for ty in
|
||||
trait_ref.substitution.iter(Interner).skip(1).filter_map(|a| a.ty(Interner))
|
||||
{
|
||||
walk_type(db, &type_.derived(ty.clone()), cb);
|
||||
if let [self_ty, params @ ..] = trait_ref.substitution.as_slice(Interner) {
|
||||
for ty in
|
||||
params.iter().filter(|&ty| ty != self_ty).filter_map(|a| a.ty(Interner))
|
||||
{
|
||||
walk_type(db, &type_.derived(ty.clone()), cb);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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) {
|
||||
Ok(ty) => ty,
|
||||
Err(_) => {
|
||||
cov_mark::hit!(promote_local_not_applicable_if_ty_not_inferred);
|
||||
return None;
|
||||
}
|
||||
Err(_) => return None,
|
||||
};
|
||||
|
||||
let initializer = let_stmt.initializer()?;
|
||||
@ -315,13 +312,17 @@ fn foo() {
|
||||
|
||||
#[test]
|
||||
fn not_applicable_unknown_ty() {
|
||||
cov_mark::check!(promote_local_not_applicable_if_ty_not_inferred);
|
||||
check_assist_not_applicable(
|
||||
check_assist(
|
||||
promote_local_to_const,
|
||||
r"
|
||||
fn foo() {
|
||||
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() {
|
||||
return None;
|
||||
}
|
||||
let x = match ascription {
|
||||
let ty = match ascription {
|
||||
TypeAscriptionTarget::Let(pat) | TypeAscriptionTarget::FnParam(pat) => {
|
||||
ctx.sema.type_of_pat(pat.as_ref()?)
|
||||
}
|
||||
@ -235,7 +235,9 @@ pub(crate) fn complete_ascribed_type(
|
||||
}
|
||||
}?
|
||||
.adjusted();
|
||||
let ty_string = x.display_source_code(ctx.db, ctx.module.into(), true).ok()?;
|
||||
acc.add(render_type_inference(ty_string, ctx));
|
||||
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));
|
||||
}
|
||||
None
|
||||
}
|
||||
|
@ -1,6 +1,8 @@
|
||||
//! See [`Label`]
|
||||
use std::fmt;
|
||||
|
||||
use stdx::always;
|
||||
|
||||
/// A type to specify UI label, like an entry in the list of assists. Enforces
|
||||
/// proper casing:
|
||||
///
|
||||
@ -30,7 +32,7 @@ fn from(label: Label) -> String {
|
||||
|
||||
impl 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)
|
||||
}
|
||||
}
|
||||
|
@ -81,14 +81,15 @@ fn field_fix(ctx: &DiagnosticsContext<'_>, d: &hir::UnresolvedField) -> Option<A
|
||||
let adt = d.receiver.strip_references().as_adt()?;
|
||||
let target_module = adt.module(ctx.sema.db);
|
||||
|
||||
let suggested_type =
|
||||
if let Some(new_field_type) = ctx.sema.type_of_expr(&expr).map(|v| v.adjusted()) {
|
||||
let display =
|
||||
new_field_type.display_source_code(ctx.sema.db, target_module.into(), false).ok();
|
||||
make::ty(display.as_deref().unwrap_or("()"))
|
||||
} else {
|
||||
make::ty("()")
|
||||
};
|
||||
let suggested_type = if let Some(new_field_type) =
|
||||
ctx.sema.type_of_expr(&expr).map(|v| v.adjusted()).filter(|it| !it.is_unknown())
|
||||
{
|
||||
let display =
|
||||
new_field_type.display_source_code(ctx.sema.db, target_module.into(), false).ok();
|
||||
make::ty(display.as_deref().unwrap_or("()"))
|
||||
} else {
|
||||
make::ty("()")
|
||||
};
|
||||
|
||||
if !is_editable_crate(target_module.krate(), ctx.sema.db) {
|
||||
return None;
|
||||
|
@ -1,8 +1,10 @@
|
||||
use hir::Name;
|
||||
use ide_db::{
|
||||
assists::{Assist, AssistId, AssistKind},
|
||||
base_db::FileRange,
|
||||
label::Label,
|
||||
source_change::SourceChange,
|
||||
RootDatabase,
|
||||
};
|
||||
use text_edit::TextEdit;
|
||||
|
||||
@ -21,7 +23,7 @@ pub(crate) fn unused_variables(
|
||||
return None;
|
||||
}
|
||||
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(
|
||||
Diagnostic::new_with_syntax_node_ptr(
|
||||
ctx,
|
||||
@ -29,23 +31,32 @@ pub(crate) fn unused_variables(
|
||||
"unused variable",
|
||||
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(),
|
||||
)
|
||||
}
|
||||
|
||||
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 {
|
||||
return None;
|
||||
}
|
||||
Some(vec![Assist {
|
||||
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,
|
||||
target: diagnostic_range.range,
|
||||
source_change: Some(SourceChange::from_text_edit(
|
||||
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,
|
||||
}])
|
||||
|
@ -3301,12 +3301,12 @@ fn foo(ar$0g: &impl Foo<S>) {}
|
||||
fn test_hover_dyn_return_has_goto_type_action() {
|
||||
check_actions(
|
||||
r#"
|
||||
trait Foo {}
|
||||
trait Foo<T> {}
|
||||
struct S;
|
||||
impl Foo for S {}
|
||||
impl Foo<S> for S {}
|
||||
|
||||
struct B<T>{}
|
||||
fn foo() -> B<dyn Foo> {}
|
||||
fn foo() -> B<dyn Foo<S>> {}
|
||||
|
||||
fn main() { let s$0t = foo(); }
|
||||
"#,
|
||||
@ -3320,8 +3320,8 @@ fn foo() -> B<dyn Foo> {}
|
||||
file_id: FileId(
|
||||
0,
|
||||
),
|
||||
full_range: 42..55,
|
||||
focus_range: 49..50,
|
||||
full_range: 48..61,
|
||||
focus_range: 55..56,
|
||||
name: "B",
|
||||
kind: Struct,
|
||||
description: "struct B<T>",
|
||||
@ -3333,11 +3333,24 @@ fn foo() -> B<dyn Foo> {}
|
||||
file_id: FileId(
|
||||
0,
|
||||
),
|
||||
full_range: 0..12,
|
||||
full_range: 0..15,
|
||||
focus_range: 6..9,
|
||||
name: "Foo",
|
||||
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(
|
||||
r#"
|
||||
//- 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