Skip to content

Commit 50c8743

Browse files
Add inlay hints for inferred consts
This provides an inlay hint for _ in const generic arguments. (cherry picked from commit 535d6f07cc59abbba511769af7d9e467a4cc36f2)
1 parent bd81611 commit 50c8743

4 files changed

Lines changed: 91 additions & 14 deletions

File tree

crates/hir/src/semantics.rs

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ use hir_ty::{
3737
diagnostics::unsafe_operations,
3838
infer_query_with_inspect,
3939
next_solver::{
40-
AnyImplId, DbInterner,
40+
AnyImplId, Const as ResolvedConst, DbInterner,
4141
format_proof_tree::{ProofTreeData, dump_proof_tree_structured},
4242
},
4343
};
@@ -1713,6 +1713,18 @@ impl<'db> SemanticsImpl<'db> {
17131713
analyze.type_of_type(self.db, ty)
17141714
}
17151715

1716+
pub fn resolve_infer(
1717+
&self,
1718+
ty: &ast::InferType,
1719+
) -> Option<Either<Type<'db>, ResolvedConst<'db>>> {
1720+
let analyze = self.analyze(ty.syntax())?;
1721+
if let Some(const_) = analyze.resolve_infer_type_as_const(ty) {
1722+
return Some(Either::Right(const_));
1723+
}
1724+
let ty = analyze.type_of_type(self.db, &ast::Type::InferType(ty.clone()))?;
1725+
Some(Either::Left(ty))
1726+
}
1727+
17161728
pub fn resolve_trait(&self, path: &ast::Path) -> Option<Trait> {
17171729
let parent_ty = path.syntax().parent().and_then(ast::Type::cast)?;
17181730
let analyze = self.analyze(path.syntax())?;
@@ -1874,6 +1886,13 @@ impl<'db> SemanticsImpl<'db> {
18741886
self.analyze(try_expr.syntax())?.resolve_try_expr(self.db, try_expr)
18751887
}
18761888

1889+
pub fn resolve_underscore_expr(
1890+
&self,
1891+
underscore_expr: &ast::UnderscoreExpr,
1892+
) -> Option<ResolvedConst<'db>> {
1893+
self.analyze(underscore_expr.syntax())?.resolve_underscore_expr(underscore_expr)
1894+
}
1895+
18771896
/// The type that the associated `try` block, closure or function expects.
18781897
pub fn try_expr_returned_type(&self, try_expr: &ast::TryExpr) -> Option<Type<'db>> {
18791898
self.ancestors_with_macros(try_expr.syntax().clone()).find_map(|parent| {

crates/hir/src/source_analyzer.rs

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ use hir_def::{
2424
lang_item::LangItems,
2525
nameres::MacroSubNs,
2626
resolver::{Resolver, TypeNs, ValueNs, resolver_for_scope},
27-
type_ref::{Mutability, TypeRef, TypeRefId},
27+
type_ref::{ConstRef, Mutability, TypeRef, TypeRefId},
2828
};
2929
use hir_expand::{
3030
HirFileId, InFile,
@@ -41,8 +41,8 @@ use hir_ty::{
4141
lang_items::lang_items_for_bin_op,
4242
method_resolution::{self, CandidateId},
4343
next_solver::{
44-
AliasTy, DbInterner, ErrorGuaranteed, GenericArgs, ParamEnv, Ty, TyKind, TypingMode,
45-
infer::DbInternerInferExt,
44+
AliasTy, Const as ResolvedConst, DbInterner, ErrorGuaranteed, GenericArgs, ParamEnv, Ty,
45+
TyKind, TypingMode, infer::DbInternerInferExt,
4646
},
4747
traits::structurally_normalize_ty,
4848
};
@@ -870,6 +870,22 @@ impl<'db> SourceAnalyzer<'db> {
870870
Some(self.resolve_impl_method_or_trait_def(db, op_fn, substs))
871871
}
872872

873+
pub(crate) fn resolve_underscore_expr(
874+
&self,
875+
underscore_expr: &ast::UnderscoreExpr,
876+
) -> Option<ResolvedConst<'db>> {
877+
let expr = self.expr_id(ast::Expr::UnderscoreExpr(underscore_expr.clone()))?.as_expr()?;
878+
self.infer()?.const_of_const_placeholder(ConstRef { expr }.into())
879+
}
880+
881+
pub(crate) fn resolve_infer_type_as_const(
882+
&self,
883+
infer_type: &ast::InferType,
884+
) -> Option<ResolvedConst<'db>> {
885+
let type_ref = self.type_id(&ast::Type::InferType(infer_type.clone()))?;
886+
self.infer()?.const_of_const_placeholder(type_ref.into())
887+
}
888+
873889
pub(crate) fn resolve_record_field(
874890
&self,
875891
db: &'db dyn HirDatabase,

crates/ide/src/inlay_hints.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,7 @@ fn hints(
240240
},
241241
ast::Expr::RangeExpr(it) => range_exclusive::hints(hints, famous_defs, config, it),
242242
ast::Expr::Literal(it) => ra_fixture::hints(hints, famous_defs.0, file_id, config, it),
243+
ast::Expr::UnderscoreExpr(it) => placeholders::const_hints(hints, famous_defs, config, display_target, it),
243244
_ => Some(()),
244245
}
245246
},

