Repository: rust-analyzer/rust-analyzer
Pull request #4672 (2020-05-31 13:36:37)

Link docs and code
bors r+
🤖


Patch:
diff --git a/crates/ra_hir_def/src/body/lower.rs b/crates/ra_hir_def/src/body/lower.rs
index f159f80af0..905c0cf5da 100644
--- a/crates/ra_hir_def/src/body/lower.rs
+++ b/crates/ra_hir_def/src/body/lower.rs
@@ -134,7 +134,7 @@ impl ExprCollector<'_> {
         self.make_expr(expr, Err(SyntheticSyntax))
     }
     fn empty_block(&mut self) -> ExprId {
-        self.alloc_expr_desugared(Expr::Block { statements: Vec::new(), tail: None, label: None })
+        self.alloc_expr_desugared(Expr::Block { statements: Vec::new(), tail: None })
     }
     fn missing_expr(&mut self) -> ExprId {
         self.alloc_expr_desugared(Expr::Missing)
@@ -215,16 +215,7 @@ impl ExprCollector<'_> {
             ast::Expr::BlockExpr(e) => self.collect_block(e),
             ast::Expr::LoopExpr(e) => {
                 let body = self.collect_block_opt(e.loop_body());
-                self.alloc_expr(
-                    Expr::Loop {
-                        body,
-                        label: e
-                            .label()
-                            .and_then(|l| l.lifetime_token())
-                            .map(|l| Name::new_lifetime(&l)),
-                    },
-                    syntax_ptr,
-                )
+                self.alloc_expr(Expr::Loop { body }, syntax_ptr)
             }
             ast::Expr::WhileExpr(e) => {
                 let body = self.collect_block_opt(e.loop_body());
@@ -239,56 +230,25 @@ impl ExprCollector<'_> {
                             let pat = self.collect_pat(pat);
                             let match_expr = self.collect_expr_opt(condition.expr());
                             let placeholder_pat = self.missing_pat();
-                            let break_ =
-                                self.alloc_expr_desugared(Expr::Break { expr: None, label: None });
+                            let break_ = self.alloc_expr_desugared(Expr::Break { expr: None });
                             let arms = vec![
                                 MatchArm { pat, expr: body, guard: None },
                                 MatchArm { pat: placeholder_pat, expr: break_, guard: None },
                             ];
                             let match_expr =
                                 self.alloc_expr_desugared(Expr::Match { expr: match_expr, arms });
-                            return self.alloc_expr(
-                                Expr::Loop {
-                                    body: match_expr,
-                                    label: e
-                                        .label()
-                                        .and_then(|l| l.lifetime_token())
-                                        .map(|l| Name::new_lifetime(&l)),
-                                },
-                                syntax_ptr,
-                            );
+                            return self.alloc_expr(Expr::Loop { body: match_expr }, syntax_ptr);
                         }
                     },
                 };
 
-                self.alloc_expr(
-                    Expr::While {
-                        condition,
-                        body,
-                        label: e
-                            .label()
-                            .and_then(|l| l.lifetime_token())
-                            .map(|l| Name::new_lifetime(&l)),
-                    },
-                    syntax_ptr,
-                )
+                self.alloc_expr(Expr::While { condition, body }, syntax_ptr)
             }
             ast::Expr::ForExpr(e) => {
                 let iterable = self.collect_expr_opt(e.iterable());
                 let pat = self.collect_pat_opt(e.pat());
                 let body = self.collect_block_opt(e.loop_body());
-                self.alloc_expr(
-                    Expr::For {
-                        iterable,
-                        pat,
-                        body,
-                        label: e
-                            .label()
-                            .and_then(|l| l.lifetime_token())
-                            .map(|l| Name::new_lifetime(&l)),
-                    },
-                    syntax_ptr,
-                )
+                self.alloc_expr(Expr::For { iterable, pat, body }, syntax_ptr)
             }
             ast::Expr::CallExpr(e) => {
                 let callee = self.collect_expr_opt(e.expr());
@@ -341,16 +301,13 @@ impl ExprCollector<'_> {
                     .unwrap_or(Expr::Missing);
                 self.alloc_expr(path, syntax_ptr)
             }
-            ast::Expr::ContinueExpr(e) => self.alloc_expr(
-                Expr::Continue { label: e.lifetime_token().map(|l| Name::new_lifetime(&l)) },
-                syntax_ptr,
-            ),
+            ast::Expr::ContinueExpr(_e) => {
+                // FIXME: labels
+                self.alloc_expr(Expr::Continue, syntax_ptr)
+            }
             ast::Expr::BreakExpr(e) => {
                 let expr = e.expr().map(|e| self.collect_expr(e));
-                self.alloc_expr(
-                    Expr::Break { expr, label: e.lifetime_token().map(|l| Name::new_lifetime(&l)) },
-                    syntax_ptr,
-                )
+                self.alloc_expr(Expr::Break { expr }, syntax_ptr)
             }
             ast::Expr::ParenExpr(e) => {
                 let inner = self.collect_expr_opt(e.expr());
@@ -572,8 +529,7 @@ impl ExprCollector<'_> {
             })
             .collect();
         let tail = block.expr().map(|e| self.collect_expr(e));
