diff options
| author | bors[bot] <26634292+bors[bot]@users.noreply.github.com> | 2021-10-03 13:35:47 +0000 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2021-10-03 13:35:47 +0000 |
| commit | 10eaa5a0afc750ad4c009bc2f3276d3413c32b5f (patch) | |
| tree | dcf78cf89685e62fe9ecfe5363fa8ee13674b462 | |
| parent | e28aa1928b039720bea15c437b6d9885007ad69e (diff) | |
| parent | a31bc668f50b7ad6f67028c80a8805457cc7ebdf (diff) | |
| download | rust-10eaa5a0afc750ad4c009bc2f3276d3413c32b5f.tar.gz rust-10eaa5a0afc750ad4c009bc2f3276d3413c32b5f.zip | |
Merge #10441
10441: feat: Hide type inlay hints for constructors r=Veykril a=Veykril Fixes https://github.com/rust-analyzer/rust-analyzer/issues/3022 bors r+ Co-authored-by: Lukas Wirth <lukastw97@gmail.com>
| -rw-r--r-- | crates/ide/src/inlay_hints.rs | 133 | ||||
| -rw-r--r-- | crates/test_utils/src/minicore.rs | 11 |
2 files changed, 136 insertions, 8 deletions
diff --git a/crates/ide/src/inlay_hints.rs b/crates/ide/src/inlay_hints.rs index 401feffff42..3dcb510ab05 100644 --- a/crates/ide/src/inlay_hints.rs +++ b/crates/ide/src/inlay_hints.rs @@ -2,6 +2,7 @@ use either::Either; use hir::{known, Callable, HasVisibility, HirDisplay, Semantics, TypeInfo}; use ide_db::RootDatabase; use ide_db::{base_db::FileRange, helpers::FamousDefs}; +use itertools::Itertools; use stdx::to_lower_snake_case; use syntax::{ ast::{self, AstNode, HasArgList, HasName}, @@ -198,28 +199,95 @@ fn get_bind_pat_hints( let descended = sema.descend_node_into_attributes(pat.clone()).pop(); let desc_pat = descended.as_ref().unwrap_or(pat); - let krate = sema.scope(desc_pat.syntax()).module().map(|it| it.krate()); - let famous_defs = FamousDefs(sema, krate); - let ty = sema.type_of_pat(&desc_pat.clone().into())?.original; if should_not_display_type_hint(sema, &pat, &ty) { return None; } + let krate = sema.scope(desc_pat.syntax()).module().map(|it| it.krate()); + let famous_defs = FamousDefs(sema, krate); + let label = hint_iterator(sema, &famous_defs, config, &ty); + + let label = match label { + Some(label) => label, + None => { + let ty_name = ty.display_truncated(sema.db, config.max_length).to_string(); + if is_named_constructor(sema, pat, &ty_name).is_some() { + return None; + } + ty_name.into() + } + }; + acc.push(InlayHint { range: match pat.name() { Some(name) => name.syntax().text_range(), None => pat.syntax().text_range(), }, kind: InlayKind::TypeHint, - label: hint_iterator(sema, &famous_defs, config, &ty) - .unwrap_or_else(|| ty.display_truncated(sema.db, config.max_length).to_string().into()), + label, }); Some(()) } +fn is_named_constructor( + sema: &Semantics<RootDatabase>, + pat: &ast::IdentPat, + ty_name: &str, +) -> Option<()> { + let let_node = pat.syntax().parent()?; + let expr = match_ast! { + match let_node { + ast::LetStmt(it) => it.initializer(), + ast::Condition(it) => it.expr(), + _ => None, + } + }?; + + let expr = sema.descend_node_into_attributes(expr.clone()).pop().unwrap_or(expr); + // unwrap postfix expressions + let expr = match expr { + ast::Expr::TryExpr(it) => it.expr(), + ast::Expr::AwaitExpr(it) => it.expr(), + expr => Some(expr), + }?; + let expr = match expr { + ast::Expr::CallExpr(call) => match call.expr()? { + ast::Expr::PathExpr(p) => p, + _ => return None, + }, + _ => return None, + }; + let path = expr.path()?; + + // Check for tuple-struct or tuple-variant in which case we can check the last segment + let callable = sema.type_of_expr(&ast::Expr::PathExpr(expr))?.original.as_callable(sema.db); + let callable_kind = callable.map(|it| it.kind()); + if let Some(hir::CallableKind::TupleStruct(_) | hir::CallableKind::TupleEnumVariant(_)) = + callable_kind + { + if let Some(ctor) = path.segment() { + return (&ctor.to_string() == ty_name).then(|| ()); + } + } + + // otherwise use the qualifying segment as the constructor name + let qual_seg = path.qualifier()?.segment()?; + let ctor_name = match qual_seg.kind()? { + ast::PathSegmentKind::Name(name_ref) => { + match qual_seg.generic_arg_list().map(|it| it.generic_args()) { + Some(generics) => format!("{}<{}>", name_ref, generics.format(", ")), + None => name_ref.to_string(), + } + } + ast::PathSegmentKind::Type { type_ref: Some(ty), trait_ref: None } => ty.to_string(), + _ => return None, + }; + (&ctor_name == ty_name).then(|| ()) +} + /// Checks if the type is an Iterator from std::iter and replaces its hint with an `impl Iterator<Item = Ty>`. fn hint_iterator( sema: &Semantics<RootDatabase>, @@ -470,10 +538,12 @@ mod tests { max_length: None, }; + #[track_caller] fn check(ra_fixture: &str) { check_with_config(TEST_CONFIG, ra_fixture); } + #[track_caller] fn check_params(ra_fixture: &str) { check_with_config( InlayHintsConfig { @@ -486,6 +556,7 @@ mod tests { ); } + #[track_caller] fn check_types(ra_fixture: &str) { check_with_config( InlayHintsConfig { @@ -498,6 +569,7 @@ mod tests { ); } + #[track_caller] fn check_chains(ra_fixture: &str) { check_with_config( InlayHintsConfig { @@ -510,6 +582,7 @@ mod tests { ); } + #[track_caller] fn check_with_config(config: InlayHintsConfig, ra_fixture: &str) { let (analysis, file_id) = fixture::file(&ra_fixture); let expected = extract_annotations(&*analysis.file_text(file_id).unwrap()); @@ -519,6 +592,7 @@ mod tests { assert_eq!(expected, actual, "\nExpected:\n{:#?}\n\nActual:\n{:#?}", expected, actual); } + #[track_caller] fn check_expect(config: InlayHintsConfig, ra_fixture: &str, expect: Expect) { let (analysis, file_id) = fixture::file(&ra_fixture); let inlay_hints = analysis.inlay_hints(&config, file_id).unwrap(); @@ -1191,11 +1265,12 @@ trait Display {} trait Sync {} fn main() { - let _v = Vec::<Box<&(dyn Display + Sync)>>::new(); + // The block expression wrapping disables the constructor hint hiding logic + let _v = { Vec::<Box<&(dyn Display + Sync)>>::new() }; //^^ Vec<Box<&(dyn Display + Sync)>> - let _v = Vec::<Box<*const (dyn Display + Sync)>>::new(); + let _v = { Vec::<Box<*const (dyn Display + Sync)>>::new() }; //^^ Vec<Box<*const (dyn Display + Sync)>> - let _v = Vec::<Box<dyn Display + Sync>>::new(); + let _v = { Vec::<Box<dyn Display + Sync>>::new() }; //^^ Vec<Box<dyn Display + Sync>> } "#, @@ -1235,6 +1310,48 @@ fn main() { } #[test] + fn skip_constructor_type_hints() { + check_types( + r#" +//- minicore: try +use core::ops::ControlFlow; + +struct Struct; +struct TupleStruct(); + +impl Struct { + fn new() -> Self { + Struct + } + fn try_new() -> ControlFlow<(), Self> { + ControlFlow::Continue(Struct) + } +} + +struct Generic<T>(T); +impl Generic<i32> { + fn new() -> Self { + Generic(0) + } +} + +fn main() { + let strukt = Struct::new(); + let tuple_struct = TupleStruct(); + let generic0 = Generic::new(); + // ^^^^^^^^ Generic<i32> + let generic1 = Generic::<i32>::new(); + let generic2 = <Generic<i32>>::new(); +} + +fn fallible() -> ControlFlow<()> { + let strukt = Struct::try_new()?; +} +"#, + ); + } + + #[test] fn closures() { check( r#" diff --git a/crates/test_utils/src/minicore.rs b/crates/test_utils/src/minicore.rs index 5e17047a407..045b4898e50 100644 --- a/crates/test_utils/src/minicore.rs +++ b/crates/test_utils/src/minicore.rs @@ -300,6 +300,17 @@ pub mod ops { #[lang = "branch"] fn branch(self) -> ControlFlow<Self::Residual, Self::Output>; } + + impl<B, C> Try for ControlFlow<B, C> { + type Output = C; + type Residual = ControlFlow<B, convert::Infallible>; + fn from_output(output: Self::Output) -> Self {} + fn branch(self) -> ControlFlow<Self::Residual, Self::Output> {} + } + + impl<B, C> FromResidual for ControlFlow<B, C> { + fn from_residual(residual: ControlFlow<B, convert::Infallible>) -> Self {} + } } pub use self::try_::{ControlFlow, FromResidual, Try}; // endregion:try |
