Skip to content

Commit 7c092a1

Browse files
committed
Auto merge of rust-lang#14243 - Veykril:inference-diags, r=Veykril
feat: Diagnose unresolved field, method call and call expression
2 parents 3ba876a + e7485a0 commit 7c092a1

File tree

11 files changed

+628
-108
lines changed

11 files changed

+628
-108
lines changed

crates/hir-ty/src/infer.rs

+66-6
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ use hir_def::{
3131
AdtId, AssocItemId, DefWithBodyId, EnumVariantId, FieldId, FunctionId, HasModule,
3232
ItemContainerId, Lookup, TraitId, TypeAliasId, VariantId,
3333
};
34-
use hir_expand::name::name;
34+
use hir_expand::name::{name, Name};
3535
use la_arena::ArenaMap;
3636
use rustc_hash::FxHashMap;
3737
use stdx::always;
@@ -164,12 +164,45 @@ pub(crate) type InferResult<T> = Result<InferOk<T>, TypeError>;
164164

165165
#[derive(Debug, PartialEq, Eq, Clone)]
166166
pub enum InferenceDiagnostic {
167-
NoSuchField { expr: ExprId },
168-
PrivateField { expr: ExprId, field: FieldId },
169-
PrivateAssocItem { id: ExprOrPatId, item: AssocItemId },
167+
NoSuchField {
168+
expr: ExprId,
169+
},
170+
PrivateField {
171+
expr: ExprId,
172+
field: FieldId,
173+
},
174+
PrivateAssocItem {
175+
id: ExprOrPatId,
176+
item: AssocItemId,
177+
},
178+
UnresolvedField {
179+
expr: ExprId,
180+
receiver: Ty,
181+
name: Name,
182+
method_with_same_name_exists: bool,
183+
},
184+
UnresolvedMethodCall {
185+
expr: ExprId,
186+
receiver: Ty,
187+
name: Name,
188+
/// Contains the type the field resolves to
189+
field_with_same_name: Option<Ty>,
190+
},
170191
// FIXME: Make this proper
171-
BreakOutsideOfLoop { expr: ExprId, is_break: bool, bad_value_break: bool },
172-
MismatchedArgCount { call_expr: ExprId, expected: usize, found: usize },
192+
BreakOutsideOfLoop {
193+
expr: ExprId,
194+
is_break: bool,
195+
bad_value_break: bool,
196+
},
197+
MismatchedArgCount {
198+
call_expr: ExprId,
199+
expected: usize,
200+
found: usize,
201+
},
202+
ExpectedFunction {
203+
call_expr: ExprId,
204+
found: Ty,
205+
},
173206
}
174207

175208
/// A mismatch between an expected and an inferred type.
@@ -505,6 +538,33 @@ impl<'a> InferenceContext<'a> {
505538
mismatch.expected = table.resolve_completely(mismatch.expected.clone());
506539
mismatch.actual = table.resolve_completely(mismatch.actual.clone());
507540
}
541+
result.diagnostics.retain_mut(|diagnostic| {
542+
if let InferenceDiagnostic::ExpectedFunction { found: ty, .. }
543+
| InferenceDiagnostic::UnresolvedField { receiver: ty, .. }
544+
| InferenceDiagnostic::UnresolvedMethodCall { receiver: ty, .. } = diagnostic
545+
{
546+
*ty = table.resolve_completely(ty.clone());
547+
// FIXME: Remove this when we are on par with rustc in terms of inference
548+
if ty.is_unknown() {
549+
return false;
550+
}
551+
552+
if let InferenceDiagnostic::UnresolvedMethodCall { field_with_same_name, .. } =
553+
diagnostic
554+
{
555+
let clear = if let Some(ty) = field_with_same_name {
556+
*ty = table.resolve_completely(ty.clone());
557+
ty.is_unknown()
558+
} else {
559+
false
560+
};
561+
if clear {
562+
*field_with_same_name = None;
563+
}
564+
}
565+
}
566+
true
567+
});
508568
for (_, subst) in result.method_resolutions.values_mut() {
509569
*subst = table.resolve_completely(subst.clone());
510570
}

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

+144-71
Original file line numberDiff line numberDiff line change
@@ -364,7 +364,13 @@ impl<'a> InferenceContext<'a> {
364364
}
365365
(params, ret_ty)
366366
}
367-
None => (Vec::new(), self.err_ty()), // FIXME diagnostic
367+
None => {
368+
self.result.diagnostics.push(InferenceDiagnostic::ExpectedFunction {
369+
call_expr: tgt_expr,
370+
found: callee_ty.clone(),
371+
});
372+
(Vec::new(), self.err_ty())
373+
}
368374
};
369375
let indices_to_skip = self.check_legacy_const_generics(derefed_callee, args);
370376
self.register_obligations_for_call(&callee_ty);
@@ -546,71 +552,7 @@ impl<'a> InferenceContext<'a> {
546552
}
547553
ty
548554
}
549-
Expr::Field { expr, name } => {
550-
let receiver_ty = self.infer_expr_inner(*expr, &Expectation::none());
551-
552-
let mut autoderef = Autoderef::new(&mut self.table, receiver_ty);
553-
let mut private_field = None;
554-
let ty = autoderef.by_ref().find_map(|(derefed_ty, _)| {
555-
let (field_id, parameters) = match derefed_ty.kind(Interner) {
556-
TyKind::Tuple(_, substs) => {
557-
return name.as_tuple_index().and_then(|idx| {
558-
substs
559-
.as_slice(Interner)
560-
.get(idx)
561-
.map(|a| a.assert_ty_ref(Interner))
562-
.cloned()
563-
});
564-
}
565-
TyKind::Adt(AdtId(hir_def::AdtId::StructId(s)), parameters) => {
566-
let local_id = self.db.struct_data(*s).variant_data.field(name)?;
567-
let field = FieldId { parent: (*s).into(), local_id };
568-
(field, parameters.clone())
569-
}
570-
TyKind::Adt(AdtId(hir_def::AdtId::UnionId(u)), parameters) => {
571-
let local_id = self.db.union_data(*u).variant_data.field(name)?;
572-
let field = FieldId { parent: (*u).into(), local_id };
573-
(field, parameters.clone())
574-
}
575-
_ => return None,
576-
};
577-
let is_visible = self.db.field_visibilities(field_id.parent)[field_id.local_id]
578-
.is_visible_from(self.db.upcast(), self.resolver.module());
579-
if !is_visible {
580-
if private_field.is_none() {
581-
private_field = Some(field_id);
582-
}
583-
return None;
584-
}
585-
// can't have `write_field_resolution` here because `self.table` is borrowed :(
586-
self.result.field_resolutions.insert(tgt_expr, field_id);
587-
let ty = self.db.field_types(field_id.parent)[field_id.local_id]
588-
.clone()
589-
.substitute(Interner, &parameters);
590-
Some(ty)
591-
});
592-
let ty = match ty {
593-
Some(ty) => {
594-
let adjustments = auto_deref_adjust_steps(&autoderef);
595-
self.write_expr_adj(*expr, adjustments);
596-
let ty = self.insert_type_vars(ty);
597-
let ty = self.normalize_associated_types_in(ty);
598-
ty
599-
}
600-
_ => {
601-
// Write down the first private field resolution if we found no field
602-
// This aids IDE features for private fields like goto def
603-
if let Some(field) = private_field {
604-
self.result.field_resolutions.insert(tgt_expr, field);
605-
self.result
606-
.diagnostics
607-
.push(InferenceDiagnostic::PrivateField { expr: tgt_expr, field });
608-
}
609-
self.err_ty()
610-
}
611-
};
612-
ty
613-
}
555+
Expr::Field { expr, name } => self.infer_field_access(tgt_expr, *expr, name),
614556
Expr::Await { expr } => {
615557
let inner_ty = self.infer_expr_inner(*expr, &Expectation::none());
616558
self.resolve_associated_type(inner_ty, self.resolve_future_future_output())
@@ -1270,6 +1212,118 @@ impl<'a> InferenceContext<'a> {
12701212
}
12711213
}
12721214

