diff --git a/crates/ra_hir_ty/src/diagnostics.rs b/crates/ra_hir_ty/src/diagnostics.rs
index c8fd5486159..41ac702724e 100644
--- a/crates/ra_hir_ty/src/diagnostics.rs
+++ b/crates/ra_hir_ty/src/diagnostics.rs
@@ -131,3 +131,31 @@ impl AstDiagnostic for MissingOkInTailExpr {
         ast::Expr::cast(node).unwrap()
     }
 }
+
+#[derive(Debug)]
+pub struct BreakOutsideOfLoop {
+    pub file: HirFileId,
+    pub expr: AstPtr<ast::Expr>,
+}
+
+impl Diagnostic for BreakOutsideOfLoop {
+    fn message(&self) -> String {
+        "break outside of loop".to_string()
+    }
+    fn source(&self) -> InFile<SyntaxNodePtr> {
+        InFile { file_id: self.file, value: self.expr.clone().into() }
+    }
+    fn as_any(&self) -> &(dyn Any + Send + 'static) {
+        self
+    }
+}
+
+impl AstDiagnostic for BreakOutsideOfLoop {
+    type AST = ast::Expr;
+
+    fn ast(&self, db: &impl AstDatabase) -> Self::AST {
+        let root = db.parse_or_expand(self.file).unwrap();
+        let node = self.source().value.to_node(&root);
+        ast::Expr::cast(node).unwrap()
+    }
+}
diff --git a/crates/ra_hir_ty/src/infer.rs b/crates/ra_hir_ty/src/infer.rs
index 413904518b1..a21ad8d8641 100644
--- a/crates/ra_hir_ty/src/infer.rs
+++ b/crates/ra_hir_ty/src/infer.rs
@@ -717,11 +717,15 @@ mod diagnostics {
     use hir_def::{expr::ExprId, FunctionId};
     use hir_expand::diagnostics::DiagnosticSink;
 
-    use crate::{db::HirDatabase, diagnostics::NoSuchField};
+    use crate::{
+        db::HirDatabase,
+        diagnostics::{BreakOutsideOfLoop, NoSuchField},
+    };
 
     #[derive(Debug, PartialEq, Eq, Clone)]
     pub(super) enum InferenceDiagnostic {
         NoSuchField { expr: ExprId, field: usize },
+        BreakOutsideOfLoop { expr: ExprId },
     }
 
     impl InferenceDiagnostic {
@@ -737,6 +741,13 @@ mod diagnostics {
                     let field = source_map.field_syntax(*expr, *field);
                     sink.push(NoSuchField { file: field.file_id, field: field.value })
                 }
+                InferenceDiagnostic::BreakOutsideOfLoop { expr } => {
+                    let (_, source_map) = db.body_with_source_map(owner.into());
+                    let ptr = source_map
+                        .expr_syntax(*expr)
+                        .expect("break outside of loop in synthetic syntax");
+                    sink.push(BreakOutsideOfLoop { file: ptr.file_id, expr: ptr.value })
+                }
             }
         }
     }
diff --git a/crates/ra_hir_ty/src/infer/expr.rs b/crates/ra_hir_ty/src/infer/expr.rs
index 9cac0c787c7..0b67d216a8b 100644
--- a/crates/ra_hir_ty/src/infer/expr.rs
+++ b/crates/ra_hir_ty/src/infer/expr.rs
@@ -235,6 +235,10 @@ impl<'a> InferenceContext<'a> {
                 }
                 if let Some(ctxt) = self.breakables.last_mut() {
                     ctxt.may_break = true;
+                } else {
+                    self.push_diagnostic(InferenceDiagnostic::BreakOutsideOfLoop {
+                        expr: tgt_expr,
+                    });
                 }
                 Ty::simple(TypeCtor::Never)
             }
diff --git a/crates/ra_hir_ty/src/tests.rs b/crates/ra_hir_ty/src/tests.rs
index d60732e1918..5af88b36846 100644
--- a/crates/ra_hir_ty/src/tests.rs
+++ b/crates/ra_hir_ty/src/tests.rs
@@ -518,3 +518,21 @@ fn missing_record_pat_field_no_diagnostic_if_not_exhaustive() {
 
     assert_snapshot!(diagnostics, @"");
 }
+
+#[test]
+fn break_outside_of_loop() {
+    let diagnostics = TestDB::with_files(
+        r"
+        //- /lib.rs
+        fn foo() {
+            break;
+        }
+        ",
+    )
+    .diagnostics()
+    .0;
+
+    assert_snapshot!(diagnostics, @r###""break": break outside of loop
+    "###
+    );
+}