diff options
| -rw-r--r-- | Cargo.toml | 1 | ||||
| -rw-r--r-- | crates/hir-ty/src/display.rs | 225 | ||||
| -rw-r--r-- | crates/hir-ty/src/lib.rs | 5 | ||||
| -rw-r--r-- | crates/hir-ty/src/tests/display_source_code.rs | 2 | ||||
| -rw-r--r-- | crates/hir/src/lib.rs | 10 | ||||
| -rw-r--r-- | crates/ide-assists/src/handlers/promote_local_to_const.rs | 13 | ||||
| -rw-r--r-- | crates/ide-completion/src/completions/type.rs | 8 | ||||
| -rw-r--r-- | crates/ide-db/src/label.rs | 4 | ||||
| -rw-r--r-- | crates/ide-diagnostics/src/handlers/unresolved_field.rs | 17 | ||||
| -rw-r--r-- | crates/ide-diagnostics/src/handlers/unused_variables.rs | 21 | ||||
| -rw-r--r-- | crates/ide/src/hover/tests.rs | 66 |
11 files changed, 228 insertions, 144 deletions
diff --git a/Cargo.toml b/Cargo.toml index 06b092a4490..f7e3ae51dfd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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 diff --git a/crates/hir-ty/src/display.rs b/crates/hir-ty/src/display.rs index 42e33a9853b..cc2b2290e48 100644 --- a/crates/hir-ty/src/display.rs +++ b/crates/hir-ty/src/display.rs @@ -28,7 +28,7 @@ use intern::{Internable, Interned}; 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 @@ use crate::{ 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 @@ impl DisplayTarget { #[derive(Debug)] pub enum DisplaySourceCodeError { PathNotFound, - UnknownType, Coroutine, OpaqueType, } @@ -418,6 +424,7 @@ impl HirDisplay for ProjectionTy { 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 @@ impl HirDisplay for Ty { .chain(fn_params) .flatten(); write!(f, "<")?; + // FIXME use `hir_fmt_generics` here f.write_joined(params, ", ")?; write!(f, ">")?; } @@ -1037,6 +1045,7 @@ impl HirDisplay for Ty { // 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 @@ impl HirDisplay for Ty { } 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; + if parameters.is_empty(Interner) { + return Ok(()); + } + + 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 } - 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 + 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; } - 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; - } + // 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.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)?; } + ¶meters[0..default_from] } + } + } else { + parameters + } +} - write!(f, ">")?; +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 @@ impl HirDisplay for Lifetime { 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 @@ impl HirDisplay for LifetimeData { 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(()), diff --git a/crates/hir-ty/src/lib.rs b/crates/hir-ty/src/lib.rs index b9f4c57366b..1727cec9893 100644 --- a/crates/hir-ty/src/lib.rs +++ b/crates/hir-ty/src/lib.rs @@ -56,7 +56,6 @@ use base_db::salsa::impl_intern_value_trivial; use chalk_ir::{ fold::{Shift, TypeFoldable}, interner::HasInterner, - visit::{TypeSuperVisitable, TypeVisitable, TypeVisitor}, NoSolution, }; use either::Either; @@ -98,7 +97,9 @@ pub use traits::TraitEnvironment; 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>; diff --git a/crates/hir-ty/src/tests/display_source_code.rs b/crates/hir-ty/src/tests/display_source_code.rs index e4c5d709da7..e8369caa771 100644 --- a/crates/hir-ty/src/tests/display_source_code.rs +++ b/crates/hir-ty/src/tests/display_source_code.rs @@ -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<'_> "#, ); } diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index 8556d35a43b..6bffb0c5e7b 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -4711,10 +4711,12 @@ impl Type { 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); + } } } } diff --git a/crates/ide-assists/src/handlers/promote_local_to_const.rs b/crates/ide-assists/src/handlers/promote_local_to_const.rs index 67fea772c79..7c2dc0e0c10 100644 --- a/crates/ide-assists/src/handlers/promote_local_to_const.rs +++ b/crates/ide-assists/src/handlers/promote_local_to_const.rs @@ -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,14 +312,18 @@ 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(); +} +", ); } diff --git a/crates/ide-completion/src/completions/type.rs b/crates/ide-completion/src/completions/type.rs index e4678089462..2361d14aae7 100644 --- a/crates/ide-completion/src/completions/type.rs +++ b/crates/ide-completion/src/completions/type.rs @@ -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 } diff --git a/crates/ide-db/src/label.rs b/crates/ide-db/src/label.rs index 4b6d54b5eab..919c1273e5a 100644 --- a/crates/ide-db/src/label.rs +++ b/crates/ide-db/src/label.rs @@ -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 @@ impl From<Label> for 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) } } diff --git a/crates/ide-diagnostics/src/handlers/unresolved_field.rs b/crates/ide-diagnostics/src/handlers/unresolved_field.rs index 7a03f176ac2..41357b59622 100644 --- a/crates/ide-diagnostics/src/handlers/unresolved_field.rs +++ b/crates/ide-diagnostics/src/handlers/unresolved_field.rs @@ -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; diff --git a/crates/ide-diagnostics/src/handlers/unused_variables.rs b/crates/ide-diagnostics/src/handlers/unused_variables.rs index cd251faab9a..f69209a10a9 100644 --- a/crates/ide-diagnostics/src/handlers/unused_variables.rs +++ b/crates/ide-diagnostics/src/handlers/unused_variables.rs @@ -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, }]) diff --git a/crates/ide/src/hover/tests.rs b/crates/ide/src/hover/tests.rs index 754fb2eccdb..67f10f0374d 100644 --- a/crates/ide/src/hover/tests.rs +++ b/crates/ide/src/hover/tests.rs @@ -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 main() { let s$0t = 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 main() { let s$0t = 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$0<'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 + ``` + "#]], + ); +} |
