about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--crates/hir-ty/src/infer.rs3
-rw-r--r--crates/hir-ty/src/infer/expr.rs14
-rw-r--r--crates/hir/src/diagnostics.rs10
-rw-r--r--crates/ide-diagnostics/src/handlers/inactive_code.rs1
-rw-r--r--crates/ide-diagnostics/src/handlers/incorrect_case.rs2
-rw-r--r--crates/ide-diagnostics/src/handlers/missing_fields.rs3
-rw-r--r--crates/ide-diagnostics/src/handlers/missing_match_arms.rs16
-rw-r--r--crates/ide-diagnostics/src/handlers/mutability_errors.rs4
-rw-r--r--crates/ide-diagnostics/src/handlers/remove_trailing_return.rs2
-rw-r--r--crates/ide-diagnostics/src/handlers/remove_unnecessary_else.rs42
-rw-r--r--crates/ide-diagnostics/src/handlers/type_mismatch.rs2
-rw-r--r--crates/ide-diagnostics/src/handlers/undeclared_label.rs8
-rw-r--r--crates/ide-diagnostics/src/handlers/unresolved_field.rs7
-rw-r--r--crates/ide-diagnostics/src/handlers/unresolved_ident.rs46
-rw-r--r--crates/ide-diagnostics/src/handlers/unresolved_method.rs4
-rw-r--r--crates/ide-diagnostics/src/lib.rs2
-rw-r--r--crates/ide-diagnostics/src/tests.rs7
17 files changed, 130 insertions, 43 deletions
diff --git a/crates/hir-ty/src/infer.rs b/crates/hir-ty/src/infer.rs
index 1977f00517c..9cea414e1a0 100644
--- a/crates/hir-ty/src/infer.rs
+++ b/crates/hir-ty/src/infer.rs
@@ -221,6 +221,9 @@ pub enum InferenceDiagnostic {
     UnresolvedAssocItem {
         id: ExprOrPatId,
     },
+    UnresolvedIdent {
+        expr: ExprId,
+    },
     // FIXME: This should be emitted in body lowering
     BreakOutsideOfLoop {
         expr: ExprId,
diff --git a/crates/hir-ty/src/infer/expr.rs b/crates/hir-ty/src/infer/expr.rs
index 428ed6748c6..c377a51e7d3 100644
--- a/crates/hir-ty/src/infer/expr.rs
+++ b/crates/hir-ty/src/infer/expr.rs
@@ -13,7 +13,7 @@ use hir_def::{
         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 @@ impl InferenceContext<'_> {
             }
             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
             }
diff --git a/crates/hir/src/diagnostics.rs b/crates/hir/src/diagnostics.rs
index d351e257d2e..80cd0c9c794 100644
--- a/crates/hir/src/diagnostics.rs
+++ b/crates/hir/src/diagnostics.rs
@@ -87,6 +87,7 @@ diagnostics![
     UnresolvedMacroCall,
     UnresolvedMethodCall,
     UnresolvedModule,
+    UnresolvedIdent,
     UnresolvedProcMacro,
     UnusedMut,
     UnusedVariable,
@@ -243,6 +244,11 @@ pub struct UnresolvedAssocItem {
 }
 
 #[derive(Debug)]
+pub struct UnresolvedIdent {
+    pub expr: InFile<AstPtr<ast::Expr>>,
+}
+
+#[derive(Debug)]
 pub struct PrivateField {
     pub expr: InFile<AstPtr<ast::Expr>>,
     pub field: Field,
@@ -588,6 +594,10 @@ impl AnyDiagnostic {
                 };
                 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()
diff --git a/crates/ide-diagnostics/src/handlers/inactive_code.rs b/crates/ide-diagnostics/src/handlers/inactive_code.rs
index 7db5ea04fbd..785a42352bf 100644
--- a/crates/ide-diagnostics/src/handlers/inactive_code.rs
+++ b/crates/ide-diagnostics/src/handlers/inactive_code.rs
@@ -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 {
diff --git a/crates/ide-diagnostics/src/handlers/incorrect_case.rs b/crates/ide-diagnostics/src/handlers/incorrect_case.rs
index 5e2541795ca..db28928a24e 100644
--- a/crates/ide-diagnostics/src/handlers/incorrect_case.rs
+++ b/crates/ide-diagnostics/src/handlers/incorrect_case.rs
@@ -512,7 +512,7 @@ impl BAD_TRAIT for () {
     fn BadFunction() {}
 }
     "#,
-            std::iter::once("unused_variables".to_owned()),
+            &["unused_variables"],
         );
     }
 
diff --git a/crates/ide-diagnostics/src/handlers/missing_fields.rs b/crates/ide-diagnostics/src/handlers/missing_fields.rs
index c70f39eb286..09daefd084d 100644
--- a/crates/ide-diagnostics/src/handlers/missing_fields.rs
+++ b/crates/ide-diagnostics/src/handlers/missing_fields.rs
@@ -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 };
 }
 "#,
         );
