feat: add unresolved-ident diagnostic
This commit is contained in:
parent
d818b531c9
commit
a492d9d164
@ -221,6 +221,9 @@ pub enum InferenceDiagnostic {
|
||||
UnresolvedAssocItem {
|
||||
id: ExprOrPatId,
|
||||
},
|
||||
UnresolvedIdent {
|
||||
expr: ExprId,
|
||||
},
|
||||
// FIXME: This should be emitted in body lowering
|
||||
BreakOutsideOfLoop {
|
||||
expr: ExprId,
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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()
|
||||
|
@ -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 {
|
||||
|
@ -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 };
|
||||
}
|
||||
"#,
|
||||
);
|
||||
|
@ -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"],
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -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"],
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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"],
|
||||
);
|
||||
}
|
||||
|
||||
|
46
crates/ide-diagnostics/src/handlers/unresolved_ident.rs
Normal file
46
crates/ide-diagnostics/src/handlers/unresolved_ident.rs
Normal 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;
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
||||
"#,
|
||||
);
|
||||
|
@ -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),
|
||||
|
Loading…
Reference in New Issue
Block a user