mirror of
				https://github.com/rust-lang/rust-analyzer.git
				synced 2025-10-31 12:04:43 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			316 lines
		
	
	
	
		
			7.7 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
			
		
		
	
	
			316 lines
		
	
	
	
		
			7.7 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
| //! Implementation of inlay hints for generic parameters.
 | |
| use ide_db::{active_parameter::generic_def_for_node, RootDatabase};
 | |
| use syntax::{
 | |
|     ast::{self, AnyHasGenericArgs, HasGenericArgs, HasName},
 | |
|     AstNode,
 | |
| };
 | |
| 
 | |
| use crate::{inlay_hints::GenericParameterHints, InlayHint, InlayHintsConfig, InlayKind};
 | |
| 
 | |
| use super::param_name::{is_argument_similar_to_param_name, render_label};
 | |
| 
 | |
| pub(crate) fn hints(
 | |
|     acc: &mut Vec<InlayHint>,
 | |
|     sema: &hir::Semantics<'_, RootDatabase>,
 | |
|     config: &InlayHintsConfig,
 | |
|     node: AnyHasGenericArgs,
 | |
| ) -> Option<()> {
 | |
|     let GenericParameterHints { type_hints, lifetime_hints, const_hints } =
 | |
|         config.generic_parameter_hints;
 | |
|     if !(type_hints || lifetime_hints || const_hints) {
 | |
|         return None;
 | |
|     }
 | |
| 
 | |
|     let generic_arg_list = node.generic_arg_list()?;
 | |
| 
 | |
|     let (generic_def, _, _, _) =
 | |
|         generic_def_for_node(sema, &generic_arg_list, &node.syntax().first_token()?)?;
 | |
| 
 | |
|     let mut args = generic_arg_list.generic_args().peekable();
 | |
|     let start_with_lifetime = matches!(args.peek()?, ast::GenericArg::LifetimeArg(_));
 | |
|     let params = generic_def.params(sema.db).into_iter().filter(|p| {
 | |
|         if let hir::GenericParam::TypeParam(it) = p {
 | |
|             if it.is_implicit(sema.db) {
 | |
|                 return false;
 | |
|             }
 | |
|         }
 | |
|         if !start_with_lifetime {
 | |
|             return !matches!(p, hir::GenericParam::LifetimeParam(_));
 | |
|         }
 | |
|         true
 | |
|     });
 | |
| 
 | |
|     let hints = params.zip(args).filter_map(|(param, arg)| {
 | |
|         if matches!(arg, ast::GenericArg::AssocTypeArg(_)) {
 | |
|             return None;
 | |
|         }
 | |
| 
 | |
|         let name = param.name(sema.db);
 | |
|         let param_name = name.as_str();
 | |
| 
 | |
|         let should_hide = {
 | |
|             let argument = get_string_representation(&arg)?;
 | |
|             is_argument_similar_to_param_name(&argument, param_name)
 | |
|         };
 | |
| 
 | |
|         if should_hide {
 | |
|             return None;
 | |
|         }
 | |
| 
 | |
|         let range = sema.original_range_opt(arg.syntax())?.range;
 | |
| 
 | |
|         let source_syntax = match param {
 | |
|             hir::GenericParam::TypeParam(it) => {
 | |
|                 if !type_hints || !matches!(arg, ast::GenericArg::TypeArg(_)) {
 | |
|                     return None;
 | |
|                 }
 | |
|                 sema.source(it.merge())?.value.syntax().clone()
 | |
|             }
 | |
|             hir::GenericParam::ConstParam(it) => {
 | |
|                 if !const_hints || !matches!(arg, ast::GenericArg::ConstArg(_)) {
 | |
|                     return None;
 | |
|                 }
 | |
|                 let syntax = sema.source(it.merge())?.value.syntax().clone();
 | |
|                 let const_param = ast::ConstParam::cast(syntax)?;
 | |
|                 const_param.name()?.syntax().clone()
 | |
|             }
 | |
|             hir::GenericParam::LifetimeParam(it) => {
 | |
|                 if !lifetime_hints || !matches!(arg, ast::GenericArg::LifetimeArg(_)) {
 | |
|                     return None;
 | |
|                 }
 | |
|                 sema.source(it)?.value.syntax().clone()
 | |
|             }
 | |
|         };
 | |
|         let linked_location = sema.original_range_opt(&source_syntax);
 | |
|         let label = render_label(param_name, config, linked_location);
 | |
| 
 | |
|         Some(InlayHint {
 | |
|             range,
 | |
|             position: crate::InlayHintPosition::Before,
 | |
|             pad_left: false,
 | |
|             pad_right: true,
 | |
|             kind: InlayKind::GenericParameter,
 | |
|             label,
 | |
|             text_edit: None,
 | |
|             resolve_parent: Some(node.syntax().text_range()),
 | |
|         })
 | |
|     });
 | |
| 
 | |
|     acc.extend(hints);
 | |
|     Some(())
 | |
| }
 | |