crates/ide/src/inlay_hints/placeholders.rs

Lines changed: 51 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,12 @@
44
//! //^ = i32
55
//! ```
66
7-
use hir::DisplayTarget;
7+
use either::Either;
8+
use hir::{DisplayTarget, HirDisplay};
89
use ide_db::famous_defs::FamousDefs;
910
use syntax::{
1011
AstNode,
11-
ast::{InferType, Type},
12+
ast::{InferType, UnderscoreExpr},
1213
};
1314

1415
use crate::{InlayHint, InlayHintPosition, InlayHintsConfig, InlayKind, inlay_hints::label_of_ty};
@@ -27,9 +28,14 @@ pub(super) fn type_hints(
2728
let syntax = placeholder.syntax();
2829
let range = syntax.text_range();
2930

30-
let ty = sema.resolve_type(&Type::InferType(placeholder))?;
31+
let type_or_const = sema.resolve_infer(&placeholder)?;
3132

32-
let mut label = label_of_ty(famous_defs, config, &ty, display_target)?;
33+
let mut label = match type_or_const {
34+
Either::Left(ty) => label_of_ty(famous_defs, config, &ty, display_target)?,
35+
Either::Right(const_) => {
36+
const_.display_truncated(sema.db, config.max_length, display_target).to_string().into()
37+
}
38+
};
3339
label.prepend_str("= ");
3440

3541
acc.push(InlayHint {
@@ -45,6 +51,38 @@ pub(super) fn type_hints(
4551
Some(())
4652
}
4753

54+
pub(super) fn const_hints(
55+
acc: &mut Vec<InlayHint>,
56+
FamousDefs(sema, _): &FamousDefs<'_, '_>,
57+
config: &InlayHintsConfig<'_>,
58+
display_target: DisplayTarget,
59+
placeholder: UnderscoreExpr,
60+
) -> Option<()> {
61+
if !config.type_hints || config.hide_inferred_type_hints {
62+
return None;
63+
}
64+
65+
let syntax = placeholder.syntax();
66+
let range = syntax.text_range();
67+
68+
let const_ = sema.resolve_underscore_expr(&placeholder)?;
69+
70+
let display = const_.display_truncated(sema.db, config.max_length, display_target);
71+
let label = format!("= {display}").into();
72+
73+
acc.push(InlayHint {
74+
range,
75+
kind: InlayKind::Type,
76+
label,
77+
text_edit: None,
78+
position: InlayHintPosition::After,
79+
pad_left: true,
80+
pad_right: false,
81+
resolve_parent: None,
82+
});
83+
Some(())
84+
}
85+
4886
#[cfg(test)]
4987
mod tests {
5088
use crate::{
@@ -58,17 +96,20 @@ mod tests {
5896
}
5997

6098
#[test]
61-
fn inferred_types() {
99+
fn inferred_types_and_consts() {
62100
check_type_infer(
63101
r#"
64-
struct S<T>(T);
102+
struct S<T, const N: usize>([T; N]);
65103
66104
fn foo() {
67-
let t: (_, _, [_; _]) = (1_u32, S(2), [false] as _);
105+
let t: (_, S<_, _>, [_; _]) = (1_u32, S([2, 3]) as _, [false] as _);
68106
//^ = u32
69-
//^ = S<i32>
70-
//^ = bool
71-
//^ = [bool; 1]
107+
//^ = i32
108+
//^ = 2
109+
//^ = bool
110+
//^ = 1
111+
//^ = S<i32, 2>
112+
//^ = [bool; 1]
72113
}
73114
"#,
74115
);

0 commit comments

Comments
 (0)