diff options
Diffstat (limited to 'crates')
31 files changed, 581 insertions, 280 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::{ | |||
9 | use base_db::CrateId; | 9 | use base_db::CrateId; |
10 | use cfg::{CfgExpr, CfgOptions}; | 10 | use cfg::{CfgExpr, CfgOptions}; |
11 | use either::Either; | 11 | use either::Either; |
12 | use hir_expand::{hygiene::Hygiene, name::AsName, AstId, AttrId, InFile}; | 12 | use hir_expand::{hygiene::Hygiene, name::AsName, AstId, InFile}; |
13 | use itertools::Itertools; | 13 | use itertools::Itertools; |
14 | use la_arena::ArenaMap; | 14 | use la_arena::ArenaMap; |
15 | use mbe::ast_to_token_tree; | 15 | use 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 | ||
561 | pub struct AttrSourceMap { | 541 | pub 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 | ||
565 | impl AttrSourceMap { | 546 | impl 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. |
581 | pub struct DocsRangeMap { | 593 | pub 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)] | ||
631 | pub(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)] |
619 | pub struct Attr { | 637 | pub 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 | ||
750 | fn collect_attrs( | 768 | fn 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 | ||
770 | pub(crate) fn variants_attrs_source_map( | 798 | pub(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)] |
243 | pub enum Statement { | 243 | pub 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 | ||
248 | impl Expr { | 248 | impl 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 | }; |
68 | use la_arena::Idx; | 67 | use la_arena::Idx; |
69 | use nameres::DefMap; | 68 | use nameres::DefMap; |
70 | use path::ModPath; | 69 | use path::ModPath; |
71 | use syntax::ast; | 70 | use syntax::ast; |
72 | 71 | ||
72 | use crate::attr::AttrId; | ||
73 | use crate::builtin_type::BuiltinType; | 73 | use crate::builtin_type::BuiltinType; |
74 | use item_tree::{ | 74 | use 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 | }; |
18 | use hir_expand::{InFile, MacroCallLoc}; | 18 | use hir_expand::{InFile, MacroCallLoc}; |
19 | use rustc_hash::{FxHashMap, FxHashSet}; | 19 | use rustc_hash::{FxHashMap, FxHashSet}; |
20 | use syntax::ast; | 20 | use syntax::ast; |
21 | 21 | ||
22 | use crate::{ | 22 | use 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 | ||
14 | use crate::{ | 14 | use 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 | ||
268 | fn macro_arg(db: &dyn AstDatabase, id: MacroCallId) -> Option<Arc<(tt::Subtree, mbe::TokenMap)>> { | 268 | fn 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 | |||
3 | use syntax::{ | ||
4 | ast::{self, AttrsOwner}, | ||
5 | AstNode, SyntaxNode, | ||
6 | }; | ||
7 | |||
8 | use crate::{ | ||
9 | db::AstDatabase, | ||
10 | name::{name, AsName}, | ||
11 | LazyMacroId, MacroCallKind, MacroCallLoc, | ||
12 | }; | ||
13 | |||
14 | pub(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`. | ||
35 | fn 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)] | ||
50 | mod 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)] | ||
80 | struct A { | ||
81 | bar: u32 | ||
82 | } | ||
83 | "#, | ||
84 | expect![[r#" | ||
85 | #[allow(unused)] | ||
86 | |||
87 | |||
88 | #[derive(Clone)] | ||
89 | struct 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; | |||
14 | pub mod proc_macro; | 14 | pub mod proc_macro; |
15 | pub mod quote; | 15 | pub mod quote; |
16 | pub mod eager; | 16 | pub mod eager; |
17 | mod input; | ||
17 | 18 | ||
18 | use either::Either; | 19 | use 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)] |
294 | pub enum MacroCallKind { | 299 | pub 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)] | ||
300 | pub struct AttrId(pub u32); | ||
301 | |||
302 | impl MacroCallKind { | 315 | impl 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 | ||
3 | use crate::db::AstDatabase; | 3 | use crate::db::AstDatabase; |
4 | use base_db::{CrateId, ProcMacroId}; | 4 | use base_db::{CrateId, ProcMacroId}; |
5 | use tt::buffer::{Cursor, TokenBuffer}; | ||
6 | 5 | ||
7 | #[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)] | 6 | #[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)] |
8 | pub struct ProcMacroExpander { | 7 | pub 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 | |||
60 | fn 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 | |||
70 | fn 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 | |||
80 | fn 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 | |||
90 | fn 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)] | ||
117 | mod 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#" | ||
139 | SUBTREE $ | ||
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 50497eecb..9476e6297 100644 --- a/crates/hir_ty/src/infer/expr.rs +++ b/crates/hir_ty/src/infer/expr.rs | |||
@@ -809,7 +809,7 @@ impl<'a> InferenceContext<'a> { | |||
809 | let ty = self.resolve_ty_as_possible(ty); | 809 | let ty = self.resolve_ty_as_possible(ty); |
810 | self.infer_pat(*pat, &ty, BindingMode::default()); | 810 | self.infer_pat(*pat, &ty, BindingMode::default()); |
811 | } | 811 | } |
812 | Statement::Expr(expr) => { | 812 | Statement::Expr { expr, .. } => { |
813 | self.infer_expr(*expr, &Expectation::none()); | 813 | self.infer_expr(*expr, &Expectation::none()); |
814 | } | 814 | } |
815 | } | 815 | } |
diff --git a/crates/hir_ty/src/tests/regression.rs b/crates/hir_ty/src/tests/regression.rs index d14f5c9bb..e23bd4da9 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] | ||
1055 | fn 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" | |||
20 | pulldown-cmark-to-cmark = "6.0.0" | 20 | pulldown-cmark-to-cmark = "6.0.0" |
21 | pulldown-cmark = { version = "0.8.0", default-features = false } | 21 | pulldown-cmark = { version = "0.8.0", default-features = false } |
22 | url = "2.1.1" | 22 | url = "2.1.1" |
23 | dot = "0.1.4" | ||
23 | 24 | ||
24 | stdx = { path = "../stdx", version = "0.0.0" } | 25 | stdx = { path = "../stdx", version = "0.0.0" } |
25 | syntax = { path = "../syntax", version = "0.0.0" } | 26 | syntax = { 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; | |||
49 | mod typing; | 49 | mod typing; |
50 | mod markdown_remove; | 50 | mod markdown_remove; |
51 | mod doc_links; | 51 | mod doc_links; |
52 | mod view_crate_graph; | ||
52 | 53 | ||
53 | use std::sync::Arc; | 54 | use 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 @@ | |||
1 | use std::sync::Arc; | ||
2 | |||
3 | use dot::{Id, LabelText}; | ||
4 | use ide_db::{ | ||
5 | base_db::{CrateGraph, CrateId, Dependency, SourceDatabase, SourceDatabaseExt}, | ||
6 | RootDatabase, | ||
7 | }; | ||
8 | use 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 | // |=== | ||
22 | pub(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 | |||
39 | struct DotCrateGraph { | ||
40 | graph: Arc<CrateGraph>, | ||
41 | crates_to_render: FxHashSet<CrateId>, | ||
42 | } | ||
43 | |||
44 | type Edge<'a> = (CrateId, &'a Dependency); | ||
45 | |||
46 | impl<'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 | |||
73 | impl<'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/merge_imports.rs b/crates/ide_assists/src/handlers/merge_imports.rs index add7b8e37..3cd090737 100644 --- a/crates/ide_assists/src/handlers/merge_imports.rs +++ b/crates/ide_assists/src/handlers/merge_imports.rs | |||
@@ -27,14 +27,14 @@ pub(crate) fn merge_imports(acc: &mut Assists, ctx: &AssistContext) -> Option<() | |||
27 | if let Some(use_item) = tree.syntax().parent().and_then(ast::Use::cast) { | 27 | if let Some(use_item) = tree.syntax().parent().and_then(ast::Use::cast) { |
28 | let (merged, to_remove) = | 28 | let (merged, to_remove) = |
29 | next_prev().filter_map(|dir| neighbor(&use_item, dir)).find_map(|use_item2| { | 29 | next_prev().filter_map(|dir| neighbor(&use_item, dir)).find_map(|use_item2| { |
30 | try_merge_imports(&use_item, &use_item2, MergeBehavior::Full).zip(Some(use_item2)) | 30 | try_merge_imports(&use_item, &use_item2, MergeBehavior::Crate).zip(Some(use_item2)) |
31 | })?; | 31 | })?; |
32 | 32 | ||
33 | imports = Some((use_item, merged, to_remove)); | 33 | imports = Some((use_item, merged, to_remove)); |
34 | } else { | 34 | } else { |
35 | let (merged, to_remove) = | 35 | let (merged, to_remove) = |
36 | next_prev().filter_map(|dir| neighbor(&tree, dir)).find_map(|use_tree| { | 36 | next_prev().filter_map(|dir| neighbor(&tree, dir)).find_map(|use_tree| { |
37 | try_merge_trees(&tree, &use_tree, MergeBehavior::Full).zip(Some(use_tree)) | 37 | try_merge_trees(&tree, &use_tree, MergeBehavior::Crate).zip(Some(use_tree)) |
38 | })?; | 38 | })?; |
39 | 39 | ||
40 | uses = Some((tree.clone(), merged, to_remove)) | 40 | uses = Some((tree.clone(), merged, to_remove)) |
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 | |||
87 | struct AssignmentsCollector<'a> { | 99 | struct 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 | ||
93 | impl<'a> AssignmentsCollector<'a> { | 105 | impl<'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 | ||
137 | fn is_equivalent( | 156 | fn 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/ide_assists/src/tests.rs b/crates/ide_assists/src/tests.rs index 9c2847998..0d3969c36 100644 --- a/crates/ide_assists/src/tests.rs +++ b/crates/ide_assists/src/tests.rs | |||
@@ -21,7 +21,7 @@ pub(crate) const TEST_CONFIG: AssistConfig = AssistConfig { | |||
21 | snippet_cap: SnippetCap::new(true), | 21 | snippet_cap: SnippetCap::new(true), |
22 | allowed: None, | 22 | allowed: None, |
23 | insert_use: InsertUseConfig { | 23 | insert_use: InsertUseConfig { |
24 | merge: Some(MergeBehavior::Full), | 24 | merge: Some(MergeBehavior::Crate), |
25 | prefix_kind: hir::PrefixKind::Plain, | 25 | prefix_kind: hir::PrefixKind::Plain, |
26 | group: true, | 26 | group: true, |
27 | }, | 27 | }, |
diff --git a/crates/ide_completion/src/test_utils.rs b/crates/ide_completion/src/test_utils.rs index c9857ec5f..939fb2d47 100644 --- a/crates/ide_completion/src/test_utils.rs +++ b/crates/ide_completion/src/test_utils.rs | |||
@@ -20,7 +20,7 @@ pub(crate) const TEST_CONFIG: CompletionConfig = CompletionConfig { | |||
20 | add_call_argument_snippets: true, | 20 | add_call_argument_snippets: true, |
21 | snippet_cap: SnippetCap::new(true), | 21 | snippet_cap: SnippetCap::new(true), |
22 | insert_use: InsertUseConfig { | 22 | insert_use: InsertUseConfig { |
23 | merge: Some(MergeBehavior::Full), | 23 | merge: Some(MergeBehavior::Crate), |
24 | prefix_kind: PrefixKind::Plain, | 24 | prefix_kind: PrefixKind::Plain, |
25 | group: true, | 25 | group: true, |
26 | }, | 26 | }, |
diff --git a/crates/ide_db/src/helpers/insert_use/tests.rs b/crates/ide_db/src/helpers/insert_use/tests.rs index 048c213e2..248227d29 100644 --- a/crates/ide_db/src/helpers/insert_use/tests.rs +++ b/crates/ide_db/src/helpers/insert_use/tests.rs | |||
@@ -44,7 +44,7 @@ fn insert_not_group_empty() { | |||
44 | 44 | ||
45 | #[test] | 45 | #[test] |
46 | fn insert_existing() { | 46 | fn insert_existing() { |
47 | check_full("std::fs", "use std::fs;", "use std::fs;") | 47 | check_crate("std::fs", "use std::fs;", "use std::fs;") |
48 | } | 48 | } |
49 | 49 | ||
50 | #[test] | 50 | #[test] |
@@ -249,7 +249,7 @@ use self::fmt;", | |||
249 | 249 | ||
250 | #[test] | 250 | #[test] |
251 | fn insert_no_imports() { | 251 | fn insert_no_imports() { |
252 | check_full( | 252 | check_crate( |
253 | "foo::bar", | 253 | "foo::bar", |
254 | "fn main() {}", | 254 | "fn main() {}", |
255 | r"use foo::bar; | 255 | r"use foo::bar; |
@@ -263,7 +263,7 @@ fn insert_empty_file() { | |||
263 | cov_mark::check!(insert_group_empty_file); | 263 | cov_mark::check!(insert_group_empty_file); |
264 | // empty files will get two trailing newlines | 264 | // empty files will get two trailing newlines |
265 | // this is due to the test case insert_no_imports above | 265 | // this is due to the test case insert_no_imports above |
266 | check_full( | 266 | check_crate( |
267 | "foo::bar", | 267 | "foo::bar", |
268 | "", | 268 | "", |
269 | r"use foo::bar; | 269 | r"use foo::bar; |
@@ -290,7 +290,7 @@ fn insert_empty_module() { | |||
290 | #[test] | 290 | #[test] |
291 | fn insert_after_inner_attr() { | 291 | fn insert_after_inner_attr() { |
292 | cov_mark::check!(insert_group_empty_inner_attr); | 292 | cov_mark::check!(insert_group_empty_inner_attr); |
293 | check_full( | 293 | check_crate( |
294 | "foo::bar", | 294 | "foo::bar", |
295 | r"#![allow(unused_imports)]", | 295 | r"#![allow(unused_imports)]", |
296 | r"#![allow(unused_imports)] | 296 | r"#![allow(unused_imports)] |
@@ -301,7 +301,7 @@ use foo::bar;", | |||
301 | 301 | ||
302 | #[test] | 302 | #[test] |
303 | fn insert_after_inner_attr2() { | 303 | fn insert_after_inner_attr2() { |
304 | check_full( | 304 | check_crate( |
305 | "foo::bar", | 305 | "foo::bar", |
306 | r"#![allow(unused_imports)] | 306 | r"#![allow(unused_imports)] |
307 | 307 | ||
@@ -371,12 +371,12 @@ fn main() {}"#, | |||
371 | 371 | ||
372 | #[test] | 372 | #[test] |
373 | fn merge_groups() { | 373 | fn merge_groups() { |
374 | check_last("std::io", r"use std::fmt;", r"use std::{fmt, io};") | 374 | check_module("std::io", r"use std::fmt;", r"use std::{fmt, io};") |
375 | } | 375 | } |
376 | 376 | ||
377 | #[test] | 377 | #[test] |
378 | fn merge_groups_last() { | 378 | fn merge_groups_last() { |
379 | check_last( | 379 | check_module( |
380 | "std::io", | 380 | "std::io", |
381 | r"use std::fmt::{Result, Display};", | 381 | r"use std::fmt::{Result, Display};", |
382 | r"use std::fmt::{Result, Display}; | 382 | r"use std::fmt::{Result, Display}; |
@@ -386,12 +386,12 @@ use std::io;", | |||
386 | 386 | ||
387 | #[test] | 387 | #[test] |
388 | fn merge_last_into_self() { | 388 | fn merge_last_into_self() { |
389 | check_last("foo::bar::baz", r"use foo::bar;", r"use foo::bar::{self, baz};"); | 389 | check_module("foo::bar::baz", r"use foo::bar;", r"use foo::bar::{self, baz};"); |
390 | } | 390 | } |
391 | 391 | ||
392 | #[test] | 392 | #[test] |
393 | fn merge_groups_full() { | 393 | fn merge_groups_full() { |
394 | check_full( | 394 | check_crate( |
395 | "std::io", | 395 | "std::io", |
396 | r"use std::fmt::{Result, Display};", | 396 | r"use std::fmt::{Result, Display};", |
397 | r"use std::{fmt::{Result, Display}, io};", | 397 | r"use std::{fmt::{Result, Display}, io};", |
@@ -400,17 +400,21 @@ fn merge_groups_full() { | |||
400 | 400 | ||
401 | #[test] | 401 | #[test] |
402 | fn merge_groups_long_full() { | 402 | fn merge_groups_long_full() { |
403 | check_full("std::foo::bar::Baz", r"use std::foo::bar::Qux;", r"use std::foo::bar::{Baz, Qux};") | 403 | check_crate("std::foo::bar::Baz", r"use std::foo::bar::Qux;", r"use std::foo::bar::{Baz, Qux};") |
404 | } | 404 | } |
405 | 405 | ||
406 | #[test] | 406 | #[test] |
407 | fn merge_groups_long_last() { | 407 | fn merge_groups_long_last() { |
408 | check_last("std::foo::bar::Baz", r"use std::foo::bar::Qux;", r"use std::foo::bar::{Baz, Qux};") | 408 | check_module( |
409 | "std::foo::bar::Baz", | ||
410 | r"use std::foo::bar::Qux;", | ||
411 | r"use std::foo::bar::{Baz, Qux};", | ||
412 | ) | ||
409 | } | 413 | } |
410 | 414 | ||
411 | #[test] | 415 | #[test] |
412 | fn merge_groups_long_full_list() { | 416 | fn merge_groups_long_full_list() { |
413 | check_full( | 417 | check_crate( |
414 | "std::foo::bar::Baz", | 418 | "std::foo::bar::Baz", |
415 | r"use std::foo::bar::{Qux, Quux};", | 419 | r"use std::foo::bar::{Qux, Quux};", |
416 | r"use std::foo::bar::{Baz, Quux, Qux};", | 420 | r"use std::foo::bar::{Baz, Quux, Qux};", |
@@ -419,7 +423,7 @@ fn merge_groups_long_full_list() { | |||
419 | 423 | ||
420 | #[test] | 424 | #[test] |
421 | fn merge_groups_long_last_list() { | 425 | fn merge_groups_long_last_list() { |
422 | check_last( | 426 | check_module( |
423 | "std::foo::bar::Baz", | 427 | "std::foo::bar::Baz", |
424 | r"use std::foo::bar::{Qux, Quux};", | 428 | r"use std::foo::bar::{Qux, Quux};", |
425 | r"use std::foo::bar::{Baz, Quux, Qux};", | 429 | r"use std::foo::bar::{Baz, Quux, Qux};", |
@@ -428,7 +432,7 @@ fn merge_groups_long_last_list() { | |||
428 | 432 | ||
429 | #[test] | 433 | #[test] |
430 | fn merge_groups_long_full_nested() { | 434 | fn merge_groups_long_full_nested() { |
431 | check_full( | 435 | check_crate( |
432 | "std::foo::bar::Baz", | 436 | "std::foo::bar::Baz", |
433 | r"use std::foo::bar::{Qux, quux::{Fez, Fizz}};", | 437 | r"use std::foo::bar::{Qux, quux::{Fez, Fizz}};", |
434 | r"use std::foo::bar::{Baz, Qux, quux::{Fez, Fizz}};", | 438 | r"use std::foo::bar::{Baz, Qux, quux::{Fez, Fizz}};", |
@@ -437,7 +441,7 @@ fn merge_groups_long_full_nested() { | |||
437 | 441 | ||
438 | #[test] | 442 | #[test] |
439 | fn merge_groups_long_last_nested() { | 443 | fn merge_groups_long_last_nested() { |
440 | check_last( | 444 | check_module( |
441 | "std::foo::bar::Baz", | 445 | "std::foo::bar::Baz", |
442 | r"use std::foo::bar::{Qux, quux::{Fez, Fizz}};", | 446 | r"use std::foo::bar::{Qux, quux::{Fez, Fizz}};", |
443 | r"use std::foo::bar::Baz; | 447 | r"use std::foo::bar::Baz; |
@@ -447,7 +451,7 @@ use std::foo::bar::{Qux, quux::{Fez, Fizz}};", | |||
447 | 451 | ||
448 | #[test] | 452 | #[test] |
449 | fn merge_groups_full_nested_deep() { | 453 | fn merge_groups_full_nested_deep() { |
450 | check_full( | 454 | check_crate( |
451 | "std::foo::bar::quux::Baz", | 455 | "std::foo::bar::quux::Baz", |
452 | r"use std::foo::bar::{Qux, quux::{Fez, Fizz}};", | 456 | r"use std::foo::bar::{Qux, quux::{Fez, Fizz}};", |
453 | r"use std::foo::bar::{Qux, quux::{Baz, Fez, Fizz}};", | 457 | r"use std::foo::bar::{Qux, quux::{Baz, Fez, Fizz}};", |
@@ -456,7 +460,7 @@ fn merge_groups_full_nested_deep() { | |||
456 | 460 | ||
457 | #[test] | 461 | #[test] |
458 | fn merge_groups_full_nested_long() { | 462 | fn merge_groups_full_nested_long() { |
459 | check_full( | 463 | check_crate( |
460 | "std::foo::bar::Baz", | 464 | "std::foo::bar::Baz", |
461 | r"use std::{foo::bar::Qux};", | 465 | r"use std::{foo::bar::Qux};", |
462 | r"use std::{foo::bar::{Baz, Qux}};", | 466 | r"use std::{foo::bar::{Baz, Qux}};", |
@@ -465,7 +469,7 @@ fn merge_groups_full_nested_long() { | |||
465 | 469 | ||
466 | #[test] | 470 | #[test] |
467 | fn merge_groups_last_nested_long() { | 471 | fn merge_groups_last_nested_long() { |
468 | check_full( | 472 | check_crate( |
469 | "std::foo::bar::Baz", | 473 | "std::foo::bar::Baz", |
470 | r"use std::{foo::bar::Qux};", | 474 | r"use std::{foo::bar::Qux};", |
471 | r"use std::{foo::bar::{Baz, Qux}};", | 475 | r"use std::{foo::bar::{Baz, Qux}};", |
@@ -474,7 +478,7 @@ fn merge_groups_last_nested_long() { | |||
474 | 478 | ||
475 | #[test] | 479 | #[test] |
476 | fn merge_groups_skip_pub() { | 480 | fn merge_groups_skip_pub() { |
477 | check_full( | 481 | check_crate( |
478 | "std::io", | 482 | "std::io", |
479 | r"pub use std::fmt::{Result, Display};", | 483 | r"pub use std::fmt::{Result, Display};", |
480 | r"pub use std::fmt::{Result, Display}; | 484 | r"pub use std::fmt::{Result, Display}; |
@@ -484,7 +488,7 @@ use std::io;", | |||
484 | 488 | ||
485 | #[test] | 489 | #[test] |
486 | fn merge_groups_skip_pub_crate() { | 490 | fn merge_groups_skip_pub_crate() { |
487 | check_full( | 491 | check_crate( |
488 | "std::io", | 492 | "std::io", |
489 | r"pub(crate) use std::fmt::{Result, Display};", | 493 | r"pub(crate) use std::fmt::{Result, Display};", |
490 | r"pub(crate) use std::fmt::{Result, Display}; | 494 | r"pub(crate) use std::fmt::{Result, Display}; |
@@ -494,7 +498,7 @@ use std::io;", | |||
494 | 498 | ||
495 | #[test] | 499 | #[test] |
496 | fn merge_groups_skip_attributed() { | 500 | fn merge_groups_skip_attributed() { |
497 | check_full( | 501 | check_crate( |
498 | "std::io", | 502 | "std::io", |
499 | r#" | 503 | r#" |
500 | #[cfg(feature = "gated")] use std::fmt::{Result, Display}; | 504 | #[cfg(feature = "gated")] use std::fmt::{Result, Display}; |
@@ -509,7 +513,7 @@ use std::io; | |||
509 | #[test] | 513 | #[test] |
510 | #[ignore] // FIXME: Support this | 514 | #[ignore] // FIXME: Support this |
511 | fn split_out_merge() { | 515 | fn split_out_merge() { |
512 | check_last( | 516 | check_module( |
513 | "std::fmt::Result", | 517 | "std::fmt::Result", |
514 | r"use std::{fmt, io};", | 518 | r"use std::{fmt, io};", |
515 | r"use std::fmt::{self, Result}; | 519 | r"use std::fmt::{self, Result}; |
@@ -519,29 +523,33 @@ use std::io;", | |||
519 | 523 | ||
520 | #[test] | 524 | #[test] |
521 | fn merge_into_module_import() { | 525 | fn merge_into_module_import() { |
522 | check_full("std::fmt::Result", r"use std::{fmt, io};", r"use std::{fmt::{self, Result}, io};") | 526 | check_crate("std::fmt::Result", r"use std::{fmt, io};", r"use std::{fmt::{self, Result}, io};") |
523 | } | 527 | } |
524 | 528 | ||
525 | #[test] | 529 | #[test] |
526 | fn merge_groups_self() { | 530 | fn merge_groups_self() { |
527 | check_full("std::fmt::Debug", r"use std::fmt;", r"use std::fmt::{self, Debug};") | 531 | check_crate("std::fmt::Debug", r"use std::fmt;", r"use std::fmt::{self, Debug};") |
528 | } | 532 | } |
529 | 533 | ||
530 | #[test] | 534 | #[test] |
531 | fn merge_mod_into_glob() { | 535 | fn merge_mod_into_glob() { |
532 | check_full("token::TokenKind", r"use token::TokenKind::*;", r"use token::TokenKind::{*, self};") | 536 | check_crate( |
537 | "token::TokenKind", | ||
538 | r"use token::TokenKind::*;", | ||
539 | r"use token::TokenKind::{*, self};", | ||
540 | ) | ||
533 | // FIXME: have it emit `use token::TokenKind::{self, *}`? | 541 | // FIXME: have it emit `use token::TokenKind::{self, *}`? |
534 | } | 542 | } |
535 | 543 | ||
536 | #[test] | 544 | #[test] |
537 | fn merge_self_glob() { | 545 | fn merge_self_glob() { |
538 | check_full("self", r"use self::*;", r"use self::{*, self};") | 546 | check_crate("self", r"use self::*;", r"use self::{*, self};") |
539 | // FIXME: have it emit `use {self, *}`? | 547 | // FIXME: have it emit `use {self, *}`? |
540 | } | 548 | } |
541 | 549 | ||
542 | #[test] | 550 | #[test] |
543 | fn merge_glob_nested() { | 551 | fn merge_glob_nested() { |
544 | check_full( | 552 | check_crate( |
545 | "foo::bar::quux::Fez", | 553 | "foo::bar::quux::Fez", |
546 | r"use foo::bar::{Baz, quux::*};", | 554 | r"use foo::bar::{Baz, quux::*};", |
547 | r"use foo::bar::{Baz, quux::{self::*, Fez}};", | 555 | r"use foo::bar::{Baz, quux::{self::*, Fez}};", |
@@ -550,7 +558,7 @@ fn merge_glob_nested() { | |||
550 | 558 | ||
551 | #[test] | 559 | #[test] |
552 | fn merge_nested_considers_first_segments() { | 560 | fn merge_nested_considers_first_segments() { |
553 | check_full( | 561 | check_crate( |
554 | "hir_ty::display::write_bounds_like_dyn_trait", | 562 | "hir_ty::display::write_bounds_like_dyn_trait", |
555 | r"use hir_ty::{autoderef, display::{HirDisplayError, HirFormatter}, method_resolution};", | 563 | r"use hir_ty::{autoderef, display::{HirDisplayError, HirFormatter}, method_resolution};", |
556 | r"use hir_ty::{autoderef, display::{HirDisplayError, HirFormatter, write_bounds_like_dyn_trait}, method_resolution};", | 564 | r"use hir_ty::{autoderef, display::{HirDisplayError, HirFormatter, write_bounds_like_dyn_trait}, method_resolution};", |
@@ -559,7 +567,7 @@ fn merge_nested_considers_first_segments() { | |||
559 | 567 | ||
560 | #[test] | 568 | #[test] |
561 | fn skip_merge_last_too_long() { | 569 | fn skip_merge_last_too_long() { |
562 | check_last( | 570 | check_module( |
563 | "foo::bar", | 571 | "foo::bar", |
564 | r"use foo::bar::baz::Qux;", | 572 | r"use foo::bar::baz::Qux;", |
565 | r"use foo::bar; | 573 | r"use foo::bar; |
@@ -569,7 +577,7 @@ use foo::bar::baz::Qux;", | |||
569 | 577 | ||
570 | #[test] | 578 | #[test] |
571 | fn skip_merge_last_too_long2() { | 579 | fn skip_merge_last_too_long2() { |
572 | check_last( | 580 | check_module( |
573 | "foo::bar::baz::Qux", | 581 | "foo::bar::baz::Qux", |
574 | r"use foo::bar;", | 582 | r"use foo::bar;", |
575 | r"use foo::bar; | 583 | r"use foo::bar; |
@@ -592,7 +600,7 @@ fn merge_last_fail() { | |||
592 | check_merge_only_fail( | 600 | check_merge_only_fail( |
593 | r"use foo::bar::{baz::{Qux, Fez}};", | 601 | r"use foo::bar::{baz::{Qux, Fez}};", |
594 | r"use foo::bar::{baaz::{Quux, Feez}};", | 602 | r"use foo::bar::{baaz::{Quux, Feez}};", |
595 | MergeBehavior::Last, | 603 | MergeBehavior::Module, |
596 | ); | 604 | ); |
597 | } | 605 | } |
598 | 606 | ||
@@ -601,7 +609,7 @@ fn merge_last_fail1() { | |||
601 | check_merge_only_fail( | 609 | check_merge_only_fail( |
602 | r"use foo::bar::{baz::{Qux, Fez}};", | 610 | r"use foo::bar::{baz::{Qux, Fez}};", |
603 | r"use foo::bar::baaz::{Quux, Feez};", | 611 | r"use foo::bar::baaz::{Quux, Feez};", |
604 | MergeBehavior::Last, | 612 | MergeBehavior::Module, |
605 | ); | 613 | ); |
606 | } | 614 | } |
607 | 615 | ||
@@ -610,7 +618,7 @@ fn merge_last_fail2() { | |||
610 | check_merge_only_fail( | 618 | check_merge_only_fail( |
611 | r"use foo::bar::baz::{Qux, Fez};", | 619 | r"use foo::bar::baz::{Qux, Fez};", |
612 | r"use foo::bar::{baaz::{Quux, Feez}};", | 620 | r"use foo::bar::{baaz::{Quux, Feez}};", |
613 | MergeBehavior::Last, | 621 | MergeBehavior::Module, |
614 | ); | 622 | ); |
615 | } | 623 | } |
616 | 624 | ||
@@ -619,7 +627,7 @@ fn merge_last_fail3() { | |||
619 | check_merge_only_fail( | 627 | check_merge_only_fail( |
620 | r"use foo::bar::baz::{Qux, Fez};", | 628 | r"use foo::bar::baz::{Qux, Fez};", |
621 | r"use foo::bar::baaz::{Quux, Feez};", | 629 | r"use foo::bar::baaz::{Quux, Feez};", |
622 | MergeBehavior::Last, | 630 | MergeBehavior::Module, |
623 | ); | 631 | ); |
624 | } | 632 | } |
625 | 633 | ||
@@ -648,12 +656,12 @@ fn check( | |||
648 | assert_eq_text!(ra_fixture_after, &result); | 656 | assert_eq_text!(ra_fixture_after, &result); |
649 | } | 657 | } |
650 | 658 | ||
651 | fn check_full(path: &str, ra_fixture_before: &str, ra_fixture_after: &str) { | 659 | fn check_crate(path: &str, ra_fixture_before: &str, ra_fixture_after: &str) { |
652 | check(path, ra_fixture_before, ra_fixture_after, Some(MergeBehavior::Full), false, true) | 660 | check(path, ra_fixture_before, ra_fixture_after, Some(MergeBehavior::Crate), false, true) |
653 | } | 661 | } |
654 | 662 | ||
655 | fn check_last(path: &str, ra_fixture_before: &str, ra_fixture_after: &str) { | 663 | fn check_module(path: &str, ra_fixture_before: &str, ra_fixture_after: &str) { |
656 | check(path, ra_fixture_before, ra_fixture_after, Some(MergeBehavior::Last), false, true) | 664 | check(path, ra_fixture_before, ra_fixture_after, Some(MergeBehavior::Module), false, true) |
657 | } | 665 | } |
658 | 666 | ||
659 | fn check_none(path: &str, ra_fixture_before: &str, ra_fixture_after: &str) { | 667 | fn check_none(path: &str, ra_fixture_before: &str, ra_fixture_after: &str) { |
diff --git a/crates/ide_db/src/helpers/merge_imports.rs b/crates/ide_db/src/helpers/merge_imports.rs index 148297279..af2a51a4d 100644 --- a/crates/ide_db/src/helpers/merge_imports.rs +++ b/crates/ide_db/src/helpers/merge_imports.rs | |||
@@ -9,19 +9,19 @@ use syntax::ast::{ | |||
9 | /// What type of merges are allowed. | 9 | /// What type of merges are allowed. |
10 | #[derive(Copy, Clone, Debug, PartialEq, Eq)] | 10 | #[derive(Copy, Clone, Debug, PartialEq, Eq)] |
11 | pub enum MergeBehavior { | 11 | pub enum MergeBehavior { |
12 | /// Merge everything together creating deeply nested imports. | 12 | /// Merge imports from the same crate into a single use statement. |
13 | Full, | 13 | Crate, |
14 | /// Only merge the last import level, doesn't allow import nesting. | 14 | /// Merge imports from the same module into a single use statement. |
15 | Last, | 15 | Module, |
16 | } | 16 | } |
17 | 17 | ||
18 | impl MergeBehavior { | 18 | impl MergeBehavior { |
19 | #[inline] | 19 | #[inline] |
20 | fn is_tree_allowed(&self, tree: &ast::UseTree) -> bool { | 20 | fn is_tree_allowed(&self, tree: &ast::UseTree) -> bool { |
21 | match self { | 21 | match self { |
22 | MergeBehavior::Full => true, | 22 | MergeBehavior::Crate => true, |
23 | // only simple single segment paths are allowed | 23 | // only simple single segment paths are allowed |
24 | MergeBehavior::Last => { | 24 | MergeBehavior::Module => { |
25 | tree.use_tree_list().is_none() && tree.path().map(path_len) <= Some(1) | 25 | tree.use_tree_list().is_none() && tree.path().map(path_len) <= Some(1) |
26 | } | 26 | } |
27 | } | 27 | } |
@@ -153,7 +153,7 @@ fn recursive_merge( | |||
153 | } | 153 | } |
154 | } | 154 | } |
155 | Err(_) | 155 | Err(_) |
156 | if merge == MergeBehavior::Last | 156 | if merge == MergeBehavior::Module |
157 | && use_trees.len() > 0 | 157 | && use_trees.len() > 0 |
158 | && rhs_t.use_tree_list().is_some() => | 158 | && rhs_t.use_tree_list().is_some() => |
159 | { | 159 | { |
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 { | |||
119 | pub struct PackageDependency { | 119 | pub 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)] | ||
126 | pub 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 | |||
135 | impl 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 | ||
7 | use anyhow::{Context, Result}; | 7 | use anyhow::{Context, Result}; |
8 | use base_db::{CrateDisplayName, CrateGraph, CrateId, CrateName, Edition, Env, FileId, ProcMacro}; | 8 | use base_db::{CrateDisplayName, CrateGraph, CrateId, CrateName, Edition, Env, FileId, ProcMacro}; |
9 | use cargo_workspace::DepKind; | ||
9 | use cfg::CfgOptions; | 10 | use cfg::CfgOptions; |
10 | use paths::{AbsPath, AbsPathBuf}; | 11 | use paths::{AbsPath, AbsPathBuf}; |
11 | use proc_macro_api::ProcMacroClient; | 12 | use 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/config.rs b/crates/rust-analyzer/src/config.rs index 8879a9161..d83670bda 100644 --- a/crates/rust-analyzer/src/config.rs +++ b/crates/rust-analyzer/src/config.rs | |||
@@ -36,7 +36,7 @@ config_data! { | |||
36 | struct ConfigData { | 36 | struct ConfigData { |
37 | /// The strategy to use when inserting new imports or merging imports. | 37 | /// The strategy to use when inserting new imports or merging imports. |
38 | assist_importMergeBehavior | | 38 | assist_importMergeBehavior | |
39 | assist_importMergeBehaviour: MergeBehaviorDef = "\"full\"", | 39 | assist_importMergeBehaviour: MergeBehaviorDef = "\"crate\"", |
40 | /// The path structure for newly inserted paths to use. | 40 | /// The path structure for newly inserted paths to use. |
41 | assist_importPrefix: ImportPrefixDef = "\"plain\"", | 41 | assist_importPrefix: ImportPrefixDef = "\"plain\"", |
42 | /// Group inserted imports by the [following order](https://rust-analyzer.github.io/manual.html#auto-import). Groups are separated by newlines. | 42 | /// Group inserted imports by the [following order](https://rust-analyzer.github.io/manual.html#auto-import). Groups are separated by newlines. |
@@ -604,8 +604,8 @@ impl Config { | |||
604 | InsertUseConfig { | 604 | InsertUseConfig { |
605 | merge: match self.data.assist_importMergeBehavior { | 605 | merge: match self.data.assist_importMergeBehavior { |
606 | MergeBehaviorDef::None => None, | 606 | MergeBehaviorDef::None => None, |
607 | MergeBehaviorDef::Full => Some(MergeBehavior::Full), | 607 | MergeBehaviorDef::Crate => Some(MergeBehavior::Crate), |
608 | MergeBehaviorDef::Last => Some(MergeBehavior::Last), | 608 | MergeBehaviorDef::Module => Some(MergeBehavior::Module), |
609 | }, | 609 | }, |
610 | prefix_kind: match self.data.assist_importPrefix { | 610 | prefix_kind: match self.data.assist_importPrefix { |
611 | ImportPrefixDef::Plain => PrefixKind::Plain, | 611 | ImportPrefixDef::Plain => PrefixKind::Plain, |
@@ -709,8 +709,10 @@ enum ManifestOrProjectJson { | |||
709 | #[serde(rename_all = "snake_case")] | 709 | #[serde(rename_all = "snake_case")] |
710 | enum MergeBehaviorDef { | 710 | enum MergeBehaviorDef { |
711 | None, | 711 | None, |
712 | Full, | 712 | #[serde(alias = "full")] |
713 | Last, | 713 | Crate, |
714 | #[serde(alias = "last")] | ||
715 | Module, | ||
714 | } | 716 | } |
715 | 717 | ||
716 | #[derive(Deserialize, Debug, Clone)] | 718 | #[derive(Deserialize, Debug, Clone)] |
@@ -867,11 +869,11 @@ fn field_props(field: &str, ty: &str, doc: &[&str], default: &str) -> serde_json | |||
867 | }, | 869 | }, |
868 | "MergeBehaviorDef" => set! { | 870 | "MergeBehaviorDef" => set! { |
869 | "type": "string", | 871 | "type": "string", |
870 | "enum": ["none", "full", "last"], | 872 | "enum": ["none", "crate", "module"], |
871 | "enumDescriptions": [ | 873 | "enumDescriptions": [ |
872 | "No merging", | 874 | "Do not merge imports at all.", |
873 | "Merge all layers of the import trees", | 875 | "Merge imports from the same crate into a single `use` statement.", |
874 | "Only merge the last layer of the import trees" | 876 | "Merge imports from the same module into a single `use` statement." |
875 | ], | 877 | ], |
876 | }, | 878 | }, |
877 | "ImportPrefixDef" => set! { | 879 | "ImportPrefixDef" => set! { |
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 | ||
5 | use std::{ | 5 | use std::{ |
6 | io::Write as _, | 6 | io::{Read, Write as _}, |
7 | process::{self, Stdio}, | 7 | process::{self, Command, Stdio}, |
8 | }; | 8 | }; |
9 | 9 | ||
10 | use ide::{ | 10 | use ide::{ |
@@ -117,6 +117,24 @@ pub(crate) fn handle_view_hir( | |||
117 | Ok(res) | 117 | Ok(res) |
118 | } | 118 | } |
119 | 119 | ||
120 | pub(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 | |||
120 | pub(crate) fn handle_expand_macro( | 138 | pub(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/integrated_benchmarks.rs b/crates/rust-analyzer/src/integrated_benchmarks.rs index 3dcbe397a..56de9681c 100644 --- a/crates/rust-analyzer/src/integrated_benchmarks.rs +++ b/crates/rust-analyzer/src/integrated_benchmarks.rs | |||
@@ -133,7 +133,7 @@ fn integrated_completion_benchmark() { | |||
133 | add_call_argument_snippets: true, | 133 | add_call_argument_snippets: true, |
134 | snippet_cap: SnippetCap::new(true), | 134 | snippet_cap: SnippetCap::new(true), |
135 | insert_use: InsertUseConfig { | 135 | insert_use: InsertUseConfig { |
136 | merge: Some(MergeBehavior::Full), | 136 | merge: Some(MergeBehavior::Crate), |
137 | prefix_kind: hir::PrefixKind::ByCrate, | 137 | prefix_kind: hir::PrefixKind::ByCrate, |
138 | group: true, | 138 | group: true, |
139 | }, | 139 | }, |
@@ -166,7 +166,7 @@ fn integrated_completion_benchmark() { | |||
166 | add_call_argument_snippets: true, | 166 | add_call_argument_snippets: true, |
167 | snippet_cap: SnippetCap::new(true), | 167 | snippet_cap: SnippetCap::new(true), |
168 | insert_use: InsertUseConfig { | 168 | insert_use: InsertUseConfig { |
169 | merge: Some(MergeBehavior::Full), | 169 | merge: Some(MergeBehavior::Crate), |
170 | prefix_kind: hir::PrefixKind::ByCrate, | 170 | prefix_kind: hir::PrefixKind::ByCrate, |
171 | group: true, | 171 | group: true, |
172 | }, | 172 | }, |
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 | ||
64 | pub enum ViewCrateGraph {} | ||
65 | |||
66 | impl Request for ViewCrateGraph { | ||
67 | type Params = (); | ||
68 | type Result = String; | ||
69 | const METHOD: &'static str = "rust-analyzer/viewCrateGraph"; | ||
70 | } | ||
71 | |||
64 | pub enum ExpandMacro {} | 72 | pub enum ExpandMacro {} |
65 | 73 | ||
66 | impl Request for ExpandMacro { | 74 | impl 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) |