feat: add unresolved-ident diagnostic

This commit is contained in:
Rose Hudson 2024-02-11 15:34:52 +00:00 committed by Lukas Wirth
parent d818b531c9
commit a492d9d164
12 changed files with 113 additions and 26 deletions

View File

@ -221,6 +221,9 @@ pub enum InferenceDiagnostic {
UnresolvedAssocItem {
id: ExprOrPatId,
},
UnresolvedIdent {
expr: ExprId,
},
// FIXME: This should be emitted in body lowering
BreakOutsideOfLoop {
expr: ExprId,

View File

@ -13,7 +13,7 @@
ArithOp, Array, BinaryOp, ClosureKind, Expr, ExprId, LabelId, Literal, Statement, UnaryOp,
},
lang_item::{LangItem, LangItemTarget},
path::{GenericArg, GenericArgs},
path::{GenericArg, GenericArgs, Path},
BlockId, ConstParamId, FieldId, ItemContainerId, Lookup, TupleFieldId, TupleId,
};
use hir_expand::name::{name, Name};
@ -439,7 +439,17 @@ fn infer_expr_inner(&mut self, tgt_expr: ExprId, expected: &Expectation) -> Ty {
}
Expr::Path(p) => {
let g = self.resolver.update_to_inner_scope(self.db.upcast(), self.owner, tgt_expr);
let ty = self.infer_path(p, tgt_expr.into()).unwrap_or_else(|| self.err_ty());
let ty = match self.infer_path(p, tgt_expr.into()) {
Some(ty) => ty,
None => {
if matches!(p, Path::Normal { mod_path, .. } if mod_path.is_ident()) {
self.push_diagnostic(InferenceDiagnostic::UnresolvedIdent {
expr: tgt_expr,
});
}
self.err_ty()
}
};
self.resolver.reset_to_guard(g);
ty
}

View File

