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/consts.rs21
-rw-r--r--crates/hir_ty/src/diagnostics/expr.rs2
-rw-r--r--crates/hir_ty/src/display.rs2
-rw-r--r--crates/hir_ty/src/infer/expr.rs28
-rw-r--r--crates/hir_ty/src/interner.rs16
-rw-r--r--crates/hir_ty/src/lib.rs7
-rw-r--r--crates/hir_ty/src/lower.rs2
-rw-r--r--crates/hir_ty/src/tests/coercion.rs90
-rw-r--r--crates/hir_ty/src/tests/patterns.rs6
-rw-r--r--crates/hir_ty/src/tests/regression.rs61
-rw-r--r--crates/hir_ty/src/tests/simple.rs80
-rw-r--r--crates/hir_ty/src/tests/traits.rs2
-rw-r--r--crates/ide/Cargo.toml1
-rw-r--r--crates/ide/src/inlay_hints.rs2
-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/ide_assists/src/handlers/replace_unwrap_with_match.rs2
-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
36 files changed, 794 insertions, 340 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/consts.rs b/crates/hir_ty/src/consts.rs
new file mode 100644
index 000000000..0044b1cff
--- /dev/null
+++ b/crates/hir_ty/src/consts.rs
@@ -0,0 +1,21 @@
1//! Handling of concrete const values
2
3/// A concrete constant value
4#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
5pub enum ConstScalar {
6 // for now, we only support the trivial case of constant evaluating the length of an array
7 // Note that this is u64 because the target usize may be bigger than our usize
8 Usize(u64),
9
10 /// Case of an unknown value that rustc might know but we don't
11 Unknown,
12}
13
14impl std::fmt::Display for ConstScalar {
15 fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
16 match self {
17 ConstScalar::Usize(us) => write!(fmt, "{}", us),
18 ConstScalar::Unknown => write!(fmt, "_"),
19 }
20 }
21}
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/display.rs b/crates/hir_ty/src/display.rs
index 1f6edf7a2..8a4296697 100644
--- a/crates/hir_ty/src/display.rs
+++ b/crates/hir_ty/src/display.rs
@@ -308,7 +308,7 @@ impl HirDisplay for Const {
308 let param_data = &generics.params.consts[id.local_id]; 308 let param_data = &generics.params.consts[id.local_id];
309 write!(f, "{}", param_data.name) 309 write!(f, "{}", param_data.name)
310 } 310 }
311 ConstValue::Concrete(_) => write!(f, "_"), 311 ConstValue::Concrete(c) => write!(f, "{}", c.interned),
312 } 312 }
313 } 313 }
314} 314}
diff --git a/crates/hir_ty/src/infer/expr.rs b/crates/hir_ty/src/infer/expr.rs
index 50497eecb..2178ffd07 100644
--- a/crates/hir_ty/src/infer/expr.rs
+++ b/crates/hir_ty/src/infer/expr.rs
@@ -3,7 +3,7 @@
3use std::iter::{repeat, repeat_with}; 3use std::iter::{repeat, repeat_with};
4use std::{mem, sync::Arc}; 4use std::{mem, sync::Arc};
5 5
6use chalk_ir::{cast::Cast, fold::Shift, Mutability, TyVariableKind}; 6use chalk_ir::{cast::Cast, fold::Shift, ConstData, Mutability, TyVariableKind};
7use hir_def::{ 7use hir_def::{
8 expr::{Array, BinaryOp, Expr, ExprId, Literal, Statement, UnaryOp}, 8 expr::{Array, BinaryOp, Expr, ExprId, Literal, Statement, UnaryOp},
9 path::{GenericArg, GenericArgs}, 9 path::{GenericArg, GenericArgs},
@@ -15,7 +15,9 @@ use stdx::always;
15use syntax::ast::RangeOp; 15use syntax::ast::RangeOp;
16 16
17use crate::{ 17use crate::{
18 autoderef, dummy_usize_const, 18 autoderef,
19 consts::ConstScalar,
20 dummy_usize_const,
19 lower::lower_to_chalk_mutability, 21 lower::lower_to_chalk_mutability,
20 mapping::from_chalk, 22 mapping::from_chalk,
21 method_resolution, op, 23 method_resolution, op,
@@ -23,7 +25,7 @@ use crate::{
23 static_lifetime, to_chalk_trait_id, 25 static_lifetime, to_chalk_trait_id,
24 traits::FnTrait, 26 traits::FnTrait,
25 utils::{generics, Generics}, 27 utils::{generics, Generics},
26 AdtId, Binders, CallableDefId, FnPointer, FnSig, FnSubst, InEnvironment, Interner, 28 AdtId, Binders, CallableDefId, ConstValue, FnPointer, FnSig, FnSubst, InEnvironment, Interner,
27 ProjectionTyExt, Rawness, Scalar, Substitution, TraitRef, Ty, TyBuilder, TyExt, TyKind, 29 ProjectionTyExt, Rawness, Scalar, Substitution, TraitRef, Ty, TyBuilder, TyExt, TyKind,
28}; 30};
29 31
@@ -717,11 +719,12 @@ impl<'a> InferenceContext<'a> {
717 _ => self.table.new_type_var(), 719 _ => self.table.new_type_var(),
718 }; 720 };
719 721
720 match array { 722 let len = match array {
721 Array::ElementList(items) => { 723 Array::ElementList(items) => {
722 for expr in items.iter() { 724 for expr in items.iter() {
723 self.infer_expr_coerce(*expr, &Expectation::has_type(elem_ty.clone())); 725 self.infer_expr_coerce(*expr, &Expectation::has_type(elem_ty.clone()));
724 } 726 }
727 Some(items.len())
725 } 728 }
726 Array::Repeat { initializer, repeat } => { 729 Array::Repeat { initializer, repeat } => {
727 self.infer_expr_coerce( 730 self.infer_expr_coerce(
@@ -734,10 +737,20 @@ impl<'a> InferenceContext<'a> {
734 TyKind::Scalar(Scalar::Uint(UintTy::Usize)).intern(&Interner), 737 TyKind::Scalar(Scalar::Uint(UintTy::Usize)).intern(&Interner),
735 ), 738 ),
736 ); 739 );
740 // FIXME: support length for Repeat array expressions
741 None
737 } 742 }
738 } 743 };
739 744
740 TyKind::Array(elem_ty, dummy_usize_const()).intern(&Interner) 745 let cd = ConstData {
746 ty: TyKind::Scalar(Scalar::Uint(UintTy::Usize)).intern(&Interner),
747 value: ConstValue::Concrete(chalk_ir::ConcreteConst {
748 interned: len
749 .map(|len| ConstScalar::Usize(len as u64))
750 .unwrap_or(ConstScalar::Unknown),
751 }),
752 };
753 TyKind::Array(elem_ty, cd.intern(&Interner)).intern(&Interner)
741 } 754 }
742 Expr::Literal(lit) => match lit { 755 Expr::Literal(lit) => match lit {
743 Literal::Bool(..) => TyKind::Scalar(Scalar::Bool).intern(&Interner), 756 Literal::Bool(..) => TyKind::Scalar(Scalar::Bool).intern(&Interner),
@@ -747,6 +760,7 @@ impl<'a> InferenceContext<'a> {
747 } 760 }
748 Literal::ByteString(..) => { 761 Literal::ByteString(..) => {
749 let byte_type = TyKind::Scalar(Scalar::Uint(UintTy::U8)).intern(&Interner); 762 let byte_type = TyKind::Scalar(Scalar::Uint(UintTy::U8)).intern(&Interner);
763
750 let array_type = 764 let array_type =
751 TyKind::Array(byte_type, dummy_usize_const()).intern(&Interner); 765 TyKind::Array(byte_type, dummy_usize_const()).intern(&Interner);
752 TyKind::Ref(Mutability::Not, static_lifetime(), array_type).intern(&Interner) 766 TyKind::Ref(Mutability::Not, static_lifetime(), array_type).intern(&Interner)
@@ -809,7 +823,7 @@ impl<'a> InferenceContext<'a> {
809 let ty = self.resolve_ty_as_possible(ty); 823 let ty = self.resolve_ty_as_possible(ty);
810 self.infer_pat(*pat, &ty, BindingMode::default()); 824 self.infer_pat(*pat, &ty, BindingMode::default());
811 } 825 }
812 Statement::Expr(expr) => { 826 Statement::Expr { expr, .. } => {
813 self.infer_expr(*expr, &Expectation::none()); 827 self.infer_expr(*expr, &Expectation::none());
814 } 828 }
815 } 829 }
diff --git a/crates/hir_ty/src/interner.rs b/crates/hir_ty/src/interner.rs
index a1656115d..4cbc9cd4f 100644
--- a/crates/hir_ty/src/interner.rs
+++ b/crates/hir_ty/src/interner.rs
@@ -1,7 +1,7 @@
1//! Implementation of the Chalk `Interner` trait, which allows customizing the 1//! Implementation of the Chalk `Interner` trait, which allows customizing the
2//! representation of the various objects Chalk deals with (types, goals etc.). 2//! representation of the various objects Chalk deals with (types, goals etc.).
3 3
4use crate::{chalk_db, tls, GenericArg}; 4use crate::{chalk_db, consts::ConstScalar, tls, GenericArg};
5use base_db::salsa::InternId; 5use base_db::salsa::InternId;
6use chalk_ir::{Goal, GoalData}; 6use chalk_ir::{Goal, GoalData};
7use hir_def::{ 7use hir_def::{
@@ -31,6 +31,7 @@ impl_internable!(
31 InternedWrapper<chalk_ir::TyData<Interner>>, 31 InternedWrapper<chalk_ir::TyData<Interner>>,
32 InternedWrapper<chalk_ir::LifetimeData<Interner>>, 32 InternedWrapper<chalk_ir::LifetimeData<Interner>>,
33 InternedWrapper<chalk_ir::ConstData<Interner>>, 33 InternedWrapper<chalk_ir::ConstData<Interner>>,
34 InternedWrapper<ConstScalar>,
34 InternedWrapper<Vec<chalk_ir::CanonicalVarKind<Interner>>>, 35 InternedWrapper<Vec<chalk_ir::CanonicalVarKind<Interner>>>,
35 InternedWrapper<Vec<chalk_ir::ProgramClause<Interner>>>, 36 InternedWrapper<Vec<chalk_ir::ProgramClause<Interner>>>,
36 InternedWrapper<Vec<chalk_ir::QuantifiedWhereClause<Interner>>>, 37 InternedWrapper<Vec<chalk_ir::QuantifiedWhereClause<Interner>>>,
@@ -41,7 +42,7 @@ impl chalk_ir::interner::Interner for Interner {
41 type InternedType = Interned<InternedWrapper<chalk_ir::TyData<Interner>>>; 42 type InternedType = Interned<InternedWrapper<chalk_ir::TyData<Interner>>>;
42 type InternedLifetime = Interned<InternedWrapper<chalk_ir::LifetimeData<Self>>>; 43 type InternedLifetime = Interned<InternedWrapper<chalk_ir::LifetimeData<Self>>>;
43 type InternedConst = Interned<InternedWrapper<chalk_ir::ConstData<Self>>>; 44 type InternedConst = Interned<InternedWrapper<chalk_ir::ConstData<Self>>>;
44 type InternedConcreteConst = (); 45 type InternedConcreteConst = ConstScalar;
45 type InternedGenericArg = chalk_ir::GenericArgData<Self>; 46 type InternedGenericArg = chalk_ir::GenericArgData<Self>;
46 type InternedGoal = Arc<GoalData<Self>>; 47 type InternedGoal = Arc<GoalData<Self>>;
47 type InternedGoals = Vec<Goal<Self>>; 48 type InternedGoals = Vec<Goal<Self>>;
@@ -245,10 +246,15 @@ impl chalk_ir::interner::Interner for Interner {
245 fn const_eq( 246 fn const_eq(
246 &self, 247 &self,
247 _ty: &Self::InternedType, 248 _ty: &Self::InternedType,
248 _c1: &Self::InternedConcreteConst, 249 c1: &Self::InternedConcreteConst,
249 _c2: &Self::InternedConcreteConst, 250 c2: &Self::InternedConcreteConst,
250 ) -> bool { 251 ) -> bool {
251 true 252 match (c1, c2) {
253 (&ConstScalar::Usize(a), &ConstScalar::Usize(b)) => a == b,
254 // we were previously assuming this to be true, I'm not whether true or false on
255 // unknown values is safer.
256 (_, _) => true,
257 }
252 } 258 }
253 259
254 fn intern_generic_arg( 260 fn intern_generic_arg(
diff --git a/crates/hir_ty/src/lib.rs b/crates/hir_ty/src/lib.rs
index 0505fa4ae..d23eff513 100644
--- a/crates/hir_ty/src/lib.rs
+++ b/crates/hir_ty/src/lib.rs
@@ -12,6 +12,7 @@ mod chalk_db;
12mod chalk_ext; 12mod chalk_ext;
13mod infer; 13mod infer;
14mod interner; 14mod interner;
15mod consts;
15mod lower; 16mod lower;
16mod mapping; 17mod mapping;
17mod op; 18mod op;
@@ -39,7 +40,7 @@ use chalk_ir::{
39}; 40};
40use hir_def::{expr::ExprId, type_ref::Rawness, TypeParamId}; 41use hir_def::{expr::ExprId, type_ref::Rawness, TypeParamId};
41 42
42use crate::{db::HirDatabase, display::HirDisplay, utils::generics}; 43use crate::{consts::ConstScalar, db::HirDatabase, display::HirDisplay, utils::generics};
43 44
44pub use autoderef::autoderef; 45pub use autoderef::autoderef;
45pub use builder::TyBuilder; 46pub use builder::TyBuilder;
@@ -250,7 +251,9 @@ pub fn dummy_usize_const() -> Const {
250 let usize_ty = chalk_ir::TyKind::Scalar(Scalar::Uint(UintTy::Usize)).intern(&Interner); 251 let usize_ty = chalk_ir::TyKind::Scalar(Scalar::Uint(UintTy::Usize)).intern(&Interner);
251 chalk_ir::ConstData { 252 chalk_ir::ConstData {
252 ty: usize_ty, 253 ty: usize_ty,
253 value: chalk_ir::ConstValue::Concrete(chalk_ir::ConcreteConst { interned: () }), 254 value: chalk_ir::ConstValue::Concrete(chalk_ir::ConcreteConst {
255 interned: ConstScalar::Unknown,
256 }),
254 } 257 }
255 .intern(&Interner) 258 .intern(&Interner)
256} 259}
diff --git a/crates/hir_ty/src/lower.rs b/crates/hir_ty/src/lower.rs
index c99dd8d0a..9751b45e4 100644
--- a/crates/hir_ty/src/lower.rs
+++ b/crates/hir_ty/src/lower.rs
@@ -174,6 +174,8 @@ impl<'a> TyLoweringContext<'a> {
174 } 174 }
175 TypeRef::Array(inner) => { 175 TypeRef::Array(inner) => {
176 let inner_ty = self.lower_ty(inner); 176 let inner_ty = self.lower_ty(inner);
177 // FIXME: we don't have length info here because we don't store an expression for
178 // the length
177 TyKind::Array(inner_ty, dummy_usize_const()).intern(&Interner) 179 TyKind::Array(inner_ty, dummy_usize_const()).intern(&Interner)
178 } 180 }
179 TypeRef::Slice(inner) => { 181 TypeRef::Slice(inner) => {
diff --git a/crates/hir_ty/src/tests/coercion.rs b/crates/hir_ty/src/tests/coercion.rs
index 63d9d4e0b..aad3d610e 100644
--- a/crates/hir_ty/src/tests/coercion.rs
+++ b/crates/hir_ty/src/tests/coercion.rs
@@ -55,7 +55,7 @@ fn coerce_places() {
55 impl<'a, 'b: 'a, T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<&'a U> for &'b T {} 55 impl<'a, 'b: 'a, T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<&'a U> for &'b T {}
56 impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<*mut U> for *mut T {} 56 impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<*mut U> for *mut T {}
57 "#, 57 "#,
58 expect![[r" 58 expect![[r#"
59 30..31 '_': &[T] 59 30..31 '_': &[T]
60 44..55 '{ loop {} }': T 60 44..55 '{ loop {} }': T
61 46..53 'loop {}': ! 61 46..53 'loop {}': !
@@ -72,8 +72,8 @@ fn coerce_places() {
72 165..170 'gen()': *mut [U; _] 72 165..170 'gen()': *mut [U; _]
73 185..419 '{ ...rr); }': () 73 185..419 '{ ...rr); }': ()
74 195..198 'arr': &[u8; _] 74 195..198 'arr': &[u8; _]
75 211..215 '&[1]': &[u8; _] 75 211..215 '&[1]': &[u8; 1]
76 212..215 '[1]': [u8; _] 76 212..215 '[1]': [u8; 1]
77 213..214 '1': u8 77 213..214 '1': u8
78 226..227 'a': &[u8] 78 226..227 'a': &[u8]
79 236..239 'arr': &[u8; _] 79 236..239 'arr': &[u8; _]
@@ -90,7 +90,7 @@ fn coerce_places() {
90 302..314 'S { a: arr }': S<&[u8]> 90 302..314 'S { a: arr }': S<&[u8]>
91 309..312 'arr': &[u8; _] 91 309..312 'arr': &[u8; _]
92 325..326 'e': [&[u8]; _] 92 325..326 'e': [&[u8]; _]
93 340..345 '[arr]': [&[u8]; _] 93 340..345 '[arr]': [&[u8]; 1]
94 341..344 'arr': &[u8; _] 94 341..344 'arr': &[u8; _]
95 355..356 'f': [&[u8]; _] 95 355..356 'f': [&[u8]; _]
96 370..378 '[arr; 2]': [&[u8]; _] 96 370..378 '[arr; 2]': [&[u8]; _]
@@ -100,7 +100,7 @@ fn coerce_places() {
100 406..416 '(arr, arr)': (&[u8], &[u8]) 100 406..416 '(arr, arr)': (&[u8], &[u8])
101 407..410 'arr': &[u8; _] 101 407..410 'arr': &[u8; _]
102 412..415 'arr': &[u8; _] 102 412..415 'arr': &[u8; _]
103 "]], 103 "#]],
104 ); 104 );
105} 105}
106 106
@@ -113,17 +113,17 @@ fn infer_let_stmt_coerce() {
113 let x: *const [isize] = &[1]; 113 let x: *const [isize] = &[1];
114 } 114 }
115 ", 115 ",
116 expect![[r" 116 expect![[r#"
117 10..75 '{ ...[1]; }': () 117 10..75 '{ ...[1]; }': ()
118 20..21 'x': &[isize] 118 20..21 'x': &[isize]
119 34..38 '&[1]': &[isize; _] 119 34..38 '&[1]': &[isize; 1]
120 35..38 '[1]': [isize; _] 120 35..38 '[1]': [isize; 1]
121 36..37 '1': isize 121 36..37 '1': isize
122 48..49 'x': *const [isize] 122 48..49 'x': *const [isize]
123 68..72 '&[1]': &[isize; _] 123 68..72 '&[1]': &[isize; 1]
124 69..72 '[1]': [isize; _] 124 69..72 '[1]': [isize; 1]
125 70..71 '1': isize 125 70..71 '1': isize
126 "]], 126 "#]],
127 ); 127 );
128} 128}
129 129
@@ -208,7 +208,7 @@ fn infer_if_coerce() {
208 #[lang = "unsize"] 208 #[lang = "unsize"]
209 pub trait Unsize<T: ?Sized> {} 209 pub trait Unsize<T: ?Sized> {}
210 "#, 210 "#,
211 expect![[r" 211 expect![[r#"
212 10..11 'x': &[T] 212 10..11 'x': &[T]
213 27..38 '{ loop {} }': &[T] 213 27..38 '{ loop {} }': &[T]
214 29..36 'loop {}': ! 214 29..36 'loop {}': !
@@ -220,14 +220,14 @@ fn infer_if_coerce() {
220 71..96 '{ ... }': &[i32] 220 71..96 '{ ... }': &[i32]
221 81..84 'foo': fn foo<i32>(&[i32]) -> &[i32] 221 81..84 'foo': fn foo<i32>(&[i32]) -> &[i32]
222 81..90 'foo(&[1])': &[i32] 222 81..90 'foo(&[1])': &[i32]
223 85..89 '&[1]': &[i32; _] 223 85..89 '&[1]': &[i32; 1]
224 86..89 '[1]': [i32; _] 224 86..89 '[1]': [i32; 1]
225 87..88 '1': i32 225 87..88 '1': i32
226 102..122 '{ ... }': &[i32; _] 226 102..122 '{ ... }': &[i32; 1]
227 112..116 '&[1]': &[i32; _] 227 112..116 '&[1]': &[i32; 1]
228 113..116 '[1]': [i32; _] 228 113..116 '[1]': [i32; 1]
229 114..115 '1': i32 229 114..115 '1': i32
230 "]], 230 "#]],
231 ); 231 );
232} 232}
233 233
@@ -254,7 +254,7 @@ fn infer_if_else_coerce() {
254 impl<'a, 'b: 'a, T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<&'a U> for &'b T {} 254 impl<'a, 'b: 'a, T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<&'a U> for &'b T {}
255 impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<*mut U> for *mut T {} 255 impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<*mut U> for *mut T {}
256 "#, 256 "#,
257 expect![[r" 257 expect![[r#"
258 10..11 'x': &[T] 258 10..11 'x': &[T]
259 27..38 '{ loop {} }': &[T] 259 27..38 '{ loop {} }': &[T]
260 29..36 'loop {}': ! 260 29..36 'loop {}': !
@@ -263,17 +263,17 @@ fn infer_if_else_coerce() {
263 59..60 'x': &[i32] 263 59..60 'x': &[i32]
264 63..122 'if tru... }': &[i32] 264 63..122 'if tru... }': &[i32]
265 66..70 'true': bool 265 66..70 'true': bool
266 71..91 '{ ... }': &[i32; _] 266 71..91 '{ ... }': &[i32; 1]
267 81..85 '&[1]': &[i32; _] 267 81..85 '&[1]': &[i32; 1]
268 82..85 '[1]': [i32; _] 268 82..85 '[1]': [i32; 1]
269 83..84 '1': i32 269 83..84 '1': i32
270 97..122 '{ ... }': &[i32] 270 97..122 '{ ... }': &[i32]
271 107..110 'foo': fn foo<i32>(&[i32]) -> &[i32] 271 107..110 'foo': fn foo<i32>(&[i32]) -> &[i32]
272 107..116 'foo(&[1])': &[i32] 272 107..116 'foo(&[1])': &[i32]
273 111..115 '&[1]': &[i32; _] 273 111..115 '&[1]': &[i32; 1]
274 112..115 '[1]': [i32; _] 274 112..115 '[1]': [i32; 1]
275 113..114 '1': i32 275 113..114 '1': i32
276 "]], 276 "#]],
277 ) 277 )
278} 278}
279 279
@@ -295,7 +295,7 @@ fn infer_match_first_coerce() {
295 #[lang = "unsize"] 295 #[lang = "unsize"]
296 pub trait Unsize<T: ?Sized> {} 296 pub trait Unsize<T: ?Sized> {}
297 "#, 297 "#,
298 expect![[r" 298 expect![[r#"
299 10..11 'x': &[T] 299 10..11 'x': &[T]
300 27..38 '{ loop {} }': &[T] 300 27..38 '{ loop {} }': &[T]
301 29..36 'loop {}': ! 301 29..36 'loop {}': !
@@ -309,19 +309,19 @@ fn infer_match_first_coerce() {
309 87..88 '2': i32 309 87..88 '2': i32
310 92..95 'foo': fn foo<i32>(&[i32]) -> &[i32] 310 92..95 'foo': fn foo<i32>(&[i32]) -> &[i32]
311 92..101 'foo(&[2])': &[i32] 311 92..101 'foo(&[2])': &[i32]
312 96..100 '&[2]': &[i32; _] 312 96..100 '&[2]': &[i32; 1]
313 97..100 '[2]': [i32; _] 313 97..100 '[2]': [i32; 1]
314 98..99 '2': i32 314 98..99 '2': i32
315 111..112 '1': i32 315 111..112 '1': i32
316 111..112 '1': i32 316 111..112 '1': i32
317 116..120 '&[1]': &[i32; _] 317 116..120 '&[1]': &[i32; 1]
318 117..120 '[1]': [i32; _] 318 117..120 '[1]': [i32; 1]
319 118..119 '1': i32 319 118..119 '1': i32
320 130..131 '_': i32 320 130..131 '_': i32
321 135..139 '&[3]': &[i32; _] 321 135..139 '&[3]': &[i32; 1]
322 136..139 '[3]': [i32; _] 322 136..139 '[3]': [i32; 1]
323 137..138 '3': i32 323 137..138 '3': i32
324 "]], 324 "#]],
325 ); 325 );
326} 326}
327 327
@@ -348,7 +348,7 @@ fn infer_match_second_coerce() {
348 impl<'a, 'b: 'a, T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<&'a U> for &'b T {} 348 impl<'a, 'b: 'a, T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<&'a U> for &'b T {}
349 impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<*mut U> for *mut T {} 349 impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<*mut U> for *mut T {}
350 "#, 350 "#,
351 expect![[r" 351 expect![[r#"
352 10..11 'x': &[T] 352 10..11 'x': &[T]
353 27..38 '{ loop {} }': &[T] 353 27..38 '{ loop {} }': &[T]
354 29..36 'loop {}': ! 354 29..36 'loop {}': !
@@ -360,21 +360,21 @@ fn infer_match_second_coerce() {
360 75..76 'i': i32 360 75..76 'i': i32
361 87..88 '1': i32 361 87..88 '1': i32
362 87..88 '1': i32 362 87..88 '1': i32
363 92..96 '&[1]': &[i32; _] 363 92..96 '&[1]': &[i32; 1]
364 93..96 '[1]': [i32; _] 364 93..96 '[1]': [i32; 1]
365 94..95 '1': i32 365 94..95 '1': i32
366 106..107 '2': i32 366 106..107 '2': i32
367 106..107 '2': i32 367 106..107 '2': i32
368 111..114 'foo': fn foo<i32>(&[i32]) -> &[i32] 368 111..114 'foo': fn foo<i32>(&[i32]) -> &[i32]
369 111..120 'foo(&[2])': &[i32] 369 111..120 'foo(&[2])': &[i32]
370 115..119 '&[2]': &[i32; _] 370 115..119 '&[2]': &[i32; 1]
371 116..119 '[2]': [i32; _] 371 116..119 '[2]': [i32; 1]
372 117..118 '2': i32 372 117..118 '2': i32
373 130..131 '_': i32 373 130..131 '_': i32
374 135..139 '&[3]': &[i32; _] 374 135..139 '&[3]': &[i32; 1]
375 136..139 '[3]': [i32; _] 375 136..139 '[3]': [i32; 1]
376 137..138 '3': i32 376 137..138 '3': i32
377 "]], 377 "#]],
378 ); 378 );
379} 379}
380 380
@@ -685,15 +685,15 @@ fn coerce_unsize_array() {
685 let f: &[usize] = &[1, 2, 3]; 685 let f: &[usize] = &[1, 2, 3];
686 } 686 }
687 "#, 687 "#,
688 expect![[r" 688 expect![[r#"
689 161..198 '{ ... 3]; }': () 689 161..198 '{ ... 3]; }': ()
690 171..172 'f': &[usize] 690 171..172 'f': &[usize]
691 185..195 '&[1, 2, 3]': &[usize; _] 691 185..195 '&[1, 2, 3]': &[usize; 3]
692 186..195 '[1, 2, 3]': [usize; _] 692 186..195 '[1, 2, 3]': [usize; 3]
693 187..188 '1': usize 693 187..188 '1': usize
694 190..191 '2': usize 694 190..191 '2': usize
695 193..194 '3': usize 695 193..194 '3': usize
696 "]], 696 "#]],
697 ); 697 );
698} 698}
699 699
diff --git a/crates/hir_ty/src/tests/patterns.rs b/crates/hir_ty/src/tests/patterns.rs
index f514b3efe..33305f208 100644
--- a/crates/hir_ty/src/tests/patterns.rs
+++ b/crates/hir_ty/src/tests/patterns.rs
@@ -243,8 +243,8 @@ fn infer_pattern_match_slice() {
243 expect![[r#" 243 expect![[r#"
244 10..209 '{ ... } }': () 244 10..209 '{ ... } }': ()
245 20..25 'slice': &[f64] 245 20..25 'slice': &[f64]
246 36..42 '&[0.0]': &[f64; _] 246 36..42 '&[0.0]': &[f64; 1]
247 37..42 '[0.0]': [f64; _] 247 37..42 '[0.0]': [f64; 1]
248 38..41 '0.0': f64 248 38..41 '0.0': f64
249 48..207 'match ... }': () 249 48..207 'match ... }': ()
250 54..59 'slice': &[f64] 250 54..59 'slice': &[f64]
@@ -346,7 +346,7 @@ fn infer_pattern_match_arr() {
346 expect![[r#" 346 expect![[r#"
347 10..179 '{ ... } }': () 347 10..179 '{ ... } }': ()
348 20..23 'arr': [f64; _] 348 20..23 'arr': [f64; _]
349 36..46 '[0.0, 1.0]': [f64; _] 349 36..46 '[0.0, 1.0]': [f64; 2]
350 37..40 '0.0': f64 350 37..40 '0.0': f64
351 42..45 '1.0': f64 351 42..45 '1.0': f64
352 52..177 'match ... }': () 352 52..177 'match ... }': ()
diff --git a/crates/hir_ty/src/tests/regression.rs b/crates/hir_ty/src/tests/regression.rs
index d14f5c9bb..431861712 100644
--- a/crates/hir_ty/src/tests/regression.rs
+++ b/crates/hir_ty/src/tests/regression.rs
@@ -99,7 +99,7 @@ fn recursive_vars() {
99 10..47 '{ ...&y]; }': () 99 10..47 '{ ...&y]; }': ()
100 20..21 'y': &{unknown} 100 20..21 'y': &{unknown}
101 24..31 'unknown': &{unknown} 101 24..31 'unknown': &{unknown}
102 37..44 '[y, &y]': [&&{unknown}; _] 102 37..44 '[y, &y]': [&&{unknown}; 2]
103 38..39 'y': &{unknown} 103 38..39 'y': &{unknown}
104 41..43 '&y': &&{unknown} 104 41..43 '&y': &&{unknown}
105 42..43 'y': &{unknown} 105 42..43 'y': &{unknown}
@@ -123,7 +123,7 @@ fn recursive_vars_2() {
123 24..31 'unknown': &&{unknown} 123 24..31 'unknown': &&{unknown}
124 41..42 'y': &&{unknown} 124 41..42 'y': &&{unknown}
125 45..52 'unknown': &&{unknown} 125 45..52 'unknown': &&{unknown}
126 58..76 '[(x, y..., &x)]': [(&&&{unknown}, &&&{unknown}); _] 126 58..76 '[(x, y..., &x)]': [(&&&{unknown}, &&&{unknown}); 2]
127 59..65 '(x, y)': (&&&{unknown}, &&&{unknown}) 127 59..65 '(x, y)': (&&&{unknown}, &&&{unknown})
128 60..61 'x': &&{unknown} 128 60..61 'x': &&{unknown}
129 63..64 'y': &&{unknown} 129 63..64 'y': &&{unknown}
@@ -175,8 +175,8 @@ fn infer_std_crash_2() {
175 "#, 175 "#,
176 expect![[r#" 176 expect![[r#"
177 22..52 '{ ...n']; }': () 177 22..52 '{ ...n']; }': ()
178 28..49 '&[0, b...b'\n']': &[u8; _] 178 28..49 '&[0, b...b'\n']': &[u8; 4]
179 29..49 '[0, b'...b'\n']': [u8; _] 179 29..49 '[0, b'...b'\n']': [u8; 4]
180 30..31 '0': u8 180 30..31 '0': u8
181 33..38 'b'\n'': u8 181 33..38 'b'\n'': u8
182 40..41 '1': u8 182 40..41 '1': u8
@@ -336,8 +336,8 @@ fn infer_array_macro_call() {
336 expect![[r#" 336 expect![[r#"
337 !0..4 '0u32': u32 337 !0..4 '0u32': u32
338 44..69 '{ ...()]; }': () 338 44..69 '{ ...()]; }': ()
339 54..55 'a': [u32; _] 339 54..55 'a': [u32; 1]
340 58..66 '[bar!()]': [u32; _] 340 58..66 '[bar!()]': [u32; 1]
341 "#]], 341 "#]],
342 ); 342 );
343} 343}
@@ -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/hir_ty/src/tests/simple.rs b/crates/hir_ty/src/tests/simple.rs
index 0eefd70f2..8b09f2e4a 100644
--- a/crates/hir_ty/src/tests/simple.rs
+++ b/crates/hir_ty/src/tests/simple.rs
@@ -11,7 +11,7 @@ fn test() {
11 let x = box 1; 11 let x = box 1;
12 let t = (x, box x, box &1, box [1]); 12 let t = (x, box x, box &1, box [1]);
13 t; 13 t;
14} //^ (Box<i32>, Box<Box<i32>>, Box<&i32>, Box<[i32; _]>) 14} //^ (Box<i32>, Box<Box<i32>>, Box<&i32>, Box<[i32; 1]>)
15 15
16//- /std.rs crate:std 16//- /std.rs crate:std
17#[prelude_import] use prelude::*; 17#[prelude_import] use prelude::*;
@@ -36,7 +36,7 @@ fn test() {
36 let x = box 1; 36 let x = box 1;
37 let t = (x, box x, box &1, box [1]); 37 let t = (x, box x, box &1, box [1]);
38 t; 38 t;
39} //^ (Box<i32, {unknown}>, Box<Box<i32, {unknown}>, {unknown}>, Box<&i32, {unknown}>, Box<[i32; _], {unknown}>) 39} //^ (Box<i32, {unknown}>, Box<Box<i32, {unknown}>, {unknown}>, Box<&i32, {unknown}>, Box<[i32; 1], {unknown}>)
40 40
41//- /std.rs crate:std 41//- /std.rs crate:std
42#[prelude_import] use prelude::*; 42#[prelude_import] use prelude::*;
@@ -1266,55 +1266,55 @@ fn infer_array() {
1266 8..9 'x': &str 1266 8..9 'x': &str
1267 17..18 'y': isize 1267 17..18 'y': isize
1268 27..292 '{ ... []; }': () 1268 27..292 '{ ... []; }': ()
1269 37..38 'a': [&str; _] 1269 37..38 'a': [&str; 1]
1270 41..44 '[x]': [&str; _] 1270 41..44 '[x]': [&str; 1]
1271 42..43 'x': &str 1271 42..43 'x': &str
1272 54..55 'b': [[&str; _]; _] 1272 54..55 'b': [[&str; 1]; 2]
1273 58..64 '[a, a]': [[&str; _]; _] 1273 58..64 '[a, a]': [[&str; 1]; 2]
1274 59..60 'a': [&str; _] 1274 59..60 'a': [&str; 1]
1275 62..63 'a': [&str; _] 1275 62..63 'a': [&str; 1]
1276 74..75 'c': [[[&str; _]; _]; _] 1276 74..75 'c': [[[&str; 1]; 2]; 2]
1277 78..84 '[b, b]': [[[&str; _]; _]; _] 1277 78..84 '[b, b]': [[[&str; 1]; 2]; 2]
1278 79..80 'b': [[&str; _]; _] 1278 79..80 'b': [[&str; 1]; 2]
1279 82..83 'b': [[&str; _]; _] 1279 82..83 'b': [[&str; 1]; 2]
1280 95..96 'd': [isize; _] 1280 95..96 'd': [isize; 4]
1281 99..111 '[y, 1, 2, 3]': [isize; _] 1281 99..111 '[y, 1, 2, 3]': [isize; 4]
1282 100..101 'y': isize 1282 100..101 'y': isize
1283 103..104 '1': isize 1283 103..104 '1': isize
1284 106..107 '2': isize 1284 106..107 '2': isize
1285 109..110 '3': isize 1285 109..110 '3': isize
1286 121..122 'd': [isize; _] 1286 121..122 'd': [isize; 4]
1287 125..137 '[1, y, 2, 3]': [isize; _] 1287 125..137 '[1, y, 2, 3]': [isize; 4]
1288 126..127 '1': isize 1288 126..127 '1': isize
1289 129..130 'y': isize 1289 129..130 'y': isize
1290 132..133 '2': isize 1290 132..133 '2': isize
1291 135..136 '3': isize 1291 135..136 '3': isize
1292 147..148 'e': [isize; _] 1292 147..148 'e': [isize; 1]
1293 151..154 '[y]': [isize; _] 1293 151..154 '[y]': [isize; 1]
1294 152..153 'y': isize 1294 152..153 'y': isize
1295 164..165 'f': [[isize; _]; _] 1295 164..165 'f': [[isize; 4]; 2]
1296 168..174 '[d, d]': [[isize; _]; _] 1296 168..174 '[d, d]': [[isize; 4]; 2]
1297 169..170 'd': [isize; _] 1297 169..170 'd': [isize; 4]
1298 172..173 'd': [isize; _] 1298 172..173 'd': [isize; 4]
1299 184..185 'g': [[isize; _]; _] 1299 184..185 'g': [[isize; 1]; 2]
1300 188..194 '[e, e]': [[isize; _]; _] 1300 188..194 '[e, e]': [[isize; 1]; 2]
1301 189..190 'e': [isize; _] 1301 189..190 'e': [isize; 1]
1302 192..193 'e': [isize; _] 1302 192..193 'e': [isize; 1]
1303 205..206 'h': [i32; _] 1303 205..206 'h': [i32; 2]
1304 209..215 '[1, 2]': [i32; _] 1304 209..215 '[1, 2]': [i32; 2]
1305 210..211 '1': i32 1305 210..211 '1': i32
1306 213..214 '2': i32 1306 213..214 '2': i32
1307 225..226 'i': [&str; _] 1307 225..226 'i': [&str; 2]
1308 229..239 '["a", "b"]': [&str; _] 1308 229..239 '["a", "b"]': [&str; 2]
1309 230..233 '"a"': &str 1309 230..233 '"a"': &str
1310 235..238 '"b"': &str 1310 235..238 '"b"': &str
1311 250..251 'b': [[&str; _]; _] 1311 250..251 'b': [[&str; 1]; 2]
1312 254..264 '[a, ["b"]]': [[&str; _]; _] 1312 254..264 '[a, ["b"]]': [[&str; 1]; 2]
1313 255..256 'a': [&str; _] 1313 255..256 'a': [&str; 1]
1314 258..263 '["b"]': [&str; _] 1314 258..263 '["b"]': [&str; 1]
1315 259..262 '"b"': &str 1315 259..262 '"b"': &str
1316 274..275 'x': [u8; _] 1316 274..275 'x': [u8; _]
1317 287..289 '[]': [u8; _] 1317 287..289 '[]': [u8; 0]
1318 "#]], 1318 "#]],
1319 ); 1319 );
1320} 1320}
@@ -2429,20 +2429,20 @@ fn infer_operator_overload() {
2429 394..395 '1': i32 2429 394..395 '1': i32
2430 406..408 'V2': V2([f32; _]) -> V2 2430 406..408 'V2': V2([f32; _]) -> V2
2431 406..416 'V2([x, y])': V2 2431 406..416 'V2([x, y])': V2
2432 409..415 '[x, y]': [f32; _] 2432 409..415 '[x, y]': [f32; 2]
2433 410..411 'x': f32 2433 410..411 'x': f32
2434 413..414 'y': f32 2434 413..414 'y': f32
2435 436..519 '{ ... vb; }': () 2435 436..519 '{ ... vb; }': ()
2436 446..448 'va': V2 2436 446..448 'va': V2
2437 451..453 'V2': V2([f32; _]) -> V2 2437 451..453 'V2': V2([f32; _]) -> V2
2438 451..465 'V2([0.0, 1.0])': V2 2438 451..465 'V2([0.0, 1.0])': V2
2439 454..464 '[0.0, 1.0]': [f32; _] 2439 454..464 '[0.0, 1.0]': [f32; 2]
2440 455..458 '0.0': f32 2440 455..458 '0.0': f32
2441 460..463 '1.0': f32 2441 460..463 '1.0': f32
2442 475..477 'vb': V2 2442 475..477 'vb': V2
2443 480..482 'V2': V2([f32; _]) -> V2 2443 480..482 'V2': V2([f32; _]) -> V2
2444 480..494 'V2([0.0, 1.0])': V2 2444 480..494 'V2([0.0, 1.0])': V2
2445 483..493 '[0.0, 1.0]': [f32; _] 2445 483..493 '[0.0, 1.0]': [f32; 2]
2446 484..487 '0.0': f32 2446 484..487 '0.0': f32
2447 489..492 '1.0': f32 2447 489..492 '1.0': f32
2448 505..506 'r': V2 2448 505..506 'r': V2
@@ -2593,8 +2593,8 @@ fn test() {
2593 658..661 'vec': Vec<i32, Global> 2593 658..661 'vec': Vec<i32, Global>
2594 664..679 '<[_]>::into_vec': fn into_vec<i32, Global>(Box<[i32], Global>) -> Vec<i32, Global> 2594 664..679 '<[_]>::into_vec': fn into_vec<i32, Global>(Box<[i32], Global>) -> Vec<i32, Global>
2595 664..691 '<[_]>:...1i32])': Vec<i32, Global> 2595 664..691 '<[_]>:...1i32])': Vec<i32, Global>
2596 680..690 'box [1i32]': Box<[i32; _], Global> 2596 680..690 'box [1i32]': Box<[i32; 1], Global>
2597 684..690 '[1i32]': [i32; _] 2597 684..690 '[1i32]': [i32; 1]
2598 685..689 '1i32': i32 2598 685..689 '1i32': i32
2599 "#]], 2599 "#]],
2600 ) 2600 )
diff --git a/crates/hir_ty/src/tests/traits.rs b/crates/hir_ty/src/tests/traits.rs
index ffc7c8ef4..47a1455fd 100644
--- a/crates/hir_ty/src/tests/traits.rs
+++ b/crates/hir_ty/src/tests/traits.rs
@@ -531,7 +531,7 @@ fn indexing_arrays() {
531 expect![[r#" 531 expect![[r#"
532 10..26 '{ &mut...[2]; }': () 532 10..26 '{ &mut...[2]; }': ()
533 12..23 '&mut [9][2]': &mut {unknown} 533 12..23 '&mut [9][2]': &mut {unknown}
534 17..20 '[9]': [i32; _] 534 17..20 '[9]': [i32; 1]
535 17..23 '[9][2]': {unknown} 535 17..23 '[9][2]': {unknown}
536 18..19 '9': i32 536 18..19 '9': i32
537 21..22 '2': i32 537 21..22 '2': i32
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/inlay_hints.rs b/crates/ide/src/inlay_hints.rs
index e0bf660c4..960d169f4 100644
--- a/crates/ide/src/inlay_hints.rs
+++ b/crates/ide/src/inlay_hints.rs
@@ -1126,7 +1126,7 @@ fn main() {
1126 r#" 1126 r#"
1127fn main() { 1127fn main() {
1128 let data = &[1i32, 2, 3]; 1128 let data = &[1i32, 2, 3];
1129 //^^^^ &[i32; _] 1129 //^^^^ &[i32; 3]
1130 for i 1130 for i
1131}"#, 1131}"#,
1132 ); 1132 );
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/ide_assists/src/handlers/replace_unwrap_with_match.rs b/crates/ide_assists/src/handlers/replace_unwrap_with_match.rs
index 0fec961b4..a3bfa221c 100644
--- a/crates/ide_assists/src/handlers/replace_unwrap_with_match.rs
+++ b/crates/ide_assists/src/handlers/replace_unwrap_with_match.rs
@@ -17,7 +17,7 @@ use ide_db::ty_filter::TryEnum;
17 17
18// Assist: replace_unwrap_with_match 18// Assist: replace_unwrap_with_match
19// 19//
20// Replaces `unwrap` a `match` expression. Works for Result and Option. 20// Replaces `unwrap` with a `match` expression. Works for Result and Option.
21// 21//
22// ``` 22// ```
23// enum Result<T, E> { Ok(T), Err(E) } 23// enum Result<T, E> { Ok(T), Err(E) }
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)