mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-10-03 15:15:24 +00:00
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>
This commit is contained in:
commit
10eaa5a0af
2 changed files with 136 additions and 8 deletions
|
@ -2,6 +2,7 @@ use either::Either;
|
||||||
use hir::{known, Callable, HasVisibility, HirDisplay, Semantics, TypeInfo};
|
use hir::{known, Callable, HasVisibility, HirDisplay, Semantics, TypeInfo};
|
||||||
use ide_db::RootDatabase;
|
use ide_db::RootDatabase;
|
||||||
use ide_db::{base_db::FileRange, helpers::FamousDefs};
|
use ide_db::{base_db::FileRange, helpers::FamousDefs};
|
||||||
|
use itertools::Itertools;
|
||||||
use stdx::to_lower_snake_case;
|
use stdx::to_lower_snake_case;
|
||||||
use syntax::{
|
use syntax::{
|
||||||
ast::{self, AstNode, HasArgList, HasName},
|
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 descended = sema.descend_node_into_attributes(pat.clone()).pop();
|
||||||
let desc_pat = descended.as_ref().unwrap_or(pat);
|
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;
|
let ty = sema.type_of_pat(&desc_pat.clone().into())?.original;
|
||||||
|
|
||||||
if should_not_display_type_hint(sema, &pat, &ty) {
|
if should_not_display_type_hint(sema, &pat, &ty) {
|
||||||
return None;
|
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 {
|
acc.push(InlayHint {
|
||||||
range: match pat.name() {
|
range: match pat.name() {
|
||||||
Some(name) => name.syntax().text_range(),
|
Some(name) => name.syntax().text_range(),
|
||||||
None => pat.syntax().text_range(),
|
None => pat.syntax().text_range(),
|
||||||
},
|
},
|
||||||
kind: InlayKind::TypeHint,
|
kind: InlayKind::TypeHint,
|
||||||
label: hint_iterator(sema, &famous_defs, config, &ty)
|
label,
|
||||||
.unwrap_or_else(|| ty.display_truncated(sema.db, config.max_length).to_string().into()),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
Some(())
|
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>`.
|
/// Checks if the type is an Iterator from std::iter and replaces its hint with an `impl Iterator<Item = Ty>`.
|
||||||
fn hint_iterator(
|
fn hint_iterator(
|
||||||
sema: &Semantics<RootDatabase>,
|
sema: &Semantics<RootDatabase>,
|
||||||
|
@ -470,10 +538,12 @@ mod tests {
|
||||||
max_length: None,
|
max_length: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#[track_caller]
|
||||||
fn check(ra_fixture: &str) {
|
fn check(ra_fixture: &str) {
|
||||||
check_with_config(TEST_CONFIG, ra_fixture);
|
check_with_config(TEST_CONFIG, ra_fixture);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[track_caller]
|
||||||
fn check_params(ra_fixture: &str) {
|
fn check_params(ra_fixture: &str) {
|
||||||
check_with_config(
|
check_with_config(
|
||||||
InlayHintsConfig {
|
InlayHintsConfig {
|
||||||
|
@ -486,6 +556,7 @@ mod tests {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[track_caller]
|
||||||
fn check_types(ra_fixture: &str) {
|
fn check_types(ra_fixture: &str) {
|
||||||
check_with_config(
|
check_with_config(
|
||||||
InlayHintsConfig {
|
InlayHintsConfig {
|
||||||
|
@ -498,6 +569,7 @@ mod tests {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[track_caller]
|
||||||
fn check_chains(ra_fixture: &str) {
|
fn check_chains(ra_fixture: &str) {
|
||||||
check_with_config(
|
check_with_config(
|
||||||
InlayHintsConfig {
|
InlayHintsConfig {
|
||||||
|
@ -510,6 +582,7 @@ mod tests {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[track_caller]
|
||||||
fn check_with_config(config: InlayHintsConfig, ra_fixture: &str) {
|
fn check_with_config(config: InlayHintsConfig, ra_fixture: &str) {
|
||||||
let (analysis, file_id) = fixture::file(&ra_fixture);
|
let (analysis, file_id) = fixture::file(&ra_fixture);
|
||||||
let expected = extract_annotations(&*analysis.file_text(file_id).unwrap());
|
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);
|
assert_eq!(expected, actual, "\nExpected:\n{:#?}\n\nActual:\n{:#?}", expected, actual);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[track_caller]
|
||||||
fn check_expect(config: InlayHintsConfig, ra_fixture: &str, expect: Expect) {
|
fn check_expect(config: InlayHintsConfig, ra_fixture: &str, expect: Expect) {
|
||||||
let (analysis, file_id) = fixture::file(&ra_fixture);
|
let (analysis, file_id) = fixture::file(&ra_fixture);
|
||||||
let inlay_hints = analysis.inlay_hints(&config, file_id).unwrap();
|
let inlay_hints = analysis.inlay_hints(&config, file_id).unwrap();
|
||||||
|
@ -1191,11 +1265,12 @@ trait Display {}
|
||||||
trait Sync {}
|
trait Sync {}
|
||||||
|
|
||||||
fn main() {
|
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)>>
|
//^^ 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)>>
|
//^^ 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>>
|
//^^ Vec<Box<dyn Display + Sync>>
|
||||||
}
|
}
|
||||||
"#,
|
"#,
|
||||||
|
@ -1234,6 +1309,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]
|
#[test]
|
||||||
fn closures() {
|
fn closures() {
|
||||||
check(
|
check(
|
||||||
|
|
|
@ -300,6 +300,17 @@ pub mod ops {
|
||||||
#[lang = "branch"]
|
#[lang = "branch"]
|
||||||
fn branch(self) -> ControlFlow<Self::Residual, Self::Output>;
|
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};
|
pub use self::try_::{ControlFlow, FromResidual, Try};
|
||||||
// endregion:try
|
// endregion:try
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue