Skip to content

Commit 5d2cd18

Browse files
Merge #11780
11780: feat: Add type mismatch diagnostic r=flodiebold a=flodiebold This adds a proper diagnostic for type mismatches, turning "Add reference here", "Missing Ok or Some" and "Remove this semicolon" into quickfixes for this single diagnostic. The diagnostic is marked as experimental when it does not have one of these quickfixes, so it can be turned off with `rust-analyzer.diagnostics.enableExperimental` (or specifically with `rust-analyzer.diagnostics.disabled` of course, the ID is `type-mismatch`). There will still be some false positives, but I think there shouldn't be too many especially when the Chalk fix lands, and it's still experimental anyway 🙂 This also fixes type checking for `rustc_legacy_const_generics` just to avoid some errors in tests. Co-authored-by: Florian Diebold <[email protected]>
2 parents 754d868 + 3bdb68d commit 5d2cd18

File tree

17 files changed

+772
-688
lines changed

17 files changed

+772
-688
lines changed

crates/hir/src/diagnostics.rs

Lines changed: 6 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
//! be expressed in terms of hir types themselves.
66
use cfg::{CfgExpr, CfgOptions};
77
use either::Either;
8-
use hir_def::{path::ModPath, type_ref::Mutability};
8+
use hir_def::path::ModPath;
99
use hir_expand::{name::Name, HirFileId, InFile};
1010
use syntax::{ast, AstPtr, SyntaxNodePtr, TextRange};
1111

@@ -28,7 +28,6 @@ macro_rules! diagnostics {
2828
}
2929