diff --git a/crates/ide-diagnostics/src/handlers/missing_match_arms.rs b/crates/ide-diagnostics/src/handlers/missing_match_arms.rs
index 7632fdf1d09..8596f5792e0 100644
--- a/crates/ide-diagnostics/src/handlers/missing_match_arms.rs
+++ b/crates/ide-diagnostics/src/handlers/missing_match_arms.rs
@@ -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 main() {
         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"],
         );
     }
 
diff --git a/crates/ide-diagnostics/src/handlers/mutability_errors.rs b/crates/ide-diagnostics/src/handlers/mutability_errors.rs
index 3c71f84dc48..91f1058d65b 100644
--- a/crates/ide-diagnostics/src/handlers/mutability_errors.rs
+++ b/crates/ide-diagnostics/src/handlers/mutability_errors.rs
@@ -448,7 +448,7 @@ fn main(b: bool) {
     &mut x;
 }
 "#,
-            std::iter::once("remove-unnecessary-else".to_owned()),
+            &["remove-unnecessary-else"],
         );
         check_diagnostics_with_disabled(
             r#"
@@ -463,7 +463,7 @@ fn main(b: bool) {
     &mut x;
 }
 "#,
-            std::iter::once("remove-unnecessary-else".to_owned()),
+            &["remove-unnecessary-else"],
         );
     }
 
diff --git a/crates/ide-diagnostics/src/handlers/remove_trailing_return.rs b/crates/ide-diagnostics/src/handlers/remove_trailing_return.rs
index b7667dc318f..7a040e46e33 100644
--- a/crates/ide-diagnostics/src/handlers/remove_trailing_return.rs
+++ b/crates/ide-diagnostics/src/handlers/remove_trailing_return.rs
@@ -140,7 +140,7 @@ fn foo(x: usize) -> u8 {
     } //^^^^^^^^^ 💡 weak: replace return <expr>; with <expr>
 }
 "#,
-            std::iter::once("remove-unnecessary-else".to_owned()),
+            &["remove-unnecessary-else"],
         );
     }
 