@ -87,6 +87,7 @@ fn from(d: $diag) -> AnyDiagnostic {
UnresolvedMacroCall,
UnresolvedMethodCall,
UnresolvedModule,
UnresolvedIdent,
UnresolvedProcMacro,
UnusedMut,
UnusedVariable,
@ -242,6 +243,11 @@ pub struct UnresolvedAssocItem {
pub expr_or_pat: InFile<AstPtr<Either<ast::Expr, Either<ast::Pat, ast::SelfParam>>>>,
}
#[derive(Debug)]
pub struct UnresolvedIdent {
pub expr: InFile<AstPtr<ast::Expr>>,
}
#[derive(Debug)]
pub struct PrivateField {
pub expr: InFile<AstPtr<ast::Expr>>,
@ -588,6 +594,10 @@ pub(crate) fn inference_diagnostic(
};
UnresolvedAssocItem { expr_or_pat }.into()
}
&InferenceDiagnostic::UnresolvedIdent { expr } => {
let expr = expr_syntax(expr);
UnresolvedIdent { expr }.into()
}
&InferenceDiagnostic::BreakOutsideOfLoop { expr, is_break, bad_value_break } => {
let expr = expr_syntax(expr);
BreakOutsideOfLoop { expr, is_break, bad_value_break }.into()

View File

@ -60,6 +60,7 @@ fn f() {
#[cfg(a)] let x = 0; // let statement
//^^^^^^^^^^^^^^^^^^^^ weak: code is inactive due to #[cfg] directives: a is disabled
fn abc() {}
abc(#[cfg(a)] 0);
//^^^^^^^^^^^ weak: code is inactive due to #[cfg] directives: a is disabled
let x = Struct {

View File

@ -634,7 +634,8 @@ struct TestStruct { one: i32, two: i64 }
fn test_fn() {
let one = 1;
let s = TestStruct{ ..a };
let a = TestStruct{ one, two: 2 };
let _ = TestStruct{ ..a };
}
"#,
);

View File

@ -18,7 +18,9 @@ pub(crate) fn missing_match_arms(
#[cfg(test)]
mod tests {
use crate::{
tests::{check_diagnostics, check_diagnostics_with_config},
tests::{
check_diagnostics, check_diagnostics_with_config, check_diagnostics_with_disabled,
},
DiagnosticsConfig,
};
@ -282,7 +284,7 @@ fn mismatched_types() {
cov_mark::check_count!(validate_match_bailed_out, 4);
// Match statements with arms that don't match the
// expression pattern do not fire this diagnostic.
check_diagnostics(
check_diagnostics_with_disabled(
r#"
enum Either { A, B }
enum Either2 { C, D }
@ -307,6 +309,7 @@ fn main() {
match Unresolved::Bar { Unresolved::Baz => () }
}
"#,
&["E0425"],
);
}
@ -397,11 +400,11 @@ fn main() {
match loop {} {
Either::A => (),
}
match loop { break Foo::A } {
//^^^^^^^^^^^^^^^^^^^^^ error: missing match arm: `B` not covered
match loop { break Either::A } {
//^^^^^^^^^^^^^^^^^^^^^^^^ error: missing match arm: `B` not covered
Either::A => (),
}
match loop { break Foo::A } {
match loop { break Either::A } {
Either::A => (),
Either::B => (),
}
@ -977,7 +980,7 @@ fn f(ty: Enum) {
#[test]
fn unexpected_ty_fndef() {
cov_mark::check!(validate_match_bailed_out);
check_diagnostics(
check_diagnostics_with_disabled(
r"
enum Exp {
Tuple(()),
@ -987,6 +990,7 @@ fn f() {
Exp::Tuple => {}
}
}",
&["E0425"],
);
}

View File

@ -95,7 +95,7 @@ fn fixes(ctx: &DiagnosticsContext<'_>, d: &RemoveUnnecessaryElse) -> Option<Vec<
#[cfg(test)]
mod tests {
use crate::tests::{check_diagnostics, check_diagnostics_with_disabled, check_fix};
use crate::tests::{check_diagnostics_with_disabled, check_fix};
#[test]
fn remove_unnecessary_else_for_return() {
@ -110,7 +110,7 @@ fn test() {
}
}
"#,
&["needless_return"],
&["needless_return", "E0425"],
);
check_fix(
r#"
@ -148,7 +148,7 @@ fn test() {
}
}
"#,
&["needless_return"],
&["needless_return", "E0425"],
);
check_fix(
r#"
@ -227,7 +227,7 @@ fn test() {
}
}
"#,
&["needless_return"],
&["needless_return", "E0425"],
);
check_fix(
r#"
@ -293,7 +293,7 @@ fn test() {
#[test]
fn remove_unnecessary_else_for_break() {
check_diagnostics(
check_diagnostics_with_disabled(
r#"
fn test() {
loop {
@ -306,6 +306,7 @@ fn test() {
}
}
"#,
&["E0425"],
);
check_fix(
r#"
@ -334,7 +335,7 @@ fn test() {
#[test]
fn remove_unnecessary_else_for_continue() {
check_diagnostics(
check_diagnostics_with_disabled(
r#"
fn test() {
loop {
@ -347,6 +348,7 @@ fn test() {
}
}
"#,
&["E0425"],
);
check_fix(
r#"
@ -375,7 +377,7 @@ fn test() {
#[test]
fn remove_unnecessary_else_for_never() {
check_diagnostics(
check_diagnostics_with_disabled(
r#"
fn test() {
if foo {
@ -390,6 +392,7 @@ fn never() -> ! {
loop {}
}
"#,
&["E0425"],
);
check_fix(
r#"
@ -422,7 +425,7 @@ fn never() -> ! {
#[test]
fn no_diagnostic_if_no_else_branch() {
check_diagnostics(
check_diagnostics_with_disabled(
r#"
fn test() {
if foo {
@ -432,12 +435,13 @@ fn test() {
do_something_else();
}
"#,
&["E0425"],
);
}
#[test]
fn no_diagnostic_if_no_divergence() {
check_diagnostics(
check_diagnostics_with_disabled(
r#"
fn test() {
if foo {
@ -447,6 +451,7 @@ fn test() {
}
}
"#,
&["E0425"],
);
}
@ -462,7 +467,7 @@ fn test() {
}
}
"#,
&["needless_return"],
&["needless_return", "E0425"],
);
}

View File

@ -38,10 +38,12 @@ fn foo() {
fn while_let_loop_with_label_in_condition() {
check_diagnostics(
r#"
//- minicore: option
fn foo() {
let mut optional = Some(0);
'my_label: while let Some(a) = match optional {
'my_label: while let Some(_) = match optional {
None => break 'my_label,
Some(val) => Some(val),
} {
@ -59,8 +61,8 @@ fn for_loop() {
r#"
//- minicore: iterator
fn foo() {
'xxx: for _ in unknown {
'yyy: for _ in unknown {
'xxx: for _ in [] {
'yyy: for _ in [] {
break 'xxx;
continue 'yyy;
break 'zzz;

View File

@ -78,7 +78,9 @@ fn method_fix(
#[cfg(test)]
mod tests {
use crate::{
tests::{check_diagnostics, check_diagnostics_with_config},
tests::{
check_diagnostics, check_diagnostics_with_config, check_diagnostics_with_disabled,
},
DiagnosticsConfig,
};
@ -148,7 +150,7 @@ fn foo() {
#[test]
fn no_diagnostic_on_unknown() {
check_diagnostics(
check_diagnostics_with_disabled(
r#"
fn foo() {
x.foo;
@ -156,6 +158,7 @@ fn foo() {
(&((x,),),).foo;
}
"#,
&["E0425"],
);
}

View File

@ -0,0 +1,46 @@
use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext};
// Diagnostic: unresolved-ident
//
// This diagnostic is triggered if an expr-position ident is invalid.
pub(crate) fn unresolved_ident(
ctx: &DiagnosticsContext<'_>,
d: &hir::UnresolvedIdent,
) -> Diagnostic {
Diagnostic::new_with_syntax_node_ptr(
ctx,
DiagnosticCode::RustcHardError("E0425"),
"no such value in this scope",
d.expr.map(Into::into),
)
.experimental()
}
#[cfg(test)]
mod tests {
use crate::tests::check_diagnostics;
#[test]
fn missing() {
check_diagnostics(
r#"
fn main() {
let _ = x;
//^ error: no such value in this scope
}
"#,
);
}
#[test]
fn present() {
check_diagnostics(
r#"
fn main() {
let x = 5;
let _ = x;
}
"#,
);
}
}

View File

@ -335,8 +335,8 @@ fn field() {
r#"
struct Foo { bar: i32 }
fn foo() {
Foo { bar: i32 }.bar();
// ^^^ error: no method `bar` on type `Foo`, but a field with a similar name exists
Foo { bar: 0 }.bar();
// ^^^ error: no method `bar` on type `Foo`, but a field with a similar name exists
}
"#,
);

View File

@ -59,6 +59,7 @@ mod handlers {
pub(crate) mod unresolved_assoc_item;
pub(crate) mod unresolved_extern_crate;
pub(crate) mod unresolved_field;
pub(crate) mod unresolved_ident;
pub(crate) mod unresolved_import;
pub(crate) mod unresolved_macro_call;
pub(crate) mod unresolved_method;
@ -377,6 +378,7 @@ pub fn diagnostics(
AnyDiagnostic::UnresolvedAssocItem(d) => handlers::unresolved_assoc_item::unresolved_assoc_item(&ctx, &d),
AnyDiagnostic::UnresolvedExternCrate(d) => handlers::unresolved_extern_crate::unresolved_extern_crate(&ctx, &d),
AnyDiagnostic::UnresolvedField(d) => handlers::unresolved_field::unresolved_field(&ctx, &d),
AnyDiagnostic::UnresolvedIdent(d) => handlers::unresolved_ident::unresolved_ident(&ctx, &d),
AnyDiagnostic::UnresolvedImport(d) => handlers::unresolved_import::unresolved_import(&ctx, &d),
AnyDiagnostic::UnresolvedMacroCall(d) => handlers::unresolved_macro_call::unresolved_macro_call(&ctx, &d),
AnyDiagnostic::UnresolvedMethodCall(d) => handlers::unresolved_method::unresolved_method(&ctx, &d),