1215+
fn lookup_field(
1216+
&mut self,
1217+
receiver_ty: &Ty,
1218+
name: &Name,
1219+
) -> Option<(Ty, Option<FieldId>, Vec<Adjustment>, bool)> {
1220+
let mut autoderef = Autoderef::new(&mut self.table, receiver_ty.clone());
1221+
let mut private_field = None;
1222+
let res = autoderef.by_ref().find_map(|(derefed_ty, _)| {
1223+
let (field_id, parameters) = match derefed_ty.kind(Interner) {
1224+
TyKind::Tuple(_, substs) => {
1225+
return name.as_tuple_index().and_then(|idx| {
1226+
substs
1227+
.as_slice(Interner)
1228+
.get(idx)
1229+
.map(|a| a.assert_ty_ref(Interner))
1230+
.cloned()
1231+
.map(|ty| (None, ty))
1232+
});
1233+
}
1234+
TyKind::Adt(AdtId(hir_def::AdtId::StructId(s)), parameters) => {
1235+
let local_id = self.db.struct_data(*s).variant_data.field(name)?;
1236+
let field = FieldId { parent: (*s).into(), local_id };
1237+
(field, parameters.clone())
1238+
}
1239+
TyKind::Adt(AdtId(hir_def::AdtId::UnionId(u)), parameters) => {
1240+
let local_id = self.db.union_data(*u).variant_data.field(name)?;
1241+
let field = FieldId { parent: (*u).into(), local_id };
1242+
(field, parameters.clone())
1243+
}
1244+
_ => return None,
1245+
};
1246+
let is_visible = self.db.field_visibilities(field_id.parent)[field_id.local_id]
1247+
.is_visible_from(self.db.upcast(), self.resolver.module());
1248+
if !is_visible {
1249+
if private_field.is_none() {
1250+
private_field = Some((field_id, parameters));
1251+
}
1252+
return None;
1253+
}
1254+
let ty = self.db.field_types(field_id.parent)[field_id.local_id]
1255+
.clone()
1256+
.substitute(Interner, &parameters);
1257+
Some((Some(field_id), ty))
1258+
});
1259+
1260+
Some(match res {
1261+
Some((field_id, ty)) => {
1262+
let adjustments = auto_deref_adjust_steps(&autoderef);
1263+
let ty = self.insert_type_vars(ty);
1264+
let ty = self.normalize_associated_types_in(ty);
1265+
1266+
(ty, field_id, adjustments, true)
1267+
}
1268+
None => {
1269+
let (field_id, subst) = private_field?;
1270+
let adjustments = auto_deref_adjust_steps(&autoderef);
1271+
let ty = self.db.field_types(field_id.parent)[field_id.local_id]
1272+
.clone()
1273+
.substitute(Interner, &subst);
1274+
let ty = self.insert_type_vars(ty);
1275+
let ty = self.normalize_associated_types_in(ty);
1276+
1277+
(ty, Some(field_id), adjustments, false)
1278+
}
1279+
})
1280+
}
1281+
1282+
fn infer_field_access(&mut self, tgt_expr: ExprId, receiver: ExprId, name: &Name) -> Ty {
1283+
let receiver_ty = self.infer_expr_inner(receiver, &Expectation::none());
1284+
match self.lookup_field(&receiver_ty, name) {
1285+
Some((ty, field_id, adjustments, is_public)) => {
1286+
self.write_expr_adj(receiver, adjustments);
1287+
if let Some(field_id) = field_id {
1288+
self.result.field_resolutions.insert(tgt_expr, field_id);
1289+
}
1290+
if !is_public {
1291+
if let Some(field) = field_id {
1292+
// FIXME: Merge this diagnostic into UnresolvedField?
1293+
self.result
1294+
.diagnostics
1295+
.push(InferenceDiagnostic::PrivateField { expr: tgt_expr, field });
1296+
}
1297+
}
1298+
ty
1299+
}
1300+
None => {
1301+
// no field found,
1302+
let method_with_same_name_exists = {
1303+
let canonicalized_receiver = self.canonicalize(receiver_ty.clone());
1304+
let traits_in_scope = self.resolver.traits_in_scope(self.db.upcast());
1305+
1306+
method_resolution::lookup_method(
1307+
self.db,
1308+
&canonicalized_receiver.value,
1309+
self.trait_env.clone(),
1310+
&traits_in_scope,
1311+
VisibleFromModule::Filter(self.resolver.module()),
1312+
name,
1313+
)
1314+
.is_some()
1315+
};
1316+
self.result.diagnostics.push(InferenceDiagnostic::UnresolvedField {
1317+
expr: tgt_expr,
1318+
receiver: receiver_ty,
1319+
name: name.clone(),
1320+
method_with_same_name_exists,
1321+
});
1322+
self.err_ty()
1323+
}
1324+
}
1325+
}
1326+
12731327
fn infer_method_call(
12741328
&mut self,
12751329
tgt_expr: ExprId,
@@ -1307,11 +1361,30 @@ impl<'a> InferenceContext<'a> {
13071361
}
13081362
(ty, self.db.value_ty(func.into()), substs)
13091363
}
1310-
None => (
1311-
receiver_ty,
1312-
Binders::empty(Interner, self.err_ty()),
1313-
Substitution::empty(Interner),
1314-
),
1364+
None => {
1365+
let field_with_same_name_exists = match self.lookup_field(&receiver_ty, method_name)
1366+
{
1367+
Some((ty, field_id, adjustments, _public)) => {
1368+
self.write_expr_adj(receiver, adjustments);
1369+
if let Some(field_id) = field_id {
1370+
self.result.field_resolutions.insert(tgt_expr, field_id);
1371+
}
1372+
Some(ty)
1373+
}
1374+
None => None,
1375+
};
1376+
self.result.diagnostics.push(InferenceDiagnostic::UnresolvedMethodCall {
1377+
expr: tgt_expr,
1378+
receiver: receiver_ty.clone(),
1379+
name: method_name.clone(),
1380+
field_with_same_name: field_with_same_name_exists,
1381+
});
1382+
(
1383+
receiver_ty,
1384+
Binders::empty(Interner, self.err_ty()),
1385+
Substitution::empty(Interner),
1386+
)
1387+
}
13151388
};
13161389
let method_ty = method_ty.substitute(Interner, &substs);
13171390
self.register_obligations_for_call(&method_ty);