-        let label = block.label().and_then(|l| l.lifetime_token()).map(|t| Name::new_lifetime(&t));
-        self.alloc_expr(Expr::Block { statements, tail, label }, syntax_node_ptr)
+        self.alloc_expr(Expr::Block { statements, tail }, syntax_node_ptr)
     }
 
     fn collect_block_items(&mut self, block: &ast::BlockExpr) {
diff --git a/crates/ra_hir_def/src/body/scope.rs b/crates/ra_hir_def/src/body/scope.rs
index e48ff38f96..09e92b74e1 100644
--- a/crates/ra_hir_def/src/body/scope.rs
+++ b/crates/ra_hir_def/src/body/scope.rs
@@ -138,10 +138,10 @@ fn compute_block_scopes(
 fn compute_expr_scopes(expr: ExprId, body: &Body, scopes: &mut ExprScopes, scope: ScopeId) {
     scopes.set_scope(expr, scope);
     match &body[expr] {
-        Expr::Block { statements, tail, .. } => {
+        Expr::Block { statements, tail } => {
             compute_block_scopes(&statements, *tail, body, scopes, scope);
         }
-        Expr::For { iterable, pat, body: body_expr, .. } => {
+        Expr::For { iterable, pat, body: body_expr } => {
             compute_expr_scopes(*iterable, body, scopes, scope);
             let scope = scopes.new_scope(scope);
             scopes.add_bindings(body, scope, *pat);
diff --git a/crates/ra_hir_def/src/expr.rs b/crates/ra_hir_def/src/expr.rs
index ca49b26d15..f25c6f9580 100644
--- a/crates/ra_hir_def/src/expr.rs
+++ b/crates/ra_hir_def/src/expr.rs
@@ -52,22 +52,18 @@ pub enum Expr {
     Block {
         statements: Vec<Statement>,
         tail: Option<ExprId>,
-        label: Option<Name>,
     },
     Loop {
         body: ExprId,
-        label: Option<Name>,
     },
     While {
         condition: ExprId,
         body: ExprId,
-        label: Option<Name>,
     },
     For {
         iterable: ExprId,
         pat: PatId,
         body: ExprId,
-        label: Option<Name>,
     },
     Call {
         callee: ExprId,
@@ -83,12 +79,9 @@ pub enum Expr {
         expr: ExprId,
         arms: Vec<MatchArm>,
     },
-    Continue {
-        label: Option<Name>,
-    },
+    Continue,
     Break {
         expr: Option<ExprId>,
-        label: Option<Name>,
     },
     Return {
         expr: Option<ExprId>,
@@ -232,7 +225,7 @@ impl Expr {
                     f(*else_branch);
                 }
             }
-            Expr::Block { statements, tail, .. } => {
+            Expr::Block { statements, tail } => {
                 for stmt in statements {
                     match stmt {
                         Statement::Let { initializer, .. } => {
@@ -248,8 +241,8 @@ impl Expr {
                 }
             }
             Expr::TryBlock { body } => f(*body),
-            Expr::Loop { body, .. } => f(*body),
-            Expr::While { condition, body, .. } => {
+            Expr::Loop { body } => f(*body),
+            Expr::While { condition, body } => {
                 f(*condition);
                 f(*body);
             }
@@ -275,8 +268,8 @@ impl Expr {
                     f(arm.expr);
                 }
             }
-            Expr::Continue { .. } => {}
-            Expr::Break { expr, .. } | Expr::Return { expr } => {
+            Expr::Continue => {}
+            Expr::Break { expr } | Expr::Return { expr } => {
                 if let Some(expr) = expr {
                     f(*expr);
                 }
diff --git a/crates/ra_hir_expand/src/name.rs b/crates/ra_hir_expand/src/name.rs
index ea495cb11a..fecce224ee 100644
--- a/crates/ra_hir_expand/src/name.rs
+++ b/crates/ra_hir_expand/src/name.rs
@@ -37,11 +37,6 @@ impl Name {
         Name(Repr::TupleField(idx))
     }
 
-    pub fn new_lifetime(lt: &ra_syntax::SyntaxToken) -> Name {
-        assert!(lt.kind() == ra_syntax::SyntaxKind::LIFETIME);
-        Name(Repr::Text(lt.text().clone()))
-    }
-
     /// Shortcut to create inline plain text name
     const fn new_inline_ascii(text: &[u8]) -> Name {
         Name::new_text(SmolStr::new_inline_from_ascii(text.len(), text))
diff --git a/crates/ra_hir_ty/src/infer.rs b/crates/ra_hir_ty/src/infer.rs
index dc77e88e50..957d6e0b57 100644
--- a/crates/ra_hir_ty/src/infer.rs
+++ b/crates/ra_hir_ty/src/infer.rs
@@ -219,17 +219,6 @@ struct InferenceContext<'a> {
 struct BreakableContext {
     pub may_break: bool,
     pub break_ty: Ty,
-    pub label: Option<name::Name>,
-}
-
-fn find_breakable<'c>(
-    ctxs: &'c mut [BreakableContext],
-    label: Option<&name::Name>,
-) -> Option<&'c mut BreakableContext> {
-    match label {
-        Some(_) => ctxs.iter_mut().rev().find(|ctx| ctx.label.as_ref() == label),
-        None => ctxs.last_mut(),
-    }
 }
 
 impl<'a> InferenceContext<'a> {
diff --git a/crates/ra_hir_ty/src/infer/expr.rs b/crates/ra_hir_ty/src/infer/expr.rs
index 4a98e2debf..78084cb573 100644
--- a/crates/ra_hir_ty/src/infer/expr.rs
+++ b/crates/ra_hir_ty/src/infer/expr.rs
@@ -22,8 +22,8 @@ use crate::{
 };
 
 use super::{
-    find_breakable, BindingMode, BreakableContext, Diverges, Expectation, InferenceContext,
-    InferenceDiagnostic, TypeMismatch,
+    BindingMode, BreakableContext, Diverges, Expectation, InferenceContext, InferenceDiagnostic,
+    TypeMismatch,
 };
 
 impl<'a> InferenceContext<'a> {
@@ -86,20 +86,16 @@ impl<'a> InferenceContext<'a> {
 
                 self.coerce_merge_branch(&then_ty, &else_ty)
             }
-            Expr::Block { statements, tail, .. } => {
-                // FIXME: Breakable block inference
-                self.infer_block(statements, *tail, expected)
-            }
+            Expr::Block { statements, tail } => self.infer_block(statements, *tail, expected),
             Expr::TryBlock { body } => {
                 let _inner = self.infer_expr(*body, expected);
                 // FIXME should be std::result::Result<{inner}, _>
                 Ty::Unknown
             }
-            Expr::Loop { body, label } => {
+            Expr::Loop { body } => {
                 self.breakables.push(BreakableContext {
                     may_break: false,
                     break_ty: self.table.new_type_var(),
-                    label: label.clone(),
                 });
                 self.infer_expr(*body, &Expectation::has_type(Ty::unit()));
 
@@ -114,12 +110,8 @@ impl<'a> InferenceContext<'a> {
                     Ty::simple(TypeCtor::Never)
                 }
             }
-            Expr::While { condition, body, label } => {
-                self.breakables.push(BreakableContext {
-                    may_break: false,
-                    break_ty: Ty::Unknown,
-                    label: label.clone(),
-                });
+            Expr::While { condition, body } => {
+                self.breakables.push(BreakableContext { may_break: false, break_ty: Ty::Unknown });
                 // while let is desugared to a match loop, so this is always simple while
                 self.infer_expr(*condition, &Expectation::has_type(Ty::simple(TypeCtor::Bool)));
                 self.infer_expr(*body, &Expectation::has_type(Ty::unit()));
@@ -128,14 +120,10 @@ impl<'a> InferenceContext<'a> {
                 self.diverges = Diverges::Maybe;
                 Ty::unit()
             }
-            Expr::For { iterable, body, pat, label } => {
+            Expr::For { iterable, body, pat } => {
                 let iterable_ty = self.infer_expr(*iterable, &Expectation::none());
 
-                self.breakables.push(BreakableContext {
-                    may_break: false,
-                    break_ty: Ty::Unknown,
-                    label: label.clone(),
-                });
+                self.breakables.push(BreakableContext { may_break: false, break_ty: Ty::Unknown });
                 let pat_ty =
                     self.resolve_associated_type(iterable_ty, self.resolve_into_iter_item());
 
@@ -248,24 +236,23 @@ impl<'a> InferenceContext<'a> {
                 let resolver = resolver_for_expr(self.db.upcast(), self.owner, tgt_expr);
                 self.infer_path(&resolver, p, tgt_expr.into()).unwrap_or(Ty::Unknown)
             }
-            Expr::Continue { .. } => Ty::simple(TypeCtor::Never),
-            Expr::Break { expr, label } => {
+            Expr::Continue => Ty::simple(TypeCtor::Never),
+            Expr::Break { expr } => {
                 let val_ty = if let Some(expr) = expr {
                     self.infer_expr(*expr, &Expectation::none())
                 } else {
                     Ty::unit()
                 };
 
-                let last_ty =
-                    if let Some(ctxt) = find_breakable(&mut self.breakables, label.as_ref()) {
-                        ctxt.break_ty.clone()
-                    } else {
-                        Ty::Unknown
-                    };
+                let last_ty = if let Some(ctxt) = self.breakables.last() {
+                    ctxt.break_ty.clone()
+                } else {
+                    Ty::Unknown
+                };
 
                 let merged_type = self.coerce_merge_branch(&last_ty, &val_ty);
 
-                if let Some(ctxt) = find_breakable(&mut self.breakables, label.as_ref()) {
+                if let Some(ctxt) = self.breakables.last_mut() {
                     ctxt.break_ty = merged_type;
                     ctxt.may_break = true;
                 } else {
diff --git a/crates/ra_syntax/src/ast/generated/nodes.rs b/crates/ra_syntax/src/ast/generated/nodes.rs
index cb430ca013..255402fbce 100644
--- a/crates/ra_syntax/src/ast/generated/nodes.rs
+++ b/crates/ra_syntax/src/ast/generated/nodes.rs
@@ -1081,7 +1081,6 @@ pub struct BlockExpr {
 impl ast::AttrsOwner for BlockExpr {}
 impl ast::ModuleItemOwner for BlockExpr {}
 impl BlockExpr {
-    pub fn label(&self) -> Option<Label> { support::child(&self.syntax) }
     pub fn l_curly_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T!['{']) }
     pub fn statements(&self) -> AstChildren<Stmt> { support::children(&self.syntax) }
     pub fn expr(&self) -> Option<Expr> { support::child(&self.syntax) }
diff --git a/docs/user/generated_assists.adoc b/docs/user/generated_assists.adoc
index b8cdfb1cdf..580ab4358e 100644
--- a/docs/user/generated_assists.adoc
+++ b/docs/user/generated_assists.adoc
@@ -1,5 +1,6 @@
 [discrete]
 === `add_custom_impl`
+**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/add_custom_impl.rs#L14[add_custom_impl.rs]
 
 Adds impl block for derived trait.
 
@@ -22,6 +23,7 @@ impl Debug for S {
 
 [discrete]
 === `add_derive`
+**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/add_derive.rs#L9[add_derive.rs]
 
 Adds a new `#[derive()]` clause to a struct or enum.
 
@@ -45,6 +47,7 @@ struct Point {
 
 [discrete]
 === `add_explicit_type`
+**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/add_explicit_type.rs#L9[add_explicit_type.rs]
 
 Specify type for a let binding.
 
@@ -65,6 +68,7 @@ fn main() {
 
 [discrete]
 === `add_from_impl_for_enum`
+**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/add_from_impl_for_enum.rs#L7[add_from_impl_for_enum.rs]
 
 Adds a From impl for an enum variant with one tuple field.
 
@@ -87,6 +91,7 @@ impl From<u32> for A {
 
 [discrete]
 === `add_function`
+**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/add_function.rs#L19[add_function.rs]
 
 Adds a stub function with a signature matching the function under the cursor.
 
@@ -117,6 +122,7 @@ fn bar(arg: &str, baz: Baz) {
 
 [discrete]
 === `add_hash`
+**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/raw_string.rs#L65[raw_string.rs]
 
 Adds a hash to a raw string literal.
 
@@ -137,6 +143,7 @@ fn main() {
 
 [discrete]
 === `add_impl`
+**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/add_impl.rs#L6[add_impl.rs]
 
 Adds a new inherent impl for a type.
 
@@ -161,6 +168,7 @@ impl<T: Clone> Ctx<T> {
 
 [discrete]
 === `add_impl_default_members`
+**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/add_missing_impl_members.rs#L64[add_missing_impl_members.rs]
 
 Adds scaffold for overriding default impl members.
 
@@ -198,6 +206,7 @@ impl Trait for () {
 
 [discrete]
 === `add_impl_missing_members`
+**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/add_missing_impl_members.rs#L24[add_missing_impl_members.rs]
 
 Adds scaffold for required impl members.
 
@@ -233,6 +242,7 @@ impl Trait<u32> for () {
 
 [discrete]
 === `add_new`
+**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/add_new.rs#L12[add_new.rs]
 
 Adds a new inherent impl for a type.
 
@@ -258,6 +268,7 @@ impl<T: Clone> Ctx<T> {
 
 [discrete]
 === `add_turbo_fish`
+**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/add_turbo_fish.rs#L10[add_turbo_fish.rs]
 
 Adds `::<_>` to a call of a generic method or function.
 
@@ -280,6 +291,7 @@ fn main() {
 
 [discrete]
 === `apply_demorgan`
+**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/apply_demorgan.rs#L5[apply_demorgan.rs]
 
 Apply [De Morgan's law](https://en.wikipedia.org/wiki/De_Morgan%27s_laws).
 This transforms expressions of the form `!l || !r` into `!(l && r)`.
@@ -304,6 +316,7 @@ fn main() {
 
 [discrete]
 === `auto_import`
+**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/auto_import.rs#L18[auto_import.rs]
 
 If the name is unresolved, provides all possible imports for it.
 
@@ -326,6 +339,7 @@ fn main() {
 
 [discrete]
 === `change_lifetime_anon_to_named`
+**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/change_lifetime_anon_to_named.rs#L9[change_lifetime_anon_to_named.rs]
 
 Change an anonymous lifetime to a named lifetime.
 
@@ -354,6 +368,7 @@ impl<'a> Cursor<'a> {
 
 [discrete]
 === `change_return_type_to_result`
+**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/change_return_type_to_result.rs#L8[change_return_type_to_result.rs]
 
 Change the function's return type to Result.
 
@@ -370,6 +385,7 @@ fn foo() -> Result<i32, ${0:_}> { Ok(42i32) }
 
 [discrete]
 === `change_visibility`
+**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/change_visibility.rs#L14[change_visibility.rs]
 
 Adds or changes existing visibility specifier.
 
@@ -386,6 +402,7 @@ pub(crate) fn frobnicate() {}
 
 [discrete]
 === `convert_to_guarded_return`
+**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/early_return.rs#L21[early_return.rs]
 
 Replace a large conditional with a guarded return.
 
@@ -413,6 +430,7 @@ fn main() {
 
 [discrete]
 === `fill_match_arms`
+**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/fill_match_arms.rs#L14[fill_match_arms.rs]
 
 Adds missing clauses to a `match` expression.
 
@@ -442,6 +460,7 @@ fn handle(action: Action) {
 
 [discrete]
 === `fix_visibility`
+**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/fix_visibility.rs#L13[fix_visibility.rs]
 
 Makes inaccessible item public.
 
@@ -468,6 +487,7 @@ fn main() {
 
 [discrete]
 === `flip_binexpr`
+**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/flip_binexpr.rs#L5[flip_binexpr.rs]
 
 Flips operands of a binary expression.
 
@@ -488,6 +508,7 @@ fn main() {
 
 [discrete]
 === `flip_comma`
+**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/flip_comma.rs#L5[flip_comma.rs]
 
 Flips two comma-separated items.
 
@@ -508,6 +529,7 @@ fn main() {
 
 [discrete]
 === `flip_trait_bound`
+**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/flip_trait_bound.rs#L9[flip_trait_bound.rs]
 
 Flips two trait bounds.
 
@@ -524,6 +546,7 @@ fn foo<T: Copy + Clone>() { }
 
 [discrete]
 === `inline_local_variable`
+**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/inline_local_variable.rs#L13[inline_local_variable.rs]
 
 Inlines local variable.
 
@@ -545,6 +568,7 @@ fn main() {
 
 [discrete]
 === `introduce_variable`
+**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/introduce_variable.rs#L14[introduce_variable.rs]
 
 Extracts subexpression into a variable.
 
@@ -566,6 +590,7 @@ fn main() {
 
 [discrete]
 === `invert_if`
+**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/invert_if.rs#L12[invert_if.rs]
 
 Apply invert_if
 This transforms if expressions of the form `if !x {A} else {B}` into `if x {B} else {A}`
@@ -589,6 +614,7 @@ fn main() {
 
 [discrete]
 === `make_raw_string`
+**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/raw_string.rs#L10[raw_string.rs]
 
 Adds `r#` to a plain string literal.
 
@@ -609,6 +635,7 @@ fn main() {
 
 [discrete]
 === `make_usual_string`
+**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/raw_string.rs#L39[raw_string.rs]
 
 Turns a raw string into a plain string.
 
@@ -629,6 +656,7 @@ fn main() {
 
 [discrete]
 === `merge_imports`
+**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/merge_imports.rs#L14[merge_imports.rs]
 
 Merges two imports with a common prefix.
 
@@ -646,6 +674,7 @@ use std::{fmt::Formatter, io};
 
 [discrete]
 === `merge_match_arms`
+**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/merge_match_arms.rs#L11[merge_match_arms.rs]
 
 Merges identical match arms.
 
@@ -675,6 +704,7 @@ fn handle(action: Action) {
 
 [discrete]
 === `move_arm_cond_to_match_guard`
+**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/move_guard.rs#L56[move_guard.rs]
 
 Moves if expression from match arm body into a guard.
 
@@ -705,6 +735,7 @@ fn handle(action: Action) {
 
 [discrete]
 === `move_bounds_to_where_clause`
+**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/move_bounds.rs#L10[move_bounds.rs]
 
 Moves inline type bounds to a where clause.
 
@@ -725,6 +756,7 @@ fn apply<T, U, F>(f: F, x: T) -> U where F: FnOnce(T) -> U {
 
 [discrete]
 === `move_guard_to_arm_body`
+**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/move_guard.rs#L8[move_guard.rs]
 
 Moves match guard into match arm body.
 
@@ -755,6 +787,7 @@ fn handle(action: Action) {
 
 [discrete]
 === `remove_dbg`
+**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/remove_dbg.rs#L8[remove_dbg.rs]
 
 Removes `dbg!()` macro call.
 
@@ -775,6 +808,7 @@ fn main() {
 
 [discrete]
 === `remove_hash`
+**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/raw_string.rs#L89[raw_string.rs]
 
 Removes a hash from a raw string literal.
 
@@ -795,6 +829,7 @@ fn main() {
 
 [discrete]
 === `remove_mut`
+**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/remove_mut.rs#L5[remove_mut.rs]
 
 Removes the `mut` keyword.
 
@@ -815,6 +850,7 @@ impl Walrus {
 
 [discrete]
 === `reorder_fields`
+**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/reorder_fields.rs#L10[reorder_fields.rs]
 
 Reorder the fields of record literals and record patterns in the same order as in
 the definition.
@@ -834,6 +870,7 @@ const test: Foo = Foo {foo: 1, bar: 0}
 
 [discrete]
 === `replace_if_let_with_match`
+**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/replace_if_let_with_match.rs#L13[replace_if_let_with_match.rs]
 
 Replaces `if let` with an else branch with a `match` expression.
 
@@ -865,6 +902,7 @@ fn handle(action: Action) {
 
 [discrete]
 === `replace_let_with_if_let`
+**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/replace_let_with_if_let.rs#L14[replace_let_with_if_let.rs]
 
 Replaces `let` with an `if-let`.
 
@@ -892,6 +930,7 @@ fn compute() -> Option<i32> { None }
 
 [discrete]
 === `replace_qualified_name_with_use`
+**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/replace_qualified_name_with_use.rs#L6[replace_qualified_name_with_use.rs]
 
 Adds a use statement for a given fully-qualified name.
 
@@ -910,6 +949,7 @@ fn process(map: HashMap<String, String>) {}
 
 [discrete]
 === `replace_unwrap_with_match`
+**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/replace_unwrap_with_match.rs#L17[replace_unwrap_with_match.rs]
 
 Replaces `unwrap` a `match` expression. Works for Result and Option.
 
@@ -937,6 +977,7 @@ fn main() {
 
 [discrete]
 === `split_import`
+**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/split_import.rs#L7[split_import.rs]
 
 Wraps the tail of import into braces.
 
@@ -953,6 +994,7 @@ use std::{collections::HashMap};
 
 [discrete]
 === `unwrap_block`
+**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/unwrap_block.rs#L9[unwrap_block.rs]
 
 This assist removes if...else, for, while and loop control statements to just keep the body.
 
diff --git a/docs/user/generated_features.adoc b/docs/user/generated_features.adoc
index 803073d550..12812fa0be 100644
--- a/docs/user/generated_features.adoc
+++ b/docs/user/generated_features.adoc
@@ -1,5 +1,5 @@
 === Expand Macro Recursively
-**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/expand_macro.rs[expand_macro.rs]
+**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/expand_macro.rs#L15[expand_macro.rs]
 
 Shows the full macro expansion of the macro at current cursor.
 
@@ -11,7 +11,7 @@ Shows the full macro expansion of the macro at current cursor.
 
 
 === Extend Selection
-**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/extend_selection.rs[extend_selection.rs]
+**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/extend_selection.rs#L15[extend_selection.rs]
 
 Extends the current selection to the encompassing syntactic construct
 (expression, statement, item, module, etc). It works with multiple cursors.
@@ -24,7 +24,7 @@ Extends the current selection to the encompassing syntactic construct
 
 
 === File Structure
-**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/display/structure.rs[structure.rs]
+**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/display/structure.rs#L17[structure.rs]
 
 Provides a tree of the symbols defined in the file. Can be used to
 
@@ -40,7 +40,7 @@ Provides a tree of the symbols defined in the file. Can be used to
 
 
 === Go to Definition
-**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/goto_definition.rs[goto_definition.rs]
+**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/goto_definition.rs#L18[goto_definition.rs]
 
 Navigates to the definition of an identifier.
 
@@ -52,7 +52,7 @@ Navigates to the definition of an identifier.
 
 
 === Go to Implementation
-**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/goto_implementation.rs[goto_implementation.rs]
+**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/goto_implementation.rs#L7[goto_implementation.rs]
 
 Navigates to the impl block of structs, enums or traits. Also implemented as a code lens.
 
@@ -64,7 +64,7 @@ Navigates to the impl block of structs, enums or traits. Also implemented as a c
 
 
 === Go to Type Definition
-**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/goto_type_definition.rs[goto_type_definition.rs]
+**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/goto_type_definition.rs#L6[goto_type_definition.rs]
 
 Navigates to the type of an identifier.
 
@@ -76,14 +76,14 @@ Navigates to the type of an identifier.
 
 
 === Hover
-**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/hover.rs[hover.rs]
+**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/hover.rs#L63[hover.rs]
 
 Shows additional information, like type of an expression or documentation for definition when "focusing" code.
 Focusing is usually hovering with a mouse, but can also be triggered with a shortcut.
 
 
 === Inlay Hints
-**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/inlay_hints.rs[inlay_hints.rs]
+**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/inlay_hints.rs#L40[inlay_hints.rs]
 
 rust-analyzer shows additional information inline with the source code.
 Editors usually render this using read-only virtual text snippets interspersed with code.
@@ -106,7 +106,7 @@ https://github.com/rust-analyzer/rust-analyzer/issues/1623[1], https://github.co
 
 
 === Join Lines
-**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/join_lines.rs[join_lines.rs]
+**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/join_lines.rs#L12[join_lines.rs]
 
 Join selected lines into one, smartly fixing up whitespace, trailing commas, and braces.
 
@@ -118,7 +118,7 @@ Join selected lines into one, smartly fixing up whitespace, trailing commas, and
 
 
 === Magic Completions
-**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/completion.rs[completion.rs]
+**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/completion.rs#L38[completion.rs]
 
 In addition to usual reference completion, rust-analyzer provides some ✨magic✨
 completions as well:
@@ -163,7 +163,7 @@ mod tests {
 
 
 === Matching Brace
-**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/matching_brace.rs[matching_brace.rs]
+**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/matching_brace.rs#L3[matching_brace.rs]
 
 If the cursor is on any brace (`<>(){}[]`) which is a part of a brace-pair,
 moves cursor to the matching brace. It uses the actual parser to determine
@@ -177,7 +177,7 @@ braces, so it won't confuse generics with comparisons.
 
 
 === On Typing Assists
-**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/typing.rs[typing.rs]
+**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/typing.rs#L35[typing.rs]
 
 Some features trigger on typing certain characters:
 
@@ -187,7 +187,7 @@ Some features trigger on typing certain characters:
 
 
 === Parent Module
-**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/parent_module.rs[parent_module.rs]
+**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/parent_module.rs#L12[parent_module.rs]
 
 Navigates to the parent module of the current module.
 
@@ -199,7 +199,7 @@ Navigates to the parent module of the current module.
 
 
 === Run
-**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/runnables.rs[runnables.rs]
+**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/runnables.rs#L45[runnables.rs]
 
 Shows a popup suggesting to run a test/benchmark/binary **at the current cursor
 location**. Super useful for repeatedly running just a single test. Do bind this
@@ -213,7 +213,7 @@ to a shortcut!
 
 
 === Semantic Syntax Highlighting
-**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/syntax_highlighting.rs[syntax_highlighting.rs]
+**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/syntax_highlighting.rs#L33[syntax_highlighting.rs]
 
 rust-analyzer highlights the code semantically.
 For example, `bar` in `foo::Bar` might be colored differently depending on whether `Bar` is an enum or a trait.
@@ -225,7 +225,7 @@ We also give special modifier for `mut` and `&mut` local variables.
 
 
 === Show Syntax Tree
-**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/syntax_tree.rs[syntax_tree.rs]
+**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/syntax_tree.rs#L9[syntax_tree.rs]
 
 Shows the parse tree of the current file. It exists mostly for debugging
 rust-analyzer itself.
@@ -238,7 +238,7 @@ rust-analyzer itself.
 
 
 === Status
-**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/status.rs[status.rs]
+**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/status.rs#L27[status.rs]
 
 Shows internal statistic about memory usage of rust-analyzer.
 
@@ -250,7 +250,7 @@ Shows internal statistic about memory usage of rust-analyzer.
 
 
 === Structural Seach and Replace
-**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/ssr.rs[ssr.rs]
+**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/ssr.rs#L26[ssr.rs]
 
 Search and replace with named wildcards that will match any expression.
 The syntax for a structural search replace command is `<search_pattern> ==>> <replace_pattern>`.
@@ -275,7 +275,7 @@ String::from((y + 5).foo(z))
 
 
 === Workspace Symbol
-**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide_db/src/symbol_index.rs[symbol_index.rs]
+**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide_db/src/symbol_index.rs#L113[symbol_index.rs]
 
 Uses fuzzy-search to find types, modules and functions by name across your
 project and dependencies. This is **the** most useful feature, which improves code
diff --git a/xtask/src/ast_src.rs b/xtask/src/ast_src.rs
index f60f0fb16c..d4621930ed 100644
--- a/xtask/src/ast_src.rs
+++ b/xtask/src/ast_src.rs
@@ -1058,7 +1058,7 @@ pub(crate) const AST_SRC: AstSrc = AstSrc {
         /// [Reference](https://doc.rust-lang.org/reference/expressions/block-expr.html)
         /// [Labels for blocks RFC](https://github.com/rust-lang/rfcs/blob/master/text/2046-label-break-value.md)
         struct BlockExpr: AttrsOwner, ModuleItemOwner {
-            Label, T!['{'], statements: [Stmt], Expr, T!['}'],
+            T!['{'], statements: [Stmt], Expr, T!['}'],
         }
 
         /// Return expression.
diff --git a/xtask/src/codegen.rs b/xtask/src/codegen.rs
index f3917a244a..5511c01d54 100644
--- a/xtask/src/codegen.rs
+++ b/xtask/src/codegen.rs
@@ -61,18 +61,18 @@ fn update(path: &Path, contents: &str, mode: Mode) -> Result<()> {
 }
 
 fn extract_comment_blocks(text: &str) -> Vec<Vec<String>> {
-    do_extract_comment_blocks(text, false)
+    do_extract_comment_blocks(text, false).into_iter().map(|(_line, block)| block).collect()
 }
 
 fn extract_comment_blocks_with_empty_lines(tag: &str, text: &str) -> Vec<CommentBlock> {
     assert!(tag.starts_with(char::is_uppercase));
     let tag = format!("{}:", tag);
     let mut res = Vec::new();
-    for mut block in do_extract_comment_blocks(text, true) {
+    for (line, mut block) in do_extract_comment_blocks(text, true) {
         let first = block.remove(0);
         if first.starts_with(&tag) {
             let id = first[tag.len()..].trim().to_string();
-            let block = CommentBlock { id, contents: block };
+            let block = CommentBlock { id, line, contents: block };
             res.push(block);
         }
     }
@@ -81,31 +81,38 @@ fn extract_comment_blocks_with_empty_lines(tag: &str, text: &str) -> Vec<Comment
 
 struct CommentBlock {
     id: String,
+    line: usize,
     contents: Vec<String>,
 }
 
-fn do_extract_comment_blocks(text: &str, allow_blocks_with_empty_lines: bool) -> Vec<Vec<String>> {
+fn do_extract_comment_blocks(
+    text: &str,
+    allow_blocks_with_empty_lines: bool,
+) -> Vec<(usize, Vec<String>)> {
     let mut res = Vec::new();
 
     let prefix = "// ";
     let lines = text.lines().map(str::trim_start);
 
-    let mut block = vec![];
-    for line in lines {
+    let mut block = (0, vec![]);
+    for (line_num, line) in lines.enumerate() {
         if line == "//" && allow_blocks_with_empty_lines {
-            block.push(String::new());
+            block.1.push(String::new());
             continue;
         }
 
         let is_comment = line.starts_with(prefix);
         if is_comment {
-            block.push(line[prefix.len()..].to_string());
-        } else if !block.is_empty() {
-            res.push(mem::replace(&mut block, Vec::new()));
+            block.1.push(line[prefix.len()..].to_string());
+        } else {
+            if !block.1.is_empty() {
+                res.push(mem::take(&mut block));
+            }
+            block.0 = line_num + 2;
         }
     }
-    if !block.is_empty() {
-        res.push(mem::replace(&mut block, Vec::new()))
+    if !block.1.is_empty() {
+        res.push(block)
     }
     res
 }
@@ -113,11 +120,12 @@ fn do_extract_comment_blocks(text: &str, allow_blocks_with_empty_lines: bool) ->
 #[derive(Debug)]
 struct Location {
     file: PathBuf,
+    line: usize,
 }
 
 impl Location {
-    fn new(file: PathBuf) -> Self {
-        Self { file }
+    fn new(file: PathBuf, line: usize) -> Self {
+        Self { file, line }
     }
 }
 
@@ -128,8 +136,9 @@ impl fmt::Display for Location {
         let name = self.file.file_name().unwrap();
         write!(
             f,
-            "https://github.com/rust-analyzer/rust-analyzer/blob/master/{}[{}]",
+            "https://github.com/rust-analyzer/rust-analyzer/blob/master/{}#L{}[{}]",
             path,
+            self.line,
             name.to_str().unwrap()
         )
     }
diff --git a/xtask/src/codegen/gen_assists_docs.rs b/xtask/src/codegen/gen_assists_docs.rs
index 15a02d317b..6c1be53503 100644
--- a/xtask/src/codegen/gen_assists_docs.rs
+++ b/xtask/src/codegen/gen_assists_docs.rs
@@ -64,7 +64,7 @@ impl Assist {
                 assert_eq!(lines.next().unwrap().as_str(), "->");
                 assert_eq!(lines.next().unwrap().as_str(), "```");
                 let after = take_until(lines.by_ref(), "```");
-                let location = Location::new(path.to_path_buf());
+                let location = Location::new(path.to_path_buf(), block.line);
                 acc.push(Assist { id, location, doc, before, after })
             }
 
@@ -90,6 +90,7 @@ impl fmt::Display for Assist {
         writeln!(
             f,
             "[discrete]\n=== `{}`
+**Source:** {}
 
 {}
 
@@ -101,6 +102,7 @@ impl fmt::Display for Assist {
 ```rust
 {}```",
             self.id,
+            self.location,
             self.doc,
             hide_hash_comments(&before),
             hide_hash_comments(&after)
diff --git a/xtask/src/codegen/gen_feature_docs.rs b/xtask/src/codegen/gen_feature_docs.rs
index 731e7ecf25..31bc3839d0 100644
--- a/xtask/src/codegen/gen_feature_docs.rs
+++ b/xtask/src/codegen/gen_feature_docs.rs
@@ -40,7 +40,8 @@ impl Feature {
                 let id = block.id;
                 assert!(is_valid_feature_name(&id), "invalid feature name: {:?}", id);
                 let doc = block.contents.join("\n");
-                acc.push(Feature { id, location: Location::new(path.clone()), doc })
+                let location = Location::new(path.clone(), block.line);
+                acc.push(Feature { id, location, doc })
             }
 
             Ok(())


Test patch:
diff --git a/crates/ra_hir_ty/src/tests/simple.rs b/crates/ra_hir_ty/src/tests/simple.rs
index 88309157b7..839491b9e2 100644
--- a/crates/ra_hir_ty/src/tests/simple.rs
+++ b/crates/ra_hir_ty/src/tests/simple.rs
@@ -1943,57 +1943,3 @@ fn test() {
     "###
     );
 }
-
-#[test]
-fn infer_labelled_break_with_val() {
-    assert_snapshot!(
-        infer(r#"
-fn foo() {
-    let _x = || 'outer: loop {
-        let inner = 'inner: loop {
-            let i = Default::default();
-            if (break 'outer i) {
-                loop { break 'inner 5i8; };
-            } else if true {
-                break 'inner 6;
-            }
-            break 7;
-        };
-        break inner < 8;
-    };
-}
-"#),
-        @r###"
-    10..336 '{     ...  }; }': ()
-    20..22 '_x': || -> bool
-    25..333 '|| 'ou...     }': || -> bool
-    28..333 ''outer...     }': bool
-    41..333 '{     ...     }': ()
-    55..60 'inner': i8
-    63..301 ''inner...     }': i8
-    76..301 '{     ...     }': ()
-    94..95 'i': bool
-    98..114 'Defaul...efault': {unknown}
-    98..116 'Defaul...ault()': bool
-    130..270 'if (br...     }': ()
-    134..148 'break 'outer i': !
-    147..148 'i': bool
-    150..209 '{     ...     }': ()
-    168..194 'loop {...5i8; }': !
-    173..194 '{ brea...5i8; }': ()
-    175..191 'break ...er 5i8': !
-    188..191 '5i8': i8
-    215..270 'if tru...     }': ()
-    218..222 'true': bool
-    223..270 '{     ...     }': ()
-    241..255 'break 'inner 6': !
-    254..255 '6': i8
-    283..290 'break 7': !
-    289..290 '7': i8
-    311..326 'break inner < 8': !
-    317..322 'inner': i8
-    317..326 'inner < 8': bool
-    325..326 '8': i8
-    "###
-    );
-}