| 
 | |
| fn get_string_representation(arg: &ast::GenericArg) -> Option<String> {
 | |
|     return match arg {
 | |
|         ast::GenericArg::AssocTypeArg(_) => None,
 | |
|         ast::GenericArg::ConstArg(const_arg) => Some(const_arg.to_string()),
 | |
|         ast::GenericArg::LifetimeArg(lifetime_arg) => {
 | |
|             let lifetime = lifetime_arg.lifetime()?;
 | |
|             Some(lifetime.to_string())
 | |
|         }
 | |
|         ast::GenericArg::TypeArg(type_arg) => {
 | |
|             let ty = type_arg.ty()?;
 | |
|             Some(
 | |
|                 type_path_segment(&ty)
 | |
|                     .map_or_else(|| type_arg.to_string(), |segment| segment.to_string()),
 | |
|             )
 | |
|         }
 | |
|     };
 | |
| 
 | |
|     fn type_path_segment(ty: &ast::Type) -> Option<ast::PathSegment> {
 | |
|         match ty {
 | |
|             ast::Type::ArrayType(it) => type_path_segment(&it.ty()?),
 | |
|             ast::Type::ForType(it) => type_path_segment(&it.ty()?),
 | |
|             ast::Type::ParenType(it) => type_path_segment(&it.ty()?),
 | |
|             ast::Type::PathType(path_type) => path_type.path()?.segment(),
 | |
|             ast::Type::PtrType(it) => type_path_segment(&it.ty()?),
 | |
|             ast::Type::RefType(it) => type_path_segment(&it.ty()?),
 | |
|             ast::Type::SliceType(it) => type_path_segment(&it.ty()?),
 | |
|             _ => None,
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| #[cfg(test)]
 | |
| mod tests {
 | |
|     use crate::{
 | |
|         inlay_hints::{
 | |
|             tests::{check_with_config, DISABLED_CONFIG},
 | |
|             GenericParameterHints,
 | |
|         },
 | |
|         InlayHintsConfig,
 | |
|     };
 | |
| 
 | |
|     #[track_caller]
 | |
|     fn generic_param_name_hints_always(#[rust_analyzer::rust_fixture] ra_fixture: &str) {
 | |
|         check_with_config(
 | |
|             InlayHintsConfig {
 | |
|                 generic_parameter_hints: GenericParameterHints {
 | |
|                     type_hints: true,
 | |
|                     lifetime_hints: true,
 | |
|                     const_hints: true,
 | |
|                 },
 | |
|                 ..DISABLED_CONFIG
 | |
|             },
 | |
|             ra_fixture,
 | |
|         );
 | |
|     }
 | |
| 
 | |
|     #[track_caller]
 | |
|     fn generic_param_name_hints_const_only(#[rust_analyzer::rust_fixture] ra_fixture: &str) {
 | |
|         check_with_config(
 | |
|             InlayHintsConfig {
 | |
|                 generic_parameter_hints: GenericParameterHints {
 | |
|                     type_hints: false,
 | |
|                     lifetime_hints: false,
 | |
|                     const_hints: true,
 | |
|                 },
 | |
|                 ..DISABLED_CONFIG
 | |
|             },
 | |
|             ra_fixture,
 | |
|         );
 | |
|     }
 | |
| 
 | |
|     #[test]
 | |
|     fn type_only() {
 | |
|         generic_param_name_hints_always(
 | |
|             r#"
 | |
| struct A<X, Y> {
 | |
|     x: X,
 | |
|     y: Y,
 | |
| }
 | |
| 
 | |
| fn foo(a: A<usize,  u32>) {}
 | |
|           //^^^^^ X ^^^ Y
 | |
| "#,
 | |
|         )
 | |
|     }
 | |
| 
 | |
|     #[test]
 | |
|     fn lifetime_and_type() {
 | |
|         generic_param_name_hints_always(
 | |
|             r#"
 | |
| struct A<'a, X> {
 | |
|     x: &'a X
 | |
| }
 | |
| 
 | |
| fn foo<'b>(a: A<'b,  u32>) {}
 | |
|               //^^ 'a^^^ X
 | |
| "#,
 | |
|         )
 | |
|     }
 | |
| 
 | |
|     #[test]
 | |
|     fn omit_lifetime() {
 | |
|         generic_param_name_hints_always(
 | |
|             r#"
 | |
| struct A<'a, X> {
 | |
|     x: &'a X
 | |
| }
 | |
| 
 | |
| fn foo() {
 | |
|     let x: i32 = 1;
 | |
|     let a: A<i32> = A { x: &x };
 | |
|           // ^^^ X
 | |
| }
 | |
| "#,
 | |
|         )
 | |
|     }
 | |
| 
 | |
|     #[test]
 | |
|     fn const_only() {
 | |
|         generic_param_name_hints_always(
 | |
|             r#"
 | |
| struct A<const X: usize, const Y: usize> {};
 | |
| 
 | |
| fn foo(a: A<12, 2>) {}
 | |
|           //^^ X^ Y
 | |
| "#,
 | |
|         )
 | |
|     }
 | |
| 
 | |
|     #[test]
 | |
|     fn lifetime_and_type_and_const() {
 | |
|         generic_param_name_hints_always(
 | |
|             r#"
 | |
| struct A<'a, X, const LEN: usize> {
 | |
|     x: &'a [X; LEN],
 | |
| }
 | |
| 
 | |
| fn foo<'b>(a: A<
 | |
|     'b,
 | |
|  // ^^ 'a
 | |
|     u32,
 | |
|  // ^^^ X
 | |
|     3
 | |
|  // ^ LEN
 | |
|     >) {}
 | |
| "#,
 | |
|         )
 | |