3030
diagnostics![
31-
AddReferenceHere,
3231
BreakOutsideOfLoop,
3332
InactiveCode,
3433
IncorrectCase,
@@ -38,11 +37,10 @@ diagnostics![
3837
MismatchedArgCount,
3938
MissingFields,
4039
MissingMatchArms,
41-
MissingOkOrSomeInTailExpr,
4240
MissingUnsafe,
4341
NoSuchField,
44-
RemoveThisSemicolon,
4542
ReplaceFilterMapNextWithFindMap,
43+
TypeMismatch,
4644
UnimplementedBuiltinMacro,
4745
UnresolvedExternCrate,
4846
UnresolvedImport,
@@ -147,29 +145,18 @@ pub struct MismatchedArgCount {
147145
pub found: usize,
148146
}
149147

150-
#[derive(Debug)]
151-
pub struct RemoveThisSemicolon {
152-
pub expr: InFile<AstPtr<ast::Expr>>,
153-
}
154-
155-
#[derive(Debug)]
156-
pub struct MissingOkOrSomeInTailExpr {
157-
pub expr: InFile<AstPtr<ast::Expr>>,
158-
// `Some` or `Ok` depending on whether the return type is Result or Option
159-
pub required: String,
160-
pub expected: Type,
161-
}
162-
163148
#[derive(Debug)]
164149
pub struct MissingMatchArms {
165150
pub file: HirFileId,
166151
pub match_expr: AstPtr<ast::Expr>,
167152
}
168153

169154
#[derive(Debug)]
170-
pub struct AddReferenceHere {
155+
pub struct TypeMismatch {
156+
// FIXME: add mismatches in patterns as well
171157
pub expr: InFile<AstPtr<ast::Expr>>,
172-
pub mutability: Mutability,
158+
pub expected: Type,
159+
pub actual: Type,
173160
}
174161

175162
pub use hir_ty::diagnostics::IncorrectCase;

crates/hir/src/lib.rs

Lines changed: 79 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,6 @@ use hir_ty::{
5858
consteval::{
5959
eval_const, unknown_const_as_generic, ComputedExpr, ConstEvalCtx, ConstEvalError, ConstExt,
6060
},
61-
could_unify,
6261
diagnostics::BodyValidationDiagnostic,
6362
method_resolution::{self, TyFingerprint},
6463
primitive::UintTy,
@@ -85,12 +84,11 @@ use crate::db::{DefDatabase, HirDatabase};
8584
pub use crate::{
8685
attrs::{HasAttrs, Namespace},
8786
diagnostics::{
88-
AddReferenceHere, AnyDiagnostic, BreakOutsideOfLoop, InactiveCode, IncorrectCase,
89-
InvalidDeriveTarget, MacroError, MalformedDerive, MismatchedArgCount, MissingFields,
90-
MissingMatchArms, MissingOkOrSomeInTailExpr, MissingUnsafe, NoSuchField,
91-
RemoveThisSemicolon, ReplaceFilterMapNextWithFindMap, UnimplementedBuiltinMacro,
92-
UnresolvedExternCrate, UnresolvedImport, UnresolvedMacroCall, UnresolvedModule,
93-
UnresolvedProcMacro,
87+
AnyDiagnostic, BreakOutsideOfLoop, InactiveCode, IncorrectCase, InvalidDeriveTarget,
88+
MacroError, MalformedDerive, MismatchedArgCount, MissingFields, MissingMatchArms,
89+
MissingUnsafe, NoSuchField, ReplaceFilterMapNextWithFindMap, TypeMismatch,
90+
UnimplementedBuiltinMacro, UnresolvedExternCrate, UnresolvedImport, UnresolvedMacroCall,
91+
UnresolvedModule, UnresolvedProcMacro,
9492
},
9593
has_source::HasSource,
9694
semantics::{PathResolution, Semantics, SemanticsScope, TypeInfo},
@@ -1005,6 +1003,24 @@ impl Adt {
10051003
Type::from_def(db, id.module(db.upcast()).krate(), id)
10061004
}
10071005

1006+
/// Turns this ADT into a type with the given type parameters. This isn't
1007+
/// the greatest API, FIXME find a better one.
1008+
pub fn ty_with_args(self, db: &dyn HirDatabase, args: &[Type]) -> Type {
1009+
let id = AdtId::from(self);
1010+
let mut it = args.iter().map(|t| t.ty.clone());
1011+
let ty = TyBuilder::def_ty(db, id.into())
1012+
.fill(|x| {
1013+
let r = it.next().unwrap_or_else(|| TyKind::Error.intern(Interner));
1014+
match x {
1015+
ParamKind::Type => GenericArgData::Ty(r).intern(Interner),
1016+
ParamKind::Const(ty) => unknown_const_as_generic(ty.clone()),
1017+
}
1018+
})
1019+
.build();
1020+
let krate = id.module(db.upcast()).krate();
1021+
Type::new(db, krate, id, ty)
1022+
}
1023+
10081024
pub fn module(self, db: &dyn HirDatabase) -> Module {
10091025
match self {
10101026
Adt::Struct(s) => s.module(db),
@@ -1020,6 +1036,14 @@ impl Adt {
10201036
Adt::Enum(e) => e.name(db),
10211037
}
10221038
}
1039+
1040+
pub fn as_enum(&self) -> Option<Enum> {
1041+
if let Self::Enum(v) = self {
1042+
Some(*v)
1043+
} else {
1044+
None
1045+
}
1046+
}
10231047
}
10241048

10251049
impl HasVisibility for Adt {
@@ -1163,6 +1187,30 @@ impl DefWithBody {
11631187
}
11641188
}
11651189
}
1190+
for (expr, mismatch) in infer.expr_type_mismatches() {
1191+
let expr = match source_map.expr_syntax(expr) {
1192+
Ok(expr) => expr,
1193+
Err(SyntheticSyntax) => continue,
1194+
};
1195+
acc.push(
1196+
TypeMismatch {
1197+
expr,
1198+
expected: Type::new(
1199+
db,
1200+
krate,
1201+
DefWithBodyId::from(self),
1202+
mismatch.expected.clone(),
1203+
),
1204+
actual: Type::new(
1205+
db,
1206+
krate,
1207+
DefWithBodyId::from(self),
1208+
mismatch.actual.clone(),
1209+
),
1210+
}
1211+
.into(),
1212+
);
1213+
}
11661214

11671215
for expr in hir_ty::diagnostics::missing_unsafe(db, self.into()) {
11681216
match source_map.expr_syntax(expr) {
@@ -1259,25 +1307,6 @@ impl DefWithBody {
12591307
Err(SyntheticSyntax) => (),
12601308
}
12611309
}
1262-
BodyValidationDiagnostic::RemoveThisSemicolon { expr } => {
1263-
match source_map.expr_syntax(expr) {
1264-
Ok(expr) => acc.push(RemoveThisSemicolon { expr }.into()),
1265-
Err(SyntheticSyntax) => (),
1266-
}
1267-
}
1268-
BodyValidationDiagnostic::MissingOkOrSomeInTailExpr { expr, required } => {
1269-
match source_map.expr_syntax(expr) {
1270-
Ok(expr) => acc.push(
1271-
MissingOkOrSomeInTailExpr {
1272-
expr,
1273-
required,
1274-
expected: self.body_type(db),
1275-
}
1276-
.into(),
1277-
),
1278-
Err(SyntheticSyntax) => (),
1279-
}
1280-
}
12811310
BodyValidationDiagnostic::MissingMatchArms { match_expr } => {
12821311
match source_map.expr_syntax(match_expr) {
12831312
Ok(source_ptr) => {
@@ -1299,12 +1328,6 @@ impl DefWithBody {
12991328
Err(SyntheticSyntax) => (),
13001329
}
13011330
}
1302-
BodyValidationDiagnostic::AddReferenceHere { arg_expr, mutability } => {
1303-
match source_map.expr_syntax(arg_expr) {
1304-
Ok(expr) => acc.push(AddReferenceHere { expr, mutability }.into()),
1305-
Err(SyntheticSyntax) => (),
1306-
}
1307-
}
13081331
}
13091332
}
13101333

@@ -2618,6 +2641,17 @@ impl Type {
26182641
Type { krate, env: environment, ty }
26192642
}
26202643

2644+
pub fn reference(inner: &Type, m: Mutability) -> Type {
2645+
inner.derived(
2646+
TyKind::Ref(
2647+
if m.is_mut() { hir_ty::Mutability::Mut } else { hir_ty::Mutability::Not },
2648+
hir_ty::static_lifetime(),
2649+
inner.ty.clone(),
2650+
)
2651+
.intern(Interner),
2652+
)
2653+
}
2654+
26212655
fn new(db: &dyn HirDatabase, krate: CrateId, lexical_env: impl HasResolver, ty: Ty) -> Type {
26222656
let resolver = lexical_env.resolver(db.upcast());
26232657
let environment = resolver
@@ -2659,6 +2693,12 @@ impl Type {
26592693
matches!(self.ty.kind(Interner), TyKind::Ref(..))
26602694
}
26612695

2696+
pub fn as_reference(&self) -> Option<(Type, Mutability)> {
2697+
let (ty, _lt, m) = self.ty.as_reference()?;
2698+
let m = Mutability::from_mutable(matches!(m, hir_ty::Mutability::Mut));
2699+
Some((self.derived(ty.clone()), m))
2700+
}
2701+
26622702
pub fn is_slice(&self) -> bool {
26632703
matches!(self.ty.kind(Interner), TyKind::Slice(..))
26642704
}
@@ -2900,7 +2940,7 @@ impl Type {
29002940
self.autoderef_(db).map(move |ty| self.derived(ty))
29012941
}
29022942

2903-
pub fn autoderef_<'a>(&'a self, db: &'a dyn HirDatabase) -> impl Iterator<Item = Ty> + 'a {
2943+
fn autoderef_<'a>(&'a self, db: &'a dyn HirDatabase) -> impl Iterator<Item = Ty> + 'a {
29042944
// There should be no inference vars in types passed here
29052945
let canonical = hir_ty::replace_errors_with_variables(&self.ty);
29062946
let environment = self.env.clone();
@@ -3238,7 +3278,12 @@ impl Type {
32383278

32393279
pub fn could_unify_with(&self, db: &dyn HirDatabase, other: &Type) -> bool {
32403280
let tys = hir_ty::replace_errors_with_variables(&(self.ty.clone(), other.ty.clone()));
3241-
could_unify(db, self.env.clone(), &tys)
3281+
hir_ty::could_unify(db, self.env.clone(), &tys)
3282+
}
3283+
3284+
pub fn could_coerce_to(&self, db: &dyn HirDatabase, to: &Type) -> bool {
3285+
let tys = hir_ty::replace_errors_with_variables(&(self.ty.clone(), to.ty.clone()));
3286+
hir_ty::could_coerce(db, self.env.clone(), &tys)
32423287
}
32433288
}
32443289

crates/hir_def/src/type_ref.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,22 @@ impl Mutability {
3838
Mutability::Mut => "mut ",
3939
}
4040
}
41+
42+
/// Returns `true` if the mutability is [`Mut`].
43+
///
44+
/// [`Mut`]: Mutability::Mut
45+
#[must_use]
46+
pub fn is_mut(&self) -> bool {
47+
matches!(self, Self::Mut)
48+
}
49+
50+
/// Returns `true` if the mutability is [`Shared`].
51+
///
52+
/// [`Shared`]: Mutability::Shared
53+
#[must_use]
54+
pub fn is_shared(&self) -> bool {
55+
matches!(self, Self::Shared)
56+
}
4157
}
4258

4359
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]

0 commit comments

Comments
 (0)