aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
Diffstat (limited to 'crates')
-rw-r--r--crates/hir_def/src/attr.rs144
-rw-r--r--crates/hir_def/src/body/lower.rs24
-rw-r--r--crates/hir_def/src/body/scope.rs2
-rw-r--r--crates/hir_def/src/expr.rs4
-rw-r--r--crates/hir_def/src/lib.rs6
-rw-r--r--crates/hir_def/src/nameres/collector.rs4
-rw-r--r--crates/hir_expand/src/builtin_derive.rs4
-rw-r--r--crates/hir_expand/src/db.rs18
-rw-r--r--crates/hir_expand/src/input.rs94
-rw-r--r--crates/hir_expand/src/lib.rs23
-rw-r--r--crates/hir_expand/src/proc_macro.rs102
-rw-r--r--crates/hir_ty/src/diagnostics/expr.rs2
-rw-r--r--crates/hir_ty/src/infer/expr.rs2
-rw-r--r--crates/hir_ty/src/tests/regression.rs49
-rw-r--r--crates/ide/Cargo.toml1
-rw-r--r--crates/ide/src/lib.rs6
-rw-r--r--crates/ide/src/view_crate_graph.rs90
-rw-r--r--crates/ide_assists/src/handlers/extract_struct_from_enum_variant.rs135
-rw-r--r--crates/ide_assists/src/handlers/pull_assignment_up.rs50
-rw-r--r--crates/project_model/src/cargo_workspace.rs34
-rw-r--r--crates/project_model/src/workspace.rs39
-rw-r--r--crates/rust-analyzer/src/cargo_target_spec.rs2
-rw-r--r--crates/rust-analyzer/src/handlers.rs22
-rw-r--r--crates/rust-analyzer/src/lsp_ext.rs8
-rw-r--r--crates/rust-analyzer/src/main_loop.rs1
25 files changed, 637 insertions, 229 deletions
diff --git a/crates/hir_def/src/attr.rs b/crates/hir_def/src/attr.rs
index a2479016e..aadd4e44a 100644
--- a/crates/hir_def/src/attr.rs
+++ b/crates/hir_def/src/attr.rs
@@ -9,7 +9,7 @@ use std::{
9use base_db::CrateId; 9use base_db::CrateId;
10use cfg::{CfgExpr, CfgOptions}; 10use cfg::{CfgExpr, CfgOptions};
11use either::Either; 11use either::Either;
12use hir_expand::{hygiene::Hygiene, name::AsName, AstId, AttrId, InFile}; 12use hir_expand::{hygiene::Hygiene, name::AsName, AstId, InFile};
13use itertools::Itertools; 13use itertools::Itertools;
14use la_arena::ArenaMap; 14use la_arena::ArenaMap;
15use mbe::ast_to_token_tree; 15use mbe::ast_to_token_tree;
@@ -101,17 +101,13 @@ impl RawAttrs {
101 hygiene: &Hygiene, 101 hygiene: &Hygiene,
102 ) -> Self { 102 ) -> Self {
103 let entries = collect_attrs(owner) 103 let entries = collect_attrs(owner)
104 .enumerate() 104 .flat_map(|(id, attr)| match attr {
105 .flat_map(|(i, attr)| { 105 Either::Left(attr) => Attr::from_src(db, attr, hygiene, id),
106 let index = AttrId(i as u32); 106 Either::Right(comment) => comment.doc_comment().map(|doc| Attr {
107 match attr { 107 id,
108 Either::Left(attr) => Attr::from_src(db, attr, hygiene, index), 108 input: Some(AttrInput::Literal(SmolStr::new(doc))),
109 Either::Right(comment) => comment.doc_comment().map(|doc| Attr { 109 path: Interned::new(ModPath::from(hir_expand::name!(doc))),
110 id: index, 110 }),
111 input: Some(AttrInput::Literal(SmolStr::new(doc))),
112 path: Interned::new(ModPath::from(hir_expand::name!(doc))),
113 }),
114 }
115 }) 111 })
116 .collect::<Arc<_>>(); 112 .collect::<Arc<_>>();
117 113
@@ -124,6 +120,7 @@ impl RawAttrs {
124 } 120 }
125 121
126 pub(crate) fn merge(&self, other: Self) -> Self { 122 pub(crate) fn merge(&self, other: Self) -> Self {
123 // FIXME: This needs to fixup `AttrId`s
127 match (&self.entries, &other.entries) { 124 match (&self.entries, &other.entries) {
128 (None, None) => Self::EMPTY, 125 (None, None) => Self::EMPTY,
129 (Some(entries), None) | (None, Some(entries)) => { 126 (Some(entries), None) | (None, Some(entries)) => {
@@ -375,39 +372,26 @@ impl AttrsWithOwner {
375 372
376 let def_map = module.def_map(db); 373 let def_map = module.def_map(db);
377 let mod_data = &def_map[module.local_id]; 374 let mod_data = &def_map[module.local_id];
378 let attrs = match mod_data.declaration_source(db) { 375 match mod_data.declaration_source(db) {
379 Some(it) => { 376 Some(it) => {
380 let mut attrs: Vec<_> = collect_attrs(&it.value as &dyn ast::AttrsOwner) 377 let mut map = AttrSourceMap::new(InFile::new(it.file_id, &it.value));
381 .map(|attr| InFile::new(it.file_id, attr))
382 .collect();
383 if let InFile { file_id, value: ModuleSource::SourceFile(file) } = 378 if let InFile { file_id, value: ModuleSource::SourceFile(file) } =
384 mod_data.definition_source(db) 379 mod_data.definition_source(db)
385 { 380 {
386 attrs.extend( 381 map.merge(AttrSourceMap::new(InFile::new(file_id, &file)));
387 collect_attrs(&file as &dyn ast::AttrsOwner)
388 .map(|attr| InFile::new(file_id, attr)),
389 )
390 } 382 }
391 attrs 383 return map;
392 } 384 }
393 None => { 385 None => {
394 let InFile { file_id, value } = mod_data.definition_source(db); 386 let InFile { file_id, value } = mod_data.definition_source(db);
395 match &value { 387 let attrs_owner = match &value {
396 ModuleSource::SourceFile(file) => { 388 ModuleSource::SourceFile(file) => file as &dyn ast::AttrsOwner,
397 collect_attrs(file as &dyn ast::AttrsOwner) 389 ModuleSource::Module(module) => module as &dyn ast::AttrsOwner,
398 } 390 ModuleSource::BlockExpr(block) => block as &dyn ast::AttrsOwner,
399 ModuleSource::Module(module) => { 391 };
400 collect_attrs(module as &dyn ast::AttrsOwner) 392 return AttrSourceMap::new(InFile::new(file_id, attrs_owner));
401 }
402 ModuleSource::BlockExpr(block) => {
403 collect_attrs(block as &dyn ast::AttrsOwner)
404 }
405 }
406 .map(|attr| InFile::new(file_id, attr))
407 .collect()
408 } 393 }
409 }; 394 }
410 return AttrSourceMap { attrs };
411 } 395 }
412 AttrDefId::FieldId(id) => { 396 AttrDefId::FieldId(id) => {
413 let map = db.fields_attrs_source_map(id.parent); 397 let map = db.fields_attrs_source_map(id.parent);
@@ -462,11 +446,7 @@ impl AttrsWithOwner {
462 }, 446 },
463 }; 447 };
464 448
465 AttrSourceMap { 449 AttrSourceMap::new(owner.as_ref().map(|node| node as &dyn AttrsOwner))
466 attrs: collect_attrs(&owner.value)
467 .map(|attr| InFile::new(owner.file_id, attr))
468 .collect(),
469 }
470 } 450 }
471 451
472 pub fn docs_with_rangemap( 452 pub fn docs_with_rangemap(
@@ -518,7 +498,7 @@ impl AttrsWithOwner {
518 if buf.is_empty() { 498 if buf.is_empty() {
519 None 499 None
520 } else { 500 } else {
521 Some((Documentation(buf), DocsRangeMap { mapping, source: self.source_map(db).attrs })) 501 Some((Documentation(buf), DocsRangeMap { mapping, source_map: self.source_map(db) }))
522 } 502 }
523 } 503 }
524} 504}
@@ -559,27 +539,59 @@ fn inner_attributes(
559} 539}
560 540
561pub struct AttrSourceMap { 541pub struct AttrSourceMap {
562 attrs: Vec<InFile<Either<ast::Attr, ast::Comment>>>, 542 attrs: Vec<InFile<ast::Attr>>,
543 doc_comments: Vec<InFile<ast::Comment>>,
563} 544}
564 545
565impl AttrSourceMap { 546impl AttrSourceMap {
547 fn new(owner: InFile<&dyn ast::AttrsOwner>) -> Self {
548 let mut attrs = Vec::new();
549 let mut doc_comments = Vec::new();
550 for (_, attr) in collect_attrs(owner.value) {
551 match attr {
552 Either::Left(attr) => attrs.push(owner.with_value(attr)),
553 Either::Right(comment) => doc_comments.push(owner.with_value(comment)),
554 }
555 }
556
557 Self { attrs, doc_comments }
558 }
559
560 fn merge(&mut self, other: Self) {
561 self.attrs.extend(other.attrs);
562 self.doc_comments.extend(other.doc_comments);
563 }
564
566 /// Maps the lowered `Attr` back to its original syntax node. 565 /// Maps the lowered `Attr` back to its original syntax node.
567 /// 566 ///
568 /// `attr` must come from the `owner` used for AttrSourceMap 567 /// `attr` must come from the `owner` used for AttrSourceMap
569 /// 568 ///
570 /// Note that the returned syntax node might be a `#[cfg_attr]`, or a doc comment, instead of 569 /// Note that the returned syntax node might be a `#[cfg_attr]`, or a doc comment, instead of
571 /// the attribute represented by `Attr`. 570 /// the attribute represented by `Attr`.
572 pub fn source_of(&self, attr: &Attr) -> InFile<&Either<ast::Attr, ast::Comment>> { 571 pub fn source_of(&self, attr: &Attr) -> InFile<Either<ast::Attr, ast::Comment>> {
573 self.attrs 572 self.source_of_id(attr.id)
574 .get(attr.id.0 as usize) 573 }
575 .unwrap_or_else(|| panic!("cannot find `Attr` at index {:?}", attr.id)) 574
576 .as_ref() 575 fn source_of_id(&self, id: AttrId) -> InFile<Either<ast::Attr, ast::Comment>> {
576 if id.is_doc_comment {
577 self.doc_comments
578 .get(id.ast_index as usize)
579 .unwrap_or_else(|| panic!("cannot find doc comment at index {:?}", id))
580 .clone()
581 .map(|attr| Either::Right(attr))
582 } else {
583 self.attrs
584 .get(id.ast_index as usize)
585 .unwrap_or_else(|| panic!("cannot find `Attr` at index {:?}", id))
586 .clone()
587 .map(|attr| Either::Left(attr))
588 }
577 } 589 }
578} 590}
579 591
580/// A struct to map text ranges from [`Documentation`] back to TextRanges in the syntax tree. 592/// A struct to map text ranges from [`Documentation`] back to TextRanges in the syntax tree.
581pub struct DocsRangeMap { 593pub struct DocsRangeMap {
582 source: Vec<InFile<Either<ast::Attr, ast::Comment>>>, 594 source_map: AttrSourceMap,
583 // (docstring-line-range, attr_index, attr-string-range) 595 // (docstring-line-range, attr_index, attr-string-range)
584 // a mapping from the text range of a line of the [`Documentation`] to the attribute index and 596 // a mapping from the text range of a line of the [`Documentation`] to the attribute index and
585 // the original (untrimmed) syntax doc line 597 // the original (untrimmed) syntax doc line
@@ -596,7 +608,7 @@ impl DocsRangeMap {
596 608
597 let relative_range = range - line_docs_range.start(); 609 let relative_range = range - line_docs_range.start();
598 610
599 let &InFile { file_id, value: ref source } = &self.source[idx.0 as usize]; 611 let &InFile { file_id, value: ref source } = &self.source_map.source_of_id(idx);
600 match source { 612 match source {
601 Either::Left(_) => None, // FIXME, figure out a nice way to handle doc attributes here 613 Either::Left(_) => None, // FIXME, figure out a nice way to handle doc attributes here
602 // as well as for whats done in syntax highlight doc injection 614 // as well as for whats done in syntax highlight doc injection
@@ -615,6 +627,12 @@ impl DocsRangeMap {
615 } 627 }
616} 628}
617 629
630#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
631pub(crate) struct AttrId {
632 is_doc_comment: bool,
633 pub(crate) ast_index: u32,
634}
635
618#[derive(Debug, Clone, PartialEq, Eq)] 636#[derive(Debug, Clone, PartialEq, Eq)]
619pub struct Attr { 637pub struct Attr {
620 pub(crate) id: AttrId, 638 pub(crate) id: AttrId,
@@ -749,22 +767,32 @@ fn attrs_from_item_tree<N: ItemTreeNode>(id: ItemTreeId<N>, db: &dyn DefDatabase
749 767
750fn collect_attrs( 768fn collect_attrs(
751 owner: &dyn ast::AttrsOwner, 769 owner: &dyn ast::AttrsOwner,
752) -> impl Iterator<Item = Either<ast::Attr, ast::Comment>> { 770) -> impl Iterator<Item = (AttrId, Either<ast::Attr, ast::Comment>)> {
753 let (inner_attrs, inner_docs) = inner_attributes(owner.syntax()) 771 let (inner_attrs, inner_docs) = inner_attributes(owner.syntax())
754 .map_or((None, None), |(attrs, docs)| (Some(attrs), Some(docs))); 772 .map_or((None, None), |(attrs, docs)| (Some(attrs), Some(docs)));
755 773
756 let outer_attrs = owner.attrs().filter(|attr| attr.kind().is_outer()); 774 let outer_attrs = owner.attrs().filter(|attr| attr.kind().is_outer());
757 let attrs = outer_attrs 775 let attrs =
758 .chain(inner_attrs.into_iter().flatten()) 776 outer_attrs.chain(inner_attrs.into_iter().flatten()).enumerate().map(|(idx, attr)| {
759 .map(|attr| (attr.syntax().text_range().start(), Either::Left(attr))); 777 (
778 AttrId { ast_index: idx as u32, is_doc_comment: false },
779 attr.syntax().text_range().start(),
780 Either::Left(attr),
781 )
782 });
760 783
761 let outer_docs = 784 let outer_docs =
762 ast::CommentIter::from_syntax_node(owner.syntax()).filter(ast::Comment::is_outer); 785 ast::CommentIter::from_syntax_node(owner.syntax()).filter(ast::Comment::is_outer);
763 let docs = outer_docs 786 let docs =
764 .chain(inner_docs.into_iter().flatten()) 787 outer_docs.chain(inner_docs.into_iter().flatten()).enumerate().map(|(idx, docs_text)| {
765 .map(|docs_text| (docs_text.syntax().text_range().start(), Either::Right(docs_text))); 788 (
789 AttrId { ast_index: idx as u32, is_doc_comment: true },
790 docs_text.syntax().text_range().start(),
791 Either::Right(docs_text),
792 )
793 });
766 // sort here by syntax node offset because the source can have doc attributes and doc strings be interleaved 794 // sort here by syntax node offset because the source can have doc attributes and doc strings be interleaved
767 docs.chain(attrs).sorted_by_key(|&(offset, _)| offset).map(|(_, attr)| attr) 795 docs.chain(attrs).sorted_by_key(|&(_, offset, _)| offset).map(|(id, _, attr)| (id, attr))
768} 796}
769 797
770pub(crate) fn variants_attrs_source_map( 798pub(crate) fn variants_attrs_source_map(
diff --git a/crates/hir_def/src/body/lower.rs b/crates/hir_def/src/body/lower.rs
index 75dc19c11..9f278d35b 100644
--- a/crates/hir_def/src/body/lower.rs
+++ b/crates/hir_def/src/body/lower.rs
@@ -205,7 +205,7 @@ impl ExprCollector<'_> {
205 self.maybe_collect_expr(expr).unwrap_or_else(|| self.missing_expr()) 205 self.maybe_collect_expr(expr).unwrap_or_else(|| self.missing_expr())
206 } 206 }
207 207
208 /// Returns `None` if the expression is `#[cfg]`d out. 208 /// Returns `None` if and only if the expression is `#[cfg]`d out.
209 fn maybe_collect_expr(&mut self, expr: ast::Expr) -> Option<ExprId> { 209 fn maybe_collect_expr(&mut self, expr: ast::Expr) -> Option<ExprId> {
210 let syntax_ptr = AstPtr::new(&expr); 210 let syntax_ptr = AstPtr::new(&expr);
211 self.check_cfg(&expr)?; 211 self.check_cfg(&expr)?;
@@ -668,7 +668,7 @@ impl ExprCollector<'_> {
668 if self.check_cfg(&stmt).is_none() { 668 if self.check_cfg(&stmt).is_none() {
669 return; 669 return;
670 } 670 }
671 671 let has_semi = stmt.semicolon_token().is_some();
672 // Note that macro could be expended to multiple statements 672 // Note that macro could be expended to multiple statements
673 if let Some(ast::Expr::MacroCall(m)) = stmt.expr() { 673 if let Some(ast::Expr::MacroCall(m)) = stmt.expr() {
674 let macro_ptr = AstPtr::new(&m); 674 let macro_ptr = AstPtr::new(&m);
@@ -685,18 +685,19 @@ impl ExprCollector<'_> {
685 statements.statements().for_each(|stmt| this.collect_stmt(stmt)); 685 statements.statements().for_each(|stmt| this.collect_stmt(stmt));
686 if let Some(expr) = statements.expr() { 686 if let Some(expr) = statements.expr() {
687 let expr = this.collect_expr(expr); 687 let expr = this.collect_expr(expr);
688 this.statements_in_scope.push(Statement::Expr(expr)); 688 this.statements_in_scope
689 .push(Statement::Expr { expr, has_semi });
689 } 690 }
690 } 691 }
691 None => { 692 None => {
692 let expr = this.alloc_expr(Expr::Missing, syntax_ptr.clone()); 693 let expr = this.alloc_expr(Expr::Missing, syntax_ptr.clone());
693 this.statements_in_scope.push(Statement::Expr(expr)); 694 this.statements_in_scope.push(Statement::Expr { expr, has_semi });
694 } 695 }
695 }, 696 },
696 ); 697 );
697 } else { 698 } else {
698 let expr = self.collect_expr_opt(stmt.expr()); 699 let expr = self.collect_expr_opt(stmt.expr());
699 self.statements_in_scope.push(Statement::Expr(expr)); 700 self.statements_in_scope.push(Statement::Expr { expr, has_semi });
700 } 701 }
701 } 702 }
702 ast::Stmt::Item(item) => { 703 ast::Stmt::Item(item) => {
@@ -725,8 +726,17 @@ impl ExprCollector<'_> {
725 let prev_statements = std::mem::take(&mut self.statements_in_scope); 726 let prev_statements = std::mem::take(&mut self.statements_in_scope);
726 727
727 block.statements().for_each(|s| self.collect_stmt(s)); 728 block.statements().for_each(|s| self.collect_stmt(s));
728 729 block.tail_expr().and_then(|e| {
729 let tail = block.tail_expr().map(|e| self.collect_expr(e)); 730 let expr = self.maybe_collect_expr(e)?;
731 Some(self.statements_in_scope.push(Statement::Expr { expr, has_semi: false }))
732 });
733
734 let mut tail = None;
735 if let Some(Statement::Expr { expr, has_semi: false }) = self.statements_in_scope.last() {
736 tail = Some(*expr);
737 self.statements_in_scope.pop();
738 }
739 let tail = tail;
730 let statements = std::mem::replace(&mut self.statements_in_scope, prev_statements); 740 let statements = std::mem::replace(&mut self.statements_in_scope, prev_statements);
731 let syntax_node_ptr = AstPtr::new(&block.into()); 741 let syntax_node_ptr = AstPtr::new(&block.into());
732 let expr_id = self.alloc_expr( 742 let expr_id = self.alloc_expr(
diff --git a/crates/hir_def/src/body/scope.rs b/crates/hir_def/src/body/scope.rs
index bd7005ca6..6764de3a7 100644
--- a/crates/hir_def/src/body/scope.rs
+++ b/crates/hir_def/src/body/scope.rs
@@ -157,7 +157,7 @@ fn compute_block_scopes(
157 scope = scopes.new_scope(scope); 157 scope = scopes.new_scope(scope);
158 scopes.add_bindings(body, scope, *pat); 158 scopes.add_bindings(body, scope, *pat);
159 } 159 }
160 Statement::Expr(expr) => { 160 Statement::Expr { expr, .. } => {
161 scopes.set_scope(*expr, scope); 161 scopes.set_scope(*expr, scope);
162 compute_expr_scopes(*expr, body, scopes, scope); 162 compute_expr_scopes(*expr, body, scopes, scope);
163 } 163 }
diff --git a/crates/hir_def/src/expr.rs b/crates/hir_def/src/expr.rs
index b4ad984bd..0c3b41080 100644
--- a/crates/hir_def/src/expr.rs
+++ b/crates/hir_def/src/expr.rs
@@ -242,7 +242,7 @@ pub struct RecordLitField {
242#[derive(Debug, Clone, Eq, PartialEq)] 242#[derive(Debug, Clone, Eq, PartialEq)]
243pub enum Statement { 243pub enum Statement {
244 Let { pat: PatId, type_ref: Option<Interned<TypeRef>>, initializer: Option<ExprId> }, 244 Let { pat: PatId, type_ref: Option<Interned<TypeRef>>, initializer: Option<ExprId> },
245 Expr(ExprId), 245 Expr { expr: ExprId, has_semi: bool },
246} 246}
247 247
248impl Expr { 248impl Expr {
@@ -265,7 +265,7 @@ impl Expr {
265 f(*expr); 265 f(*expr);
266 } 266 }
267 } 267 }
268 Statement::Expr(e) => f(*e), 268 Statement::Expr { expr: expression, .. } => f(*expression),
269 } 269 }
270 } 270 }
271 if let Some(expr) = tail { 271 if let Some(expr) = tail {
diff --git a/crates/hir_def/src/lib.rs b/crates/hir_def/src/lib.rs
index e96ca953f..a82ea5957 100644
--- a/crates/hir_def/src/lib.rs
+++ b/crates/hir_def/src/lib.rs
@@ -62,14 +62,14 @@ use hir_expand::{
62 ast_id_map::FileAstId, 62 ast_id_map::FileAstId,
63 eager::{expand_eager_macro, ErrorEmitted, ErrorSink}, 63 eager::{expand_eager_macro, ErrorEmitted, ErrorSink},
64 hygiene::Hygiene, 64 hygiene::Hygiene,
65 AstId, AttrId, FragmentKind, HirFileId, InFile, MacroCallId, MacroCallKind, MacroDefId, 65 AstId, FragmentKind, HirFileId, InFile, MacroCallId, MacroCallKind, MacroDefId, MacroDefKind,
66 MacroDefKind,
67}; 66};
68use la_arena::Idx; 67use la_arena::Idx;
69use nameres::DefMap; 68use nameres::DefMap;
70use path::ModPath; 69use path::ModPath;
71use syntax::ast; 70use syntax::ast;
72 71
72use crate::attr::AttrId;
73use crate::builtin_type::BuiltinType; 73use crate::builtin_type::BuiltinType;
74use item_tree::{ 74use item_tree::{
75 Const, Enum, Function, Impl, ItemTreeId, ItemTreeNode, ModItem, Static, Struct, Trait, 75 Const, Enum, Function, Impl, ItemTreeId, ItemTreeNode, ModItem, Static, Struct, Trait,
@@ -753,7 +753,7 @@ fn derive_macro_as_call_id(
753 MacroCallKind::Derive { 753 MacroCallKind::Derive {
754 ast_id: item_attr.ast_id, 754 ast_id: item_attr.ast_id,
755 derive_name: last_segment.to_string(), 755 derive_name: last_segment.to_string(),
756 derive_attr, 756 derive_attr_index: derive_attr.ast_index,
757 }, 757 },
758 ) 758 )
759 .into(); 759 .into();
diff --git a/crates/hir_def/src/nameres/collector.rs b/crates/hir_def/src/nameres/collector.rs
index e89136ed1..adfb78c94 100644
--- a/crates/hir_def/src/nameres/collector.rs
+++ b/crates/hir_def/src/nameres/collector.rs
@@ -13,14 +13,14 @@ use hir_expand::{
13 builtin_macro::find_builtin_macro, 13 builtin_macro::find_builtin_macro,
14 name::{AsName, Name}, 14 name::{AsName, Name},
15 proc_macro::ProcMacroExpander, 15 proc_macro::ProcMacroExpander,
16 AttrId, FragmentKind, HirFileId, MacroCallId, MacroCallKind, MacroDefId, MacroDefKind, 16 FragmentKind, HirFileId, MacroCallId, MacroCallKind, MacroDefId, MacroDefKind,
17}; 17};
18use hir_expand::{InFile, MacroCallLoc}; 18use hir_expand::{InFile, MacroCallLoc};
19use rustc_hash::{FxHashMap, FxHashSet}; 19use rustc_hash::{FxHashMap, FxHashSet};
20use syntax::ast; 20use syntax::ast;
21 21
22use crate::{ 22use crate::{
23 attr::Attrs, 23 attr::{AttrId, Attrs},
24 db::DefDatabase, 24 db::DefDatabase,
25 derive_macro_as_call_id, 25 derive_macro_as_call_id,
26 intern::Interned, 26 intern::Interned,
diff --git a/crates/hir_expand/src/builtin_derive.rs b/crates/hir_expand/src/builtin_derive.rs
index 537c03028..b6a6d602f 100644
--- a/crates/hir_expand/src/builtin_derive.rs
+++ b/crates/hir_expand/src/builtin_derive.rs
@@ -269,7 +269,7 @@ mod tests {
269 use expect_test::{expect, Expect}; 269 use expect_test::{expect, Expect};
270 use name::AsName; 270 use name::AsName;
271 271
272 use crate::{test_db::TestDB, AstId, AttrId, MacroCallId, MacroCallKind, MacroCallLoc}; 272 use crate::{test_db::TestDB, AstId, MacroCallId, MacroCallKind, MacroCallLoc};
273 273
274 use super::*; 274 use super::*;
275 275
@@ -320,7 +320,7 @@ $0
320 kind: MacroCallKind::Derive { 320 kind: MacroCallKind::Derive {
321 ast_id, 321 ast_id,
322 derive_name: name.to_string(), 322 derive_name: name.to_string(),
323 derive_attr: AttrId(0), 323 derive_attr_index: 0,
324 }, 324 },
325 }; 325 };
326 326
diff --git a/crates/hir_expand/src/db.rs b/crates/hir_expand/src/db.rs
index 6647e57e7..c43d382ad 100644
--- a/crates/hir_expand/src/db.rs
+++ b/crates/hir_expand/src/db.rs
@@ -12,9 +12,9 @@ use syntax::{
12}; 12};
13 13
14use crate::{ 14use crate::{
15 ast_id_map::AstIdMap, hygiene::HygieneFrame, BuiltinDeriveExpander, BuiltinFnLikeExpander, 15 ast_id_map::AstIdMap, hygiene::HygieneFrame, input::process_macro_input, BuiltinDeriveExpander,
16 EagerCallLoc, EagerMacroId, HirFileId, HirFileIdRepr, LazyMacroId, MacroCallId, MacroCallLoc, 16 BuiltinFnLikeExpander, EagerCallLoc, EagerMacroId, HirFileId, HirFileIdRepr, LazyMacroId,
17 MacroDefId, MacroDefKind, MacroFile, ProcMacroExpander, 17 MacroCallId, MacroCallLoc, MacroDefId, MacroDefKind, MacroFile, ProcMacroExpander,
18}; 18};
19 19
20/// Total limit on the number of tokens produced by any macro invocation. 20/// Total limit on the number of tokens produced by any macro invocation.
@@ -267,7 +267,16 @@ fn parse_macro_expansion(
267 267
268fn macro_arg(db: &dyn AstDatabase, id: MacroCallId) -> Option<Arc<(tt::Subtree, mbe::TokenMap)>> { 268fn macro_arg(db: &dyn AstDatabase, id: MacroCallId) -> Option<Arc<(tt::Subtree, mbe::TokenMap)>> {
269 let arg = db.macro_arg_text(id)?; 269 let arg = db.macro_arg_text(id)?;
270 let (tt, tmap) = mbe::syntax_node_to_token_tree(&SyntaxNode::new_root(arg)); 270 let (mut tt, tmap) = mbe::syntax_node_to_token_tree(&SyntaxNode::new_root(arg));
271
272 if let MacroCallId::LazyMacro(id) = id {
273 let loc: MacroCallLoc = db.lookup_intern_macro(id);
274 if loc.def.is_proc_macro() {
275 // proc macros expect their inputs without parentheses, MBEs expect it with them included
276 tt.delimiter = None;
277 }
278 }
279
271 Some(Arc::new((tt, tmap))) 280 Some(Arc::new((tt, tmap)))
272} 281}
273 282
@@ -281,6 +290,7 @@ fn macro_arg_text(db: &dyn AstDatabase, id: MacroCallId) -> Option<GreenNode> {
281 }; 290 };
282 let loc = db.lookup_intern_macro(id); 291 let loc = db.lookup_intern_macro(id);
283 let arg = loc.kind.arg(db)?; 292 let arg = loc.kind.arg(db)?;
293 let arg = process_macro_input(db, arg, id);
284 Some(arg.green().into()) 294 Some(arg.green().into())
285} 295}
286 296
diff --git a/crates/hir_expand/src/input.rs b/crates/hir_expand/src/input.rs
new file mode 100644
index 000000000..112216859
--- /dev/null
+++ b/crates/hir_expand/src/input.rs
@@ -0,0 +1,94 @@
1//! Macro input conditioning.
2
3use syntax::{
4 ast::{self, AttrsOwner},
5 AstNode, SyntaxNode,
6};
7
8use crate::{
9 db::AstDatabase,
10 name::{name, AsName},
11 LazyMacroId, MacroCallKind, MacroCallLoc,
12};
13
14pub(crate) fn process_macro_input(
15 db: &dyn AstDatabase,
16 node: SyntaxNode,
17 id: LazyMacroId,
18) -> SyntaxNode {
19 let loc: MacroCallLoc = db.lookup_intern_macro(id);
20
21 match loc.kind {
22 MacroCallKind::FnLike { .. } => node,
23 MacroCallKind::Derive { derive_attr_index, .. } => {
24 let item = match ast::Item::cast(node.clone()) {
25 Some(item) => item,
26 None => return node,
27 };
28
29 remove_derives_up_to(item, derive_attr_index as usize).syntax().clone()
30 }
31 }
32}
33
34/// Removes `#[derive]` attributes from `item`, up to `attr_index`.
35fn remove_derives_up_to(item: ast::Item, attr_index: usize) -> ast::Item {
36 let item = item.clone_for_update();
37 for attr in item.attrs().take(attr_index + 1) {
38 if let Some(name) =
39 attr.path().and_then(|path| path.as_single_segment()).and_then(|seg| seg.name_ref())
40 {
41 if name.as_name() == name![derive] {
42 attr.syntax().detach();
43 }
44 }
45 }
46 item
47}
48
49#[cfg(test)]
50mod tests {
51 use base_db::fixture::WithFixture;
52 use base_db::SourceDatabase;
53 use expect_test::{expect, Expect};
54
55 use crate::test_db::TestDB;
56
57 use super::*;
58
59 fn test_remove_derives_up_to(attr: usize, ra_fixture: &str, expect: Expect) {
60 let (db, file_id) = TestDB::with_single_file(&ra_fixture);
61 let parsed = db.parse(file_id);
62
63 let mut items: Vec<_> =
64 parsed.syntax_node().descendants().filter_map(ast::Item::cast).collect();
65 assert_eq!(items.len(), 1);
66
67 let item = remove_derives_up_to(items.pop().unwrap(), attr);
68 expect.assert_eq(&item.to_string());
69 }
70
71 #[test]
72 fn remove_derive() {
73 test_remove_derives_up_to(
74 2,
75 r#"
76#[allow(unused)]
77#[derive(Copy)]
78#[derive(Hello)]
79#[derive(Clone)]
80struct A {
81 bar: u32
82}
83 "#,
84 expect![[r#"
85#[allow(unused)]
86
87
88#[derive(Clone)]
89struct A {
90 bar: u32
91}"#]],
92 );
93 }
94}
diff --git a/crates/hir_expand/src/lib.rs b/crates/hir_expand/src/lib.rs
index 80ab3aeee..88cb16ca4 100644
--- a/crates/hir_expand/src/lib.rs
+++ b/crates/hir_expand/src/lib.rs
@@ -14,6 +14,7 @@ pub mod builtin_macro;
14pub mod proc_macro; 14pub mod proc_macro;
15pub mod quote; 15pub mod quote;
16pub mod eager; 16pub mod eager;
17mod input;
17 18
18use either::Either; 19use either::Either;
19 20
@@ -271,6 +272,10 @@ impl MacroDefId {
271 }; 272 };
272 Either::Left(*id) 273 Either::Left(*id)
273 } 274 }
275
276 pub fn is_proc_macro(&self) -> bool {
277 matches!(self.kind, MacroDefKind::ProcMacro(..))
278 }
274} 279}
275 280
276#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] 281#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
@@ -292,13 +297,21 @@ pub struct MacroCallLoc {
292 297
293#[derive(Debug, Clone, PartialEq, Eq, Hash)] 298#[derive(Debug, Clone, PartialEq, Eq, Hash)]
294pub enum MacroCallKind { 299pub enum MacroCallKind {
295 FnLike { ast_id: AstId<ast::MacroCall>, fragment: FragmentKind }, 300 FnLike {
296 Derive { ast_id: AstId<ast::Item>, derive_name: String, derive_attr: AttrId }, 301 ast_id: AstId<ast::MacroCall>,
302 fragment: FragmentKind,
303 },
304 Derive {
305 ast_id: AstId<ast::Item>,
306 derive_name: String,
307 /// Syntactical index of the invoking `#[derive]` attribute.
308 ///
309 /// Outer attributes are counted first, then inner attributes. This does not support
310 /// out-of-line modules, which may have attributes spread across 2 files!
311 derive_attr_index: u32,
312 },
297} 313}
298 314
299#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
300pub struct AttrId(pub u32);
301
302impl MacroCallKind { 315impl MacroCallKind {
303 fn file_id(&self) -> HirFileId { 316 fn file_id(&self) -> HirFileId {
304 match self { 317 match self {
diff --git a/crates/hir_expand/src/proc_macro.rs b/crates/hir_expand/src/proc_macro.rs
index 75e950816..d5643393a 100644
--- a/crates/hir_expand/src/proc_macro.rs
+++ b/crates/hir_expand/src/proc_macro.rs
@@ -2,7 +2,6 @@
2 2
3use crate::db::AstDatabase; 3use crate::db::AstDatabase;
4use base_db::{CrateId, ProcMacroId}; 4use base_db::{CrateId, ProcMacroId};
5use tt::buffer::{Cursor, TokenBuffer};
6 5
7#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)] 6#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
8pub struct ProcMacroExpander { 7pub struct ProcMacroExpander {
@@ -44,9 +43,6 @@ impl ProcMacroExpander {
44 .clone() 43 .clone()
45 .ok_or_else(|| err!("No derive macro found."))?; 44 .ok_or_else(|| err!("No derive macro found."))?;
46 45
47 let tt = remove_derive_attrs(tt)
48 .ok_or_else(|| err!("Fail to remove derive for custom derive"))?;
49
50 // Proc macros have access to the environment variables of the invoking crate. 46 // Proc macros have access to the environment variables of the invoking crate.
51 let env = &krate_graph[calling_crate].env; 47 let env = &krate_graph[calling_crate].env;
52 48
@@ -56,101 +52,3 @@ impl ProcMacroExpander {
56 } 52 }
57 } 53 }
58} 54}
59
60fn eat_punct(cursor: &mut Cursor, c: char) -> bool {
61 if let Some(tt::buffer::TokenTreeRef::Leaf(tt::Leaf::Punct(punct), _)) = cursor.token_tree() {
62 if punct.char == c {
63 *cursor = cursor.bump();
64 return true;
65 }
66 }
67 false
68}
69
70fn eat_subtree(cursor: &mut Cursor, kind: tt::DelimiterKind) -> bool {
71 if let Some(tt::buffer::TokenTreeRef::Subtree(subtree, _)) = cursor.token_tree() {
72 if Some(kind) == subtree.delimiter_kind() {
73 *cursor = cursor.bump_subtree();
74 return true;
75 }
76 }
77 false
78}
79
80fn eat_ident(cursor: &mut Cursor, t: &str) -> bool {
81 if let Some(tt::buffer::TokenTreeRef::Leaf(tt::Leaf::Ident(ident), _)) = cursor.token_tree() {
82 if t == ident.text.as_str() {
83 *cursor = cursor.bump();
84 return true;
85 }
86 }
87 false
88}
89
90fn remove_derive_attrs(tt: &tt::Subtree) -> Option<tt::Subtree> {
91 let buffer = TokenBuffer::from_tokens(&tt.token_trees);
92 let mut p = buffer.begin();
93 let mut result = tt::Subtree::default();
94
95 while !p.eof() {
96 let curr = p;
97
98 if eat_punct(&mut p, '#') {
99 eat_punct(&mut p, '!');
100 let parent = p;
101 if eat_subtree(&mut p, tt::DelimiterKind::Bracket) {
102 if eat_ident(&mut p, "derive") {
103 p = parent.bump();
104 continue;
105 }
106 }
107 }
108
109 result.token_trees.push(curr.token_tree()?.cloned());
110 p = curr.bump();
111 }
112
113 Some(result)
114}
115
116#[cfg(test)]
117mod tests {
118 use super::*;
119 use test_utils::assert_eq_text;
120
121 #[test]
122 fn test_remove_derive_attrs() {
123 let tt = mbe::parse_to_token_tree(
124 r#"
125 #[allow(unused)]
126 #[derive(Copy)]
127 #[derive(Hello)]
128 struct A {
129 bar: u32
130 }
131"#,
132 )
133 .unwrap()
134 .0;
135 let result = format!("{:#?}", remove_derive_attrs(&tt).unwrap());
136
137 assert_eq_text!(
138 r#"
139SUBTREE $
140 PUNCH # [alone] 0
141 SUBTREE [] 1
142 IDENT allow 2
143 SUBTREE () 3
144 IDENT unused 4
145 IDENT struct 15
146 IDENT A 16
147 SUBTREE {} 17
148 IDENT bar 18
149 PUNCH : [alone] 19
150 IDENT u32 20
151"#
152 .trim(),
153 &result
154 );
155 }
156}
diff --git a/crates/hir_ty/src/diagnostics/expr.rs b/crates/hir_ty/src/diagnostics/expr.rs
index 79602c3dd..47709c1e8 100644
--- a/crates/hir_ty/src/diagnostics/expr.rs
+++ b/crates/hir_ty/src/diagnostics/expr.rs
@@ -83,7 +83,7 @@ impl<'a, 'b> ExprValidator<'a, 'b> {
83 if let Expr::Block { statements, tail, .. } = body_expr { 83 if let Expr::Block { statements, tail, .. } = body_expr {
84 if let Some(t) = tail { 84 if let Some(t) = tail {
85 self.validate_results_in_tail_expr(body.body_expr, *t, db); 85 self.validate_results_in_tail_expr(body.body_expr, *t, db);
86 } else if let Some(Statement::Expr(id)) = statements.last() { 86 } else if let Some(Statement::Expr { expr: id, .. }) = statements.last() {
87 self.validate_missing_tail_expr(body.body_expr, *id, db); 87 self.validate_missing_tail_expr(body.body_expr, *id, db);
88 } 88 }
89 } 89 }
diff --git a/crates/hir_ty/src/infer/expr.rs b/crates/hir_ty/src/infer/expr.rs
index a7e720f88..2178ffd07 100644
--- a/crates/hir_ty/src/infer/expr.rs
+++ b/crates/hir_ty/src/infer/expr.rs
@@ -823,7 +823,7 @@ impl<'a> InferenceContext<'a> {
823 let ty = self.resolve_ty_as_possible(ty); 823 let ty = self.resolve_ty_as_possible(ty);
824 self.infer_pat(*pat, &ty, BindingMode::default()); 824 self.infer_pat(*pat, &ty, BindingMode::default());
825 } 825 }
826 Statement::Expr(expr) => { 826 Statement::Expr { expr, .. } => {
827 self.infer_expr(*expr, &Expectation::none()); 827 self.infer_expr(*expr, &Expectation::none());
828 } 828 }
829 } 829 }
diff --git a/crates/hir_ty/src/tests/regression.rs b/crates/hir_ty/src/tests/regression.rs
index 769809edf..431861712 100644
--- a/crates/hir_ty/src/tests/regression.rs
+++ b/crates/hir_ty/src/tests/regression.rs
@@ -1050,3 +1050,52 @@ fn test() {
1050 "#]], 1050 "#]],
1051 ); 1051 );
1052} 1052}
1053
1054#[test]
1055fn cfg_tail() {
1056 // https://github.com/rust-analyzer/rust-analyzer/issues/8378
1057 check_infer(
1058 r#"
1059 fn fake_tail(){
1060 { "first" }
1061 #[cfg(never)] 9
1062 }
1063 fn multiple_fake(){
1064 { "fake" }
1065 { "fake" }
1066 { "second" }
1067 #[cfg(never)] { 11 }
1068 #[cfg(never)] 12;
1069 #[cfg(never)] 13
1070 }
1071 fn no_normal_tail(){
1072 { "third" }
1073 #[cfg(never)] 14;
1074 #[cfg(never)] 15;
1075 }
1076 fn no_actual_tail(){
1077 { "fourth" };
1078 #[cfg(never)] 14;
1079 #[cfg(never)] 15
1080 }
1081 "#,
1082 expect![[r#"
1083 14..53 '{ ...)] 9 }': &str
1084 20..31 '{ "first" }': &str
1085 22..29 '"first"': &str
1086 72..190 '{ ...] 13 }': &str
1087 78..88 '{ "fake" }': &str
1088 80..86 '"fake"': &str
1089 93..103 '{ "fake" }': &str
1090 95..101 '"fake"': &str
1091 108..120 '{ "second" }': &str
1092 110..118 '"second"': &str
1093 210..273 '{ ... 15; }': &str
1094 216..227 '{ "third" }': &str
1095 218..225 '"third"': &str
1096 293..357 '{ ...] 15 }': ()
1097 299..311 '{ "fourth" }': &str
1098 301..309 '"fourth"': &str
1099 "#]],
1100 )
1101}
diff --git a/crates/ide/Cargo.toml b/crates/ide/Cargo.toml
index f04bcf531..88f3d09d3 100644
--- a/crates/ide/Cargo.toml
+++ b/crates/ide/Cargo.toml
@@ -20,6 +20,7 @@ oorandom = "11.1.2"
20pulldown-cmark-to-cmark = "6.0.0" 20pulldown-cmark-to-cmark = "6.0.0"
21pulldown-cmark = { version = "0.8.0", default-features = false } 21pulldown-cmark = { version = "0.8.0", default-features = false }
22url = "2.1.1" 22url = "2.1.1"
23dot = "0.1.4"
23 24
24stdx = { path = "../stdx", version = "0.0.0" } 25stdx = { path = "../stdx", version = "0.0.0" }
25syntax = { path = "../syntax", version = "0.0.0" } 26syntax = { path = "../syntax", version = "0.0.0" }
diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs
index 8e5b72044..db08547d1 100644
--- a/crates/ide/src/lib.rs
+++ b/crates/ide/src/lib.rs
@@ -49,6 +49,7 @@ mod syntax_tree;
49mod typing; 49mod typing;
50mod markdown_remove; 50mod markdown_remove;
51mod doc_links; 51mod doc_links;
52mod view_crate_graph;
52 53
53use std::sync::Arc; 54use std::sync::Arc;
54 55
@@ -287,6 +288,11 @@ impl Analysis {
287 self.with_db(|db| view_hir::view_hir(&db, position)) 288 self.with_db(|db| view_hir::view_hir(&db, position))
288 } 289 }
289 290
291 /// Renders the crate graph to GraphViz "dot" syntax.
292 pub fn view_crate_graph(&self) -> Cancelable<Result<String, String>> {
293 self.with_db(|db| view_crate_graph::view_crate_graph(&db))
294 }
295
290 pub fn expand_macro(&self, position: FilePosition) -> Cancelable<Option<ExpandedMacro>> { 296 pub fn expand_macro(&self, position: FilePosition) -> Cancelable<Option<ExpandedMacro>> {
291 self.with_db(|db| expand_macro::expand_macro(db, position)) 297 self.with_db(|db| expand_macro::expand_macro(db, position))
292 } 298 }
diff --git a/crates/ide/src/view_crate_graph.rs b/crates/ide/src/view_crate_graph.rs
new file mode 100644
index 000000000..df6cc8aed
--- /dev/null
+++ b/crates/ide/src/view_crate_graph.rs
@@ -0,0 +1,90 @@
1use std::sync::Arc;
2
3use dot::{Id, LabelText};
4use ide_db::{
5 base_db::{CrateGraph, CrateId, Dependency, SourceDatabase, SourceDatabaseExt},
6 RootDatabase,
7};
8use rustc_hash::FxHashSet;
9
10// Feature: View Crate Graph
11//
12// Renders the currently loaded crate graph as an SVG graphic. Requires the `dot` tool, which
13// is part of graphviz, to be installed.
14//
15// Only workspace crates are included, no crates.io dependencies or sysroot crates.
16//
17// |===
18// | Editor | Action Name
19//
20// | VS Code | **Rust Analyzer: View Crate Graph**
21// |===
22pub(crate) fn view_crate_graph(db: &RootDatabase) -> Result<String, String> {
23 let crate_graph = db.crate_graph();
24 let crates_to_render = crate_graph
25 .iter()
26 .filter(|krate| {
27 // Only render workspace crates
28 let root_id = db.file_source_root(crate_graph[*krate].root_file_id);
29 !db.source_root(root_id).is_library
30 })
31 .collect();
32 let graph = DotCrateGraph { graph: crate_graph, crates_to_render };
33
34 let mut dot = Vec::new();
35 dot::render(&graph, &mut dot).unwrap();
36 Ok(String::from_utf8(dot).unwrap())
37}
38
39struct DotCrateGraph {
40 graph: Arc<CrateGraph>,
41 crates_to_render: FxHashSet<CrateId>,
42}
43
44type Edge<'a> = (CrateId, &'a Dependency);
45
46impl<'a> dot::GraphWalk<'a, CrateId, Edge<'a>> for DotCrateGraph {
47 fn nodes(&'a self) -> dot::Nodes<'a, CrateId> {
48 self.crates_to_render.iter().copied().collect()
49 }
50
51 fn edges(&'a self) -> dot::Edges<'a, Edge<'a>> {
52 self.crates_to_render
53 .iter()
54 .flat_map(|krate| {
55 self.graph[*krate]
56 .dependencies
57 .iter()
58 .filter(|dep| self.crates_to_render.contains(&dep.crate_id))
59 .map(move |dep| (*krate, dep))
60 })
61 .collect()
62 }
63
64 fn source(&'a self, edge: &Edge<'a>) -> CrateId {
65 edge.0
66 }
67
68 fn target(&'a self, edge: &Edge<'a>) -> CrateId {
69 edge.1.crate_id
70 }
71}
72
73impl<'a> dot::Labeller<'a, CrateId, Edge<'a>> for DotCrateGraph {
74 fn graph_id(&'a self) -> Id<'a> {
75 Id::new("rust_analyzer_crate_graph").unwrap()
76 }
77
78 fn node_id(&'a self, n: &CrateId) -> Id<'a> {
79 Id::new(format!("_{}", n.0)).unwrap()
80 }
81
82 fn node_shape(&'a self, _node: &CrateId) -> Option<LabelText<'a>> {
83 Some(LabelText::LabelStr("box".into()))
84 }
85
86 fn node_label(&'a self, n: &CrateId) -> LabelText<'a> {
87 let name = self.graph[*n].display_name.as_ref().map_or("(unnamed crate)", |name| &*name);
88 LabelText::LabelStr(name.into())
89 }
90}
diff --git a/crates/ide_assists/src/handlers/extract_struct_from_enum_variant.rs b/crates/ide_assists/src/handlers/extract_struct_from_enum_variant.rs
index 66f274fa7..8e2178391 100644
--- a/crates/ide_assists/src/handlers/extract_struct_from_enum_variant.rs
+++ b/crates/ide_assists/src/handlers/extract_struct_from_enum_variant.rs
@@ -151,20 +151,37 @@ fn create_struct_def(
151 field_list: &Either<ast::RecordFieldList, ast::TupleFieldList>, 151 field_list: &Either<ast::RecordFieldList, ast::TupleFieldList>,
152 visibility: Option<ast::Visibility>, 152 visibility: Option<ast::Visibility>,
153) -> ast::Struct { 153) -> ast::Struct {
154 let pub_vis = Some(make::visibility_pub()); 154 let pub_vis = make::visibility_pub();
155
156 let insert_pub = |node: &'_ SyntaxNode| {
157 let pub_vis = pub_vis.clone_for_update();
158 ted::insert(ted::Position::before(node), pub_vis.syntax());
159 };
160
161 // for fields without any existing visibility, use pub visibility
155 let field_list = match field_list { 162 let field_list = match field_list {
156 Either::Left(field_list) => { 163 Either::Left(field_list) => {
157 make::record_field_list(field_list.fields().flat_map(|field| { 164 let field_list = field_list.clone_for_update();
158 Some(make::record_field(pub_vis.clone(), field.name()?, field.ty()?)) 165
159 })) 166 field_list
160 .into() 167 .fields()
168 .filter(|field| field.visibility().is_none())
169 .filter_map(|field| field.name())
170 .for_each(|it| insert_pub(it.syntax()));
171
172 field_list.into()
161 } 173 }
162 Either::Right(field_list) => make::tuple_field_list( 174 Either::Right(field_list) => {
175 let field_list = field_list.clone_for_update();
176
163 field_list 177 field_list
164 .fields() 178 .fields()
165 .flat_map(|field| Some(make::tuple_field(pub_vis.clone(), field.ty()?))), 179 .filter(|field| field.visibility().is_none())
166 ) 180 .filter_map(|field| field.ty())
167 .into(), 181 .for_each(|it| insert_pub(it.syntax()));
182
183 field_list.into()
184 }
168 }; 185 };
169 186
170 make::struct_(visibility, variant_name, None, field_list).clone_for_update() 187 make::struct_(visibility, variant_name, None, field_list).clone_for_update()
@@ -295,6 +312,106 @@ enum A { One(One) }"#,
295 } 312 }
296 313
297 #[test] 314 #[test]
315 fn test_extract_struct_keep_comments_and_attrs_one_field_named() {
316 check_assist(
317 extract_struct_from_enum_variant,
318 r#"
319enum A {
320 $0One {
321 // leading comment
322 /// doc comment
323 #[an_attr]
324 foo: u32
325 // trailing comment
326 }
327}"#,
328 r#"
329struct One{
330 // leading comment
331 /// doc comment
332 #[an_attr]
333 pub foo: u32
334 // trailing comment
335 }
336
337enum A {
338 One(One)
339}"#,
340 );
341 }
342
343 #[test]
344 fn test_extract_struct_keep_comments_and_attrs_several_fields_named() {
345 check_assist(
346 extract_struct_from_enum_variant,
347 r#"
348enum A {
349 $0One {
350 // comment
351 /// doc
352 #[attr]
353 foo: u32,
354 // comment
355 #[attr]
356 /// doc
357 bar: u32
358 }
359}"#,
360 r#"
361struct One{
362 // comment
363 /// doc
364 #[attr]
365 pub foo: u32,
366 // comment
367 #[attr]
368 /// doc
369 pub bar: u32
370 }
371
372enum A {
373 One(One)
374}"#,
375 );
376 }
377
378 #[test]
379 fn test_extract_struct_keep_comments_and_attrs_several_fields_tuple() {
380 check_assist(
381 extract_struct_from_enum_variant,
382 "enum A { $0One(/* comment */ #[attr] u32, /* another */ u32 /* tail */) }",
383 r#"
384struct One(/* comment */ #[attr] pub u32, /* another */ pub u32 /* tail */);
385
386enum A { One(One) }"#,
387 );
388 }
389
390 #[test]
391 fn test_extract_struct_keep_existing_visibility_named() {
392 check_assist(
393 extract_struct_from_enum_variant,
394 "enum A { $0One{ pub a: u32, pub(crate) b: u32, pub(super) c: u32, d: u32 } }",
395 r#"
396struct One{ pub a: u32, pub(crate) b: u32, pub(super) c: u32, pub d: u32 }
397
398enum A { One(One) }"#,
399 );
400 }
401
402 #[test]
403 fn test_extract_struct_keep_existing_visibility_tuple() {
404 check_assist(
405 extract_struct_from_enum_variant,
406 "enum A { $0One(pub u32, pub(crate) u32, pub(super) u32, u32) }",
407 r#"
408struct One(pub u32, pub(crate) u32, pub(super) u32, pub u32);
409
410enum A { One(One) }"#,
411 );
412 }
413
414 #[test]
298 fn test_extract_enum_variant_name_value_namespace() { 415 fn test_extract_enum_variant_name_value_namespace() {
299 check_assist( 416 check_assist(
300 extract_struct_from_enum_variant, 417 extract_struct_from_enum_variant,
diff --git a/crates/ide_assists/src/handlers/pull_assignment_up.rs b/crates/ide_assists/src/handlers/pull_assignment_up.rs
index 28d14b9c3..3128faa68 100644
--- a/crates/ide_assists/src/handlers/pull_assignment_up.rs
+++ b/crates/ide_assists/src/handlers/pull_assignment_up.rs
@@ -60,6 +60,12 @@ pub(crate) fn pull_assignment_up(acc: &mut Assists, ctx: &AssistContext) -> Opti
60 return None; 60 return None;
61 }; 61 };
62 62
63 if let Some(parent) = tgt.syntax().parent() {
64 if matches!(parent.kind(), syntax::SyntaxKind::BIN_EXPR | syntax::SyntaxKind::LET_STMT) {
65 return None;
66 }
67 }
68
63 acc.add( 69 acc.add(
64 AssistId("pull_assignment_up", AssistKind::RefactorExtract), 70 AssistId("pull_assignment_up", AssistKind::RefactorExtract),
65 "Pull assignment up", 71 "Pull assignment up",
@@ -74,7 +80,13 @@ pub(crate) fn pull_assignment_up(acc: &mut Assists, ctx: &AssistContext) -> Opti
74 let tgt = edit.make_ast_mut(tgt); 80 let tgt = edit.make_ast_mut(tgt);
75 81
76 for (stmt, rhs) in assignments { 82 for (stmt, rhs) in assignments {
77 ted::replace(stmt.syntax(), rhs.syntax()); 83 let mut stmt = stmt.syntax().clone();
84 if let Some(parent) = stmt.parent() {
85 if ast::ExprStmt::cast(parent.clone()).is_some() {
86 stmt = parent.clone();
87 }
88 }
89 ted::replace(stmt, rhs.syntax());
78 } 90 }
79 let assign_expr = make::expr_assignment(collector.common_lhs, tgt.clone()); 91 let assign_expr = make::expr_assignment(collector.common_lhs, tgt.clone());
80 let assign_stmt = make::expr_stmt(assign_expr); 92 let assign_stmt = make::expr_stmt(assign_expr);
@@ -87,7 +99,7 @@ pub(crate) fn pull_assignment_up(acc: &mut Assists, ctx: &AssistContext) -> Opti
87struct AssignmentsCollector<'a> { 99struct AssignmentsCollector<'a> {
88 sema: &'a hir::Semantics<'a, ide_db::RootDatabase>, 100 sema: &'a hir::Semantics<'a, ide_db::RootDatabase>,
89 common_lhs: ast::Expr, 101 common_lhs: ast::Expr,
90 assignments: Vec<(ast::ExprStmt, ast::Expr)>, 102 assignments: Vec<(ast::BinExpr, ast::Expr)>,
91} 103}
92 104
93impl<'a> AssignmentsCollector<'a> { 105impl<'a> AssignmentsCollector<'a> {
@@ -95,6 +107,7 @@ impl<'a> AssignmentsCollector<'a> {
95 for arm in match_expr.match_arm_list()?.arms() { 107 for arm in match_expr.match_arm_list()?.arms() {
96 match arm.expr()? { 108 match arm.expr()? {
97 ast::Expr::BlockExpr(block) => self.collect_block(&block)?, 109 ast::Expr::BlockExpr(block) => self.collect_block(&block)?,
110 ast::Expr::BinExpr(expr) => self.collect_expr(&expr)?,
98 _ => return None, 111 _ => return None,
99 } 112 }
100 } 113 }
@@ -114,24 +127,30 @@ impl<'a> AssignmentsCollector<'a> {
114 } 127 }
115 } 128 }
116 fn collect_block(&mut self, block: &ast::BlockExpr) -> Option<()> { 129 fn collect_block(&mut self, block: &ast::BlockExpr) -> Option<()> {
117 if block.tail_expr().is_some() { 130 let last_expr = block.tail_expr().or_else(|| {
118 return None; 131 if let ast::Stmt::ExprStmt(stmt) = block.statements().last()? {
119 } 132 stmt.expr()
120 133 } else {
121 let last_stmt = block.statements().last()?; 134 None
122 if let ast::Stmt::ExprStmt(stmt) = last_stmt {
123 if let ast::Expr::BinExpr(expr) = stmt.expr()? {
124 if expr.op_kind()? == ast::BinOp::Assignment
125 && is_equivalent(self.sema, &expr.lhs()?, &self.common_lhs)
126 {
127 self.assignments.push((stmt, expr.rhs()?));
128 return Some(());
129 }
130 } 135 }
136 })?;
137
138 if let ast::Expr::BinExpr(expr) = last_expr {
139 return self.collect_expr(&expr);
131 } 140 }
132 141
133 None 142 None
134 } 143 }
144
145 fn collect_expr(&mut self, expr: &ast::BinExpr) -> Option<()> {
146 if expr.op_kind()? == ast::BinOp::Assignment
147 && is_equivalent(self.sema, &expr.lhs()?, &self.common_lhs)
148 {
149 self.assignments.push((expr.clone(), expr.rhs()?));
150 return Some(());
151 }
152 None
153 }
135} 154}
136 155
137fn is_equivalent( 156fn is_equivalent(
@@ -241,7 +260,6 @@ fn foo() {
241 } 260 }
242 261
243 #[test] 262 #[test]
244 #[ignore]
245 fn test_pull_assignment_up_assignment_expressions() { 263 fn test_pull_assignment_up_assignment_expressions() {
246 check_assist( 264 check_assist(
247 pull_assignment_up, 265 pull_assignment_up,
diff --git a/crates/project_model/src/cargo_workspace.rs b/crates/project_model/src/cargo_workspace.rs
index b18699b77..4a4996cf4 100644
--- a/crates/project_model/src/cargo_workspace.rs
+++ b/crates/project_model/src/cargo_workspace.rs
@@ -119,6 +119,32 @@ pub struct RustAnalyzerPackageMetaData {
119pub struct PackageDependency { 119pub struct PackageDependency {
120 pub pkg: Package, 120 pub pkg: Package,
121 pub name: String, 121 pub name: String,
122 pub kind: DepKind,
123}
124
125#[derive(Debug, Clone, Eq, PartialEq)]
126pub enum DepKind {
127 /// Available to the library, binary, and dev targets in the package (but not the build script).
128 Normal,
129 /// Available only to test and bench targets (and the library target, when built with `cfg(test)`).
130 Dev,
131 /// Available only to the build script target.
132 Build,
133}
134
135impl DepKind {
136 fn new(list: &[cargo_metadata::DepKindInfo]) -> Self {
137 for info in list {
138 match info.kind {
139 cargo_metadata::DependencyKind::Normal => return Self::Normal,
140 cargo_metadata::DependencyKind::Development => return Self::Dev,
141 cargo_metadata::DependencyKind::Build => return Self::Build,
142 cargo_metadata::DependencyKind::Unknown => continue,
143 }
144 }
145
146 Self::Normal
147 }
122} 148}
123 149
124/// Information associated with a package's target 150/// Information associated with a package's target
@@ -144,6 +170,7 @@ pub enum TargetKind {
144 Example, 170 Example,
145 Test, 171 Test,
146 Bench, 172 Bench,
173 BuildScript,
147 Other, 174 Other,
148} 175}
149 176
@@ -155,6 +182,7 @@ impl TargetKind {
155 "test" => TargetKind::Test, 182 "test" => TargetKind::Test,
156 "bench" => TargetKind::Bench, 183 "bench" => TargetKind::Bench,
157 "example" => TargetKind::Example, 184 "example" => TargetKind::Example,
185 "custom-build" => TargetKind::BuildScript,
158 "proc-macro" => TargetKind::Lib, 186 "proc-macro" => TargetKind::Lib,
159 _ if kind.contains("lib") => TargetKind::Lib, 187 _ if kind.contains("lib") => TargetKind::Lib,
160 _ => continue, 188 _ => continue,
@@ -301,7 +329,11 @@ impl CargoWorkspace {
301 continue; 329 continue;
302 } 330 }
303 }; 331 };
304 let dep = PackageDependency { name: dep_node.name, pkg }; 332 let dep = PackageDependency {
333 name: dep_node.name,
334 pkg,
335 kind: DepKind::new(&dep_node.dep_kinds),
336 };
305 packages[source].dependencies.push(dep); 337 packages[source].dependencies.push(dep);
306 } 338 }
307 packages[source].active_features.extend(node.features); 339 packages[source].active_features.extend(node.features);
diff --git a/crates/project_model/src/workspace.rs b/crates/project_model/src/workspace.rs
index 84c702fdf..607e62ea5 100644
--- a/crates/project_model/src/workspace.rs
+++ b/crates/project_model/src/workspace.rs
@@ -6,6 +6,7 @@ use std::{collections::VecDeque, fmt, fs, path::Path, process::Command};
6 6
7use anyhow::{Context, Result}; 7use anyhow::{Context, Result};
8use base_db::{CrateDisplayName, CrateGraph, CrateId, CrateName, Edition, Env, FileId, ProcMacro}; 8use base_db::{CrateDisplayName, CrateGraph, CrateId, CrateName, Edition, Env, FileId, ProcMacro};
9use cargo_workspace::DepKind;
9use cfg::CfgOptions; 10use cfg::CfgOptions;
10use paths::{AbsPath, AbsPathBuf}; 11use paths::{AbsPath, AbsPathBuf};
11use proc_macro_api::ProcMacroClient; 12use proc_macro_api::ProcMacroClient;
@@ -390,6 +391,7 @@ fn cargo_to_crate_graph(
390 &cfg_options, 391 &cfg_options,
391 proc_macro_loader, 392 proc_macro_loader,
392 file_id, 393 file_id,
394 &cargo[tgt].name,
393 ); 395 );
394 if cargo[tgt].kind == TargetKind::Lib { 396 if cargo[tgt].kind == TargetKind::Lib {
395 lib_tgt = Some((crate_id, cargo[tgt].name.clone())); 397 lib_tgt = Some((crate_id, cargo[tgt].name.clone()));
@@ -406,23 +408,25 @@ fn cargo_to_crate_graph(
406 } 408 }
407 } 409 }
408 410
409 pkg_crates.entry(pkg).or_insert_with(Vec::new).push(crate_id); 411 pkg_crates.entry(pkg).or_insert_with(Vec::new).push((crate_id, cargo[tgt].kind));
410 } 412 }
411 } 413 }
412 414
413 // Set deps to the core, std and to the lib target of the current package 415 // Set deps to the core, std and to the lib target of the current package
414 for &from in pkg_crates.get(&pkg).into_iter().flatten() { 416 for (from, kind) in pkg_crates.get(&pkg).into_iter().flatten() {
415 if let Some((to, name)) = lib_tgt.clone() { 417 if let Some((to, name)) = lib_tgt.clone() {
416 if to != from { 418 if to != *from && *kind != TargetKind::BuildScript {
419 // (build script can not depend on its library target)
420
417 // For root projects with dashes in their name, 421 // For root projects with dashes in their name,
418 // cargo metadata does not do any normalization, 422 // cargo metadata does not do any normalization,
419 // so we do it ourselves currently 423 // so we do it ourselves currently
420 let name = CrateName::normalize_dashes(&name); 424 let name = CrateName::normalize_dashes(&name);
421 add_dep(&mut crate_graph, from, name, to); 425 add_dep(&mut crate_graph, *from, name, to);
422 } 426 }
423 } 427 }
424 for (name, krate) in public_deps.iter() { 428 for (name, krate) in public_deps.iter() {
425 add_dep(&mut crate_graph, from, name.clone(), *krate); 429 add_dep(&mut crate_graph, *from, name.clone(), *krate);
426 } 430 }
427 } 431 }
428 } 432 }
@@ -433,8 +437,17 @@ fn cargo_to_crate_graph(
433 for dep in cargo[pkg].dependencies.iter() { 437 for dep in cargo[pkg].dependencies.iter() {
434 let name = CrateName::new(&dep.name).unwrap(); 438 let name = CrateName::new(&dep.name).unwrap();
435 if let Some(&to) = pkg_to_lib_crate.get(&dep.pkg) { 439 if let Some(&to) = pkg_to_lib_crate.get(&dep.pkg) {
436 for &from in pkg_crates.get(&pkg).into_iter().flatten() { 440 for (from, kind) in pkg_crates.get(&pkg).into_iter().flatten() {
437 add_dep(&mut crate_graph, from, name.clone(), to) 441 if dep.kind == DepKind::Build && *kind != TargetKind::BuildScript {
442 // Only build scripts may depend on build dependencies.
443 continue;
444 }
445 if dep.kind != DepKind::Build && *kind == TargetKind::BuildScript {
446 // Build scripts may only depend on build dependencies.
447 continue;
448 }
449
450 add_dep(&mut crate_graph, *from, name.clone(), to)
438 } 451 }
439 } 452 }
440 } 453 }
@@ -471,7 +484,7 @@ fn handle_rustc_crates(
471 pkg_to_lib_crate: &mut FxHashMap<la_arena::Idx<crate::PackageData>, CrateId>, 484 pkg_to_lib_crate: &mut FxHashMap<la_arena::Idx<crate::PackageData>, CrateId>,
472 public_deps: &[(CrateName, CrateId)], 485 public_deps: &[(CrateName, CrateId)],
473 cargo: &CargoWorkspace, 486 cargo: &CargoWorkspace,
474 pkg_crates: &FxHashMap<la_arena::Idx<crate::PackageData>, Vec<CrateId>>, 487 pkg_crates: &FxHashMap<la_arena::Idx<crate::PackageData>, Vec<(CrateId, TargetKind)>>,
475) { 488) {
476 let mut rustc_pkg_crates = FxHashMap::default(); 489 let mut rustc_pkg_crates = FxHashMap::default();
477 // The root package of the rustc-dev component is rustc_driver, so we match that 490 // The root package of the rustc-dev component is rustc_driver, so we match that
@@ -505,6 +518,7 @@ fn handle_rustc_crates(
505 &cfg_options, 518 &cfg_options,
506 proc_macro_loader, 519 proc_macro_loader,
507 file_id, 520 file_id,
521 &rustc_workspace[tgt].name,
508 ); 522 );
509 pkg_to_lib_crate.insert(pkg, crate_id); 523 pkg_to_lib_crate.insert(pkg, crate_id);
510 // Add dependencies on core / std / alloc for this crate 524 // Add dependencies on core / std / alloc for this crate
@@ -539,13 +553,13 @@ fn handle_rustc_crates(
539 if !package.metadata.rustc_private { 553 if !package.metadata.rustc_private {
540 continue; 554 continue;
541 } 555 }
542 for &from in pkg_crates.get(&pkg).into_iter().flatten() { 556 for (from, _) in pkg_crates.get(&pkg).into_iter().flatten() {
543 // Avoid creating duplicate dependencies 557 // Avoid creating duplicate dependencies
544 // This avoids the situation where `from` depends on e.g. `arrayvec`, but 558 // This avoids the situation where `from` depends on e.g. `arrayvec`, but
545 // `rust_analyzer` thinks that it should use the one from the `rustcSource` 559 // `rust_analyzer` thinks that it should use the one from the `rustcSource`
546 // instead of the one from `crates.io` 560 // instead of the one from `crates.io`
547 if !crate_graph[from].dependencies.iter().any(|d| d.name == name) { 561 if !crate_graph[*from].dependencies.iter().any(|d| d.name == name) {
548 add_dep(crate_graph, from, name.clone(), to); 562 add_dep(crate_graph, *from, name.clone(), to);
549 } 563 }
550 } 564 }
551 } 565 }
@@ -560,6 +574,7 @@ fn add_target_crate_root(
560 cfg_options: &CfgOptions, 574 cfg_options: &CfgOptions,
561 proc_macro_loader: &dyn Fn(&Path) -> Vec<ProcMacro>, 575 proc_macro_loader: &dyn Fn(&Path) -> Vec<ProcMacro>,
562 file_id: FileId, 576 file_id: FileId,
577 cargo_name: &str,
563) -> CrateId { 578) -> CrateId {
564 let edition = pkg.edition; 579 let edition = pkg.edition;
565 let cfg_options = { 580 let cfg_options = {
@@ -586,7 +601,7 @@ fn add_target_crate_root(
586 .map(|it| proc_macro_loader(&it)) 601 .map(|it| proc_macro_loader(&it))
587 .unwrap_or_default(); 602 .unwrap_or_default();
588 603
589 let display_name = CrateDisplayName::from_canonical_name(pkg.name.clone()); 604 let display_name = CrateDisplayName::from_canonical_name(cargo_name.to_string());
590 let crate_id = crate_graph.add_crate_root( 605 let crate_id = crate_graph.add_crate_root(
591 file_id, 606 file_id,
592 edition, 607 edition,
diff --git a/crates/rust-analyzer/src/cargo_target_spec.rs b/crates/rust-analyzer/src/cargo_target_spec.rs
index 909c21532..f4cd43448 100644
--- a/crates/rust-analyzer/src/cargo_target_spec.rs
+++ b/crates/rust-analyzer/src/cargo_target_spec.rs
@@ -159,7 +159,7 @@ impl CargoTargetSpec {
159 TargetKind::Lib => { 159 TargetKind::Lib => {
160 buf.push("--lib".to_string()); 160 buf.push("--lib".to_string());
161 } 161 }
162 TargetKind::Other => (), 162 TargetKind::Other | TargetKind::BuildScript => (),
163 } 163 }
164 } 164 }
165} 165}
diff --git a/crates/rust-analyzer/src/handlers.rs b/crates/rust-analyzer/src/handlers.rs
index f6e40f872..551013aa9 100644
--- a/crates/rust-analyzer/src/handlers.rs
+++ b/crates/rust-analyzer/src/handlers.rs
@@ -3,8 +3,8 @@
3//! `ide` crate. 3//! `ide` crate.
4 4
5use std::{ 5use std::{
6 io::Write as _, 6 io::{Read, Write as _},
7 process::{self, Stdio}, 7 process::{self, Command, Stdio},
8}; 8};
9 9
10use ide::{ 10use ide::{
@@ -117,6 +117,24 @@ pub(crate) fn handle_view_hir(
117 Ok(res) 117 Ok(res)
118} 118}
119 119
120pub(crate) fn handle_view_crate_graph(snap: GlobalStateSnapshot, (): ()) -> Result<String> {
121 let _p = profile::span("handle_view_crate_graph");
122 let dot = snap.analysis.view_crate_graph()??;
123
124 // We shell out to `dot` to render to SVG, as there does not seem to be a pure-Rust renderer.
125 let child = Command::new("dot")
126 .arg("-Tsvg")
127 .stdin(Stdio::piped())
128 .stdout(Stdio::piped())
129 .spawn()
130 .map_err(|err| format!("failed to spawn `dot`: {}", err))?;
131 child.stdin.unwrap().write_all(dot.as_bytes())?;
132
133 let mut svg = String::new();
134 child.stdout.unwrap().read_to_string(&mut svg)?;
135 Ok(svg)
136}
137
120pub(crate) fn handle_expand_macro( 138pub(crate) fn handle_expand_macro(
121 snap: GlobalStateSnapshot, 139 snap: GlobalStateSnapshot,
122 params: lsp_ext::ExpandMacroParams, 140 params: lsp_ext::ExpandMacroParams,
diff --git a/crates/rust-analyzer/src/lsp_ext.rs b/crates/rust-analyzer/src/lsp_ext.rs
index b8835a534..3bd098058 100644
--- a/crates/rust-analyzer/src/lsp_ext.rs
+++ b/crates/rust-analyzer/src/lsp_ext.rs
@@ -61,6 +61,14 @@ impl Request for ViewHir {
61 const METHOD: &'static str = "rust-analyzer/viewHir"; 61 const METHOD: &'static str = "rust-analyzer/viewHir";
62} 62}
63 63
64pub enum ViewCrateGraph {}
65
66impl Request for ViewCrateGraph {
67 type Params = ();
68 type Result = String;
69 const METHOD: &'static str = "rust-analyzer/viewCrateGraph";
70}
71
64pub enum ExpandMacro {} 72pub enum ExpandMacro {}
65 73
66impl Request for ExpandMacro { 74impl Request for ExpandMacro {
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs
index ce7ece559..c7bd7eee1 100644
--- a/crates/rust-analyzer/src/main_loop.rs
+++ b/crates/rust-analyzer/src/main_loop.rs
@@ -513,6 +513,7 @@ impl GlobalState {
513 .on::<lsp_ext::AnalyzerStatus>(handlers::handle_analyzer_status) 513 .on::<lsp_ext::AnalyzerStatus>(handlers::handle_analyzer_status)
514 .on::<lsp_ext::SyntaxTree>(handlers::handle_syntax_tree) 514 .on::<lsp_ext::SyntaxTree>(handlers::handle_syntax_tree)
515 .on::<lsp_ext::ViewHir>(handlers::handle_view_hir) 515 .on::<lsp_ext::ViewHir>(handlers::handle_view_hir)
516 .on::<lsp_ext::ViewCrateGraph>(handlers::handle_view_crate_graph)
516 .on::<lsp_ext::ExpandMacro>(handlers::handle_expand_macro) 517 .on::<lsp_ext::ExpandMacro>(handlers::handle_expand_macro)
517 .on::<lsp_ext::ParentModule>(handlers::handle_parent_module) 518 .on::<lsp_ext::ParentModule>(handlers::handle_parent_module)
518 .on::<lsp_ext::Runnables>(handlers::handle_runnables) 519 .on::<lsp_ext::Runnables>(handlers::handle_runnables)