|     }
 | |
| 
 | |
|     #[test]
 | |
|     fn const_only_config() {
 | |
|         generic_param_name_hints_const_only(
 | |
|             r#"
 | |
| struct A<'a, X, const LEN: usize> {
 | |
|     x: &'a [X; LEN],
 | |
| }
 | |
| 
 | |
| fn foo<'b>(a: A<
 | |
|     'b,
 | |
|     u32,
 | |
|     3
 | |
|  // ^ LEN
 | |
|     >) {}
 | |
| "#,
 | |
|         )
 | |
|     }
 | |
| 
 | |
|     #[test]
 | |
|     fn assoc_type() {
 | |
|         generic_param_name_hints_always(
 | |
|             r#"
 | |
| trait Trait<T> {
 | |
|     type Assoc1;
 | |
|     type Assoc2;
 | |
| }
 | |
| 
 | |
| fn foo() -> impl Trait<i32, Assoc1 = u32, Assoc2 = u32> {}
 | |
|                     // ^^^ T
 | |
| "#,
 | |
|         )
 | |
|     }
 | |
| 
 | |
|     #[test]
 | |
|     fn hide_similar() {
 | |
|         generic_param_name_hints_always(
 | |
|             r#"
 | |
| struct A<'a, X, const N: usize> {
 | |
|     x: &'a [X; N],
 | |
| }
 | |
| 
 | |
| const N: usize = 3;
 | |
| 
 | |
| mod m {
 | |
|     type X = u32;
 | |
| }
 | |
| 
 | |
| fn foo<'a>(a: A<'a, m::X, N>) {}
 | |
| "#,
 | |
|         )
 | |
|     }
 | |
| 
 | |
|     #[test]
 | |
|     fn mismatching_args() {
 | |
|         generic_param_name_hints_always(
 | |
|             r#"
 | |
| struct A<X, const N: usize> {
 | |
|     x: [X; N]
 | |
| }
 | |
| 
 | |
| type InvalidType = A<3, i32>;
 | |
| "#,
 | |
|         )
 | |
|     }
 | |
| }
 | 
