Skip to content

Commit 10eaa5a

Browse files
bors[bot]Veykril
andauthored
Merge #10441
10441: feat: Hide type inlay hints for constructors r=Veykril a=Veykril Fixes #3022 bors r+ Co-authored-by: Lukas Wirth <[email protected]>
2 parents e28aa19 + a31bc66 commit 10eaa5a

File tree

2 files changed

+136
-8
lines changed

2 files changed

+136
-8
lines changed

crates/ide/src/inlay_hints.rs

Lines changed: 125 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ use either::Either;
22
use hir::{known, Callable, HasVisibility, HirDisplay, Semantics, TypeInfo};
33
use ide_db::RootDatabase;
44
use ide_db::{base_db::FileRange, helpers::FamousDefs};
5+
use itertools::Itertools;
56
use stdx::to_lower_snake_case;
67
use syntax::{
78
ast::{self, AstNode, HasArgList, HasName},
@@ -198,28 +199,95 @@ fn get_bind_pat_hints(
198199

199200
let descended = sema.descend_node_into_attributes(pat.clone()).pop();
200201
let desc_pat = descended.as_ref().unwrap_or(pat);
201-
let krate = sema.scope(desc_pat.syntax()).module().map(|it| it.krate());
202-
let famous_defs = FamousDefs(sema, krate);
203-
204202
let ty = sema.type_of_pat(&desc_pat.clone().into())?.original;
205203

206204
if should_not_display_type_hint(sema, &pat, &ty) {
207205
return None;
208206
}
209207

208+
let krate = sema.scope(desc_pat.syntax()).module().map(|it| it.krate());
209+
let famous_defs = FamousDefs(sema, krate);
210+
let label = hint_iterator(sema, &famous_defs, config, &ty);
211+
212+
let label = match label {
213+
Some(label) => label,
214+
None => {
215+
let ty_name = ty.display_truncated(sema.db, config.max_length).to_string();
216+
if is_named_constructor(sema, pat, &ty_name).is_some() {
217+
return None;
218+
}
219+
ty_name.into()
220+
}
221+
};
222+
210223
acc.push(InlayHint {
211224
range: match pat.name() {
212225
Some(name) => name.syntax().text_range(),
213226
None => pat.syntax().text_range(),
214227
},
215228
kind: InlayKind::TypeHint,
216-
label: hint_iterator(sema, &famous_defs, config, &ty)
217-
.unwrap_or_else(|| ty.display_truncated(sema.db, config.max_length).to_string().into()),
229+
label,
218230
});
219231

220232
Some(())
221233
}
222234

235+
fn is_named_constructor(
236+
sema: &Semantics<RootDatabase>,
237+
pat: &ast::IdentPat,
238+
ty_name: &str,
239+
) -> Option<()> {
240+
let let_node = pat.syntax().parent()?;
241+
let expr = match_ast! {
242+
match let_node {
243+
ast::LetStmt(it) => it.initializer(),
244+
ast::Condition(it) => it.expr(),
245+
_ => None,
246+
}
247+
}?;
248+
249+
let expr = sema.descend_node_into_attributes(expr.clone()).pop().unwrap_or(expr);
250+
// unwrap postfix expressions
251+
let expr = match expr {
252+
ast::Expr::TryExpr(it) => it.expr(),
253+
ast::Expr::AwaitExpr(it) => it.expr(),
254+
expr => Some(expr),
255+
}?;
256+
let expr = match expr {
257+
ast::Expr::CallExpr(call) => match call.expr()? {
258+
ast::Expr::PathExpr(p) => p,
259+
_ => return None,
260+
},
261+
_ => return None,
262+
};
263+
let path = expr.path()?;
264+
265+
// Check for tuple-struct or tuple-variant in which case we can check the last segment
266+
let callable = sema.type_of_expr(&ast::Expr::PathExpr(expr))?.original.as_callable(sema.db);
267+
let callable_kind = callable.map(|it| it.kind());
268+
if let Some(hir::CallableKind::TupleStruct(_) | hir::CallableKind::TupleEnumVariant(_)) =
269+
callable_kind
270+
{
271+
if let Some(ctor) = path.segment() {
272+
return (&ctor.to_string() == ty_name).then(|| ());
273+
}
274+
}
275+
276+
// otherwise use the qualifying segment as the constructor name
277+
let qual_seg = path.qualifier()?.segment()?;
278+
let ctor_name = match qual_seg.kind()? {
279+
ast::PathSegmentKind::Name(name_ref) => {
280+
match qual_seg.generic_arg_list().map(|it| it.generic_args()) {
281+
Some(generics) => format!("{}<{}>", name_ref, generics.format(", ")),
282+
None => name_ref.to_string(),
283+
}
284+
}
285+
ast::PathSegmentKind::Type { type_ref: Some(ty), trait_ref: None } => ty.to_string(),
286+
_ => return None,
287+
};
288+
(&ctor_name == ty_name).then(|| ())
289+
}
290+
223291
/// Checks if the type is an Iterator from std::iter and replaces its hint with an `impl Iterator<Item = Ty>`.
224292
fn hint_iterator(
225293
sema: &Semantics<RootDatabase>,
@@ -470,10 +538,12 @@ mod tests {
470538
max_length: None,
471539
};
472540

541+
#[track_caller]
473542
fn check(ra_fixture: &str) {
474543
check_with_config(TEST_CONFIG, ra_fixture);
475544
}
476545

546+
#[track_caller]
477547
fn check_params(ra_fixture: &str) {
478548
check_with_config(
479549
InlayHintsConfig {
@@ -486,6 +556,7 @@ mod tests {
486556
);
487557
}
488558

559+
#[track_caller]
489560
fn check_types(ra_fixture: &str) {
490561
check_with_config(
491562
InlayHintsConfig {
@@ -498,6 +569,7 @@ mod tests {
498569
);
499570
}
500571

572+
#[track_caller]
501573
fn check_chains(ra_fixture: &str) {
502574
check_with_config(
503575
InlayHintsConfig {
@@ -510,6 +582,7 @@ mod tests {
510582
);
511583
}
512584

585+
#[track_caller]
513586
fn check_with_config(config: InlayHintsConfig, ra_fixture: &str) {
514587
let (analysis, file_id) = fixture::file(&ra_fixture);
515588
let expected = extract_annotations(&*analysis.file_text(file_id).unwrap());
@@ -519,6 +592,7 @@ mod tests {
519592
assert_eq!(expected, actual, "\nExpected:\n{:#?}\n\nActual:\n{:#?}", expected, actual);
520593
}
521594

595+
#[track_caller]
522596
fn check_expect(config: InlayHintsConfig, ra_fixture: &str, expect: Expect) {
523597
let (analysis, file_id) = fixture::file(&ra_fixture);
524598
let inlay_hints = analysis.inlay_hints(&config, file_id).unwrap();
@@ -1191,11 +1265,12 @@ trait Display {}
11911265
trait Sync {}
11921266
11931267
fn main() {
1194-
let _v = Vec::<Box<&(dyn Display + Sync)>>::new();
1268+
// The block expression wrapping disables the constructor hint hiding logic
1269+
let _v = { Vec::<Box<&(dyn Display + Sync)>>::new() };
11951270
//^^ Vec<Box<&(dyn Display + Sync)>>
1196-
let _v = Vec::<Box<*const (dyn Display + Sync)>>::new();
1271+
let _v = { Vec::<Box<*const (dyn Display + Sync)>>::new() };
11971272
//^^ Vec<Box<*const (dyn Display + Sync)>>
1198-
let _v = Vec::<Box<dyn Display + Sync>>::new();
1273+
let _v = { Vec::<Box<dyn Display + Sync>>::new() };
11991274
//^^ Vec<Box<dyn Display + Sync>>
12001275
}
12011276
"#,
@@ -1234,6 +1309,48 @@ fn main() {
12341309
);
12351310
}
12361311

1312+
#[test]
1313+
fn skip_constructor_type_hints() {
1314+
check_types(
1315+
r#"
1316+
//- minicore: try
1317+
use core::ops::ControlFlow;
1318+
1319+
struct Struct;
1320+
struct TupleStruct();
1321+
1322+
impl Struct {
1323+
fn new() -> Self {
1324+
Struct
1325+
}
1326+
fn try_new() -> ControlFlow<(), Self> {
1327+
ControlFlow::Continue(Struct)
1328+
}
1329+
}
1330+
1331+
struct Generic<T>(T);
1332+
impl Generic<i32> {
1333+
fn new() -> Self {
1334+
Generic(0)
1335+
}
1336+
}
1337+
1338+
fn main() {
1339+
let strukt = Struct::new();
1340+
let tuple_struct = TupleStruct();
1341+
let generic0 = Generic::new();
1342+
// ^^^^^^^^ Generic<i32>
1343+
let generic1 = Generic::<i32>::new();
1344+
let generic2 = <Generic<i32>>::new();
1345+
}
1346+
1347+
fn fallible() -> ControlFlow<()> {
1348+
let strukt = Struct::try_new()?;
1349+
}
1350+
"#,
1351+
);
1352+
}
1353+
12371354
#[test]
12381355
fn closures() {
12391356
check(

crates/test_utils/src/minicore.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -300,6 +300,17 @@ pub mod ops {
300300
#[lang = "branch"]
301301
fn branch(self) -> ControlFlow<Self::Residual, Self::Output>;
302302
}
303+
304+
impl<B, C> Try for ControlFlow<B, C> {
305+
type Output = C;
306+
type Residual = ControlFlow<B, convert::Infallible>;
307+
fn from_output(output: Self::Output) -> Self {}
308+
fn branch(self) -> ControlFlow<Self::Residual, Self::Output> {}
309+
}
310+
311+
impl<B, C> FromResidual for ControlFlow<B, C> {
312+
fn from_residual(residual: ControlFlow<B, convert::Infallible>) -> Self {}
313+
}
303314
}
304315
pub use self::try_::{ControlFlow, FromResidual, Try};
305316
// endregion:try

0 commit comments

Comments
 (0)