diff --git a/crates/ide-diagnostics/src/handlers/remove_unnecessary_else.rs b/crates/ide-diagnostics/src/handlers/remove_unnecessary_else.rs
index 9c63d79d910..47844876dc5 100644
--- a/crates/ide-diagnostics/src/handlers/remove_unnecessary_else.rs
+++ b/crates/ide-diagnostics/src/handlers/remove_unnecessary_else.rs
@@ -95,15 +95,11 @@ fn fixes(ctx: &DiagnosticsContext<'_>, d: &RemoveUnnecessaryElse) -> Option<Vec<
 
 #[cfg(test)]
 mod tests {
-    use crate::tests::{check_diagnostics, check_diagnostics_with_disabled, check_fix};
-
-    fn check_diagnostics_with_needless_return_disabled(ra_fixture: &str) {
-        check_diagnostics_with_disabled(ra_fixture, std::iter::once("needless_return".to_owned()));
-    }
+    use crate::tests::{check_diagnostics_with_disabled, check_fix};
 
     #[test]
     fn remove_unnecessary_else_for_return() {
-        check_diagnostics_with_needless_return_disabled(
+        check_diagnostics_with_disabled(
             r#"
 fn test() {
     if foo {
@@ -114,6 +110,7 @@ fn test() {
     }
 }
 "#,
+            &["needless_return", "E0425"],
         );
         check_fix(
             r#"
@@ -138,7 +135,7 @@ fn test() {
 
     #[test]
     fn remove_unnecessary_else_for_return2() {
-        check_diagnostics_with_needless_return_disabled(
+        check_diagnostics_with_disabled(
             r#"
 fn test() {
     if foo {
@@ -151,6 +148,7 @@ fn test() {
     }
 }
 "#,
+            &["needless_return", "E0425"],
         );
         check_fix(
             r#"
@@ -181,7 +179,7 @@ fn test() {
 
     #[test]
     fn remove_unnecessary_else_for_return3() {
-        check_diagnostics_with_needless_return_disabled(
+        check_diagnostics_with_disabled(
             r#"
 fn test(a: bool) -> i32 {
     if a {
@@ -192,6 +190,7 @@ fn test(a: bool) -> i32 {
     }
 }
 "#,
+            &["needless_return", "E0425"],
         );
         check_fix(
             r#"
@@ -216,7 +215,7 @@ fn test(a: bool) -> i32 {
 
     #[test]
     fn remove_unnecessary_else_for_return_in_child_if_expr() {
-        check_diagnostics_with_needless_return_disabled(
+        check_diagnostics_with_disabled(
             r#"
 fn test() {
     if foo {
@@ -229,6 +228,7 @@ fn test() {
     }
 }
 "#,
+            &["needless_return", "E0425"],
         );
         check_fix(
             r#"
@@ -294,7 +294,7 @@ fn test() {
 
     #[test]
     fn remove_unnecessary_else_for_break() {
-        check_diagnostics(
+        check_diagnostics_with_disabled(
             r#"
 fn test() {
     loop {
@@ -307,6 +307,7 @@ fn test() {
     }
 }
 "#,
+            &["E0425"],
         );
         check_fix(
             r#"
@@ -335,7 +336,7 @@ fn test() {
 
     #[test]
     fn remove_unnecessary_else_for_continue() {
-        check_diagnostics(
+        check_diagnostics_with_disabled(
             r#"
 fn test() {
     loop {
@@ -348,6 +349,7 @@ fn test() {
     }
 }
 "#,
+            &["E0425"],
         );
         check_fix(
             r#"
@@ -376,7 +378,7 @@ fn test() {
 
     #[test]
     fn remove_unnecessary_else_for_never() {
-        check_diagnostics(
+        check_diagnostics_with_disabled(
             r#"
 fn test() {
     if foo {
@@ -391,6 +393,7 @@ fn never() -> ! {
     loop {}
 }
 "#,
+            &["E0425"],
         );
         check_fix(
             r#"
@@ -423,7 +426,7 @@ fn never() -> ! {
 
     #[test]
     fn no_diagnostic_if_no_else_branch() {
-        check_diagnostics(
+        check_diagnostics_with_disabled(
             r#"
 fn test() {
     if foo {
@@ -433,12 +436,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 {
@@ -448,12 +452,13 @@ fn test() {
     }
 }
 "#,
+            &["E0425"],
         );
     }
 
     #[test]
     fn no_diagnostic_if_no_divergence_in_else_branch() {
-        check_diagnostics_with_needless_return_disabled(
+        check_diagnostics_with_disabled(
             r#"
 fn test() {
     if foo {
@@ -463,12 +468,13 @@ fn test() {
     }
 }
 "#,
+            &["needless_return", "E0425"],
         );
     }
 
     #[test]
     fn no_diagnostic_if_not_expr_stmt() {
-        check_diagnostics_with_needless_return_disabled(
+        check_diagnostics_with_disabled(
             r#"
 fn test1() {
     let _x = if a {
@@ -490,13 +496,15 @@ fn test2() {
     };
 }
 "#,
+            &["needless_return", "E0425"],
         );
-        check_diagnostics(
+        check_diagnostics_with_disabled(
             r#"
 fn test3() -> u8 {
     foo(if a { return 1 } else { 0 })
 }
 "#,
+            &["E0425"],
         );
     }
 }
diff --git a/crates/ide-diagnostics/src/handlers/type_mismatch.rs b/crates/ide-diagnostics/src/handlers/type_mismatch.rs
index 8c97281b783..4c255322280 100644
--- a/crates/ide-diagnostics/src/handlers/type_mismatch.rs
+++ b/crates/ide-diagnostics/src/handlers/type_mismatch.rs
@@ -730,7 +730,7 @@ fn f() -> i32 {
 }
 fn g() { return; }
 "#,
-            std::iter::once("needless_return".to_owned()),
+            &["needless_return"],
         );
     }
 
diff --git a/crates/ide-diagnostics/src/handlers/undeclared_label.rs b/crates/ide-diagnostics/src/handlers/undeclared_label.rs
index a6a0fdc655f..97943b7e8b3 100644
--- a/crates/ide-diagnostics/src/handlers/undeclared_label.rs
+++ b/crates/ide-diagnostics/src/handlers/undeclared_label.rs
@@ -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 foo() {
             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;
diff --git a/crates/ide-diagnostics/src/handlers/unresolved_field.rs b/crates/ide-diagnostics/src/handlers/unresolved_field.rs
index 65abfd8a294..4c01a2d155a 100644
--- a/crates/ide-diagnostics/src/handlers/unresolved_field.rs
+++ b/crates/ide-diagnostics/src/handlers/unresolved_field.rs
@@ -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"],
         );
     }
 
diff --git a/crates/ide-diagnostics/src/handlers/unresolved_ident.rs b/crates/ide-diagnostics/src/handlers/unresolved_ident.rs
new file mode 100644
index 00000000000..295c8a2c615
--- /dev/null
+++ b/crates/ide-diagnostics/src/handlers/unresolved_ident.rs
@@ -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;
+}
+"#,
+        );
+    }
+}
diff --git a/crates/ide-diagnostics/src/handlers/unresolved_method.rs b/crates/ide-diagnostics/src/handlers/unresolved_method.rs
index 648d081898c..0614fdc5514 100644
--- a/crates/ide-diagnostics/src/handlers/unresolved_method.rs
+++ b/crates/ide-diagnostics/src/handlers/unresolved_method.rs
@@ -335,8 +335,8 @@ fn main() {
             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
 }
 "#,
         );
diff --git a/crates/ide-diagnostics/src/lib.rs b/crates/ide-diagnostics/src/lib.rs
index 3a3888011d7..4428b8baafb 100644
--- a/crates/ide-diagnostics/src/lib.rs
+++ b/crates/ide-diagnostics/src/lib.rs
@@ -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),
diff --git a/crates/ide-diagnostics/src/tests.rs b/crates/ide-diagnostics/src/tests.rs
index 4e4a851f67e..901ceffbb26 100644
--- a/crates/ide-diagnostics/src/tests.rs
+++ b/crates/ide-diagnostics/src/tests.rs
@@ -198,12 +198,9 @@ pub(crate) fn check_diagnostics(ra_fixture: &str) {
 }
 
 #[track_caller]
-pub(crate) fn check_diagnostics_with_disabled(
-    ra_fixture: &str,
-    disabled: impl Iterator<Item = String>,
-) {
+pub(crate) fn check_diagnostics_with_disabled(ra_fixture: &str, disabled: &[&str]) {
     let mut config = DiagnosticsConfig::test_sample();
-    config.disabled.extend(disabled);
+    config.disabled.extend(disabled.iter().map(|&s| s.to_owned()));
     check_diagnostics_with_config(config, ra_fixture)
 }