Skip to content

Commit 505fd09

Browse files
committed
Auto merge of #14916 - Veykril:typed-hole, r=Veykril
feat: Add diagnostic for `_` expressions (typed holes)
2 parents 150082b + 8bc826d commit 505fd09

File tree

9 files changed

+368
-61
lines changed

9 files changed

+368
-61
lines changed

crates/base-db/src/fixture.rs

+7
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ use crate::{
2121
pub const WORKSPACE: SourceRootId = SourceRootId(0);
2222

2323
pub trait WithFixture: Default + SourceDatabaseExt + 'static {
24+
#[track_caller]
2425
fn with_single_file(ra_fixture: &str) -> (Self, FileId) {
2526
let fixture = ChangeFixture::parse(ra_fixture);
2627
let mut db = Self::default();
@@ -29,6 +30,7 @@ pub trait WithFixture: Default + SourceDatabaseExt + 'static {
2930
(db, fixture.files[0])
3031
}
3132

33+
#[track_caller]
3234
fn with_many_files(ra_fixture: &str) -> (Self, Vec<FileId>) {
3335
let fixture = ChangeFixture::parse(ra_fixture);
3436
let mut db = Self::default();
@@ -37,6 +39,7 @@ pub trait WithFixture: Default + SourceDatabaseExt + 'static {
3739
(db, fixture.files)
3840
}
3941

42+
#[track_caller]
4043
fn with_files(ra_fixture: &str) -> Self {
4144
let fixture = ChangeFixture::parse(ra_fixture);
4245
let mut db = Self::default();
@@ -45,6 +48,7 @@ pub trait WithFixture: Default + SourceDatabaseExt + 'static {
4548
db
4649
}
4750

51+
#[track_caller]
4852
fn with_files_extra_proc_macros(
4953
ra_fixture: &str,
5054
proc_macros: Vec<(String, ProcMacro)>,
@@ -56,18 +60,21 @@ pub trait WithFixture: Default + SourceDatabaseExt + 'static {
5660
db
5761
}
5862

63+
#[track_caller]
5964
fn with_position(ra_fixture: &str) -> (Self, FilePosition) {
6065
let (db, file_id, range_or_offset) = Self::with_range_or_offset(ra_fixture);
6166
let offset = range_or_offset.expect_offset();
6267
(db, FilePosition { file_id, offset })
6368
}
6469

70+
#[track_caller]
6571
fn with_range(ra_fixture: &str) -> (Self, FileRange) {
6672
let (db, file_id, range_or_offset) = Self::with_range_or_offset(ra_fixture);
6773
let range = range_or_offset.expect_range();
6874
(db, FileRange { file_id, range })
6975
}
7076

77+
#[track_caller]
7178
fn with_range_or_offset(ra_fixture: &str) -> (Self, FileId, RangeOrOffset) {
7279
let fixture = ChangeFixture::parse(ra_fixture);
7380
let mut db = Self::default();

crates/hir-def/src/resolver.rs

+6
Original file line numberDiff line numberDiff line change
@@ -1046,6 +1046,12 @@ impl HasResolver for GenericDefId {
10461046
}
10471047
}
10481048

1049+
impl HasResolver for EnumVariantId {
1050+
fn resolver(self, db: &dyn DefDatabase) -> Resolver {
1051+
self.parent.resolver(db)
1052+
}
1053+
}
1054+
10491055
impl HasResolver for VariantId {
10501056
fn resolver(self, db: &dyn DefDatabase) -> Resolver {
10511057
match self {

crates/hir-ty/src/builder.rs

+13
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,19 @@ impl TyBuilder<()> {
194194
params.placeholder_subst(db)
195195
}
196196

197+
pub fn unknown_subst(db: &dyn HirDatabase, def: impl Into<GenericDefId>) -> Substitution {
198+
let params = generics(db.upcast(), def.into());
199+
Substitution::from_iter(
200+
Interner,
201+
params.iter_id().map(|id| match id {
202+
either::Either::Left(_) => TyKind::Error.intern(Interner).cast(Interner),
203+
either::Either::Right(id) => {
204+
unknown_const_as_generic(db.const_param_ty(id)).cast(Interner)
205+
}
206+
}),
207+
)
208+
}
209+
197210
pub fn subst_for_def(
198211
db: &dyn HirDatabase,
199212
def: impl Into<GenericDefId>,

crates/hir-ty/src/infer.rs

+25-20
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,10 @@ pub enum InferenceDiagnostic {
215215
call_expr: ExprId,
216216
found: Ty,
217217
},
218+
TypedHole {
219+
expr: ExprId,
220+
expected: Ty,
221+
},
218222
}
219223

220224
/// A mismatch between an expected and an inferred type.
@@ -600,29 +604,30 @@ impl<'a> InferenceContext<'a> {
600604
mismatch.actual = table.resolve_completely(mismatch.actual.clone());
601605
}
602606
result.diagnostics.retain_mut(|diagnostic| {
603-
if let InferenceDiagnostic::ExpectedFunction { found: ty, .. }
604-
| InferenceDiagnostic::UnresolvedField { receiver: ty, .. }
605-
| InferenceDiagnostic::UnresolvedMethodCall { receiver: ty, .. } = diagnostic
606-
{
607-
*ty = table.resolve_completely(ty.clone());
608-
// FIXME: Remove this when we are on par with rustc in terms of inference
609-
if ty.contains_unknown() {
610-
return false;
611-
}
607+
use InferenceDiagnostic::*;
608+
match diagnostic {
609+
ExpectedFunction { found: ty, .. }
610+
| UnresolvedField { receiver: ty, .. }
611+
| UnresolvedMethodCall { receiver: ty, .. } => {
612+
*ty = table.resolve_completely(ty.clone());
613+
// FIXME: Remove this when we are on par with rustc in terms of inference
614+
if ty.contains_unknown() {
615+
return false;
616+
}
612617

613-
if let InferenceDiagnostic::UnresolvedMethodCall { field_with_same_name, .. } =
614-
diagnostic
615-
{
616-
let clear = if let Some(ty) = field_with_same_name {
617-
*ty = table.resolve_completely(ty.clone());
618-
ty.contains_unknown()
619-
} else {
620-
false
621-
};
622-
if clear {
623-
*field_with_same_name = None;
618+
if let UnresolvedMethodCall { field_with_same_name, .. } = diagnostic {
619+
if let Some(ty) = field_with_same_name {
620+
*ty = table.resolve_completely(ty.clone());
621+
if ty.contains_unknown() {
622+
*field_with_same_name = None;
623+
}
624+
}
624625
}
625626
}
627+
TypedHole { expected: ty, .. } => {
628+
*ty = table.resolve_completely(ty.clone());
629+
}
630+
_ => (),
626631
}
627632
true
628633
});

crates/hir-ty/src/infer/expr.rs

+16-9
Original file line numberDiff line numberDiff line change
@@ -874,9 +874,15 @@ impl<'a> InferenceContext<'a> {
874874
},
875875
Expr::Underscore => {
876876
// Underscore expressions may only appear in assignee expressions,
877-
// which are handled by `infer_assignee_expr()`, so any underscore
878-
// expression reaching this branch is an error.
879-
self.err_ty()
877+
// which are handled by `infer_assignee_expr()`.
878+
// Any other underscore expression is an error, we render a specialized diagnostic
879+
// to let the user know what type is expected though.
880+
let expected = expected.to_option(&mut self.table).unwrap_or_else(|| self.err_ty());
881+
self.push_diagnostic(InferenceDiagnostic::TypedHole {
882+
expr: tgt_expr,
883+
expected: expected.clone(),
884+
});
885+
expected
880886
}
881887
};
882888
// use a new type variable if we got unknown here
@@ -1001,12 +1007,13 @@ impl<'a> InferenceContext<'a> {
10011007
}
10021008
&Array::Repeat { initializer, repeat } => {
10031009
self.infer_expr_coerce(initializer, &Expectation::has_type(elem_ty.clone()));
1004-
self.infer_expr(
1005-
repeat,
1006-
&Expectation::HasType(
1007-
TyKind::Scalar(Scalar::Uint(UintTy::Usize)).intern(Interner),
1008-
),
1009-
);
1010+
let usize = TyKind::Scalar(Scalar::Uint(UintTy::Usize)).intern(Interner);
1011+
match self.body[repeat] {
1012+
Expr::Underscore => {
1013+
self.write_expr_ty(repeat, usize);
1014+
}
1015+
_ => _ = self.infer_expr(repeat, &Expectation::HasType(usize)),
1016+
}
10101017

10111018
(
10121019
elem_ty,

crates/hir/src/diagnostics.rs

+7
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ diagnostics![
5252
PrivateAssocItem,
5353
PrivateField,
5454
ReplaceFilterMapNextWithFindMap,
55+
TypedHole,
5556
TypeMismatch,
5657
UndeclaredLabel,
5758
UnimplementedBuiltinMacro,
@@ -73,6 +74,12 @@ pub struct BreakOutsideOfLoop {
7374
pub bad_value_break: bool,
7475
}
7576

77+
#[derive(Debug)]
78+
pub struct TypedHole {
79+
pub expr: InFile<AstPtr<ast::Expr>>,
80+
pub expected: Type,
81+
}
82+
7683
#[derive(Debug)]
7784
pub struct UnresolvedModule {
7885
pub decl: InFile<AstPtr<ast::Module>>,

crates/hir/src/lib.rs

+60-32
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,8 @@ use hir_ty::{
6969
traits::FnTrait,
7070
AliasTy, CallableDefId, CallableSig, Canonical, CanonicalVarKinds, Cast, ClosureId,
7171
GenericArgData, Interner, ParamKind, QuantifiedWhereClause, Scalar, Substitution,
72-
TraitEnvironment, TraitRefExt, Ty, TyBuilder, TyDefId, TyExt, TyKind, WhereClause,
72+
TraitEnvironment, TraitRefExt, Ty, TyBuilder, TyDefId, TyExt, TyKind, ValueTyDefId,
73+
WhereClause,
7374
};
7475
use itertools::Itertools;
7576
use nameres::diagnostics::DefDiagnosticKind;
@@ -91,10 +92,10 @@ pub use crate::{
9192
IncorrectCase, InvalidDeriveTarget, MacroDefError, MacroError, MacroExpansionParseError,
9293
MalformedDerive, MismatchedArgCount, MissingFields, MissingMatchArms, MissingUnsafe,
9394
MovedOutOfRef, NeedMut, NoSuchField, PrivateAssocItem, PrivateField,
94-
ReplaceFilterMapNextWithFindMap, TypeMismatch, UndeclaredLabel, UnimplementedBuiltinMacro,
95-
UnreachableLabel, UnresolvedExternCrate, UnresolvedField, UnresolvedImport,
96-
UnresolvedMacroCall, UnresolvedMethodCall, UnresolvedModule, UnresolvedProcMacro,
97-
UnusedMut,
95+
ReplaceFilterMapNextWithFindMap, TypeMismatch, TypedHole, UndeclaredLabel,
96+
UnimplementedBuiltinMacro, UnreachableLabel, UnresolvedExternCrate, UnresolvedField,
97+
UnresolvedImport, UnresolvedMacroCall, UnresolvedMethodCall, UnresolvedModule,
98+
UnresolvedProcMacro, UnusedMut,
9899
},
99100
has_source::HasSource,
100101
semantics::{PathResolution, Semantics, SemanticsScope, TypeInfo, VisibleTraits},
@@ -1005,6 +1006,10 @@ impl Struct {
10051006
Type::from_def(db, self.id)
10061007
}
10071008

1009+
pub fn constructor_ty(self, db: &dyn HirDatabase) -> Type {
1010+
Type::from_value_def(db, self.id)
1011+
}
1012+
10081013
pub fn repr(self, db: &dyn HirDatabase) -> Option<ReprOptions> {
10091014
db.struct_data(self.id).repr
10101015
}
@@ -1042,6 +1047,10 @@ impl Union {
10421047
Type::from_def(db, self.id)
10431048
}
10441049

1050+
pub fn constructor_ty(self, db: &dyn HirDatabase) -> Type {
1051+
Type::from_value_def(db, self.id)
1052+
}
1053+
10451054
pub fn fields(self, db: &dyn HirDatabase) -> Vec<Field> {
10461055
db.union_data(self.id)
10471056
.variant_data
@@ -1173,6 +1182,10 @@ impl Variant {
11731182
self.parent
11741183
}
11751184

1185+
pub fn constructor_ty(self, db: &dyn HirDatabase) -> Type {
1186+
Type::from_value_def(db, EnumVariantId { parent: self.parent.id, local_id: self.id })
1187+
}
1188+
11761189
pub fn name(self, db: &dyn HirDatabase) -> Name {
11771190
db.enum_data(self.parent.id).variants[self.id].name.clone()
11781191
}
@@ -1574,6 +1587,16 @@ impl DefWithBody {
15741587
let expr = expr_syntax(expr);
15751588
acc.push(BreakOutsideOfLoop { expr, is_break, bad_value_break }.into())
15761589
}
1590+
hir_ty::InferenceDiagnostic::TypedHole { expr, expected } => {
1591+
let expr = expr_syntax(*expr);
1592+
acc.push(
1593+
TypedHole {
1594+
expr,
1595+
expected: Type::new(db, DefWithBodyId::from(self), expected.clone()),
1596+
}
1597+
.into(),
1598+
)
1599+
}
15771600
}
15781601
}
15791602
for (pat_or_expr, mismatch) in infer.type_mismatches() {
@@ -1806,6 +1829,10 @@ impl Function {
18061829
db.function_data(self.id).name.clone()
18071830
}
18081831

1832+
pub fn ty(self, db: &dyn HirDatabase) -> Type {
1833+
Type::from_value_def(db, self.id)
1834+
}
1835+
18091836
/// Get this function's return type
18101837
pub fn ret_type(self, db: &dyn HirDatabase) -> Type {
18111838
let resolver = self.id.resolver(db.upcast());
@@ -2085,11 +2112,7 @@ impl Const {
20852112
}
20862113

20872114
pub fn ty(self, db: &dyn HirDatabase) -> Type {
2088-
let data = db.const_data(self.id);
2089-
let resolver = self.id.resolver(db.upcast());
2090-
let ctx = hir_ty::TyLoweringContext::new(db, &resolver);
2091-
let ty = ctx.lower_ty(&data.type_ref);
2092-
Type::new_with_resolver_inner(db, &resolver, ty)
2115+
Type::from_value_def(db, self.id)
20932116
}
20942117

20952118
pub fn render_eval(self, db: &dyn HirDatabase) -> Result<String, ConstEvalError> {
@@ -2136,11 +2159,7 @@ impl Static {
21362159
}
21372160

21382161
pub fn ty(self, db: &dyn HirDatabase) -> Type {
2139-
let data = db.static_data(self.id);
2140-
let resolver = self.id.resolver(db.upcast());
2141-
let ctx = hir_ty::TyLoweringContext::new(db, &resolver);
2142-
let ty = ctx.lower_ty(&data.type_ref);
2143-
Type::new_with_resolver_inner(db, &resolver, ty)
2162+
Type::from_value_def(db, self.id)
21442163
}
21452164
}
21462165

@@ -3409,24 +3428,33 @@ impl Type {
34093428
Type { env: environment, ty }
34103429
}
34113430

3412-
fn from_def(db: &dyn HirDatabase, def: impl HasResolver + Into<TyDefId>) -> Type {
3413-
let ty_def = def.into();
3414-
let parent_subst = match ty_def {
3415-
TyDefId::TypeAliasId(id) => match id.lookup(db.upcast()).container {
3416-
ItemContainerId::TraitId(id) => {
3417-
let subst = TyBuilder::subst_for_def(db, id, None).fill_with_unknown().build();
3418-
Some(subst)
3419-
}
3420-
ItemContainerId::ImplId(id) => {
3421-
let subst = TyBuilder::subst_for_def(db, id, None).fill_with_unknown().build();
3422-
Some(subst)
3423-
}
3424-
_ => None,
3431+
fn from_def(db: &dyn HirDatabase, def: impl Into<TyDefId> + HasResolver) -> Type {
3432+
let ty = db.ty(def.into());
3433+
let substs = TyBuilder::unknown_subst(
3434+
db,
3435+
match def.into() {
3436+
TyDefId::AdtId(it) => GenericDefId::AdtId(it),
3437+
TyDefId::TypeAliasId(it) => GenericDefId::TypeAliasId(it),
3438+
TyDefId::BuiltinType(_) => return Type::new(db, def, ty.skip_binders().clone()),
34253439
},
3426-
_ => None,
3427-
};
3428-
let ty = TyBuilder::def_ty(db, ty_def, parent_subst).fill_with_unknown().build();
3429-
Type::new(db, def, ty)
3440+
);
3441+
Type::new(db, def, ty.substitute(Interner, &substs))
3442+
}
3443+
3444+
fn from_value_def(db: &dyn HirDatabase, def: impl Into<ValueTyDefId> + HasResolver) -> Type {
3445+
let ty = db.value_ty(def.into());
3446+
let substs = TyBuilder::unknown_subst(
3447+
db,
3448+
match def.into() {
3449+
ValueTyDefId::ConstId(it) => GenericDefId::ConstId(it),
3450+
ValueTyDefId::FunctionId(it) => GenericDefId::FunctionId(it),
3451+
ValueTyDefId::StructId(it) => GenericDefId::AdtId(AdtId::StructId(it)),
3452+
ValueTyDefId::UnionId(it) => GenericDefId::AdtId(AdtId::UnionId(it)),
3453+
ValueTyDefId::EnumVariantId(it) => GenericDefId::EnumVariantId(it),
3454+
ValueTyDefId::StaticId(_) => return Type::new(db, def, ty.skip_binders().clone()),
3455+
},
3456+
);
3457+
Type::new(db, def, ty.substitute(Interner, &substs))
34303458
}
34313459

34323460
pub fn new_slice(ty: Type) -> Type {

0 commit comments

Comments
 (0)