crates/hir/src/diagnostics.rs

+25
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ macro_rules! diagnostics {
3131

3232
diagnostics![
3333
BreakOutsideOfLoop,
34+
ExpectedFunction,
3435
InactiveCode,
3536
IncorrectCase,
3637
InvalidDeriveTarget,
@@ -47,8 +48,10 @@ diagnostics![
4748
TypeMismatch,
4849
UnimplementedBuiltinMacro,
4950
UnresolvedExternCrate,
51+
UnresolvedField,
5052
UnresolvedImport,
5153
UnresolvedMacroCall,
54+
UnresolvedMethodCall,
5255
UnresolvedModule,
5356
UnresolvedProcMacro,
5457
];
@@ -130,6 +133,28 @@ pub struct PrivateAssocItem {
130133
pub item: AssocItem,
131134
}
132135

136+
#[derive(Debug)]
137+
pub struct ExpectedFunction {
138+
pub call: InFile<AstPtr<ast::Expr>>,
139+
pub found: Type,
140+
}
141+
142+
#[derive(Debug)]
143+
pub struct UnresolvedField {
144+
pub expr: InFile<AstPtr<ast::Expr>>,
145+
pub receiver: Type,
146+
pub name: Name,
147+
pub method_with_same_name_exists: bool,
148+
}
149+
150+
#[derive(Debug)]
151+
pub struct UnresolvedMethodCall {
152+
pub expr: InFile<AstPtr<ast::Expr>>,
153+
pub receiver: Type,
154+
pub name: Name,
155+
pub field_with_same_name: Option<Type>,
156+
}
157+
133158
#[derive(Debug)]
134159
pub struct PrivateField {
135160
pub expr: InFile<AstPtr<ast::Expr>>,

0 commit comments

Comments
 (0)