diff --git a/crates/hir-ty/src/infer.rs b/crates/hir-ty/src/infer.rs index 51e9435c804d..1bfce5c785c7 100644 --- a/crates/hir-ty/src/infer.rs +++ b/crates/hir-ty/src/infer.rs @@ -352,6 +352,10 @@ pub enum InferenceDiagnostic { #[type_visitable(ignore)] id: ExprOrPatId, }, + UnresolvedVariant { + #[type_visitable(ignore)] + id: ExprOrPatId, + }, // FIXME: This should be emitted in body lowering BreakOutsideOfLoop { #[type_visitable(ignore)] @@ -2274,6 +2278,8 @@ impl<'body, 'db> InferenceContext<'body, 'db> { let interner = DbInterner::conjure(); let (resolution, unresolved) = if value_ns { let Some(res) = path_ctx.resolve_path_in_value_ns(HygieneId::ROOT) else { + drop(ctx); + self.push_diagnostic(InferenceDiagnostic::UnresolvedVariant { id: node }); return (self.types.types.error, None); }; match res { @@ -2309,7 +2315,11 @@ impl<'body, 'db> InferenceContext<'body, 'db> { } else { match path_ctx.resolve_path_in_type_ns() { Some((it, idx)) => (it, idx), - None => return (self.types.types.error, None), + None => { + drop(ctx); + self.push_diagnostic(InferenceDiagnostic::UnresolvedVariant { id: node }); + return (self.types.types.error, None); + } } }; return match resolution { diff --git a/crates/hir-ty/src/infer/expr.rs b/crates/hir-ty/src/infer/expr.rs index 3cff0ea89693..1ea6a431d375 100644 --- a/crates/hir-ty/src/infer/expr.rs +++ b/crates/hir-ty/src/infer/expr.rs @@ -964,7 +964,6 @@ impl<'db> InferenceContext<'_, 'db> { ) -> Ty<'db> { // Find the relevant variant let (adt_ty, Some(variant)) = self.resolve_variant(expr.into(), path, false) else { - // FIXME: Emit an error. for field in fields { self.infer_expr_no_expect(field.expr, ExprIsRead::Yes); } diff --git a/crates/hir/src/diagnostics.rs b/crates/hir/src/diagnostics.rs index f4a29a4bcc08..0b6e43f52172 100644 --- a/crates/hir/src/diagnostics.rs +++ b/crates/hir/src/diagnostics.rs @@ -160,6 +160,7 @@ diagnostics![AnyDiagnostic<'db> -> UnresolvedMethodCall<'db>, UnresolvedModule, UnresolvedIdent, + UnresolvedVariant, UnusedMut, UnusedVariable, GenericArgsProhibited, @@ -362,6 +363,11 @@ pub struct UnresolvedIdent { pub node: InFile<(ExprOrPatPtr, Option)>, } +#[derive(Debug)] +pub struct UnresolvedVariant { + pub node: InFile, +} + #[derive(Debug)] pub struct PrivateField { pub expr: InFile, @@ -901,6 +907,10 @@ impl<'db> AnyDiagnostic<'db> { }; UnresolvedIdent { node }.into() } + &InferenceDiagnostic::UnresolvedVariant { id } => { + let node = expr_or_pat_syntax(id)?; + UnresolvedVariant { node }.into() + } &InferenceDiagnostic::BreakOutsideOfLoop { expr, is_break, bad_value_break } => { let expr = expr_syntax(expr)?; BreakOutsideOfLoop { expr, is_break, bad_value_break }.into() diff --git a/crates/ide-diagnostics/src/handlers/inactive_code.rs b/crates/ide-diagnostics/src/handlers/inactive_code.rs index 09f3e8bfb319..b48e85a2d358 100644 --- a/crates/ide-diagnostics/src/handlers/inactive_code.rs +++ b/crates/ide-diagnostics/src/handlers/inactive_code.rs @@ -71,7 +71,8 @@ fn f() { fn abc() {} abc(#[cfg(a)] 0); //^^^^^^^^^^^ weak: code is inactive due to #[cfg] directives: a is disabled - let x = Struct { + struct Struct {} + let _x = Struct { #[cfg(a)] f: 0, //^^^^^^^^^^^^^^ weak: code is inactive due to #[cfg] directives: a is disabled }; diff --git a/crates/ide-diagnostics/src/handlers/remove_trailing_return.rs b/crates/ide-diagnostics/src/handlers/remove_trailing_return.rs index b5a47e508e14..68f619f229cc 100644 --- a/crates/ide-diagnostics/src/handlers/remove_trailing_return.rs +++ b/crates/ide-diagnostics/src/handlers/remove_trailing_return.rs @@ -158,6 +158,7 @@ fn foo(x: usize) -> u8 { fn remove_trailing_return_in_match() { check_diagnostics( r#" +//- minicore: result fn foo(x: Result) -> u8 { match x { Ok(_) => return 1, diff --git a/crates/ide-diagnostics/src/handlers/unresolved_variant.rs b/crates/ide-diagnostics/src/handlers/unresolved_variant.rs new file mode 100644 index 000000000000..e89194122ecb --- /dev/null +++ b/crates/ide-diagnostics/src/handlers/unresolved_variant.rs @@ -0,0 +1,79 @@ +use either::Either; +use syntax::{AstNode, ast}; + +use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext, adjusted_display_range}; + +// Diagnostic: unresolved-variant +// +// This diagnostic is triggered if the struct, variant, or union type referred to by a record expression does not exist in the current scope. +pub(crate) fn unresolved_variant( + ctx: &DiagnosticsContext<'_, '_>, + d: &hir::UnresolvedVariant, +) -> Diagnostic { + Diagnostic::new( + DiagnosticCode::RustcHardError("E0422"), + "cannot find struct, variant or union type in this scope".to_owned(), + adjusted_display_range(ctx, d.node, &|node| match node { + Either::Left(ast::Expr::RecordExpr(it)) => it.path().map(|p| p.syntax().text_range()), + Either::Right(ast::Pat::RecordPat(it)) => it.path().map(|p| p.syntax().text_range()), + Either::Right(ast::Pat::TupleStructPat(it)) => { + it.path().map(|p| p.syntax().text_range()) + } + _ => None, + }), + ) + .stable() +} + +#[cfg(test)] +mod tests { + use crate::tests::check_diagnostics; + + #[test] + fn unresolved_record_expr() { + check_diagnostics( + r#" +fn main() { + let _ = DoesNotExist { x: 1, y: 2 }; + // ^^^^^^^^^^^^ error: cannot find struct, variant or union type in this scope +} +"#, + ); + } + + #[test] + fn unresolved_record_pat() { + check_diagnostics( + r#" +fn main() { + struct Exist { x: i32, y: i32 } + let a = Exist { x: 1, y: 2 }; + match a { + Exist { .. } => {} + DoesNotExist { .. } => {} + // ^^^^^^^^^^^^ error: cannot find struct, variant or union type in this scope + _ => {} + } +} +"#, + ); + } + + #[test] + fn unresolved_tuple_struct_pat() { + check_diagnostics( + r#" +fn main() { + struct Tuple(i32, i32); + let t = Tuple(1, 2); + match t { + Tuple( .. ) => {} + DoesNotExist( .. ) => {} + // ^^^^^^^^^^^^ error: cannot find struct, variant or union type in this scope + _ => {} + } +} +"#, + ); + } +} diff --git a/crates/ide-diagnostics/src/lib.rs b/crates/ide-diagnostics/src/lib.rs index d38780ede234..ee6721918b15 100644 --- a/crates/ide-diagnostics/src/lib.rs +++ b/crates/ide-diagnostics/src/lib.rs @@ -92,6 +92,7 @@ mod handlers { pub(crate) mod unresolved_macro_call; pub(crate) mod unresolved_method; pub(crate) mod unresolved_module; + pub(crate) mod unresolved_variant; pub(crate) mod unused_must_use; pub(crate) mod unused_variables; @@ -501,6 +502,7 @@ pub fn semantic_diagnostics( AnyDiagnostic::UnresolvedMacroCall(d) => handlers::unresolved_macro_call::unresolved_macro_call(&ctx, &d), AnyDiagnostic::UnresolvedMethodCall(d) => handlers::unresolved_method::unresolved_method(&ctx, &d), AnyDiagnostic::UnresolvedModule(d) => handlers::unresolved_module::unresolved_module(&ctx, &d), + AnyDiagnostic::UnresolvedVariant(d) => handlers::unresolved_variant::unresolved_variant(&ctx, &d), AnyDiagnostic::UnusedMustUse(d) => handlers::unused_must_use::unused_must_use(&ctx, &d), AnyDiagnostic::UnusedMut(d) => match handlers::mutability_errors::unused_mut(&ctx, &d) { Some(it) => it,