Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions crates/hir-ty/src/infer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -352,6 +352,10 @@ pub enum InferenceDiagnostic {
#[type_visitable(ignore)]
id: ExprOrPatId,
},
UnresolvedRecordExpr {
#[type_visitable(ignore)]
expr: ExprId,
},
// FIXME: This should be emitted in body lowering
BreakOutsideOfLoop {
#[type_visitable(ignore)]
Expand Down
2 changes: 1 addition & 1 deletion crates/hir-ty/src/infer/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -964,7 +964,7 @@ 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.
self.push_diagnostic(InferenceDiagnostic::UnresolvedRecordExpr { expr });
Copy link
Copy Markdown
Contributor

@ChayimFriedman2 ChayimFriedman2 May 17, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know there was a FIXME, but I think the error should go inside resolve_variant(), which will also fix it for patterns.

View changes since the review

for field in fields {
self.infer_expr_no_expect(field.expr, ExprIsRead::Yes);
}
Expand Down
10 changes: 10 additions & 0 deletions crates/hir/src/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,7 @@ diagnostics![AnyDiagnostic<'db> ->
UnresolvedMethodCall<'db>,
UnresolvedModule,
UnresolvedIdent,
UnresolvedRecordExpr,
UnusedMut,
UnusedVariable,
GenericArgsProhibited,
Expand Down Expand Up @@ -362,6 +363,11 @@ pub struct UnresolvedIdent {
pub node: InFile<(ExprOrPatPtr, Option<TextRange>)>,
}

#[derive(Debug)]
pub struct UnresolvedRecordExpr {
pub expr: InFile<ExprOrPatPtr>,
}

#[derive(Debug)]
pub struct PrivateField {
pub expr: InFile<ExprOrPatPtr>,
Expand Down Expand Up @@ -901,6 +907,10 @@ impl<'db> AnyDiagnostic<'db> {
};
UnresolvedIdent { node }.into()
}
&InferenceDiagnostic::UnresolvedRecordExpr { expr } => {
let expr = expr_syntax(expr)?;
UnresolvedRecordExpr { expr }.into()
}
&InferenceDiagnostic::BreakOutsideOfLoop { expr, is_break, bad_value_break } => {
let expr = expr_syntax(expr)?;
BreakOutsideOfLoop { expr, is_break, bad_value_break }.into()
Expand Down
3 changes: 2 additions & 1 deletion crates/ide-diagnostics/src/handlers/inactive_code.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
};
Expand Down
45 changes: 45 additions & 0 deletions crates/ide-diagnostics/src/handlers/unresolved_record_expr.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
use either::Either;
use syntax::{AstNode, ast::Expr};
Copy link
Copy Markdown
Contributor

@ChayimFriedman2 ChayimFriedman2 May 17, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

By convention we don't use specific ast nodes, instead we refer to them as ast::Node.

View changes since the review


use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext};

// Diagnostic: unresolved-record-expr
//
// 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_record_expr(
ctx: &DiagnosticsContext<'_, '_>,
d: &hir::UnresolvedRecordExpr,
) -> Diagnostic {
Diagnostic::new(
DiagnosticCode::RustcHardError("E0422"),
"cannot find struct, variant or union type in this scope".to_owned(),
crate::adjusted_display_range(ctx, d.expr, &|expr| match expr {
Either::Left(Expr::RecordExpr(it)) => it.path().map(|p| p.syntax().text_range()),
_ => None,
Copy link
Copy Markdown
Contributor

@ChayimFriedman2 ChayimFriedman2 May 17, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you emit the error in resolve_variant(), you also need to account for ast::Pat::{RecordPat,TupleStructPat}.

View changes since the review

}),
)
.stable()
}

#[cfg(test)]
mod tests {
use crate::tests::check_diagnostics;

#[test]
fn unresolved_record_expr() {
check_diagnostics(
r#"
struct Exist {
x: i32,
y: i32,
}

fn main() {
let _ = Exist { x: 1, y: 2 };
let _ = DoesNotExist { x: 1, y: 2 };
// ^^^^^^^^^^^^ error: cannot find struct, variant or union type in this scope
}
"#,
);
}
}
2 changes: 2 additions & 0 deletions crates/ide-diagnostics/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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_record_expr;
pub(crate) mod unused_must_use;
pub(crate) mod unused_variables;

Expand Down Expand Up @@ -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::UnresolvedRecordExpr(d) => handlers::unresolved_record_expr::unresolved_record_expr(&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,
Expand Down
Loading