diff options
Diffstat (limited to 'crates')
135 files changed, 4620 insertions, 3178 deletions
diff --git a/crates/cfg/src/lib.rs b/crates/cfg/src/lib.rs index d88ecf8b0..59fd38880 100644 --- a/crates/cfg/src/lib.rs +++ b/crates/cfg/src/lib.rs | |||
@@ -13,7 +13,7 @@ use tt::SmolStr; | |||
13 | pub use cfg_expr::{CfgAtom, CfgExpr}; | 13 | pub use cfg_expr::{CfgAtom, CfgExpr}; |
14 | pub use dnf::DnfExpr; | 14 | pub use dnf::DnfExpr; |
15 | 15 | ||
16 | /// Configuration options used for conditional compilition on items with `cfg` attributes. | 16 | /// Configuration options used for conditional compilation on items with `cfg` attributes. |
17 | /// We have two kind of options in different namespaces: atomic options like `unix`, and | 17 | /// We have two kind of options in different namespaces: atomic options like `unix`, and |
18 | /// key-value options like `target_arch="x86"`. | 18 | /// key-value options like `target_arch="x86"`. |
19 | /// | 19 | /// |
diff --git a/crates/flycheck/src/lib.rs b/crates/flycheck/src/lib.rs index 1682d8bde..93cf6a3d6 100644 --- a/crates/flycheck/src/lib.rs +++ b/crates/flycheck/src/lib.rs | |||
@@ -215,6 +215,7 @@ impl FlycheckActor { | |||
215 | } => { | 215 | } => { |
216 | let mut cmd = Command::new(toolchain::cargo()); | 216 | let mut cmd = Command::new(toolchain::cargo()); |
217 | cmd.arg(command); | 217 | cmd.arg(command); |
218 | cmd.current_dir(&self.workspace_root); | ||
218 | cmd.args(&["--workspace", "--message-format=json", "--manifest-path"]) | 219 | cmd.args(&["--workspace", "--message-format=json", "--manifest-path"]) |
219 | .arg(self.workspace_root.join("Cargo.toml")); | 220 | .arg(self.workspace_root.join("Cargo.toml")); |
220 | 221 | ||
diff --git a/crates/hir/Cargo.toml b/crates/hir/Cargo.toml index 9e329656f..560b15238 100644 --- a/crates/hir/Cargo.toml +++ b/crates/hir/Cargo.toml | |||
@@ -25,3 +25,4 @@ hir_expand = { path = "../hir_expand", version = "0.0.0" } | |||
25 | hir_def = { path = "../hir_def", version = "0.0.0" } | 25 | hir_def = { path = "../hir_def", version = "0.0.0" } |
26 | hir_ty = { path = "../hir_ty", version = "0.0.0" } | 26 | hir_ty = { path = "../hir_ty", version = "0.0.0" } |
27 | tt = { path = "../tt", version = "0.0.0" } | 27 | tt = { path = "../tt", version = "0.0.0" } |
28 | cfg = { path = "../cfg", version = "0.0.0" } | ||
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index f876339de..d443b124c 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs | |||
@@ -52,7 +52,9 @@ use hir_def::{ | |||
52 | }; | 52 | }; |
53 | use hir_expand::{diagnostics::DiagnosticSink, name::name, MacroDefKind}; | 53 | use hir_expand::{diagnostics::DiagnosticSink, name::name, MacroDefKind}; |
54 | use hir_ty::{ | 54 | use hir_ty::{ |
55 | autoderef, could_unify, | 55 | autoderef, |
56 | consteval::ConstExt, | ||
57 | could_unify, | ||
56 | method_resolution::{self, def_crates, TyFingerprint}, | 58 | method_resolution::{self, def_crates, TyFingerprint}, |
57 | primitive::UintTy, | 59 | primitive::UintTy, |
58 | subst_prefix, | 60 | subst_prefix, |
@@ -89,6 +91,7 @@ pub use crate::{ | |||
89 | // Generally, a refactoring which *removes* a name from this list is a good | 91 | // Generally, a refactoring which *removes* a name from this list is a good |
90 | // idea! | 92 | // idea! |
91 | pub use { | 93 | pub use { |
94 | cfg::{CfgAtom, CfgExpr, CfgOptions}, | ||
92 | hir_def::{ | 95 | hir_def::{ |
93 | adt::StructKind, | 96 | adt::StructKind, |
94 | attr::{Attr, Attrs, AttrsWithOwner, Documentation}, | 97 | attr::{Attr, Attrs, AttrsWithOwner, Documentation}, |
@@ -215,6 +218,10 @@ impl Crate { | |||
215 | 218 | ||
216 | doc_url.map(|s| s.trim_matches('"').trim_end_matches('/').to_owned() + "/") | 219 | doc_url.map(|s| s.trim_matches('"').trim_end_matches('/').to_owned() + "/") |
217 | } | 220 | } |
221 | |||
222 | pub fn cfg(&self, db: &dyn HirDatabase) -> CfgOptions { | ||
223 | db.crate_graph()[self.id].cfg_options.clone() | ||
224 | } | ||
218 | } | 225 | } |
219 | 226 | ||
220 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | 227 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
@@ -868,6 +875,10 @@ impl Function { | |||
868 | db.function_data(self.id).is_unsafe() | 875 | db.function_data(self.id).is_unsafe() |
869 | } | 876 | } |
870 | 877 | ||
878 | pub fn is_async(self, db: &dyn HirDatabase) -> bool { | ||
879 | db.function_data(self.id).is_async() | ||
880 | } | ||
881 | |||
871 | pub fn diagnostics(self, db: &dyn HirDatabase, sink: &mut DiagnosticSink) { | 882 | pub fn diagnostics(self, db: &dyn HirDatabase, sink: &mut DiagnosticSink) { |
872 | let krate = self.module(db).id.krate(); | 883 | let krate = self.module(db).id.krate(); |
873 | hir_def::diagnostics::validate_body(db.upcast(), self.id.into(), sink); | 884 | hir_def::diagnostics::validate_body(db.upcast(), self.id.into(), sink); |
@@ -1905,6 +1916,7 @@ impl Type { | |||
1905 | substs.iter(&Interner).filter_map(|a| a.ty(&Interner)).any(go) | 1916 | substs.iter(&Interner).filter_map(|a| a.ty(&Interner)).any(go) |
1906 | } | 1917 | } |
1907 | 1918 | ||
1919 | TyKind::Array(_ty, len) if len.is_unknown() => true, | ||
1908 | TyKind::Array(ty, _) | 1920 | TyKind::Array(ty, _) |
1909 | | TyKind::Slice(ty) | 1921 | | TyKind::Slice(ty) |
1910 | | TyKind::Raw(_, ty) | 1922 | | TyKind::Raw(_, ty) |
@@ -2066,6 +2078,10 @@ impl Type { | |||
2066 | Some(adt.into()) | 2078 | Some(adt.into()) |
2067 | } | 2079 | } |
2068 | 2080 | ||
2081 | pub fn as_builtin(&self) -> Option<BuiltinType> { | ||
2082 | self.ty.as_builtin().map(|inner| BuiltinType { inner }) | ||
2083 | } | ||
2084 | |||
2069 | pub fn as_dyn_trait(&self) -> Option<Trait> { | 2085 | pub fn as_dyn_trait(&self) -> Option<Trait> { |
2070 | self.ty.dyn_trait().map(Into::into) | 2086 | self.ty.dyn_trait().map(Into::into) |
2071 | } | 2087 | } |
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.rs b/crates/hir_def/src/body.rs index 8360426f1..98b485b60 100644 --- a/crates/hir_def/src/body.rs +++ b/crates/hir_def/src/body.rs | |||
@@ -21,8 +21,6 @@ use profile::Count; | |||
21 | use rustc_hash::FxHashMap; | 21 | use rustc_hash::FxHashMap; |
22 | use syntax::{ast, AstNode, AstPtr}; | 22 | use syntax::{ast, AstNode, AstPtr}; |
23 | 23 | ||
24 | pub use lower::LowerCtx; | ||
25 | |||
26 | use crate::{ | 24 | use crate::{ |
27 | attr::{Attrs, RawAttrs}, | 25 | attr::{Attrs, RawAttrs}, |
28 | db::DefDatabase, | 26 | db::DefDatabase, |
@@ -35,6 +33,8 @@ use crate::{ | |||
35 | UnresolvedMacro, | 33 | UnresolvedMacro, |
36 | }; | 34 | }; |
37 | 35 | ||
36 | pub use lower::LowerCtx; | ||
37 | |||
38 | /// A subset of Expander that only deals with cfg attributes. We only need it to | 38 | /// A subset of Expander that only deals with cfg attributes. We only need it to |
39 | /// avoid cyclic queries in crate def map during enum processing. | 39 | /// avoid cyclic queries in crate def map during enum processing. |
40 | #[derive(Debug)] | 40 | #[derive(Debug)] |
diff --git a/crates/hir_def/src/body/lower.rs b/crates/hir_def/src/body/lower.rs index 75dc19c11..2a7e0205f 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( |
@@ -996,23 +1006,27 @@ impl From<ast::BinOp> for BinaryOp { | |||
996 | impl From<ast::LiteralKind> for Literal { | 1006 | impl From<ast::LiteralKind> for Literal { |
997 | fn from(ast_lit_kind: ast::LiteralKind) -> Self { | 1007 | fn from(ast_lit_kind: ast::LiteralKind) -> Self { |
998 | match ast_lit_kind { | 1008 | match ast_lit_kind { |
1009 | // FIXME: these should have actual values filled in, but unsure on perf impact | ||
999 | LiteralKind::IntNumber(lit) => { | 1010 | LiteralKind::IntNumber(lit) => { |
1000 | if let builtin @ Some(_) = lit.suffix().and_then(BuiltinFloat::from_suffix) { | 1011 | if let builtin @ Some(_) = lit.suffix().and_then(BuiltinFloat::from_suffix) { |
1001 | return Literal::Float(Default::default(), builtin); | 1012 | return Literal::Float(Default::default(), builtin); |
1002 | } else if let builtin @ Some(_) = | 1013 | } else if let builtin @ Some(_) = |
1003 | lit.suffix().and_then(|it| BuiltinInt::from_suffix(&it)) | 1014 | lit.suffix().and_then(|it| BuiltinInt::from_suffix(&it)) |
1004 | { | 1015 | { |
1005 | Literal::Int(Default::default(), builtin) | 1016 | Literal::Int(lit.value().unwrap_or(0) as i128, builtin) |
1006 | } else { | 1017 | } else { |
1007 | let builtin = lit.suffix().and_then(|it| BuiltinUint::from_suffix(&it)); | 1018 | let builtin = lit.suffix().and_then(|it| BuiltinUint::from_suffix(&it)); |
1008 | Literal::Uint(Default::default(), builtin) | 1019 | Literal::Uint(lit.value().unwrap_or(0), builtin) |
1009 | } | 1020 | } |
1010 | } | 1021 | } |
1011 | LiteralKind::FloatNumber(lit) => { | 1022 | LiteralKind::FloatNumber(lit) => { |
1012 | let ty = lit.suffix().and_then(|it| BuiltinFloat::from_suffix(&it)); | 1023 | let ty = lit.suffix().and_then(|it| BuiltinFloat::from_suffix(&it)); |
1013 | Literal::Float(Default::default(), ty) | 1024 | Literal::Float(Default::default(), ty) |
1014 | } | 1025 | } |
1015 | LiteralKind::ByteString(_) => Literal::ByteString(Default::default()), | 1026 | LiteralKind::ByteString(bs) => { |
1027 | let text = bs.value().map(Vec::from).unwrap_or_else(Default::default); | ||
1028 | Literal::ByteString(text) | ||
1029 | } | ||
1016 | LiteralKind::String(_) => Literal::String(Default::default()), | 1030 | LiteralKind::String(_) => Literal::String(Default::default()), |
1017 | LiteralKind::Byte => Literal::Uint(Default::default(), Some(BuiltinUint::U8)), | 1031 | LiteralKind::Byte => Literal::Uint(Default::default(), Some(BuiltinUint::U8)), |
1018 | LiteralKind::Bool(val) => Literal::Bool(val), | 1032 | LiteralKind::Bool(val) => Literal::Bool(val), |
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..2ba619d23 100644 --- a/crates/hir_def/src/expr.rs +++ b/crates/hir_def/src/expr.rs | |||
@@ -43,8 +43,8 @@ pub enum Literal { | |||
43 | ByteString(Vec<u8>), | 43 | ByteString(Vec<u8>), |
44 | Char(char), | 44 | Char(char), |
45 | Bool(bool), | 45 | Bool(bool), |
46 | Int(u64, Option<BuiltinInt>), | 46 | Int(i128, Option<BuiltinInt>), |
47 | Uint(u64, Option<BuiltinUint>), | 47 | Uint(u128, Option<BuiltinUint>), |
48 | Float(u64, Option<BuiltinFloat>), // FIXME: f64 is not Eq | 48 | Float(u64, Option<BuiltinFloat>), // FIXME: f64 is not Eq |
49 | } | 49 | } |
50 | 50 | ||
@@ -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/find_path.rs b/crates/hir_def/src/find_path.rs index 858e88038..ee52794aa 100644 --- a/crates/hir_def/src/find_path.rs +++ b/crates/hir_def/src/find_path.rs | |||
@@ -5,10 +5,10 @@ use std::iter; | |||
5 | use hir_expand::name::{known, AsName, Name}; | 5 | use hir_expand::name::{known, AsName, Name}; |
6 | use rustc_hash::FxHashSet; | 6 | use rustc_hash::FxHashSet; |
7 | 7 | ||
8 | use crate::nameres::DefMap; | ||
9 | use crate::{ | 8 | use crate::{ |
10 | db::DefDatabase, | 9 | db::DefDatabase, |
11 | item_scope::ItemInNs, | 10 | item_scope::ItemInNs, |
11 | nameres::DefMap, | ||
12 | path::{ModPath, PathKind}, | 12 | path::{ModPath, PathKind}, |
13 | visibility::Visibility, | 13 | visibility::Visibility, |
14 | ModuleDefId, ModuleId, | 14 | ModuleDefId, ModuleId, |
@@ -134,7 +134,16 @@ fn find_path_inner( | |||
134 | for (name, def_id) in root_def_map.extern_prelude() { | 134 | for (name, def_id) in root_def_map.extern_prelude() { |
135 | if item == ItemInNs::Types(*def_id) { | 135 | if item == ItemInNs::Types(*def_id) { |
136 | let name = scope_name.unwrap_or_else(|| name.clone()); | 136 | let name = scope_name.unwrap_or_else(|| name.clone()); |
137 | return Some(ModPath::from_segments(PathKind::Plain, vec![name])); | 137 | |
138 | let name_already_occupied_in_type_ns = def_map | ||
139 | .with_ancestor_maps(db, from.local_id, &mut |def_map, local_id| { | ||
140 | def_map[local_id].scope.get(&name).take_types().filter(|&id| id != *def_id) | ||
141 | }) | ||
142 | .is_some(); | ||
143 | return Some(ModPath::from_segments( | ||
144 | if name_already_occupied_in_type_ns { PathKind::Abs } else { PathKind::Plain }, | ||
145 | vec![name], | ||
146 | )); | ||
138 | } | 147 | } |
139 | } | 148 | } |
140 | 149 | ||
diff --git a/crates/hir_def/src/item_tree.rs b/crates/hir_def/src/item_tree.rs index 8d13c7e04..cad8a7479 100644 --- a/crates/hir_def/src/item_tree.rs +++ b/crates/hir_def/src/item_tree.rs | |||
@@ -18,7 +18,7 @@ use hir_expand::{ | |||
18 | ast_id_map::FileAstId, | 18 | ast_id_map::FileAstId, |
19 | hygiene::Hygiene, | 19 | hygiene::Hygiene, |
20 | name::{name, AsName, Name}, | 20 | name::{name, AsName, Name}, |
21 | HirFileId, InFile, | 21 | FragmentKind, HirFileId, InFile, |
22 | }; | 22 | }; |
23 | use la_arena::{Arena, Idx, RawIdx}; | 23 | use la_arena::{Arena, Idx, RawIdx}; |
24 | use profile::Count; | 24 | use profile::Count; |
@@ -656,6 +656,7 @@ pub struct MacroCall { | |||
656 | /// Path to the called macro. | 656 | /// Path to the called macro. |
657 | pub path: Interned<ModPath>, | 657 | pub path: Interned<ModPath>, |
658 | pub ast_id: FileAstId<ast::MacroCall>, | 658 | pub ast_id: FileAstId<ast::MacroCall>, |
659 | pub fragment: FragmentKind, | ||
659 | } | 660 | } |
660 | 661 | ||
661 | #[derive(Debug, Clone, Eq, PartialEq)] | 662 | #[derive(Debug, Clone, Eq, PartialEq)] |
diff --git a/crates/hir_def/src/item_tree/lower.rs b/crates/hir_def/src/item_tree/lower.rs index 5743b3386..fe348091d 100644 --- a/crates/hir_def/src/item_tree/lower.rs +++ b/crates/hir_def/src/item_tree/lower.rs | |||
@@ -624,7 +624,8 @@ impl<'a> Ctx<'a> { | |||
624 | fn lower_macro_call(&mut self, m: &ast::MacroCall) -> Option<FileItemTreeId<MacroCall>> { | 624 | fn lower_macro_call(&mut self, m: &ast::MacroCall) -> Option<FileItemTreeId<MacroCall>> { |
625 | let path = Interned::new(ModPath::from_src(self.db, m.path()?, &self.hygiene)?); | 625 | let path = Interned::new(ModPath::from_src(self.db, m.path()?, &self.hygiene)?); |
626 | let ast_id = self.source_ast_id_map.ast_id(m); | 626 | let ast_id = self.source_ast_id_map.ast_id(m); |
627 | let res = MacroCall { path, ast_id }; | 627 | let fragment = hir_expand::to_fragment_kind(m); |
628 | let res = MacroCall { path, ast_id, fragment }; | ||
628 | Some(id(self.data().macro_calls.alloc(res))) | 629 | Some(id(self.data().macro_calls.alloc(res))) |
629 | } | 630 | } |
630 | 631 | ||
diff --git a/crates/hir_def/src/lib.rs b/crates/hir_def/src/lib.rs index da46f16f7..a82ea5957 100644 --- a/crates/hir_def/src/lib.rs +++ b/crates/hir_def/src/lib.rs | |||
@@ -62,13 +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, HirFileId, InFile, MacroCallId, MacroCallKind, MacroDefId, MacroDefKind, | 65 | AstId, FragmentKind, HirFileId, InFile, MacroCallId, MacroCallKind, MacroDefId, MacroDefKind, |
66 | }; | 66 | }; |
67 | use la_arena::Idx; | 67 | use la_arena::Idx; |
68 | use nameres::DefMap; | 68 | use nameres::DefMap; |
69 | use path::ModPath; | 69 | use path::ModPath; |
70 | use syntax::ast; | 70 | use syntax::ast; |
71 | 71 | ||
72 | use crate::attr::AttrId; | ||
72 | use crate::builtin_type::BuiltinType; | 73 | use crate::builtin_type::BuiltinType; |
73 | use item_tree::{ | 74 | use item_tree::{ |
74 | Const, Enum, Function, Impl, ItemTreeId, ItemTreeNode, ModItem, Static, Struct, Trait, | 75 | Const, Enum, Function, Impl, ItemTreeId, ItemTreeNode, ModItem, Static, Struct, Trait, |
@@ -652,6 +653,7 @@ impl AsMacroCall for InFile<&ast::MacroCall> { | |||
652 | resolver: impl Fn(path::ModPath) -> Option<MacroDefId>, | 653 | resolver: impl Fn(path::ModPath) -> Option<MacroDefId>, |
653 | mut error_sink: &mut dyn FnMut(mbe::ExpandError), | 654 | mut error_sink: &mut dyn FnMut(mbe::ExpandError), |
654 | ) -> Result<Result<MacroCallId, ErrorEmitted>, UnresolvedMacro> { | 655 | ) -> Result<Result<MacroCallId, ErrorEmitted>, UnresolvedMacro> { |
656 | let fragment = hir_expand::to_fragment_kind(self.value); | ||
655 | let ast_id = AstId::new(self.file_id, db.ast_id_map(self.file_id).ast_id(self.value)); | 657 | let ast_id = AstId::new(self.file_id, db.ast_id_map(self.file_id).ast_id(self.value)); |
656 | let h = Hygiene::new(db.upcast(), self.file_id); | 658 | let h = Hygiene::new(db.upcast(), self.file_id); |
657 | let path = self.value.path().and_then(|path| path::ModPath::from_src(db, path, &h)); | 659 | let path = self.value.path().and_then(|path| path::ModPath::from_src(db, path, &h)); |
@@ -667,6 +669,7 @@ impl AsMacroCall for InFile<&ast::MacroCall> { | |||
667 | 669 | ||
668 | macro_call_as_call_id( | 670 | macro_call_as_call_id( |
669 | &AstIdWithPath::new(ast_id.file_id, ast_id.value, path), | 671 | &AstIdWithPath::new(ast_id.file_id, ast_id.value, path), |
672 | fragment, | ||
670 | db, | 673 | db, |
671 | krate, | 674 | krate, |
672 | resolver, | 675 | resolver, |
@@ -695,6 +698,7 @@ pub struct UnresolvedMacro { | |||
695 | 698 | ||
696 | fn macro_call_as_call_id( | 699 | fn macro_call_as_call_id( |
697 | call: &AstIdWithPath<ast::MacroCall>, | 700 | call: &AstIdWithPath<ast::MacroCall>, |
701 | fragment: FragmentKind, | ||
698 | db: &dyn db::DefDatabase, | 702 | db: &dyn db::DefDatabase, |
699 | krate: CrateId, | 703 | krate: CrateId, |
700 | resolver: impl Fn(path::ModPath) -> Option<MacroDefId>, | 704 | resolver: impl Fn(path::ModPath) -> Option<MacroDefId>, |
@@ -718,7 +722,11 @@ fn macro_call_as_call_id( | |||
718 | .map(MacroCallId::from) | 722 | .map(MacroCallId::from) |
719 | } else { | 723 | } else { |
720 | Ok(def | 724 | Ok(def |
721 | .as_lazy_macro(db.upcast(), krate, MacroCallKind::FnLike { ast_id: call.ast_id }) | 725 | .as_lazy_macro( |
726 | db.upcast(), | ||
727 | krate, | ||
728 | MacroCallKind::FnLike { ast_id: call.ast_id, fragment }, | ||
729 | ) | ||
722 | .into()) | 730 | .into()) |
723 | }; | 731 | }; |
724 | Ok(res) | 732 | Ok(res) |
@@ -745,7 +753,7 @@ fn derive_macro_as_call_id( | |||
745 | MacroCallKind::Derive { | 753 | MacroCallKind::Derive { |
746 | ast_id: item_attr.ast_id, | 754 | ast_id: item_attr.ast_id, |
747 | derive_name: last_segment.to_string(), | 755 | derive_name: last_segment.to_string(), |
748 | derive_attr, | 756 | derive_attr_index: derive_attr.ast_index, |
749 | }, | 757 | }, |
750 | ) | 758 | ) |
751 | .into(); | 759 | .into(); |
diff --git a/crates/hir_def/src/nameres.rs b/crates/hir_def/src/nameres.rs index 1bc72ec1f..249af6fc8 100644 --- a/crates/hir_def/src/nameres.rs +++ b/crates/hir_def/src/nameres.rs | |||
@@ -629,7 +629,7 @@ mod diagnostics { | |||
629 | DiagnosticKind::UnresolvedProcMacro { ast } => { | 629 | DiagnosticKind::UnresolvedProcMacro { ast } => { |
630 | let mut precise_location = None; | 630 | let mut precise_location = None; |
631 | let (file, ast, name) = match ast { | 631 | let (file, ast, name) = match ast { |
632 | MacroCallKind::FnLike { ast_id } => { | 632 | MacroCallKind::FnLike { ast_id, .. } => { |
633 | let node = ast_id.to_node(db.upcast()); | 633 | let node = ast_id.to_node(db.upcast()); |
634 | (ast_id.file_id, SyntaxNodePtr::from(AstPtr::new(&node)), None) | 634 | (ast_id.file_id, SyntaxNodePtr::from(AstPtr::new(&node)), None) |
635 | } | 635 | } |
diff --git a/crates/hir_def/src/nameres/collector.rs b/crates/hir_def/src/nameres/collector.rs index 05ceb1efb..19db6cc59 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, 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, |
@@ -215,7 +215,7 @@ struct MacroDirective { | |||
215 | 215 | ||
216 | #[derive(Clone, Debug, Eq, PartialEq)] | 216 | #[derive(Clone, Debug, Eq, PartialEq)] |
217 | enum MacroDirectiveKind { | 217 | enum MacroDirectiveKind { |
218 | FnLike { ast_id: AstIdWithPath<ast::MacroCall> }, | 218 | FnLike { ast_id: AstIdWithPath<ast::MacroCall>, fragment: FragmentKind }, |
219 | Derive { ast_id: AstIdWithPath<ast::Item>, derive_attr: AttrId }, | 219 | Derive { ast_id: AstIdWithPath<ast::Item>, derive_attr: AttrId }, |
220 | } | 220 | } |
221 | 221 | ||
@@ -481,6 +481,11 @@ impl DefCollector<'_> { | |||
481 | let res = self.def_map.resolve_name_in_extern_prelude(self.db, &extern_crate.name); | 481 | let res = self.def_map.resolve_name_in_extern_prelude(self.db, &extern_crate.name); |
482 | 482 | ||
483 | if let Some(ModuleDefId::ModuleId(m)) = res.take_types() { | 483 | if let Some(ModuleDefId::ModuleId(m)) = res.take_types() { |
484 | if m == self.def_map.module_id(current_module_id) { | ||
485 | cov_mark::hit!(ignore_macro_use_extern_crate_self); | ||
486 | return; | ||
487 | } | ||
488 | |||
484 | cov_mark::hit!(macro_rules_from_other_crates_are_visible_with_macro_use); | 489 | cov_mark::hit!(macro_rules_from_other_crates_are_visible_with_macro_use); |
485 | self.import_all_macros_exported(current_module_id, m.krate); | 490 | self.import_all_macros_exported(current_module_id, m.krate); |
486 | } | 491 | } |
@@ -807,9 +812,10 @@ impl DefCollector<'_> { | |||
807 | let mut res = ReachedFixedPoint::Yes; | 812 | let mut res = ReachedFixedPoint::Yes; |
808 | macros.retain(|directive| { | 813 | macros.retain(|directive| { |
809 | match &directive.kind { | 814 | match &directive.kind { |
810 | MacroDirectiveKind::FnLike { ast_id } => { | 815 | MacroDirectiveKind::FnLike { ast_id, fragment } => { |
811 | match macro_call_as_call_id( | 816 | match macro_call_as_call_id( |
812 | ast_id, | 817 | ast_id, |
818 | *fragment, | ||
813 | self.db, | 819 | self.db, |
814 | self.def_map.krate, | 820 | self.def_map.krate, |
815 | |path| { | 821 | |path| { |
@@ -926,8 +932,9 @@ impl DefCollector<'_> { | |||
926 | 932 | ||
927 | for directive in &self.unexpanded_macros { | 933 | for directive in &self.unexpanded_macros { |
928 | match &directive.kind { | 934 | match &directive.kind { |
929 | MacroDirectiveKind::FnLike { ast_id, .. } => match macro_call_as_call_id( | 935 | MacroDirectiveKind::FnLike { ast_id, fragment } => match macro_call_as_call_id( |
930 | ast_id, | 936 | ast_id, |
937 | *fragment, | ||
931 | self.db, | 938 | self.db, |
932 | self.def_map.krate, | 939 | self.def_map.krate, |
933 | |path| { | 940 | |path| { |
@@ -1496,6 +1503,7 @@ impl ModCollector<'_, '_> { | |||
1496 | let mut error = None; | 1503 | let mut error = None; |
1497 | match macro_call_as_call_id( | 1504 | match macro_call_as_call_id( |
1498 | &ast_id, | 1505 | &ast_id, |
1506 | mac.fragment, | ||
1499 | self.def_collector.db, | 1507 | self.def_collector.db, |
1500 | self.def_collector.def_map.krate, | 1508 | self.def_collector.def_map.krate, |
1501 | |path| { | 1509 | |path| { |
@@ -1524,9 +1532,14 @@ impl ModCollector<'_, '_> { | |||
1524 | } | 1532 | } |
1525 | Ok(Err(_)) => { | 1533 | Ok(Err(_)) => { |
1526 | // Built-in macro failed eager expansion. | 1534 | // Built-in macro failed eager expansion. |
1535 | |||
1536 | // FIXME: don't parse the file here | ||
1537 | let fragment = hir_expand::to_fragment_kind( | ||
1538 | &ast_id.ast_id.to_node(self.def_collector.db.upcast()), | ||
1539 | ); | ||
1527 | self.def_collector.def_map.diagnostics.push(DefDiagnostic::macro_error( | 1540 | self.def_collector.def_map.diagnostics.push(DefDiagnostic::macro_error( |
1528 | self.module_id, | 1541 | self.module_id, |
1529 | MacroCallKind::FnLike { ast_id: ast_id.ast_id }, | 1542 | MacroCallKind::FnLike { ast_id: ast_id.ast_id, fragment }, |
1530 | error.unwrap().to_string(), | 1543 | error.unwrap().to_string(), |
1531 | )); | 1544 | )); |
1532 | return; | 1545 | return; |
@@ -1543,7 +1556,7 @@ impl ModCollector<'_, '_> { | |||
1543 | self.def_collector.unexpanded_macros.push(MacroDirective { | 1556 | self.def_collector.unexpanded_macros.push(MacroDirective { |
1544 | module_id: self.module_id, | 1557 | module_id: self.module_id, |
1545 | depth: self.macro_depth + 1, | 1558 | depth: self.macro_depth + 1, |
1546 | kind: MacroDirectiveKind::FnLike { ast_id }, | 1559 | kind: MacroDirectiveKind::FnLike { ast_id, fragment: mac.fragment }, |
1547 | }); | 1560 | }); |
1548 | } | 1561 | } |
1549 | 1562 | ||
diff --git a/crates/hir_def/src/nameres/tests.rs b/crates/hir_def/src/nameres/tests.rs index 4f2e7a2f9..9f652731d 100644 --- a/crates/hir_def/src/nameres/tests.rs +++ b/crates/hir_def/src/nameres/tests.rs | |||
@@ -411,6 +411,22 @@ struct Arc; | |||
411 | } | 411 | } |
412 | 412 | ||
413 | #[test] | 413 | #[test] |
414 | fn macro_use_extern_crate_self() { | ||
415 | cov_mark::check!(ignore_macro_use_extern_crate_self); | ||
416 | check( | ||
417 | r#" | ||
418 | //- /main.rs crate:main | ||
419 | #[macro_use] | ||
420 | extern crate self as bla; | ||
421 | "#, | ||
422 | expect![[r#" | ||
423 | crate | ||
424 | bla: t | ||
425 | "#]], | ||
426 | ); | ||
427 | } | ||
428 | |||
429 | #[test] | ||
414 | fn reexport_across_crates() { | 430 | fn reexport_across_crates() { |
415 | check( | 431 | check( |
416 | r#" | 432 | r#" |
diff --git a/crates/hir_def/src/nameres/tests/incremental.rs b/crates/hir_def/src/nameres/tests/incremental.rs index 227ecd162..d884a6eb4 100644 --- a/crates/hir_def/src/nameres/tests/incremental.rs +++ b/crates/hir_def/src/nameres/tests/incremental.rs | |||
@@ -137,6 +137,9 @@ m!(Z); | |||
137 | }); | 137 | }); |
138 | let n_recalculated_item_trees = events.iter().filter(|it| it.contains("item_tree")).count(); | 138 | let n_recalculated_item_trees = events.iter().filter(|it| it.contains("item_tree")).count(); |
139 | assert_eq!(n_recalculated_item_trees, 6); | 139 | assert_eq!(n_recalculated_item_trees, 6); |
140 | let n_reparsed_macros = | ||
141 | events.iter().filter(|it| it.contains("parse_macro_expansion")).count(); | ||
142 | assert_eq!(n_reparsed_macros, 3); | ||
140 | } | 143 | } |
141 | 144 | ||
142 | let new_text = r#" | 145 | let new_text = r#" |
@@ -155,5 +158,8 @@ m!(Z); | |||
155 | }); | 158 | }); |
156 | let n_recalculated_item_trees = events.iter().filter(|it| it.contains("item_tree")).count(); | 159 | let n_recalculated_item_trees = events.iter().filter(|it| it.contains("item_tree")).count(); |
157 | assert_eq!(n_recalculated_item_trees, 1); | 160 | assert_eq!(n_recalculated_item_trees, 1); |
161 | let n_reparsed_macros = | ||
162 | events.iter().filter(|it| it.contains("parse_macro_expansion")).count(); | ||
163 | assert_eq!(n_reparsed_macros, 0); | ||
158 | } | 164 | } |
159 | } | 165 | } |
diff --git a/crates/hir_def/src/type_ref.rs b/crates/hir_def/src/type_ref.rs index ea29da5da..9e44547cb 100644 --- a/crates/hir_def/src/type_ref.rs +++ b/crates/hir_def/src/type_ref.rs | |||
@@ -2,6 +2,7 @@ | |||
2 | //! be directly created from an ast::TypeRef, without further queries. | 2 | //! be directly created from an ast::TypeRef, without further queries. |
3 | 3 | ||
4 | use hir_expand::{name::Name, AstId, InFile}; | 4 | use hir_expand::{name::Name, AstId, InFile}; |
5 | use std::convert::TryInto; | ||
5 | use syntax::ast; | 6 | use syntax::ast; |
6 | 7 | ||
7 | use crate::{body::LowerCtx, path::Path}; | 8 | use crate::{body::LowerCtx, path::Path}; |
@@ -79,7 +80,9 @@ pub enum TypeRef { | |||
79 | Path(Path), | 80 | Path(Path), |
80 | RawPtr(Box<TypeRef>, Mutability), | 81 | RawPtr(Box<TypeRef>, Mutability), |
81 | Reference(Box<TypeRef>, Option<LifetimeRef>, Mutability), | 82 | Reference(Box<TypeRef>, Option<LifetimeRef>, Mutability), |
82 | Array(Box<TypeRef> /*, Expr*/), | 83 | // FIXME: for full const generics, the latter element (length) here is going to have to be an |
84 | // expression that is further lowered later in hir_ty. | ||
85 | Array(Box<TypeRef>, ConstScalar), | ||
83 | Slice(Box<TypeRef>), | 86 | Slice(Box<TypeRef>), |
84 | /// A fn pointer. Last element of the vector is the return type. | 87 | /// A fn pointer. Last element of the vector is the return type. |
85 | Fn(Vec<TypeRef>, bool /*varargs*/), | 88 | Fn(Vec<TypeRef>, bool /*varargs*/), |
@@ -140,7 +143,16 @@ impl TypeRef { | |||
140 | TypeRef::RawPtr(Box::new(inner_ty), mutability) | 143 | TypeRef::RawPtr(Box::new(inner_ty), mutability) |
141 | } | 144 | } |
142 | ast::Type::ArrayType(inner) => { | 145 | ast::Type::ArrayType(inner) => { |
143 | TypeRef::Array(Box::new(TypeRef::from_ast_opt(&ctx, inner.ty()))) | 146 | // FIXME: This is a hack. We should probably reuse the machinery of |
147 | // `hir_def::body::lower` to lower this into an `Expr` and then evaluate it at the | ||
148 | // `hir_ty` level, which would allow knowing the type of: | ||
149 | // let v: [u8; 2 + 2] = [0u8; 4]; | ||
150 | let len = inner | ||
151 | .expr() | ||
152 | .map(ConstScalar::usize_from_literal_expr) | ||
153 | .unwrap_or(ConstScalar::Unknown); | ||
154 | |||
155 | TypeRef::Array(Box::new(TypeRef::from_ast_opt(&ctx, inner.ty())), len) | ||
144 | } | 156 | } |
145 | ast::Type::SliceType(inner) => { | 157 | ast::Type::SliceType(inner) => { |
146 | TypeRef::Slice(Box::new(TypeRef::from_ast_opt(&ctx, inner.ty()))) | 158 | TypeRef::Slice(Box::new(TypeRef::from_ast_opt(&ctx, inner.ty()))) |
@@ -212,7 +224,7 @@ impl TypeRef { | |||
212 | } | 224 | } |
213 | TypeRef::RawPtr(type_ref, _) | 225 | TypeRef::RawPtr(type_ref, _) |
214 | | TypeRef::Reference(type_ref, ..) | 226 | | TypeRef::Reference(type_ref, ..) |
215 | | TypeRef::Array(type_ref) | 227 | | TypeRef::Array(type_ref, _) |
216 | | TypeRef::Slice(type_ref) => go(&type_ref, f), | 228 | | TypeRef::Slice(type_ref) => go(&type_ref, f), |
217 | TypeRef::ImplTrait(bounds) | TypeRef::DynTrait(bounds) => { | 229 | TypeRef::ImplTrait(bounds) | TypeRef::DynTrait(bounds) => { |
218 | for bound in bounds { | 230 | for bound in bounds { |
@@ -298,3 +310,58 @@ impl TypeBound { | |||
298 | } | 310 | } |
299 | } | 311 | } |
300 | } | 312 | } |
313 | |||
314 | /// A concrete constant value | ||
315 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
316 | pub enum ConstScalar { | ||
317 | // for now, we only support the trivial case of constant evaluating the length of an array | ||
318 | // Note that this is u64 because the target usize may be bigger than our usize | ||
319 | Usize(u64), | ||
320 | |||
321 | /// Case of an unknown value that rustc might know but we don't | ||
322 | // FIXME: this is a hack to get around chalk not being able to represent unevaluatable | ||
323 | // constants | ||
324 | // https://github.com/rust-analyzer/rust-analyzer/pull/8813#issuecomment-840679177 | ||
325 | // https://rust-lang.zulipchat.com/#narrow/stream/144729-wg-traits/topic/Handling.20non.20evaluatable.20constants'.20equality/near/238386348 | ||
326 | Unknown, | ||
327 | } | ||
328 | |||
329 | impl std::fmt::Display for ConstScalar { | ||
330 | fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { | ||
331 | match self { | ||
332 | ConstScalar::Usize(us) => write!(fmt, "{}", us), | ||
333 | ConstScalar::Unknown => write!(fmt, "_"), | ||
334 | } | ||
335 | } | ||
336 | } | ||
337 | |||
338 | impl ConstScalar { | ||
339 | /// Gets a target usize out of the ConstScalar | ||
340 | pub fn as_usize(&self) -> Option<u64> { | ||
341 | match self { | ||
342 | &ConstScalar::Usize(us) => Some(us), | ||
343 | _ => None, | ||
344 | } | ||
345 | } | ||
346 | |||
347 | // FIXME: as per the comments on `TypeRef::Array`, this evaluation should not happen at this | ||
348 | // parse stage. | ||
349 | fn usize_from_literal_expr(expr: ast::Expr) -> ConstScalar { | ||
350 | match expr { | ||
351 | ast::Expr::Literal(lit) => { | ||
352 | let lkind = lit.kind(); | ||
353 | match lkind { | ||
354 | ast::LiteralKind::IntNumber(num) | ||
355 | if num.suffix() == None || num.suffix() == Some("usize") => | ||
356 | { | ||
357 | num.value().and_then(|v| v.try_into().ok()) | ||
358 | } | ||
359 | _ => None, | ||
360 | } | ||
361 | } | ||
362 | _ => None, | ||
363 | } | ||
364 | .map(ConstScalar::Usize) | ||
365 | .unwrap_or(ConstScalar::Unknown) | ||
366 | } | ||
367 | } | ||
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/builtin_macro.rs b/crates/hir_expand/src/builtin_macro.rs index 179de61f9..280c25f11 100644 --- a/crates/hir_expand/src/builtin_macro.rs +++ b/crates/hir_expand/src/builtin_macro.rs | |||
@@ -118,6 +118,7 @@ register_builtin! { | |||
118 | EAGER: | 118 | EAGER: |
119 | (compile_error, CompileError) => compile_error_expand, | 119 | (compile_error, CompileError) => compile_error_expand, |
120 | (concat, Concat) => concat_expand, | 120 | (concat, Concat) => concat_expand, |
121 | (concat_idents, ConcatIdents) => concat_idents_expand, | ||
121 | (include, Include) => include_expand, | 122 | (include, Include) => include_expand, |
122 | (include_bytes, IncludeBytes) => include_bytes_expand, | 123 | (include_bytes, IncludeBytes) => include_bytes_expand, |
123 | (include_str, IncludeStr) => include_str_expand, | 124 | (include_str, IncludeStr) => include_str_expand, |
@@ -373,6 +374,28 @@ fn concat_expand( | |||
373 | ExpandResult { value: Some(ExpandedEager::new(quote!(#text), FragmentKind::Expr)), err } | 374 | ExpandResult { value: Some(ExpandedEager::new(quote!(#text), FragmentKind::Expr)), err } |
374 | } | 375 | } |
375 | 376 | ||
377 | fn concat_idents_expand( | ||
378 | _db: &dyn AstDatabase, | ||
379 | _arg_id: EagerMacroId, | ||
380 | tt: &tt::Subtree, | ||
381 | ) -> ExpandResult<Option<ExpandedEager>> { | ||
382 | let mut err = None; | ||
383 | let mut ident = String::new(); | ||
384 | for (i, t) in tt.token_trees.iter().enumerate() { | ||
385 | match t { | ||
386 | tt::TokenTree::Leaf(tt::Leaf::Ident(id)) => { | ||
387 | ident.push_str(id.text.as_str()); | ||
388 | } | ||
389 | tt::TokenTree::Leaf(tt::Leaf::Punct(punct)) if i % 2 == 1 && punct.char == ',' => (), | ||
390 | _ => { | ||
391 | err.get_or_insert(mbe::ExpandError::UnexpectedToken); | ||
392 | } | ||
393 | } | ||
394 | } | ||
395 | let ident = tt::Ident { text: ident.into(), id: tt::TokenId::unspecified() }; | ||
396 | ExpandResult { value: Some(ExpandedEager::new(quote!(#ident), FragmentKind::Expr)), err } | ||
397 | } | ||
398 | |||
376 | fn relative_file( | 399 | fn relative_file( |
377 | db: &dyn AstDatabase, | 400 | db: &dyn AstDatabase, |
378 | call_id: MacroCallId, | 401 | call_id: MacroCallId, |
@@ -578,6 +601,7 @@ mod tests { | |||
578 | krate, | 601 | krate, |
579 | kind: MacroCallKind::FnLike { | 602 | kind: MacroCallKind::FnLike { |
580 | ast_id: AstId::new(file_id.into(), ast_id_map.ast_id(¯o_call)), | 603 | ast_id: AstId::new(file_id.into(), ast_id_map.ast_id(¯o_call)), |
604 | fragment: FragmentKind::Expr, | ||
581 | }, | 605 | }, |
582 | }; | 606 | }; |
583 | 607 | ||
@@ -788,9 +812,21 @@ mod tests { | |||
788 | r##" | 812 | r##" |
789 | #[rustc_builtin_macro] | 813 | #[rustc_builtin_macro] |
790 | macro_rules! concat {} | 814 | macro_rules! concat {} |
791 | concat!("foo", "r", 0, r#"bar"#, false); | 815 | concat!("foo", "r", 0, r#"bar"#, "\n", false); |
816 | "##, | ||
817 | expect![[r#""foor0bar\nfalse""#]], | ||
818 | ); | ||
819 | } | ||
820 | |||
821 | #[test] | ||
822 | fn test_concat_idents_expand() { | ||
823 | check_expansion( | ||
824 | r##" | ||
825 | #[rustc_builtin_macro] | ||
826 | macro_rules! concat_idents {} | ||
827 | concat_idents!(foo, bar); | ||
792 | "##, | 828 | "##, |
793 | expect![[r#""foor0barfalse""#]], | 829 | expect![[r#"foobar"#]], |
794 | ); | 830 | ); |
795 | } | 831 | } |
796 | } | 832 | } |
diff --git a/crates/hir_expand/src/db.rs b/crates/hir_expand/src/db.rs index d61f4b31a..c43d382ad 100644 --- a/crates/hir_expand/src/db.rs +++ b/crates/hir_expand/src/db.rs | |||
@@ -8,15 +8,13 @@ use parser::FragmentKind; | |||
8 | use syntax::{ | 8 | use syntax::{ |
9 | algo::diff, | 9 | algo::diff, |
10 | ast::{self, NameOwner}, | 10 | ast::{self, NameOwner}, |
11 | AstNode, GreenNode, Parse, | 11 | AstNode, GreenNode, Parse, SyntaxNode, SyntaxToken, |
12 | SyntaxKind::*, | ||
13 | SyntaxNode, SyntaxToken, | ||
14 | }; | 12 | }; |
15 | 13 | ||
16 | use crate::{ | 14 | use crate::{ |
17 | ast_id_map::AstIdMap, hygiene::HygieneFrame, BuiltinDeriveExpander, BuiltinFnLikeExpander, | 15 | ast_id_map::AstIdMap, hygiene::HygieneFrame, input::process_macro_input, BuiltinDeriveExpander, |
18 | EagerCallLoc, EagerMacroId, HirFileId, HirFileIdRepr, LazyMacroId, MacroCallId, MacroCallLoc, | 16 | BuiltinFnLikeExpander, EagerCallLoc, EagerMacroId, HirFileId, HirFileIdRepr, LazyMacroId, |
19 | MacroDefId, MacroDefKind, MacroFile, ProcMacroExpander, | 17 | MacroCallId, MacroCallLoc, MacroDefId, MacroDefKind, MacroFile, ProcMacroExpander, |
20 | }; | 18 | }; |
21 | 19 | ||
22 | /// 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. |
@@ -160,7 +158,7 @@ pub fn expand_hypothetical( | |||
160 | 158 | ||
161 | let hypothetical_expansion = macro_def.expand(db, lazy_id, &tt); | 159 | let hypothetical_expansion = macro_def.expand(db, lazy_id, &tt); |
162 | 160 | ||
163 | let fragment_kind = to_fragment_kind(db, actual_macro_call); | 161 | let fragment_kind = macro_fragment_kind(db, actual_macro_call); |
164 | 162 | ||
165 | let (node, tmap_2) = | 163 | let (node, tmap_2) = |
166 | mbe::token_tree_to_syntax_node(&hypothetical_expansion.value, fragment_kind).ok()?; | 164 | mbe::token_tree_to_syntax_node(&hypothetical_expansion.value, fragment_kind).ok()?; |
@@ -226,7 +224,7 @@ fn parse_macro_expansion( | |||
226 | None => return ExpandResult { value: None, err: result.err }, | 224 | None => return ExpandResult { value: None, err: result.err }, |
227 | }; | 225 | }; |
228 | 226 | ||
229 | let fragment_kind = to_fragment_kind(db, macro_file.macro_call_id); | 227 | let fragment_kind = macro_fragment_kind(db, macro_file.macro_call_id); |
230 | 228 | ||
231 | log::debug!("expanded = {}", tt.as_debug_string()); | 229 | log::debug!("expanded = {}", tt.as_debug_string()); |
232 | log::debug!("kind = {:?}", fragment_kind); | 230 | log::debug!("kind = {:?}", fragment_kind); |
@@ -269,7 +267,16 @@ fn parse_macro_expansion( | |||
269 | 267 | ||
270 | 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)>> { |
271 | let arg = db.macro_arg_text(id)?; | 269 | let arg = db.macro_arg_text(id)?; |
272 | 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 | |||
273 | Some(Arc::new((tt, tmap))) | 280 | Some(Arc::new((tt, tmap))) |
274 | } | 281 | } |
275 | 282 | ||
@@ -283,6 +290,7 @@ fn macro_arg_text(db: &dyn AstDatabase, id: MacroCallId) -> Option<GreenNode> { | |||
283 | }; | 290 | }; |
284 | let loc = db.lookup_intern_macro(id); | 291 | let loc = db.lookup_intern_macro(id); |
285 | let arg = loc.kind.arg(db)?; | 292 | let arg = loc.kind.arg(db)?; |
293 | let arg = process_macro_input(db, arg, id); | ||
286 | Some(arg.green().into()) | 294 | Some(arg.green().into()) |
287 | } | 295 | } |
288 | 296 | ||
@@ -427,62 +435,15 @@ fn hygiene_frame(db: &dyn AstDatabase, file_id: HirFileId) -> Arc<HygieneFrame> | |||
427 | Arc::new(HygieneFrame::new(db, file_id)) | 435 | Arc::new(HygieneFrame::new(db, file_id)) |
428 | } | 436 | } |
429 | 437 | ||
430 | /// Given a `MacroCallId`, return what `FragmentKind` it belongs to. | 438 | fn macro_fragment_kind(db: &dyn AstDatabase, id: MacroCallId) -> FragmentKind { |
431 | /// FIXME: Not completed | 439 | match id { |
432 | fn to_fragment_kind(db: &dyn AstDatabase, id: MacroCallId) -> FragmentKind { | 440 | MacroCallId::LazyMacro(id) => { |
433 | let lazy_id = match id { | 441 | let loc: MacroCallLoc = db.lookup_intern_macro(id); |
434 | MacroCallId::LazyMacro(id) => id, | 442 | loc.kind.fragment_kind() |
435 | MacroCallId::EagerMacro(id) => { | ||
436 | return db.lookup_intern_eager_expansion(id).fragment; | ||
437 | } | ||
438 | }; | ||
439 | let syn = db.lookup_intern_macro(lazy_id).kind.node(db).value; | ||
440 | |||
441 | let parent = match syn.parent() { | ||
442 | Some(it) => it, | ||
443 | None => return FragmentKind::Statements, | ||
444 | }; | ||
445 | |||
446 | match parent.kind() { | ||
447 | MACRO_ITEMS | SOURCE_FILE => FragmentKind::Items, | ||
448 | MACRO_STMTS => FragmentKind::Statements, | ||
449 | MACRO_PAT => FragmentKind::Pattern, | ||
450 | MACRO_TYPE => FragmentKind::Type, | ||
451 | ITEM_LIST => FragmentKind::Items, | ||
452 | LET_STMT => { | ||
453 | // FIXME: Handle LHS Pattern | ||
454 | FragmentKind::Expr | ||
455 | } | 443 | } |
456 | EXPR_STMT => FragmentKind::Statements, | 444 | MacroCallId::EagerMacro(id) => { |
457 | BLOCK_EXPR => FragmentKind::Statements, | 445 | let loc: EagerCallLoc = db.lookup_intern_eager_expansion(id); |
458 | ARG_LIST => FragmentKind::Expr, | 446 | loc.fragment |
459 | TRY_EXPR => FragmentKind::Expr, | ||
460 | TUPLE_EXPR => FragmentKind::Expr, | ||
461 | PAREN_EXPR => FragmentKind::Expr, | ||
462 | ARRAY_EXPR => FragmentKind::Expr, | ||
463 | FOR_EXPR => FragmentKind::Expr, | ||
464 | PATH_EXPR => FragmentKind::Expr, | ||
465 | CLOSURE_EXPR => FragmentKind::Expr, | ||
466 | CONDITION => FragmentKind::Expr, | ||
467 | BREAK_EXPR => FragmentKind::Expr, | ||
468 | RETURN_EXPR => FragmentKind::Expr, | ||
469 | MATCH_EXPR => FragmentKind::Expr, | ||
470 | MATCH_ARM => FragmentKind::Expr, | ||
471 | MATCH_GUARD => FragmentKind::Expr, | ||
472 | RECORD_EXPR_FIELD => FragmentKind::Expr, | ||
473 | CALL_EXPR => FragmentKind::Expr, | ||
474 | INDEX_EXPR => FragmentKind::Expr, | ||
475 | METHOD_CALL_EXPR => FragmentKind::Expr, | ||
476 | FIELD_EXPR => FragmentKind::Expr, | ||
477 | AWAIT_EXPR => FragmentKind::Expr, | ||
478 | CAST_EXPR => FragmentKind::Expr, | ||
479 | REF_EXPR => FragmentKind::Expr, | ||
480 | PREFIX_EXPR => FragmentKind::Expr, | ||
481 | RANGE_EXPR => FragmentKind::Expr, | ||
482 | BIN_EXPR => FragmentKind::Expr, | ||
483 | _ => { | ||
484 | // Unknown , Just guess it is `Items` | ||
485 | FragmentKind::Items | ||
486 | } | 447 | } |
487 | } | 448 | } |
488 | } | 449 | } |
diff --git a/crates/hir_expand/src/eager.rs b/crates/hir_expand/src/eager.rs index f12132f84..85491fe8b 100644 --- a/crates/hir_expand/src/eager.rs +++ b/crates/hir_expand/src/eager.rs | |||
@@ -175,8 +175,13 @@ fn lazy_expand( | |||
175 | ) -> ExpandResult<Option<InFile<SyntaxNode>>> { | 175 | ) -> ExpandResult<Option<InFile<SyntaxNode>>> { |
176 | let ast_id = db.ast_id_map(macro_call.file_id).ast_id(¯o_call.value); | 176 | let ast_id = db.ast_id_map(macro_call.file_id).ast_id(¯o_call.value); |
177 | 177 | ||
178 | let fragment = crate::to_fragment_kind(¯o_call.value); | ||
178 | let id: MacroCallId = def | 179 | let id: MacroCallId = def |
179 | .as_lazy_macro(db, krate, MacroCallKind::FnLike { ast_id: macro_call.with_value(ast_id) }) | 180 | .as_lazy_macro( |
181 | db, | ||
182 | krate, | ||
183 | MacroCallKind::FnLike { ast_id: macro_call.with_value(ast_id), fragment }, | ||
184 | ) | ||
180 | .into(); | 185 | .into(); |
181 | 186 | ||
182 | let err = db.macro_expand_error(id); | 187 | let err = db.macro_expand_error(id); |
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 0402640de..88cb16ca4 100644 --- a/crates/hir_expand/src/lib.rs +++ b/crates/hir_expand/src/lib.rs | |||
@@ -14,9 +14,12 @@ 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; |
20 | |||
19 | pub use mbe::{ExpandError, ExpandResult}; | 21 | pub use mbe::{ExpandError, ExpandResult}; |
22 | pub use parser::FragmentKind; | ||
20 | 23 | ||
21 | use std::hash::Hash; | 24 | use std::hash::Hash; |
22 | use std::sync::Arc; | 25 | use std::sync::Arc; |
@@ -269,6 +272,10 @@ impl MacroDefId { | |||
269 | }; | 272 | }; |
270 | Either::Left(*id) | 273 | Either::Left(*id) |
271 | } | 274 | } |
275 | |||
276 | pub fn is_proc_macro(&self) -> bool { | ||
277 | matches!(self.kind, MacroDefKind::ProcMacro(..)) | ||
278 | } | ||
272 | } | 279 | } |
273 | 280 | ||
274 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | 281 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
@@ -290,13 +297,21 @@ pub struct MacroCallLoc { | |||
290 | 297 | ||
291 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] | 298 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] |
292 | pub enum MacroCallKind { | 299 | pub enum MacroCallKind { |
293 | FnLike { ast_id: AstId<ast::MacroCall> }, | 300 | FnLike { |
294 | 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 | }, | ||
295 | } | 313 | } |
296 | 314 | ||
297 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
298 | pub struct AttrId(pub u32); | ||
299 | |||
300 | impl MacroCallKind { | 315 | impl MacroCallKind { |
301 | fn file_id(&self) -> HirFileId { | 316 | fn file_id(&self) -> HirFileId { |
302 | match self { | 317 | match self { |
@@ -324,6 +339,13 @@ impl MacroCallKind { | |||
324 | MacroCallKind::Derive { ast_id, .. } => Some(ast_id.to_node(db).syntax().clone()), | 339 | MacroCallKind::Derive { ast_id, .. } => Some(ast_id.to_node(db).syntax().clone()), |
325 | } | 340 | } |
326 | } | 341 | } |
342 | |||
343 | fn fragment_kind(&self) -> FragmentKind { | ||
344 | match self { | ||
345 | MacroCallKind::FnLike { fragment, .. } => *fragment, | ||
346 | MacroCallKind::Derive { .. } => FragmentKind::Items, | ||
347 | } | ||
348 | } | ||
327 | } | 349 | } |
328 | 350 | ||
329 | impl MacroCallId { | 351 | impl MacroCallId { |
@@ -357,7 +379,6 @@ pub struct ExpansionInfo { | |||
357 | } | 379 | } |
358 | 380 | ||
359 | pub use mbe::Origin; | 381 | pub use mbe::Origin; |
360 | use parser::FragmentKind; | ||
361 | 382 | ||
362 | impl ExpansionInfo { | 383 | impl ExpansionInfo { |
363 | pub fn call_node(&self) -> Option<InFile<SyntaxNode>> { | 384 | pub fn call_node(&self) -> Option<InFile<SyntaxNode>> { |
@@ -562,3 +583,59 @@ impl<N: AstNode> InFile<N> { | |||
562 | self.with_value(self.value.syntax()) | 583 | self.with_value(self.value.syntax()) |
563 | } | 584 | } |
564 | } | 585 | } |
586 | |||
587 | /// Given a `MacroCallId`, return what `FragmentKind` it belongs to. | ||
588 | /// FIXME: Not completed | ||
589 | pub fn to_fragment_kind(call: &ast::MacroCall) -> FragmentKind { | ||
590 | use syntax::SyntaxKind::*; | ||
591 | |||
592 | let syn = call.syntax(); | ||
593 | |||
594 | let parent = match syn.parent() { | ||
595 | Some(it) => it, | ||
596 | None => return FragmentKind::Statements, | ||
597 | }; | ||
598 | |||
599 | match parent.kind() { | ||
600 | MACRO_ITEMS | SOURCE_FILE => FragmentKind::Items, | ||
601 | MACRO_STMTS => FragmentKind::Statements, | ||
602 | MACRO_PAT => FragmentKind::Pattern, | ||
603 | MACRO_TYPE => FragmentKind::Type, | ||
604 | ITEM_LIST => FragmentKind::Items, | ||
605 | LET_STMT => { | ||
606 | // FIXME: Handle LHS Pattern | ||
607 | FragmentKind::Expr | ||
608 | } | ||
609 | EXPR_STMT => FragmentKind::Statements, | ||
610 | BLOCK_EXPR => FragmentKind::Statements, | ||
611 | ARG_LIST => FragmentKind::Expr, | ||
612 | TRY_EXPR => FragmentKind::Expr, | ||
613 | TUPLE_EXPR => FragmentKind::Expr, | ||
614 | PAREN_EXPR => FragmentKind::Expr, | ||
615 | ARRAY_EXPR => FragmentKind::Expr, | ||
616 | FOR_EXPR => FragmentKind::Expr, | ||
617 | PATH_EXPR => FragmentKind::Expr, | ||
618 | CLOSURE_EXPR => FragmentKind::Expr, | ||
619 | CONDITION => FragmentKind::Expr, | ||
620 | BREAK_EXPR => FragmentKind::Expr, | ||
621 | RETURN_EXPR => FragmentKind::Expr, | ||
622 | MATCH_EXPR => FragmentKind::Expr, | ||
623 | MATCH_ARM => FragmentKind::Expr, | ||
624 | MATCH_GUARD => FragmentKind::Expr, | ||
625 | RECORD_EXPR_FIELD => FragmentKind::Expr, | ||
626 | CALL_EXPR => FragmentKind::Expr, | ||
627 | INDEX_EXPR => FragmentKind::Expr, | ||
628 | METHOD_CALL_EXPR => FragmentKind::Expr, | ||
629 | FIELD_EXPR => FragmentKind::Expr, | ||
630 | AWAIT_EXPR => FragmentKind::Expr, | ||
631 | CAST_EXPR => FragmentKind::Expr, | ||
632 | REF_EXPR => FragmentKind::Expr, | ||
633 | PREFIX_EXPR => FragmentKind::Expr, | ||
634 | RANGE_EXPR => FragmentKind::Expr, | ||
635 | BIN_EXPR => FragmentKind::Expr, | ||
636 | _ => { | ||
637 | // Unknown , Just guess it is `Items` | ||
638 | FragmentKind::Items | ||
639 | } | ||
640 | } | ||
641 | } | ||
diff --git a/crates/hir_expand/src/name.rs b/crates/hir_expand/src/name.rs index bcfd3e524..5a5dc9afd 100644 --- a/crates/hir_expand/src/name.rs +++ b/crates/hir_expand/src/name.rs | |||
@@ -212,6 +212,7 @@ pub mod known { | |||
212 | std_panic, | 212 | std_panic, |
213 | stringify, | 213 | stringify, |
214 | concat, | 214 | concat, |
215 | concat_idents, | ||
215 | include, | 216 | include, |
216 | include_bytes, | 217 | include_bytes, |
217 | include_str, | 218 | include_str, |
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_expand/src/quote.rs b/crates/hir_expand/src/quote.rs index c82487ef0..230a59964 100644 --- a/crates/hir_expand/src/quote.rs +++ b/crates/hir_expand/src/quote.rs | |||
@@ -196,8 +196,8 @@ impl_to_to_tokentrees! { | |||
196 | tt::Literal => self { self }; | 196 | tt::Literal => self { self }; |
197 | tt::Ident => self { self }; | 197 | tt::Ident => self { self }; |
198 | tt::Punct => self { self }; | 198 | tt::Punct => self { self }; |
199 | &str => self { tt::Literal{text: format!("{:?}", self.escape_default().to_string()).into(), id: tt::TokenId::unspecified()}}; | 199 | &str => self { tt::Literal{text: format!("\"{}\"", self.escape_debug()).into(), id: tt::TokenId::unspecified()}}; |
200 | String => self { tt::Literal{text: format!("{:?}", self.escape_default().to_string()).into(), id: tt::TokenId::unspecified()}} | 200 | String => self { tt::Literal{text: format!("\"{}\"", self.escape_debug()).into(), id: tt::TokenId::unspecified()}} |
201 | } | 201 | } |
202 | 202 | ||
203 | #[cfg(test)] | 203 | #[cfg(test)] |
diff --git a/crates/hir_ty/src/chalk_ext.rs b/crates/hir_ty/src/chalk_ext.rs index 8c4542956..5232a7d80 100644 --- a/crates/hir_ty/src/chalk_ext.rs +++ b/crates/hir_ty/src/chalk_ext.rs | |||
@@ -1,8 +1,10 @@ | |||
1 | //! Various extensions traits for Chalk types. | 1 | //! Various extensions traits for Chalk types. |
2 | 2 | ||
3 | use chalk_ir::Mutability; | 3 | use chalk_ir::{FloatTy, IntTy, Mutability, Scalar, UintTy}; |
4 | use hir_def::{ | 4 | use hir_def::{ |
5 | type_ref::Rawness, AssocContainerId, FunctionId, GenericDefId, HasModule, Lookup, TraitId, | 5 | builtin_type::{BuiltinFloat, BuiltinInt, BuiltinType, BuiltinUint}, |
6 | type_ref::Rawness, | ||
7 | AssocContainerId, FunctionId, GenericDefId, HasModule, Lookup, TraitId, | ||
6 | }; | 8 | }; |
7 | 9 | ||
8 | use crate::{ | 10 | use crate::{ |
@@ -18,6 +20,7 @@ pub trait TyExt { | |||
18 | fn is_unknown(&self) -> bool; | 20 | fn is_unknown(&self) -> bool; |
19 | 21 | ||
20 | fn as_adt(&self) -> Option<(hir_def::AdtId, &Substitution)>; | 22 | fn as_adt(&self) -> Option<(hir_def::AdtId, &Substitution)>; |
23 | fn as_builtin(&self) -> Option<BuiltinType>; | ||
21 | fn as_tuple(&self) -> Option<&Substitution>; | 24 | fn as_tuple(&self) -> Option<&Substitution>; |
22 | fn as_fn_def(&self, db: &dyn HirDatabase) -> Option<FunctionId>; | 25 | fn as_fn_def(&self, db: &dyn HirDatabase) -> Option<FunctionId>; |
23 | fn as_reference(&self) -> Option<(&Ty, Lifetime, Mutability)>; | 26 | fn as_reference(&self) -> Option<(&Ty, Lifetime, Mutability)>; |
@@ -59,6 +62,35 @@ impl TyExt for Ty { | |||
59 | } | 62 | } |
60 | } | 63 | } |
61 | 64 | ||
65 | fn as_builtin(&self) -> Option<BuiltinType> { | ||
66 | match self.kind(&Interner) { | ||
67 | TyKind::Str => Some(BuiltinType::Str), | ||
68 | TyKind::Scalar(Scalar::Bool) => Some(BuiltinType::Bool), | ||
69 | TyKind::Scalar(Scalar::Char) => Some(BuiltinType::Char), | ||
70 | TyKind::Scalar(Scalar::Float(fty)) => Some(BuiltinType::Float(match fty { | ||
71 | FloatTy::F64 => BuiltinFloat::F64, | ||
72 | FloatTy::F32 => BuiltinFloat::F32, | ||
73 | })), | ||
74 | TyKind::Scalar(Scalar::Int(ity)) => Some(BuiltinType::Int(match ity { | ||
75 | IntTy::Isize => BuiltinInt::Isize, | ||
76 | IntTy::I8 => BuiltinInt::I8, | ||
77 | IntTy::I16 => BuiltinInt::I16, | ||
78 | IntTy::I32 => BuiltinInt::I32, | ||
79 | IntTy::I64 => BuiltinInt::I64, | ||
80 | IntTy::I128 => BuiltinInt::I128, | ||
81 | })), | ||
82 | TyKind::Scalar(Scalar::Uint(ity)) => Some(BuiltinType::Uint(match ity { | ||
83 | UintTy::Usize => BuiltinUint::Usize, | ||
84 | UintTy::U8 => BuiltinUint::U8, | ||
85 | UintTy::U16 => BuiltinUint::U16, | ||
86 | UintTy::U32 => BuiltinUint::U32, | ||
87 | UintTy::U64 => BuiltinUint::U64, | ||
88 | UintTy::U128 => BuiltinUint::U128, | ||
89 | })), | ||
90 | _ => None, | ||
91 | } | ||
92 | } | ||
93 | |||
62 | fn as_tuple(&self) -> Option<&Substitution> { | 94 | fn as_tuple(&self) -> Option<&Substitution> { |
63 | match self.kind(&Interner) { | 95 | match self.kind(&Interner) { |
64 | TyKind::Tuple(_, substs) => Some(substs), | 96 | TyKind::Tuple(_, substs) => Some(substs), |
diff --git a/crates/hir_ty/src/consteval.rs b/crates/hir_ty/src/consteval.rs new file mode 100644 index 000000000..e3ceb3d62 --- /dev/null +++ b/crates/hir_ty/src/consteval.rs | |||
@@ -0,0 +1,56 @@ | |||
1 | //! Constant evaluation details | ||
2 | |||
3 | use std::convert::TryInto; | ||
4 | |||
5 | use hir_def::{ | ||
6 | builtin_type::BuiltinUint, | ||
7 | expr::{Expr, Literal}, | ||
8 | type_ref::ConstScalar, | ||
9 | }; | ||
10 | |||
11 | use crate::{Const, ConstData, ConstValue, Interner, TyKind}; | ||
12 | |||
13 | /// Extension trait for [`Const`] | ||
14 | pub trait ConstExt { | ||
15 | /// Is a [`Const`] unknown? | ||
16 | fn is_unknown(&self) -> bool; | ||
17 | } | ||
18 | |||
19 | impl ConstExt for Const { | ||
20 | fn is_unknown(&self) -> bool { | ||
21 | match self.data(&Interner).value { | ||
22 | // interned Unknown | ||
23 | chalk_ir::ConstValue::Concrete(chalk_ir::ConcreteConst { | ||
24 | interned: ConstScalar::Unknown, | ||
25 | }) => true, | ||
26 | |||
27 | // interned concrete anything else | ||
28 | chalk_ir::ConstValue::Concrete(..) => false, | ||
29 | |||
30 | _ => { | ||
31 | log::error!("is_unknown was called on a non-concrete constant value! {:?}", self); | ||
32 | true | ||
33 | } | ||
34 | } | ||
35 | } | ||
36 | } | ||
37 | |||
38 | // FIXME: support more than just evaluating literals | ||
39 | pub fn eval_usize(expr: &Expr) -> Option<u64> { | ||
40 | match expr { | ||
41 | Expr::Literal(Literal::Uint(v, None)) | ||
42 | | Expr::Literal(Literal::Uint(v, Some(BuiltinUint::Usize))) => (*v).try_into().ok(), | ||
43 | _ => None, | ||
44 | } | ||
45 | } | ||
46 | |||
47 | /// Interns a possibly-unknown target usize | ||
48 | pub fn usize_const(value: Option<u64>) -> Const { | ||
49 | ConstData { | ||
50 | ty: TyKind::Scalar(chalk_ir::Scalar::Uint(chalk_ir::UintTy::Usize)).intern(&Interner), | ||
51 | value: ConstValue::Concrete(chalk_ir::ConcreteConst { | ||
52 | interned: value.map(|value| ConstScalar::Usize(value)).unwrap_or(ConstScalar::Unknown), | ||
53 | }), | ||
54 | } | ||
55 | .intern(&Interner) | ||
56 | } | ||
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/diagnostics/match_check.rs b/crates/hir_ty/src/diagnostics/match_check.rs index e9762622f..6ee0529c6 100644 --- a/crates/hir_ty/src/diagnostics/match_check.rs +++ b/crates/hir_ty/src/diagnostics/match_check.rs | |||
@@ -1119,6 +1119,7 @@ fn main() { | |||
1119 | (true, false, true) => (), | 1119 | (true, false, true) => (), |
1120 | (true) => (), | 1120 | (true) => (), |
1121 | } | 1121 | } |
1122 | match (true, false) { (true,) => {} } | ||
1122 | match (0) { () => () } | 1123 | match (0) { () => () } |
1123 | match Unresolved::Bar { Unresolved::Baz => () } | 1124 | match Unresolved::Bar { Unresolved::Baz => () } |
1124 | } | 1125 | } |
diff --git a/crates/hir_ty/src/display.rs b/crates/hir_ty/src/display.rs index 1f6edf7a2..7bbd1a1f7 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 | } |
@@ -962,11 +962,10 @@ impl HirDisplay for TypeRef { | |||
962 | write!(f, "{}", mutability)?; | 962 | write!(f, "{}", mutability)?; |
963 | inner.hir_fmt(f)?; | 963 | inner.hir_fmt(f)?; |
964 | } | 964 | } |
965 | TypeRef::Array(inner) => { | 965 | TypeRef::Array(inner, len) => { |
966 | write!(f, "[")?; | 966 | write!(f, "[")?; |
967 | inner.hir_fmt(f)?; | 967 | inner.hir_fmt(f)?; |
968 | // FIXME: Array length? | 968 | write!(f, "; {}]", len)?; |
969 | write!(f, "; _]")?; | ||
970 | } | 969 | } |
971 | TypeRef::Slice(inner) => { | 970 | TypeRef::Slice(inner) => { |
972 | write!(f, "[")?; | 971 | write!(f, "[")?; |
diff --git a/crates/hir_ty/src/infer/expr.rs b/crates/hir_ty/src/infer/expr.rs index 50497eecb..b6b5a1b75 100644 --- a/crates/hir_ty/src/infer/expr.rs +++ b/crates/hir_ty/src/infer/expr.rs | |||
@@ -15,7 +15,7 @@ use stdx::always; | |||
15 | use syntax::ast::RangeOp; | 15 | use syntax::ast::RangeOp; |
16 | 16 | ||
17 | use crate::{ | 17 | use crate::{ |
18 | autoderef, dummy_usize_const, | 18 | autoderef, consteval, |
19 | lower::lower_to_chalk_mutability, | 19 | lower::lower_to_chalk_mutability, |
20 | mapping::from_chalk, | 20 | mapping::from_chalk, |
21 | method_resolution, op, | 21 | method_resolution, op, |
@@ -717,11 +717,12 @@ impl<'a> InferenceContext<'a> { | |||
717 | _ => self.table.new_type_var(), | 717 | _ => self.table.new_type_var(), |
718 | }; | 718 | }; |
719 | 719 | ||
720 | match array { | 720 | let len = match array { |
721 | Array::ElementList(items) => { | 721 | Array::ElementList(items) => { |
722 | for expr in items.iter() { | 722 | for expr in items.iter() { |
723 | self.infer_expr_coerce(*expr, &Expectation::has_type(elem_ty.clone())); | 723 | self.infer_expr_coerce(*expr, &Expectation::has_type(elem_ty.clone())); |
724 | } | 724 | } |
725 | Some(items.len() as u64) | ||
725 | } | 726 | } |
726 | Array::Repeat { initializer, repeat } => { | 727 | Array::Repeat { initializer, repeat } => { |
727 | self.infer_expr_coerce( | 728 | self.infer_expr_coerce( |
@@ -734,10 +735,13 @@ impl<'a> InferenceContext<'a> { | |||
734 | TyKind::Scalar(Scalar::Uint(UintTy::Usize)).intern(&Interner), | 735 | TyKind::Scalar(Scalar::Uint(UintTy::Usize)).intern(&Interner), |
735 | ), | 736 | ), |
736 | ); | 737 | ); |
738 | |||
739 | let repeat_expr = &self.body.exprs[*repeat]; | ||
740 | consteval::eval_usize(repeat_expr) | ||
737 | } | 741 | } |
738 | } | 742 | }; |
739 | 743 | ||
740 | TyKind::Array(elem_ty, dummy_usize_const()).intern(&Interner) | 744 | TyKind::Array(elem_ty, consteval::usize_const(len)).intern(&Interner) |
741 | } | 745 | } |
742 | Expr::Literal(lit) => match lit { | 746 | Expr::Literal(lit) => match lit { |
743 | Literal::Bool(..) => TyKind::Scalar(Scalar::Bool).intern(&Interner), | 747 | Literal::Bool(..) => TyKind::Scalar(Scalar::Bool).intern(&Interner), |
@@ -745,10 +749,12 @@ impl<'a> InferenceContext<'a> { | |||
745 | TyKind::Ref(Mutability::Not, static_lifetime(), TyKind::Str.intern(&Interner)) | 749 | TyKind::Ref(Mutability::Not, static_lifetime(), TyKind::Str.intern(&Interner)) |
746 | .intern(&Interner) | 750 | .intern(&Interner) |
747 | } | 751 | } |
748 | Literal::ByteString(..) => { | 752 | Literal::ByteString(bs) => { |
749 | let byte_type = TyKind::Scalar(Scalar::Uint(UintTy::U8)).intern(&Interner); | 753 | let byte_type = TyKind::Scalar(Scalar::Uint(UintTy::U8)).intern(&Interner); |
750 | let array_type = | 754 | |
751 | TyKind::Array(byte_type, dummy_usize_const()).intern(&Interner); | 755 | let len = consteval::usize_const(Some(bs.len() as u64)); |
756 | |||
757 | let array_type = TyKind::Array(byte_type, len).intern(&Interner); | ||
752 | TyKind::Ref(Mutability::Not, static_lifetime(), array_type).intern(&Interner) | 758 | TyKind::Ref(Mutability::Not, static_lifetime(), array_type).intern(&Interner) |
753 | } | 759 | } |
754 | Literal::Char(..) => TyKind::Scalar(Scalar::Char).intern(&Interner), | 760 | Literal::Char(..) => TyKind::Scalar(Scalar::Char).intern(&Interner), |
@@ -809,7 +815,7 @@ impl<'a> InferenceContext<'a> { | |||
809 | let ty = self.resolve_ty_as_possible(ty); | 815 | let ty = self.resolve_ty_as_possible(ty); |
810 | self.infer_pat(*pat, &ty, BindingMode::default()); | 816 | self.infer_pat(*pat, &ty, BindingMode::default()); |
811 | } | 817 | } |
812 | Statement::Expr(expr) => { | 818 | Statement::Expr { expr, .. } => { |
813 | self.infer_expr(*expr, &Expectation::none()); | 819 | self.infer_expr(*expr, &Expectation::none()); |
814 | } | 820 | } |
815 | } | 821 | } |
diff --git a/crates/hir_ty/src/infer/pat.rs b/crates/hir_ty/src/infer/pat.rs index aea354cde..60b94a642 100644 --- a/crates/hir_ty/src/infer/pat.rs +++ b/crates/hir_ty/src/infer/pat.rs | |||
@@ -126,11 +126,12 @@ impl<'a> InferenceContext<'a> { | |||
126 | _ => &[], | 126 | _ => &[], |
127 | }; | 127 | }; |
128 | 128 | ||
129 | let (pre, post) = match ellipsis { | 129 | let ((pre, post), n_uncovered_patterns) = match ellipsis { |
130 | Some(idx) => args.split_at(idx), | 130 | Some(idx) => { |
131 | None => (&args[..], &[][..]), | 131 | (args.split_at(idx), expectations.len().saturating_sub(args.len())) |
132 | } | ||
133 | None => ((&args[..], &[][..]), 0), | ||
132 | }; | 134 | }; |
133 | let n_uncovered_patterns = expectations.len().saturating_sub(args.len()); | ||
134 | let err_ty = self.err_ty(); | 135 | let err_ty = self.err_ty(); |
135 | let mut expectations_iter = | 136 | let mut expectations_iter = |
136 | expectations.iter().map(|a| a.assert_ty_ref(&Interner)).chain(repeat(&err_ty)); | 137 | expectations.iter().map(|a| a.assert_ty_ref(&Interner)).chain(repeat(&err_ty)); |
diff --git a/crates/hir_ty/src/interner.rs b/crates/hir_ty/src/interner.rs index a1656115d..7b4119747 100644 --- a/crates/hir_ty/src/interner.rs +++ b/crates/hir_ty/src/interner.rs | |||
@@ -6,6 +6,7 @@ use base_db::salsa::InternId; | |||
6 | use chalk_ir::{Goal, GoalData}; | 6 | use chalk_ir::{Goal, GoalData}; |
7 | use hir_def::{ | 7 | use hir_def::{ |
8 | intern::{impl_internable, InternStorage, Internable, Interned}, | 8 | intern::{impl_internable, InternStorage, Internable, Interned}, |
9 | type_ref::ConstScalar, | ||
9 | TypeAliasId, | 10 | TypeAliasId, |
10 | }; | 11 | }; |
11 | use smallvec::SmallVec; | 12 | use smallvec::SmallVec; |
@@ -31,6 +32,7 @@ impl_internable!( | |||
31 | InternedWrapper<chalk_ir::TyData<Interner>>, | 32 | InternedWrapper<chalk_ir::TyData<Interner>>, |
32 | InternedWrapper<chalk_ir::LifetimeData<Interner>>, | 33 | InternedWrapper<chalk_ir::LifetimeData<Interner>>, |
33 | InternedWrapper<chalk_ir::ConstData<Interner>>, | 34 | InternedWrapper<chalk_ir::ConstData<Interner>>, |
35 | InternedWrapper<ConstScalar>, | ||
34 | InternedWrapper<Vec<chalk_ir::CanonicalVarKind<Interner>>>, | 36 | InternedWrapper<Vec<chalk_ir::CanonicalVarKind<Interner>>>, |
35 | InternedWrapper<Vec<chalk_ir::ProgramClause<Interner>>>, | 37 | InternedWrapper<Vec<chalk_ir::ProgramClause<Interner>>>, |
36 | InternedWrapper<Vec<chalk_ir::QuantifiedWhereClause<Interner>>>, | 38 | InternedWrapper<Vec<chalk_ir::QuantifiedWhereClause<Interner>>>, |
@@ -41,7 +43,7 @@ impl chalk_ir::interner::Interner for Interner { | |||
41 | type InternedType = Interned<InternedWrapper<chalk_ir::TyData<Interner>>>; | 43 | type InternedType = Interned<InternedWrapper<chalk_ir::TyData<Interner>>>; |
42 | type InternedLifetime = Interned<InternedWrapper<chalk_ir::LifetimeData<Self>>>; | 44 | type InternedLifetime = Interned<InternedWrapper<chalk_ir::LifetimeData<Self>>>; |
43 | type InternedConst = Interned<InternedWrapper<chalk_ir::ConstData<Self>>>; | 45 | type InternedConst = Interned<InternedWrapper<chalk_ir::ConstData<Self>>>; |
44 | type InternedConcreteConst = (); | 46 | type InternedConcreteConst = ConstScalar; |
45 | type InternedGenericArg = chalk_ir::GenericArgData<Self>; | 47 | type InternedGenericArg = chalk_ir::GenericArgData<Self>; |
46 | type InternedGoal = Arc<GoalData<Self>>; | 48 | type InternedGoal = Arc<GoalData<Self>>; |
47 | type InternedGoals = Vec<Goal<Self>>; | 49 | type InternedGoals = Vec<Goal<Self>>; |
@@ -245,10 +247,15 @@ impl chalk_ir::interner::Interner for Interner { | |||
245 | fn const_eq( | 247 | fn const_eq( |
246 | &self, | 248 | &self, |
247 | _ty: &Self::InternedType, | 249 | _ty: &Self::InternedType, |
248 | _c1: &Self::InternedConcreteConst, | 250 | c1: &Self::InternedConcreteConst, |
249 | _c2: &Self::InternedConcreteConst, | 251 | c2: &Self::InternedConcreteConst, |
250 | ) -> bool { | 252 | ) -> bool { |
251 | true | 253 | match (c1, c2) { |
254 | (&ConstScalar::Usize(a), &ConstScalar::Usize(b)) => a == b, | ||
255 | // we were previously assuming this to be true, I'm not whether true or false on | ||
256 | // unknown values is safer. | ||
257 | (_, _) => true, | ||
258 | } | ||
252 | } | 259 | } |
253 | 260 | ||
254 | fn intern_generic_arg( | 261 | fn intern_generic_arg( |
diff --git a/crates/hir_ty/src/lib.rs b/crates/hir_ty/src/lib.rs index 0505fa4ae..15b61bedc 100644 --- a/crates/hir_ty/src/lib.rs +++ b/crates/hir_ty/src/lib.rs | |||
@@ -10,6 +10,7 @@ mod autoderef; | |||
10 | mod builder; | 10 | mod builder; |
11 | mod chalk_db; | 11 | mod chalk_db; |
12 | mod chalk_ext; | 12 | mod chalk_ext; |
13 | pub mod consteval; | ||
13 | mod infer; | 14 | mod infer; |
14 | mod interner; | 15 | mod interner; |
15 | mod lower; | 16 | mod lower; |
@@ -37,7 +38,11 @@ use chalk_ir::{ | |||
37 | interner::HasInterner, | 38 | interner::HasInterner, |
38 | UintTy, | 39 | UintTy, |
39 | }; | 40 | }; |
40 | use hir_def::{expr::ExprId, type_ref::Rawness, TypeParamId}; | 41 | use hir_def::{ |
42 | expr::ExprId, | ||
43 | type_ref::{ConstScalar, Rawness}, | ||
44 | TypeParamId, | ||
45 | }; | ||
41 | 46 | ||
42 | use crate::{db::HirDatabase, display::HirDisplay, utils::generics}; | 47 | use crate::{db::HirDatabase, display::HirDisplay, utils::generics}; |
43 | 48 | ||
@@ -250,7 +255,9 @@ pub fn dummy_usize_const() -> Const { | |||
250 | let usize_ty = chalk_ir::TyKind::Scalar(Scalar::Uint(UintTy::Usize)).intern(&Interner); | 255 | let usize_ty = chalk_ir::TyKind::Scalar(Scalar::Uint(UintTy::Usize)).intern(&Interner); |
251 | chalk_ir::ConstData { | 256 | chalk_ir::ConstData { |
252 | ty: usize_ty, | 257 | ty: usize_ty, |
253 | value: chalk_ir::ConstValue::Concrete(chalk_ir::ConcreteConst { interned: () }), | 258 | value: chalk_ir::ConstValue::Concrete(chalk_ir::ConcreteConst { |
259 | interned: ConstScalar::Unknown, | ||
260 | }), | ||
254 | } | 261 | } |
255 | .intern(&Interner) | 262 | .intern(&Interner) |
256 | } | 263 | } |
diff --git a/crates/hir_ty/src/lower.rs b/crates/hir_ty/src/lower.rs index c99dd8d0a..bd8bb6028 100644 --- a/crates/hir_ty/src/lower.rs +++ b/crates/hir_ty/src/lower.rs | |||
@@ -29,8 +29,8 @@ use stdx::impl_from; | |||
29 | use syntax::ast; | 29 | use syntax::ast; |
30 | 30 | ||
31 | use crate::{ | 31 | use crate::{ |
32 | consteval, | ||
32 | db::HirDatabase, | 33 | db::HirDatabase, |
33 | dummy_usize_const, | ||
34 | mapping::ToChalk, | 34 | mapping::ToChalk, |
35 | static_lifetime, to_assoc_type_id, to_chalk_trait_id, to_placeholder_idx, | 35 | static_lifetime, to_assoc_type_id, to_chalk_trait_id, to_placeholder_idx, |
36 | utils::{ | 36 | utils::{ |
@@ -172,9 +172,12 @@ impl<'a> TyLoweringContext<'a> { | |||
172 | let inner_ty = self.lower_ty(inner); | 172 | let inner_ty = self.lower_ty(inner); |
173 | TyKind::Raw(lower_to_chalk_mutability(*mutability), inner_ty).intern(&Interner) | 173 | TyKind::Raw(lower_to_chalk_mutability(*mutability), inner_ty).intern(&Interner) |
174 | } | 174 | } |
175 | TypeRef::Array(inner) => { | 175 | TypeRef::Array(inner, len) => { |
176 | let inner_ty = self.lower_ty(inner); | 176 | let inner_ty = self.lower_ty(inner); |
177 | TyKind::Array(inner_ty, dummy_usize_const()).intern(&Interner) | 177 | |
178 | let const_len = consteval::usize_const(len.as_usize()); | ||
179 | |||
180 | TyKind::Array(inner_ty, const_len).intern(&Interner) | ||
178 | } | 181 | } |
179 | TypeRef::Slice(inner) => { | 182 | TypeRef::Slice(inner) => { |
180 | let inner_ty = self.lower_ty(inner); | 183 | let inner_ty = self.lower_ty(inner); |
diff --git a/crates/hir_ty/src/tests/coercion.rs b/crates/hir_ty/src/tests/coercion.rs index 63d9d4e0b..190471069 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 {}': ! |
@@ -64,43 +64,43 @@ fn coerce_places() { | |||
64 | 81..92 '{ loop {} }': T | 64 | 81..92 '{ loop {} }': T |
65 | 83..90 'loop {}': ! | 65 | 83..90 'loop {}': ! |
66 | 88..90 '{}': () | 66 | 88..90 '{}': () |
67 | 121..132 '{ loop {} }': *mut [T; _] | 67 | 121..132 '{ loop {} }': *mut [T; 2] |
68 | 123..130 'loop {}': ! | 68 | 123..130 'loop {}': ! |
69 | 128..130 '{}': () | 69 | 128..130 '{}': () |
70 | 159..172 '{ gen() }': *mut [U] | 70 | 159..172 '{ gen() }': *mut [U] |
71 | 165..168 'gen': fn gen<U>() -> *mut [U; _] | 71 | 165..168 'gen': fn gen<U>() -> *mut [U; 2] |
72 | 165..170 'gen()': *mut [U; _] | 72 | 165..170 'gen()': *mut [U; 2] |
73 | 185..419 '{ ...rr); }': () | 73 | 185..419 '{ ...rr); }': () |
74 | 195..198 'arr': &[u8; _] | 74 | 195..198 'arr': &[u8; 1] |
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; 1] |
80 | 249..250 'b': u8 | 80 | 249..250 'b': u8 |
81 | 253..254 'f': fn f<u8>(&[u8]) -> u8 | 81 | 253..254 'f': fn f<u8>(&[u8]) -> u8 |
82 | 253..259 'f(arr)': u8 | 82 | 253..259 'f(arr)': u8 |
83 | 255..258 'arr': &[u8; _] | 83 | 255..258 'arr': &[u8; 1] |
84 | 269..270 'c': &[u8] | 84 | 269..270 'c': &[u8] |
85 | 279..286 '{ arr }': &[u8] | 85 | 279..286 '{ arr }': &[u8] |
86 | 281..284 'arr': &[u8; _] | 86 | 281..284 'arr': &[u8; 1] |
87 | 296..297 'd': u8 | 87 | 296..297 'd': u8 |
88 | 300..301 'g': fn g<u8>(S<&[u8]>) -> u8 | 88 | 300..301 'g': fn g<u8>(S<&[u8]>) -> u8 |
89 | 300..315 'g(S { a: arr })': u8 | 89 | 300..315 'g(S { a: arr })': u8 |
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; 1] |
92 | 325..326 'e': [&[u8]; _] | 92 | 325..326 'e': [&[u8]; 1] |
93 | 340..345 '[arr]': [&[u8]; _] | 93 | 340..345 '[arr]': [&[u8]; 1] |
94 | 341..344 'arr': &[u8; _] | 94 | 341..344 'arr': &[u8; 1] |
95 | 355..356 'f': [&[u8]; _] | 95 | 355..356 'f': [&[u8]; 2] |
96 | 370..378 '[arr; 2]': [&[u8]; _] | 96 | 370..378 '[arr; 2]': [&[u8]; 2] |
97 | 371..374 'arr': &[u8; _] | 97 | 371..374 'arr': &[u8; 1] |
98 | 376..377 '2': usize | 98 | 376..377 '2': usize |
99 | 388..389 'g': (&[u8], &[u8]) | 99 | 388..389 'g': (&[u8], &[u8]) |
100 | 406..416 '(arr, arr)': (&[u8], &[u8]) | 100 | 406..416 '(arr, arr)': (&[u8], &[u8]) |
101 | 407..410 'arr': &[u8; _] | 101 | 407..410 'arr': &[u8; 1] |
102 | 412..415 'arr': &[u8; _] | 102 | 412..415 'arr': &[u8; 1] |
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 | ||
@@ -159,7 +159,7 @@ fn infer_custom_coerce_unsized() { | |||
159 | impl<'a, 'b: 'a, T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<&'a U> for &'b T {} | 159 | impl<'a, 'b: 'a, T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<&'a U> for &'b T {} |
160 | impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<*mut U> for *mut T {} | 160 | impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<*mut U> for *mut T {} |
161 | "#, | 161 | "#, |
162 | expect![[r" | 162 | expect![[r#" |
163 | 257..258 'x': A<[T]> | 163 | 257..258 'x': A<[T]> |
164 | 278..283 '{ x }': A<[T]> | 164 | 278..283 '{ x }': A<[T]> |
165 | 280..281 'x': A<[T]> | 165 | 280..281 'x': A<[T]> |
@@ -169,23 +169,23 @@ fn infer_custom_coerce_unsized() { | |||
169 | 333..334 'x': C<[T]> | 169 | 333..334 'x': C<[T]> |
170 | 354..359 '{ x }': C<[T]> | 170 | 354..359 '{ x }': C<[T]> |
171 | 356..357 'x': C<[T]> | 171 | 356..357 'x': C<[T]> |
172 | 369..370 'a': A<[u8; _]> | 172 | 369..370 'a': A<[u8; 2]> |
173 | 384..385 'b': B<[u8; _]> | 173 | 384..385 'b': B<[u8; 2]> |
174 | 399..400 'c': C<[u8; _]> | 174 | 399..400 'c': C<[u8; 2]> |
175 | 414..480 '{ ...(c); }': () | 175 | 414..480 '{ ...(c); }': () |
176 | 424..425 'd': A<[{unknown}]> | 176 | 424..425 'd': A<[{unknown}]> |
177 | 428..432 'foo1': fn foo1<{unknown}>(A<[{unknown}]>) -> A<[{unknown}]> | 177 | 428..432 'foo1': fn foo1<{unknown}>(A<[{unknown}]>) -> A<[{unknown}]> |
178 | 428..435 'foo1(a)': A<[{unknown}]> | 178 | 428..435 'foo1(a)': A<[{unknown}]> |
179 | 433..434 'a': A<[u8; _]> | 179 | 433..434 'a': A<[u8; 2]> |
180 | 445..446 'e': B<[u8]> | 180 | 445..446 'e': B<[u8]> |
181 | 449..453 'foo2': fn foo2<u8>(B<[u8]>) -> B<[u8]> | 181 | 449..453 'foo2': fn foo2<u8>(B<[u8]>) -> B<[u8]> |
182 | 449..456 'foo2(b)': B<[u8]> | 182 | 449..456 'foo2(b)': B<[u8]> |
183 | 454..455 'b': B<[u8; _]> | 183 | 454..455 'b': B<[u8; 2]> |
184 | 466..467 'f': C<[u8]> | 184 | 466..467 'f': C<[u8]> |
185 | 470..474 'foo3': fn foo3<u8>(C<[u8]>) -> C<[u8]> | 185 | 470..474 'foo3': fn foo3<u8>(C<[u8]>) -> C<[u8]> |
186 | 470..477 'foo3(c)': C<[u8]> | 186 | 470..477 'foo3(c)': C<[u8]> |
187 | 475..476 'c': C<[u8; _]> | 187 | 475..476 'c': C<[u8; 2]> |
188 | "]], | 188 | "#]], |
189 | ); | 189 | ); |
190 | } | 190 | } |
191 | 191 | ||
@@ -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..787647e9f 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] |
@@ -345,19 +345,19 @@ fn infer_pattern_match_arr() { | |||
345 | "#, | 345 | "#, |
346 | expect![[r#" | 346 | expect![[r#" |
347 | 10..179 '{ ... } }': () | 347 | 10..179 '{ ... } }': () |
348 | 20..23 'arr': [f64; _] | 348 | 20..23 'arr': [f64; 2] |
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 ... }': () |
353 | 58..61 'arr': [f64; _] | 353 | 58..61 'arr': [f64; 2] |
354 | 72..80 '[1.0, a]': [f64; _] | 354 | 72..80 '[1.0, a]': [f64; 2] |
355 | 73..76 '1.0': f64 | 355 | 73..76 '1.0': f64 |
356 | 73..76 '1.0': f64 | 356 | 73..76 '1.0': f64 |
357 | 78..79 'a': f64 | 357 | 78..79 'a': f64 |
358 | 84..110 '{ ... }': () | 358 | 84..110 '{ ... }': () |
359 | 98..99 'a': f64 | 359 | 98..99 'a': f64 |
360 | 120..126 '[b, c]': [f64; _] | 360 | 120..126 '[b, c]': [f64; 2] |
361 | 121..122 'b': f64 | 361 | 121..122 'b': f64 |
362 | 124..125 'c': f64 | 362 | 124..125 'c': f64 |
363 | 130..171 '{ ... }': () | 363 | 130..171 '{ ... }': () |
@@ -732,7 +732,7 @@ fn foo(tuple: (u8, i16, f32)) { | |||
732 | 111..112 'a': u8 | 732 | 111..112 'a': u8 |
733 | 114..115 'b': i16 | 733 | 114..115 'b': i16 |
734 | 124..126 '{}': () | 734 | 124..126 '{}': () |
735 | 136..142 '(a, b)': (u8, i16, f32) | 735 | 136..142 '(a, b)': (u8, i16) |
736 | 137..138 'a': u8 | 736 | 137..138 'a': u8 |
737 | 140..141 'b': i16 | 737 | 140..141 'b': i16 |
738 | 146..161 '{/*too short*/}': () | 738 | 146..161 '{/*too short*/}': () |
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] | ||
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/hir_ty/src/tests/simple.rs b/crates/hir_ty/src/tests/simple.rs index 0eefd70f2..a9cd42186 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::*; |
@@ -488,23 +488,34 @@ fn infer_literals() { | |||
488 | mod foo {} | 488 | mod foo {} |
489 | "#; | 489 | "#; |
490 | br#"yolo"#; | 490 | br#"yolo"#; |
491 | let a = b"a\x20b\ | ||
492 | c"; | ||
493 | let b = br"g\ | ||
494 | h"; | ||
495 | let c = br#"x"\"yb"#; | ||
491 | } | 496 | } |
492 | "##, | 497 | "##, |
493 | expect![[r##" | 498 | expect![[r##" |
494 | 10..216 '{ ...o"#; }': () | 499 | 18..478 '{ ... }': () |
495 | 16..20 '5i32': i32 | 500 | 32..36 '5i32': i32 |
496 | 26..30 '5f32': f32 | 501 | 50..54 '5f32': f32 |
497 | 36..40 '5f64': f64 | 502 | 68..72 '5f64': f64 |
498 | 46..53 '"hello"': &str | 503 | 86..93 '"hello"': &str |
499 | 59..67 'b"bytes"': &[u8; _] | 504 | 107..115 'b"bytes"': &[u8; 5] |
500 | 73..76 ''c'': char | 505 | 129..132 ''c'': char |
501 | 82..86 'b'b'': u8 | 506 | 146..150 'b'b'': u8 |
502 | 92..96 '3.14': f64 | 507 | 164..168 '3.14': f64 |
503 | 102..106 '5000': i32 | 508 | 182..186 '5000': i32 |
504 | 112..117 'false': bool | 509 | 200..205 'false': bool |
505 | 123..127 'true': bool | 510 | 219..223 'true': bool |
506 | 133..197 'r#" ... "#': &str | 511 | 237..333 'r#" ... "#': &str |
507 | 203..213 'br#"yolo"#': &[u8; _] | 512 | 347..357 'br#"yolo"#': &[u8; 4] |
513 | 375..376 'a': &[u8; 4] | ||
514 | 379..403 'b"a\x2... c"': &[u8; 4] | ||
515 | 421..422 'b': &[u8; 4] | ||
516 | 425..433 'br"g\ h"': &[u8; 4] | ||
517 | 451..452 'c': &[u8; 6] | ||
518 | 455..467 'br#"x"\"yb"#': &[u8; 6] | ||
508 | "##]], | 519 | "##]], |
509 | ); | 520 | ); |
510 | } | 521 | } |
@@ -1260,61 +1271,69 @@ fn infer_array() { | |||
1260 | 1271 | ||
1261 | let b = [a, ["b"]]; | 1272 | let b = [a, ["b"]]; |
1262 | let x: [u8; 0] = []; | 1273 | let x: [u8; 0] = []; |
1274 | // FIXME: requires const evaluation/taking type from rhs somehow | ||
1275 | let y: [u8; 2+2] = [1,2,3,4]; | ||
1263 | } | 1276 | } |
1264 | "#, | 1277 | "#, |
1265 | expect![[r#" | 1278 | expect![[r#" |
1266 | 8..9 'x': &str | 1279 | 8..9 'x': &str |
1267 | 17..18 'y': isize | 1280 | 17..18 'y': isize |
1268 | 27..292 '{ ... []; }': () | 1281 | 27..395 '{ ...,4]; }': () |
1269 | 37..38 'a': [&str; _] | 1282 | 37..38 'a': [&str; 1] |
1270 | 41..44 '[x]': [&str; _] | 1283 | 41..44 '[x]': [&str; 1] |
1271 | 42..43 'x': &str | 1284 | 42..43 'x': &str |
1272 | 54..55 'b': [[&str; _]; _] | 1285 | 54..55 'b': [[&str; 1]; 2] |
1273 | 58..64 '[a, a]': [[&str; _]; _] | 1286 | 58..64 '[a, a]': [[&str; 1]; 2] |
1274 | 59..60 'a': [&str; _] | 1287 | 59..60 'a': [&str; 1] |
1275 | 62..63 'a': [&str; _] | 1288 | 62..63 'a': [&str; 1] |
1276 | 74..75 'c': [[[&str; _]; _]; _] | 1289 | 74..75 'c': [[[&str; 1]; 2]; 2] |
1277 | 78..84 '[b, b]': [[[&str; _]; _]; _] | 1290 | 78..84 '[b, b]': [[[&str; 1]; 2]; 2] |
1278 | 79..80 'b': [[&str; _]; _] | 1291 | 79..80 'b': [[&str; 1]; 2] |
1279 | 82..83 'b': [[&str; _]; _] | 1292 | 82..83 'b': [[&str; 1]; 2] |
1280 | 95..96 'd': [isize; _] | 1293 | 95..96 'd': [isize; 4] |
1281 | 99..111 '[y, 1, 2, 3]': [isize; _] | 1294 | 99..111 '[y, 1, 2, 3]': [isize; 4] |
1282 | 100..101 'y': isize | 1295 | 100..101 'y': isize |
1283 | 103..104 '1': isize | 1296 | 103..104 '1': isize |
1284 | 106..107 '2': isize | 1297 | 106..107 '2': isize |
1285 | 109..110 '3': isize | 1298 | 109..110 '3': isize |
1286 | 121..122 'd': [isize; _] | 1299 | 121..122 'd': [isize; 4] |
1287 | 125..137 '[1, y, 2, 3]': [isize; _] | 1300 | 125..137 '[1, y, 2, 3]': [isize; 4] |
1288 | 126..127 '1': isize | 1301 | 126..127 '1': isize |
1289 | 129..130 'y': isize | 1302 | 129..130 'y': isize |
1290 | 132..133 '2': isize | 1303 | 132..133 '2': isize |
1291 | 135..136 '3': isize | 1304 | 135..136 '3': isize |
1292 | 147..148 'e': [isize; _] | 1305 | 147..148 'e': [isize; 1] |
1293 | 151..154 '[y]': [isize; _] | 1306 | 151..154 '[y]': [isize; 1] |
1294 | 152..153 'y': isize | 1307 | 152..153 'y': isize |
1295 | 164..165 'f': [[isize; _]; _] | 1308 | 164..165 'f': [[isize; 4]; 2] |
1296 | 168..174 '[d, d]': [[isize; _]; _] | 1309 | 168..174 '[d, d]': [[isize; 4]; 2] |
1297 | 169..170 'd': [isize; _] | 1310 | 169..170 'd': [isize; 4] |
1298 | 172..173 'd': [isize; _] | 1311 | 172..173 'd': [isize; 4] |
1299 | 184..185 'g': [[isize; _]; _] | 1312 | 184..185 'g': [[isize; 1]; 2] |
1300 | 188..194 '[e, e]': [[isize; _]; _] | 1313 | 188..194 '[e, e]': [[isize; 1]; 2] |
1301 | 189..190 'e': [isize; _] | 1314 | 189..190 'e': [isize; 1] |
1302 | 192..193 'e': [isize; _] | 1315 | 192..193 'e': [isize; 1] |
1303 | 205..206 'h': [i32; _] | 1316 | 205..206 'h': [i32; 2] |
1304 | 209..215 '[1, 2]': [i32; _] | 1317 | 209..215 '[1, 2]': [i32; 2] |
1305 | 210..211 '1': i32 | 1318 | 210..211 '1': i32 |
1306 | 213..214 '2': i32 | 1319 | 213..214 '2': i32 |
1307 | 225..226 'i': [&str; _] | 1320 | 225..226 'i': [&str; 2] |
1308 | 229..239 '["a", "b"]': [&str; _] | 1321 | 229..239 '["a", "b"]': [&str; 2] |
1309 | 230..233 '"a"': &str | 1322 | 230..233 '"a"': &str |
1310 | 235..238 '"b"': &str | 1323 | 235..238 '"b"': &str |
1311 | 250..251 'b': [[&str; _]; _] | 1324 | 250..251 'b': [[&str; 1]; 2] |
1312 | 254..264 '[a, ["b"]]': [[&str; _]; _] | 1325 | 254..264 '[a, ["b"]]': [[&str; 1]; 2] |
1313 | 255..256 'a': [&str; _] | 1326 | 255..256 'a': [&str; 1] |
1314 | 258..263 '["b"]': [&str; _] | 1327 | 258..263 '["b"]': [&str; 1] |
1315 | 259..262 '"b"': &str | 1328 | 259..262 '"b"': &str |
1316 | 274..275 'x': [u8; _] | 1329 | 274..275 'x': [u8; 0] |
1317 | 287..289 '[]': [u8; _] | 1330 | 287..289 '[]': [u8; 0] |
1331 | 368..369 'y': [u8; _] | ||
1332 | 383..392 '[1,2,3,4]': [u8; 4] | ||
1333 | 384..385 '1': u8 | ||
1334 | 386..387 '2': u8 | ||
1335 | 388..389 '3': u8 | ||
1336 | 390..391 '4': u8 | ||
1318 | "#]], | 1337 | "#]], |
1319 | ); | 1338 | ); |
1320 | } | 1339 | } |
@@ -2409,40 +2428,40 @@ fn infer_operator_overload() { | |||
2409 | 320..422 '{ ... }': V2 | 2428 | 320..422 '{ ... }': V2 |
2410 | 334..335 'x': f32 | 2429 | 334..335 'x': f32 |
2411 | 338..342 'self': V2 | 2430 | 338..342 'self': V2 |
2412 | 338..344 'self.0': [f32; _] | 2431 | 338..344 'self.0': [f32; 2] |
2413 | 338..347 'self.0[0]': {unknown} | 2432 | 338..347 'self.0[0]': {unknown} |
2414 | 338..358 'self.0...s.0[0]': f32 | 2433 | 338..358 'self.0...s.0[0]': f32 |
2415 | 345..346 '0': i32 | 2434 | 345..346 '0': i32 |
2416 | 350..353 'rhs': V2 | 2435 | 350..353 'rhs': V2 |
2417 | 350..355 'rhs.0': [f32; _] | 2436 | 350..355 'rhs.0': [f32; 2] |
2418 | 350..358 'rhs.0[0]': {unknown} | 2437 | 350..358 'rhs.0[0]': {unknown} |
2419 | 356..357 '0': i32 | 2438 | 356..357 '0': i32 |
2420 | 372..373 'y': f32 | 2439 | 372..373 'y': f32 |
2421 | 376..380 'self': V2 | 2440 | 376..380 'self': V2 |
2422 | 376..382 'self.0': [f32; _] | 2441 | 376..382 'self.0': [f32; 2] |
2423 | 376..385 'self.0[1]': {unknown} | 2442 | 376..385 'self.0[1]': {unknown} |
2424 | 376..396 'self.0...s.0[1]': f32 | 2443 | 376..396 'self.0...s.0[1]': f32 |
2425 | 383..384 '1': i32 | 2444 | 383..384 '1': i32 |
2426 | 388..391 'rhs': V2 | 2445 | 388..391 'rhs': V2 |
2427 | 388..393 'rhs.0': [f32; _] | 2446 | 388..393 'rhs.0': [f32; 2] |
2428 | 388..396 'rhs.0[1]': {unknown} | 2447 | 388..396 'rhs.0[1]': {unknown} |
2429 | 394..395 '1': i32 | 2448 | 394..395 '1': i32 |
2430 | 406..408 'V2': V2([f32; _]) -> V2 | 2449 | 406..408 'V2': V2([f32; 2]) -> V2 |
2431 | 406..416 'V2([x, y])': V2 | 2450 | 406..416 'V2([x, y])': V2 |
2432 | 409..415 '[x, y]': [f32; _] | 2451 | 409..415 '[x, y]': [f32; 2] |
2433 | 410..411 'x': f32 | 2452 | 410..411 'x': f32 |
2434 | 413..414 'y': f32 | 2453 | 413..414 'y': f32 |
2435 | 436..519 '{ ... vb; }': () | 2454 | 436..519 '{ ... vb; }': () |
2436 | 446..448 'va': V2 | 2455 | 446..448 'va': V2 |
2437 | 451..453 'V2': V2([f32; _]) -> V2 | 2456 | 451..453 'V2': V2([f32; 2]) -> V2 |
2438 | 451..465 'V2([0.0, 1.0])': V2 | 2457 | 451..465 'V2([0.0, 1.0])': V2 |
2439 | 454..464 '[0.0, 1.0]': [f32; _] | 2458 | 454..464 '[0.0, 1.0]': [f32; 2] |
2440 | 455..458 '0.0': f32 | 2459 | 455..458 '0.0': f32 |
2441 | 460..463 '1.0': f32 | 2460 | 460..463 '1.0': f32 |
2442 | 475..477 'vb': V2 | 2461 | 475..477 'vb': V2 |
2443 | 480..482 'V2': V2([f32; _]) -> V2 | 2462 | 480..482 'V2': V2([f32; 2]) -> V2 |
2444 | 480..494 'V2([0.0, 1.0])': V2 | 2463 | 480..494 'V2([0.0, 1.0])': V2 |
2445 | 483..493 '[0.0, 1.0]': [f32; _] | 2464 | 483..493 '[0.0, 1.0]': [f32; 2] |
2446 | 484..487 '0.0': f32 | 2465 | 484..487 '0.0': f32 |
2447 | 489..492 '1.0': f32 | 2466 | 489..492 '1.0': f32 |
2448 | 505..506 'r': V2 | 2467 | 505..506 'r': V2 |
@@ -2593,8 +2612,8 @@ fn test() { | |||
2593 | 658..661 'vec': Vec<i32, Global> | 2612 | 658..661 'vec': Vec<i32, Global> |
2594 | 664..679 '<[_]>::into_vec': fn into_vec<i32, Global>(Box<[i32], Global>) -> Vec<i32, Global> | 2613 | 664..679 '<[_]>::into_vec': fn into_vec<i32, Global>(Box<[i32], Global>) -> Vec<i32, Global> |
2595 | 664..691 '<[_]>:...1i32])': Vec<i32, Global> | 2614 | 664..691 '<[_]>:...1i32])': Vec<i32, Global> |
2596 | 680..690 'box [1i32]': Box<[i32; _], Global> | 2615 | 680..690 'box [1i32]': Box<[i32; 1], Global> |
2597 | 684..690 '[1i32]': [i32; _] | 2616 | 684..690 '[1i32]': [i32; 1] |
2598 | 685..689 '1i32': i32 | 2617 | 685..689 '1i32': i32 |
2599 | "#]], | 2618 | "#]], |
2600 | ) | 2619 | ) |
diff --git a/crates/hir_ty/src/tests/traits.rs b/crates/hir_ty/src/tests/traits.rs index ffc7c8ef4..f80cf9879 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 |
@@ -3474,3 +3474,100 @@ fn main(){ | |||
3474 | "#]], | 3474 | "#]], |
3475 | ) | 3475 | ) |
3476 | } | 3476 | } |
3477 | |||
3478 | #[test] | ||
3479 | fn array_length() { | ||
3480 | check_infer( | ||
3481 | r#" | ||
3482 | trait T { | ||
3483 | type Output; | ||
3484 | fn do_thing(&self) -> Self::Output; | ||
3485 | } | ||
3486 | |||
3487 | impl T for [u8; 4] { | ||
3488 | type Output = usize; | ||
3489 | fn do_thing(&self) -> Self::Output { | ||
3490 | 2 | ||
3491 | } | ||
3492 | } | ||
3493 | |||
3494 | impl T for [u8; 2] { | ||
3495 | type Output = u8; | ||
3496 | fn do_thing(&self) -> Self::Output { | ||
3497 | 2 | ||
3498 | } | ||
3499 | } | ||
3500 | |||
3501 | fn main() { | ||
3502 | let v = [0u8; 2]; | ||
3503 | let v2 = v.do_thing(); | ||
3504 | let v3 = [0u8; 4]; | ||
3505 | let v4 = v3.do_thing(); | ||
3506 | } | ||
3507 | "#, | ||
3508 | expect![[r#" | ||
3509 | 44..48 'self': &Self | ||
3510 | 133..137 'self': &[u8; 4] | ||
3511 | 155..172 '{ ... }': usize | ||
3512 | 165..166 '2': usize | ||
3513 | 236..240 'self': &[u8; 2] | ||
3514 | 258..275 '{ ... }': u8 | ||
3515 | 268..269 '2': u8 | ||
3516 | 289..392 '{ ...g(); }': () | ||
3517 | 299..300 'v': [u8; 2] | ||
3518 | 303..311 '[0u8; 2]': [u8; 2] | ||
3519 | 304..307 '0u8': u8 | ||
3520 | 309..310 '2': usize | ||
3521 | 321..323 'v2': u8 | ||
3522 | 326..327 'v': [u8; 2] | ||
3523 | 326..338 'v.do_thing()': u8 | ||
3524 | 348..350 'v3': [u8; 4] | ||
3525 | 353..361 '[0u8; 4]': [u8; 4] | ||
3526 | 354..357 '0u8': u8 | ||
3527 | 359..360 '4': usize | ||
3528 | 371..373 'v4': usize | ||
3529 | 376..378 'v3': [u8; 4] | ||
3530 | 376..389 'v3.do_thing()': usize | ||
3531 | "#]], | ||
3532 | ) | ||
3533 | } | ||
3534 | |||
3535 | // FIXME: We should infer the length of the returned array :) | ||
3536 | #[test] | ||
3537 | fn const_generics() { | ||
3538 | check_infer( | ||
3539 | r#" | ||
3540 | trait T { | ||
3541 | type Output; | ||
3542 | fn do_thing(&self) -> Self::Output; | ||
3543 | } | ||
3544 | |||
3545 | impl<const L: usize> T for [u8; L] { | ||
3546 | type Output = [u8; L]; | ||
3547 | fn do_thing(&self) -> Self::Output { | ||
3548 | *self | ||
3549 | } | ||
3550 | } | ||
3551 | |||
3552 | fn main() { | ||
3553 | let v = [0u8; 2]; | ||
3554 | let v2 = v.do_thing(); | ||
3555 | } | ||
3556 | "#, | ||
3557 | expect![[r#" | ||
3558 | 44..48 'self': &Self | ||
3559 | 151..155 'self': &[u8; _] | ||
3560 | 173..194 '{ ... }': [u8; _] | ||
3561 | 183..188 '*self': [u8; _] | ||
3562 | 184..188 'self': &[u8; _] | ||
3563 | 208..260 '{ ...g(); }': () | ||
3564 | 218..219 'v': [u8; 2] | ||
3565 | 222..230 '[0u8; 2]': [u8; 2] | ||
3566 | 223..226 '0u8': u8 | ||
3567 | 228..229 '2': usize | ||
3568 | 240..242 'v2': [u8; _] | ||
3569 | 245..246 'v': [u8; 2] | ||
3570 | 245..257 'v.do_thing()': [u8; _] | ||
3571 | "#]], | ||
3572 | ) | ||
3573 | } | ||
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/diagnostics.rs b/crates/ide/src/diagnostics.rs index 273d8cfbb..4172f6cae 100644 --- a/crates/ide/src/diagnostics.rs +++ b/crates/ide/src/diagnostics.rs | |||
@@ -375,7 +375,7 @@ mod tests { | |||
375 | assert_eq!(diagnostics.len(), 0, "unexpected diagnostics:\n{:#?}", diagnostics); | 375 | assert_eq!(diagnostics.len(), 0, "unexpected diagnostics:\n{:#?}", diagnostics); |
376 | } | 376 | } |
377 | 377 | ||
378 | fn check_expect(ra_fixture: &str, expect: Expect) { | 378 | pub(crate) fn check_expect(ra_fixture: &str, expect: Expect) { |
379 | let (analysis, file_id) = fixture::file(ra_fixture); | 379 | let (analysis, file_id) = fixture::file(ra_fixture); |
380 | let diagnostics = analysis | 380 | let diagnostics = analysis |
381 | .diagnostics(&DiagnosticsConfig::default(), AssistResolveStrategy::All, file_id) | 381 | .diagnostics(&DiagnosticsConfig::default(), AssistResolveStrategy::All, file_id) |
@@ -384,374 +384,6 @@ mod tests { | |||
384 | } | 384 | } |
385 | 385 | ||
386 | #[test] | 386 | #[test] |
387 | fn test_wrap_return_type_option() { | ||
388 | check_fix( | ||
389 | r#" | ||
390 | //- /main.rs crate:main deps:core | ||
391 | use core::option::Option::{self, Some, None}; | ||
392 | |||
393 | fn div(x: i32, y: i32) -> Option<i32> { | ||
394 | if y == 0 { | ||
395 | return None; | ||
396 | } | ||
397 | x / y$0 | ||
398 | } | ||
399 | //- /core/lib.rs crate:core | ||
400 | pub mod result { | ||
401 | pub enum Result<T, E> { Ok(T), Err(E) } | ||
402 | } | ||
403 | pub mod option { | ||
404 | pub enum Option<T> { Some(T), None } | ||
405 | } | ||
406 | "#, | ||
407 | r#" | ||
408 | use core::option::Option::{self, Some, None}; | ||
409 | |||
410 | fn div(x: i32, y: i32) -> Option<i32> { | ||
411 | if y == 0 { | ||
412 | return None; | ||
413 | } | ||
414 | Some(x / y) | ||
415 | } | ||
416 | "#, | ||
417 | ); | ||
418 | } | ||
419 | |||
420 | #[test] | ||
421 | fn test_wrap_return_type() { | ||
422 | check_fix( | ||
423 | r#" | ||
424 | //- /main.rs crate:main deps:core | ||
425 | use core::result::Result::{self, Ok, Err}; | ||
426 | |||
427 | fn div(x: i32, y: i32) -> Result<i32, ()> { | ||
428 | if y == 0 { | ||
429 | return Err(()); | ||
430 | } | ||
431 | x / y$0 | ||
432 | } | ||
433 | //- /core/lib.rs crate:core | ||
434 | pub mod result { | ||
435 | pub enum Result<T, E> { Ok(T), Err(E) } | ||
436 | } | ||
437 | pub mod option { | ||
438 | pub enum Option<T> { Some(T), None } | ||
439 | } | ||
440 | "#, | ||
441 | r#" | ||
442 | use core::result::Result::{self, Ok, Err}; | ||
443 | |||
444 | fn div(x: i32, y: i32) -> Result<i32, ()> { | ||
445 | if y == 0 { | ||
446 | return Err(()); | ||
447 | } | ||
448 | Ok(x / y) | ||
449 | } | ||
450 | "#, | ||
451 | ); | ||
452 | } | ||
453 | |||
454 | #[test] | ||
455 | fn test_wrap_return_type_handles_generic_functions() { | ||
456 | check_fix( | ||
457 | r#" | ||
458 | //- /main.rs crate:main deps:core | ||
459 | use core::result::Result::{self, Ok, Err}; | ||
460 | |||
461 | fn div<T>(x: T) -> Result<T, i32> { | ||
462 | if x == 0 { | ||
463 | return Err(7); | ||
464 | } | ||
465 | $0x | ||
466 | } | ||
467 | //- /core/lib.rs crate:core | ||
468 | pub mod result { | ||
469 | pub enum Result<T, E> { Ok(T), Err(E) } | ||
470 | } | ||
471 | pub mod option { | ||
472 | pub enum Option<T> { Some(T), None } | ||
473 | } | ||
474 | "#, | ||
475 | r#" | ||
476 | use core::result::Result::{self, Ok, Err}; | ||
477 | |||
478 | fn div<T>(x: T) -> Result<T, i32> { | ||
479 | if x == 0 { | ||
480 | return Err(7); | ||
481 | } | ||
482 | Ok(x) | ||
483 | } | ||
484 | "#, | ||
485 | ); | ||
486 | } | ||
487 | |||
488 | #[test] | ||
489 | fn test_wrap_return_type_handles_type_aliases() { | ||
490 | check_fix( | ||
491 | r#" | ||
492 | //- /main.rs crate:main deps:core | ||
493 | use core::result::Result::{self, Ok, Err}; | ||
494 | |||
495 | type MyResult<T> = Result<T, ()>; | ||
496 | |||
497 | fn div(x: i32, y: i32) -> MyResult<i32> { | ||
498 | if y == 0 { | ||
499 | return Err(()); | ||
500 | } | ||
501 | x $0/ y | ||
502 | } | ||
503 | //- /core/lib.rs crate:core | ||
504 | pub mod result { | ||
505 | pub enum Result<T, E> { Ok(T), Err(E) } | ||
506 | } | ||
507 | pub mod option { | ||
508 | pub enum Option<T> { Some(T), None } | ||
509 | } | ||
510 | "#, | ||
511 | r#" | ||
512 | use core::result::Result::{self, Ok, Err}; | ||
513 | |||
514 | type MyResult<T> = Result<T, ()>; | ||
515 | |||
516 | fn div(x: i32, y: i32) -> MyResult<i32> { | ||
517 | if y == 0 { | ||
518 | return Err(()); | ||
519 | } | ||
520 | Ok(x / y) | ||
521 | } | ||
522 | "#, | ||
523 | ); | ||
524 | } | ||
525 | |||
526 | #[test] | ||
527 | fn test_wrap_return_type_not_applicable_when_expr_type_does_not_match_ok_type() { | ||
528 | check_no_diagnostics( | ||
529 | r#" | ||
530 | //- /main.rs crate:main deps:core | ||
531 | use core::result::Result::{self, Ok, Err}; | ||
532 | |||
533 | fn foo() -> Result<(), i32> { 0 } | ||
534 | |||
535 | //- /core/lib.rs crate:core | ||
536 | pub mod result { | ||
537 | pub enum Result<T, E> { Ok(T), Err(E) } | ||
538 | } | ||
539 | pub mod option { | ||
540 | pub enum Option<T> { Some(T), None } | ||
541 | } | ||
542 | "#, | ||
543 | ); | ||
544 | } | ||
545 | |||
546 | #[test] | ||
547 | fn test_wrap_return_type_not_applicable_when_return_type_is_not_result_or_option() { | ||
548 | check_no_diagnostics( | ||
549 | r#" | ||
550 | //- /main.rs crate:main deps:core | ||
551 | use core::result::Result::{self, Ok, Err}; | ||
552 | |||
553 | enum SomeOtherEnum { Ok(i32), Err(String) } | ||
554 | |||
555 | fn foo() -> SomeOtherEnum { 0 } | ||
556 | |||
557 | //- /core/lib.rs crate:core | ||
558 | pub mod result { | ||
559 | pub enum Result<T, E> { Ok(T), Err(E) } | ||
560 | } | ||
561 | pub mod option { | ||
562 | pub enum Option<T> { Some(T), None } | ||
563 | } | ||
564 | "#, | ||
565 | ); | ||
566 | } | ||
567 | |||
568 | #[test] | ||
569 | fn test_fill_struct_fields_empty() { | ||
570 | check_fix( | ||
571 | r#" | ||
572 | struct TestStruct { one: i32, two: i64 } | ||
573 | |||
574 | fn test_fn() { | ||
575 | let s = TestStruct {$0}; | ||
576 | } | ||
577 | "#, | ||
578 | r#" | ||
579 | struct TestStruct { one: i32, two: i64 } | ||
580 | |||
581 | fn test_fn() { | ||
582 | let s = TestStruct { one: (), two: ()}; | ||
583 | } | ||
584 | "#, | ||
585 | ); | ||
586 | } | ||
587 | |||
588 | #[test] | ||
589 | fn test_fill_struct_fields_self() { | ||
590 | check_fix( | ||
591 | r#" | ||
592 | struct TestStruct { one: i32 } | ||
593 | |||
594 | impl TestStruct { | ||
595 | fn test_fn() { let s = Self {$0}; } | ||
596 | } | ||
597 | "#, | ||
598 | r#" | ||
599 | struct TestStruct { one: i32 } | ||
600 | |||
601 | impl TestStruct { | ||
602 | fn test_fn() { let s = Self { one: ()}; } | ||
603 | } | ||
604 | "#, | ||
605 | ); | ||
606 | } | ||
607 | |||
608 | #[test] | ||
609 | fn test_fill_struct_fields_enum() { | ||
610 | check_fix( | ||
611 | r#" | ||
612 | enum Expr { | ||
613 | Bin { lhs: Box<Expr>, rhs: Box<Expr> } | ||
614 | } | ||
615 | |||
616 | impl Expr { | ||
617 | fn new_bin(lhs: Box<Expr>, rhs: Box<Expr>) -> Expr { | ||
618 | Expr::Bin {$0 } | ||
619 | } | ||
620 | } | ||
621 | "#, | ||
622 | r#" | ||
623 | enum Expr { | ||
624 | Bin { lhs: Box<Expr>, rhs: Box<Expr> } | ||
625 | } | ||
626 | |||
627 | impl Expr { | ||
628 | fn new_bin(lhs: Box<Expr>, rhs: Box<Expr>) -> Expr { | ||
629 | Expr::Bin { lhs: (), rhs: () } | ||
630 | } | ||
631 | } | ||
632 | "#, | ||
633 | ); | ||
634 | } | ||
635 | |||
636 | #[test] | ||
637 | fn test_fill_struct_fields_partial() { | ||
638 | check_fix( | ||
639 | r#" | ||
640 | struct TestStruct { one: i32, two: i64 } | ||
641 | |||
642 | fn test_fn() { | ||
643 | let s = TestStruct{ two: 2$0 }; | ||
644 | } | ||
645 | "#, | ||
646 | r" | ||
647 | struct TestStruct { one: i32, two: i64 } | ||
648 | |||
649 | fn test_fn() { | ||
650 | let s = TestStruct{ two: 2, one: () }; | ||
651 | } | ||
652 | ", | ||
653 | ); | ||
654 | } | ||
655 | |||
656 | #[test] | ||
657 | fn test_fill_struct_fields_raw_ident() { | ||
658 | check_fix( | ||
659 | r#" | ||
660 | struct TestStruct { r#type: u8 } | ||
661 | |||
662 | fn test_fn() { | ||
663 | TestStruct { $0 }; | ||
664 | } | ||
665 | "#, | ||
666 | r" | ||
667 | struct TestStruct { r#type: u8 } | ||
668 | |||
669 | fn test_fn() { | ||
670 | TestStruct { r#type: () }; | ||
671 | } | ||
672 | ", | ||
673 | ); | ||
674 | } | ||
675 | |||
676 | #[test] | ||
677 | fn test_fill_struct_fields_no_diagnostic() { | ||
678 | check_no_diagnostics( | ||
679 | r" | ||
680 | struct TestStruct { one: i32, two: i64 } | ||
681 | |||
682 | fn test_fn() { | ||
683 | let one = 1; | ||
684 | let s = TestStruct{ one, two: 2 }; | ||
685 | } | ||
686 | ", | ||
687 | ); | ||
688 | } | ||
689 | |||
690 | #[test] | ||
691 | fn test_fill_struct_fields_no_diagnostic_on_spread() { | ||
692 | check_no_diagnostics( | ||
693 | r" | ||
694 | struct TestStruct { one: i32, two: i64 } | ||
695 | |||
696 | fn test_fn() { | ||
697 | let one = 1; | ||
698 | let s = TestStruct{ ..a }; | ||
699 | } | ||
700 | ", | ||
701 | ); | ||
702 | } | ||
703 | |||
704 | #[test] | ||
705 | fn test_unresolved_module_diagnostic() { | ||
706 | check_expect( | ||
707 | r#"mod foo;"#, | ||
708 | expect![[r#" | ||
709 | [ | ||
710 | Diagnostic { | ||
711 | message: "unresolved module", | ||
712 | range: 0..8, | ||
713 | severity: Error, | ||
714 | fix: Some( | ||
715 | Assist { | ||
716 | id: AssistId( | ||
717 | "create_module", | ||
718 | QuickFix, | ||
719 | ), | ||
720 | label: "Create module", | ||
721 | group: None, | ||
722 | target: 0..8, | ||
723 | source_change: Some( | ||
724 | SourceChange { | ||
725 | source_file_edits: {}, | ||
726 | file_system_edits: [ | ||
727 | CreateFile { | ||
728 | dst: AnchoredPathBuf { | ||
729 | anchor: FileId( | ||
730 | 0, | ||
731 | ), | ||
732 | path: "foo.rs", | ||
733 | }, | ||
734 | initial_contents: "", | ||
735 | }, | ||
736 | ], | ||
737 | is_snippet: false, | ||
738 | }, | ||
739 | ), | ||
740 | }, | ||
741 | ), | ||
742 | unused: false, | ||
743 | code: Some( | ||
744 | DiagnosticCode( | ||
745 | "unresolved-module", | ||
746 | ), | ||
747 | ), | ||
748 | }, | ||
749 | ] | ||
750 | "#]], | ||
751 | ); | ||
752 | } | ||
753 | |||
754 | #[test] | ||
755 | fn test_unresolved_macro_range() { | 387 | fn test_unresolved_macro_range() { |
756 | check_expect( | 388 | check_expect( |
757 | r#"foo::bar!(92);"#, | 389 | r#"foo::bar!(92);"#, |
@@ -792,7 +424,7 @@ fn main() { | |||
792 | pub struct Foo { pub a: i32, pub b: i32 } | 424 | pub struct Foo { pub a: i32, pub b: i32 } |
793 | "#, | 425 | "#, |
794 | r#" | 426 | r#" |
795 | fn some(, b: ()) {} | 427 | fn some(, b: () ) {} |
796 | fn items() {} | 428 | fn items() {} |
797 | fn here() {} | 429 | fn here() {} |
798 | 430 | ||
@@ -891,53 +523,6 @@ mod a { | |||
891 | } | 523 | } |
892 | 524 | ||
893 | #[test] | 525 | #[test] |
894 | fn test_add_field_from_usage() { | ||
895 | check_fix( | ||
896 | r" | ||
897 | fn main() { | ||
898 | Foo { bar: 3, baz$0: false}; | ||
899 | } | ||
900 | struct Foo { | ||
901 | bar: i32 | ||
902 | } | ||
903 | ", | ||
904 | r" | ||
905 | fn main() { | ||
906 | Foo { bar: 3, baz: false}; | ||
907 | } | ||
908 | struct Foo { | ||
909 | bar: i32, | ||
910 | baz: bool | ||
911 | } | ||
912 | ", | ||
913 | ) | ||
914 | } | ||
915 | |||
916 | #[test] | ||
917 | fn test_add_field_in_other_file_from_usage() { | ||
918 | check_fix( | ||
919 | r#" | ||
920 | //- /main.rs | ||
921 | mod foo; | ||
922 | |||
923 | fn main() { | ||
924 | foo::Foo { bar: 3, $0baz: false}; | ||
925 | } | ||
926 | //- /foo.rs | ||
927 | struct Foo { | ||
928 | bar: i32 | ||
929 | } | ||
930 | "#, | ||
931 | r#" | ||
932 | struct Foo { | ||
933 | bar: i32, | ||
934 | pub(crate) baz: bool | ||
935 | } | ||
936 | "#, | ||
937 | ) | ||
938 | } | ||
939 | |||
940 | #[test] | ||
941 | fn test_disabled_diagnostics() { | 526 | fn test_disabled_diagnostics() { |
942 | let mut config = DiagnosticsConfig::default(); | 527 | let mut config = DiagnosticsConfig::default(); |
943 | config.disabled.insert("unresolved-module".into()); | 528 | config.disabled.insert("unresolved-module".into()); |
@@ -955,120 +540,6 @@ struct Foo { | |||
955 | } | 540 | } |
956 | 541 | ||
957 | #[test] | 542 | #[test] |
958 | fn test_rename_incorrect_case() { | ||
959 | check_fix( | ||
960 | r#" | ||
961 | pub struct test_struct$0 { one: i32 } | ||
962 | |||
963 | pub fn some_fn(val: test_struct) -> test_struct { | ||
964 | test_struct { one: val.one + 1 } | ||
965 | } | ||
966 | "#, | ||
967 | r#" | ||
968 | pub struct TestStruct { one: i32 } | ||
969 | |||
970 | pub fn some_fn(val: TestStruct) -> TestStruct { | ||
971 | TestStruct { one: val.one + 1 } | ||
972 | } | ||
973 | "#, | ||
974 | ); | ||
975 | |||
976 | check_fix( | ||
977 | r#" | ||
978 | pub fn some_fn(NonSnakeCase$0: u8) -> u8 { | ||
979 | NonSnakeCase | ||
980 | } | ||
981 | "#, | ||
982 | r#" | ||
983 | pub fn some_fn(non_snake_case: u8) -> u8 { | ||
984 | non_snake_case | ||
985 | } | ||
986 | "#, | ||
987 | ); | ||
988 | |||
989 | check_fix( | ||
990 | r#" | ||
991 | pub fn SomeFn$0(val: u8) -> u8 { | ||
992 | if val != 0 { SomeFn(val - 1) } else { val } | ||
993 | } | ||
994 | "#, | ||
995 | r#" | ||
996 | pub fn some_fn(val: u8) -> u8 { | ||
997 | if val != 0 { some_fn(val - 1) } else { val } | ||
998 | } | ||
999 | "#, | ||
1000 | ); | ||
1001 | |||
1002 | check_fix( | ||
1003 | r#" | ||
1004 | fn some_fn() { | ||
1005 | let whatAWeird_Formatting$0 = 10; | ||
1006 | another_func(whatAWeird_Formatting); | ||
1007 | } | ||
1008 | "#, | ||
1009 | r#" | ||
1010 | fn some_fn() { | ||
1011 | let what_a_weird_formatting = 10; | ||
1012 | another_func(what_a_weird_formatting); | ||
1013 | } | ||
1014 | "#, | ||
1015 | ); | ||
1016 | } | ||
1017 | |||
1018 | #[test] | ||
1019 | fn test_uppercase_const_no_diagnostics() { | ||
1020 | check_no_diagnostics( | ||
1021 | r#" | ||
1022 | fn foo() { | ||
1023 | const ANOTHER_ITEM$0: &str = "some_item"; | ||
1024 | } | ||
1025 | "#, | ||
1026 | ); | ||
1027 | } | ||
1028 | |||
1029 | #[test] | ||
1030 | fn test_rename_incorrect_case_struct_method() { | ||
1031 | check_fix( | ||
1032 | r#" | ||
1033 | pub struct TestStruct; | ||
1034 | |||
1035 | impl TestStruct { | ||
1036 | pub fn SomeFn$0() -> TestStruct { | ||
1037 | TestStruct | ||
1038 | } | ||
1039 | } | ||
1040 | "#, | ||
1041 | r#" | ||
1042 | pub struct TestStruct; | ||
1043 | |||
1044 | impl TestStruct { | ||
1045 | pub fn some_fn() -> TestStruct { | ||
1046 | TestStruct | ||
1047 | } | ||
1048 | } | ||
1049 | "#, | ||
1050 | ); | ||
1051 | } | ||
1052 | |||
1053 | #[test] | ||
1054 | fn test_single_incorrect_case_diagnostic_in_function_name_issue_6970() { | ||
1055 | let input = r#"fn FOO$0() {}"#; | ||
1056 | let expected = r#"fn foo() {}"#; | ||
1057 | |||
1058 | let (analysis, file_position) = fixture::position(input); | ||
1059 | let diagnostics = analysis | ||
1060 | .diagnostics( | ||
1061 | &DiagnosticsConfig::default(), | ||
1062 | AssistResolveStrategy::All, | ||
1063 | file_position.file_id, | ||
1064 | ) | ||
1065 | .unwrap(); | ||
1066 | assert_eq!(diagnostics.len(), 1); | ||
1067 | |||
1068 | check_fix(input, expected); | ||
1069 | } | ||
1070 | |||
1071 | #[test] | ||
1072 | fn unlinked_file_prepend_first_item() { | 543 | fn unlinked_file_prepend_first_item() { |
1073 | cov_mark::check!(unlinked_file_prepend_before_first_item); | 544 | cov_mark::check!(unlinked_file_prepend_before_first_item); |
1074 | check_fix( | 545 | check_fix( |
diff --git a/crates/ide/src/diagnostics/fixes.rs b/crates/ide/src/diagnostics/fixes.rs index 15821500f..92b3f5a2d 100644 --- a/crates/ide/src/diagnostics/fixes.rs +++ b/crates/ide/src/diagnostics/fixes.rs | |||
@@ -1,31 +1,18 @@ | |||
1 | //! Provides a way to attach fixes to the diagnostics. | 1 | //! Provides a way to attach fixes to the diagnostics. |
2 | //! The same module also has all curret custom fixes for the diagnostics implemented. | 2 | //! The same module also has all curret custom fixes for the diagnostics implemented. |
3 | use hir::{ | 3 | mod change_case; |
4 | db::AstDatabase, | 4 | mod create_field; |
5 | diagnostics::{ | 5 | mod fill_missing_fields; |
6 | Diagnostic, IncorrectCase, MissingFields, MissingOkOrSomeInTailExpr, NoSuchField, | 6 | mod remove_semicolon; |
7 | RemoveThisSemicolon, ReplaceFilterMapNextWithFindMap, UnresolvedModule, | 7 | mod replace_with_find_map; |
8 | }, | 8 | mod unresolved_module; |
9 | HasSource, HirDisplay, InFile, Semantics, VariantDef, | 9 | mod wrap_tail_expr; |
10 | }; | 10 | |
11 | use hir::{diagnostics::Diagnostic, Semantics}; | ||
11 | use ide_assists::AssistResolveStrategy; | 12 | use ide_assists::AssistResolveStrategy; |
12 | use ide_db::{ | 13 | use ide_db::RootDatabase; |
13 | base_db::{AnchoredPathBuf, FileId}, | ||
14 | source_change::{FileSystemEdit, SourceChange}, | ||
15 | RootDatabase, | ||
16 | }; | ||
17 | use syntax::{ | ||
18 | algo, | ||
19 | ast::{self, edit::IndentLevel, make, ArgListOwner}, | ||
20 | AstNode, TextRange, | ||
21 | }; | ||
22 | use text_edit::TextEdit; | ||
23 | 14 | ||
24 | use crate::{ | 15 | use crate::Assist; |
25 | diagnostics::{fix, unresolved_fix}, | ||
26 | references::rename::rename_with_semantics, | ||
27 | Assist, FilePosition, | ||
28 | }; | ||
29 | 16 | ||
30 | /// A [Diagnostic] that potentially has a fix available. | 17 | /// A [Diagnostic] that potentially has a fix available. |
31 | /// | 18 | /// |
@@ -42,256 +29,3 @@ pub(crate) trait DiagnosticWithFix: Diagnostic { | |||
42 | _resolve: &AssistResolveStrategy, | 29 | _resolve: &AssistResolveStrategy, |
43 | ) -> Option<Assist>; | 30 | ) -> Option<Assist>; |
44 | } | 31 | } |
45 | |||
46 | impl DiagnosticWithFix for UnresolvedModule { | ||
47 | fn fix( | ||
48 | &self, | ||
49 | sema: &Semantics<RootDatabase>, | ||
50 | _resolve: &AssistResolveStrategy, | ||
51 | ) -> Option<Assist> { | ||
52 | let root = sema.db.parse_or_expand(self.file)?; | ||
53 | let unresolved_module = self.decl.to_node(&root); | ||
54 | Some(fix( | ||
55 | "create_module", | ||
56 | "Create module", | ||
57 | FileSystemEdit::CreateFile { | ||
58 | dst: AnchoredPathBuf { | ||
59 | anchor: self.file.original_file(sema.db), | ||
60 | path: self.candidate.clone(), | ||
61 | }, | ||
62 | initial_contents: "".to_string(), | ||
63 | } | ||
64 | .into(), | ||
65 | unresolved_module.syntax().text_range(), | ||
66 | )) | ||
67 | } | ||
68 | } | ||
69 | |||
70 | impl DiagnosticWithFix for NoSuchField { | ||
71 | fn fix( | ||
72 | &self, | ||
73 | sema: &Semantics<RootDatabase>, | ||
74 | _resolve: &AssistResolveStrategy, | ||
75 | ) -> Option<Assist> { | ||
76 | let root = sema.db.parse_or_expand(self.file)?; | ||
77 | missing_record_expr_field_fix( | ||
78 | &sema, | ||
79 | self.file.original_file(sema.db), | ||
80 | &self.field.to_node(&root), | ||
81 | ) | ||
82 | } | ||
83 | } | ||
84 | |||
85 | impl DiagnosticWithFix for MissingFields { | ||
86 | fn fix( | ||
87 | &self, | ||
88 | sema: &Semantics<RootDatabase>, | ||
89 | _resolve: &AssistResolveStrategy, | ||
90 | ) -> Option<Assist> { | ||
91 | // Note that although we could add a diagnostics to | ||
92 | // fill the missing tuple field, e.g : | ||
93 | // `struct A(usize);` | ||
94 | // `let a = A { 0: () }` | ||
95 | // but it is uncommon usage and it should not be encouraged. | ||
96 | if self.missed_fields.iter().any(|it| it.as_tuple_index().is_some()) { | ||
97 | return None; | ||
98 | } | ||
99 | |||
100 | let root = sema.db.parse_or_expand(self.file)?; | ||
101 | let field_list_parent = self.field_list_parent.to_node(&root); | ||
102 | let old_field_list = field_list_parent.record_expr_field_list()?; | ||
103 | let mut new_field_list = old_field_list.clone(); | ||
104 | for f in self.missed_fields.iter() { | ||
105 | let field = | ||
106 | make::record_expr_field(make::name_ref(&f.to_string()), Some(make::expr_unit())); | ||
107 | new_field_list = new_field_list.append_field(&field); | ||
108 | } | ||
109 | |||
110 | let edit = { | ||
111 | let mut builder = TextEdit::builder(); | ||
112 | algo::diff(&old_field_list.syntax(), &new_field_list.syntax()) | ||
113 | .into_text_edit(&mut builder); | ||
114 | builder.finish() | ||
115 | }; | ||
116 | Some(fix( | ||
117 | "fill_missing_fields", | ||
118 | "Fill struct fields", | ||
119 | SourceChange::from_text_edit(self.file.original_file(sema.db), edit), | ||
120 | sema.original_range(&field_list_parent.syntax()).range, | ||
121 | )) | ||
122 | } | ||
123 | } | ||
124 | |||
125 | impl DiagnosticWithFix for MissingOkOrSomeInTailExpr { | ||
126 | fn fix( | ||
127 | &self, | ||
128 | sema: &Semantics<RootDatabase>, | ||
129 | _resolve: &AssistResolveStrategy, | ||
130 | ) -> Option<Assist> { | ||
131 | let root = sema.db.parse_or_expand(self.file)?; | ||
132 | let tail_expr = self.expr.to_node(&root); | ||
133 | let tail_expr_range = tail_expr.syntax().text_range(); | ||
134 | let replacement = format!("{}({})", self.required, tail_expr.syntax()); | ||
135 | let edit = TextEdit::replace(tail_expr_range, replacement); | ||
136 | let source_change = SourceChange::from_text_edit(self.file.original_file(sema.db), edit); | ||
137 | let name = if self.required == "Ok" { "Wrap with Ok" } else { "Wrap with Some" }; | ||
138 | Some(fix("wrap_tail_expr", name, source_change, tail_expr_range)) | ||
139 | } | ||
140 | } | ||
141 | |||
142 | impl DiagnosticWithFix for RemoveThisSemicolon { | ||
143 | fn fix( | ||
144 | &self, | ||
145 | sema: &Semantics<RootDatabase>, | ||
146 | _resolve: &AssistResolveStrategy, | ||
147 | ) -> Option<Assist> { | ||
148 | let root = sema.db.parse_or_expand(self.file)?; | ||
149 | |||
150 | let semicolon = self | ||
151 | .expr | ||
152 | .to_node(&root) | ||
153 | .syntax() | ||
154 | .parent() | ||
155 | .and_then(ast::ExprStmt::cast) | ||
156 | .and_then(|expr| expr.semicolon_token())? | ||
157 | .text_range(); | ||
158 | |||
159 | let edit = TextEdit::delete(semicolon); | ||
160 | let source_change = SourceChange::from_text_edit(self.file.original_file(sema.db), edit); | ||
161 | |||
162 | Some(fix("remove_semicolon", "Remove this semicolon", source_change, semicolon)) | ||
163 | } | ||
164 | } | ||
165 | |||
166 | impl DiagnosticWithFix for IncorrectCase { | ||
167 | fn fix( | ||
168 | &self, | ||
169 | sema: &Semantics<RootDatabase>, | ||
170 | resolve: &AssistResolveStrategy, | ||
171 | ) -> Option<Assist> { | ||
172 | let root = sema.db.parse_or_expand(self.file)?; | ||
173 | let name_node = self.ident.to_node(&root); | ||
174 | |||
175 | let name_node = InFile::new(self.file, name_node.syntax()); | ||
176 | let frange = name_node.original_file_range(sema.db); | ||
177 | let file_position = FilePosition { file_id: frange.file_id, offset: frange.range.start() }; | ||
178 | |||
179 | let label = format!("Rename to {}", self.suggested_text); | ||
180 | let mut res = unresolved_fix("change_case", &label, frange.range); | ||
181 | if resolve.should_resolve(&res.id) { | ||
182 | let source_change = rename_with_semantics(sema, file_position, &self.suggested_text); | ||
183 | res.source_change = Some(source_change.ok().unwrap_or_default()); | ||
184 | } | ||
185 | |||
186 | Some(res) | ||
187 | } | ||
188 | } | ||
189 | |||
190 | impl DiagnosticWithFix for ReplaceFilterMapNextWithFindMap { | ||
191 | fn fix( | ||
192 | &self, | ||
193 | sema: &Semantics<RootDatabase>, | ||
194 | _resolve: &AssistResolveStrategy, | ||
195 | ) -> Option<Assist> { | ||
196 | let root = sema.db.parse_or_expand(self.file)?; | ||
197 | let next_expr = self.next_expr.to_node(&root); | ||
198 | let next_call = ast::MethodCallExpr::cast(next_expr.syntax().clone())?; | ||
199 | |||
200 | let filter_map_call = ast::MethodCallExpr::cast(next_call.receiver()?.syntax().clone())?; | ||
201 | let filter_map_name_range = filter_map_call.name_ref()?.ident_token()?.text_range(); | ||
202 | let filter_map_args = filter_map_call.arg_list()?; | ||
203 | |||
204 | let range_to_replace = | ||
205 | TextRange::new(filter_map_name_range.start(), next_expr.syntax().text_range().end()); | ||
206 | let replacement = format!("find_map{}", filter_map_args.syntax().text()); | ||
207 | let trigger_range = next_expr.syntax().text_range(); | ||
208 | |||
209 | let edit = TextEdit::replace(range_to_replace, replacement); | ||
210 | |||
211 | let source_change = SourceChange::from_text_edit(self.file.original_file(sema.db), edit); | ||
212 | |||
213 | Some(fix( | ||
214 | "replace_with_find_map", | ||
215 | "Replace filter_map(..).next() with find_map()", | ||
216 | source_change, | ||
217 | trigger_range, | ||
218 | )) | ||
219 | } | ||
220 | } | ||
221 | |||
222 | fn missing_record_expr_field_fix( | ||
223 | sema: &Semantics<RootDatabase>, | ||
224 | usage_file_id: FileId, | ||
225 | record_expr_field: &ast::RecordExprField, | ||
226 | ) -> Option<Assist> { | ||
227 | let record_lit = ast::RecordExpr::cast(record_expr_field.syntax().parent()?.parent()?)?; | ||
228 | let def_id = sema.resolve_variant(record_lit)?; | ||
229 | let module; | ||
230 | let def_file_id; | ||
231 | let record_fields = match def_id { | ||
232 | VariantDef::Struct(s) => { | ||
233 | module = s.module(sema.db); | ||
234 | let source = s.source(sema.db)?; | ||
235 | def_file_id = source.file_id; | ||
236 | let fields = source.value.field_list()?; | ||
237 | record_field_list(fields)? | ||
238 | } | ||
239 | VariantDef::Union(u) => { | ||
240 | module = u.module(sema.db); | ||
241 | let source = u.source(sema.db)?; | ||
242 | def_file_id = source.file_id; | ||
243 | source.value.record_field_list()? | ||
244 | } | ||
245 | VariantDef::Variant(e) => { | ||
246 | module = e.module(sema.db); | ||
247 | let source = e.source(sema.db)?; | ||
248 | def_file_id = source.file_id; | ||
249 | let fields = source.value.field_list()?; | ||
250 | record_field_list(fields)? | ||
251 | } | ||
252 | }; | ||
253 | let def_file_id = def_file_id.original_file(sema.db); | ||
254 | |||
255 | let new_field_type = sema.type_of_expr(&record_expr_field.expr()?)?; | ||
256 | if new_field_type.is_unknown() { | ||
257 | return None; | ||
258 | } | ||
259 | let new_field = make::record_field( | ||
260 | None, | ||
261 | make::name(&record_expr_field.field_name()?.text()), | ||
262 | make::ty(&new_field_type.display_source_code(sema.db, module.into()).ok()?), | ||
263 | ); | ||
264 | |||
265 | let last_field = record_fields.fields().last()?; | ||
266 | let last_field_syntax = last_field.syntax(); | ||
267 | let indent = IndentLevel::from_node(last_field_syntax); | ||
268 | |||
269 | let mut new_field = new_field.to_string(); | ||
270 | if usage_file_id != def_file_id { | ||
271 | new_field = format!("pub(crate) {}", new_field); | ||
272 | } | ||
273 | new_field = format!("\n{}{}", indent, new_field); | ||
274 | |||
275 | let needs_comma = !last_field_syntax.to_string().ends_with(','); | ||
276 | if needs_comma { | ||
277 | new_field = format!(",{}", new_field); | ||
278 | } | ||
279 | |||
280 | let source_change = SourceChange::from_text_edit( | ||
281 | def_file_id, | ||
282 | TextEdit::insert(last_field_syntax.text_range().end(), new_field), | ||
283 | ); | ||
284 | return Some(fix( | ||
285 | "create_field", | ||
286 | "Create field", | ||
287 | source_change, | ||
288 | record_expr_field.syntax().text_range(), | ||
289 | )); | ||
290 | |||
291 | fn record_field_list(field_def_list: ast::FieldList) -> Option<ast::RecordFieldList> { | ||
292 | match field_def_list { | ||
293 | ast::FieldList::RecordFieldList(it) => Some(it), | ||
294 | ast::FieldList::TupleFieldList(_) => None, | ||
295 | } | ||
296 | } | ||
297 | } | ||
diff --git a/crates/ide/src/diagnostics/fixes/change_case.rs b/crates/ide/src/diagnostics/fixes/change_case.rs new file mode 100644 index 000000000..80aca58a1 --- /dev/null +++ b/crates/ide/src/diagnostics/fixes/change_case.rs | |||
@@ -0,0 +1,155 @@ | |||
1 | use hir::{db::AstDatabase, diagnostics::IncorrectCase, InFile, Semantics}; | ||
2 | use ide_assists::{Assist, AssistResolveStrategy}; | ||
3 | use ide_db::{base_db::FilePosition, RootDatabase}; | ||
4 | use syntax::AstNode; | ||
5 | |||
6 | use crate::{ | ||
7 | diagnostics::{unresolved_fix, DiagnosticWithFix}, | ||
8 | references::rename::rename_with_semantics, | ||
9 | }; | ||
10 | |||
11 | impl DiagnosticWithFix for IncorrectCase { | ||
12 | fn fix( | ||
13 | &self, | ||
14 | sema: &Semantics<RootDatabase>, | ||
15 | resolve: &AssistResolveStrategy, | ||
16 | ) -> Option<Assist> { | ||
17 | let root = sema.db.parse_or_expand(self.file)?; | ||
18 | let name_node = self.ident.to_node(&root); | ||
19 | |||
20 | let name_node = InFile::new(self.file, name_node.syntax()); | ||
21 | let frange = name_node.original_file_range(sema.db); | ||
22 | let file_position = FilePosition { file_id: frange.file_id, offset: frange.range.start() }; | ||
23 | |||
24 | let label = format!("Rename to {}", self.suggested_text); | ||
25 | let mut res = unresolved_fix("change_case", &label, frange.range); | ||
26 | if resolve.should_resolve(&res.id) { | ||
27 | let source_change = rename_with_semantics(sema, file_position, &self.suggested_text); | ||
28 | res.source_change = Some(source_change.ok().unwrap_or_default()); | ||
29 | } | ||
30 | |||
31 | Some(res) | ||
32 | } | ||
33 | } | ||
34 | |||
35 | #[cfg(test)] | ||
36 | mod change_case { | ||
37 | use crate::{ | ||
38 | diagnostics::tests::{check_fix, check_no_diagnostics}, | ||
39 | fixture, AssistResolveStrategy, DiagnosticsConfig, | ||
40 | }; | ||
41 | |||
42 | #[test] | ||
43 | fn test_rename_incorrect_case() { | ||
44 | check_fix( | ||
45 | r#" | ||
46 | pub struct test_struct$0 { one: i32 } | ||
47 | |||
48 | pub fn some_fn(val: test_struct) -> test_struct { | ||
49 | test_struct { one: val.one + 1 } | ||
50 | } | ||
51 | "#, | ||
52 | r#" | ||
53 | pub struct TestStruct { one: i32 } | ||
54 | |||
55 | pub fn some_fn(val: TestStruct) -> TestStruct { | ||
56 | TestStruct { one: val.one + 1 } | ||
57 | } | ||
58 | "#, | ||
59 | ); | ||
60 | |||
61 | check_fix( | ||
62 | r#" | ||
63 | pub fn some_fn(NonSnakeCase$0: u8) -> u8 { | ||
64 | NonSnakeCase | ||
65 | } | ||
66 | "#, | ||
67 | r#" | ||
68 | pub fn some_fn(non_snake_case: u8) -> u8 { | ||
69 | non_snake_case | ||
70 | } | ||
71 | "#, | ||
72 | ); | ||
73 | |||
74 | check_fix( | ||
75 | r#" | ||
76 | pub fn SomeFn$0(val: u8) -> u8 { | ||
77 | if val != 0 { SomeFn(val - 1) } else { val } | ||
78 | } | ||
79 | "#, | ||
80 | r#" | ||
81 | pub fn some_fn(val: u8) -> u8 { | ||
82 | if val != 0 { some_fn(val - 1) } else { val } | ||
83 | } | ||
84 | "#, | ||
85 | ); | ||
86 | |||
87 | check_fix( | ||
88 | r#" | ||
89 | fn some_fn() { | ||
90 | let whatAWeird_Formatting$0 = 10; | ||
91 | another_func(whatAWeird_Formatting); | ||
92 | } | ||
93 | "#, | ||
94 | r#" | ||
95 | fn some_fn() { | ||
96 | let what_a_weird_formatting = 10; | ||
97 | another_func(what_a_weird_formatting); | ||
98 | } | ||
99 | "#, | ||
100 | ); | ||
101 | } | ||
102 | |||
103 | #[test] | ||
104 | fn test_uppercase_const_no_diagnostics() { | ||
105 | check_no_diagnostics( | ||
106 | r#" | ||
107 | fn foo() { | ||
108 | const ANOTHER_ITEM$0: &str = "some_item"; | ||
109 | } | ||
110 | "#, | ||
111 | ); | ||
112 | } | ||
113 | |||
114 | #[test] | ||
115 | fn test_rename_incorrect_case_struct_method() { | ||
116 | check_fix( | ||
117 | r#" | ||
118 | pub struct TestStruct; | ||
119 | |||
120 | impl TestStruct { | ||
121 | pub fn SomeFn$0() -> TestStruct { | ||
122 | TestStruct | ||
123 | } | ||
124 | } | ||
125 | "#, | ||
126 | r#" | ||
127 | pub struct TestStruct; | ||
128 | |||
129 | impl TestStruct { | ||
130 | pub fn some_fn() -> TestStruct { | ||
131 | TestStruct | ||
132 | } | ||
133 | } | ||
134 | "#, | ||
135 | ); | ||
136 | } | ||
137 | |||
138 | #[test] | ||
139 | fn test_single_incorrect_case_diagnostic_in_function_name_issue_6970() { | ||
140 | let input = r#"fn FOO$0() {}"#; | ||
141 | let expected = r#"fn foo() {}"#; | ||
142 | |||
143 | let (analysis, file_position) = fixture::position(input); | ||
144 | let diagnostics = analysis | ||
145 | .diagnostics( | ||
146 | &DiagnosticsConfig::default(), | ||
147 | AssistResolveStrategy::All, | ||
148 | file_position.file_id, | ||
149 | ) | ||
150 | .unwrap(); | ||
151 | assert_eq!(diagnostics.len(), 1); | ||
152 | |||
153 | check_fix(input, expected); | ||
154 | } | ||
155 | } | ||
diff --git a/crates/ide/src/diagnostics/fixes/create_field.rs b/crates/ide/src/diagnostics/fixes/create_field.rs new file mode 100644 index 000000000..24e0fda52 --- /dev/null +++ b/crates/ide/src/diagnostics/fixes/create_field.rs | |||
@@ -0,0 +1,157 @@ | |||
1 | use hir::{db::AstDatabase, diagnostics::NoSuchField, HasSource, HirDisplay, Semantics}; | ||
2 | use ide_db::{base_db::FileId, source_change::SourceChange, RootDatabase}; | ||
3 | use syntax::{ | ||
4 | ast::{self, edit::IndentLevel, make}, | ||
5 | AstNode, | ||
6 | }; | ||
7 | use text_edit::TextEdit; | ||
8 | |||
9 | use crate::{ | ||
10 | diagnostics::{fix, DiagnosticWithFix}, | ||
11 | Assist, AssistResolveStrategy, | ||
12 | }; | ||
13 | |||
14 | impl DiagnosticWithFix for NoSuchField { | ||
15 | fn fix( | ||
16 | &self, | ||
17 | sema: &Semantics<RootDatabase>, | ||
18 | _resolve: &AssistResolveStrategy, | ||
19 | ) -> Option<Assist> { | ||
20 | let root = sema.db.parse_or_expand(self.file)?; | ||
21 | missing_record_expr_field_fix( | ||
22 | &sema, | ||
23 | self.file.original_file(sema.db), | ||
24 | &self.field.to_node(&root), | ||
25 | ) | ||
26 | } | ||
27 | } | ||
28 | |||
29 | fn missing_record_expr_field_fix( | ||
30 | sema: &Semantics<RootDatabase>, | ||
31 | usage_file_id: FileId, | ||
32 | record_expr_field: &ast::RecordExprField, | ||
33 | ) -> Option<Assist> { | ||
34 | let record_lit = ast::RecordExpr::cast(record_expr_field.syntax().parent()?.parent()?)?; | ||
35 | let def_id = sema.resolve_variant(record_lit)?; | ||
36 | let module; | ||
37 | let def_file_id; | ||
38 | let record_fields = match def_id { | ||
39 | hir::VariantDef::Struct(s) => { | ||
40 | module = s.module(sema.db); | ||
41 | let source = s.source(sema.db)?; | ||
42 | def_file_id = source.file_id; | ||
43 | let fields = source.value.field_list()?; | ||
44 | record_field_list(fields)? | ||
45 | } | ||
46 | hir::VariantDef::Union(u) => { | ||
47 | module = u.module(sema.db); | ||
48 | let source = u.source(sema.db)?; | ||
49 | def_file_id = source.file_id; | ||
50 | source.value.record_field_list()? | ||
51 | } | ||
52 | hir::VariantDef::Variant(e) => { | ||
53 | module = e.module(sema.db); | ||
54 | let source = e.source(sema.db)?; | ||
55 | def_file_id = source.file_id; | ||
56 | let fields = source.value.field_list()?; | ||
57 | record_field_list(fields)? | ||
58 | } | ||
59 | }; | ||
60 | let def_file_id = def_file_id.original_file(sema.db); | ||
61 | |||
62 | let new_field_type = sema.type_of_expr(&record_expr_field.expr()?)?; | ||
63 | if new_field_type.is_unknown() { | ||
64 | return None; | ||
65 | } | ||
66 | let new_field = make::record_field( | ||
67 | None, | ||
68 | make::name(&record_expr_field.field_name()?.text()), | ||
69 | make::ty(&new_field_type.display_source_code(sema.db, module.into()).ok()?), | ||
70 | ); | ||
71 | |||
72 | let last_field = record_fields.fields().last()?; | ||
73 | let last_field_syntax = last_field.syntax(); | ||
74 | let indent = IndentLevel::from_node(last_field_syntax); | ||
75 | |||
76 | let mut new_field = new_field.to_string(); | ||
77 | if usage_file_id != def_file_id { | ||
78 | new_field = format!("pub(crate) {}", new_field); | ||
79 | } | ||
80 | new_field = format!("\n{}{}", indent, new_field); | ||
81 | |||
82 | let needs_comma = !last_field_syntax.to_string().ends_with(','); | ||
83 | if needs_comma { | ||
84 | new_field = format!(",{}", new_field); | ||
85 | } | ||
86 | |||
87 | let source_change = SourceChange::from_text_edit( | ||
88 | def_file_id, | ||
89 | TextEdit::insert(last_field_syntax.text_range().end(), new_field), | ||
90 | ); | ||
91 | |||
92 | return Some(fix( | ||
93 | "create_field", | ||
94 | "Create field", | ||
95 | source_change, | ||
96 | record_expr_field.syntax().text_range(), | ||
97 | )); | ||
98 | |||
99 | fn record_field_list(field_def_list: ast::FieldList) -> Option<ast::RecordFieldList> { | ||
100 | match field_def_list { | ||
101 | ast::FieldList::RecordFieldList(it) => Some(it), | ||
102 | ast::FieldList::TupleFieldList(_) => None, | ||
103 | } | ||
104 | } | ||
105 | } | ||
106 | |||
107 | #[cfg(test)] | ||
108 | mod tests { | ||
109 | use crate::diagnostics::tests::check_fix; | ||
110 | |||
111 | #[test] | ||
112 | fn test_add_field_from_usage() { | ||
113 | check_fix( | ||
114 | r" | ||
115 | fn main() { | ||
116 | Foo { bar: 3, baz$0: false}; | ||
117 | } | ||
118 | struct Foo { | ||
119 | bar: i32 | ||
120 | } | ||
121 | ", | ||
122 | r" | ||
123 | fn main() { | ||
124 | Foo { bar: 3, baz: false}; | ||
125 | } | ||
126 | struct Foo { | ||
127 | bar: i32, | ||
128 | baz: bool | ||
129 | } | ||
130 | ", | ||
131 | ) | ||
132 | } | ||
133 | |||
134 | #[test] | ||
135 | fn test_add_field_in_other_file_from_usage() { | ||
136 | check_fix( | ||
137 | r#" | ||
138 | //- /main.rs | ||
139 | mod foo; | ||
140 | |||
141 | fn main() { | ||
142 | foo::Foo { bar: 3, $0baz: false}; | ||
143 | } | ||
144 | //- /foo.rs | ||
145 | struct Foo { | ||
146 | bar: i32 | ||
147 | } | ||
148 | "#, | ||
149 | r#" | ||
150 | struct Foo { | ||
151 | bar: i32, | ||
152 | pub(crate) baz: bool | ||
153 | } | ||
154 | "#, | ||
155 | ) | ||
156 | } | ||
157 | } | ||
diff --git a/crates/ide/src/diagnostics/fixes/fill_missing_fields.rs b/crates/ide/src/diagnostics/fixes/fill_missing_fields.rs new file mode 100644 index 000000000..37a0e37a9 --- /dev/null +++ b/crates/ide/src/diagnostics/fixes/fill_missing_fields.rs | |||
@@ -0,0 +1,217 @@ | |||
1 | use hir::{db::AstDatabase, diagnostics::MissingFields, Semantics}; | ||
2 | use ide_assists::AssistResolveStrategy; | ||
3 | use ide_db::{source_change::SourceChange, RootDatabase}; | ||
4 | use syntax::{algo, ast::make, AstNode}; | ||
5 | use text_edit::TextEdit; | ||
6 | |||
7 | use crate::{ | ||
8 | diagnostics::{fix, fixes::DiagnosticWithFix}, | ||
9 | Assist, | ||
10 | }; | ||
11 | |||
12 | impl DiagnosticWithFix for MissingFields { | ||
13 | fn fix( | ||
14 | &self, | ||
15 | sema: &Semantics<RootDatabase>, | ||
16 | _resolve: &AssistResolveStrategy, | ||
17 | ) -> Option<Assist> { | ||
18 | // Note that although we could add a diagnostics to | ||
19 | // fill the missing tuple field, e.g : | ||
20 | // `struct A(usize);` | ||
21 | // `let a = A { 0: () }` | ||
22 | // but it is uncommon usage and it should not be encouraged. | ||
23 | if self.missed_fields.iter().any(|it| it.as_tuple_index().is_some()) { | ||
24 | return None; | ||
25 | } | ||
26 | |||
27 | let root = sema.db.parse_or_expand(self.file)?; | ||
28 | let field_list_parent = self.field_list_parent.to_node(&root); | ||
29 | let old_field_list = field_list_parent.record_expr_field_list()?; | ||
30 | let new_field_list = old_field_list.clone_for_update(); | ||
31 | for f in self.missed_fields.iter() { | ||
32 | let field = | ||
33 | make::record_expr_field(make::name_ref(&f.to_string()), Some(make::expr_unit())) | ||
34 | .clone_for_update(); | ||
35 | new_field_list.add_field(field); | ||
36 | } | ||
37 | |||
38 | let edit = { | ||
39 | let mut builder = TextEdit::builder(); | ||
40 | algo::diff(&old_field_list.syntax(), &new_field_list.syntax()) | ||
41 | .into_text_edit(&mut builder); | ||
42 | builder.finish() | ||
43 | }; | ||
44 | Some(fix( | ||
45 | "fill_missing_fields", | ||
46 | "Fill struct fields", | ||
47 | SourceChange::from_text_edit(self.file.original_file(sema.db), edit), | ||
48 | sema.original_range(&field_list_parent.syntax()).range, | ||
49 | )) | ||
50 | } | ||
51 | } | ||
52 | |||
53 | #[cfg(test)] | ||
54 | mod tests { | ||
55 | use crate::diagnostics::tests::{check_fix, check_no_diagnostics}; | ||
56 | |||
57 | #[test] | ||
58 | fn test_fill_struct_fields_empty() { | ||
59 | check_fix( | ||
60 | r#" | ||
61 | struct TestStruct { one: i32, two: i64 } | ||
62 | |||
63 | fn test_fn() { | ||
64 | let s = TestStruct {$0}; | ||
65 | } | ||
66 | "#, | ||
67 | r#" | ||
68 | struct TestStruct { one: i32, two: i64 } | ||
69 | |||
70 | fn test_fn() { | ||
71 | let s = TestStruct { one: (), two: () }; | ||
72 | } | ||
73 | "#, | ||
74 | ); | ||
75 | } | ||
76 | |||
77 | #[test] | ||
78 | fn test_fill_struct_fields_self() { | ||
79 | check_fix( | ||
80 | r#" | ||
81 | struct TestStruct { one: i32 } | ||
82 | |||
83 | impl TestStruct { | ||
84 | fn test_fn() { let s = Self {$0}; } | ||
85 | } | ||
86 | "#, | ||
87 | r#" | ||
88 | struct TestStruct { one: i32 } | ||
89 | |||
90 | impl TestStruct { | ||
91 | fn test_fn() { let s = Self { one: () }; } | ||
92 | } | ||
93 | "#, | ||
94 | ); | ||
95 | } | ||
96 | |||
97 | #[test] | ||
98 | fn test_fill_struct_fields_enum() { | ||
99 | check_fix( | ||
100 | r#" | ||
101 | enum Expr { | ||
102 | Bin { lhs: Box<Expr>, rhs: Box<Expr> } | ||
103 | } | ||
104 | |||
105 | impl Expr { | ||
106 | fn new_bin(lhs: Box<Expr>, rhs: Box<Expr>) -> Expr { | ||
107 | Expr::Bin {$0 } | ||
108 | } | ||
109 | } | ||
110 | "#, | ||
111 | r#" | ||
112 | enum Expr { | ||
113 | Bin { lhs: Box<Expr>, rhs: Box<Expr> } | ||
114 | } | ||
115 | |||
116 | impl Expr { | ||
117 | fn new_bin(lhs: Box<Expr>, rhs: Box<Expr>) -> Expr { | ||
118 | Expr::Bin { lhs: (), rhs: () } | ||
119 | } | ||
120 | } | ||
121 | "#, | ||
122 | ); | ||
123 | } | ||
124 | |||
125 | #[test] | ||
126 | fn test_fill_struct_fields_partial() { | ||
127 | check_fix( | ||
128 | r#" | ||
129 | struct TestStruct { one: i32, two: i64 } | ||
130 | |||
131 | fn test_fn() { | ||
132 | let s = TestStruct{ two: 2$0 }; | ||
133 | } | ||
134 | "#, | ||
135 | r" | ||
136 | struct TestStruct { one: i32, two: i64 } | ||
137 | |||
138 | fn test_fn() { | ||
139 | let s = TestStruct{ two: 2, one: () }; | ||
140 | } | ||
141 | ", | ||
142 | ); | ||
143 | } | ||
144 | |||
145 | #[test] | ||
146 | fn test_fill_struct_fields_raw_ident() { | ||
147 | check_fix( | ||
148 | r#" | ||
149 | struct TestStruct { r#type: u8 } | ||
150 | |||
151 | fn test_fn() { | ||
152 | TestStruct { $0 }; | ||
153 | } | ||
154 | "#, | ||
155 | r" | ||
156 | struct TestStruct { r#type: u8 } | ||
157 | |||
158 | fn test_fn() { | ||
159 | TestStruct { r#type: () }; | ||
160 | } | ||
161 | ", | ||
162 | ); | ||
163 | } | ||
164 | |||
165 | #[test] | ||
166 | fn test_fill_struct_fields_no_diagnostic() { | ||
167 | check_no_diagnostics( | ||
168 | r#" | ||
169 | struct TestStruct { one: i32, two: i64 } | ||
170 | |||
171 | fn test_fn() { | ||
172 | let one = 1; | ||
173 | let s = TestStruct{ one, two: 2 }; | ||
174 | } | ||
175 | "#, | ||
176 | ); | ||
177 | } | ||
178 | |||
179 | #[test] | ||
180 | fn test_fill_struct_fields_no_diagnostic_on_spread() { | ||
181 | check_no_diagnostics( | ||
182 | r#" | ||
183 | struct TestStruct { one: i32, two: i64 } | ||
184 | |||
185 | fn test_fn() { | ||
186 | let one = 1; | ||
187 | let s = TestStruct{ ..a }; | ||
188 | } | ||
189 | "#, | ||
190 | ); | ||
191 | } | ||
192 | |||
193 | #[test] | ||
194 | fn test_fill_struct_fields_blank_line() { | ||
195 | check_fix( | ||
196 | r#" | ||
197 | struct S { a: (), b: () } | ||
198 | |||
199 | fn f() { | ||
200 | S { | ||
201 | $0 | ||
202 | }; | ||
203 | } | ||
204 | "#, | ||
205 | r#" | ||
206 | struct S { a: (), b: () } | ||
207 | |||
208 | fn f() { | ||
209 | S { | ||
210 | a: (), | ||
211 | b: (), | ||
212 | }; | ||
213 | } | ||
214 | "#, | ||
215 | ); | ||
216 | } | ||
217 | } | ||
diff --git a/crates/ide/src/diagnostics/fixes/remove_semicolon.rs b/crates/ide/src/diagnostics/fixes/remove_semicolon.rs new file mode 100644 index 000000000..45471da41 --- /dev/null +++ b/crates/ide/src/diagnostics/fixes/remove_semicolon.rs | |||
@@ -0,0 +1,41 @@ | |||
1 | use hir::{db::AstDatabase, diagnostics::RemoveThisSemicolon, Semantics}; | ||
2 | use ide_assists::{Assist, AssistResolveStrategy}; | ||
3 | use ide_db::{source_change::SourceChange, RootDatabase}; | ||
4 | use syntax::{ast, AstNode}; | ||
5 | use text_edit::TextEdit; | ||
6 | |||
7 | use crate::diagnostics::{fix, DiagnosticWithFix}; | ||
8 | |||
9 | impl DiagnosticWithFix for RemoveThisSemicolon { | ||
10 | fn fix( | ||
11 | &self, | ||
12 | sema: &Semantics<RootDatabase>, | ||
13 | _resolve: &AssistResolveStrategy, | ||
14 | ) -> Option<Assist> { | ||
15 | let root = sema.db.parse_or_expand(self.file)?; | ||
16 | |||
17 | let semicolon = self | ||
18 | .expr | ||
19 | .to_node(&root) | ||
20 | .syntax() | ||
21 | .parent() | ||
22 | .and_then(ast::ExprStmt::cast) | ||
23 | .and_then(|expr| expr.semicolon_token())? | ||
24 | .text_range(); | ||
25 | |||
26 | let edit = TextEdit::delete(semicolon); | ||
27 | let source_change = SourceChange::from_text_edit(self.file.original_file(sema.db), edit); | ||
28 | |||
29 | Some(fix("remove_semicolon", "Remove this semicolon", source_change, semicolon)) | ||
30 | } | ||
31 | } | ||
32 | |||
33 | #[cfg(test)] | ||
34 | mod tests { | ||
35 | use crate::diagnostics::tests::check_fix; | ||
36 | |||
37 | #[test] | ||
38 | fn remove_semicolon() { | ||
39 | check_fix(r#"fn f() -> i32 { 92$0; }"#, r#"fn f() -> i32 { 92 }"#); | ||
40 | } | ||
41 | } | ||
diff --git a/crates/ide/src/diagnostics/fixes/replace_with_find_map.rs b/crates/ide/src/diagnostics/fixes/replace_with_find_map.rs new file mode 100644 index 000000000..b0ef7b44a --- /dev/null +++ b/crates/ide/src/diagnostics/fixes/replace_with_find_map.rs | |||
@@ -0,0 +1,84 @@ | |||
1 | use hir::{db::AstDatabase, diagnostics::ReplaceFilterMapNextWithFindMap, Semantics}; | ||
2 | use ide_assists::{Assist, AssistResolveStrategy}; | ||
3 | use ide_db::{source_change::SourceChange, RootDatabase}; | ||
4 | use syntax::{ | ||
5 | ast::{self, ArgListOwner}, | ||
6 | AstNode, TextRange, | ||
7 | }; | ||
8 | use text_edit::TextEdit; | ||
9 | |||
10 | use crate::diagnostics::{fix, DiagnosticWithFix}; | ||
11 | |||
12 | impl DiagnosticWithFix for ReplaceFilterMapNextWithFindMap { | ||
13 | fn fix( | ||
14 | &self, | ||
15 | sema: &Semantics<RootDatabase>, | ||
16 | _resolve: &AssistResolveStrategy, | ||
17 | ) -> Option<Assist> { | ||
18 | let root = sema.db.parse_or_expand(self.file)?; | ||
19 | let next_expr = self.next_expr.to_node(&root); | ||
20 | let next_call = ast::MethodCallExpr::cast(next_expr.syntax().clone())?; | ||
21 | |||
22 | let filter_map_call = ast::MethodCallExpr::cast(next_call.receiver()?.syntax().clone())?; | ||
23 | let filter_map_name_range = filter_map_call.name_ref()?.ident_token()?.text_range(); | ||
24 | let filter_map_args = filter_map_call.arg_list()?; | ||
25 | |||
26 | let range_to_replace = | ||
27 | TextRange::new(filter_map_name_range.start(), next_expr.syntax().text_range().end()); | ||
28 | let replacement = format!("find_map{}", filter_map_args.syntax().text()); | ||
29 | let trigger_range = next_expr.syntax().text_range(); | ||
30 | |||
31 | let edit = TextEdit::replace(range_to_replace, replacement); | ||
32 | |||
33 | let source_change = SourceChange::from_text_edit(self.file.original_file(sema.db), edit); | ||
34 | |||
35 | Some(fix( | ||
36 | "replace_with_find_map", | ||
37 | "Replace filter_map(..).next() with find_map()", | ||
38 | source_change, | ||
39 | trigger_range, | ||
40 | )) | ||
41 | } | ||
42 | } | ||
43 | |||
44 | #[cfg(test)] | ||
45 | mod tests { | ||
46 | use crate::diagnostics::tests::check_fix; | ||
47 | |||
48 | #[test] | ||
49 | fn replace_with_wind_map() { | ||
50 | check_fix( | ||
51 | r#" | ||
52 | //- /main.rs crate:main deps:core | ||
53 | use core::iter::Iterator; | ||
54 | use core::option::Option::{self, Some, None}; | ||
55 | fn foo() { | ||
56 | let m = [1, 2, 3].iter().$0filter_map(|x| if *x == 2 { Some (4) } else { None }).next(); | ||
57 | } | ||
58 | //- /core/lib.rs crate:core | ||
59 | pub mod option { | ||
60 | pub enum Option<T> { Some(T), None } | ||
61 | } | ||
62 | pub mod iter { | ||
63 | pub trait Iterator { | ||
64 | type Item; | ||
65 | fn filter_map<B, F>(self, f: F) -> FilterMap where F: FnMut(Self::Item) -> Option<B> { FilterMap } | ||
66 | fn next(&mut self) -> Option<Self::Item>; | ||
67 | } | ||
68 | pub struct FilterMap {} | ||
69 | impl Iterator for FilterMap { | ||
70 | type Item = i32; | ||
71 | fn next(&mut self) -> i32 { 7 } | ||
72 | } | ||
73 | } | ||
74 | "#, | ||
75 | r#" | ||
76 | use core::iter::Iterator; | ||
77 | use core::option::Option::{self, Some, None}; | ||
78 | fn foo() { | ||
79 | let m = [1, 2, 3].iter().find_map(|x| if *x == 2 { Some (4) } else { None }); | ||
80 | } | ||
81 | "#, | ||
82 | ) | ||
83 | } | ||
84 | } | ||
diff --git a/crates/ide/src/diagnostics/fixes/unresolved_module.rs b/crates/ide/src/diagnostics/fixes/unresolved_module.rs new file mode 100644 index 000000000..81244b293 --- /dev/null +++ b/crates/ide/src/diagnostics/fixes/unresolved_module.rs | |||
@@ -0,0 +1,87 @@ | |||
1 | use hir::{db::AstDatabase, diagnostics::UnresolvedModule, Semantics}; | ||
2 | use ide_assists::{Assist, AssistResolveStrategy}; | ||
3 | use ide_db::{base_db::AnchoredPathBuf, source_change::FileSystemEdit, RootDatabase}; | ||
4 | use syntax::AstNode; | ||
5 | |||
6 | use crate::diagnostics::{fix, DiagnosticWithFix}; | ||
7 | |||
8 | impl DiagnosticWithFix for UnresolvedModule { | ||
9 | fn fix( | ||
10 | &self, | ||
11 | sema: &Semantics<RootDatabase>, | ||
12 | _resolve: &AssistResolveStrategy, | ||
13 | ) -> Option<Assist> { | ||
14 | let root = sema.db.parse_or_expand(self.file)?; | ||
15 | let unresolved_module = self.decl.to_node(&root); | ||
16 | Some(fix( | ||
17 | "create_module", | ||
18 | "Create module", | ||
19 | FileSystemEdit::CreateFile { | ||
20 | dst: AnchoredPathBuf { | ||
21 | anchor: self.file.original_file(sema.db), | ||
22 | path: self.candidate.clone(), | ||
23 | }, | ||
24 | initial_contents: "".to_string(), | ||
25 | } | ||
26 | .into(), | ||
27 | unresolved_module.syntax().text_range(), | ||
28 | )) | ||
29 | } | ||
30 | } | ||
31 | |||
32 | #[cfg(test)] | ||
33 | mod tests { | ||
34 | use expect_test::expect; | ||
35 | |||
36 | use crate::diagnostics::tests::check_expect; | ||
37 | |||
38 | #[test] | ||
39 | fn test_unresolved_module_diagnostic() { | ||
40 | check_expect( | ||
41 | r#"mod foo;"#, | ||
42 | expect![[r#" | ||
43 | [ | ||
44 | Diagnostic { | ||
45 | message: "unresolved module", | ||
46 | range: 0..8, | ||
47 | severity: Error, | ||
48 | fix: Some( | ||
49 | Assist { | ||
50 | id: AssistId( | ||
51 | "create_module", | ||
52 | QuickFix, | ||
53 | ), | ||
54 | label: "Create module", | ||
55 | group: None, | ||
56 | target: 0..8, | ||
57 | source_change: Some( | ||
58 | SourceChange { | ||
59 | source_file_edits: {}, | ||
60 | file_system_edits: [ | ||
61 | CreateFile { | ||
62 | dst: AnchoredPathBuf { | ||
63 | anchor: FileId( | ||
64 | 0, | ||
65 | ), | ||
66 | path: "foo.rs", | ||
67 | }, | ||
68 | initial_contents: "", | ||
69 | }, | ||
70 | ], | ||
71 | is_snippet: false, | ||
72 | }, | ||
73 | ), | ||
74 | }, | ||
75 | ), | ||
76 | unused: false, | ||
77 | code: Some( | ||
78 | DiagnosticCode( | ||
79 | "unresolved-module", | ||
80 | ), | ||
81 | ), | ||
82 | }, | ||
83 | ] | ||
84 | "#]], | ||
85 | ); | ||
86 | } | ||
87 | } | ||
diff --git a/crates/ide/src/diagnostics/fixes/wrap_tail_expr.rs b/crates/ide/src/diagnostics/fixes/wrap_tail_expr.rs new file mode 100644 index 000000000..66676064a --- /dev/null +++ b/crates/ide/src/diagnostics/fixes/wrap_tail_expr.rs | |||
@@ -0,0 +1,211 @@ | |||
1 | use hir::{db::AstDatabase, diagnostics::MissingOkOrSomeInTailExpr, Semantics}; | ||
2 | use ide_assists::{Assist, AssistResolveStrategy}; | ||
3 | use ide_db::{source_change::SourceChange, RootDatabase}; | ||
4 | use syntax::AstNode; | ||
5 | use text_edit::TextEdit; | ||
6 | |||
7 | use crate::diagnostics::{fix, DiagnosticWithFix}; | ||
8 | |||
9 | impl DiagnosticWithFix for MissingOkOrSomeInTailExpr { | ||
10 | fn fix( | ||
11 | &self, | ||
12 | sema: &Semantics<RootDatabase>, | ||
13 | _resolve: &AssistResolveStrategy, | ||
14 | ) -> Option<Assist> { | ||
15 | let root = sema.db.parse_or_expand(self.file)?; | ||
16 | let tail_expr = self.expr.to_node(&root); | ||
17 | let tail_expr_range = tail_expr.syntax().text_range(); | ||
18 | let replacement = format!("{}({})", self.required, tail_expr.syntax()); | ||
19 | let edit = TextEdit::replace(tail_expr_range, replacement); | ||
20 | let source_change = SourceChange::from_text_edit(self.file.original_file(sema.db), edit); | ||
21 | let name = if self.required == "Ok" { "Wrap with Ok" } else { "Wrap with Some" }; | ||
22 | Some(fix("wrap_tail_expr", name, source_change, tail_expr_range)) | ||
23 | } | ||
24 | } | ||
25 | |||
26 | #[cfg(test)] | ||
27 | mod tests { | ||
28 | use crate::diagnostics::tests::{check_fix, check_no_diagnostics}; | ||
29 | |||
30 | #[test] | ||
31 | fn test_wrap_return_type_option() { | ||
32 | check_fix( | ||
33 | r#" | ||
34 | //- /main.rs crate:main deps:core | ||
35 | use core::option::Option::{self, Some, None}; | ||
36 | |||
37 | fn div(x: i32, y: i32) -> Option<i32> { | ||
38 | if y == 0 { | ||
39 | return None; | ||
40 | } | ||
41 | x / y$0 | ||
42 | } | ||
43 | //- /core/lib.rs crate:core | ||
44 | pub mod result { | ||
45 | pub enum Result<T, E> { Ok(T), Err(E) } | ||
46 | } | ||
47 | pub mod option { | ||
48 | pub enum Option<T> { Some(T), None } | ||
49 | } | ||
50 | "#, | ||
51 | r#" | ||
52 | use core::option::Option::{self, Some, None}; | ||
53 | |||
54 | fn div(x: i32, y: i32) -> Option<i32> { | ||
55 | if y == 0 { | ||
56 | return None; | ||
57 | } | ||
58 | Some(x / y) | ||
59 | } | ||
60 | "#, | ||
61 | ); | ||
62 | } | ||
63 | |||
64 | #[test] | ||
65 | fn test_wrap_return_type() { | ||
66 | check_fix( | ||
67 | r#" | ||
68 | //- /main.rs crate:main deps:core | ||
69 | use core::result::Result::{self, Ok, Err}; | ||
70 | |||
71 | fn div(x: i32, y: i32) -> Result<i32, ()> { | ||
72 | if y == 0 { | ||
73 | return Err(()); | ||
74 | } | ||
75 | x / y$0 | ||
76 | } | ||
77 | //- /core/lib.rs crate:core | ||
78 | pub mod result { | ||
79 | pub enum Result<T, E> { Ok(T), Err(E) } | ||
80 | } | ||
81 | pub mod option { | ||
82 | pub enum Option<T> { Some(T), None } | ||
83 | } | ||
84 | "#, | ||
85 | r#" | ||
86 | use core::result::Result::{self, Ok, Err}; | ||
87 | |||
88 | fn div(x: i32, y: i32) -> Result<i32, ()> { | ||
89 | if y == 0 { | ||
90 | return Err(()); | ||
91 | } | ||
92 | Ok(x / y) | ||
93 | } | ||
94 | "#, | ||
95 | ); | ||
96 | } | ||
97 | |||
98 | #[test] | ||
99 | fn test_wrap_return_type_handles_generic_functions() { | ||
100 | check_fix( | ||
101 | r#" | ||
102 | //- /main.rs crate:main deps:core | ||
103 | use core::result::Result::{self, Ok, Err}; | ||
104 | |||
105 | fn div<T>(x: T) -> Result<T, i32> { | ||
106 | if x == 0 { | ||
107 | return Err(7); | ||
108 | } | ||
109 | $0x | ||
110 | } | ||
111 | //- /core/lib.rs crate:core | ||
112 | pub mod result { | ||
113 | pub enum Result<T, E> { Ok(T), Err(E) } | ||
114 | } | ||
115 | pub mod option { | ||
116 | pub enum Option<T> { Some(T), None } | ||
117 | } | ||
118 | "#, | ||
119 | r#" | ||
120 | use core::result::Result::{self, Ok, Err}; | ||
121 | |||
122 | fn div<T>(x: T) -> Result<T, i32> { | ||
123 | if x == 0 { | ||
124 | return Err(7); | ||
125 | } | ||
126 | Ok(x) | ||
127 | } | ||
128 | "#, | ||
129 | ); | ||
130 | } | ||
131 | |||
132 | #[test] | ||
133 | fn test_wrap_return_type_handles_type_aliases() { | ||
134 | check_fix( | ||
135 | r#" | ||
136 | //- /main.rs crate:main deps:core | ||
137 | use core::result::Result::{self, Ok, Err}; | ||
138 | |||
139 | type MyResult<T> = Result<T, ()>; | ||
140 | |||
141 | fn div(x: i32, y: i32) -> MyResult<i32> { | ||
142 | if y == 0 { | ||
143 | return Err(()); | ||
144 | } | ||
145 | x $0/ y | ||
146 | } | ||
147 | //- /core/lib.rs crate:core | ||
148 | pub mod result { | ||
149 | pub enum Result<T, E> { Ok(T), Err(E) } | ||
150 | } | ||
151 | pub mod option { | ||
152 | pub enum Option<T> { Some(T), None } | ||
153 | } | ||
154 | "#, | ||
155 | r#" | ||
156 | use core::result::Result::{self, Ok, Err}; | ||
157 | |||
158 | type MyResult<T> = Result<T, ()>; | ||
159 | |||
160 | fn div(x: i32, y: i32) -> MyResult<i32> { | ||
161 | if y == 0 { | ||
162 | return Err(()); | ||
163 | } | ||
164 | Ok(x / y) | ||
165 | } | ||
166 | "#, | ||
167 | ); | ||
168 | } | ||
169 | |||
170 | #[test] | ||
171 | fn test_wrap_return_type_not_applicable_when_expr_type_does_not_match_ok_type() { | ||
172 | check_no_diagnostics( | ||
173 | r#" | ||
174 | //- /main.rs crate:main deps:core | ||
175 | use core::result::Result::{self, Ok, Err}; | ||
176 | |||
177 | fn foo() -> Result<(), i32> { 0 } | ||
178 | |||
179 | //- /core/lib.rs crate:core | ||
180 | pub mod result { | ||
181 | pub enum Result<T, E> { Ok(T), Err(E) } | ||
182 | } | ||
183 | pub mod option { | ||
184 | pub enum Option<T> { Some(T), None } | ||
185 | } | ||
186 | "#, | ||
187 | ); | ||
188 | } | ||
189 | |||
190 | #[test] | ||
191 | fn test_wrap_return_type_not_applicable_when_return_type_is_not_result_or_option() { | ||
192 | check_no_diagnostics( | ||
193 | r#" | ||
194 | //- /main.rs crate:main deps:core | ||
195 | use core::result::Result::{self, Ok, Err}; | ||
196 | |||
197 | enum SomeOtherEnum { Ok(i32), Err(String) } | ||
198 | |||
199 | fn foo() -> SomeOtherEnum { 0 } | ||
200 | |||
201 | //- /core/lib.rs crate:core | ||
202 | pub mod result { | ||
203 | pub enum Result<T, E> { Ok(T), Err(E) } | ||
204 | } | ||
205 | pub mod option { | ||
206 | pub enum Option<T> { Some(T), None } | ||
207 | } | ||
208 | "#, | ||
209 | ); | ||
210 | } | ||
211 | } | ||
diff --git a/crates/ide/src/doc_links.rs b/crates/ide/src/doc_links.rs index cb5a8e19a..320694a17 100644 --- a/crates/ide/src/doc_links.rs +++ b/crates/ide/src/doc_links.rs | |||
@@ -29,7 +29,8 @@ pub(crate) type DocumentationLink = String; | |||
29 | /// Rewrite documentation links in markdown to point to an online host (e.g. docs.rs) | 29 | /// Rewrite documentation links in markdown to point to an online host (e.g. docs.rs) |
30 | pub(crate) fn rewrite_links(db: &RootDatabase, markdown: &str, definition: &Definition) -> String { | 30 | pub(crate) fn rewrite_links(db: &RootDatabase, markdown: &str, definition: &Definition) -> String { |
31 | let mut cb = broken_link_clone_cb; | 31 | let mut cb = broken_link_clone_cb; |
32 | let doc = Parser::new_with_broken_link_callback(markdown, Options::empty(), Some(&mut cb)); | 32 | let doc = |
33 | Parser::new_with_broken_link_callback(markdown, Options::ENABLE_TASKLISTS, Some(&mut cb)); | ||
33 | 34 | ||
34 | let doc = map_links(doc, |target, title: &str| { | 35 | let doc = map_links(doc, |target, title: &str| { |
35 | // This check is imperfect, there's some overlap between valid intra-doc links | 36 | // This check is imperfect, there's some overlap between valid intra-doc links |
@@ -64,8 +65,7 @@ pub(crate) fn rewrite_links(db: &RootDatabase, markdown: &str, definition: &Defi | |||
64 | pub(crate) fn remove_links(markdown: &str) -> String { | 65 | pub(crate) fn remove_links(markdown: &str) -> String { |
65 | let mut drop_link = false; | 66 | let mut drop_link = false; |
66 | 67 | ||
67 | let mut opts = Options::empty(); | 68 | let opts = Options::ENABLE_TASKLISTS | Options::ENABLE_FOOTNOTES; |
68 | opts.insert(Options::ENABLE_FOOTNOTES); | ||
69 | 69 | ||
70 | let mut cb = |_: BrokenLink| { | 70 | let mut cb = |_: BrokenLink| { |
71 | let empty = InlineStr::try_from("").unwrap(); | 71 | let empty = InlineStr::try_from("").unwrap(); |
@@ -123,7 +123,7 @@ pub(crate) fn extract_definitions_from_markdown( | |||
123 | ) -> Vec<(TextRange, String, Option<hir::Namespace>)> { | 123 | ) -> Vec<(TextRange, String, Option<hir::Namespace>)> { |
124 | Parser::new_with_broken_link_callback( | 124 | Parser::new_with_broken_link_callback( |
125 | markdown, | 125 | markdown, |
126 | Options::empty(), | 126 | Options::ENABLE_TASKLISTS, |
127 | Some(&mut broken_link_clone_cb), | 127 | Some(&mut broken_link_clone_cb), |
128 | ) | 128 | ) |
129 | .into_offset_iter() | 129 | .into_offset_iter() |
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#" |
1127 | fn main() { | 1127 | fn 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/join_lines.rs b/crates/ide/src/join_lines.rs index fe2a349e6..c67ccd1a9 100644 --- a/crates/ide/src/join_lines.rs +++ b/crates/ide/src/join_lines.rs | |||
@@ -1,8 +1,10 @@ | |||
1 | use std::convert::TryFrom; | ||
2 | |||
1 | use ide_assists::utils::extract_trivial_expression; | 3 | use ide_assists::utils::extract_trivial_expression; |
2 | use itertools::Itertools; | 4 | use itertools::Itertools; |
3 | use syntax::{ | 5 | use syntax::{ |
4 | algo::non_trivia_sibling, | 6 | algo::non_trivia_sibling, |
5 | ast::{self, AstNode, AstToken}, | 7 | ast::{self, AstNode, AstToken, IsString}, |
6 | Direction, NodeOrToken, SourceFile, | 8 | Direction, NodeOrToken, SourceFile, |
7 | SyntaxKind::{self, USE_TREE, WHITESPACE}, | 9 | SyntaxKind::{self, USE_TREE, WHITESPACE}, |
8 | SyntaxNode, SyntaxToken, TextRange, TextSize, T, | 10 | SyntaxNode, SyntaxToken, TextRange, TextSize, T, |
@@ -65,14 +67,6 @@ fn remove_newlines(edit: &mut TextEditBuilder, token: &SyntaxToken, range: TextR | |||
65 | 67 | ||
66 | fn remove_newline(edit: &mut TextEditBuilder, token: &SyntaxToken, offset: TextSize) { | 68 | fn remove_newline(edit: &mut TextEditBuilder, token: &SyntaxToken, offset: TextSize) { |
67 | if token.kind() != WHITESPACE || token.text().bytes().filter(|&b| b == b'\n').count() != 1 { | 69 | if token.kind() != WHITESPACE || token.text().bytes().filter(|&b| b == b'\n').count() != 1 { |
68 | let mut string_open_quote = false; | ||
69 | if let Some(string) = ast::String::cast(token.clone()) { | ||
70 | if let Some(range) = string.open_quote_text_range() { | ||
71 | cov_mark::hit!(join_string_literal); | ||
72 | string_open_quote = range.end() == offset; | ||
73 | } | ||
74 | } | ||
75 | |||
76 | let n_spaces_after_line_break = { | 70 | let n_spaces_after_line_break = { |
77 | let suff = &token.text()[TextRange::new( | 71 | let suff = &token.text()[TextRange::new( |
78 | offset - token.text_range().start() + TextSize::of('\n'), | 72 | offset - token.text_range().start() + TextSize::of('\n'), |
@@ -81,8 +75,23 @@ fn remove_newline(edit: &mut TextEditBuilder, token: &SyntaxToken, offset: TextS | |||
81 | suff.bytes().take_while(|&b| b == b' ').count() | 75 | suff.bytes().take_while(|&b| b == b' ').count() |
82 | }; | 76 | }; |
83 | 77 | ||
78 | let mut no_space = false; | ||
79 | if let Some(string) = ast::String::cast(token.clone()) { | ||
80 | if let Some(range) = string.open_quote_text_range() { | ||
81 | cov_mark::hit!(join_string_literal_open_quote); | ||
82 | no_space |= range.end() == offset; | ||
83 | } | ||
84 | if let Some(range) = string.close_quote_text_range() { | ||
85 | cov_mark::hit!(join_string_literal_close_quote); | ||
86 | no_space |= range.start() | ||
87 | == offset | ||
88 | + TextSize::of('\n') | ||
89 | + TextSize::try_from(n_spaces_after_line_break).unwrap(); | ||
90 | } | ||
91 | } | ||
92 | |||
84 | let range = TextRange::at(offset, ((n_spaces_after_line_break + 1) as u32).into()); | 93 | let range = TextRange::at(offset, ((n_spaces_after_line_break + 1) as u32).into()); |
85 | let replace_with = if string_open_quote { "" } else { " " }; | 94 | let replace_with = if no_space { "" } else { " " }; |
86 | edit.replace(range, replace_with.to_string()); | 95 | edit.replace(range, replace_with.to_string()); |
87 | return; | 96 | return; |
88 | } | 97 | } |
@@ -797,22 +806,54 @@ fn foo() { | |||
797 | 806 | ||
798 | #[test] | 807 | #[test] |
799 | fn join_string_literal() { | 808 | fn join_string_literal() { |
800 | cov_mark::check!(join_string_literal); | 809 | { |
801 | check_join_lines( | 810 | cov_mark::check!(join_string_literal_open_quote); |
802 | r#" | 811 | check_join_lines( |
812 | r#" | ||
803 | fn main() { | 813 | fn main() { |
804 | $0" | 814 | $0" |
805 | hello | 815 | hello |
806 | "; | 816 | "; |
807 | } | 817 | } |
808 | "#, | 818 | "#, |
809 | r#" | 819 | r#" |
810 | fn main() { | 820 | fn main() { |
811 | $0"hello | 821 | $0"hello |
812 | "; | 822 | "; |
813 | } | 823 | } |
814 | "#, | 824 | "#, |
815 | ); | 825 | ); |
826 | } | ||
827 | |||
828 | { | ||
829 | cov_mark::check!(join_string_literal_close_quote); | ||
830 | check_join_lines( | ||
831 | r#" | ||
832 | fn main() { | ||
833 | $0"hello | ||
834 | "; | ||
835 | } | ||
836 | "#, | ||
837 | r#" | ||
838 | fn main() { | ||
839 | $0"hello"; | ||
840 | } | ||
841 | "#, | ||
842 | ); | ||
843 | check_join_lines( | ||
844 | r#" | ||
845 | fn main() { | ||
846 | $0r"hello | ||
847 | "; | ||
848 | } | ||
849 | "#, | ||
850 | r#" | ||
851 | fn main() { | ||
852 | $0r"hello"; | ||
853 | } | ||
854 | "#, | ||
855 | ); | ||
856 | } | ||
816 | 857 | ||
817 | check_join_lines( | 858 | check_join_lines( |
818 | r#" | 859 | r#" |
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/matching_brace.rs b/crates/ide/src/matching_brace.rs index 261dcc255..011c8cc55 100644 --- a/crates/ide/src/matching_brace.rs +++ b/crates/ide/src/matching_brace.rs | |||
@@ -19,14 +19,10 @@ use syntax::{ | |||
19 | pub(crate) fn matching_brace(file: &SourceFile, offset: TextSize) -> Option<TextSize> { | 19 | pub(crate) fn matching_brace(file: &SourceFile, offset: TextSize) -> Option<TextSize> { |
20 | const BRACES: &[SyntaxKind] = | 20 | const BRACES: &[SyntaxKind] = |
21 | &[T!['{'], T!['}'], T!['['], T![']'], T!['('], T![')'], T![<], T![>], T![|], T![|]]; | 21 | &[T!['{'], T!['}'], T!['['], T![']'], T!['('], T![')'], T![<], T![>], T![|], T![|]]; |
22 | let (brace_token, brace_idx) = file | 22 | let (brace_token, brace_idx) = file.syntax().token_at_offset(offset).find_map(|node| { |
23 | .syntax() | 23 | let idx = BRACES.iter().position(|&brace| brace == node.kind())?; |
24 | .token_at_offset(offset) | 24 | Some((node, idx)) |
25 | .filter_map(|node| { | 25 | })?; |
26 | let idx = BRACES.iter().position(|&brace| brace == node.kind())?; | ||
27 | Some((node, idx)) | ||
28 | }) | ||
29 | .next()?; | ||
30 | let parent = brace_token.parent()?; | 26 | let parent = brace_token.parent()?; |
31 | if brace_token.kind() == T![|] && !ast::ParamList::can_cast(parent.kind()) { | 27 | if brace_token.kind() == T![|] && !ast::ParamList::can_cast(parent.kind()) { |
32 | cov_mark::hit!(pipes_not_braces); | 28 | cov_mark::hit!(pipes_not_braces); |
diff --git a/crates/ide/src/parent_module.rs b/crates/ide/src/parent_module.rs index 99365c8a7..9b1f48044 100644 --- a/crates/ide/src/parent_module.rs +++ b/crates/ide/src/parent_module.rs | |||
@@ -1,6 +1,8 @@ | |||
1 | use hir::Semantics; | 1 | use hir::Semantics; |
2 | use ide_db::base_db::{CrateId, FileId, FilePosition}; | 2 | use ide_db::{ |
3 | use ide_db::RootDatabase; | 3 | base_db::{CrateId, FileId, FilePosition}, |
4 | RootDatabase, | ||
5 | }; | ||
4 | use itertools::Itertools; | 6 | use itertools::Itertools; |
5 | use syntax::{ | 7 | use syntax::{ |
6 | algo::find_node_at_offset, | 8 | algo::find_node_at_offset, |
diff --git a/crates/ide/src/references.rs b/crates/ide/src/references.rs index 11ca7ec6b..ae492a264 100644 --- a/crates/ide/src/references.rs +++ b/crates/ide/src/references.rs | |||
@@ -65,7 +65,7 @@ pub(crate) fn find_all_refs( | |||
65 | (find_def(&sema, &syntax, position)?, false) | 65 | (find_def(&sema, &syntax, position)?, false) |
66 | }; | 66 | }; |
67 | 67 | ||
68 | let mut usages = def.usages(sema).set_scope(search_scope).all(); | 68 | let mut usages = def.usages(sema).set_scope(search_scope).include_self_refs().all(); |
69 | if is_literal_search { | 69 | if is_literal_search { |
70 | // filter for constructor-literals | 70 | // filter for constructor-literals |
71 | let refs = usages.references.values_mut(); | 71 | let refs = usages.references.values_mut(); |
@@ -1163,22 +1163,76 @@ fn foo<const FOO$0: usize>() -> usize { | |||
1163 | } | 1163 | } |
1164 | 1164 | ||
1165 | #[test] | 1165 | #[test] |
1166 | fn test_find_self_ty_in_trait_def() { | 1166 | fn test_trait() { |
1167 | check( | 1167 | check( |
1168 | r#" | 1168 | r#" |
1169 | trait Foo { | 1169 | trait Foo$0 where Self: {} |
1170 | fn f() -> Self$0; | 1170 | |
1171 | impl Foo for () {} | ||
1172 | "#, | ||
1173 | expect![[r#" | ||
1174 | Foo Trait FileId(0) 0..24 6..9 | ||
1175 | |||
1176 | FileId(0) 31..34 | ||
1177 | "#]], | ||
1178 | ); | ||
1179 | } | ||
1180 | |||
1181 | #[test] | ||
1182 | fn test_trait_self() { | ||
1183 | check( | ||
1184 | r#" | ||
1185 | trait Foo where Self$0 { | ||
1186 | fn f() -> Self; | ||
1171 | } | 1187 | } |
1188 | |||
1189 | impl Foo for () {} | ||
1172 | "#, | 1190 | "#, |
1173 | expect![[r#" | 1191 | expect![[r#" |
1174 | Self TypeParam FileId(0) 6..9 6..9 | 1192 | Self TypeParam FileId(0) 6..9 6..9 |
1175 | 1193 | ||
1176 | FileId(0) 26..30 | 1194 | FileId(0) 16..20 |
1195 | FileId(0) 37..41 | ||
1177 | "#]], | 1196 | "#]], |
1178 | ); | 1197 | ); |
1179 | } | 1198 | } |
1180 | 1199 | ||
1181 | #[test] | 1200 | #[test] |
1201 | fn test_self_ty() { | ||
1202 | check( | ||
1203 | r#" | ||
1204 | struct $0Foo; | ||
1205 | |||
1206 | impl Foo where Self: { | ||
1207 | fn f() -> Self; | ||
1208 | } | ||
1209 | "#, | ||
1210 | expect![[r#" | ||
1211 | Foo Struct FileId(0) 0..11 7..10 | ||
1212 | |||
1213 | FileId(0) 18..21 | ||
1214 | FileId(0) 28..32 | ||
1215 | FileId(0) 50..54 | ||
1216 | "#]], | ||
1217 | ); | ||
1218 | check( | ||
1219 | r#" | ||
1220 | struct Foo; | ||
1221 | |||
1222 | impl Foo where Self: { | ||
1223 | fn f() -> Self$0; | ||
1224 | } | ||
1225 | "#, | ||
1226 | expect![[r#" | ||
1227 | impl Impl FileId(0) 13..57 18..21 | ||
1228 | |||
1229 | FileId(0) 18..21 | ||
1230 | FileId(0) 28..32 | ||
1231 | FileId(0) 50..54 | ||
1232 | "#]], | ||
1233 | ); | ||
1234 | } | ||
1235 | #[test] | ||
1182 | fn test_self_variant_with_payload() { | 1236 | fn test_self_variant_with_payload() { |
1183 | check( | 1237 | check( |
1184 | r#" | 1238 | r#" |
diff --git a/crates/ide/src/references/rename.rs b/crates/ide/src/references/rename.rs index 175e7a31d..2bf953305 100644 --- a/crates/ide/src/references/rename.rs +++ b/crates/ide/src/references/rename.rs | |||
@@ -1888,4 +1888,21 @@ impl Foo { | |||
1888 | "error: Cannot rename `Self`", | 1888 | "error: Cannot rename `Self`", |
1889 | ); | 1889 | ); |
1890 | } | 1890 | } |
1891 | |||
1892 | #[test] | ||
1893 | fn test_rename_ignores_self_ty() { | ||
1894 | check( | ||
1895 | "Fo0", | ||
1896 | r#" | ||
1897 | struct $0Foo; | ||
1898 | |||
1899 | impl Foo where Self: {} | ||
1900 | "#, | ||
1901 | r#" | ||
1902 | struct Fo0; | ||
1903 | |||
1904 | impl Fo0 where Self: {} | ||
1905 | "#, | ||
1906 | ); | ||
1907 | } | ||
1891 | } | 1908 | } |
diff --git a/crates/ide/src/syntax_highlighting/highlight.rs b/crates/ide/src/syntax_highlighting/highlight.rs index b586dcc17..baed8e217 100644 --- a/crates/ide/src/syntax_highlighting/highlight.rs +++ b/crates/ide/src/syntax_highlighting/highlight.rs | |||
@@ -227,8 +227,8 @@ pub(super) fn element( | |||
227 | k if k.is_keyword() => { | 227 | k if k.is_keyword() => { |
228 | let h = Highlight::new(HlTag::Keyword); | 228 | let h = Highlight::new(HlTag::Keyword); |
229 | match k { | 229 | match k { |
230 | T![await] | 230 | T![await] => h | HlMod::Async | HlMod::ControlFlow, |
231 | | T![break] | 231 | T![break] |
232 | | T![continue] | 232 | | T![continue] |
233 | | T![else] | 233 | | T![else] |
234 | | T![if] | 234 | | T![if] |
@@ -255,6 +255,7 @@ pub(super) fn element( | |||
255 | }) | 255 | }) |
256 | .map(|modifier| h | modifier) | 256 | .map(|modifier| h | modifier) |
257 | .unwrap_or(h), | 257 | .unwrap_or(h), |
258 | T![async] => h | HlMod::Async, | ||
258 | _ => h, | 259 | _ => h, |
259 | } | 260 | } |
260 | } | 261 | } |
@@ -310,6 +311,9 @@ fn highlight_def(db: &RootDatabase, def: Definition) -> Highlight { | |||
310 | if func.is_unsafe(db) { | 311 | if func.is_unsafe(db) { |
311 | h |= HlMod::Unsafe; | 312 | h |= HlMod::Unsafe; |
312 | } | 313 | } |
314 | if func.is_async(db) { | ||
315 | h |= HlMod::Async; | ||
316 | } | ||
313 | return h; | 317 | return h; |
314 | } | 318 | } |
315 | hir::ModuleDef::Adt(hir::Adt::Struct(_)) => HlTag::Symbol(SymbolKind::Struct), | 319 | hir::ModuleDef::Adt(hir::Adt::Struct(_)) => HlTag::Symbol(SymbolKind::Struct), |
@@ -409,6 +413,9 @@ fn highlight_method_call( | |||
409 | if func.is_unsafe(sema.db) || sema.is_unsafe_method_call(&method_call) { | 413 | if func.is_unsafe(sema.db) || sema.is_unsafe_method_call(&method_call) { |
410 | h |= HlMod::Unsafe; | 414 | h |= HlMod::Unsafe; |
411 | } | 415 | } |
416 | if func.is_async(sema.db) { | ||
417 | h |= HlMod::Async; | ||
418 | } | ||
412 | if func.as_assoc_item(sema.db).and_then(|it| it.containing_trait(sema.db)).is_some() { | 419 | if func.as_assoc_item(sema.db).and_then(|it| it.containing_trait(sema.db)).is_some() { |
413 | h |= HlMod::Trait | 420 | h |= HlMod::Trait |
414 | } | 421 | } |
diff --git a/crates/ide/src/syntax_highlighting/inject.rs b/crates/ide/src/syntax_highlighting/inject.rs index bc221d599..4269d339e 100644 --- a/crates/ide/src/syntax_highlighting/inject.rs +++ b/crates/ide/src/syntax_highlighting/inject.rs | |||
@@ -6,7 +6,7 @@ use either::Either; | |||
6 | use hir::{InFile, Semantics}; | 6 | use hir::{InFile, Semantics}; |
7 | use ide_db::{call_info::ActiveParameter, helpers::rust_doc::is_rust_fence, SymbolKind}; | 7 | use ide_db::{call_info::ActiveParameter, helpers::rust_doc::is_rust_fence, SymbolKind}; |
8 | use syntax::{ | 8 | use syntax::{ |
9 | ast::{self, AstNode}, | 9 | ast::{self, AstNode, IsString}, |
10 | AstToken, NodeOrToken, SyntaxNode, SyntaxToken, TextRange, TextSize, | 10 | AstToken, NodeOrToken, SyntaxNode, SyntaxToken, TextRange, TextSize, |
11 | }; | 11 | }; |
12 | 12 | ||
diff --git a/crates/ide/src/syntax_highlighting/tags.rs b/crates/ide/src/syntax_highlighting/tags.rs index a304b3250..f4a2e7506 100644 --- a/crates/ide/src/syntax_highlighting/tags.rs +++ b/crates/ide/src/syntax_highlighting/tags.rs | |||
@@ -65,6 +65,8 @@ pub enum HlMod { | |||
65 | Static, | 65 | Static, |
66 | /// Used for items in traits and trait impls. | 66 | /// Used for items in traits and trait impls. |
67 | Trait, | 67 | Trait, |
68 | /// Used with keywords like `async` and `await`. | ||
69 | Async, | ||
68 | // Keep this last! | 70 | // Keep this last! |
69 | /// Used for unsafe functions, mutable statics, union accesses and unsafe operations. | 71 | /// Used for unsafe functions, mutable statics, union accesses and unsafe operations. |
70 | Unsafe, | 72 | Unsafe, |
@@ -186,6 +188,7 @@ impl HlMod { | |||
186 | HlMod::Mutable, | 188 | HlMod::Mutable, |
187 | HlMod::Static, | 189 | HlMod::Static, |
188 | HlMod::Trait, | 190 | HlMod::Trait, |
191 | HlMod::Async, | ||
189 | HlMod::Unsafe, | 192 | HlMod::Unsafe, |
190 | ]; | 193 | ]; |
191 | 194 | ||
@@ -203,6 +206,7 @@ impl HlMod { | |||
203 | HlMod::Mutable => "mutable", | 206 | HlMod::Mutable => "mutable", |
204 | HlMod::Static => "static", | 207 | HlMod::Static => "static", |
205 | HlMod::Trait => "trait", | 208 | HlMod::Trait => "trait", |
209 | HlMod::Async => "async", | ||
206 | HlMod::Unsafe => "unsafe", | 210 | HlMod::Unsafe => "unsafe", |
207 | } | 211 | } |
208 | } | 212 | } |
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html b/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html index 8d83ba206..921a956e6 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html | |||
@@ -37,13 +37,25 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd | |||
37 | 37 | ||
38 | .unresolved_reference { color: #FC5555; text-decoration: wavy underline; } | 38 | .unresolved_reference { color: #FC5555; text-decoration: wavy underline; } |
39 | </style> | 39 | </style> |
40 | <pre><code><span class="comment documentation">/// ```</span> | 40 | <pre><code><span class="comment documentation">//! This is a module to test doc injection.</span> |
41 | <span class="comment documentation">//! ```</span> | ||
42 | <span class="comment documentation">//! </span><span class="keyword injected">fn</span><span class="none injected"> </span><span class="function declaration injected">test</span><span class="parenthesis injected">(</span><span class="parenthesis injected">)</span><span class="none injected"> </span><span class="brace injected">{</span><span class="brace injected">}</span> | ||
43 | <span class="comment documentation">//! ```</span> | ||
44 | |||
45 | <span class="comment documentation">/// ```</span> | ||
41 | <span class="comment documentation">/// </span><span class="keyword injected">let</span><span class="none injected"> </span><span class="punctuation injected">_</span><span class="none injected"> </span><span class="operator injected">=</span><span class="none injected"> </span><span class="string_literal injected">"early doctests should not go boom"</span><span class="semicolon injected">;</span> | 46 | <span class="comment documentation">/// </span><span class="keyword injected">let</span><span class="none injected"> </span><span class="punctuation injected">_</span><span class="none injected"> </span><span class="operator injected">=</span><span class="none injected"> </span><span class="string_literal injected">"early doctests should not go boom"</span><span class="semicolon injected">;</span> |
42 | <span class="comment documentation">/// ```</span> | 47 | <span class="comment documentation">/// ```</span> |
43 | <span class="keyword">struct</span> <span class="struct declaration">Foo</span> <span class="brace">{</span> | 48 | <span class="keyword">struct</span> <span class="struct declaration">Foo</span> <span class="brace">{</span> |
44 | <span class="field declaration">bar</span><span class="colon">:</span> <span class="builtin_type">bool</span><span class="comma">,</span> | 49 | <span class="field declaration">bar</span><span class="colon">:</span> <span class="builtin_type">bool</span><span class="comma">,</span> |
45 | <span class="brace">}</span> | 50 | <span class="brace">}</span> |
46 | 51 | ||
52 | <span class="comment documentation">/// This is an impl with a code block.</span> | ||
53 | <span class="comment documentation">///</span> | ||
54 | <span class="comment documentation">/// ```</span> | ||
55 | <span class="comment documentation">/// </span><span class="keyword injected">fn</span><span class="none injected"> </span><span class="function declaration injected">foo</span><span class="parenthesis injected">(</span><span class="parenthesis injected">)</span><span class="none injected"> </span><span class="brace injected">{</span> | ||
56 | <span class="comment documentation">///</span> | ||
57 | <span class="comment documentation">/// </span><span class="brace injected">}</span> | ||
58 | <span class="comment documentation">/// ```</span> | ||
47 | <span class="keyword">impl</span> <span class="struct">Foo</span> <span class="brace">{</span> | 59 | <span class="keyword">impl</span> <span class="struct">Foo</span> <span class="brace">{</span> |
48 | <span class="comment documentation">/// ```</span> | 60 | <span class="comment documentation">/// ```</span> |
49 | <span class="comment documentation">/// </span><span class="keyword injected">let</span><span class="none injected"> </span><span class="punctuation injected">_</span><span class="none injected"> </span><span class="operator injected">=</span><span class="none injected"> </span><span class="string_literal injected">"Call me</span> | 61 | <span class="comment documentation">/// </span><span class="keyword injected">let</span><span class="none injected"> </span><span class="punctuation injected">_</span><span class="none injected"> </span><span class="operator injected">=</span><span class="none injected"> </span><span class="string_literal injected">"Call me</span> |
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlighting.html b/crates/ide/src/syntax_highlighting/test_data/highlighting.html index df4192194..0d325f3f3 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlighting.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlighting.html | |||
@@ -66,11 +66,11 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd | |||
66 | <span class="keyword">pub</span> <span class="field declaration">y</span><span class="colon">:</span> <span class="builtin_type">i32</span><span class="comma">,</span> | 66 | <span class="keyword">pub</span> <span class="field declaration">y</span><span class="colon">:</span> <span class="builtin_type">i32</span><span class="comma">,</span> |
67 | <span class="brace">}</span> | 67 | <span class="brace">}</span> |
68 | 68 | ||
69 | <span class="keyword">trait</span> <span class="trait declaration">Bar</span> <span class="brace">{</span> | 69 | <span class="keyword">trait</span> <span class="trait declaration">Bar</span> <span class="keyword">where</span> <span class="type_param">Self</span><span class="colon">:</span> <span class="brace">{</span> |
70 | <span class="keyword">fn</span> <span class="function associated declaration trait">bar</span><span class="parenthesis">(</span><span class="operator">&</span><span class="self_keyword declaration">self</span><span class="parenthesis">)</span> <span class="operator">-></span> <span class="builtin_type">i32</span><span class="semicolon">;</span> | 70 | <span class="keyword">fn</span> <span class="function associated declaration trait">bar</span><span class="parenthesis">(</span><span class="operator">&</span><span class="self_keyword declaration">self</span><span class="parenthesis">)</span> <span class="operator">-></span> <span class="builtin_type">i32</span><span class="semicolon">;</span> |
71 | <span class="brace">}</span> | 71 | <span class="brace">}</span> |
72 | 72 | ||
73 | <span class="keyword">impl</span> <span class="trait">Bar</span> <span class="keyword">for</span> <span class="struct">Foo</span> <span class="brace">{</span> | 73 | <span class="keyword">impl</span> <span class="trait">Bar</span> <span class="keyword">for</span> <span class="struct">Foo</span> <span class="keyword">where</span> <span class="self_type">Self</span><span class="colon">:</span> <span class="brace">{</span> |
74 | <span class="keyword">fn</span> <span class="function associated declaration trait">bar</span><span class="parenthesis">(</span><span class="operator">&</span><span class="self_keyword declaration">self</span><span class="parenthesis">)</span> <span class="operator">-></span> <span class="builtin_type">i32</span> <span class="brace">{</span> | 74 | <span class="keyword">fn</span> <span class="function associated declaration trait">bar</span><span class="parenthesis">(</span><span class="operator">&</span><span class="self_keyword declaration">self</span><span class="parenthesis">)</span> <span class="operator">-></span> <span class="builtin_type">i32</span> <span class="brace">{</span> |
75 | <span class="self_keyword">self</span><span class="operator">.</span><span class="field">x</span> | 75 | <span class="self_keyword">self</span><span class="operator">.</span><span class="field">x</span> |
76 | <span class="brace">}</span> | 76 | <span class="brace">}</span> |
@@ -234,4 +234,15 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd | |||
234 | <span class="variable declaration">Nope</span> <span class="operator">=></span> <span class="variable">Nope</span><span class="comma">,</span> | 234 | <span class="variable declaration">Nope</span> <span class="operator">=></span> <span class="variable">Nope</span><span class="comma">,</span> |
235 | <span class="brace">}</span> | 235 | <span class="brace">}</span> |
236 | <span class="brace">}</span> | 236 | <span class="brace">}</span> |
237 | <span class="brace">}</span> | ||
238 | |||
239 | <span class="keyword async">async</span> <span class="keyword">fn</span> <span class="function declaration async">learn_and_sing</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span> | ||
240 | <span class="keyword">let</span> <span class="variable declaration">song</span> <span class="operator">=</span> <span class="unresolved_reference">learn_song</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="operator">.</span><span class="keyword control async">await</span><span class="semicolon">;</span> | ||
241 | <span class="unresolved_reference">sing_song</span><span class="parenthesis">(</span><span class="variable consuming">song</span><span class="parenthesis">)</span><span class="operator">.</span><span class="keyword control async">await</span><span class="semicolon">;</span> | ||
242 | <span class="brace">}</span> | ||
243 | |||
244 | <span class="keyword async">async</span> <span class="keyword">fn</span> <span class="function declaration async">async_main</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span> | ||
245 | <span class="keyword">let</span> <span class="variable declaration">f1</span> <span class="operator">=</span> <span class="function async">learn_and_sing</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="semicolon">;</span> | ||
246 | <span class="keyword">let</span> <span class="variable declaration">f2</span> <span class="operator">=</span> <span class="unresolved_reference">dance</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="semicolon">;</span> | ||
247 | futures::<span class="macro">join!</span><span class="parenthesis">(</span>f1<span class="comma">,</span> f2<span class="parenthesis">)</span><span class="semicolon">;</span> | ||
237 | <span class="brace">}</span></code></pre> \ No newline at end of file | 248 | <span class="brace">}</span></code></pre> \ No newline at end of file |
diff --git a/crates/ide/src/syntax_highlighting/tests.rs b/crates/ide/src/syntax_highlighting/tests.rs index b6e952b08..8c8878d36 100644 --- a/crates/ide/src/syntax_highlighting/tests.rs +++ b/crates/ide/src/syntax_highlighting/tests.rs | |||
@@ -39,11 +39,11 @@ struct Foo { | |||
39 | pub y: i32, | 39 | pub y: i32, |
40 | } | 40 | } |
41 | 41 | ||
42 | trait Bar { | 42 | trait Bar where Self: { |
43 | fn bar(&self) -> i32; | 43 | fn bar(&self) -> i32; |
44 | } | 44 | } |
45 | 45 | ||
46 | impl Bar for Foo { | 46 | impl Bar for Foo where Self: { |
47 | fn bar(&self) -> i32 { | 47 | fn bar(&self) -> i32 { |
48 | self.x | 48 | self.x |
49 | } | 49 | } |
@@ -208,6 +208,17 @@ impl<T> Option<T> { | |||
208 | } | 208 | } |
209 | } | 209 | } |
210 | } | 210 | } |
211 | |||
212 | async fn learn_and_sing() { | ||
213 | let song = learn_song().await; | ||
214 | sing_song(song).await; | ||
215 | } | ||
216 | |||
217 | async fn async_main() { | ||
218 | let f1 = learn_and_sing(); | ||
219 | let f2 = dance(); | ||
220 | futures::join!(f1, f2); | ||
221 | } | ||
211 | "# | 222 | "# |
212 | .trim(), | 223 | .trim(), |
213 | expect_file!["./test_data/highlighting.html"], | 224 | expect_file!["./test_data/highlighting.html"], |
@@ -513,6 +524,11 @@ fn main() { | |||
513 | fn test_highlight_doc_comment() { | 524 | fn test_highlight_doc_comment() { |
514 | check_highlighting( | 525 | check_highlighting( |
515 | r#" | 526 | r#" |
527 | //! This is a module to test doc injection. | ||
528 | //! ``` | ||
529 | //! fn test() {} | ||
530 | //! ``` | ||
531 | |||
516 | /// ``` | 532 | /// ``` |
517 | /// let _ = "early doctests should not go boom"; | 533 | /// let _ = "early doctests should not go boom"; |
518 | /// ``` | 534 | /// ``` |
@@ -520,6 +536,13 @@ struct Foo { | |||
520 | bar: bool, | 536 | bar: bool, |
521 | } | 537 | } |
522 | 538 | ||
539 | /// This is an impl with a code block. | ||
540 | /// | ||
541 | /// ``` | ||
542 | /// fn foo() { | ||
543 | /// | ||
544 | /// } | ||
545 | /// ``` | ||
523 | impl Foo { | 546 | impl Foo { |
524 | /// ``` | 547 | /// ``` |
525 | /// let _ = "Call me | 548 | /// let _ = "Call me |
diff --git a/crates/ide/src/typing.rs b/crates/ide/src/typing.rs index 82c732390..4ad49eca0 100644 --- a/crates/ide/src/typing.rs +++ b/crates/ide/src/typing.rs | |||
@@ -88,7 +88,7 @@ fn on_char_typed_inner( | |||
88 | } | 88 | } |
89 | 89 | ||
90 | /// Inserts a closing `}` when the user types an opening `{`, wrapping an existing expression in a | 90 | /// Inserts a closing `}` when the user types an opening `{`, wrapping an existing expression in a |
91 | /// block. | 91 | /// block, or a part of a `use` item. |
92 | fn on_opening_brace_typed(file: &Parse<SourceFile>, offset: TextSize) -> Option<TextEdit> { | 92 | fn on_opening_brace_typed(file: &Parse<SourceFile>, offset: TextSize) -> Option<TextEdit> { |
93 | if !stdx::always!(file.tree().syntax().text().char_at(offset) == Some('{')) { | 93 | if !stdx::always!(file.tree().syntax().text().char_at(offset) == Some('{')) { |
94 | return None; | 94 | return None; |
@@ -99,30 +99,59 @@ fn on_opening_brace_typed(file: &Parse<SourceFile>, offset: TextSize) -> Option< | |||
99 | // Remove the `{` to get a better parse tree, and reparse | 99 | // Remove the `{` to get a better parse tree, and reparse |
100 | let file = file.reparse(&Indel::delete(brace_token.text_range())); | 100 | let file = file.reparse(&Indel::delete(brace_token.text_range())); |
101 | 101 | ||
102 | let mut expr: ast::Expr = find_node_at_offset(file.tree().syntax(), offset)?; | 102 | if let Some(edit) = brace_expr(&file.tree(), offset) { |
103 | if expr.syntax().text_range().start() != offset { | 103 | return Some(edit); |
104 | return None; | ||
105 | } | 104 | } |
106 | 105 | ||
107 | // Enclose the outermost expression starting at `offset` | 106 | if let Some(edit) = brace_use_path(&file.tree(), offset) { |
108 | while let Some(parent) = expr.syntax().parent() { | 107 | return Some(edit); |
109 | if parent.text_range().start() != expr.syntax().text_range().start() { | 108 | } |
110 | break; | ||
111 | } | ||
112 | 109 | ||
113 | match ast::Expr::cast(parent) { | 110 | return None; |
114 | Some(parent) => expr = parent, | 111 | |
115 | None => break, | 112 | fn brace_use_path(file: &SourceFile, offset: TextSize) -> Option<TextEdit> { |
113 | let segment: ast::PathSegment = find_node_at_offset(file.syntax(), offset)?; | ||
114 | if segment.syntax().text_range().start() != offset { | ||
115 | return None; | ||
116 | } | 116 | } |
117 | } | ||
118 | 117 | ||
119 | // If it's a statement in a block, we don't know how many statements should be included | 118 | let tree: ast::UseTree = find_node_at_offset(file.syntax(), offset)?; |
120 | if ast::ExprStmt::can_cast(expr.syntax().parent()?.kind()) { | 119 | |
121 | return None; | 120 | Some(TextEdit::insert( |
121 | tree.syntax().text_range().end() + TextSize::of("{"), | ||
122 | "}".to_string(), | ||
123 | )) | ||
122 | } | 124 | } |
123 | 125 | ||
124 | // Insert `}` right after the expression. | 126 | fn brace_expr(file: &SourceFile, offset: TextSize) -> Option<TextEdit> { |
125 | Some(TextEdit::insert(expr.syntax().text_range().end() + TextSize::of("{"), "}".to_string())) | 127 | let mut expr: ast::Expr = find_node_at_offset(file.syntax(), offset)?; |
128 | if expr.syntax().text_range().start() != offset { | ||
129 | return None; | ||
130 | } | ||
131 | |||
132 | // Enclose the outermost expression starting at `offset` | ||
133 | while let Some(parent) = expr.syntax().parent() { | ||
134 | if parent.text_range().start() != expr.syntax().text_range().start() { | ||
135 | break; | ||
136 | } | ||
137 | |||
138 | match ast::Expr::cast(parent) { | ||
139 | Some(parent) => expr = parent, | ||
140 | None => break, | ||
141 | } | ||
142 | } | ||
143 | |||
144 | // If it's a statement in a block, we don't know how many statements should be included | ||
145 | if ast::ExprStmt::can_cast(expr.syntax().parent()?.kind()) { | ||
146 | return None; | ||
147 | } | ||
148 | |||
149 | // Insert `}` right after the expression. | ||
150 | Some(TextEdit::insert( | ||
151 | expr.syntax().text_range().end() + TextSize::of("{"), | ||
152 | "}".to_string(), | ||
153 | )) | ||
154 | } | ||
126 | } | 155 | } |
127 | 156 | ||
128 | /// Returns an edit which should be applied after `=` was typed. Primarily, | 157 | /// Returns an edit which should be applied after `=` was typed. Primarily, |
@@ -440,7 +469,7 @@ fn foo() -> { 92 } | |||
440 | } | 469 | } |
441 | 470 | ||
442 | #[test] | 471 | #[test] |
443 | fn adds_closing_brace() { | 472 | fn adds_closing_brace_for_expr() { |
444 | type_char( | 473 | type_char( |
445 | '{', | 474 | '{', |
446 | r#" | 475 | r#" |
@@ -519,4 +548,87 @@ fn f() { | |||
519 | "#, | 548 | "#, |
520 | ); | 549 | ); |
521 | } | 550 | } |
551 | |||
552 | #[test] | ||
553 | fn adds_closing_brace_for_use_tree() { | ||
554 | type_char( | ||
555 | '{', | ||
556 | r#" | ||
557 | use some::$0Path; | ||
558 | "#, | ||
559 | r#" | ||
560 | use some::{Path}; | ||
561 | "#, | ||
562 | ); | ||
563 | type_char( | ||
564 | '{', | ||
565 | r#" | ||
566 | use some::{Path, $0Other}; | ||
567 | "#, | ||
568 | r#" | ||
569 | use some::{Path, {Other}}; | ||
570 | "#, | ||
571 | ); | ||
572 | type_char( | ||
573 | '{', | ||
574 | r#" | ||
575 | use some::{$0Path, Other}; | ||
576 | "#, | ||
577 | r#" | ||
578 | use some::{{Path}, Other}; | ||
579 | "#, | ||
580 | ); | ||
581 | type_char( | ||
582 | '{', | ||
583 | r#" | ||
584 | use some::path::$0to::Item; | ||
585 | "#, | ||
586 | r#" | ||
587 | use some::path::{to::Item}; | ||
588 | "#, | ||
589 | ); | ||
590 | type_char( | ||
591 | '{', | ||
592 | r#" | ||
593 | use some::$0path::to::Item; | ||
594 | "#, | ||
595 | r#" | ||
596 | use some::{path::to::Item}; | ||
597 | "#, | ||
598 | ); | ||
599 | type_char( | ||
600 | '{', | ||
601 | r#" | ||
602 | use $0some::path::to::Item; | ||
603 | "#, | ||
604 | r#" | ||
605 | use {some::path::to::Item}; | ||
606 | "#, | ||
607 | ); | ||
608 | type_char( | ||
609 | '{', | ||
610 | r#" | ||
611 | use some::path::$0to::{Item}; | ||
612 | "#, | ||
613 | r#" | ||
614 | use some::path::{to::{Item}}; | ||
615 | "#, | ||
616 | ); | ||
617 | type_char( | ||
618 | '{', | ||
619 | r#" | ||
620 | use $0Thing as _; | ||
621 | "#, | ||
622 | r#" | ||
623 | use {Thing as _}; | ||
624 | "#, | ||
625 | ); | ||
626 | |||
627 | type_char_noop( | ||
628 | '{', | ||
629 | r#" | ||
630 | use some::pa$0th::to::Item; | ||
631 | "#, | ||
632 | ); | ||
633 | } | ||
522 | } | 634 | } |
diff --git a/crates/ide/src/typing/on_enter.rs b/crates/ide/src/typing/on_enter.rs index 7d2db201a..81c4d95b1 100644 --- a/crates/ide/src/typing/on_enter.rs +++ b/crates/ide/src/typing/on_enter.rs | |||
@@ -54,6 +54,14 @@ pub(crate) fn on_enter(db: &RootDatabase, position: FilePosition) -> Option<Text | |||
54 | cov_mark::hit!(indent_block_contents); | 54 | cov_mark::hit!(indent_block_contents); |
55 | return Some(edit); | 55 | return Some(edit); |
56 | } | 56 | } |
57 | |||
58 | // Typing enter after the `{` of a use tree list. | ||
59 | if let Some(edit) = find_node_at_offset(file.syntax(), position.offset - TextSize::of('{')) | ||
60 | .and_then(|list| on_enter_in_use_tree_list(list, position)) | ||
61 | { | ||
62 | cov_mark::hit!(indent_block_contents); | ||
63 | return Some(edit); | ||
64 | } | ||
57 | } | 65 | } |
58 | 66 | ||
59 | None | 67 | None |
@@ -111,6 +119,21 @@ fn on_enter_in_block(block: ast::BlockExpr, position: FilePosition) -> Option<Te | |||
111 | Some(edit) | 119 | Some(edit) |
112 | } | 120 | } |
113 | 121 | ||
122 | fn on_enter_in_use_tree_list(list: ast::UseTreeList, position: FilePosition) -> Option<TextEdit> { | ||
123 | if list.syntax().text().contains_char('\n') { | ||
124 | return None; | ||
125 | } | ||
126 | |||
127 | let indent = IndentLevel::from_node(list.syntax()); | ||
128 | let mut edit = TextEdit::insert(position.offset, format!("\n{}$0", indent + 1)); | ||
129 | edit.union(TextEdit::insert( | ||
130 | list.r_curly_token()?.text_range().start(), | ||
131 | format!("\n{}", indent), | ||
132 | )) | ||
133 | .ok()?; | ||
134 | Some(edit) | ||
135 | } | ||
136 | |||
114 | fn block_contents(block: &ast::BlockExpr) -> Option<SyntaxNode> { | 137 | fn block_contents(block: &ast::BlockExpr) -> Option<SyntaxNode> { |
115 | let mut node = block.tail_expr().map(|e| e.syntax().clone()); | 138 | let mut node = block.tail_expr().map(|e| e.syntax().clone()); |
116 | 139 | ||
@@ -484,4 +507,96 @@ fn f() {$0 | |||
484 | "#, | 507 | "#, |
485 | ); | 508 | ); |
486 | } | 509 | } |
510 | |||
511 | #[test] | ||
512 | fn indents_use_tree_list() { | ||
513 | do_check( | ||
514 | r#" | ||
515 | use crate::{$0}; | ||
516 | "#, | ||
517 | r#" | ||
518 | use crate::{ | ||
519 | $0 | ||
520 | }; | ||
521 | "#, | ||
522 | ); | ||
523 | do_check( | ||
524 | r#" | ||
525 | use crate::{$0Object, path::to::OtherThing}; | ||
526 | "#, | ||
527 | r#" | ||
528 | use crate::{ | ||
529 | $0Object, path::to::OtherThing | ||
530 | }; | ||
531 | "#, | ||
532 | ); | ||
533 | do_check( | ||
534 | r#" | ||
535 | use {crate::{$0Object, path::to::OtherThing}}; | ||
536 | "#, | ||
537 | r#" | ||
538 | use {crate::{ | ||
539 | $0Object, path::to::OtherThing | ||
540 | }}; | ||
541 | "#, | ||
542 | ); | ||
543 | do_check( | ||
544 | r#" | ||
545 | use { | ||
546 | crate::{$0Object, path::to::OtherThing} | ||
547 | }; | ||
548 | "#, | ||
549 | r#" | ||
550 | use { | ||
551 | crate::{ | ||
552 | $0Object, path::to::OtherThing | ||
553 | } | ||
554 | }; | ||
555 | "#, | ||
556 | ); | ||
557 | } | ||
558 | |||
559 | #[test] | ||
560 | fn does_not_indent_use_tree_list_when_not_at_curly_brace() { | ||
561 | do_check_noop( | ||
562 | r#" | ||
563 | use path::{Thing$0}; | ||
564 | "#, | ||
565 | ); | ||
566 | } | ||
567 | |||
568 | #[test] | ||
569 | fn does_not_indent_use_tree_list_without_curly_braces() { | ||
570 | do_check_noop( | ||
571 | r#" | ||
572 | use path::Thing$0; | ||
573 | "#, | ||
574 | ); | ||
575 | do_check_noop( | ||
576 | r#" | ||
577 | use path::$0Thing; | ||
578 | "#, | ||
579 | ); | ||
580 | do_check_noop( | ||
581 | r#" | ||
582 | use path::Thing$0}; | ||
583 | "#, | ||
584 | ); | ||
585 | do_check_noop( | ||
586 | r#" | ||
587 | use path::{$0Thing; | ||
588 | "#, | ||
589 | ); | ||
590 | } | ||
591 | |||
592 | #[test] | ||
593 | fn does_not_indent_multiline_use_tree_list() { | ||
594 | do_check_noop( | ||
595 | r#" | ||
596 | use path::{$0 | ||
597 | Thing | ||
598 | }; | ||
599 | "#, | ||
600 | ); | ||
601 | } | ||
487 | } | 602 | } |
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/assist_context.rs b/crates/ide_assists/src/assist_context.rs index 112939948..20754a02a 100644 --- a/crates/ide_assists/src/assist_context.rs +++ b/crates/ide_assists/src/assist_context.rs | |||
@@ -13,7 +13,7 @@ use ide_db::{ | |||
13 | RootDatabase, | 13 | RootDatabase, |
14 | }; | 14 | }; |
15 | use syntax::{ | 15 | use syntax::{ |
16 | algo::{self, find_node_at_offset, find_node_at_range, SyntaxRewriter}, | 16 | algo::{self, find_node_at_offset, find_node_at_range}, |
17 | AstNode, AstToken, SourceFile, SyntaxElement, SyntaxKind, SyntaxNode, SyntaxNodePtr, | 17 | AstNode, AstToken, SourceFile, SyntaxElement, SyntaxKind, SyntaxNode, SyntaxNodePtr, |
18 | SyntaxToken, TextRange, TextSize, TokenAtOffset, | 18 | SyntaxToken, TextRange, TextSize, TokenAtOffset, |
19 | }; | 19 | }; |
@@ -238,8 +238,8 @@ impl AssistBuilder { | |||
238 | } | 238 | } |
239 | } | 239 | } |
240 | 240 | ||
241 | pub(crate) fn make_ast_mut<N: AstNode>(&mut self, node: N) -> N { | 241 | pub(crate) fn make_mut<N: AstNode>(&mut self, node: N) -> N { |
242 | N::cast(self.make_mut(node.syntax().clone())).unwrap() | 242 | self.mutated_tree.get_or_insert_with(|| TreeMutator::new(node.syntax())).make_mut(&node) |
243 | } | 243 | } |
244 | /// Returns a copy of the `node`, suitable for mutation. | 244 | /// Returns a copy of the `node`, suitable for mutation. |
245 | /// | 245 | /// |
@@ -251,7 +251,7 @@ impl AssistBuilder { | |||
251 | /// The typical pattern for an assist is to find specific nodes in the read | 251 | /// The typical pattern for an assist is to find specific nodes in the read |
252 | /// phase, and then get their mutable couterparts using `make_mut` in the | 252 | /// phase, and then get their mutable couterparts using `make_mut` in the |
253 | /// mutable state. | 253 | /// mutable state. |
254 | pub(crate) fn make_mut(&mut self, node: SyntaxNode) -> SyntaxNode { | 254 | pub(crate) fn make_syntax_mut(&mut self, node: SyntaxNode) -> SyntaxNode { |
255 | self.mutated_tree.get_or_insert_with(|| TreeMutator::new(&node)).make_syntax_mut(&node) | 255 | self.mutated_tree.get_or_insert_with(|| TreeMutator::new(&node)).make_syntax_mut(&node) |
256 | } | 256 | } |
257 | 257 | ||
@@ -290,12 +290,6 @@ impl AssistBuilder { | |||
290 | pub(crate) fn replace_ast<N: AstNode>(&mut self, old: N, new: N) { | 290 | pub(crate) fn replace_ast<N: AstNode>(&mut self, old: N, new: N) { |
291 | algo::diff(old.syntax(), new.syntax()).into_text_edit(&mut self.edit) | 291 | algo::diff(old.syntax(), new.syntax()).into_text_edit(&mut self.edit) |
292 | } | 292 | } |
293 | pub(crate) fn rewrite(&mut self, rewriter: SyntaxRewriter) { | ||
294 | if let Some(node) = rewriter.rewrite_root() { | ||
295 | let new = rewriter.rewrite(&node); | ||
296 | algo::diff(&node, &new).into_text_edit(&mut self.edit); | ||
297 | } | ||
298 | } | ||
299 | pub(crate) fn create_file(&mut self, dst: AnchoredPathBuf, content: impl Into<String>) { | 293 | pub(crate) fn create_file(&mut self, dst: AnchoredPathBuf, content: impl Into<String>) { |
300 | let file_system_edit = | 294 | let file_system_edit = |
301 | FileSystemEdit::CreateFile { dst: dst, initial_contents: content.into() }; | 295 | FileSystemEdit::CreateFile { dst: dst, initial_contents: content.into() }; |
diff --git a/crates/ide_assists/src/handlers/add_explicit_type.rs b/crates/ide_assists/src/handlers/add_explicit_type.rs index 62db31952..36589203d 100644 --- a/crates/ide_assists/src/handlers/add_explicit_type.rs +++ b/crates/ide_assists/src/handlers/add_explicit_type.rs | |||
@@ -198,6 +198,34 @@ fn main() { | |||
198 | ) | 198 | ) |
199 | } | 199 | } |
200 | 200 | ||
201 | /// https://github.com/rust-analyzer/rust-analyzer/issues/2922 | ||
202 | #[test] | ||
203 | fn regression_issue_2922() { | ||
204 | check_assist( | ||
205 | add_explicit_type, | ||
206 | r#" | ||
207 | fn main() { | ||
208 | let $0v = [0.0; 2]; | ||
209 | } | ||
210 | "#, | ||
211 | r#" | ||
212 | fn main() { | ||
213 | let v: [f64; 2] = [0.0; 2]; | ||
214 | } | ||
215 | "#, | ||
216 | ); | ||
217 | // note: this may break later if we add more consteval. it just needs to be something that our | ||
218 | // consteval engine doesn't understand | ||
219 | check_assist_not_applicable( | ||
220 | add_explicit_type, | ||
221 | r#" | ||
222 | fn main() { | ||
223 | let $0l = [0.0; 2+2]; | ||
224 | } | ||
225 | "#, | ||
226 | ); | ||
227 | } | ||
228 | |||
201 | #[test] | 229 | #[test] |
202 | fn default_generics_should_not_be_added() { | 230 | fn default_generics_should_not_be_added() { |
203 | check_assist( | 231 | check_assist( |
diff --git a/crates/ide_assists/src/handlers/add_missing_impl_members.rs b/crates/ide_assists/src/handlers/add_missing_impl_members.rs index 0148635f9..8225ae22c 100644 --- a/crates/ide_assists/src/handlers/add_missing_impl_members.rs +++ b/crates/ide_assists/src/handlers/add_missing_impl_members.rs | |||
@@ -64,7 +64,6 @@ pub(crate) fn add_missing_impl_members(acc: &mut Assists, ctx: &AssistContext) - | |||
64 | // impl Trait for () { | 64 | // impl Trait for () { |
65 | // type X = (); | 65 | // type X = (); |
66 | // fn foo(&self) {}$0 | 66 | // fn foo(&self) {}$0 |
67 | // | ||
68 | // } | 67 | // } |
69 | // ``` | 68 | // ``` |
70 | // -> | 69 | // -> |
@@ -195,6 +194,7 @@ impl Foo for S { | |||
195 | fn baz(&self) { | 194 | fn baz(&self) { |
196 | todo!() | 195 | todo!() |
197 | } | 196 | } |
197 | |||
198 | }"#, | 198 | }"#, |
199 | ); | 199 | ); |
200 | } | 200 | } |
@@ -231,6 +231,7 @@ impl Foo for S { | |||
231 | fn foo(&self) { | 231 | fn foo(&self) { |
232 | ${0:todo!()} | 232 | ${0:todo!()} |
233 | } | 233 | } |
234 | |||
234 | }"#, | 235 | }"#, |
235 | ); | 236 | ); |
236 | } | 237 | } |
diff --git a/crates/ide_assists/src/handlers/auto_import.rs b/crates/ide_assists/src/handlers/auto_import.rs index a454a2af3..dda5a6631 100644 --- a/crates/ide_assists/src/handlers/auto_import.rs +++ b/crates/ide_assists/src/handlers/auto_import.rs | |||
@@ -102,8 +102,8 @@ pub(crate) fn auto_import(acc: &mut Assists, ctx: &AssistContext) -> Option<()> | |||
102 | range, | 102 | range, |
103 | |builder| { | 103 | |builder| { |
104 | let scope = match scope.clone() { | 104 | let scope = match scope.clone() { |
105 | ImportScope::File(it) => ImportScope::File(builder.make_ast_mut(it)), | 105 | ImportScope::File(it) => ImportScope::File(builder.make_mut(it)), |
106 | ImportScope::Module(it) => ImportScope::Module(builder.make_ast_mut(it)), | 106 | ImportScope::Module(it) => ImportScope::Module(builder.make_mut(it)), |
107 | }; | 107 | }; |
108 | insert_use(&scope, mod_path_to_ast(&import.import_path), ctx.config.insert_use); | 108 | insert_use(&scope, mod_path_to_ast(&import.import_path), ctx.config.insert_use); |
109 | }, | 109 | }, |
@@ -969,4 +969,30 @@ mod bar { | |||
969 | "#, | 969 | "#, |
970 | ); | 970 | ); |
971 | } | 971 | } |
972 | |||
973 | #[test] | ||
974 | fn uses_abs_path_with_extern_crate_clash() { | ||
975 | check_assist( | ||
976 | auto_import, | ||
977 | r#" | ||
978 | //- /main.rs crate:main deps:foo | ||
979 | mod foo {} | ||
980 | |||
981 | const _: () = { | ||
982 | Foo$0 | ||
983 | }; | ||
984 | //- /foo.rs crate:foo | ||
985 | pub struct Foo | ||
986 | "#, | ||
987 | r#" | ||
988 | use ::foo::Foo; | ||
989 | |||
990 | mod foo {} | ||
991 | |||
992 | const _: () = { | ||
993 | Foo | ||
994 | }; | ||
995 | "#, | ||
996 | ); | ||
997 | } | ||
972 | } | 998 | } |
diff --git a/crates/ide_assists/src/handlers/convert_tuple_struct_to_named_struct.rs b/crates/ide_assists/src/handlers/convert_tuple_struct_to_named_struct.rs index b5b5ada5e..70949ca35 100644 --- a/crates/ide_assists/src/handlers/convert_tuple_struct_to_named_struct.rs +++ b/crates/ide_assists/src/handlers/convert_tuple_struct_to_named_struct.rs | |||
@@ -107,7 +107,7 @@ fn edit_struct_references( | |||
107 | names: &[ast::Name], | 107 | names: &[ast::Name], |
108 | ) { | 108 | ) { |
109 | let strukt_def = Definition::ModuleDef(hir::ModuleDef::Adt(hir::Adt::Struct(strukt))); | 109 | let strukt_def = Definition::ModuleDef(hir::ModuleDef::Adt(hir::Adt::Struct(strukt))); |
110 | let usages = strukt_def.usages(&ctx.sema).include_self_kw_refs(true).all(); | 110 | let usages = strukt_def.usages(&ctx.sema).include_self_refs().all(); |
111 | 111 | ||
112 | let edit_node = |edit: &mut AssistBuilder, node: SyntaxNode| -> Option<()> { | 112 | let edit_node = |edit: &mut AssistBuilder, node: SyntaxNode| -> Option<()> { |
113 | match_ast! { | 113 | match_ast! { |
diff --git a/crates/ide_assists/src/handlers/early_return.rs b/crates/ide_assists/src/handlers/early_return.rs index c66f8c05d..5eb6a57f0 100644 --- a/crates/ide_assists/src/handlers/early_return.rs +++ b/crates/ide_assists/src/handlers/early_return.rs | |||
@@ -130,9 +130,7 @@ pub(crate) fn convert_to_guarded_return(acc: &mut Assists, ctx: &AssistContext) | |||
130 | once(make::ident_pat(make::name("it")).into()), | 130 | once(make::ident_pat(make::name("it")).into()), |
131 | ); | 131 | ); |
132 | let expr = { | 132 | let expr = { |
133 | let name_ref = make::name_ref("it"); | 133 | let path = make::ext::ident_path("it"); |
134 | let segment = make::path_segment(name_ref); | ||
135 | let path = make::path_unqualified(segment); | ||
136 | make::expr_path(path) | 134 | make::expr_path(path) |
137 | }; | 135 | }; |
138 | make::match_arm(once(pat.into()), expr) | 136 | make::match_arm(once(pat.into()), expr) |
diff --git a/crates/ide_assists/src/handlers/expand_glob_import.rs b/crates/ide_assists/src/handlers/expand_glob_import.rs index 98389e4f7..79cb08d69 100644 --- a/crates/ide_assists/src/handlers/expand_glob_import.rs +++ b/crates/ide_assists/src/handlers/expand_glob_import.rs | |||
@@ -4,10 +4,10 @@ use ide_db::{ | |||
4 | defs::{Definition, NameRefClass}, | 4 | defs::{Definition, NameRefClass}, |
5 | search::SearchScope, | 5 | search::SearchScope, |
6 | }; | 6 | }; |
7 | use stdx::never; | ||
7 | use syntax::{ | 8 | use syntax::{ |
8 | algo::SyntaxRewriter, | ||
9 | ast::{self, make}, | 9 | ast::{self, make}, |
10 | AstNode, Direction, SyntaxNode, SyntaxToken, T, | 10 | ted, AstNode, Direction, SyntaxNode, SyntaxToken, T, |
11 | }; | 11 | }; |
12 | 12 | ||
13 | use crate::{ | 13 | use crate::{ |
@@ -42,6 +42,7 @@ use crate::{ | |||
42 | // ``` | 42 | // ``` |
43 | pub(crate) fn expand_glob_import(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { | 43 | pub(crate) fn expand_glob_import(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { |
44 | let star = ctx.find_token_syntax_at_offset(T![*])?; | 44 | let star = ctx.find_token_syntax_at_offset(T![*])?; |
45 | let use_tree = star.parent().and_then(ast::UseTree::cast)?; | ||
45 | let (parent, mod_path) = find_parent_and_path(&star)?; | 46 | let (parent, mod_path) = find_parent_and_path(&star)?; |
46 | let target_module = match ctx.sema.resolve_path(&mod_path)? { | 47 | let target_module = match ctx.sema.resolve_path(&mod_path)? { |
47 | PathResolution::Def(ModuleDef::Module(it)) => it, | 48 | PathResolution::Def(ModuleDef::Module(it)) => it, |
@@ -53,7 +54,6 @@ pub(crate) fn expand_glob_import(acc: &mut Assists, ctx: &AssistContext) -> Opti | |||
53 | 54 | ||
54 | let refs_in_target = find_refs_in_mod(ctx, target_module, Some(current_module))?; | 55 | let refs_in_target = find_refs_in_mod(ctx, target_module, Some(current_module))?; |
55 | let imported_defs = find_imported_defs(ctx, star)?; | 56 | let imported_defs = find_imported_defs(ctx, star)?; |
56 | let names_to_import = find_names_to_import(ctx, refs_in_target, imported_defs); | ||
57 | 57 | ||
58 | let target = parent.clone().either(|n| n.syntax().clone(), |n| n.syntax().clone()); | 58 | let target = parent.clone().either(|n| n.syntax().clone(), |n| n.syntax().clone()); |
59 | acc.add( | 59 | acc.add( |
@@ -61,9 +61,31 @@ pub(crate) fn expand_glob_import(acc: &mut Assists, ctx: &AssistContext) -> Opti | |||
61 | "Expand glob import", | 61 | "Expand glob import", |
62 | target.text_range(), | 62 | target.text_range(), |
63 | |builder| { | 63 | |builder| { |
64 | let mut rewriter = SyntaxRewriter::default(); | 64 | let use_tree = builder.make_mut(use_tree); |
65 | replace_ast(&mut rewriter, parent, mod_path, names_to_import); | 65 | |
66 | builder.rewrite(rewriter); | 66 | let names_to_import = find_names_to_import(ctx, refs_in_target, imported_defs); |
67 | let expanded = make::use_tree_list(names_to_import.iter().map(|n| { | ||
68 | let path = make::ext::ident_path(&n.to_string()); | ||
69 | make::use_tree(path, None, None, false) | ||
70 | })) | ||
71 | .clone_for_update(); | ||
72 | |||
73 | match use_tree.star_token() { | ||
74 | Some(star) => { | ||
75 | let needs_braces = use_tree.path().is_some() && names_to_import.len() > 1; | ||
76 | if needs_braces { | ||
77 | ted::replace(star, expanded.syntax()) | ||
78 | } else { | ||
79 | let without_braces = expanded | ||
80 | .syntax() | ||
81 | .children_with_tokens() | ||
82 | .filter(|child| !matches!(child.kind(), T!['{'] | T!['}'])) | ||
83 | .collect(); | ||
84 | ted::replace_with_many(star, without_braces) | ||
85 | } | ||
86 | } | ||
87 | None => never!(), | ||
88 | } | ||
67 | }, | 89 | }, |
68 | ) | 90 | ) |
69 | } | 91 | } |
@@ -232,53 +254,6 @@ fn find_names_to_import( | |||
232 | used_refs.0.iter().map(|r| r.visible_name.clone()).collect() | 254 | used_refs.0.iter().map(|r| r.visible_name.clone()).collect() |
233 | } | 255 | } |
234 | 256 | ||
235 | fn replace_ast( | ||
236 | rewriter: &mut SyntaxRewriter, | ||
237 | parent: Either<ast::UseTree, ast::UseTreeList>, | ||
238 | path: ast::Path, | ||
239 | names_to_import: Vec<Name>, | ||
240 | ) { | ||
241 | let existing_use_trees = match parent.clone() { | ||
242 | Either::Left(_) => vec![], | ||
243 | Either::Right(u) => u | ||
244 | .use_trees() | ||
245 | .filter(|n| | ||
246 | // filter out star | ||
247 | n.star_token().is_none()) | ||
248 | .collect(), | ||
249 | }; | ||
250 | |||
251 | let new_use_trees: Vec<ast::UseTree> = names_to_import | ||
252 | .iter() | ||
253 | .map(|n| { | ||
254 | let path = make::path_unqualified(make::path_segment(make::name_ref(&n.to_string()))); | ||
255 | make::use_tree(path, None, None, false) | ||
256 | }) | ||
257 | .collect(); | ||
258 | |||
259 | let use_trees = [&existing_use_trees[..], &new_use_trees[..]].concat(); | ||
260 | |||
261 | match use_trees.as_slice() { | ||
262 | [name] => { | ||
263 | if let Some(end_path) = name.path() { | ||
264 | rewriter.replace_ast( | ||
265 | &parent.left_or_else(|tl| tl.parent_use_tree()), | ||
266 | &make::use_tree(make::path_concat(path, end_path), None, None, false), | ||
267 | ); | ||
268 | } | ||
269 | } | ||
270 | names => match &parent { | ||
271 | Either::Left(parent) => rewriter.replace_ast( | ||
272 | parent, | ||
273 | &make::use_tree(path, Some(make::use_tree_list(names.to_owned())), None, false), | ||
274 | ), | ||
275 | Either::Right(parent) => { | ||
276 | rewriter.replace_ast(parent, &make::use_tree_list(names.to_owned())) | ||
277 | } | ||
278 | }, | ||
279 | }; | ||
280 | } | ||
281 | |||
282 | #[cfg(test)] | 257 | #[cfg(test)] |
283 | mod tests { | 258 | mod tests { |
284 | use crate::tests::{check_assist, check_assist_not_applicable}; | 259 | use crate::tests::{check_assist, check_assist_not_applicable}; |
@@ -350,7 +325,7 @@ mod foo { | |||
350 | pub fn f() {} | 325 | pub fn f() {} |
351 | } | 326 | } |
352 | 327 | ||
353 | use foo::{f, Baz, Bar}; | 328 | use foo::{Baz, Bar, f}; |
354 | 329 | ||
355 | fn qux(bar: Bar, baz: Baz) { | 330 | fn qux(bar: Bar, baz: Baz) { |
356 | f(); | 331 | f(); |
@@ -389,7 +364,7 @@ mod foo { | |||
389 | } | 364 | } |
390 | 365 | ||
391 | use foo::Bar; | 366 | use foo::Bar; |
392 | use foo::{f, Baz}; | 367 | use foo::{Baz, f}; |
393 | 368 | ||
394 | fn qux(bar: Bar, baz: Baz) { | 369 | fn qux(bar: Bar, baz: Baz) { |
395 | f(); | 370 | f(); |
@@ -439,7 +414,7 @@ mod foo { | |||
439 | } | 414 | } |
440 | } | 415 | } |
441 | 416 | ||
442 | use foo::{bar::{f, Baz, Bar}, baz::*}; | 417 | use foo::{bar::{Baz, Bar, f}, baz::*}; |
443 | 418 | ||
444 | fn qux(bar: Bar, baz: Baz) { | 419 | fn qux(bar: Bar, baz: Baz) { |
445 | f(); | 420 | f(); |
@@ -891,7 +866,7 @@ mod foo { | |||
891 | pub struct Bar; | 866 | pub struct Bar; |
892 | } | 867 | } |
893 | 868 | ||
894 | use foo::Bar; | 869 | use foo::{Bar}; |
895 | 870 | ||
896 | struct Baz { | 871 | struct Baz { |
897 | bar: Bar | 872 | bar: Bar |
diff --git a/crates/ide_assists/src/handlers/extract_function.rs b/crates/ide_assists/src/handlers/extract_function.rs index 93b28370c..6311afc1f 100644 --- a/crates/ide_assists/src/handlers/extract_function.rs +++ b/crates/ide_assists/src/handlers/extract_function.rs | |||
@@ -10,7 +10,6 @@ use ide_db::{ | |||
10 | use itertools::Itertools; | 10 | use itertools::Itertools; |
11 | use stdx::format_to; | 11 | use stdx::format_to; |
12 | use syntax::{ | 12 | use syntax::{ |
13 | algo::SyntaxRewriter, | ||
14 | ast::{ | 13 | ast::{ |
15 | self, | 14 | self, |
16 | edit::{AstNodeEdit, IndentLevel}, | 15 | edit::{AstNodeEdit, IndentLevel}, |
@@ -957,10 +956,10 @@ fn format_replacement(ctx: &AssistContext, fun: &Function, indent: IndentLevel) | |||
957 | let args = fun.params.iter().map(|param| param.to_arg(ctx)); | 956 | let args = fun.params.iter().map(|param| param.to_arg(ctx)); |
958 | let args = make::arg_list(args); | 957 | let args = make::arg_list(args); |
959 | let call_expr = if fun.self_param.is_some() { | 958 | let call_expr = if fun.self_param.is_some() { |
960 | let self_arg = make::expr_path(make_path_from_text("self")); | 959 | let self_arg = make::expr_path(make::ext::ident_path("self")); |
961 | make::expr_method_call(self_arg, &fun.name, args) | 960 | make::expr_method_call(self_arg, &fun.name, args) |
962 | } else { | 961 | } else { |
963 | let func = make::expr_path(make_path_from_text(&fun.name)); | 962 | let func = make::expr_path(make::ext::ident_path(&fun.name)); |
964 | make::expr_call(func, args) | 963 | make::expr_call(func, args) |
965 | }; | 964 | }; |
966 | 965 | ||
@@ -1055,11 +1054,11 @@ impl FlowHandler { | |||
1055 | make::expr_if(condition, block, None) | 1054 | make::expr_if(condition, block, None) |
1056 | } | 1055 | } |
1057 | FlowHandler::IfOption { action } => { | 1056 | FlowHandler::IfOption { action } => { |
1058 | let path = make_path_from_text("Some"); | 1057 | let path = make::ext::ident_path("Some"); |
1059 | let value_pat = make::ident_pat(make::name("value")); | 1058 | let value_pat = make::ident_pat(make::name("value")); |
1060 | let pattern = make::tuple_struct_pat(path, iter::once(value_pat.into())); | 1059 | let pattern = make::tuple_struct_pat(path, iter::once(value_pat.into())); |
1061 | let cond = make::condition(call_expr, Some(pattern.into())); | 1060 | let cond = make::condition(call_expr, Some(pattern.into())); |
1062 | let value = make::expr_path(make_path_from_text("value")); | 1061 | let value = make::expr_path(make::ext::ident_path("value")); |
1063 | let action_expr = action.make_result_handler(Some(value)); | 1062 | let action_expr = action.make_result_handler(Some(value)); |
1064 | let action_stmt = make::expr_stmt(action_expr); | 1063 | let action_stmt = make::expr_stmt(action_expr); |
1065 | let then = make::block_expr(iter::once(action_stmt.into()), None); | 1064 | let then = make::block_expr(iter::once(action_stmt.into()), None); |
@@ -1069,14 +1068,14 @@ impl FlowHandler { | |||
1069 | let some_name = "value"; | 1068 | let some_name = "value"; |
1070 | 1069 | ||
1071 | let some_arm = { | 1070 | let some_arm = { |
1072 | let path = make_path_from_text("Some"); | 1071 | let path = make::ext::ident_path("Some"); |
1073 | let value_pat = make::ident_pat(make::name(some_name)); | 1072 | let value_pat = make::ident_pat(make::name(some_name)); |
1074 | let pat = make::tuple_struct_pat(path, iter::once(value_pat.into())); | 1073 | let pat = make::tuple_struct_pat(path, iter::once(value_pat.into())); |
1075 | let value = make::expr_path(make_path_from_text(some_name)); | 1074 | let value = make::expr_path(make::ext::ident_path(some_name)); |
1076 | make::match_arm(iter::once(pat.into()), value) | 1075 | make::match_arm(iter::once(pat.into()), value) |
1077 | }; | 1076 | }; |
1078 | let none_arm = { | 1077 | let none_arm = { |
1079 | let path = make_path_from_text("None"); | 1078 | let path = make::ext::ident_path("None"); |
1080 | let pat = make::path_pat(path); | 1079 | let pat = make::path_pat(path); |
1081 | make::match_arm(iter::once(pat), none.make_result_handler(None)) | 1080 | make::match_arm(iter::once(pat), none.make_result_handler(None)) |
1082 | }; | 1081 | }; |
@@ -1088,17 +1087,17 @@ impl FlowHandler { | |||
1088 | let err_name = "value"; | 1087 | let err_name = "value"; |
1089 | 1088 | ||
1090 | let ok_arm = { | 1089 | let ok_arm = { |
1091 | let path = make_path_from_text("Ok"); | 1090 | let path = make::ext::ident_path("Ok"); |
1092 | let value_pat = make::ident_pat(make::name(ok_name)); | 1091 | let value_pat = make::ident_pat(make::name(ok_name)); |
1093 | let pat = make::tuple_struct_pat(path, iter::once(value_pat.into())); | 1092 | let pat = make::tuple_struct_pat(path, iter::once(value_pat.into())); |
1094 | let value = make::expr_path(make_path_from_text(ok_name)); | 1093 | let value = make::expr_path(make::ext::ident_path(ok_name)); |
1095 | make::match_arm(iter::once(pat.into()), value) | 1094 | make::match_arm(iter::once(pat.into()), value) |
1096 | }; | 1095 | }; |
1097 | let err_arm = { | 1096 | let err_arm = { |
1098 | let path = make_path_from_text("Err"); | 1097 | let path = make::ext::ident_path("Err"); |
1099 | let value_pat = make::ident_pat(make::name(err_name)); | 1098 | let value_pat = make::ident_pat(make::name(err_name)); |
1100 | let pat = make::tuple_struct_pat(path, iter::once(value_pat.into())); | 1099 | let pat = make::tuple_struct_pat(path, iter::once(value_pat.into())); |
1101 | let value = make::expr_path(make_path_from_text(err_name)); | 1100 | let value = make::expr_path(make::ext::ident_path(err_name)); |
1102 | make::match_arm(iter::once(pat.into()), err.make_result_handler(Some(value))) | 1101 | make::match_arm(iter::once(pat.into()), err.make_result_handler(Some(value))) |
1103 | }; | 1102 | }; |
1104 | let arms = make::match_arm_list(vec![ok_arm, err_arm]); | 1103 | let arms = make::match_arm_list(vec![ok_arm, err_arm]); |
@@ -1108,13 +1107,9 @@ impl FlowHandler { | |||
1108 | } | 1107 | } |
1109 | } | 1108 | } |
1110 | 1109 | ||
1111 | fn make_path_from_text(text: &str) -> ast::Path { | ||
1112 | make::path_unqualified(make::path_segment(make::name_ref(text))) | ||
1113 | } | ||
1114 | |||
1115 | fn path_expr_from_local(ctx: &AssistContext, var: Local) -> ast::Expr { | 1110 | fn path_expr_from_local(ctx: &AssistContext, var: Local) -> ast::Expr { |
1116 | let name = var.name(ctx.db()).unwrap().to_string(); | 1111 | let name = var.name(ctx.db()).unwrap().to_string(); |
1117 | make::expr_path(make_path_from_text(&name)) | 1112 | make::expr_path(make::ext::ident_path(&name)) |
1118 | } | 1113 | } |
1119 | 1114 | ||
1120 | fn format_function( | 1115 | fn format_function( |
@@ -1180,7 +1175,7 @@ fn make_ret_ty(ctx: &AssistContext, module: hir::Module, fun: &Function) -> Opti | |||
1180 | fun_ty.make_ty(ctx, module) | 1175 | fun_ty.make_ty(ctx, module) |
1181 | } | 1176 | } |
1182 | FlowHandler::Try { kind: TryKind::Option } => { | 1177 | FlowHandler::Try { kind: TryKind::Option } => { |
1183 | make::ty_generic(make::name_ref("Option"), iter::once(fun_ty.make_ty(ctx, module))) | 1178 | make::ext::ty_option(fun_ty.make_ty(ctx, module)) |
1184 | } | 1179 | } |
1185 | FlowHandler::Try { kind: TryKind::Result { ty: parent_ret_ty } } => { | 1180 | FlowHandler::Try { kind: TryKind::Result { ty: parent_ret_ty } } => { |
1186 | let handler_ty = parent_ret_ty | 1181 | let handler_ty = parent_ret_ty |
@@ -1188,29 +1183,21 @@ fn make_ret_ty(ctx: &AssistContext, module: hir::Module, fun: &Function) -> Opti | |||
1188 | .nth(1) | 1183 | .nth(1) |
1189 | .map(|ty| make_ty(&ty, ctx, module)) | 1184 | .map(|ty| make_ty(&ty, ctx, module)) |
1190 | .unwrap_or_else(make::ty_unit); | 1185 | .unwrap_or_else(make::ty_unit); |
1191 | make::ty_generic( | 1186 | make::ext::ty_result(fun_ty.make_ty(ctx, module), handler_ty) |
1192 | make::name_ref("Result"), | ||
1193 | vec![fun_ty.make_ty(ctx, module), handler_ty], | ||
1194 | ) | ||
1195 | } | 1187 | } |
1196 | FlowHandler::If { .. } => make::ty("bool"), | 1188 | FlowHandler::If { .. } => make::ext::ty_bool(), |
1197 | FlowHandler::IfOption { action } => { | 1189 | FlowHandler::IfOption { action } => { |
1198 | let handler_ty = action | 1190 | let handler_ty = action |
1199 | .expr_ty(ctx) | 1191 | .expr_ty(ctx) |
1200 | .map(|ty| make_ty(&ty, ctx, module)) | 1192 | .map(|ty| make_ty(&ty, ctx, module)) |
1201 | .unwrap_or_else(make::ty_unit); | 1193 | .unwrap_or_else(make::ty_unit); |
1202 | make::ty_generic(make::name_ref("Option"), iter::once(handler_ty)) | 1194 | make::ext::ty_option(handler_ty) |
1203 | } | ||
1204 | FlowHandler::MatchOption { .. } => { | ||
1205 | make::ty_generic(make::name_ref("Option"), iter::once(fun_ty.make_ty(ctx, module))) | ||
1206 | } | 1195 | } |
1196 | FlowHandler::MatchOption { .. } => make::ext::ty_option(fun_ty.make_ty(ctx, module)), | ||
1207 | FlowHandler::MatchResult { err } => { | 1197 | FlowHandler::MatchResult { err } => { |
1208 | let handler_ty = | 1198 | let handler_ty = |
1209 | err.expr_ty(ctx).map(|ty| make_ty(&ty, ctx, module)).unwrap_or_else(make::ty_unit); | 1199 | err.expr_ty(ctx).map(|ty| make_ty(&ty, ctx, module)).unwrap_or_else(make::ty_unit); |
1210 | make::ty_generic( | 1200 | make::ext::ty_result(fun_ty.make_ty(ctx, module), handler_ty) |
1211 | make::name_ref("Result"), | ||
1212 | vec![fun_ty.make_ty(ctx, module), handler_ty], | ||
1213 | ) | ||
1214 | } | 1201 | } |
1215 | }; | 1202 | }; |
1216 | Some(make::ret_type(ret_ty)) | 1203 | Some(make::ret_type(ret_ty)) |
@@ -1297,7 +1284,7 @@ fn make_body( | |||
1297 | TryKind::Option => "Some", | 1284 | TryKind::Option => "Some", |
1298 | TryKind::Result { .. } => "Ok", | 1285 | TryKind::Result { .. } => "Ok", |
1299 | }; | 1286 | }; |
1300 | let func = make::expr_path(make_path_from_text(constructor)); | 1287 | let func = make::expr_path(make::ext::ident_path(constructor)); |
1301 | let args = make::arg_list(iter::once(tail_expr)); | 1288 | let args = make::arg_list(iter::once(tail_expr)); |
1302 | make::expr_call(func, args) | 1289 | make::expr_call(func, args) |
1303 | }) | 1290 | }) |
@@ -1307,16 +1294,16 @@ fn make_body( | |||
1307 | with_tail_expr(block, lit_false.into()) | 1294 | with_tail_expr(block, lit_false.into()) |
1308 | } | 1295 | } |
1309 | FlowHandler::IfOption { .. } => { | 1296 | FlowHandler::IfOption { .. } => { |
1310 | let none = make::expr_path(make_path_from_text("None")); | 1297 | let none = make::expr_path(make::ext::ident_path("None")); |
1311 | with_tail_expr(block, none) | 1298 | with_tail_expr(block, none) |
1312 | } | 1299 | } |
1313 | FlowHandler::MatchOption { .. } => map_tail_expr(block, |tail_expr| { | 1300 | FlowHandler::MatchOption { .. } => map_tail_expr(block, |tail_expr| { |
1314 | let some = make::expr_path(make_path_from_text("Some")); | 1301 | let some = make::expr_path(make::ext::ident_path("Some")); |
1315 | let args = make::arg_list(iter::once(tail_expr)); | 1302 | let args = make::arg_list(iter::once(tail_expr)); |
1316 | make::expr_call(some, args) | 1303 | make::expr_call(some, args) |
1317 | }), | 1304 | }), |
1318 | FlowHandler::MatchResult { .. } => map_tail_expr(block, |tail_expr| { | 1305 | FlowHandler::MatchResult { .. } => map_tail_expr(block, |tail_expr| { |
1319 | let ok = make::expr_path(make_path_from_text("Ok")); | 1306 | let ok = make::expr_path(make::ext::ident_path("Ok")); |
1320 | let args = make::arg_list(iter::once(tail_expr)); | 1307 | let args = make::arg_list(iter::once(tail_expr)); |
1321 | make::expr_call(ok, args) | 1308 | make::expr_call(ok, args) |
1322 | }), | 1309 | }), |
@@ -1362,7 +1349,8 @@ fn rewrite_body_segment( | |||
1362 | syntax: &SyntaxNode, | 1349 | syntax: &SyntaxNode, |
1363 | ) -> SyntaxNode { | 1350 | ) -> SyntaxNode { |
1364 | let syntax = fix_param_usages(ctx, params, syntax); | 1351 | let syntax = fix_param_usages(ctx, params, syntax); |
1365 | update_external_control_flow(handler, &syntax) | 1352 | update_external_control_flow(handler, &syntax); |
1353 | syntax | ||
1366 | } | 1354 | } |
1367 | 1355 | ||
1368 | /// change all usages to account for added `&`/`&mut` for some params | 1356 | /// change all usages to account for added `&`/`&mut` for some params |
@@ -1415,75 +1403,65 @@ fn fix_param_usages(ctx: &AssistContext, params: &[Param], syntax: &SyntaxNode) | |||
1415 | res | 1403 | res |
1416 | } | 1404 | } |
1417 | 1405 | ||
1418 | fn update_external_control_flow(handler: &FlowHandler, syntax: &SyntaxNode) -> SyntaxNode { | 1406 | fn update_external_control_flow(handler: &FlowHandler, syntax: &SyntaxNode) { |
1419 | let mut rewriter = SyntaxRewriter::default(); | ||
1420 | |||
1421 | let mut nested_loop = None; | 1407 | let mut nested_loop = None; |
1422 | let mut nested_scope = None; | 1408 | let mut nested_scope = None; |
1423 | for event in syntax.preorder() { | 1409 | for event in syntax.preorder() { |
1424 | let node = match event { | 1410 | match event { |
1425 | WalkEvent::Enter(e) => { | 1411 | WalkEvent::Enter(e) => match e.kind() { |
1426 | match e.kind() { | 1412 | SyntaxKind::LOOP_EXPR | SyntaxKind::WHILE_EXPR | SyntaxKind::FOR_EXPR => { |
1427 | SyntaxKind::LOOP_EXPR | SyntaxKind::WHILE_EXPR | SyntaxKind::FOR_EXPR => { | 1413 | if nested_loop.is_none() { |
1428 | if nested_loop.is_none() { | 1414 | nested_loop = Some(e.clone()); |
1429 | nested_loop = Some(e.clone()); | ||
1430 | } | ||
1431 | } | 1415 | } |
1432 | SyntaxKind::FN | 1416 | } |
1433 | | SyntaxKind::CONST | 1417 | SyntaxKind::FN |
1434 | | SyntaxKind::STATIC | 1418 | | SyntaxKind::CONST |
1435 | | SyntaxKind::IMPL | 1419 | | SyntaxKind::STATIC |
1436 | | SyntaxKind::MODULE => { | 1420 | | SyntaxKind::IMPL |
1437 | if nested_scope.is_none() { | 1421 | | SyntaxKind::MODULE => { |
1438 | nested_scope = Some(e.clone()); | 1422 | if nested_scope.is_none() { |
1439 | } | 1423 | nested_scope = Some(e.clone()); |
1440 | } | 1424 | } |
1441 | _ => {} | ||
1442 | } | 1425 | } |
1443 | e | 1426 | _ => {} |
1444 | } | 1427 | }, |
1445 | WalkEvent::Leave(e) => { | 1428 | WalkEvent::Leave(e) => { |
1429 | if nested_scope.is_none() { | ||
1430 | if let Some(expr) = ast::Expr::cast(e.clone()) { | ||
1431 | match expr { | ||
1432 | ast::Expr::ReturnExpr(return_expr) if nested_scope.is_none() => { | ||
1433 | let expr = return_expr.expr(); | ||
1434 | if let Some(replacement) = make_rewritten_flow(handler, expr) { | ||
1435 | ted::replace(return_expr.syntax(), replacement.syntax()) | ||
1436 | } | ||
1437 | } | ||
1438 | ast::Expr::BreakExpr(break_expr) if nested_loop.is_none() => { | ||
1439 | let expr = break_expr.expr(); | ||
1440 | if let Some(replacement) = make_rewritten_flow(handler, expr) { | ||
1441 | ted::replace(break_expr.syntax(), replacement.syntax()) | ||
1442 | } | ||
1443 | } | ||
1444 | ast::Expr::ContinueExpr(continue_expr) if nested_loop.is_none() => { | ||
1445 | if let Some(replacement) = make_rewritten_flow(handler, None) { | ||
1446 | ted::replace(continue_expr.syntax(), replacement.syntax()) | ||
1447 | } | ||
1448 | } | ||
1449 | _ => { | ||
1450 | // do nothing | ||
1451 | } | ||
1452 | } | ||
1453 | } | ||
1454 | } | ||
1455 | |||
1446 | if nested_loop.as_ref() == Some(&e) { | 1456 | if nested_loop.as_ref() == Some(&e) { |
1447 | nested_loop = None; | 1457 | nested_loop = None; |
1448 | } | 1458 | } |
1449 | if nested_scope.as_ref() == Some(&e) { | 1459 | if nested_scope.as_ref() == Some(&e) { |
1450 | nested_scope = None; | 1460 | nested_scope = None; |
1451 | } | 1461 | } |
1452 | continue; | ||
1453 | } | 1462 | } |
1454 | }; | 1463 | }; |
1455 | if nested_scope.is_some() { | ||
1456 | continue; | ||
1457 | } | ||
1458 | let expr = match ast::Expr::cast(node) { | ||
1459 | Some(e) => e, | ||
1460 | None => continue, | ||
1461 | }; | ||
1462 | match expr { | ||
1463 | ast::Expr::ReturnExpr(return_expr) if nested_scope.is_none() => { | ||
1464 | let expr = return_expr.expr(); | ||
1465 | if let Some(replacement) = make_rewritten_flow(handler, expr) { | ||
1466 | rewriter.replace_ast(&return_expr.into(), &replacement); | ||
1467 | } | ||
1468 | } | ||
1469 | ast::Expr::BreakExpr(break_expr) if nested_loop.is_none() => { | ||
1470 | let expr = break_expr.expr(); | ||
1471 | if let Some(replacement) = make_rewritten_flow(handler, expr) { | ||
1472 | rewriter.replace_ast(&break_expr.into(), &replacement); | ||
1473 | } | ||
1474 | } | ||
1475 | ast::Expr::ContinueExpr(continue_expr) if nested_loop.is_none() => { | ||
1476 | if let Some(replacement) = make_rewritten_flow(handler, None) { | ||
1477 | rewriter.replace_ast(&continue_expr.into(), &replacement); | ||
1478 | } | ||
1479 | } | ||
1480 | _ => { | ||
1481 | // do nothing | ||
1482 | } | ||
1483 | } | ||
1484 | } | 1464 | } |
1485 | |||
1486 | rewriter.rewrite(syntax) | ||
1487 | } | 1465 | } |
1488 | 1466 | ||
1489 | fn make_rewritten_flow(handler: &FlowHandler, arg_expr: Option<ast::Expr>) -> Option<ast::Expr> { | 1467 | fn make_rewritten_flow(handler: &FlowHandler, arg_expr: Option<ast::Expr>) -> Option<ast::Expr> { |
@@ -1493,16 +1471,16 @@ fn make_rewritten_flow(handler: &FlowHandler, arg_expr: Option<ast::Expr>) -> Op | |||
1493 | FlowHandler::IfOption { .. } => { | 1471 | FlowHandler::IfOption { .. } => { |
1494 | let expr = arg_expr.unwrap_or_else(|| make::expr_tuple(Vec::new())); | 1472 | let expr = arg_expr.unwrap_or_else(|| make::expr_tuple(Vec::new())); |
1495 | let args = make::arg_list(iter::once(expr)); | 1473 | let args = make::arg_list(iter::once(expr)); |
1496 | make::expr_call(make::expr_path(make_path_from_text("Some")), args) | 1474 | make::expr_call(make::expr_path(make::ext::ident_path("Some")), args) |
1497 | } | 1475 | } |
1498 | FlowHandler::MatchOption { .. } => make::expr_path(make_path_from_text("None")), | 1476 | FlowHandler::MatchOption { .. } => make::expr_path(make::ext::ident_path("None")), |
1499 | FlowHandler::MatchResult { .. } => { | 1477 | FlowHandler::MatchResult { .. } => { |
1500 | let expr = arg_expr.unwrap_or_else(|| make::expr_tuple(Vec::new())); | 1478 | let expr = arg_expr.unwrap_or_else(|| make::expr_tuple(Vec::new())); |
1501 | let args = make::arg_list(iter::once(expr)); | 1479 | let args = make::arg_list(iter::once(expr)); |
1502 | make::expr_call(make::expr_path(make_path_from_text("Err")), args) | 1480 | make::expr_call(make::expr_path(make::ext::ident_path("Err")), args) |
1503 | } | 1481 | } |
1504 | }; | 1482 | }; |
1505 | Some(make::expr_return(Some(value))) | 1483 | Some(make::expr_return(Some(value)).clone_for_update()) |
1506 | } | 1484 | } |
1507 | 1485 | ||
1508 | #[cfg(test)] | 1486 | #[cfg(test)] |
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..007aba23d 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 | |||
@@ -70,7 +70,7 @@ pub(crate) fn extract_struct_from_enum_variant( | |||
70 | continue; | 70 | continue; |
71 | } | 71 | } |
72 | builder.edit_file(file_id); | 72 | builder.edit_file(file_id); |
73 | let source_file = builder.make_ast_mut(ctx.sema.parse(file_id)); | 73 | let source_file = builder.make_mut(ctx.sema.parse(file_id)); |
74 | let processed = process_references( | 74 | let processed = process_references( |
75 | ctx, | 75 | ctx, |
76 | &mut visited_modules_set, | 76 | &mut visited_modules_set, |
@@ -84,8 +84,8 @@ pub(crate) fn extract_struct_from_enum_variant( | |||
84 | }); | 84 | }); |
85 | } | 85 | } |
86 | builder.edit_file(ctx.frange.file_id); | 86 | builder.edit_file(ctx.frange.file_id); |
87 | let source_file = builder.make_ast_mut(ctx.sema.parse(ctx.frange.file_id)); | 87 | let source_file = builder.make_mut(ctx.sema.parse(ctx.frange.file_id)); |
88 | let variant = builder.make_ast_mut(variant.clone()); | 88 | let variant = builder.make_mut(variant.clone()); |
89 | if let Some(references) = def_file_references { | 89 | if let Some(references) = def_file_references { |
90 | let processed = process_references( | 90 | let processed = process_references( |
91 | ctx, | 91 | ctx, |
@@ -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#" | ||
319 | enum A { | ||
320 | $0One { | ||
321 | // leading comment | ||
322 | /// doc comment | ||
323 | #[an_attr] | ||
324 | foo: u32 | ||
325 | // trailing comment | ||
326 | } | ||
327 | }"#, | ||
328 | r#" | ||
329 | struct One{ | ||
330 | // leading comment | ||
331 | /// doc comment | ||
332 | #[an_attr] | ||
333 | pub foo: u32 | ||
334 | // trailing comment | ||
335 | } | ||
336 | |||
337 | enum 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#" | ||
348 | enum 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#" | ||
361 | struct One{ | ||
362 | // comment | ||
363 | /// doc | ||
364 | #[attr] | ||
365 | pub foo: u32, | ||
366 | // comment | ||
367 | #[attr] | ||
368 | /// doc | ||
369 | pub bar: u32 | ||
370 | } | ||
371 | |||
372 | enum 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#" | ||
384 | struct One(/* comment */ #[attr] pub u32, /* another */ pub u32 /* tail */); | ||
385 | |||
386 | enum 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#" | ||
396 | struct One{ pub a: u32, pub(crate) b: u32, pub(super) c: u32, pub d: u32 } | ||
397 | |||
398 | enum 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#" | ||
408 | struct One(pub u32, pub(crate) u32, pub(super) u32, pub u32); | ||
409 | |||
410 | enum 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/extract_variable.rs b/crates/ide_assists/src/handlers/extract_variable.rs index 136b9a55b..ae084c86c 100644 --- a/crates/ide_assists/src/handlers/extract_variable.rs +++ b/crates/ide_assists/src/handlers/extract_variable.rs | |||
@@ -54,7 +54,7 @@ pub(crate) fn extract_variable(acc: &mut Assists, ctx: &AssistContext) -> Option | |||
54 | 54 | ||
55 | let var_name = match &field_shorthand { | 55 | let var_name = match &field_shorthand { |
56 | Some(it) => it.to_string(), | 56 | Some(it) => it.to_string(), |
57 | None => suggest_name::variable(&to_extract, &ctx.sema), | 57 | None => suggest_name::for_variable(&to_extract, &ctx.sema), |
58 | }; | 58 | }; |
59 | let expr_range = match &field_shorthand { | 59 | let expr_range = match &field_shorthand { |
60 | Some(it) => it.syntax().text_range().cover(to_extract.syntax().text_range()), | 60 | Some(it) => it.syntax().text_range().cover(to_extract.syntax().text_range()), |
diff --git a/crates/ide_assists/src/handlers/fill_match_arms.rs b/crates/ide_assists/src/handlers/fill_match_arms.rs index be927cc1c..58b001050 100644 --- a/crates/ide_assists/src/handlers/fill_match_arms.rs +++ b/crates/ide_assists/src/handlers/fill_match_arms.rs | |||
@@ -71,6 +71,7 @@ pub(crate) fn fill_match_arms(acc: &mut Assists, ctx: &AssistContext) -> Option< | |||
71 | .filter_map(|variant| build_pat(ctx.db(), module, variant)) | 71 | .filter_map(|variant| build_pat(ctx.db(), module, variant)) |
72 | .filter(|variant_pat| is_variant_missing(&top_lvl_pats, variant_pat)) | 72 | .filter(|variant_pat| is_variant_missing(&top_lvl_pats, variant_pat)) |
73 | .map(|pat| make::match_arm(iter::once(pat), make::expr_empty_block())) | 73 | .map(|pat| make::match_arm(iter::once(pat), make::expr_empty_block())) |
74 | .map(|it| it.clone_for_update()) | ||
74 | .collect::<Vec<_>>(); | 75 | .collect::<Vec<_>>(); |
75 | if Some(enum_def) | 76 | if Some(enum_def) |
76 | == FamousDefs(&ctx.sema, Some(module.krate())) | 77 | == FamousDefs(&ctx.sema, Some(module.krate())) |
@@ -99,6 +100,7 @@ pub(crate) fn fill_match_arms(acc: &mut Assists, ctx: &AssistContext) -> Option< | |||
99 | }) | 100 | }) |
100 | .filter(|variant_pat| is_variant_missing(&top_lvl_pats, variant_pat)) | 101 | .filter(|variant_pat| is_variant_missing(&top_lvl_pats, variant_pat)) |
101 | .map(|pat| make::match_arm(iter::once(pat), make::expr_empty_block())) | 102 | .map(|pat| make::match_arm(iter::once(pat), make::expr_empty_block())) |
103 | .map(|it| it.clone_for_update()) | ||
102 | .collect() | 104 | .collect() |
103 | } else { | 105 | } else { |
104 | return None; | 106 | return None; |
@@ -114,10 +116,20 @@ pub(crate) fn fill_match_arms(acc: &mut Assists, ctx: &AssistContext) -> Option< | |||
114 | "Fill match arms", | 116 | "Fill match arms", |
115 | target, | 117 | target, |
116 | |builder| { | 118 | |builder| { |
117 | let new_arm_list = match_arm_list.remove_placeholder(); | 119 | let new_match_arm_list = match_arm_list.clone_for_update(); |
118 | let n_old_arms = new_arm_list.arms().count(); | 120 | |
119 | let new_arm_list = new_arm_list.append_arms(missing_arms); | 121 | let catch_all_arm = new_match_arm_list |
120 | let first_new_arm = new_arm_list.arms().nth(n_old_arms); | 122 | .arms() |
123 | .find(|arm| matches!(arm.pat(), Some(ast::Pat::WildcardPat(_)))); | ||
124 | if let Some(arm) = catch_all_arm { | ||
125 | arm.remove() | ||
126 | } | ||
127 | let mut first_new_arm = None; | ||
128 | for arm in missing_arms { | ||
129 | first_new_arm.get_or_insert_with(|| arm.clone()); | ||
130 | new_match_arm_list.add_arm(arm); | ||
131 | } | ||
132 | |||
121 | let old_range = ctx.sema.original_range(match_arm_list.syntax()).range; | 133 | let old_range = ctx.sema.original_range(match_arm_list.syntax()).range; |
122 | match (first_new_arm, ctx.config.snippet_cap) { | 134 | match (first_new_arm, ctx.config.snippet_cap) { |
123 | (Some(first_new_arm), Some(cap)) => { | 135 | (Some(first_new_arm), Some(cap)) => { |
@@ -131,10 +143,10 @@ pub(crate) fn fill_match_arms(acc: &mut Assists, ctx: &AssistContext) -> Option< | |||
131 | } | 143 | } |
132 | None => Cursor::Before(first_new_arm.syntax()), | 144 | None => Cursor::Before(first_new_arm.syntax()), |
133 | }; | 145 | }; |
134 | let snippet = render_snippet(cap, new_arm_list.syntax(), cursor); | 146 | let snippet = render_snippet(cap, new_match_arm_list.syntax(), cursor); |
135 | builder.replace_snippet(cap, old_range, snippet); | 147 | builder.replace_snippet(cap, old_range, snippet); |
136 | } | 148 | } |
137 | _ => builder.replace(old_range, new_arm_list.to_string()), | 149 | _ => builder.replace(old_range, new_match_arm_list.to_string()), |
138 | } | 150 | } |
139 | }, | 151 | }, |
140 | ) | 152 | ) |
@@ -263,18 +275,18 @@ mod tests { | |||
263 | check_assist_not_applicable( | 275 | check_assist_not_applicable( |
264 | fill_match_arms, | 276 | fill_match_arms, |
265 | r#" | 277 | r#" |
266 | enum A { | 278 | enum A { |
267 | As, | 279 | As, |
268 | Bs{x:i32, y:Option<i32>}, | 280 | Bs{x:i32, y:Option<i32>}, |
269 | Cs(i32, Option<i32>), | 281 | Cs(i32, Option<i32>), |
270 | } | 282 | } |
271 | fn main() { | 283 | fn main() { |
272 | match A::As$0 { | 284 | match A::As$0 { |
273 | A::As, | 285 | A::As, |
274 | A::Bs{x,y:Some(_)} => {} | 286 | A::Bs{x,y:Some(_)} => {} |
275 | A::Cs(_, Some(_)) => {} | 287 | A::Cs(_, Some(_)) => {} |
276 | } | 288 | } |
277 | } | 289 | } |
278 | "#, | 290 | "#, |
279 | ); | 291 | ); |
280 | } | 292 | } |
@@ -284,13 +296,13 @@ mod tests { | |||
284 | check_assist_not_applicable( | 296 | check_assist_not_applicable( |
285 | fill_match_arms, | 297 | fill_match_arms, |
286 | r#" | 298 | r#" |
287 | fn foo(a: bool) { | 299 | fn foo(a: bool) { |
288 | match a$0 { | 300 | match a$0 { |
289 | true => {} | 301 | true => {} |
290 | false => {} | 302 | false => {} |
291 | } | 303 | } |
292 | } | 304 | } |
293 | "#, | 305 | "#, |
294 | ) | 306 | ) |
295 | } | 307 | } |
296 | 308 | ||
@@ -301,11 +313,11 @@ mod tests { | |||
301 | check_assist_not_applicable( | 313 | check_assist_not_applicable( |
302 | fill_match_arms, | 314 | fill_match_arms, |
303 | r#" | 315 | r#" |
304 | fn main() { | 316 | fn main() { |
305 | match (0, false)$0 { | 317 | match (0, false)$0 { |
306 | } | 318 | } |
307 | } | 319 | } |
308 | "#, | 320 | "#, |
309 | ); | 321 | ); |
310 | } | 322 | } |
311 | 323 | ||
@@ -314,19 +326,19 @@ mod tests { | |||
314 | check_assist( | 326 | check_assist( |
315 | fill_match_arms, | 327 | fill_match_arms, |
316 | r#" | 328 | r#" |
317 | fn foo(a: bool) { | 329 | fn foo(a: bool) { |
318 | match a$0 { | 330 | match a$0 { |
319 | } | 331 | } |
320 | } | 332 | } |
321 | "#, | 333 | "#, |
322 | r#" | 334 | r#" |
323 | fn foo(a: bool) { | 335 | fn foo(a: bool) { |
324 | match a { | 336 | match a { |
325 | $0true => {} | 337 | $0true => {} |
326 | false => {} | 338 | false => {} |
327 | } | 339 | } |
328 | } | 340 | } |
329 | "#, | 341 | "#, |
330 | ) | 342 | ) |
331 | } | 343 | } |
332 | 344 | ||
@@ -335,20 +347,20 @@ mod tests { | |||
335 | check_assist( | 347 | check_assist( |
336 | fill_match_arms, | 348 | fill_match_arms, |
337 | r#" | 349 | r#" |
338 | fn foo(a: bool) { | 350 | fn foo(a: bool) { |
339 | match a$0 { | 351 | match a$0 { |
340 | true => {} | 352 | true => {} |
341 | } | 353 | } |
342 | } | 354 | } |
343 | "#, | 355 | "#, |
344 | r#" | 356 | r#" |
345 | fn foo(a: bool) { | 357 | fn foo(a: bool) { |
346 | match a { | 358 | match a { |
347 | true => {} | 359 | true => {} |
348 | $0false => {} | 360 | $0false => {} |
349 | } | 361 | } |
350 | } | 362 | } |
351 | "#, | 363 | "#, |
352 | ) | 364 | ) |
353 | } | 365 | } |
354 | 366 | ||
@@ -357,15 +369,15 @@ mod tests { | |||
357 | check_assist_not_applicable( | 369 | check_assist_not_applicable( |
358 | fill_match_arms, | 370 | fill_match_arms, |
359 | r#" | 371 | r#" |
360 | fn foo(a: bool) { | 372 | fn foo(a: bool) { |
361 | match (a, a)$0 { | 373 | match (a, a)$0 { |
362 | (true, true) => {} | 374 | (true, true) => {} |
363 | (true, false) => {} | 375 | (true, false) => {} |
364 | (false, true) => {} | 376 | (false, true) => {} |
365 | (false, false) => {} | 377 | (false, false) => {} |
366 | } | 378 | } |
367 | } | 379 | } |
368 | "#, | 380 | "#, |
369 | ) | 381 | ) |
370 | } | 382 | } |
371 | 383 | ||
@@ -374,21 +386,21 @@ mod tests { | |||
374 | check_assist( | 386 | check_assist( |
375 | fill_match_arms, | 387 | fill_match_arms, |
376 | r#" | 388 | r#" |
377 | fn foo(a: bool) { | 389 | fn foo(a: bool) { |
378 | match (a, a)$0 { | 390 | match (a, a)$0 { |
379 | } | 391 | } |
380 | } | 392 | } |
381 | "#, | 393 | "#, |
382 | r#" | 394 | r#" |
383 | fn foo(a: bool) { | 395 | fn foo(a: bool) { |
384 | match (a, a) { | 396 | match (a, a) { |
385 | $0(true, true) => {} | 397 | $0(true, true) => {} |
386 | (true, false) => {} | 398 | (true, false) => {} |
387 | (false, true) => {} | 399 | (false, true) => {} |
388 | (false, false) => {} | 400 | (false, false) => {} |
389 | } | 401 | } |
390 | } | 402 | } |
391 | "#, | 403 | "#, |
392 | ) | 404 | ) |
393 | } | 405 | } |
394 | 406 | ||
@@ -397,22 +409,22 @@ mod tests { | |||
397 | check_assist( | 409 | check_assist( |
398 | fill_match_arms, | 410 | fill_match_arms, |
399 | r#" | 411 | r#" |
400 | fn foo(a: bool) { | 412 | fn foo(a: bool) { |
401 | match (a, a)$0 { | 413 | match (a, a)$0 { |
402 | (false, true) => {} | 414 | (false, true) => {} |
403 | } | 415 | } |
404 | } | 416 | } |
405 | "#, | 417 | "#, |
406 | r#" | 418 | r#" |
407 | fn foo(a: bool) { | 419 | fn foo(a: bool) { |
408 | match (a, a) { | 420 | match (a, a) { |
409 | (false, true) => {} | 421 | (false, true) => {} |
410 | $0(true, true) => {} | 422 | $0(true, true) => {} |
411 | (true, false) => {} | 423 | (true, false) => {} |
412 | (false, false) => {} | 424 | (false, false) => {} |
413 | } | 425 | } |
414 | } | 426 | } |
415 | "#, | 427 | "#, |
416 | ) | 428 | ) |
417 | } | 429 | } |
418 | 430 | ||
@@ -421,32 +433,32 @@ mod tests { | |||
421 | check_assist( | 433 | check_assist( |
422 | fill_match_arms, | 434 | fill_match_arms, |
423 | r#" | 435 | r#" |
424 | enum A { | 436 | enum A { |
425 | As, | 437 | As, |
426 | Bs { x: i32, y: Option<i32> }, | 438 | Bs { x: i32, y: Option<i32> }, |
427 | Cs(i32, Option<i32>), | 439 | Cs(i32, Option<i32>), |
428 | } | 440 | } |
429 | fn main() { | 441 | fn main() { |
430 | match A::As$0 { | 442 | match A::As$0 { |
431 | A::Bs { x, y: Some(_) } => {} | 443 | A::Bs { x, y: Some(_) } => {} |
432 | A::Cs(_, Some(_)) => {} | 444 | A::Cs(_, Some(_)) => {} |
433 | } | 445 | } |
434 | } | 446 | } |
435 | "#, | 447 | "#, |
436 | r#" | 448 | r#" |
437 | enum A { | 449 | enum A { |
438 | As, | 450 | As, |
439 | Bs { x: i32, y: Option<i32> }, | 451 | Bs { x: i32, y: Option<i32> }, |
440 | Cs(i32, Option<i32>), | 452 | Cs(i32, Option<i32>), |
441 | } | 453 | } |
442 | fn main() { | 454 | fn main() { |
443 | match A::As { | 455 | match A::As { |
444 | A::Bs { x, y: Some(_) } => {} | 456 | A::Bs { x, y: Some(_) } => {} |
445 | A::Cs(_, Some(_)) => {} | 457 | A::Cs(_, Some(_)) => {} |
446 | $0A::As => {} | 458 | $0A::As => {} |
447 | } | 459 | } |
448 | } | 460 | } |
449 | "#, | 461 | "#, |
450 | ); | 462 | ); |
451 | } | 463 | } |
452 | 464 | ||
@@ -593,30 +605,30 @@ fn main() { | |||
593 | check_assist( | 605 | check_assist( |
594 | fill_match_arms, | 606 | fill_match_arms, |
595 | r#" | 607 | r#" |
596 | enum A { One, Two } | 608 | enum A { One, Two } |
597 | enum B { One, Two } | 609 | enum B { One, Two } |
598 | 610 | ||
599 | fn main() { | 611 | fn main() { |
600 | let a = A::One; | 612 | let a = A::One; |
601 | let b = B::One; | 613 | let b = B::One; |
602 | match (a$0, b) {} | 614 | match (a$0, b) {} |
603 | } | 615 | } |
604 | "#, | 616 | "#, |
605 | r#" | 617 | r#" |
606 | enum A { One, Two } | 618 | enum A { One, Two } |
607 | enum B { One, Two } | 619 | enum B { One, Two } |
608 | 620 | ||
609 | fn main() { | 621 | fn main() { |
610 | let a = A::One; | 622 | let a = A::One; |
611 | let b = B::One; | 623 | let b = B::One; |
612 | match (a, b) { | 624 | match (a, b) { |
613 | $0(A::One, B::One) => {} | 625 | $0(A::One, B::One) => {} |
614 | (A::One, B::Two) => {} | 626 | (A::One, B::Two) => {} |
615 | (A::Two, B::One) => {} | 627 | (A::Two, B::One) => {} |
616 | (A::Two, B::Two) => {} | 628 | (A::Two, B::Two) => {} |
617 | } | 629 | } |
618 | } | 630 | } |
619 | "#, | 631 | "#, |
620 | ); | 632 | ); |
621 | } | 633 | } |
622 | 634 | ||
@@ -625,30 +637,30 @@ fn main() { | |||
625 | check_assist( | 637 | check_assist( |
626 | fill_match_arms, | 638 | fill_match_arms, |
627 | r#" | 639 | r#" |
628 | enum A { One, Two } | 640 | enum A { One, Two } |
629 | enum B { One, Two } | 641 | enum B { One, Two } |
630 | 642 | ||
631 | fn main() { | 643 | fn main() { |
632 | let a = A::One; | 644 | let a = A::One; |
633 | let b = B::One; | 645 | let b = B::One; |
634 | match (&a$0, &b) {} | 646 | match (&a$0, &b) {} |
635 | } | 647 | } |
636 | "#, | 648 | "#, |
637 | r#" | 649 | r#" |
638 | enum A { One, Two } | 650 | enum A { One, Two } |
639 | enum B { One, Two } | 651 | enum B { One, Two } |
640 | 652 | ||
641 | fn main() { | 653 | fn main() { |
642 | let a = A::One; | 654 | let a = A::One; |
643 | let b = B::One; | 655 | let b = B::One; |
644 | match (&a, &b) { | 656 | match (&a, &b) { |
645 | $0(A::One, B::One) => {} | 657 | $0(A::One, B::One) => {} |
646 | (A::One, B::Two) => {} | 658 | (A::One, B::Two) => {} |
647 | (A::Two, B::One) => {} | 659 | (A::Two, B::One) => {} |
648 | (A::Two, B::Two) => {} | 660 | (A::Two, B::Two) => {} |
649 | } | 661 | } |
650 | } | 662 | } |
651 | "#, | 663 | "#, |
652 | ); | 664 | ); |
653 | } | 665 | } |
654 | 666 | ||
@@ -737,20 +749,20 @@ fn main() { | |||
737 | check_assist_not_applicable( | 749 | check_assist_not_applicable( |
738 | fill_match_arms, | 750 | fill_match_arms, |
739 | r#" | 751 | r#" |
740 | enum A { One, Two } | 752 | enum A { One, Two } |
741 | enum B { One, Two } | 753 | enum B { One, Two } |
742 | 754 | ||
743 | fn main() { | 755 | fn main() { |
744 | let a = A::One; | 756 | let a = A::One; |
745 | let b = B::One; | 757 | let b = B::One; |
746 | match (a$0, b) { | 758 | match (a$0, b) { |
747 | (A::Two, B::One) => {} | 759 | (A::Two, B::One) => {} |
748 | (A::One, B::One) => {} | 760 | (A::One, B::One) => {} |
749 | (A::One, B::Two) => {} | 761 | (A::One, B::Two) => {} |
750 | (A::Two, B::Two) => {} | 762 | (A::Two, B::Two) => {} |
751 | } | 763 | } |
752 | } | 764 | } |
753 | "#, | 765 | "#, |
754 | ); | 766 | ); |
755 | } | 767 | } |
756 | 768 | ||
@@ -759,25 +771,25 @@ fn main() { | |||
759 | check_assist( | 771 | check_assist( |
760 | fill_match_arms, | 772 | fill_match_arms, |
761 | r#" | 773 | r#" |
762 | enum A { One, Two } | 774 | enum A { One, Two } |
763 | 775 | ||
764 | fn main() { | 776 | fn main() { |
765 | let a = A::One; | 777 | let a = A::One; |
766 | match (a$0, ) { | 778 | match (a$0, ) { |
767 | } | 779 | } |
768 | } | 780 | } |
769 | "#, | 781 | "#, |
770 | r#" | 782 | r#" |
771 | enum A { One, Two } | 783 | enum A { One, Two } |
772 | 784 | ||
773 | fn main() { | 785 | fn main() { |
774 | let a = A::One; | 786 | let a = A::One; |
775 | match (a, ) { | 787 | match (a, ) { |
776 | $0(A::One,) => {} | 788 | $0(A::One,) => {} |
777 | (A::Two,) => {} | 789 | (A::Two,) => {} |
778 | } | 790 | } |
779 | } | 791 | } |
780 | "#, | 792 | "#, |
781 | ); | 793 | ); |
782 | } | 794 | } |
783 | 795 | ||
@@ -786,47 +798,47 @@ fn main() { | |||
786 | check_assist( | 798 | check_assist( |
787 | fill_match_arms, | 799 | fill_match_arms, |
788 | r#" | 800 | r#" |
789 | enum A { As } | 801 | enum A { As } |
790 | 802 | ||
791 | fn foo(a: &A) { | 803 | fn foo(a: &A) { |
792 | match a$0 { | 804 | match a$0 { |
793 | } | 805 | } |
794 | } | 806 | } |
795 | "#, | 807 | "#, |
796 | r#" | 808 | r#" |
797 | enum A { As } | 809 | enum A { As } |
798 | 810 | ||
799 | fn foo(a: &A) { | 811 | fn foo(a: &A) { |
800 | match a { | 812 | match a { |
801 | $0A::As => {} | 813 | $0A::As => {} |
802 | } | 814 | } |
803 | } | 815 | } |
804 | "#, | 816 | "#, |
805 | ); | 817 | ); |
806 | 818 | ||
807 | check_assist( | 819 | check_assist( |
808 | fill_match_arms, | 820 | fill_match_arms, |
809 | r#" | 821 | r#" |
810 | enum A { | 822 | enum A { |
811 | Es { x: usize, y: usize } | 823 | Es { x: usize, y: usize } |
812 | } | 824 | } |
813 | 825 | ||
814 | fn foo(a: &mut A) { | 826 | fn foo(a: &mut A) { |
815 | match a$0 { | 827 | match a$0 { |
816 | } | 828 | } |
817 | } | 829 | } |
818 | "#, | 830 | "#, |
819 | r#" | 831 | r#" |
820 | enum A { | 832 | enum A { |
821 | Es { x: usize, y: usize } | 833 | Es { x: usize, y: usize } |
822 | } | 834 | } |
823 | 835 | ||
824 | fn foo(a: &mut A) { | 836 | fn foo(a: &mut A) { |
825 | match a { | 837 | match a { |
826 | $0A::Es { x, y } => {} | 838 | $0A::Es { x, y } => {} |
827 | } | 839 | } |
828 | } | 840 | } |
829 | "#, | 841 | "#, |
830 | ); | 842 | ); |
831 | } | 843 | } |
832 | 844 | ||
@@ -835,12 +847,12 @@ fn main() { | |||
835 | check_assist_target( | 847 | check_assist_target( |
836 | fill_match_arms, | 848 | fill_match_arms, |
837 | r#" | 849 | r#" |
838 | enum E { X, Y } | 850 | enum E { X, Y } |
839 | 851 | ||
840 | fn main() { | 852 | fn main() { |
841 | match E::X$0 {} | 853 | match E::X$0 {} |
842 | } | 854 | } |
843 | "#, | 855 | "#, |
844 | "match E::X {}", | 856 | "match E::X {}", |
845 | ); | 857 | ); |
846 | } | 858 | } |
@@ -850,24 +862,24 @@ fn main() { | |||
850 | check_assist( | 862 | check_assist( |
851 | fill_match_arms, | 863 | fill_match_arms, |
852 | r#" | 864 | r#" |
853 | enum E { X, Y } | 865 | enum E { X, Y } |
854 | 866 | ||
855 | fn main() { | 867 | fn main() { |
856 | match E::X { | 868 | match E::X { |
857 | $0_ => {} | 869 | $0_ => {} |
858 | } | 870 | } |
859 | } | 871 | } |
860 | "#, | 872 | "#, |
861 | r#" | 873 | r#" |
862 | enum E { X, Y } | 874 | enum E { X, Y } |
863 | 875 | ||
864 | fn main() { | 876 | fn main() { |
865 | match E::X { | 877 | match E::X { |
866 | $0E::X => {} | 878 | $0E::X => {} |
867 | E::Y => {} | 879 | E::Y => {} |
868 | } | 880 | } |
869 | } | 881 | } |
870 | "#, | 882 | "#, |
871 | ); | 883 | ); |
872 | } | 884 | } |
873 | 885 | ||
@@ -876,26 +888,26 @@ fn main() { | |||
876 | check_assist( | 888 | check_assist( |
877 | fill_match_arms, | 889 | fill_match_arms, |
878 | r#" | 890 | r#" |
879 | mod foo { pub enum E { X, Y } } | 891 | mod foo { pub enum E { X, Y } } |
880 | use foo::E::X; | 892 | use foo::E::X; |
881 | 893 | ||
882 | fn main() { | 894 | fn main() { |
883 | match X { | 895 | match X { |
884 | $0 | 896 | $0 |
885 | } | 897 | } |
886 | } | 898 | } |
887 | "#, | 899 | "#, |
888 | r#" | 900 | r#" |
889 | mod foo { pub enum E { X, Y } } | 901 | mod foo { pub enum E { X, Y } } |
890 | use foo::E::X; | 902 | use foo::E::X; |
891 | 903 | ||
892 | fn main() { | 904 | fn main() { |
893 | match X { | 905 | match X { |
894 | $0X => {} | 906 | $0X => {} |
895 | foo::E::Y => {} | 907 | foo::E::Y => {} |
896 | } | 908 | } |
897 | } | 909 | } |
898 | "#, | 910 | "#, |
899 | ); | 911 | ); |
900 | } | 912 | } |
901 | 913 | ||
@@ -904,26 +916,26 @@ fn main() { | |||
904 | check_assist( | 916 | check_assist( |
905 | fill_match_arms, | 917 | fill_match_arms, |
906 | r#" | 918 | r#" |
907 | enum A { One, Two } | 919 | enum A { One, Two } |
908 | fn foo(a: A) { | 920 | fn foo(a: A) { |
909 | match a { | 921 | match a { |
910 | // foo bar baz$0 | 922 | // foo bar baz$0 |
911 | A::One => {} | 923 | A::One => {} |
912 | // This is where the rest should be | 924 | // This is where the rest should be |
913 | } | 925 | } |
914 | } | 926 | } |
915 | "#, | 927 | "#, |
916 | r#" | 928 | r#" |
917 | enum A { One, Two } | 929 | enum A { One, Two } |
918 | fn foo(a: A) { | 930 | fn foo(a: A) { |
919 | match a { | 931 | match a { |
920 | // foo bar baz | 932 | // foo bar baz |
921 | A::One => {} | 933 | A::One => {} |
922 | // This is where the rest should be | 934 | $0A::Two => {} |
923 | $0A::Two => {} | 935 | // This is where the rest should be |
924 | } | 936 | } |
925 | } | 937 | } |
926 | "#, | 938 | "#, |
927 | ); | 939 | ); |
928 | } | 940 | } |
929 | 941 | ||
@@ -932,23 +944,23 @@ fn main() { | |||
932 | check_assist( | 944 | check_assist( |
933 | fill_match_arms, | 945 | fill_match_arms, |
934 | r#" | 946 | r#" |
935 | enum A { One, Two } | 947 | enum A { One, Two } |
936 | fn foo(a: A) { | 948 | fn foo(a: A) { |
937 | match a { | 949 | match a { |
938 | // foo bar baz$0 | 950 | // foo bar baz$0 |
939 | } | 951 | } |
940 | } | 952 | } |
941 | "#, | 953 | "#, |
942 | r#" | 954 | r#" |
943 | enum A { One, Two } | 955 | enum A { One, Two } |
944 | fn foo(a: A) { | 956 | fn foo(a: A) { |
945 | match a { | 957 | match a { |
946 | // foo bar baz | 958 | $0A::One => {} |
947 | $0A::One => {} | 959 | A::Two => {} |
948 | A::Two => {} | 960 | // foo bar baz |
949 | } | 961 | } |
950 | } | 962 | } |
951 | "#, | 963 | "#, |
952 | ); | 964 | ); |
953 | } | 965 | } |
954 | 966 | ||
@@ -957,22 +969,22 @@ fn main() { | |||
957 | check_assist( | 969 | check_assist( |
958 | fill_match_arms, | 970 | fill_match_arms, |
959 | r#" | 971 | r#" |
960 | enum A { One, Two, } | 972 | enum A { One, Two, } |
961 | fn foo(a: A) { | 973 | fn foo(a: A) { |
962 | match a$0 { | 974 | match a$0 { |
963 | _ => (), | 975 | _ => (), |
964 | } | 976 | } |
965 | } | 977 | } |
966 | "#, | 978 | "#, |
967 | r#" | 979 | r#" |
968 | enum A { One, Two, } | 980 | enum A { One, Two, } |
969 | fn foo(a: A) { | 981 | fn foo(a: A) { |
970 | match a { | 982 | match a { |
971 | $0A::One => {} | 983 | $0A::One => {} |
972 | A::Two => {} | 984 | A::Two => {} |
973 | } | 985 | } |
974 | } | 986 | } |
975 | "#, | 987 | "#, |
976 | ); | 988 | ); |
977 | } | 989 | } |
978 | 990 | ||
@@ -1016,7 +1028,8 @@ enum Test { | |||
1016 | fn foo(t: Test) { | 1028 | fn foo(t: Test) { |
1017 | m!(match t$0 {}); | 1029 | m!(match t$0 {}); |
1018 | }"#, | 1030 | }"#, |
1019 | r#"macro_rules! m { ($expr:expr) => {$expr}} | 1031 | r#" |
1032 | macro_rules! m { ($expr:expr) => {$expr}} | ||
1020 | enum Test { | 1033 | enum Test { |
1021 | A, | 1034 | A, |
1022 | B, | 1035 | B, |
diff --git a/crates/ide_assists/src/handlers/generate_default_from_new.rs b/crates/ide_assists/src/handlers/generate_default_from_new.rs index dc14552d6..bad826366 100644 --- a/crates/ide_assists/src/handlers/generate_default_from_new.rs +++ b/crates/ide_assists/src/handlers/generate_default_from_new.rs | |||
@@ -3,8 +3,10 @@ use crate::{ | |||
3 | AssistId, | 3 | AssistId, |
4 | }; | 4 | }; |
5 | use ide_db::helpers::FamousDefs; | 5 | use ide_db::helpers::FamousDefs; |
6 | use itertools::Itertools; | ||
7 | use stdx::format_to; | ||
6 | use syntax::{ | 8 | use syntax::{ |
7 | ast::{self, Impl, NameOwner}, | 9 | ast::{self, GenericParamsOwner, Impl, NameOwner, TypeBoundsOwner}, |
8 | AstNode, | 10 | AstNode, |
9 | }; | 11 | }; |
10 | 12 | ||
@@ -65,23 +67,56 @@ pub(crate) fn generate_default_from_new(acc: &mut Assists, ctx: &AssistContext) | |||
65 | "Generate a Default impl from a new fn", | 67 | "Generate a Default impl from a new fn", |
66 | insert_location, | 68 | insert_location, |
67 | move |builder| { | 69 | move |builder| { |
68 | let code = default_fn_node_for_new(impl_); | 70 | let default_code = " fn default() -> Self { |
71 | Self::new() | ||
72 | }"; | ||
73 | let code = generate_trait_impl_text_from_impl(&impl_, "Default", default_code); | ||
69 | builder.insert(insert_location.end(), code); | 74 | builder.insert(insert_location.end(), code); |
70 | }, | 75 | }, |
71 | ) | 76 | ) |
72 | } | 77 | } |
73 | 78 | ||
74 | fn default_fn_node_for_new(impl_: Impl) -> String { | 79 | fn generate_trait_impl_text_from_impl(impl_: &ast::Impl, trait_text: &str, code: &str) -> String { |
75 | format!( | 80 | let generic_params = impl_.generic_param_list(); |
76 | " | 81 | let mut buf = String::with_capacity(code.len()); |
82 | buf.push_str("\n\n"); | ||
83 | buf.push_str("impl"); | ||
84 | |||
85 | if let Some(generic_params) = &generic_params { | ||
86 | let lifetimes = generic_params.lifetime_params().map(|lt| format!("{}", lt.syntax())); | ||
87 | let type_params = generic_params.type_params().map(|type_param| { | ||
88 | let mut buf = String::new(); | ||
89 | if let Some(it) = type_param.name() { | ||
90 | format_to!(buf, "{}", it.syntax()); | ||
91 | } | ||
92 | if let Some(it) = type_param.colon_token() { | ||
93 | format_to!(buf, "{} ", it); | ||
94 | } | ||
95 | if let Some(it) = type_param.type_bound_list() { | ||
96 | format_to!(buf, "{}", it.syntax()); | ||
97 | } | ||
98 | buf | ||
99 | }); | ||
100 | let const_params = generic_params.const_params().map(|t| t.syntax().to_string()); | ||
101 | let generics = lifetimes.chain(type_params).chain(const_params).format(", "); | ||
102 | format_to!(buf, "<{}>", generics); | ||
103 | } | ||
104 | |||
105 | buf.push(' '); | ||
106 | buf.push_str(trait_text); | ||
107 | buf.push_str(" for "); | ||
108 | buf.push_str(&impl_.self_ty().unwrap().syntax().text().to_string()); | ||
109 | |||
110 | match impl_.where_clause() { | ||
111 | Some(where_clause) => { | ||
112 | format_to!(buf, "\n{}\n{{\n{}\n}}", where_clause, code); | ||
113 | } | ||
114 | None => { | ||
115 | format_to!(buf, " {{\n{}\n}}", code); | ||
116 | } | ||
117 | } | ||
77 | 118 | ||
78 | impl Default for {} {{ | 119 | buf |
79 | fn default() -> Self {{ | ||
80 | Self::new() | ||
81 | }} | ||
82 | }}", | ||
83 | impl_.self_ty().unwrap().syntax().text() | ||
84 | ) | ||
85 | } | 120 | } |
86 | 121 | ||
87 | fn is_default_implemented(ctx: &AssistContext, impl_: &Impl) -> bool { | 122 | fn is_default_implemented(ctx: &AssistContext, impl_: &Impl) -> bool { |
@@ -176,6 +211,234 @@ impl Default for Test { | |||
176 | } | 211 | } |
177 | 212 | ||
178 | #[test] | 213 | #[test] |
214 | fn new_function_with_generic() { | ||
215 | check_pass( | ||
216 | r#" | ||
217 | pub struct Foo<T> { | ||
218 | _bar: *mut T, | ||
219 | } | ||
220 | |||
221 | impl<T> Foo<T> { | ||
222 | pub fn ne$0w() -> Self { | ||
223 | unimplemented!() | ||
224 | } | ||
225 | } | ||
226 | "#, | ||
227 | r#" | ||
228 | pub struct Foo<T> { | ||
229 | _bar: *mut T, | ||
230 | } | ||
231 | |||
232 | impl<T> Foo<T> { | ||
233 | pub fn new() -> Self { | ||
234 | unimplemented!() | ||
235 | } | ||
236 | } | ||
237 | |||
238 | impl<T> Default for Foo<T> { | ||
239 | fn default() -> Self { | ||
240 | Self::new() | ||
241 | } | ||
242 | } | ||
243 | "#, | ||
244 | ); | ||
245 | } | ||
246 | |||
247 | #[test] | ||
248 | fn new_function_with_generics() { | ||
249 | check_pass( | ||
250 | r#" | ||
251 | pub struct Foo<T, B> { | ||
252 | _tars: *mut T, | ||
253 | _bar: *mut B, | ||
254 | } | ||
255 | |||
256 | impl<T, B> Foo<T, B> { | ||
257 | pub fn ne$0w() -> Self { | ||
258 | unimplemented!() | ||
259 | } | ||
260 | } | ||
261 | "#, | ||
262 | r#" | ||
263 | pub struct Foo<T, B> { | ||
264 | _tars: *mut T, | ||
265 | _bar: *mut B, | ||
266 | } | ||
267 | |||
268 | impl<T, B> Foo<T, B> { | ||
269 | pub fn new() -> Self { | ||
270 | unimplemented!() | ||
271 | } | ||
272 | } | ||
273 | |||
274 | impl<T, B> Default for Foo<T, B> { | ||
275 | fn default() -> Self { | ||
276 | Self::new() | ||
277 | } | ||
278 | } | ||
279 | "#, | ||
280 | ); | ||
281 | } | ||
282 | |||
283 | #[test] | ||
284 | fn new_function_with_generic_and_bound() { | ||
285 | check_pass( | ||
286 | r#" | ||
287 | pub struct Foo<T> { | ||
288 | t: T, | ||
289 | } | ||
290 | |||
291 | impl<T: From<i32>> Foo<T> { | ||
292 | pub fn ne$0w() -> Self { | ||
293 | Foo { t: 0.into() } | ||
294 | } | ||
295 | } | ||
296 | "#, | ||
297 | r#" | ||
298 | pub struct Foo<T> { | ||
299 | t: T, | ||
300 | } | ||
301 | |||
302 | impl<T: From<i32>> Foo<T> { | ||
303 | pub fn new() -> Self { | ||
304 | Foo { t: 0.into() } | ||
305 | } | ||
306 | } | ||
307 | |||
308 | impl<T: From<i32>> Default for Foo<T> { | ||
309 | fn default() -> Self { | ||
310 | Self::new() | ||
311 | } | ||
312 | } | ||
313 | "#, | ||
314 | ); | ||
315 | } | ||
316 | |||
317 | #[test] | ||
318 | fn new_function_with_generics_and_bounds() { | ||
319 | check_pass( | ||
320 | r#" | ||
321 | pub struct Foo<T, B> { | ||
322 | _tars: T, | ||
323 | _bar: B, | ||
324 | } | ||
325 | |||
326 | impl<T: From<i32>, B: From<i64>> Foo<T, B> { | ||
327 | pub fn ne$0w() -> Self { | ||
328 | unimplemented!() | ||
329 | } | ||
330 | } | ||
331 | "#, | ||
332 | r#" | ||
333 | pub struct Foo<T, B> { | ||
334 | _tars: T, | ||
335 | _bar: B, | ||
336 | } | ||
337 | |||
338 | impl<T: From<i32>, B: From<i64>> Foo<T, B> { | ||
339 | pub fn new() -> Self { | ||
340 | unimplemented!() | ||
341 | } | ||
342 | } | ||
343 | |||
344 | impl<T: From<i32>, B: From<i64>> Default for Foo<T, B> { | ||
345 | fn default() -> Self { | ||
346 | Self::new() | ||
347 | } | ||
348 | } | ||
349 | "#, | ||
350 | ); | ||
351 | } | ||
352 | |||
353 | #[test] | ||
354 | fn new_function_with_generic_and_where() { | ||
355 | check_pass( | ||
356 | r#" | ||
357 | pub struct Foo<T> { | ||
358 | t: T, | ||
359 | } | ||
360 | |||
361 | impl<T: From<i32>> Foo<T> | ||
362 | where | ||
363 | Option<T>: Debug | ||
364 | { | ||
365 | pub fn ne$0w() -> Self { | ||
366 | Foo { t: 0.into() } | ||
367 | } | ||
368 | } | ||
369 | "#, | ||
370 | r#" | ||
371 | pub struct Foo<T> { | ||
372 | t: T, | ||
373 | } | ||
374 | |||
375 | impl<T: From<i32>> Foo<T> | ||
376 | where | ||
377 | Option<T>: Debug | ||
378 | { | ||
379 | pub fn new() -> Self { | ||
380 | Foo { t: 0.into() } | ||
381 | } | ||
382 | } | ||
383 | |||
384 | impl<T: From<i32>> Default for Foo<T> | ||
385 | where | ||
386 | Option<T>: Debug | ||
387 | { | ||
388 | fn default() -> Self { | ||
389 | Self::new() | ||
390 | } | ||
391 | } | ||
392 | "#, | ||
393 | ); | ||
394 | } | ||
395 | |||
396 | #[test] | ||
397 | fn new_function_with_generics_and_wheres() { | ||
398 | check_pass( | ||
399 | r#" | ||
400 | pub struct Foo<T, B> { | ||
401 | _tars: T, | ||
402 | _bar: B, | ||
403 | } | ||
404 | |||
405 | impl<T: From<i32>, B: From<i64>> Foo<T, B> | ||
406 | where | ||
407 | Option<T>: Debug, Option<B>: Debug, | ||
408 | { | ||
409 | pub fn ne$0w() -> Self { | ||
410 | unimplemented!() | ||
411 | } | ||
412 | } | ||
413 | "#, | ||
414 | r#" | ||
415 | pub struct Foo<T, B> { | ||
416 | _tars: T, | ||
417 | _bar: B, | ||
418 | } | ||
419 | |||
420 | impl<T: From<i32>, B: From<i64>> Foo<T, B> | ||
421 | where | ||
422 | Option<T>: Debug, Option<B>: Debug, | ||
423 | { | ||
424 | pub fn new() -> Self { | ||
425 | unimplemented!() | ||
426 | } | ||
427 | } | ||
428 | |||
429 | impl<T: From<i32>, B: From<i64>> Default for Foo<T, B> | ||
430 | where | ||
431 | Option<T>: Debug, Option<B>: Debug, | ||
432 | { | ||
433 | fn default() -> Self { | ||
434 | Self::new() | ||
435 | } | ||
436 | } | ||
437 | "#, | ||
438 | ); | ||
439 | } | ||
440 | |||
441 | #[test] | ||
179 | fn new_function_with_parameters() { | 442 | fn new_function_with_parameters() { |
180 | cov_mark::check!(new_function_with_parameters); | 443 | cov_mark::check!(new_function_with_parameters); |
181 | check_not_applicable( | 444 | check_not_applicable( |
diff --git a/crates/ide_assists/src/handlers/generate_function.rs b/crates/ide_assists/src/handlers/generate_function.rs index 6f95b1a07..bc9fc524b 100644 --- a/crates/ide_assists/src/handlers/generate_function.rs +++ b/crates/ide_assists/src/handlers/generate_function.rs | |||
@@ -175,7 +175,7 @@ impl FunctionBuilder { | |||
175 | } | 175 | } |
176 | 176 | ||
177 | fn render(self) -> FunctionTemplate { | 177 | fn render(self) -> FunctionTemplate { |
178 | let placeholder_expr = make::expr_todo(); | 178 | let placeholder_expr = make::ext::expr_todo(); |
179 | let fn_body = make::block_expr(vec![], Some(placeholder_expr)); | 179 | let fn_body = make::block_expr(vec![], Some(placeholder_expr)); |
180 | let visibility = if self.needs_pub { Some(make::visibility_pub_crate()) } else { None }; | 180 | let visibility = if self.needs_pub { Some(make::visibility_pub_crate()) } else { None }; |
181 | let mut fn_def = make::fn_( | 181 | let mut fn_def = make::fn_( |
diff --git a/crates/ide_assists/src/handlers/generate_new.rs b/crates/ide_assists/src/handlers/generate_new.rs index 8ce5930b7..959a1f86c 100644 --- a/crates/ide_assists/src/handlers/generate_new.rs +++ b/crates/ide_assists/src/handlers/generate_new.rs | |||
@@ -1,4 +1,3 @@ | |||
1 | use ast::Adt; | ||
2 | use itertools::Itertools; | 1 | use itertools::Itertools; |
3 | use stdx::format_to; | 2 | use stdx::format_to; |
4 | use syntax::ast::{self, AstNode, NameOwner, StructKind, VisibilityOwner}; | 3 | use syntax::ast::{self, AstNode, NameOwner, StructKind, VisibilityOwner}; |
@@ -37,7 +36,7 @@ pub(crate) fn generate_new(acc: &mut Assists, ctx: &AssistContext) -> Option<()> | |||
37 | }; | 36 | }; |
38 | 37 | ||
39 | // Return early if we've found an existing new fn | 38 | // Return early if we've found an existing new fn |
40 | let impl_def = find_struct_impl(&ctx, &Adt::Struct(strukt.clone()), "new")?; | 39 | let impl_def = find_struct_impl(&ctx, &ast::Adt::Struct(strukt.clone()), "new")?; |
41 | 40 | ||
42 | let target = strukt.syntax().text_range(); | 41 | let target = strukt.syntax().text_range(); |
43 | acc.add(AssistId("generate_new", AssistKind::Generate), "Generate `new`", target, |builder| { | 42 | acc.add(AssistId("generate_new", AssistKind::Generate), "Generate `new`", target, |builder| { |
@@ -60,7 +59,7 @@ pub(crate) fn generate_new(acc: &mut Assists, ctx: &AssistContext) -> Option<()> | |||
60 | let start_offset = impl_def | 59 | let start_offset = impl_def |
61 | .and_then(|impl_def| find_impl_block_start(impl_def, &mut buf)) | 60 | .and_then(|impl_def| find_impl_block_start(impl_def, &mut buf)) |
62 | .unwrap_or_else(|| { | 61 | .unwrap_or_else(|| { |
63 | buf = generate_impl_text(&Adt::Struct(strukt.clone()), &buf); | 62 | buf = generate_impl_text(&ast::Adt::Struct(strukt.clone()), &buf); |
64 | strukt.syntax().text_range().end() | 63 | strukt.syntax().text_range().end() |
65 | }); | 64 | }); |
66 | 65 | ||
@@ -81,101 +80,132 @@ mod tests { | |||
81 | use super::*; | 80 | use super::*; |
82 | 81 | ||
83 | #[test] | 82 | #[test] |
84 | #[rustfmt::skip] | ||
85 | fn test_generate_new() { | 83 | fn test_generate_new() { |
86 | // Check output of generation | ||
87 | check_assist( | 84 | check_assist( |
88 | generate_new, | 85 | generate_new, |
89 | "struct Foo {$0}", | 86 | r#" |
90 | "struct Foo {} | 87 | struct Foo {$0} |
88 | "#, | ||
89 | r#" | ||
90 | struct Foo {} | ||
91 | 91 | ||
92 | impl Foo { | 92 | impl Foo { |
93 | fn $0new() -> Self { Self { } } | 93 | fn $0new() -> Self { Self { } } |
94 | }", | 94 | } |
95 | "#, | ||
95 | ); | 96 | ); |
96 | check_assist( | 97 | check_assist( |
97 | generate_new, | 98 | generate_new, |
98 | "struct Foo<T: Clone> {$0}", | 99 | r#" |
99 | "struct Foo<T: Clone> {} | 100 | struct Foo<T: Clone> {$0} |
101 | "#, | ||
102 | r#" | ||
103 | struct Foo<T: Clone> {} | ||
100 | 104 | ||
101 | impl<T: Clone> Foo<T> { | 105 | impl<T: Clone> Foo<T> { |
102 | fn $0new() -> Self { Self { } } | 106 | fn $0new() -> Self { Self { } } |
103 | }", | 107 | } |
108 | "#, | ||
104 | ); | 109 | ); |
105 | check_assist( | 110 | check_assist( |
106 | generate_new, | 111 | generate_new, |
107 | "struct Foo<'a, T: Foo<'a>> {$0}", | 112 | r#" |
108 | "struct Foo<'a, T: Foo<'a>> {} | 113 | struct Foo<'a, T: Foo<'a>> {$0} |
114 | "#, | ||
115 | r#" | ||
116 | struct Foo<'a, T: Foo<'a>> {} | ||
109 | 117 | ||
110 | impl<'a, T: Foo<'a>> Foo<'a, T> { | 118 | impl<'a, T: Foo<'a>> Foo<'a, T> { |
111 | fn $0new() -> Self { Self { } } | 119 | fn $0new() -> Self { Self { } } |
112 | }", | 120 | } |
121 | "#, | ||
113 | ); | 122 | ); |
114 | check_assist( | 123 | check_assist( |
115 | generate_new, | 124 | generate_new, |
116 | "struct Foo { baz: String $0}", | 125 | r#" |
117 | "struct Foo { baz: String } | 126 | struct Foo { baz: String $0} |
127 | "#, | ||
128 | r#" | ||
129 | struct Foo { baz: String } | ||
118 | 130 | ||
119 | impl Foo { | 131 | impl Foo { |
120 | fn $0new(baz: String) -> Self { Self { baz } } | 132 | fn $0new(baz: String) -> Self { Self { baz } } |
121 | }", | 133 | } |
134 | "#, | ||
122 | ); | 135 | ); |
123 | check_assist( | 136 | check_assist( |
124 | generate_new, | 137 | generate_new, |
125 | "struct Foo { baz: String, qux: Vec<i32> $0}", | 138 | r#" |
126 | "struct Foo { baz: String, qux: Vec<i32> } | 139 | struct Foo { baz: String, qux: Vec<i32> $0} |
140 | "#, | ||
141 | r#" | ||
142 | struct Foo { baz: String, qux: Vec<i32> } | ||
127 | 143 | ||
128 | impl Foo { | 144 | impl Foo { |
129 | fn $0new(baz: String, qux: Vec<i32>) -> Self { Self { baz, qux } } | 145 | fn $0new(baz: String, qux: Vec<i32>) -> Self { Self { baz, qux } } |
130 | }", | 146 | } |
147 | "#, | ||
131 | ); | 148 | ); |
149 | } | ||
132 | 150 | ||
133 | // Check that visibility modifiers don't get brought in for fields | 151 | #[test] |
152 | fn check_that_visibility_modifiers_dont_get_brought_in() { | ||
134 | check_assist( | 153 | check_assist( |
135 | generate_new, | 154 | generate_new, |
136 | "struct Foo { pub baz: String, pub qux: Vec<i32> $0}", | 155 | r#" |
137 | "struct Foo { pub baz: String, pub qux: Vec<i32> } | 156 | struct Foo { pub baz: String, pub qux: Vec<i32> $0} |
157 | "#, | ||
158 | r#" | ||
159 | struct Foo { pub baz: String, pub qux: Vec<i32> } | ||
138 | 160 | ||
139 | impl Foo { | 161 | impl Foo { |
140 | fn $0new(baz: String, qux: Vec<i32>) -> Self { Self { baz, qux } } | 162 | fn $0new(baz: String, qux: Vec<i32>) -> Self { Self { baz, qux } } |
141 | }", | 163 | } |
164 | "#, | ||
142 | ); | 165 | ); |
166 | } | ||
143 | 167 | ||
144 | // Check that it reuses existing impls | 168 | #[test] |
169 | fn check_it_reuses_existing_impls() { | ||
145 | check_assist( | 170 | check_assist( |
146 | generate_new, | 171 | generate_new, |
147 | "struct Foo {$0} | 172 | r#" |
173 | struct Foo {$0} | ||
148 | 174 | ||
149 | impl Foo {} | 175 | impl Foo {} |
150 | ", | 176 | "#, |
151 | "struct Foo {} | 177 | r#" |
178 | struct Foo {} | ||
152 | 179 | ||
153 | impl Foo { | 180 | impl Foo { |
154 | fn $0new() -> Self { Self { } } | 181 | fn $0new() -> Self { Self { } } |
155 | } | 182 | } |
156 | ", | 183 | "#, |
157 | ); | 184 | ); |
158 | check_assist( | 185 | check_assist( |
159 | generate_new, | 186 | generate_new, |
160 | "struct Foo {$0} | 187 | r#" |
188 | struct Foo {$0} | ||
161 | 189 | ||
162 | impl Foo { | 190 | impl Foo { |
163 | fn qux(&self) {} | 191 | fn qux(&self) {} |
164 | } | 192 | } |
165 | ", | 193 | "#, |
166 | "struct Foo {} | 194 | r#" |
195 | struct Foo {} | ||
167 | 196 | ||
168 | impl Foo { | 197 | impl Foo { |
169 | fn $0new() -> Self { Self { } } | 198 | fn $0new() -> Self { Self { } } |
170 | 199 | ||
171 | fn qux(&self) {} | 200 | fn qux(&self) {} |
172 | } | 201 | } |
173 | ", | 202 | "#, |
174 | ); | 203 | ); |
175 | 204 | ||
176 | check_assist( | 205 | check_assist( |
177 | generate_new, | 206 | generate_new, |
178 | "struct Foo {$0} | 207 | r#" |
208 | struct Foo {$0} | ||
179 | 209 | ||
180 | impl Foo { | 210 | impl Foo { |
181 | fn qux(&self) {} | 211 | fn qux(&self) {} |
@@ -183,8 +213,9 @@ impl Foo { | |||
183 | 5 | 213 | 5 |
184 | } | 214 | } |
185 | } | 215 | } |
186 | ", | 216 | "#, |
187 | "struct Foo {} | 217 | r#" |
218 | struct Foo {} | ||
188 | 219 | ||
189 | impl Foo { | 220 | impl Foo { |
190 | fn $0new() -> Self { Self { } } | 221 | fn $0new() -> Self { Self { } } |
@@ -194,27 +225,37 @@ impl Foo { | |||
194 | 5 | 225 | 5 |
195 | } | 226 | } |
196 | } | 227 | } |
197 | ", | 228 | "#, |
198 | ); | 229 | ); |
230 | } | ||
199 | 231 | ||
200 | // Check visibility of new fn based on struct | 232 | #[test] |
233 | fn check_visibility_of_new_fn_based_on_struct() { | ||
201 | check_assist( | 234 | check_assist( |
202 | generate_new, | 235 | generate_new, |
203 | "pub struct Foo {$0}", | 236 | r#" |
204 | "pub struct Foo {} | 237 | pub struct Foo {$0} |
238 | "#, | ||
239 | r#" | ||
240 | pub struct Foo {} | ||
205 | 241 | ||
206 | impl Foo { | 242 | impl Foo { |
207 | pub fn $0new() -> Self { Self { } } | 243 | pub fn $0new() -> Self { Self { } } |
208 | }", | 244 | } |
245 | "#, | ||
209 | ); | 246 | ); |
210 | check_assist( | 247 | check_assist( |
211 | generate_new, | 248 | generate_new, |
212 | "pub(crate) struct Foo {$0}", | 249 | r#" |
213 | "pub(crate) struct Foo {} | 250 | pub(crate) struct Foo {$0} |
251 | "#, | ||
252 | r#" | ||
253 | pub(crate) struct Foo {} | ||
214 | 254 | ||
215 | impl Foo { | 255 | impl Foo { |
216 | pub(crate) fn $0new() -> Self { Self { } } | 256 | pub(crate) fn $0new() -> Self { Self { } } |
217 | }", | 257 | } |
258 | "#, | ||
218 | ); | 259 | ); |
219 | } | 260 | } |
220 | 261 | ||
@@ -222,26 +263,28 @@ impl Foo { | |||
222 | fn generate_new_not_applicable_if_fn_exists() { | 263 | fn generate_new_not_applicable_if_fn_exists() { |
223 | check_assist_not_applicable( | 264 | check_assist_not_applicable( |
224 | generate_new, | 265 | generate_new, |
225 | " | 266 | r#" |
226 | struct Foo {$0} | 267 | struct Foo {$0} |
227 | 268 | ||
228 | impl Foo { | 269 | impl Foo { |
229 | fn new() -> Self { | 270 | fn new() -> Self { |
230 | Self | 271 | Self |
231 | } | 272 | } |
232 | }", | 273 | } |
274 | "#, | ||
233 | ); | 275 | ); |
234 | 276 | ||
235 | check_assist_not_applicable( | 277 | check_assist_not_applicable( |
236 | generate_new, | 278 | generate_new, |
237 | " | 279 | r#" |
238 | struct Foo {$0} | 280 | struct Foo {$0} |
239 | 281 | ||
240 | impl Foo { | 282 | impl Foo { |
241 | fn New() -> Self { | 283 | fn New() -> Self { |
242 | Self | 284 | Self |
243 | } | 285 | } |
244 | }", | 286 | } |
287 | "#, | ||
245 | ); | 288 | ); |
246 | } | 289 | } |
247 | 290 | ||
@@ -249,12 +292,12 @@ impl Foo { | |||
249 | fn generate_new_target() { | 292 | fn generate_new_target() { |
250 | check_assist_target( | 293 | check_assist_target( |
251 | generate_new, | 294 | generate_new, |
252 | " | 295 | r#" |
253 | struct SomeThingIrrelevant; | 296 | struct SomeThingIrrelevant; |
254 | /// Has a lifetime parameter | 297 | /// Has a lifetime parameter |
255 | struct Foo<'a, T: Foo<'a>> {$0} | 298 | struct Foo<'a, T: Foo<'a>> {$0} |
256 | struct EvenMoreIrrelevant; | 299 | struct EvenMoreIrrelevant; |
257 | ", | 300 | "#, |
258 | "/// Has a lifetime parameter | 301 | "/// Has a lifetime parameter |
259 | struct Foo<'a, T: Foo<'a>> {}", | 302 | struct Foo<'a, T: Foo<'a>> {}", |
260 | ); | 303 | ); |
@@ -264,7 +307,7 @@ struct Foo<'a, T: Foo<'a>> {}", | |||
264 | fn test_unrelated_new() { | 307 | fn test_unrelated_new() { |
265 | check_assist( | 308 | check_assist( |
266 | generate_new, | 309 | generate_new, |
267 | r##" | 310 | r#" |
268 | pub struct AstId<N: AstNode> { | 311 | pub struct AstId<N: AstNode> { |
269 | file_id: HirFileId, | 312 | file_id: HirFileId, |
270 | file_ast_id: FileAstId<N>, | 313 | file_ast_id: FileAstId<N>, |
@@ -285,8 +328,9 @@ impl<T> Source<T> { | |||
285 | pub fn map<F: FnOnce(T) -> U, U>(self, f: F) -> Source<U> { | 328 | pub fn map<F: FnOnce(T) -> U, U>(self, f: F) -> Source<U> { |
286 | Source { file_id: self.file_id, ast: f(self.ast) } | 329 | Source { file_id: self.file_id, ast: f(self.ast) } |
287 | } | 330 | } |
288 | }"##, | 331 | } |
289 | r##" | 332 | "#, |
333 | r#" | ||
290 | pub struct AstId<N: AstNode> { | 334 | pub struct AstId<N: AstNode> { |
291 | file_id: HirFileId, | 335 | file_id: HirFileId, |
292 | file_ast_id: FileAstId<N>, | 336 | file_ast_id: FileAstId<N>, |
@@ -309,7 +353,8 @@ impl<T> Source<T> { | |||
309 | pub fn map<F: FnOnce(T) -> U, U>(self, f: F) -> Source<U> { | 353 | pub fn map<F: FnOnce(T) -> U, U>(self, f: F) -> Source<U> { |
310 | Source { file_id: self.file_id, ast: f(self.ast) } | 354 | Source { file_id: self.file_id, ast: f(self.ast) } |
311 | } | 355 | } |
312 | }"##, | 356 | } |
357 | "#, | ||
313 | ); | 358 | ); |
314 | } | 359 | } |
315 | } | 360 | } |
diff --git a/crates/ide_assists/src/handlers/introduce_named_lifetime.rs b/crates/ide_assists/src/handlers/introduce_named_lifetime.rs index 9f4f71d6c..16f8f9d70 100644 --- a/crates/ide_assists/src/handlers/introduce_named_lifetime.rs +++ b/crates/ide_assists/src/handlers/introduce_named_lifetime.rs | |||
@@ -84,19 +84,17 @@ fn generate_fn_def_assist( | |||
84 | } | 84 | } |
85 | }; | 85 | }; |
86 | acc.add(AssistId(ASSIST_NAME, AssistKind::Refactor), ASSIST_LABEL, lifetime_loc, |builder| { | 86 | acc.add(AssistId(ASSIST_NAME, AssistKind::Refactor), ASSIST_LABEL, lifetime_loc, |builder| { |
87 | let fn_def = builder.make_ast_mut(fn_def); | 87 | let fn_def = builder.make_mut(fn_def); |
88 | let lifetime = builder.make_ast_mut(lifetime); | 88 | let lifetime = builder.make_mut(lifetime); |
89 | let loc_needing_lifetime = | 89 | let loc_needing_lifetime = |
90 | loc_needing_lifetime.and_then(|it| it.make_mut(builder).to_position()); | 90 | loc_needing_lifetime.and_then(|it| it.make_mut(builder).to_position()); |
91 | 91 | ||
92 | add_lifetime_param(fn_def.get_or_create_generic_param_list(), new_lifetime_param); | 92 | fn_def.get_or_create_generic_param_list().add_generic_param( |
93 | ted::replace( | 93 | make::lifetime_param(new_lifetime_param.clone()).clone_for_update().into(), |
94 | lifetime.syntax(), | ||
95 | make_ast_lifetime(new_lifetime_param).clone_for_update().syntax(), | ||
96 | ); | 94 | ); |
97 | loc_needing_lifetime.map(|position| { | 95 | ted::replace(lifetime.syntax(), new_lifetime_param.clone_for_update().syntax()); |
98 | ted::insert(position, make_ast_lifetime(new_lifetime_param).clone_for_update().syntax()) | 96 | loc_needing_lifetime |
99 | }); | 97 | .map(|position| ted::insert(position, new_lifetime_param.clone_for_update().syntax())); |
100 | }) | 98 | }) |
101 | } | 99 | } |
102 | 100 | ||
@@ -109,14 +107,13 @@ fn generate_impl_def_assist( | |||
109 | ) -> Option<()> { | 107 | ) -> Option<()> { |
110 | let new_lifetime_param = generate_unique_lifetime_param_name(impl_def.generic_param_list())?; | 108 | let new_lifetime_param = generate_unique_lifetime_param_name(impl_def.generic_param_list())?; |
111 | acc.add(AssistId(ASSIST_NAME, AssistKind::Refactor), ASSIST_LABEL, lifetime_loc, |builder| { | 109 | acc.add(AssistId(ASSIST_NAME, AssistKind::Refactor), ASSIST_LABEL, lifetime_loc, |builder| { |
112 | let impl_def = builder.make_ast_mut(impl_def); | 110 | let impl_def = builder.make_mut(impl_def); |
113 | let lifetime = builder.make_ast_mut(lifetime); | 111 | let lifetime = builder.make_mut(lifetime); |
114 | 112 | ||
115 | add_lifetime_param(impl_def.get_or_create_generic_param_list(), new_lifetime_param); | 113 | impl_def.get_or_create_generic_param_list().add_generic_param( |
116 | ted::replace( | 114 | make::lifetime_param(new_lifetime_param.clone()).clone_for_update().into(), |
117 | lifetime.syntax(), | ||
118 | make_ast_lifetime(new_lifetime_param).clone_for_update().syntax(), | ||
119 | ); | 115 | ); |
116 | ted::replace(lifetime.syntax(), new_lifetime_param.clone_for_update().syntax()); | ||
120 | }) | 117 | }) |
121 | } | 118 | } |
122 | 119 | ||
@@ -124,31 +121,16 @@ fn generate_impl_def_assist( | |||
124 | /// which is not in the list | 121 | /// which is not in the list |
125 | fn generate_unique_lifetime_param_name( | 122 | fn generate_unique_lifetime_param_name( |
126 | existing_type_param_list: Option<ast::GenericParamList>, | 123 | existing_type_param_list: Option<ast::GenericParamList>, |
127 | ) -> Option<char> { | 124 | ) -> Option<ast::Lifetime> { |
128 | match existing_type_param_list { | 125 | match existing_type_param_list { |
129 | Some(type_params) => { | 126 | Some(type_params) => { |
130 | let used_lifetime_params: FxHashSet<_> = type_params | 127 | let used_lifetime_params: FxHashSet<_> = |
131 | .lifetime_params() | 128 | type_params.lifetime_params().map(|p| p.syntax().text().to_string()).collect(); |
132 | .map(|p| p.syntax().text().to_string()[1..].to_owned()) | 129 | ('a'..='z').map(|it| format!("'{}", it)).find(|it| !used_lifetime_params.contains(it)) |
133 | .collect(); | ||
134 | (b'a'..=b'z').map(char::from).find(|c| !used_lifetime_params.contains(&c.to_string())) | ||
135 | } | 130 | } |
136 | None => Some('a'), | 131 | None => Some("'a".to_string()), |
137 | } | 132 | } |
138 | } | 133 | .map(|it| make::lifetime(&it)) |
139 | |||
140 | fn add_lifetime_param(type_params: ast::GenericParamList, new_lifetime_param: char) { | ||
141 | let generic_param = | ||
142 | make::generic_param(format!("'{}", new_lifetime_param), None).clone_for_update(); | ||
143 | type_params.add_generic_param(generic_param); | ||
144 | } | ||
145 | |||
146 | fn make_ast_lifetime(new_lifetime_param: char) -> ast::Lifetime { | ||
147 | make::generic_param(format!("'{}", new_lifetime_param), None) | ||
148 | .syntax() | ||
149 | .descendants() | ||
150 | .find_map(ast::Lifetime::cast) | ||
151 | .unwrap() | ||
152 | } | 134 | } |
153 | 135 | ||
154 | enum NeedsLifetime { | 136 | enum NeedsLifetime { |
@@ -159,8 +141,8 @@ enum NeedsLifetime { | |||
159 | impl NeedsLifetime { | 141 | impl NeedsLifetime { |
160 | fn make_mut(self, builder: &mut AssistBuilder) -> Self { | 142 | fn make_mut(self, builder: &mut AssistBuilder) -> Self { |
161 | match self { | 143 | match self { |
162 | Self::SelfParam(it) => Self::SelfParam(builder.make_ast_mut(it)), | 144 | Self::SelfParam(it) => Self::SelfParam(builder.make_mut(it)), |
163 | Self::RefType(it) => Self::RefType(builder.make_ast_mut(it)), | 145 | Self::RefType(it) => Self::RefType(builder.make_mut(it)), |
164 | } | 146 | } |
165 | } | 147 | } |
166 | 148 | ||
diff --git a/crates/ide_assists/src/handlers/merge_imports.rs b/crates/ide_assists/src/handlers/merge_imports.rs index add7b8e37..31854840c 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)) |
@@ -47,16 +47,16 @@ pub(crate) fn merge_imports(acc: &mut Assists, ctx: &AssistContext) -> Option<() | |||
47 | target, | 47 | target, |
48 | |builder| { | 48 | |builder| { |
49 | if let Some((to_replace, replacement, to_remove)) = imports { | 49 | if let Some((to_replace, replacement, to_remove)) = imports { |
50 | let to_replace = builder.make_ast_mut(to_replace); | 50 | let to_replace = builder.make_mut(to_replace); |
51 | let to_remove = builder.make_ast_mut(to_remove); | 51 | let to_remove = builder.make_mut(to_remove); |
52 | 52 | ||
53 | ted::replace(to_replace.syntax(), replacement.syntax()); | 53 | ted::replace(to_replace.syntax(), replacement.syntax()); |
54 | to_remove.remove(); | 54 | to_remove.remove(); |
55 | } | 55 | } |
56 | 56 | ||
57 | if let Some((to_replace, replacement, to_remove)) = uses { | 57 | if let Some((to_replace, replacement, to_remove)) = uses { |
58 | let to_replace = builder.make_ast_mut(to_replace); | 58 | let to_replace = builder.make_mut(to_replace); |
59 | let to_remove = builder.make_ast_mut(to_remove); | 59 | let to_remove = builder.make_mut(to_remove); |
60 | 60 | ||
61 | ted::replace(to_replace.syntax(), replacement.syntax()); | 61 | ted::replace(to_replace.syntax(), replacement.syntax()); |
62 | to_remove.remove() | 62 | to_remove.remove() |
diff --git a/crates/ide_assists/src/handlers/move_bounds.rs b/crates/ide_assists/src/handlers/move_bounds.rs index 011a28d44..d89d11bdf 100644 --- a/crates/ide_assists/src/handlers/move_bounds.rs +++ b/crates/ide_assists/src/handlers/move_bounds.rs | |||
@@ -36,8 +36,8 @@ pub(crate) fn move_bounds_to_where_clause(acc: &mut Assists, ctx: &AssistContext | |||
36 | "Move to where clause", | 36 | "Move to where clause", |
37 | target, | 37 | target, |
38 | |edit| { | 38 | |edit| { |
39 | let type_param_list = edit.make_ast_mut(type_param_list); | 39 | let type_param_list = edit.make_mut(type_param_list); |
40 | let parent = edit.make_mut(parent); | 40 | let parent = edit.make_syntax_mut(parent); |
41 | 41 | ||
42 | let where_clause: ast::WhereClause = match_ast! { | 42 | let where_clause: ast::WhereClause = match_ast! { |
43 | match parent { | 43 | match parent { |
@@ -63,11 +63,7 @@ pub(crate) fn move_bounds_to_where_clause(acc: &mut Assists, ctx: &AssistContext | |||
63 | } | 63 | } |
64 | 64 | ||
65 | fn build_predicate(param: ast::TypeParam) -> Option<ast::WherePred> { | 65 | fn build_predicate(param: ast::TypeParam) -> Option<ast::WherePred> { |
66 | let path = { | 66 | let path = make::ext::ident_path(¶m.name()?.syntax().to_string()); |
67 | let name_ref = make::name_ref(¶m.name()?.syntax().to_string()); | ||
68 | let segment = make::path_segment(name_ref); | ||
69 | make::path_unqualified(segment) | ||
70 | }; | ||
71 | let predicate = make::where_pred(path, param.type_bound_list()?.bounds()); | 67 | let predicate = make::where_pred(path, param.type_bound_list()?.bounds()); |
72 | Some(predicate.clone_for_update()) | 68 | Some(predicate.clone_for_update()) |
73 | } | 69 | } |
diff --git a/crates/ide_assists/src/handlers/move_module_to_file.rs b/crates/ide_assists/src/handlers/move_module_to_file.rs index 6e685b4b2..93f702c55 100644 --- a/crates/ide_assists/src/handlers/move_module_to_file.rs +++ b/crates/ide_assists/src/handlers/move_module_to_file.rs | |||
@@ -1,4 +1,4 @@ | |||
1 | use ast::{edit::IndentLevel, VisibilityOwner}; | 1 | use ast::edit::IndentLevel; |
2 | use ide_db::base_db::AnchoredPathBuf; | 2 | use ide_db::base_db::AnchoredPathBuf; |
3 | use stdx::format_to; | 3 | use stdx::format_to; |
4 | use syntax::{ | 4 | use syntax::{ |
@@ -60,12 +60,18 @@ pub(crate) fn move_module_to_file(acc: &mut Assists, ctx: &AssistContext) -> Opt | |||
60 | }; | 60 | }; |
61 | 61 | ||
62 | let mut buf = String::new(); | 62 | let mut buf = String::new(); |
63 | if let Some(v) = module_ast.visibility() { | ||
64 | format_to!(buf, "{} ", v); | ||
65 | } | ||
66 | format_to!(buf, "mod {};", module_name); | 63 | format_to!(buf, "mod {};", module_name); |
67 | 64 | ||
68 | builder.replace(module_ast.syntax().text_range(), buf); | 65 | let replacement_start = if let Some(mod_token) = module_ast.mod_token() { |
66 | mod_token.text_range().start() | ||
67 | } else { | ||
68 | module_ast.syntax().text_range().start() | ||
69 | }; | ||
70 | |||
71 | builder.replace( | ||
72 | TextRange::new(replacement_start, module_ast.syntax().text_range().end()), | ||
73 | buf, | ||
74 | ); | ||
69 | 75 | ||
70 | let dst = AnchoredPathBuf { anchor: ctx.frange.file_id, path }; | 76 | let dst = AnchoredPathBuf { anchor: ctx.frange.file_id, path }; |
71 | builder.create_file(dst, contents); | 77 | builder.create_file(dst, contents); |
@@ -184,4 +190,26 @@ pub(crate) mod tests; | |||
184 | cov_mark::check!(available_before_curly); | 190 | cov_mark::check!(available_before_curly); |
185 | check_assist_not_applicable(move_module_to_file, r#"mod m { $0 }"#); | 191 | check_assist_not_applicable(move_module_to_file, r#"mod m { $0 }"#); |
186 | } | 192 | } |
193 | |||
194 | #[test] | ||
195 | fn keep_outer_comments_and_attributes() { | ||
196 | check_assist( | ||
197 | move_module_to_file, | ||
198 | r#" | ||
199 | /// doc comment | ||
200 | #[attribute] | ||
201 | mod $0tests { | ||
202 | #[test] fn t() {} | ||
203 | } | ||
204 | "#, | ||
205 | r#" | ||
206 | //- /main.rs | ||
207 | /// doc comment | ||
208 | #[attribute] | ||
209 | mod tests; | ||
210 | //- /tests.rs | ||
211 | #[test] fn t() {} | ||
212 | "#, | ||
213 | ); | ||
214 | } | ||
187 | } | 215 | } |
diff --git a/crates/ide_assists/src/handlers/pull_assignment_up.rs b/crates/ide_assists/src/handlers/pull_assignment_up.rs index 04bae4e58..f07b8a6c0 100644 --- a/crates/ide_assists/src/handlers/pull_assignment_up.rs +++ b/crates/ide_assists/src/handlers/pull_assignment_up.rs | |||
@@ -1,6 +1,6 @@ | |||
1 | use syntax::{ | 1 | use syntax::{ |
2 | ast::{self, edit::AstNodeEdit, make}, | 2 | ast::{self, make}, |
3 | AstNode, | 3 | ted, AstNode, |
4 | }; | 4 | }; |
5 | 5 | ||
6 | use crate::{ | 6 | use crate::{ |
@@ -37,103 +37,120 @@ use crate::{ | |||
37 | // ``` | 37 | // ``` |
38 | pub(crate) fn pull_assignment_up(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { | 38 | pub(crate) fn pull_assignment_up(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { |
39 | let assign_expr = ctx.find_node_at_offset::<ast::BinExpr>()?; | 39 | let assign_expr = ctx.find_node_at_offset::<ast::BinExpr>()?; |
40 | let name_expr = if assign_expr.op_kind()? == ast::BinOp::Assignment { | 40 | |
41 | assign_expr.lhs()? | 41 | let op_kind = assign_expr.op_kind()?; |
42 | } else { | 42 | if op_kind != ast::BinOp::Assignment { |
43 | cov_mark::hit!(test_cant_pull_non_assignments); | ||
43 | return None; | 44 | return None; |
45 | } | ||
46 | |||
47 | let mut collector = AssignmentsCollector { | ||
48 | sema: &ctx.sema, | ||
49 | common_lhs: assign_expr.lhs()?, | ||
50 | assignments: Vec::new(), | ||
44 | }; | 51 | }; |
45 | 52 | ||
46 | let (old_stmt, new_stmt) = if let Some(if_expr) = ctx.find_node_at_offset::<ast::IfExpr>() { | 53 | let tgt: ast::Expr = if let Some(if_expr) = ctx.find_node_at_offset::<ast::IfExpr>() { |
47 | ( | 54 | collector.collect_if(&if_expr)?; |
48 | ast::Expr::cast(if_expr.syntax().to_owned())?, | 55 | if_expr.into() |
49 | exprify_if(&if_expr, &ctx.sema, &name_expr)?.indent(if_expr.indent_level()), | ||
50 | ) | ||
51 | } else if let Some(match_expr) = ctx.find_node_at_offset::<ast::MatchExpr>() { | 56 | } else if let Some(match_expr) = ctx.find_node_at_offset::<ast::MatchExpr>() { |
52 | ( | 57 | collector.collect_match(&match_expr)?; |
53 | ast::Expr::cast(match_expr.syntax().to_owned())?, | 58 | match_expr.into() |
54 | exprify_match(&match_expr, &ctx.sema, &name_expr)?, | ||
55 | ) | ||
56 | } else { | 59 | } else { |
57 | return None; | 60 | return None; |
58 | }; | 61 | }; |
59 | 62 | ||
60 | let expr_stmt = make::expr_stmt(new_stmt); | 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 | } | ||
61 | 68 | ||
62 | acc.add( | 69 | acc.add( |
63 | AssistId("pull_assignment_up", AssistKind::RefactorExtract), | 70 | AssistId("pull_assignment_up", AssistKind::RefactorExtract), |
64 | "Pull assignment up", | 71 | "Pull assignment up", |
65 | old_stmt.syntax().text_range(), | 72 | tgt.syntax().text_range(), |
66 | move |edit| { | 73 | move |edit| { |
67 | edit.replace(old_stmt.syntax().text_range(), format!("{} = {};", name_expr, expr_stmt)); | 74 | let assignments: Vec<_> = collector |
75 | .assignments | ||
76 | .into_iter() | ||
77 | .map(|(stmt, rhs)| (edit.make_mut(stmt), rhs.clone_for_update())) | ||
78 | .collect(); | ||
79 | |||
80 | let tgt = edit.make_mut(tgt); | ||
81 | |||
82 | for (stmt, rhs) in assignments { | ||
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()); | ||
90 | } | ||
91 | let assign_expr = make::expr_assignment(collector.common_lhs, tgt.clone()); | ||
92 | let assign_stmt = make::expr_stmt(assign_expr); | ||
93 | |||
94 | ted::replace(tgt.syntax(), assign_stmt.syntax().clone_for_update()); | ||
68 | }, | 95 | }, |
69 | ) | 96 | ) |
70 | } | 97 | } |
71 | 98 | ||
72 | fn exprify_match( | 99 | struct AssignmentsCollector<'a> { |
73 | match_expr: &ast::MatchExpr, | 100 | sema: &'a hir::Semantics<'a, ide_db::RootDatabase>, |
74 | sema: &hir::Semantics<ide_db::RootDatabase>, | 101 | common_lhs: ast::Expr, |
75 | name: &ast::Expr, | 102 | assignments: Vec<(ast::BinExpr, ast::Expr)>, |
76 | ) -> Option<ast::Expr> { | 103 | } |
77 | let new_arm_list = match_expr | 104 | |
78 | .match_arm_list()? | 105 | impl<'a> AssignmentsCollector<'a> { |
79 | .arms() | 106 | fn collect_match(&mut self, match_expr: &ast::MatchExpr) -> Option<()> { |
80 | .map(|arm| { | 107 | for arm in match_expr.match_arm_list()?.arms() { |
81 | if let ast::Expr::BlockExpr(block) = arm.expr()? { | 108 | match arm.expr()? { |
82 | let new_block = exprify_block(&block, sema, name)?.indent(block.indent_level()); | 109 | ast::Expr::BlockExpr(block) => self.collect_block(&block)?, |
83 | Some(arm.replace_descendant(block, new_block)) | 110 | ast::Expr::BinExpr(expr) => self.collect_expr(&expr)?, |
111 | _ => return None, | ||
112 | } | ||
113 | } | ||
114 | |||
115 | Some(()) | ||
116 | } | ||
117 | fn collect_if(&mut self, if_expr: &ast::IfExpr) -> Option<()> { | ||
118 | let then_branch = if_expr.then_branch()?; | ||
119 | self.collect_block(&then_branch)?; | ||
120 | |||
121 | match if_expr.else_branch()? { | ||
122 | ast::ElseBranch::Block(block) => self.collect_block(&block), | ||
123 | ast::ElseBranch::IfExpr(expr) => { | ||
124 | cov_mark::hit!(test_pull_assignment_up_chained_if); | ||
125 | self.collect_if(&expr) | ||
126 | } | ||
127 | } | ||
128 | } | ||
129 | fn collect_block(&mut self, block: &ast::BlockExpr) -> Option<()> { | ||
130 | let last_expr = block.tail_expr().or_else(|| { | ||
131 | if let ast::Stmt::ExprStmt(stmt) = block.statements().last()? { | ||
132 | stmt.expr() | ||
84 | } else { | 133 | } else { |
85 | None | 134 | None |
86 | } | 135 | } |
87 | }) | 136 | })?; |
88 | .collect::<Option<Vec<_>>>()?; | ||
89 | let new_arm_list = match_expr | ||
90 | .match_arm_list()? | ||
91 | .replace_descendants(match_expr.match_arm_list()?.arms().zip(new_arm_list)); | ||
92 | Some(make::expr_match(match_expr.expr()?, new_arm_list)) | ||
93 | } | ||
94 | 137 | ||
95 | fn exprify_if( | 138 | if let ast::Expr::BinExpr(expr) = last_expr { |
96 | statement: &ast::IfExpr, | 139 | return self.collect_expr(&expr); |
97 | sema: &hir::Semantics<ide_db::RootDatabase>, | ||
98 | name: &ast::Expr, | ||
99 | ) -> Option<ast::Expr> { | ||
100 | let then_branch = exprify_block(&statement.then_branch()?, sema, name)?; | ||
101 | let else_branch = match statement.else_branch()? { | ||
102 | ast::ElseBranch::Block(ref block) => { | ||
103 | ast::ElseBranch::Block(exprify_block(block, sema, name)?) | ||
104 | } | ||
105 | ast::ElseBranch::IfExpr(expr) => { | ||
106 | cov_mark::hit!(test_pull_assignment_up_chained_if); | ||
107 | ast::ElseBranch::IfExpr(ast::IfExpr::cast( | ||
108 | exprify_if(&expr, sema, name)?.syntax().to_owned(), | ||
109 | )?) | ||
110 | } | 140 | } |
111 | }; | ||
112 | Some(make::expr_if(statement.condition()?, then_branch, Some(else_branch))) | ||
113 | } | ||
114 | 141 | ||
115 | fn exprify_block( | 142 | None |
116 | block: &ast::BlockExpr, | ||
117 | sema: &hir::Semantics<ide_db::RootDatabase>, | ||
118 | name: &ast::Expr, | ||
119 | ) -> Option<ast::BlockExpr> { | ||
120 | if block.tail_expr().is_some() { | ||
121 | return None; | ||
122 | } | 143 | } |
123 | 144 | ||
124 | let mut stmts: Vec<_> = block.statements().collect(); | 145 | fn collect_expr(&mut self, expr: &ast::BinExpr) -> Option<()> { |
125 | let stmt = stmts.pop()?; | 146 | if expr.op_kind()? == ast::BinOp::Assignment |
126 | 147 | && is_equivalent(self.sema, &expr.lhs()?, &self.common_lhs) | |
127 | if let ast::Stmt::ExprStmt(stmt) = stmt { | 148 | { |
128 | if let ast::Expr::BinExpr(expr) = stmt.expr()? { | 149 | self.assignments.push((expr.clone(), expr.rhs()?)); |
129 | if expr.op_kind()? == ast::BinOp::Assignment && is_equivalent(sema, &expr.lhs()?, name) | 150 | return Some(()); |
130 | { | ||
131 | // The last statement in the block is an assignment to the name we want | ||
132 | return Some(make::block_expr(stmts, Some(expr.rhs()?))); | ||
133 | } | ||
134 | } | 151 | } |
152 | None | ||
135 | } | 153 | } |
136 | None | ||
137 | } | 154 | } |
138 | 155 | ||
139 | fn is_equivalent( | 156 | fn is_equivalent( |
@@ -243,6 +260,37 @@ fn foo() { | |||
243 | } | 260 | } |
244 | 261 | ||
245 | #[test] | 262 | #[test] |
263 | fn test_pull_assignment_up_assignment_expressions() { | ||
264 | check_assist( | ||
265 | pull_assignment_up, | ||
266 | r#" | ||
267 | fn foo() { | ||
268 | let mut a = 1; | ||
269 | |||
270 | match 1 { | ||
271 | 1 => { $0a = 2; }, | ||
272 | 2 => a = 3, | ||
273 | 3 => { | ||
274 | a = 4 | ||
275 | } | ||
276 | } | ||
277 | }"#, | ||
278 | r#" | ||
279 | fn foo() { | ||
280 | let mut a = 1; | ||
281 | |||
282 | a = match 1 { | ||
283 | 1 => { 2 }, | ||
284 | 2 => 3, | ||
285 | 3 => { | ||
286 | 4 | ||
287 | } | ||
288 | }; | ||
289 | }"#, | ||
290 | ); | ||
291 | } | ||
292 | |||
293 | #[test] | ||
246 | fn test_pull_assignment_up_not_last_not_applicable() { | 294 | fn test_pull_assignment_up_not_last_not_applicable() { |
247 | check_assist_not_applicable( | 295 | check_assist_not_applicable( |
248 | pull_assignment_up, | 296 | pull_assignment_up, |
@@ -439,4 +487,24 @@ fn foo() { | |||
439 | "#, | 487 | "#, |
440 | ) | 488 | ) |
441 | } | 489 | } |
490 | |||
491 | #[test] | ||
492 | fn test_cant_pull_non_assignments() { | ||
493 | cov_mark::check!(test_cant_pull_non_assignments); | ||
494 | check_assist_not_applicable( | ||
495 | pull_assignment_up, | ||
496 | r#" | ||
497 | fn foo() { | ||
498 | let mut a = 1; | ||
499 | let b = &mut a; | ||
500 | |||
501 | if true { | ||
502 | $0*b + 2; | ||
503 | } else { | ||
504 | *b + 3; | ||
505 | } | ||
506 | } | ||
507 | "#, | ||
508 | ) | ||
509 | } | ||
442 | } | 510 | } |
diff --git a/crates/ide_assists/src/handlers/raw_string.rs b/crates/ide_assists/src/handlers/raw_string.rs index d0f1613f3..d98a55ae4 100644 --- a/crates/ide_assists/src/handlers/raw_string.rs +++ b/crates/ide_assists/src/handlers/raw_string.rs | |||
@@ -1,6 +1,6 @@ | |||
1 | use std::borrow::Cow; | 1 | use std::borrow::Cow; |
2 | 2 | ||
3 | use syntax::{ast, AstToken, TextRange, TextSize}; | 3 | use syntax::{ast, ast::IsString, AstToken, TextRange, TextSize}; |
4 | 4 | ||
5 | use crate::{AssistContext, AssistId, AssistKind, Assists}; | 5 | use crate::{AssistContext, AssistId, AssistKind, Assists}; |
6 | 6 | ||
diff --git a/crates/ide_assists/src/handlers/reorder_fields.rs b/crates/ide_assists/src/handlers/reorder_fields.rs index e90bbdbcf..933acead1 100644 --- a/crates/ide_assists/src/handlers/reorder_fields.rs +++ b/crates/ide_assists/src/handlers/reorder_fields.rs | |||
@@ -70,10 +70,10 @@ pub(crate) fn reorder_fields(acc: &mut Assists, ctx: &AssistContext) -> Option<( | |||
70 | target, | 70 | target, |
71 | |builder| match fields { | 71 | |builder| match fields { |
72 | Either::Left((sorted, field_list)) => { | 72 | Either::Left((sorted, field_list)) => { |
73 | replace(builder.make_ast_mut(field_list).fields(), sorted) | 73 | replace(builder.make_mut(field_list).fields(), sorted) |
74 | } | 74 | } |
75 | Either::Right((sorted, field_list)) => { | 75 | Either::Right((sorted, field_list)) => { |
76 | replace(builder.make_ast_mut(field_list).fields(), sorted) | 76 | replace(builder.make_mut(field_list).fields(), sorted) |
77 | } | 77 | } |
78 | }, | 78 | }, |
79 | ) | 79 | ) |
diff --git a/crates/ide_assists/src/handlers/reorder_impl.rs b/crates/ide_assists/src/handlers/reorder_impl.rs index 54a9a468e..5a6a9f158 100644 --- a/crates/ide_assists/src/handlers/reorder_impl.rs +++ b/crates/ide_assists/src/handlers/reorder_impl.rs | |||
@@ -79,8 +79,7 @@ pub(crate) fn reorder_impl(acc: &mut Assists, ctx: &AssistContext) -> Option<()> | |||
79 | "Sort methods", | 79 | "Sort methods", |
80 | target, | 80 | target, |
81 | |builder| { | 81 | |builder| { |
82 | let methods = | 82 | let methods = methods.into_iter().map(|fn_| builder.make_mut(fn_)).collect::<Vec<_>>(); |
83 | methods.into_iter().map(|fn_| builder.make_ast_mut(fn_)).collect::<Vec<_>>(); | ||
84 | methods | 83 | methods |
85 | .into_iter() | 84 | .into_iter() |
86 | .zip(sorted) | 85 | .zip(sorted) |
diff --git a/crates/ide_assists/src/handlers/replace_derive_with_manual_impl.rs b/crates/ide_assists/src/handlers/replace_derive_with_manual_impl.rs index 694d897d1..10d9cec31 100644 --- a/crates/ide_assists/src/handlers/replace_derive_with_manual_impl.rs +++ b/crates/ide_assists/src/handlers/replace_derive_with_manual_impl.rs | |||
@@ -84,7 +84,7 @@ pub(crate) fn replace_derive_with_manual_impl( | |||
84 | add_assist(acc, ctx, &attr, &args, &trait_path, Some(trait_), &adt)?; | 84 | add_assist(acc, ctx, &attr, &args, &trait_path, Some(trait_), &adt)?; |
85 | } | 85 | } |
86 | if no_traits_found { | 86 | if no_traits_found { |
87 | let trait_path = make::path_unqualified(make::path_segment(make::name_ref(trait_name))); | 87 | let trait_path = make::ext::ident_path(trait_name); |
88 | add_assist(acc, ctx, &attr, &args, &trait_path, None, &adt)?; | 88 | add_assist(acc, ctx, &attr, &args, &trait_path, None, &adt)?; |
89 | } | 89 | } |
90 | Some(()) | 90 | Some(()) |
@@ -159,10 +159,8 @@ fn impl_def_from_trait( | |||
159 | if trait_items.is_empty() { | 159 | if trait_items.is_empty() { |
160 | return None; | 160 | return None; |
161 | } | 161 | } |
162 | let impl_def = make::impl_trait( | 162 | let impl_def = |
163 | trait_path.clone(), | 163 | make::impl_trait(trait_path.clone(), make::ext::ident_path(&annotated_name.text())); |
164 | make::path_unqualified(make::path_segment(make::name_ref(&annotated_name.text()))), | ||
165 | ); | ||
166 | let (impl_def, first_assoc_item) = | 164 | let (impl_def, first_assoc_item) = |
167 | add_trait_assoc_items_to_impl(sema, trait_items, trait_, impl_def, target_scope); | 165 | add_trait_assoc_items_to_impl(sema, trait_items, trait_, impl_def, target_scope); |
168 | Some((impl_def, first_assoc_item)) | 166 | Some((impl_def, first_assoc_item)) |
diff --git a/crates/ide_assists/src/handlers/replace_impl_trait_with_generic.rs b/crates/ide_assists/src/handlers/replace_impl_trait_with_generic.rs index ff25b61ea..540a905cc 100644 --- a/crates/ide_assists/src/handlers/replace_impl_trait_with_generic.rs +++ b/crates/ide_assists/src/handlers/replace_impl_trait_with_generic.rs | |||
@@ -1,6 +1,9 @@ | |||
1 | use syntax::ast::{self, edit::AstNodeEdit, make, AstNode, GenericParamsOwner}; | 1 | use syntax::{ |
2 | ast::{self, edit_in_place::GenericParamsOwnerEdit, make, AstNode}, | ||
3 | ted, | ||
4 | }; | ||
2 | 5 | ||
3 | use crate::{AssistContext, AssistId, AssistKind, Assists}; | 6 | use crate::{utils::suggest_name, AssistContext, AssistId, AssistKind, Assists}; |
4 | 7 | ||
5 | // Assist: replace_impl_trait_with_generic | 8 | // Assist: replace_impl_trait_with_generic |
6 | // | 9 | // |
@@ -17,30 +20,29 @@ pub(crate) fn replace_impl_trait_with_generic( | |||
17 | acc: &mut Assists, | 20 | acc: &mut Assists, |
18 | ctx: &AssistContext, | 21 | ctx: &AssistContext, |
19 | ) -> Option<()> { | 22 | ) -> Option<()> { |
20 | let type_impl_trait = ctx.find_node_at_offset::<ast::ImplTraitType>()?; | 23 | let impl_trait_type = ctx.find_node_at_offset::<ast::ImplTraitType>()?; |
21 | let type_param = type_impl_trait.syntax().parent().and_then(ast::Param::cast)?; | 24 | let param = impl_trait_type.syntax().parent().and_then(ast::Param::cast)?; |
22 | let type_fn = type_param.syntax().ancestors().find_map(ast::Fn::cast)?; | 25 | let fn_ = param.syntax().ancestors().find_map(ast::Fn::cast)?; |
23 | 26 | ||
24 | let impl_trait_ty = type_impl_trait.type_bound_list()?; | 27 | let type_bound_list = impl_trait_type.type_bound_list()?; |
25 | 28 | ||
26 | let target = type_fn.syntax().text_range(); | 29 | let target = fn_.syntax().text_range(); |
27 | acc.add( | 30 | acc.add( |
28 | AssistId("replace_impl_trait_with_generic", AssistKind::RefactorRewrite), | 31 | AssistId("replace_impl_trait_with_generic", AssistKind::RefactorRewrite), |
29 | "Replace impl trait with generic", | 32 | "Replace impl trait with generic", |
30 | target, | 33 | target, |
31 | |edit| { | 34 | |edit| { |
32 | let generic_letter = impl_trait_ty.to_string().chars().next().unwrap().to_string(); | 35 | let impl_trait_type = edit.make_mut(impl_trait_type); |
36 | let fn_ = edit.make_mut(fn_); | ||
33 | 37 | ||
34 | let generic_param_list = type_fn | 38 | let type_param_name = suggest_name::for_generic_parameter(&impl_trait_type); |
35 | .generic_param_list() | ||
36 | .unwrap_or_else(|| make::generic_param_list(None)) | ||
37 | .append_param(make::generic_param(generic_letter.clone(), Some(impl_trait_ty))); | ||
38 | 39 | ||
39 | let new_type_fn = type_fn | 40 | let type_param = make::type_param(make::name(&type_param_name), Some(type_bound_list)) |
40 | .replace_descendant::<ast::Type>(type_impl_trait.into(), make::ty(&generic_letter)) | 41 | .clone_for_update(); |
41 | .with_generic_param_list(generic_param_list); | 42 | let new_ty = make::ty(&type_param_name).clone_for_update(); |
42 | 43 | ||
43 | edit.replace_ast(type_fn.clone(), new_type_fn); | 44 | ted::replace(impl_trait_type.syntax(), new_ty.syntax()); |
45 | fn_.get_or_create_generic_param_list().add_generic_param(type_param.into()) | ||
44 | }, | 46 | }, |
45 | ) | 47 | ) |
46 | } | 48 | } |
@@ -55,12 +57,8 @@ mod tests { | |||
55 | fn replace_impl_trait_with_generic_params() { | 57 | fn replace_impl_trait_with_generic_params() { |
56 | check_assist( | 58 | check_assist( |
57 | replace_impl_trait_with_generic, | 59 | replace_impl_trait_with_generic, |
58 | r#" | 60 | r#"fn foo<G>(bar: $0impl Bar) {}"#, |
59 | fn foo<G>(bar: $0impl Bar) {} | 61 | r#"fn foo<G, B: Bar>(bar: B) {}"#, |
60 | "#, | ||
61 | r#" | ||
62 | fn foo<G, B: Bar>(bar: B) {} | ||
63 | "#, | ||
64 | ); | 62 | ); |
65 | } | 63 | } |
66 | 64 | ||
@@ -68,12 +66,8 @@ mod tests { | |||
68 | fn replace_impl_trait_without_generic_params() { | 66 | fn replace_impl_trait_without_generic_params() { |
69 | check_assist( | 67 | check_assist( |
70 | replace_impl_trait_with_generic, | 68 | replace_impl_trait_with_generic, |
71 | r#" | 69 | r#"fn foo(bar: $0impl Bar) {}"#, |
72 | fn foo(bar: $0impl Bar) {} | 70 | r#"fn foo<B: Bar>(bar: B) {}"#, |
73 | "#, | ||
74 | r#" | ||
75 | fn foo<B: Bar>(bar: B) {} | ||
76 | "#, | ||
77 | ); | 71 | ); |
78 | } | 72 | } |
79 | 73 | ||
@@ -81,12 +75,8 @@ mod tests { | |||
81 | fn replace_two_impl_trait_with_generic_params() { | 75 | fn replace_two_impl_trait_with_generic_params() { |
82 | check_assist( | 76 | check_assist( |
83 | replace_impl_trait_with_generic, | 77 | replace_impl_trait_with_generic, |
84 | r#" | 78 | r#"fn foo<G>(foo: impl Foo, bar: $0impl Bar) {}"#, |
85 | fn foo<G>(foo: impl Foo, bar: $0impl Bar) {} | 79 | r#"fn foo<G, B: Bar>(foo: impl Foo, bar: B) {}"#, |
86 | "#, | ||
87 | r#" | ||
88 | fn foo<G, B: Bar>(foo: impl Foo, bar: B) {} | ||
89 | "#, | ||
90 | ); | 80 | ); |
91 | } | 81 | } |
92 | 82 | ||
@@ -94,12 +84,8 @@ mod tests { | |||
94 | fn replace_impl_trait_with_empty_generic_params() { | 84 | fn replace_impl_trait_with_empty_generic_params() { |
95 | check_assist( | 85 | check_assist( |
96 | replace_impl_trait_with_generic, | 86 | replace_impl_trait_with_generic, |
97 | r#" | 87 | r#"fn foo<>(bar: $0impl Bar) {}"#, |
98 | fn foo<>(bar: $0impl Bar) {} | 88 | r#"fn foo<B: Bar>(bar: B) {}"#, |
99 | "#, | ||
100 | r#" | ||
101 | fn foo<B: Bar>(bar: B) {} | ||
102 | "#, | ||
103 | ); | 89 | ); |
104 | } | 90 | } |
105 | 91 | ||
@@ -108,13 +94,13 @@ mod tests { | |||
108 | check_assist( | 94 | check_assist( |
109 | replace_impl_trait_with_generic, | 95 | replace_impl_trait_with_generic, |
110 | r#" | 96 | r#" |
111 | fn foo< | 97 | fn foo< |
112 | >(bar: $0impl Bar) {} | 98 | >(bar: $0impl Bar) {} |
113 | "#, | 99 | "#, |
114 | r#" | 100 | r#" |
115 | fn foo<B: Bar | 101 | fn foo<B: Bar |
116 | >(bar: B) {} | 102 | >(bar: B) {} |
117 | "#, | 103 | "#, |
118 | ); | 104 | ); |
119 | } | 105 | } |
120 | 106 | ||
@@ -123,12 +109,8 @@ mod tests { | |||
123 | fn replace_impl_trait_with_exist_generic_letter() { | 109 | fn replace_impl_trait_with_exist_generic_letter() { |
124 | check_assist( | 110 | check_assist( |
125 | replace_impl_trait_with_generic, | 111 | replace_impl_trait_with_generic, |
126 | r#" | 112 | r#"fn foo<B>(bar: $0impl Bar) {}"#, |
127 | fn foo<B>(bar: $0impl Bar) {} | 113 | r#"fn foo<B, C: Bar>(bar: C) {}"#, |
128 | "#, | ||
129 | r#" | ||
130 | fn foo<B, C: Bar>(bar: C) {} | ||
131 | "#, | ||
132 | ); | 114 | ); |
133 | } | 115 | } |
134 | 116 | ||
@@ -137,19 +119,19 @@ mod tests { | |||
137 | check_assist( | 119 | check_assist( |
138 | replace_impl_trait_with_generic, | 120 | replace_impl_trait_with_generic, |
139 | r#" | 121 | r#" |
140 | fn foo< | 122 | fn foo< |
141 | G: Foo, | 123 | G: Foo, |
142 | F, | 124 | F, |
143 | H, | 125 | H, |
144 | >(bar: $0impl Bar) {} | 126 | >(bar: $0impl Bar) {} |
145 | "#, | 127 | "#, |
146 | r#" | 128 | r#" |
147 | fn foo< | 129 | fn foo< |
148 | G: Foo, | 130 | G: Foo, |
149 | F, | 131 | F, |
150 | H, B: Bar | 132 | H, B: Bar, |
151 | >(bar: B) {} | 133 | >(bar: B) {} |
152 | "#, | 134 | "#, |
153 | ); | 135 | ); |
154 | } | 136 | } |
155 | 137 | ||
@@ -157,12 +139,8 @@ mod tests { | |||
157 | fn replace_impl_trait_multiple() { | 139 | fn replace_impl_trait_multiple() { |
158 | check_assist( | 140 | check_assist( |
159 | replace_impl_trait_with_generic, | 141 | replace_impl_trait_with_generic, |
160 | r#" | 142 | r#"fn foo(bar: $0impl Foo + Bar) {}"#, |
161 | fn foo(bar: $0impl Foo + Bar) {} | 143 | r#"fn foo<F: Foo + Bar>(bar: F) {}"#, |
162 | "#, | ||
163 | r#" | ||
164 | fn foo<F: Foo + Bar>(bar: F) {} | ||
165 | "#, | ||
166 | ); | 144 | ); |
167 | } | 145 | } |
168 | } | 146 | } |
diff --git a/crates/ide_assists/src/handlers/replace_let_with_if_let.rs b/crates/ide_assists/src/handlers/replace_let_with_if_let.rs index be7e724b5..1ad0fa816 100644 --- a/crates/ide_assists/src/handlers/replace_let_with_if_let.rs +++ b/crates/ide_assists/src/handlers/replace_let_with_if_let.rs | |||
@@ -50,22 +50,19 @@ pub(crate) fn replace_let_with_if_let(acc: &mut Assists, ctx: &AssistContext) -> | |||
50 | "Replace with if-let", | 50 | "Replace with if-let", |
51 | target, | 51 | target, |
52 | |edit| { | 52 | |edit| { |
53 | let with_placeholder: ast::Pat = match happy_variant { | 53 | let pat = match happy_variant { |
54 | None => make::wildcard_pat().into(), | 54 | None => original_pat, |
55 | Some(var_name) => make::tuple_struct_pat( | 55 | Some(var_name) => { |
56 | make::path_unqualified(make::path_segment(make::name_ref(var_name))), | 56 | make::tuple_struct_pat(make::ext::ident_path(var_name), once(original_pat)) |
57 | once(make::wildcard_pat().into()), | 57 | .into() |
58 | ) | 58 | } |
59 | .into(), | ||
60 | }; | 59 | }; |
60 | |||
61 | let block = | 61 | let block = |
62 | make::block_expr(None, None).indent(IndentLevel::from_node(let_stmt.syntax())); | 62 | make::ext::empty_block_expr().indent(IndentLevel::from_node(let_stmt.syntax())); |
63 | let if_ = make::expr_if(make::condition(init, Some(with_placeholder)), block, None); | 63 | let if_ = make::expr_if(make::condition(init, Some(pat)), block, None); |
64 | let stmt = make::expr_stmt(if_); | 64 | let stmt = make::expr_stmt(if_); |
65 | 65 | ||
66 | let placeholder = stmt.syntax().descendants().find_map(ast::WildcardPat::cast).unwrap(); | ||
67 | let stmt = stmt.replace_descendant(placeholder.into(), original_pat); | ||
68 | |||
69 | edit.replace_ast(ast::Stmt::from(let_stmt), ast::Stmt::from(stmt)); | 66 | edit.replace_ast(ast::Stmt::from(let_stmt), ast::Stmt::from(stmt)); |
70 | }, | 67 | }, |
71 | ) | 68 | ) |
diff --git a/crates/ide_assists/src/handlers/replace_qualified_name_with_use.rs b/crates/ide_assists/src/handlers/replace_qualified_name_with_use.rs index 99ba79860..39f5eb4ff 100644 --- a/crates/ide_assists/src/handlers/replace_qualified_name_with_use.rs +++ b/crates/ide_assists/src/handlers/replace_qualified_name_with_use.rs | |||
@@ -40,7 +40,7 @@ pub(crate) fn replace_qualified_name_with_use( | |||
40 | |builder| { | 40 | |builder| { |
41 | // Now that we've brought the name into scope, re-qualify all paths that could be | 41 | // Now that we've brought the name into scope, re-qualify all paths that could be |
42 | // affected (that is, all paths inside the node we added the `use` to). | 42 | // affected (that is, all paths inside the node we added the `use` to). |
43 | let syntax = builder.make_mut(syntax.clone()); | 43 | let syntax = builder.make_syntax_mut(syntax.clone()); |
44 | if let Some(ref import_scope) = ImportScope::from(syntax.clone()) { | 44 | if let Some(ref import_scope) = ImportScope::from(syntax.clone()) { |
45 | shorten_paths(&syntax, &path.clone_for_update()); | 45 | shorten_paths(&syntax, &path.clone_for_update()); |
46 | insert_use(import_scope, path, ctx.config.insert_use); | 46 | insert_use(import_scope, path, ctx.config.insert_use); |
diff --git a/crates/ide_assists/src/handlers/replace_string_with_char.rs b/crates/ide_assists/src/handlers/replace_string_with_char.rs index 634b9c0b7..0800d291e 100644 --- a/crates/ide_assists/src/handlers/replace_string_with_char.rs +++ b/crates/ide_assists/src/handlers/replace_string_with_char.rs | |||
@@ -1,4 +1,4 @@ | |||
1 | use syntax::{ast, AstToken, SyntaxKind::STRING}; | 1 | use syntax::{ast, ast::IsString, AstToken, SyntaxKind::STRING}; |
2 | 2 | ||
3 | use crate::{AssistContext, AssistId, AssistKind, Assists}; | 3 | use crate::{AssistContext, AssistId, AssistKind, Assists}; |
4 | 4 | ||
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 a986a6ae8..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) } |
@@ -32,7 +32,7 @@ use ide_db::ty_filter::TryEnum; | |||
32 | // fn main() { | 32 | // fn main() { |
33 | // let x: Result<i32, i32> = Result::Ok(92); | 33 | // let x: Result<i32, i32> = Result::Ok(92); |
34 | // let y = match x { | 34 | // let y = match x { |
35 | // Ok(a) => a, | 35 | // Ok(it) => it, |
36 | // $0_ => unreachable!(), | 36 | // $0_ => unreachable!(), |
37 | // }; | 37 | // }; |
38 | // } | 38 | // } |
@@ -52,16 +52,17 @@ pub(crate) fn replace_unwrap_with_match(acc: &mut Assists, ctx: &AssistContext) | |||
52 | "Replace unwrap with match", | 52 | "Replace unwrap with match", |
53 | target, | 53 | target, |
54 | |builder| { | 54 | |builder| { |
55 | let ok_path = make::path_unqualified(make::path_segment(make::name_ref(happy_variant))); | 55 | let ok_path = make::ext::ident_path(happy_variant); |
56 | let it = make::ident_pat(make::name("a")).into(); | 56 | let it = make::ident_pat(make::name("it")).into(); |
57 | let ok_tuple = make::tuple_struct_pat(ok_path, iter::once(it)).into(); | 57 | let ok_tuple = make::tuple_struct_pat(ok_path, iter::once(it)).into(); |
58 | 58 | ||
59 | let bind_path = make::path_unqualified(make::path_segment(make::name_ref("a"))); | 59 | let bind_path = make::ext::ident_path("it"); |
60 | let ok_arm = make::match_arm(iter::once(ok_tuple), make::expr_path(bind_path)); | 60 | let ok_arm = make::match_arm(iter::once(ok_tuple), make::expr_path(bind_path)); |
61 | 61 | ||
62 | let unreachable_call = make::expr_unreachable(); | 62 | let err_arm = make::match_arm( |
63 | let err_arm = | 63 | iter::once(make::wildcard_pat().into()), |
64 | make::match_arm(iter::once(make::wildcard_pat().into()), unreachable_call); | 64 | make::ext::expr_unreachable(), |
65 | ); | ||
65 | 66 | ||
66 | let match_arm_list = make::match_arm_list(vec![ok_arm, err_arm]); | 67 | let match_arm_list = make::match_arm_list(vec![ok_arm, err_arm]); |
67 | let match_expr = make::expr_match(caller.clone(), match_arm_list) | 68 | let match_expr = make::expr_match(caller.clone(), match_arm_list) |
@@ -110,7 +111,7 @@ fn i<T>(a: T) -> T { a } | |||
110 | fn main() { | 111 | fn main() { |
111 | let x: Result<i32, i32> = Result::Ok(92); | 112 | let x: Result<i32, i32> = Result::Ok(92); |
112 | let y = match i(x) { | 113 | let y = match i(x) { |
113 | Ok(a) => a, | 114 | Ok(it) => it, |
114 | $0_ => unreachable!(), | 115 | $0_ => unreachable!(), |
115 | }; | 116 | }; |
116 | } | 117 | } |
@@ -136,7 +137,7 @@ fn i<T>(a: T) -> T { a } | |||
136 | fn main() { | 137 | fn main() { |
137 | let x = Option::Some(92); | 138 | let x = Option::Some(92); |
138 | let y = match i(x) { | 139 | let y = match i(x) { |
139 | Some(a) => a, | 140 | Some(it) => it, |
140 | $0_ => unreachable!(), | 141 | $0_ => unreachable!(), |
141 | }; | 142 | }; |
142 | } | 143 | } |
@@ -162,7 +163,7 @@ fn i<T>(a: T) -> T { a } | |||
162 | fn main() { | 163 | fn main() { |
163 | let x: Result<i32, i32> = Result::Ok(92); | 164 | let x: Result<i32, i32> = Result::Ok(92); |
164 | let y = match i(x) { | 165 | let y = match i(x) { |
165 | Ok(a) => a, | 166 | Ok(it) => it, |
166 | $0_ => unreachable!(), | 167 | $0_ => unreachable!(), |
167 | }.count_zeroes(); | 168 | }.count_zeroes(); |
168 | } | 169 | } |
diff --git a/crates/ide_assists/src/handlers/wrap_return_type_in_result.rs b/crates/ide_assists/src/handlers/wrap_return_type_in_result.rs index e838630ea..2f1da82c7 100644 --- a/crates/ide_assists/src/handlers/wrap_return_type_in_result.rs +++ b/crates/ide_assists/src/handlers/wrap_return_type_in_result.rs | |||
@@ -54,9 +54,7 @@ pub(crate) fn wrap_return_type_in_result(acc: &mut Assists, ctx: &AssistContext) | |||
54 | 54 | ||
55 | for ret_expr_arg in tail_return_expr_collector.exprs_to_wrap { | 55 | for ret_expr_arg in tail_return_expr_collector.exprs_to_wrap { |
56 | let ok_wrapped = make::expr_call( | 56 | let ok_wrapped = make::expr_call( |
57 | make::expr_path(make::path_unqualified(make::path_segment(make::name_ref( | 57 | make::expr_path(make::ext::ident_path("Ok")), |
58 | "Ok", | ||
59 | )))), | ||
60 | make::arg_list(iter::once(ret_expr_arg.clone())), | 58 | make::arg_list(iter::once(ret_expr_arg.clone())), |
61 | ); | 59 | ); |
62 | builder.replace_ast(ret_expr_arg, ok_wrapped); | 60 | builder.replace_ast(ret_expr_arg, ok_wrapped); |
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_assists/src/tests/generated.rs b/crates/ide_assists/src/tests/generated.rs index 59bcef8fb..4406406a2 100644 --- a/crates/ide_assists/src/tests/generated.rs +++ b/crates/ide_assists/src/tests/generated.rs | |||
@@ -50,7 +50,6 @@ trait Trait { | |||
50 | impl Trait for () { | 50 | impl Trait for () { |
51 | type X = (); | 51 | type X = (); |
52 | fn foo(&self) {}$0 | 52 | fn foo(&self) {}$0 |
53 | |||
54 | } | 53 | } |
55 | "#####, | 54 | "#####, |
56 | r#####" | 55 | r#####" |
@@ -1531,7 +1530,7 @@ enum Result<T, E> { Ok(T), Err(E) } | |||
1531 | fn main() { | 1530 | fn main() { |
1532 | let x: Result<i32, i32> = Result::Ok(92); | 1531 | let x: Result<i32, i32> = Result::Ok(92); |
1533 | let y = match x { | 1532 | let y = match x { |
1534 | Ok(a) => a, | 1533 | Ok(it) => it, |
1535 | $0_ => unreachable!(), | 1534 | $0_ => unreachable!(), |
1536 | }; | 1535 | }; |
1537 | } | 1536 | } |
diff --git a/crates/ide_assists/src/utils.rs b/crates/ide_assists/src/utils.rs index 5a90ad715..fc7caee04 100644 --- a/crates/ide_assists/src/utils.rs +++ b/crates/ide_assists/src/utils.rs | |||
@@ -17,7 +17,7 @@ use syntax::{ | |||
17 | ast::AttrsOwner, | 17 | ast::AttrsOwner, |
18 | ast::NameOwner, | 18 | ast::NameOwner, |
19 | ast::{self, edit, make, ArgListOwner, GenericParamsOwner}, | 19 | ast::{self, edit, make, ArgListOwner, GenericParamsOwner}, |
20 | AstNode, Direction, SmolStr, | 20 | ted, AstNode, Direction, SmolStr, |
21 | SyntaxKind::*, | 21 | SyntaxKind::*, |
22 | SyntaxNode, TextSize, T, | 22 | SyntaxNode, TextSize, T, |
23 | }; | 23 | }; |
@@ -128,43 +128,43 @@ pub fn add_trait_assoc_items_to_impl( | |||
128 | sema: &hir::Semantics<ide_db::RootDatabase>, | 128 | sema: &hir::Semantics<ide_db::RootDatabase>, |
129 | items: Vec<ast::AssocItem>, | 129 | items: Vec<ast::AssocItem>, |
130 | trait_: hir::Trait, | 130 | trait_: hir::Trait, |
131 | impl_def: ast::Impl, | 131 | impl_: ast::Impl, |
132 | target_scope: hir::SemanticsScope, | 132 | target_scope: hir::SemanticsScope, |
133 | ) -> (ast::Impl, ast::AssocItem) { | 133 | ) -> (ast::Impl, ast::AssocItem) { |
134 | let impl_item_list = impl_def.assoc_item_list().unwrap_or_else(make::assoc_item_list); | ||
135 | |||
136 | let n_existing_items = impl_item_list.assoc_items().count(); | ||
137 | let source_scope = sema.scope_for_def(trait_); | 134 | let source_scope = sema.scope_for_def(trait_); |
138 | let ast_transform = QualifyPaths::new(&target_scope, &source_scope) | 135 | let ast_transform = QualifyPaths::new(&target_scope, &source_scope) |
139 | .or(SubstituteTypeParams::for_trait_impl(&source_scope, trait_, impl_def.clone())); | 136 | .or(SubstituteTypeParams::for_trait_impl(&source_scope, trait_, impl_.clone())); |
140 | 137 | ||
141 | let items = items | 138 | let items = items |
142 | .into_iter() | 139 | .into_iter() |
143 | .map(|it| it.clone_for_update()) | 140 | .map(|it| it.clone_for_update()) |
144 | .inspect(|it| ast_transform::apply(&*ast_transform, it)) | 141 | .inspect(|it| ast_transform::apply(&*ast_transform, it)) |
145 | .map(|it| match it { | 142 | .map(|it| edit::remove_attrs_and_docs(&it).clone_subtree().clone_for_update()); |
146 | ast::AssocItem::Fn(def) => ast::AssocItem::Fn(add_body(def)), | 143 | |
147 | ast::AssocItem::TypeAlias(def) => ast::AssocItem::TypeAlias(def.remove_bounds()), | 144 | let res = impl_.clone_for_update(); |
148 | _ => it, | 145 | |
149 | }) | 146 | let assoc_item_list = res.get_or_create_assoc_item_list(); |
150 | .map(|it| edit::remove_attrs_and_docs(&it)); | 147 | let mut first_item = None; |
151 | 148 | for item in items { | |
152 | let new_impl_item_list = impl_item_list.append_items(items); | 149 | first_item.get_or_insert_with(|| item.clone()); |
153 | let new_impl_def = impl_def.with_assoc_item_list(new_impl_item_list); | 150 | match &item { |
154 | let first_new_item = | 151 | ast::AssocItem::Fn(fn_) if fn_.body().is_none() => { |
155 | new_impl_def.assoc_item_list().unwrap().assoc_items().nth(n_existing_items).unwrap(); | 152 | let body = make::block_expr(None, Some(make::ext::expr_todo())) |
156 | return (new_impl_def, first_new_item); | 153 | .indent(edit::IndentLevel(1)); |
157 | 154 | ted::replace(fn_.get_or_create_body().syntax(), body.clone_for_update().syntax()) | |
158 | fn add_body(fn_def: ast::Fn) -> ast::Fn { | 155 | } |
159 | match fn_def.body() { | 156 | ast::AssocItem::TypeAlias(type_alias) => { |
160 | Some(_) => fn_def, | 157 | if let Some(type_bound_list) = type_alias.type_bound_list() { |
161 | None => { | 158 | type_bound_list.remove() |
162 | let body = | 159 | } |
163 | make::block_expr(None, Some(make::expr_todo())).indent(edit::IndentLevel(1)); | ||
164 | fn_def.with_body(body) | ||
165 | } | 160 | } |
161 | _ => {} | ||
166 | } | 162 | } |
163 | |||
164 | assoc_item_list.add_item(item) | ||
167 | } | 165 | } |
166 | |||
167 | (res, first_item.unwrap()) | ||
168 | } | 168 | } |
169 | 169 | ||
170 | #[derive(Clone, Copy, Debug)] | 170 | #[derive(Clone, Copy, Debug)] |
diff --git a/crates/ide_assists/src/utils/suggest_name.rs b/crates/ide_assists/src/utils/suggest_name.rs index deafcd630..b3aabeab3 100644 --- a/crates/ide_assists/src/utils/suggest_name.rs +++ b/crates/ide_assists/src/utils/suggest_name.rs | |||
@@ -6,7 +6,7 @@ use itertools::Itertools; | |||
6 | use stdx::to_lower_snake_case; | 6 | use stdx::to_lower_snake_case; |
7 | use syntax::{ | 7 | use syntax::{ |
8 | ast::{self, NameOwner}, | 8 | ast::{self, NameOwner}, |
9 | match_ast, AstNode, | 9 | match_ast, AstNode, SmolStr, |
10 | }; | 10 | }; |
11 | 11 | ||
12 | /// Trait names, that will be ignored when in `impl Trait` and `dyn Trait` | 12 | /// Trait names, that will be ignored when in `impl Trait` and `dyn Trait` |
@@ -57,6 +57,14 @@ const USELESS_METHODS: &[&str] = &[ | |||
57 | "iter_mut", | 57 | "iter_mut", |
58 | ]; | 58 | ]; |
59 | 59 | ||
60 | pub(crate) fn for_generic_parameter(ty: &ast::ImplTraitType) -> SmolStr { | ||
61 | let c = ty | ||
62 | .type_bound_list() | ||
63 | .and_then(|bounds| bounds.syntax().text().char_at(0.into())) | ||
64 | .unwrap_or('T'); | ||
65 | c.encode_utf8(&mut [0; 4]).into() | ||
66 | } | ||
67 | |||
60 | /// Suggest name of variable for given expression | 68 | /// Suggest name of variable for given expression |
61 | /// | 69 | /// |
62 | /// **NOTE**: it is caller's responsibility to guarantee uniqueness of the name. | 70 | /// **NOTE**: it is caller's responsibility to guarantee uniqueness of the name. |
@@ -75,7 +83,8 @@ const USELESS_METHODS: &[&str] = &[ | |||
75 | /// It also applies heuristics to filter out less informative names | 83 | /// It also applies heuristics to filter out less informative names |
76 | /// | 84 | /// |
77 | /// Currently it sticks to the first name found. | 85 | /// Currently it sticks to the first name found. |
78 | pub(crate) fn variable(expr: &ast::Expr, sema: &Semantics<'_, RootDatabase>) -> String { | 86 | // FIXME: Microoptimize and return a `SmolStr` here. |
87 | pub(crate) fn for_variable(expr: &ast::Expr, sema: &Semantics<'_, RootDatabase>) -> String { | ||
79 | // `from_param` does not benifit from stripping | 88 | // `from_param` does not benifit from stripping |
80 | // it need the largest context possible | 89 | // it need the largest context possible |
81 | // so we check firstmost | 90 | // so we check firstmost |
@@ -276,7 +285,7 @@ mod tests { | |||
276 | frange.range, | 285 | frange.range, |
277 | "selection is not an expression(yet contained in one)" | 286 | "selection is not an expression(yet contained in one)" |
278 | ); | 287 | ); |
279 | let name = variable(&expr, &sema); | 288 | let name = for_variable(&expr, &sema); |
280 | assert_eq!(&name, expected); | 289 | assert_eq!(&name, expected); |
281 | } | 290 | } |
282 | 291 | ||
diff --git a/crates/ide_completion/src/completions/keyword.rs b/crates/ide_completion/src/completions/keyword.rs index b635e0ca3..61b667104 100644 --- a/crates/ide_completion/src/completions/keyword.rs +++ b/crates/ide_completion/src/completions/keyword.rs | |||
@@ -9,22 +9,21 @@ use crate::{CompletionContext, CompletionItem, CompletionItemKind, CompletionKin | |||
9 | pub(crate) fn complete_use_tree_keyword(acc: &mut Completions, ctx: &CompletionContext) { | 9 | pub(crate) fn complete_use_tree_keyword(acc: &mut Completions, ctx: &CompletionContext) { |
10 | // complete keyword "crate" in use stmt | 10 | // complete keyword "crate" in use stmt |
11 | let source_range = ctx.source_range(); | 11 | let source_range = ctx.source_range(); |
12 | let kw_completion = move |text: &str| { | ||
13 | let mut item = CompletionItem::new(CompletionKind::Keyword, source_range, text); | ||
14 | item.kind(CompletionItemKind::Keyword).insert_text(text); | ||
15 | item | ||
16 | }; | ||
12 | 17 | ||
13 | if ctx.use_item_syntax.is_some() { | 18 | if ctx.use_item_syntax.is_some() { |
14 | if ctx.path_qual.is_none() { | 19 | if ctx.path_qual.is_none() { |
15 | let mut item = CompletionItem::new(CompletionKind::Keyword, source_range, "crate::"); | 20 | kw_completion("crate::").add_to(acc); |
16 | item.kind(CompletionItemKind::Keyword).insert_text("crate::"); | ||
17 | item.add_to(acc); | ||
18 | } | 21 | } |
19 | let mut item = CompletionItem::new(CompletionKind::Keyword, source_range, "self"); | 22 | kw_completion("self").add_to(acc); |
20 | item.kind(CompletionItemKind::Keyword); | ||
21 | item.add_to(acc); | ||
22 | if iter::successors(ctx.path_qual.clone(), |p| p.qualifier()) | 23 | if iter::successors(ctx.path_qual.clone(), |p| p.qualifier()) |
23 | .all(|p| p.segment().and_then(|s| s.super_token()).is_some()) | 24 | .all(|p| p.segment().and_then(|s| s.super_token()).is_some()) |
24 | { | 25 | { |
25 | let mut item = CompletionItem::new(CompletionKind::Keyword, source_range, "super::"); | 26 | kw_completion("super::").add_to(acc); |
26 | item.kind(CompletionItemKind::Keyword).insert_text("super::"); | ||
27 | item.add_to(acc); | ||
28 | } | 27 | } |
29 | } | 28 | } |
30 | 29 | ||
@@ -32,9 +31,8 @@ pub(crate) fn complete_use_tree_keyword(acc: &mut Completions, ctx: &CompletionC | |||
32 | if let Some(receiver) = &ctx.dot_receiver { | 31 | if let Some(receiver) = &ctx.dot_receiver { |
33 | if let Some(ty) = ctx.sema.type_of_expr(receiver) { | 32 | if let Some(ty) = ctx.sema.type_of_expr(receiver) { |
34 | if ty.impls_future(ctx.db) { | 33 | if ty.impls_future(ctx.db) { |
35 | let mut item = | 34 | let mut item = kw_completion("await"); |
36 | CompletionItem::new(CompletionKind::Keyword, ctx.source_range(), "await"); | 35 | item.detail("expr.await"); |
37 | item.kind(CompletionItemKind::Keyword).detail("expr.await").insert_text("await"); | ||
38 | item.add_to(acc); | 36 | item.add_to(acc); |
39 | } | 37 | } |
40 | }; | 38 | }; |
diff --git a/crates/ide_completion/src/context.rs b/crates/ide_completion/src/context.rs index 62ef40818..787eb2fd3 100644 --- a/crates/ide_completion/src/context.rs +++ b/crates/ide_completion/src/context.rs | |||
@@ -313,7 +313,8 @@ impl<'a> CompletionContext<'a> { | |||
313 | cov_mark::hit!(expected_type_let_with_leading_char); | 313 | cov_mark::hit!(expected_type_let_with_leading_char); |
314 | cov_mark::hit!(expected_type_let_without_leading_char); | 314 | cov_mark::hit!(expected_type_let_without_leading_char); |
315 | let ty = it.pat() | 315 | let ty = it.pat() |
316 | .and_then(|pat| self.sema.type_of_pat(&pat)); | 316 | .and_then(|pat| self.sema.type_of_pat(&pat)) |
317 | .or_else(|| it.initializer().and_then(|it| self.sema.type_of_expr(&it))); | ||
317 | let name = if let Some(ast::Pat::IdentPat(ident)) = it.pat() { | 318 | let name = if let Some(ast::Pat::IdentPat(ident)) = it.pat() { |
318 | ident.name().map(NameOrNameRef::Name) | 319 | ident.name().map(NameOrNameRef::Name) |
319 | } else { | 320 | } else { |
@@ -720,6 +721,26 @@ fn foo() { | |||
720 | } | 721 | } |
721 | 722 | ||
722 | #[test] | 723 | #[test] |
724 | fn expected_type_let_pat() { | ||
725 | check_expected_type_and_name( | ||
726 | r#" | ||
727 | fn foo() { | ||
728 | let x$0 = 0u32; | ||
729 | } | ||
730 | "#, | ||
731 | expect![[r#"ty: u32, name: ?"#]], | ||
732 | ); | ||
733 | check_expected_type_and_name( | ||
734 | r#" | ||
735 | fn foo() { | ||
736 | let $0 = 0u32; | ||
737 | } | ||
738 | "#, | ||
739 | expect![[r#"ty: u32, name: ?"#]], | ||
740 | ); | ||
741 | } | ||
742 | |||
743 | #[test] | ||
723 | fn expected_type_fn_param_without_leading_char() { | 744 | fn expected_type_fn_param_without_leading_char() { |
724 | cov_mark::check!(expected_type_fn_param_without_leading_char); | 745 | cov_mark::check!(expected_type_fn_param_without_leading_char); |
725 | check_expected_type_and_name( | 746 | check_expected_type_and_name( |
diff --git a/crates/ide_completion/src/lib.rs b/crates/ide_completion/src/lib.rs index e32633565..645349215 100644 --- a/crates/ide_completion/src/lib.rs +++ b/crates/ide_completion/src/lib.rs | |||
@@ -80,7 +80,7 @@ pub use crate::{ | |||
80 | // | 80 | // |
81 | // And the auto import completions, enabled with the `rust-analyzer.completion.autoimport.enable` setting and the corresponding LSP client capabilities. | 81 | // And the auto import completions, enabled with the `rust-analyzer.completion.autoimport.enable` setting and the corresponding LSP client capabilities. |
82 | // Those are the additional completion options with automatic `use` import and options from all project importable items, | 82 | // Those are the additional completion options with automatic `use` import and options from all project importable items, |
83 | // fuzzy matched agains the completion imput. | 83 | // fuzzy matched against the completion input. |
84 | // | 84 | // |
85 | // image::https://user-images.githubusercontent.com/48062697/113020667-b72ab880-917a-11eb-8778-716cf26a0eb3.gif[] | 85 | // image::https://user-images.githubusercontent.com/48062697/113020667-b72ab880-917a-11eb-8778-716cf26a0eb3.gif[] |
86 | 86 | ||
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 3f5bbef7f..8fb40e837 100644 --- a/crates/ide_db/src/helpers/merge_imports.rs +++ b/crates/ide_db/src/helpers/merge_imports.rs | |||
@@ -2,26 +2,27 @@ | |||
2 | use std::cmp::Ordering; | 2 | use std::cmp::Ordering; |
3 | 3 | ||
4 | use itertools::{EitherOrBoth, Itertools}; | 4 | use itertools::{EitherOrBoth, Itertools}; |
5 | use syntax::ast::{ | 5 | use syntax::{ |
6 | self, edit::AstNodeEdit, make, AstNode, AttrsOwner, PathSegmentKind, VisibilityOwner, | 6 | ast::{self, make, AstNode, AttrsOwner, PathSegmentKind, VisibilityOwner}, |
7 | ted, | ||
7 | }; | 8 | }; |
8 | 9 | ||
9 | /// What type of merges are allowed. | 10 | /// What type of merges are allowed. |
10 | #[derive(Copy, Clone, Debug, PartialEq, Eq)] | 11 | #[derive(Copy, Clone, Debug, PartialEq, Eq)] |
11 | pub enum MergeBehavior { | 12 | pub enum MergeBehavior { |
12 | /// Merge everything together creating deeply nested imports. | 13 | /// Merge imports from the same crate into a single use statement. |
13 | Full, | 14 | Crate, |
14 | /// Only merge the last import level, doesn't allow import nesting. | 15 | /// Merge imports from the same module into a single use statement. |
15 | Last, | 16 | Module, |
16 | } | 17 | } |
17 | 18 | ||
18 | impl MergeBehavior { | 19 | impl MergeBehavior { |
19 | #[inline] | 20 | #[inline] |
20 | fn is_tree_allowed(&self, tree: &ast::UseTree) -> bool { | 21 | fn is_tree_allowed(&self, tree: &ast::UseTree) -> bool { |
21 | match self { | 22 | match self { |
22 | MergeBehavior::Full => true, | 23 | MergeBehavior::Crate => true, |
23 | // only simple single segment paths are allowed | 24 | // only simple single segment paths are allowed |
24 | MergeBehavior::Last => { | 25 | MergeBehavior::Module => { |
25 | tree.use_tree_list().is_none() && tree.path().map(path_len) <= Some(1) | 26 | tree.use_tree_list().is_none() && tree.path().map(path_len) <= Some(1) |
26 | } | 27 | } |
27 | } | 28 | } |
@@ -41,10 +42,12 @@ pub fn try_merge_imports( | |||
41 | return None; | 42 | return None; |
42 | } | 43 | } |
43 | 44 | ||
45 | let lhs = lhs.clone_subtree().clone_for_update(); | ||
44 | let lhs_tree = lhs.use_tree()?; | 46 | let lhs_tree = lhs.use_tree()?; |
45 | let rhs_tree = rhs.use_tree()?; | 47 | let rhs_tree = rhs.use_tree()?; |
46 | let merged = try_merge_trees(&lhs_tree, &rhs_tree, merge_behavior)?; | 48 | let merged = try_merge_trees(&lhs_tree, &rhs_tree, merge_behavior)?; |
47 | Some(lhs.with_use_tree(merged).clone_for_update()) | 49 | ted::replace(lhs_tree.syntax(), merged.syntax()); |
50 | Some(lhs) | ||
48 | } | 51 | } |
49 | 52 | ||
50 | pub fn try_merge_trees( | 53 | pub fn try_merge_trees( |
@@ -65,7 +68,7 @@ pub fn try_merge_trees( | |||
65 | } else { | 68 | } else { |
66 | (lhs.split_prefix(&lhs_prefix), rhs.split_prefix(&rhs_prefix)) | 69 | (lhs.split_prefix(&lhs_prefix), rhs.split_prefix(&rhs_prefix)) |
67 | }; | 70 | }; |
68 | recursive_merge(&lhs, &rhs, merge) | 71 | recursive_merge(&lhs, &rhs, merge).map(|it| it.clone_for_update()) |
69 | } | 72 | } |
70 | 73 | ||
71 | /// Recursively "zips" together lhs and rhs. | 74 | /// Recursively "zips" together lhs and rhs. |
@@ -78,7 +81,8 @@ fn recursive_merge( | |||
78 | .use_tree_list() | 81 | .use_tree_list() |
79 | .into_iter() | 82 | .into_iter() |
80 | .flat_map(|list| list.use_trees()) | 83 | .flat_map(|list| list.use_trees()) |
81 | // we use Option here to early return from this function(this is not the same as a `filter` op) | 84 | // We use Option here to early return from this function(this is not the |
85 | // same as a `filter` op). | ||
82 | .map(|tree| match merge.is_tree_allowed(&tree) { | 86 | .map(|tree| match merge.is_tree_allowed(&tree) { |
83 | true => Some(tree), | 87 | true => Some(tree), |
84 | false => None, | 88 | false => None, |
@@ -111,8 +115,10 @@ fn recursive_merge( | |||
111 | let tree_is_self = |tree: ast::UseTree| { | 115 | let tree_is_self = |tree: ast::UseTree| { |
112 | tree.path().as_ref().map(path_is_self).unwrap_or(false) | 116 | tree.path().as_ref().map(path_is_self).unwrap_or(false) |
113 | }; | 117 | }; |
114 | // check if only one of the two trees has a tree list, and whether that then contains `self` or not. | 118 | // Check if only one of the two trees has a tree list, and |
115 | // If this is the case we can skip this iteration since the path without the list is already included in the other one via `self` | 119 | // whether that then contains `self` or not. If this is the |
120 | // case we can skip this iteration since the path without | ||
121 | // the list is already included in the other one via `self`. | ||
116 | let tree_contains_self = |tree: &ast::UseTree| { | 122 | let tree_contains_self = |tree: &ast::UseTree| { |
117 | tree.use_tree_list() | 123 | tree.use_tree_list() |
118 | .map(|tree_list| tree_list.use_trees().any(tree_is_self)) | 124 | .map(|tree_list| tree_list.use_trees().any(tree_is_self)) |
@@ -127,9 +133,11 @@ fn recursive_merge( | |||
127 | _ => (), | 133 | _ => (), |
128 | } | 134 | } |
129 | 135 | ||
130 | // glob imports arent part of the use-tree lists so we need to special handle them here as well | 136 | // Glob imports aren't part of the use-tree lists so we need |
131 | // this special handling is only required for when we merge a module import into a glob import of said module | 137 | // to special handle them here as well this special handling |
132 | // see the `merge_self_glob` or `merge_mod_into_glob` tests | 138 | // is only required for when we merge a module import into a |
139 | // glob import of said module see the `merge_self_glob` or | ||
140 | // `merge_mod_into_glob` tests. | ||
133 | if lhs_t.star_token().is_some() || rhs_t.star_token().is_some() { | 141 | if lhs_t.star_token().is_some() || rhs_t.star_token().is_some() { |
134 | *lhs_t = make::use_tree( | 142 | *lhs_t = make::use_tree( |
135 | make::path_unqualified(make::path_segment_self()), | 143 | make::path_unqualified(make::path_segment_self()), |
@@ -137,7 +145,7 @@ fn recursive_merge( | |||
137 | None, | 145 | None, |
138 | false, | 146 | false, |
139 | ); | 147 | ); |
140 | use_trees.insert(idx, make::glob_use_tree()); | 148 | use_trees.insert(idx, make::use_tree_glob()); |
141 | continue; | 149 | continue; |
142 | } | 150 | } |
143 | 151 | ||
@@ -153,7 +161,7 @@ fn recursive_merge( | |||
153 | } | 161 | } |
154 | } | 162 | } |
155 | Err(_) | 163 | Err(_) |
156 | if merge == MergeBehavior::Last | 164 | if merge == MergeBehavior::Module |
157 | && use_trees.len() > 0 | 165 | && use_trees.len() > 0 |
158 | && rhs_t.use_tree_list().is_some() => | 166 | && rhs_t.use_tree_list().is_some() => |
159 | { | 167 | { |
@@ -165,11 +173,11 @@ fn recursive_merge( | |||
165 | } | 173 | } |
166 | } | 174 | } |
167 | 175 | ||
168 | Some(if let Some(old) = lhs.use_tree_list() { | 176 | let lhs = lhs.clone_subtree().clone_for_update(); |
169 | lhs.replace_descendant(old, make::use_tree_list(use_trees)).clone_for_update() | 177 | if let Some(old) = lhs.use_tree_list() { |
170 | } else { | 178 | ted::replace(old.syntax(), make::use_tree_list(use_trees).syntax().clone_for_update()); |
171 | lhs.clone() | 179 | } |
172 | }) | 180 | ast::UseTree::cast(lhs.syntax().clone_subtree()) |
173 | } | 181 | } |
174 | 182 | ||
175 | /// Traverses both paths until they differ, returning the common prefix of both. | 183 | /// Traverses both paths until they differ, returning the common prefix of both. |
diff --git a/crates/ide_db/src/search.rs b/crates/ide_db/src/search.rs index 8f899ea56..67840602b 100644 --- a/crates/ide_db/src/search.rs +++ b/crates/ide_db/src/search.rs | |||
@@ -233,6 +233,13 @@ impl Definition { | |||
233 | }; | 233 | }; |
234 | } | 234 | } |
235 | 235 | ||
236 | if let Definition::SelfType(impl_) = self { | ||
237 | return match impl_.source(db).map(|src| src.value.syntax().text_range()) { | ||
238 | Some(range) => SearchScope::file_range(FileRange { file_id, range }), | ||
239 | None => SearchScope::single_file(file_id), | ||
240 | }; | ||
241 | } | ||
242 | |||
236 | if let Definition::GenericParam(hir::GenericParam::LifetimeParam(param)) = self { | 243 | if let Definition::GenericParam(hir::GenericParam::LifetimeParam(param)) = self { |
237 | let range = match param.parent(db) { | 244 | let range = match param.parent(db) { |
238 | hir::GenericDef::Function(it) => { | 245 | hir::GenericDef::Function(it) => { |
@@ -297,7 +304,7 @@ impl Definition { | |||
297 | } | 304 | } |
298 | 305 | ||
299 | pub fn usages<'a>(&'a self, sema: &'a Semantics<RootDatabase>) -> FindUsages<'a> { | 306 | pub fn usages<'a>(&'a self, sema: &'a Semantics<RootDatabase>) -> FindUsages<'a> { |
300 | FindUsages { def: self, sema, scope: None, include_self_kw_refs: false } | 307 | FindUsages { def: self, sema, scope: None, include_self_kw_refs: None } |
301 | } | 308 | } |
302 | } | 309 | } |
303 | 310 | ||
@@ -305,12 +312,13 @@ pub struct FindUsages<'a> { | |||
305 | def: &'a Definition, | 312 | def: &'a Definition, |
306 | sema: &'a Semantics<'a, RootDatabase>, | 313 | sema: &'a Semantics<'a, RootDatabase>, |
307 | scope: Option<SearchScope>, | 314 | scope: Option<SearchScope>, |
308 | include_self_kw_refs: bool, | 315 | include_self_kw_refs: Option<hir::Type>, |
309 | } | 316 | } |
310 | 317 | ||
311 | impl<'a> FindUsages<'a> { | 318 | impl<'a> FindUsages<'a> { |
312 | pub fn include_self_kw_refs(mut self, include: bool) -> FindUsages<'a> { | 319 | /// Enable searching for `Self` when the definition is a type. |
313 | self.include_self_kw_refs = include; | 320 | pub fn include_self_refs(mut self) -> FindUsages<'a> { |
321 | self.include_self_kw_refs = def_to_ty(self.sema, self.def); | ||
314 | self | 322 | self |
315 | } | 323 | } |
316 | 324 | ||
@@ -354,13 +362,18 @@ impl<'a> FindUsages<'a> { | |||
354 | } | 362 | } |
355 | }; | 363 | }; |
356 | 364 | ||
357 | let name = match self.def.name(sema.db) { | 365 | let name = self.def.name(sema.db).or_else(|| { |
358 | Some(it) => it.to_string(), | 366 | self.include_self_kw_refs.as_ref().and_then(|ty| { |
367 | ty.as_adt() | ||
368 | .map(|adt| adt.name(self.sema.db)) | ||
369 | .or_else(|| ty.as_builtin().map(|builtin| builtin.name())) | ||
370 | }) | ||
371 | }); | ||
372 | let name = match name { | ||
373 | Some(name) => name.to_string(), | ||
359 | None => return, | 374 | None => return, |
360 | }; | 375 | }; |
361 | 376 | let name = name.as_str(); | |
362 | let pat = name.as_str(); | ||
363 | let search_for_self = self.include_self_kw_refs; | ||
364 | 377 | ||
365 | for (file_id, search_range) in search_scope { | 378 | for (file_id, search_range) in search_scope { |
366 | let text = sema.db.file_text(file_id); | 379 | let text = sema.db.file_text(file_id); |
@@ -369,51 +382,63 @@ impl<'a> FindUsages<'a> { | |||
369 | 382 | ||
370 | let tree = Lazy::new(|| sema.parse(file_id).syntax().clone()); | 383 | let tree = Lazy::new(|| sema.parse(file_id).syntax().clone()); |
371 | 384 | ||
372 | let mut handle_match = |idx: usize| -> bool { | 385 | for (idx, _) in text.match_indices(name) { |
373 | let offset: TextSize = idx.try_into().unwrap(); | 386 | let offset: TextSize = idx.try_into().unwrap(); |
374 | if !search_range.contains_inclusive(offset) { | 387 | if !search_range.contains_inclusive(offset) { |
375 | return false; | 388 | continue; |
376 | } | 389 | } |
377 | 390 | ||
378 | if let Some(name) = sema.find_node_at_offset_with_descend(&tree, offset) { | 391 | if let Some(name) = sema.find_node_at_offset_with_descend(&tree, offset) { |
379 | match name { | 392 | if match name { |
380 | ast::NameLike::NameRef(name_ref) => { | 393 | ast::NameLike::NameRef(name_ref) => self.found_name_ref(&name_ref, sink), |
381 | if self.found_name_ref(&name_ref, sink) { | 394 | ast::NameLike::Name(name) => self.found_name(&name, sink), |
382 | return true; | 395 | ast::NameLike::Lifetime(lifetime) => self.found_lifetime(&lifetime, sink), |
383 | } | 396 | } { |
384 | } | 397 | return; |
385 | ast::NameLike::Name(name) => { | ||
386 | if self.found_name(&name, sink) { | ||
387 | return true; | ||
388 | } | ||
389 | } | ||
390 | ast::NameLike::Lifetime(lifetime) => { | ||
391 | if self.found_lifetime(&lifetime, sink) { | ||
392 | return true; | ||
393 | } | ||
394 | } | ||
395 | } | 398 | } |
396 | } | 399 | } |
397 | |||
398 | return false; | ||
399 | }; | ||
400 | |||
401 | for (idx, _) in text.match_indices(pat) { | ||
402 | if handle_match(idx) { | ||
403 | return; | ||
404 | } | ||
405 | } | 400 | } |
406 | 401 | if let Some(self_ty) = &self.include_self_kw_refs { | |
407 | if search_for_self { | ||
408 | for (idx, _) in text.match_indices("Self") { | 402 | for (idx, _) in text.match_indices("Self") { |
409 | if handle_match(idx) { | 403 | let offset: TextSize = idx.try_into().unwrap(); |
410 | return; | 404 | if !search_range.contains_inclusive(offset) { |
405 | continue; | ||
406 | } | ||
407 | |||
408 | if let Some(ast::NameLike::NameRef(name_ref)) = | ||
409 | sema.find_node_at_offset_with_descend(&tree, offset) | ||
410 | { | ||
411 | if self.found_self_ty_name_ref(&self_ty, &name_ref, sink) { | ||
412 | return; | ||
413 | } | ||
411 | } | 414 | } |
412 | } | 415 | } |
413 | } | 416 | } |
414 | } | 417 | } |
415 | } | 418 | } |
416 | 419 | ||
420 | fn found_self_ty_name_ref( | ||
421 | &self, | ||
422 | self_ty: &hir::Type, | ||
423 | name_ref: &ast::NameRef, | ||
424 | sink: &mut dyn FnMut(FileId, FileReference) -> bool, | ||
425 | ) -> bool { | ||
426 | match NameRefClass::classify(self.sema, &name_ref) { | ||
427 | Some(NameRefClass::Definition(Definition::SelfType(impl_))) | ||
428 | if impl_.self_ty(self.sema.db) == *self_ty => | ||
429 | { | ||
430 | let FileRange { file_id, range } = self.sema.original_range(name_ref.syntax()); | ||
431 | let reference = FileReference { | ||
432 | range, | ||
433 | name: ast::NameLike::NameRef(name_ref.clone()), | ||
434 | access: None, | ||
435 | }; | ||
436 | sink(file_id, reference) | ||
437 | } | ||
438 | _ => false, | ||
439 | } | ||
440 | } | ||
441 | |||
417 | fn found_lifetime( | 442 | fn found_lifetime( |
418 | &self, | 443 | &self, |
419 | lifetime: &ast::Lifetime, | 444 | lifetime: &ast::Lifetime, |
@@ -429,7 +454,7 @@ impl<'a> FindUsages<'a> { | |||
429 | }; | 454 | }; |
430 | sink(file_id, reference) | 455 | sink(file_id, reference) |
431 | } | 456 | } |
432 | _ => false, // not a usage | 457 | _ => false, |
433 | } | 458 | } |
434 | } | 459 | } |
435 | 460 | ||
@@ -448,42 +473,35 @@ impl<'a> FindUsages<'a> { | |||
448 | }; | 473 | }; |
449 | sink(file_id, reference) | 474 | sink(file_id, reference) |
450 | } | 475 | } |
451 | Some(NameRefClass::Definition(Definition::SelfType(impl_))) => { | 476 | Some(NameRefClass::Definition(def)) if self.include_self_kw_refs.is_some() => { |
452 | let ty = impl_.self_ty(self.sema.db); | 477 | if self.include_self_kw_refs == def_to_ty(self.sema, &def) { |
453 | 478 | let FileRange { file_id, range } = self.sema.original_range(name_ref.syntax()); | |
454 | if let Some(adt) = ty.as_adt() { | 479 | let reference = FileReference { |
455 | if &Definition::ModuleDef(ModuleDef::Adt(adt)) == self.def { | 480 | range, |
456 | let FileRange { file_id, range } = | 481 | name: ast::NameLike::NameRef(name_ref.clone()), |
457 | self.sema.original_range(name_ref.syntax()); | 482 | access: reference_access(&def, &name_ref), |
458 | let reference = FileReference { | 483 | }; |
459 | range, | 484 | sink(file_id, reference) |
460 | name: ast::NameLike::NameRef(name_ref.clone()), | 485 | } else { |
461 | access: None, | 486 | false |
462 | }; | ||
463 | return sink(file_id, reference); | ||
464 | } | ||
465 | } | 487 | } |
466 | |||
467 | false | ||
468 | } | 488 | } |
469 | Some(NameRefClass::FieldShorthand { local_ref: local, field_ref: field }) => { | 489 | Some(NameRefClass::FieldShorthand { local_ref: local, field_ref: field }) => { |
470 | let FileRange { file_id, range } = self.sema.original_range(name_ref.syntax()); | 490 | let FileRange { file_id, range } = self.sema.original_range(name_ref.syntax()); |
471 | let reference = match self.def { | 491 | let access = match self.def { |
472 | Definition::Field(_) if &field == self.def => FileReference { | 492 | Definition::Field(_) if &field == self.def => { |
473 | range, | 493 | reference_access(&field, &name_ref) |
474 | name: ast::NameLike::NameRef(name_ref.clone()), | 494 | } |
475 | access: reference_access(&field, &name_ref), | 495 | Definition::Local(l) if &local == l => { |
476 | }, | 496 | reference_access(&Definition::Local(local), &name_ref) |
477 | Definition::Local(l) if &local == l => FileReference { | 497 | } |
478 | range, | 498 | _ => return false, |
479 | name: ast::NameLike::NameRef(name_ref.clone()), | ||
480 | access: reference_access(&Definition::Local(local), &name_ref), | ||
481 | }, | ||
482 | _ => return false, // not a usage | ||
483 | }; | 499 | }; |
500 | let reference = | ||
501 | FileReference { range, name: ast::NameLike::NameRef(name_ref.clone()), access }; | ||
484 | sink(file_id, reference) | 502 | sink(file_id, reference) |
485 | } | 503 | } |
486 | _ => false, // not a usage | 504 | _ => false, |
487 | } | 505 | } |
488 | } | 506 | } |
489 | 507 | ||
@@ -513,11 +531,30 @@ impl<'a> FindUsages<'a> { | |||
513 | FileReference { range, name: ast::NameLike::Name(name.clone()), access: None }; | 531 | FileReference { range, name: ast::NameLike::Name(name.clone()), access: None }; |
514 | sink(file_id, reference) | 532 | sink(file_id, reference) |
515 | } | 533 | } |
516 | _ => false, // not a usage | 534 | _ => false, |
517 | } | 535 | } |
518 | } | 536 | } |
519 | } | 537 | } |
520 | 538 | ||
539 | fn def_to_ty(sema: &Semantics<RootDatabase>, def: &Definition) -> Option<hir::Type> { | ||
540 | match def { | ||
541 | Definition::ModuleDef(def) => match def { | ||
542 | ModuleDef::Adt(adt) => Some(adt.ty(sema.db)), | ||
543 | ModuleDef::TypeAlias(it) => Some(it.ty(sema.db)), | ||
544 | ModuleDef::BuiltinType(it) => { | ||
545 | let graph = sema.db.crate_graph(); | ||
546 | let krate = graph.iter().next()?; | ||
547 | let root_file = graph[krate].root_file_id; | ||
548 | let module = sema.to_module_def(root_file)?; | ||
549 | Some(it.ty(sema.db, module)) | ||
550 | } | ||
551 | _ => None, | ||
552 | }, | ||
553 | Definition::SelfType(it) => Some(it.self_ty(sema.db)), | ||
554 | _ => None, | ||
555 | } | ||
556 | } | ||
557 | |||
521 | fn reference_access(def: &Definition, name_ref: &ast::NameRef) -> Option<ReferenceAccess> { | 558 | fn reference_access(def: &Definition, name_ref: &ast::NameRef) -> Option<ReferenceAccess> { |
522 | // Only Locals and Fields have accesses for now. | 559 | // Only Locals and Fields have accesses for now. |
523 | if !matches!(def, Definition::Local(_) | Definition::Field(_)) { | 560 | if !matches!(def, Definition::Local(_) | Definition::Field(_)) { |
diff --git a/crates/ide_db/src/ty_filter.rs b/crates/ide_db/src/ty_filter.rs index 988ecd060..00678bf3e 100644 --- a/crates/ide_db/src/ty_filter.rs +++ b/crates/ide_db/src/ty_filter.rs | |||
@@ -43,7 +43,7 @@ impl TryEnum { | |||
43 | pub fn sad_pattern(self) -> ast::Pat { | 43 | pub fn sad_pattern(self) -> ast::Pat { |
44 | match self { | 44 | match self { |
45 | TryEnum::Result => make::tuple_struct_pat( | 45 | TryEnum::Result => make::tuple_struct_pat( |
46 | make::path_unqualified(make::path_segment(make::name_ref("Err"))), | 46 | make::ext::ident_path("Err"), |
47 | iter::once(make::wildcard_pat().into()), | 47 | iter::once(make::wildcard_pat().into()), |
48 | ) | 48 | ) |
49 | .into(), | 49 | .into(), |
@@ -54,12 +54,12 @@ impl TryEnum { | |||
54 | pub fn happy_pattern(self) -> ast::Pat { | 54 | pub fn happy_pattern(self) -> ast::Pat { |
55 | match self { | 55 | match self { |
56 | TryEnum::Result => make::tuple_struct_pat( | 56 | TryEnum::Result => make::tuple_struct_pat( |
57 | make::path_unqualified(make::path_segment(make::name_ref("Ok"))), | 57 | make::ext::ident_path("Ok"), |
58 | iter::once(make::wildcard_pat().into()), | 58 | iter::once(make::wildcard_pat().into()), |
59 | ) | 59 | ) |
60 | .into(), | 60 | .into(), |
61 | TryEnum::Option => make::tuple_struct_pat( | 61 | TryEnum::Option => make::tuple_struct_pat( |
62 | make::path_unqualified(make::path_segment(make::name_ref("Some"))), | 62 | make::ext::ident_path("Some"), |
63 | iter::once(make::wildcard_pat().into()), | 63 | iter::once(make::wildcard_pat().into()), |
64 | ) | 64 | ) |
65 | .into(), | 65 | .into(), |
diff --git a/crates/parser/src/grammar/patterns.rs b/crates/parser/src/grammar/patterns.rs index 3ab347834..f1d1f9eaa 100644 --- a/crates/parser/src/grammar/patterns.rs +++ b/crates/parser/src/grammar/patterns.rs | |||
@@ -83,7 +83,7 @@ fn pattern_single_r(p: &mut Parser, recovery_set: TokenSet) { | |||
83 | } | 83 | } |
84 | 84 | ||
85 | const PAT_RECOVERY_SET: TokenSet = | 85 | const PAT_RECOVERY_SET: TokenSet = |
86 | TokenSet::new(&[T![let], T![if], T![while], T![loop], T![match], T![')'], T![,]]); | 86 | TokenSet::new(&[T![let], T![if], T![while], T![loop], T![match], T![')'], T![,], T![=]]); |
87 | 87 | ||
88 | fn atom_pat(p: &mut Parser, recovery_set: TokenSet) -> Option<CompletedMarker> { | 88 | fn atom_pat(p: &mut Parser, recovery_set: TokenSet) -> Option<CompletedMarker> { |
89 | let m = match p.nth(0) { | 89 | let m = match p.nth(0) { |
diff --git a/crates/project_model/src/build_data.rs b/crates/project_model/src/build_data.rs index 7b88dca63..3aa546980 100644 --- a/crates/project_model/src/build_data.rs +++ b/crates/project_model/src/build_data.rs | |||
@@ -143,6 +143,7 @@ impl WorkspaceBuildData { | |||
143 | cmd.env("RA_RUSTC_WRAPPER", "1"); | 143 | cmd.env("RA_RUSTC_WRAPPER", "1"); |
144 | } | 144 | } |
145 | 145 | ||
146 | cmd.current_dir(cargo_toml.parent().unwrap()); | ||
146 | cmd.args(&["check", "--quiet", "--workspace", "--message-format=json", "--manifest-path"]) | 147 | cmd.args(&["check", "--quiet", "--workspace", "--message-format=json", "--manifest-path"]) |
147 | .arg(cargo_toml.as_ref()); | 148 | .arg(cargo_toml.as_ref()); |
148 | 149 | ||
diff --git a/crates/project_model/src/cargo_workspace.rs b/crates/project_model/src/cargo_workspace.rs index bc6e20341..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, |
@@ -201,31 +229,12 @@ impl CargoWorkspace { | |||
201 | if let Some(parent) = cargo_toml.parent() { | 229 | if let Some(parent) = cargo_toml.parent() { |
202 | meta.current_dir(parent.to_path_buf()); | 230 | meta.current_dir(parent.to_path_buf()); |
203 | } | 231 | } |
204 | let target = if let Some(target) = config.target.as_ref() { | 232 | let target = if let Some(target) = &config.target { |
205 | Some(target.clone()) | 233 | Some(target.clone()) |
234 | } else if let stdout @ Some(_) = cargo_config_build_target(cargo_toml) { | ||
235 | stdout | ||
206 | } else { | 236 | } else { |
207 | // cargo metadata defaults to giving information for _all_ targets. | 237 | rustc_discover_host_triple(cargo_toml) |
208 | // In the absence of a preference from the user, we use the host platform. | ||
209 | let mut rustc = Command::new(toolchain::rustc()); | ||
210 | rustc.current_dir(cargo_toml.parent().unwrap()).arg("-vV"); | ||
211 | log::debug!("Discovering host platform by {:?}", rustc); | ||
212 | match utf8_stdout(rustc) { | ||
213 | Ok(stdout) => { | ||
214 | let field = "host: "; | ||
215 | let target = stdout.lines().find_map(|l| l.strip_prefix(field)); | ||
216 | if let Some(target) = target { | ||
217 | Some(target.to_string()) | ||
218 | } else { | ||
219 | // If we fail to resolve the host platform, it's not the end of the world. | ||
220 | log::info!("rustc -vV did not report host platform, got:\n{}", stdout); | ||
221 | None | ||
222 | } | ||
223 | } | ||
224 | Err(e) => { | ||
225 | log::warn!("Failed to discover host platform: {}", e); | ||
226 | None | ||
227 | } | ||
228 | } | ||
229 | }; | 238 | }; |
230 | if let Some(target) = target { | 239 | if let Some(target) = target { |
231 | meta.other_options(vec![String::from("--filter-platform"), target]); | 240 | meta.other_options(vec![String::from("--filter-platform"), target]); |
@@ -320,7 +329,11 @@ impl CargoWorkspace { | |||
320 | continue; | 329 | continue; |
321 | } | 330 | } |
322 | }; | 331 | }; |
323 | 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 | }; | ||
324 | packages[source].dependencies.push(dep); | 337 | packages[source].dependencies.push(dep); |
325 | } | 338 | } |
326 | packages[source].active_features.extend(node.features); | 339 | packages[source].active_features.extend(node.features); |
@@ -368,3 +381,43 @@ impl CargoWorkspace { | |||
368 | self.packages.iter().filter(|(_, v)| v.name == name).count() == 1 | 381 | self.packages.iter().filter(|(_, v)| v.name == name).count() == 1 |
369 | } | 382 | } |
370 | } | 383 | } |
384 | |||
385 | fn rustc_discover_host_triple(cargo_toml: &AbsPath) -> Option<String> { | ||
386 | let mut rustc = Command::new(toolchain::rustc()); | ||
387 | rustc.current_dir(cargo_toml.parent().unwrap()).arg("-vV"); | ||
388 | log::debug!("Discovering host platform by {:?}", rustc); | ||
389 | match utf8_stdout(rustc) { | ||
390 | Ok(stdout) => { | ||
391 | let field = "host: "; | ||
392 | let target = stdout.lines().find_map(|l| l.strip_prefix(field)); | ||
393 | if let Some(target) = target { | ||
394 | Some(target.to_string()) | ||
395 | } else { | ||
396 | // If we fail to resolve the host platform, it's not the end of the world. | ||
397 | log::info!("rustc -vV did not report host platform, got:\n{}", stdout); | ||
398 | None | ||
399 | } | ||
400 | } | ||
401 | Err(e) => { | ||
402 | log::warn!("Failed to discover host platform: {}", e); | ||
403 | None | ||
404 | } | ||
405 | } | ||
406 | } | ||
407 | |||
408 | fn cargo_config_build_target(cargo_toml: &AbsPath) -> Option<String> { | ||
409 | let mut cargo_config = Command::new(toolchain::cargo()); | ||
410 | cargo_config | ||
411 | .current_dir(cargo_toml.parent().unwrap()) | ||
412 | .args(&["-Z", "unstable-options", "config", "get", "build.target"]) | ||
413 | .env("RUSTC_BOOTSTRAP", "1"); | ||
414 | // if successful we receive `build.target = "target-triple"` | ||
415 | log::debug!("Discovering cargo config target by {:?}", cargo_config); | ||
416 | match utf8_stdout(cargo_config) { | ||
417 | Ok(stdout) => stdout | ||
418 | .strip_prefix("build.target = \"") | ||
419 | .and_then(|stdout| stdout.strip_suffix('"')) | ||
420 | .map(ToOwned::to_owned), | ||
421 | Err(_) => None, | ||
422 | } | ||
423 | } | ||
diff --git a/crates/project_model/src/rustc_cfg.rs b/crates/project_model/src/rustc_cfg.rs index 312708575..012eab256 100644 --- a/crates/project_model/src/rustc_cfg.rs +++ b/crates/project_model/src/rustc_cfg.rs | |||
@@ -2,9 +2,12 @@ | |||
2 | 2 | ||
3 | use std::process::Command; | 3 | use std::process::Command; |
4 | 4 | ||
5 | use anyhow::Result; | ||
6 | use paths::AbsPath; | ||
7 | |||
5 | use crate::{cfg_flag::CfgFlag, utf8_stdout}; | 8 | use crate::{cfg_flag::CfgFlag, utf8_stdout}; |
6 | 9 | ||
7 | pub(crate) fn get(target: Option<&str>) -> Vec<CfgFlag> { | 10 | pub(crate) fn get(cargo_toml: Option<&AbsPath>, target: Option<&str>) -> Vec<CfgFlag> { |
8 | let _p = profile::span("rustc_cfg::get"); | 11 | let _p = profile::span("rustc_cfg::get"); |
9 | let mut res = Vec::with_capacity(6 * 2 + 1); | 12 | let mut res = Vec::with_capacity(6 * 2 + 1); |
10 | 13 | ||
@@ -16,19 +19,39 @@ pub(crate) fn get(target: Option<&str>) -> Vec<CfgFlag> { | |||
16 | } | 19 | } |
17 | } | 20 | } |
18 | 21 | ||
19 | let rustc_cfgs = { | 22 | match get_rust_cfgs(cargo_toml, target) { |
20 | let mut cmd = Command::new(toolchain::rustc()); | ||
21 | cmd.args(&["--print", "cfg", "-O"]); | ||
22 | if let Some(target) = target { | ||
23 | cmd.args(&["--target", target]); | ||
24 | } | ||
25 | utf8_stdout(cmd) | ||
26 | }; | ||
27 | |||
28 | match rustc_cfgs { | ||
29 | Ok(rustc_cfgs) => res.extend(rustc_cfgs.lines().map(|it| it.parse().unwrap())), | 23 | Ok(rustc_cfgs) => res.extend(rustc_cfgs.lines().map(|it| it.parse().unwrap())), |
30 | Err(e) => log::error!("failed to get rustc cfgs: {:#}", e), | 24 | Err(e) => log::error!("failed to get rustc cfgs: {:#}", e), |
31 | } | 25 | } |
32 | 26 | ||
33 | res | 27 | res |
34 | } | 28 | } |
29 | |||
30 | fn get_rust_cfgs(cargo_toml: Option<&AbsPath>, target: Option<&str>) -> Result<String> { | ||
31 | let cargo_rust_cfgs = match cargo_toml { | ||
32 | Some(cargo_toml) => { | ||
33 | let mut cargo_config = Command::new(toolchain::cargo()); | ||
34 | cargo_config | ||
35 | .current_dir(cargo_toml.parent().unwrap()) | ||
36 | .args(&["-Z", "unstable-options", "rustc", "--print", "cfg"]) | ||
37 | .env("RUSTC_BOOTSTRAP", "1"); | ||
38 | if let Some(target) = target { | ||
39 | cargo_config.args(&["--target", target]); | ||
40 | } | ||
41 | utf8_stdout(cargo_config).ok() | ||
42 | } | ||
43 | None => None, | ||
44 | }; | ||
45 | match cargo_rust_cfgs { | ||
46 | Some(stdout) => Ok(stdout), | ||
47 | None => { | ||
48 | // using unstable cargo features failed, fall back to using plain rustc | ||
49 | let mut cmd = Command::new(toolchain::rustc()); | ||
50 | cmd.args(&["--print", "cfg", "-O"]); | ||
51 | if let Some(target) = target { | ||
52 | cmd.args(&["--target", target]); | ||
53 | } | ||
54 | utf8_stdout(cmd) | ||
55 | } | ||
56 | } | ||
57 | } | ||
diff --git a/crates/project_model/src/workspace.rs b/crates/project_model/src/workspace.rs index 2fcd0f8fa..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; |
@@ -143,7 +144,8 @@ impl ProjectWorkspace { | |||
143 | } else { | 144 | } else { |
144 | None | 145 | None |
145 | }; | 146 | }; |
146 | let rustc_cfg = rustc_cfg::get(config.target.as_deref()); | 147 | |
148 | let rustc_cfg = rustc_cfg::get(Some(&cargo_toml), config.target.as_deref()); | ||
147 | ProjectWorkspace::Cargo { cargo, sysroot, rustc, rustc_cfg } | 149 | ProjectWorkspace::Cargo { cargo, sysroot, rustc, rustc_cfg } |
148 | } | 150 | } |
149 | }; | 151 | }; |
@@ -159,7 +161,7 @@ impl ProjectWorkspace { | |||
159 | Some(path) => Some(Sysroot::load(path)?), | 161 | Some(path) => Some(Sysroot::load(path)?), |
160 | None => None, | 162 | None => None, |
161 | }; | 163 | }; |
162 | let rustc_cfg = rustc_cfg::get(target); | 164 | let rustc_cfg = rustc_cfg::get(None, target); |
163 | Ok(ProjectWorkspace::Json { project: project_json, sysroot, rustc_cfg }) | 165 | Ok(ProjectWorkspace::Json { project: project_json, sysroot, rustc_cfg }) |
164 | } | 166 | } |
165 | 167 | ||
@@ -310,7 +312,7 @@ fn project_json_to_crate_graph( | |||
310 | 312 | ||
311 | let target_cfgs = match krate.target.as_deref() { | 313 | let target_cfgs = match krate.target.as_deref() { |
312 | Some(target) => { | 314 | Some(target) => { |
313 | cfg_cache.entry(target).or_insert_with(|| rustc_cfg::get(Some(target))) | 315 | cfg_cache.entry(target).or_insert_with(|| rustc_cfg::get(None, Some(target))) |
314 | } | 316 | } |
315 | None => &rustc_cfg, | 317 | None => &rustc_cfg, |
316 | }; | 318 | }; |
@@ -389,6 +391,7 @@ fn cargo_to_crate_graph( | |||
389 | &cfg_options, | 391 | &cfg_options, |
390 | proc_macro_loader, | 392 | proc_macro_loader, |
391 | file_id, | 393 | file_id, |
394 | &cargo[tgt].name, | ||
392 | ); | 395 | ); |
393 | if cargo[tgt].kind == TargetKind::Lib { | 396 | if cargo[tgt].kind == TargetKind::Lib { |
394 | lib_tgt = Some((crate_id, cargo[tgt].name.clone())); | 397 | lib_tgt = Some((crate_id, cargo[tgt].name.clone())); |
@@ -405,23 +408,25 @@ fn cargo_to_crate_graph( | |||
405 | } | 408 | } |
406 | } | 409 | } |
407 | 410 | ||
408 | 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)); |
409 | } | 412 | } |
410 | } | 413 | } |
411 | 414 | ||
412 | // 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 |
413 | for &from in pkg_crates.get(&pkg).into_iter().flatten() { | 416 | for (from, kind) in pkg_crates.get(&pkg).into_iter().flatten() { |
414 | if let Some((to, name)) = lib_tgt.clone() { | 417 | if let Some((to, name)) = lib_tgt.clone() { |
415 | if to != from { | 418 | if to != *from && *kind != TargetKind::BuildScript { |
419 | // (build script can not depend on its library target) | ||
420 | |||
416 | // For root projects with dashes in their name, | 421 | // For root projects with dashes in their name, |
417 | // cargo metadata does not do any normalization, | 422 | // cargo metadata does not do any normalization, |
418 | // so we do it ourselves currently | 423 | // so we do it ourselves currently |
419 | let name = CrateName::normalize_dashes(&name); | 424 | let name = CrateName::normalize_dashes(&name); |
420 | add_dep(&mut crate_graph, from, name, to); | 425 | add_dep(&mut crate_graph, *from, name, to); |
421 | } | 426 | } |
422 | } | 427 | } |
423 | for (name, krate) in public_deps.iter() { | 428 | for (name, krate) in public_deps.iter() { |
424 | add_dep(&mut crate_graph, from, name.clone(), *krate); | 429 | add_dep(&mut crate_graph, *from, name.clone(), *krate); |
425 | } | 430 | } |
426 | } | 431 | } |
427 | } | 432 | } |
@@ -432,8 +437,17 @@ fn cargo_to_crate_graph( | |||
432 | for dep in cargo[pkg].dependencies.iter() { | 437 | for dep in cargo[pkg].dependencies.iter() { |
433 | let name = CrateName::new(&dep.name).unwrap(); | 438 | let name = CrateName::new(&dep.name).unwrap(); |
434 | if let Some(&to) = pkg_to_lib_crate.get(&dep.pkg) { | 439 | if let Some(&to) = pkg_to_lib_crate.get(&dep.pkg) { |
435 | for &from in pkg_crates.get(&pkg).into_iter().flatten() { | 440 | for (from, kind) in pkg_crates.get(&pkg).into_iter().flatten() { |
436 | 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) | ||
437 | } | 451 | } |
438 | } | 452 | } |
439 | } | 453 | } |
@@ -470,7 +484,7 @@ fn handle_rustc_crates( | |||
470 | 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>, |
471 | public_deps: &[(CrateName, CrateId)], | 485 | public_deps: &[(CrateName, CrateId)], |
472 | cargo: &CargoWorkspace, | 486 | cargo: &CargoWorkspace, |
473 | pkg_crates: &FxHashMap<la_arena::Idx<crate::PackageData>, Vec<CrateId>>, | 487 | pkg_crates: &FxHashMap<la_arena::Idx<crate::PackageData>, Vec<(CrateId, TargetKind)>>, |
474 | ) { | 488 | ) { |
475 | let mut rustc_pkg_crates = FxHashMap::default(); | 489 | let mut rustc_pkg_crates = FxHashMap::default(); |
476 | // 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 |
@@ -504,6 +518,7 @@ fn handle_rustc_crates( | |||
504 | &cfg_options, | 518 | &cfg_options, |
505 | proc_macro_loader, | 519 | proc_macro_loader, |
506 | file_id, | 520 | file_id, |
521 | &rustc_workspace[tgt].name, | ||
507 | ); | 522 | ); |
508 | pkg_to_lib_crate.insert(pkg, crate_id); | 523 | pkg_to_lib_crate.insert(pkg, crate_id); |
509 | // Add dependencies on core / std / alloc for this crate | 524 | // Add dependencies on core / std / alloc for this crate |
@@ -538,13 +553,13 @@ fn handle_rustc_crates( | |||
538 | if !package.metadata.rustc_private { | 553 | if !package.metadata.rustc_private { |
539 | continue; | 554 | continue; |
540 | } | 555 | } |
541 | for &from in pkg_crates.get(&pkg).into_iter().flatten() { | 556 | for (from, _) in pkg_crates.get(&pkg).into_iter().flatten() { |
542 | // Avoid creating duplicate dependencies | 557 | // Avoid creating duplicate dependencies |
543 | // 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 |
544 | // `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` |
545 | // instead of the one from `crates.io` | 560 | // instead of the one from `crates.io` |
546 | if !crate_graph[from].dependencies.iter().any(|d| d.name == name) { | 561 | if !crate_graph[*from].dependencies.iter().any(|d| d.name == name) { |
547 | add_dep(crate_graph, from, name.clone(), to); | 562 | add_dep(crate_graph, *from, name.clone(), to); |
548 | } | 563 | } |
549 | } | 564 | } |
550 | } | 565 | } |
@@ -559,6 +574,7 @@ fn add_target_crate_root( | |||
559 | cfg_options: &CfgOptions, | 574 | cfg_options: &CfgOptions, |
560 | proc_macro_loader: &dyn Fn(&Path) -> Vec<ProcMacro>, | 575 | proc_macro_loader: &dyn Fn(&Path) -> Vec<ProcMacro>, |
561 | file_id: FileId, | 576 | file_id: FileId, |
577 | cargo_name: &str, | ||
562 | ) -> CrateId { | 578 | ) -> CrateId { |
563 | let edition = pkg.edition; | 579 | let edition = pkg.edition; |
564 | let cfg_options = { | 580 | let cfg_options = { |
@@ -585,7 +601,7 @@ fn add_target_crate_root( | |||
585 | .map(|it| proc_macro_loader(&it)) | 601 | .map(|it| proc_macro_loader(&it)) |
586 | .unwrap_or_default(); | 602 | .unwrap_or_default(); |
587 | 603 | ||
588 | let display_name = CrateDisplayName::from_canonical_name(pkg.name.clone()); | 604 | let display_name = CrateDisplayName::from_canonical_name(cargo_name.to_string()); |
589 | let crate_id = crate_graph.add_crate_root( | 605 | let crate_id = crate_graph.add_crate_root( |
590 | file_id, | 606 | file_id, |
591 | 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 123b63f53..905a6ee55 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. |
@@ -614,8 +614,8 @@ impl Config { | |||
614 | InsertUseConfig { | 614 | InsertUseConfig { |
615 | merge: match self.data.assist_importMergeBehavior { | 615 | merge: match self.data.assist_importMergeBehavior { |
616 | MergeBehaviorDef::None => None, | 616 | MergeBehaviorDef::None => None, |
617 | MergeBehaviorDef::Full => Some(MergeBehavior::Full), | 617 | MergeBehaviorDef::Crate => Some(MergeBehavior::Crate), |
618 | MergeBehaviorDef::Last => Some(MergeBehavior::Last), | 618 | MergeBehaviorDef::Module => Some(MergeBehavior::Module), |
619 | }, | 619 | }, |
620 | prefix_kind: match self.data.assist_importPrefix { | 620 | prefix_kind: match self.data.assist_importPrefix { |
621 | ImportPrefixDef::Plain => PrefixKind::Plain, | 621 | ImportPrefixDef::Plain => PrefixKind::Plain, |
@@ -719,8 +719,10 @@ enum ManifestOrProjectJson { | |||
719 | #[serde(rename_all = "snake_case")] | 719 | #[serde(rename_all = "snake_case")] |
720 | enum MergeBehaviorDef { | 720 | enum MergeBehaviorDef { |
721 | None, | 721 | None, |
722 | Full, | 722 | #[serde(alias = "full")] |
723 | Last, | 723 | Crate, |
724 | #[serde(alias = "last")] | ||
725 | Module, | ||
724 | } | 726 | } |
725 | 727 | ||
726 | #[derive(Deserialize, Debug, Clone)] | 728 | #[derive(Deserialize, Debug, Clone)] |
@@ -877,11 +879,11 @@ fn field_props(field: &str, ty: &str, doc: &[&str], default: &str) -> serde_json | |||
877 | }, | 879 | }, |
878 | "MergeBehaviorDef" => set! { | 880 | "MergeBehaviorDef" => set! { |
879 | "type": "string", | 881 | "type": "string", |
880 | "enum": ["none", "full", "last"], | 882 | "enum": ["none", "crate", "module"], |
881 | "enumDescriptions": [ | 883 | "enumDescriptions": [ |
882 | "No merging", | 884 | "Do not merge imports at all.", |
883 | "Merge all layers of the import trees", | 885 | "Merge imports from the same crate into a single `use` statement.", |
884 | "Only merge the last layer of the import trees" | 886 | "Merge imports from the same module into a single `use` statement." |
885 | ], | 887 | ], |
886 | }, | 888 | }, |
887 | "ImportPrefixDef" => set! { | 889 | "ImportPrefixDef" => set! { |
diff --git a/crates/rust-analyzer/src/dispatch.rs b/crates/rust-analyzer/src/dispatch.rs index 81b85a269..baf2199d9 100644 --- a/crates/rust-analyzer/src/dispatch.rs +++ b/crates/rust-analyzer/src/dispatch.rs | |||
@@ -33,7 +33,12 @@ impl<'a> RequestDispatcher<'a> { | |||
33 | let world = panic::AssertUnwindSafe(&mut *self.global_state); | 33 | let world = panic::AssertUnwindSafe(&mut *self.global_state); |
34 | 34 | ||
35 | let response = panic::catch_unwind(move || { | 35 | let response = panic::catch_unwind(move || { |
36 | let _pctx = stdx::panic_context::enter(format!("request: {} {:#?}", R::METHOD, params)); | 36 | let _pctx = stdx::panic_context::enter(format!( |
37 | "\nversion: {}\nrequest: {} {:#?}", | ||
38 | env!("REV"), | ||
39 | R::METHOD, | ||
40 | params | ||
41 | )); | ||
37 | let result = f(world.0, params); | 42 | let result = f(world.0, params); |
38 | result_to_response::<R>(id, result) | 43 | result_to_response::<R>(id, result) |
39 | }) | 44 | }) |
@@ -61,8 +66,12 @@ impl<'a> RequestDispatcher<'a> { | |||
61 | let world = self.global_state.snapshot(); | 66 | let world = self.global_state.snapshot(); |
62 | 67 | ||
63 | move || { | 68 | move || { |
64 | let _pctx = | 69 | let _pctx = stdx::panic_context::enter(format!( |
65 | stdx::panic_context::enter(format!("request: {} {:#?}", R::METHOD, params)); | 70 | "\nversion: {}\nrequest: {} {:#?}", |
71 | env!("REV"), | ||
72 | R::METHOD, | ||
73 | params | ||
74 | )); | ||
66 | let result = f(world, params); | 75 | let result = f(world, params); |
67 | Task::Response(result_to_response::<R>(id, result)) | 76 | Task::Response(result_to_response::<R>(id, result)) |
68 | } | 77 | } |
@@ -166,7 +175,11 @@ impl<'a> NotificationDispatcher<'a> { | |||
166 | return Ok(self); | 175 | return Ok(self); |
167 | } | 176 | } |
168 | }; | 177 | }; |
169 | let _pctx = stdx::panic_context::enter(format!("notification: {}", N::METHOD)); | 178 | let _pctx = stdx::panic_context::enter(format!( |
179 | "\nversion: {}\nnotification: {}", | ||
180 | env!("REV"), | ||
181 | N::METHOD | ||
182 | )); | ||
170 | f(self.global_state, params)?; | 183 | f(self.global_state, params)?; |
171 | Ok(self) | 184 | Ok(self) |
172 | } | 185 | } |
diff --git a/crates/rust-analyzer/src/handlers.rs b/crates/rust-analyzer/src/handlers.rs index 78b558a21..85e45337c 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..ba2790acb 100644 --- a/crates/rust-analyzer/src/integrated_benchmarks.rs +++ b/crates/rust-analyzer/src/integrated_benchmarks.rs | |||
@@ -123,7 +123,7 @@ fn integrated_completion_benchmark() { | |||
123 | }; | 123 | }; |
124 | 124 | ||
125 | { | 125 | { |
126 | let _it = stdx::timeit("unqualified path completion"); | 126 | let _p = profile::span("unqualified path completion"); |
127 | let _span = profile::cpu_span(); | 127 | let _span = profile::cpu_span(); |
128 | let analysis = host.analysis(); | 128 | let analysis = host.analysis(); |
129 | let config = CompletionConfig { | 129 | let config = CompletionConfig { |
@@ -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 | }, |
@@ -156,7 +156,7 @@ fn integrated_completion_benchmark() { | |||
156 | }; | 156 | }; |
157 | 157 | ||
158 | { | 158 | { |
159 | let _it = stdx::timeit("dot completion"); | 159 | let _p = profile::span("dot completion"); |
160 | let _span = profile::cpu_span(); | 160 | let _span = profile::cpu_span(); |
161 | let analysis = host.analysis(); | 161 | let analysis = host.analysis(); |
162 | let config = CompletionConfig { | 162 | let config = CompletionConfig { |
@@ -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) |
diff --git a/crates/rust-analyzer/src/semantic_tokens.rs b/crates/rust-analyzer/src/semantic_tokens.rs index ecab89b2a..c9d38693e 100644 --- a/crates/rust-analyzer/src/semantic_tokens.rs +++ b/crates/rust-analyzer/src/semantic_tokens.rs | |||
@@ -91,6 +91,7 @@ define_semantic_token_modifiers![ | |||
91 | (INJECTED, "injected"), | 91 | (INJECTED, "injected"), |
92 | (MUTABLE, "mutable"), | 92 | (MUTABLE, "mutable"), |
93 | (CONSUMING, "consuming"), | 93 | (CONSUMING, "consuming"), |
94 | (ASYNC, "async"), | ||
94 | (UNSAFE, "unsafe"), | 95 | (UNSAFE, "unsafe"), |
95 | (ATTRIBUTE_MODIFIER, "attribute"), | 96 | (ATTRIBUTE_MODIFIER, "attribute"), |
96 | (TRAIT_MODIFIER, "trait"), | 97 | (TRAIT_MODIFIER, "trait"), |
diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs index 5f2dd418f..6dc9f82ab 100644 --- a/crates/rust-analyzer/src/to_proto.rs +++ b/crates/rust-analyzer/src/to_proto.rs | |||
@@ -426,7 +426,7 @@ fn semantic_token_type_and_modifiers( | |||
426 | let type_ = match highlight.tag { | 426 | let type_ = match highlight.tag { |
427 | HlTag::Symbol(symbol) => match symbol { | 427 | HlTag::Symbol(symbol) => match symbol { |
428 | SymbolKind::Module => lsp_types::SemanticTokenType::NAMESPACE, | 428 | SymbolKind::Module => lsp_types::SemanticTokenType::NAMESPACE, |
429 | SymbolKind::Impl => lsp_types::SemanticTokenType::TYPE, | 429 | SymbolKind::Impl => semantic_tokens::TYPE_ALIAS, |
430 | SymbolKind::Field => lsp_types::SemanticTokenType::PROPERTY, | 430 | SymbolKind::Field => lsp_types::SemanticTokenType::PROPERTY, |
431 | SymbolKind::TypeParam => lsp_types::SemanticTokenType::TYPE_PARAMETER, | 431 | SymbolKind::TypeParam => lsp_types::SemanticTokenType::TYPE_PARAMETER, |
432 | SymbolKind::ConstParam => semantic_tokens::CONST_PARAMETER, | 432 | SymbolKind::ConstParam => semantic_tokens::CONST_PARAMETER, |
@@ -500,6 +500,7 @@ fn semantic_token_type_and_modifiers( | |||
500 | HlMod::ControlFlow => semantic_tokens::CONTROL_FLOW, | 500 | HlMod::ControlFlow => semantic_tokens::CONTROL_FLOW, |
501 | HlMod::Mutable => semantic_tokens::MUTABLE, | 501 | HlMod::Mutable => semantic_tokens::MUTABLE, |
502 | HlMod::Consuming => semantic_tokens::CONSUMING, | 502 | HlMod::Consuming => semantic_tokens::CONSUMING, |
503 | HlMod::Async => semantic_tokens::ASYNC, | ||
503 | HlMod::Unsafe => semantic_tokens::UNSAFE, | 504 | HlMod::Unsafe => semantic_tokens::UNSAFE, |
504 | HlMod::Callable => semantic_tokens::CALLABLE, | 505 | HlMod::Callable => semantic_tokens::CALLABLE, |
505 | HlMod::Static => lsp_types::SemanticTokenModifier::STATIC, | 506 | HlMod::Static => lsp_types::SemanticTokenModifier::STATIC, |
diff --git a/crates/syntax/Cargo.toml b/crates/syntax/Cargo.toml index c0bc59918..747f0b9eb 100644 --- a/crates/syntax/Cargo.toml +++ b/crates/syntax/Cargo.toml | |||
@@ -13,7 +13,7 @@ doctest = false | |||
13 | [dependencies] | 13 | [dependencies] |
14 | cov-mark = { version = "1.1", features = ["thread-local"] } | 14 | cov-mark = { version = "1.1", features = ["thread-local"] } |
15 | itertools = "0.10.0" | 15 | itertools = "0.10.0" |
16 | rowan = "=0.13.0-pre.5" | 16 | rowan = "=0.13.0-pre.6" |
17 | rustc_lexer = { version = "716.0.0", package = "rustc-ap-rustc_lexer" } | 17 | rustc_lexer = { version = "716.0.0", package = "rustc-ap-rustc_lexer" } |
18 | rustc-hash = "1.1.0" | 18 | rustc-hash = "1.1.0" |
19 | arrayvec = "0.7" | 19 | arrayvec = "0.7" |
diff --git a/crates/syntax/src/algo.rs b/crates/syntax/src/algo.rs index ba263be0d..241713c48 100644 --- a/crates/syntax/src/algo.rs +++ b/crates/syntax/src/algo.rs | |||
@@ -1,10 +1,6 @@ | |||
1 | //! FIXME: write short doc here | 1 | //! FIXME: write short doc here |
2 | 2 | ||
3 | use std::{ | 3 | use std::{hash::BuildHasherDefault, ops::RangeInclusive}; |
4 | fmt, | ||
5 | hash::BuildHasherDefault, | ||
6 | ops::{self, RangeInclusive}, | ||
7 | }; | ||
8 | 4 | ||
9 | use indexmap::IndexMap; | 5 | use indexmap::IndexMap; |
10 | use itertools::Itertools; | 6 | use itertools::Itertools; |
@@ -334,253 +330,6 @@ fn _replace_children( | |||
334 | with_children(parent, new_children) | 330 | with_children(parent, new_children) |
335 | } | 331 | } |
336 | 332 | ||
337 | #[derive(Debug, PartialEq, Eq, Hash)] | ||
338 | enum InsertPos { | ||
339 | FirstChildOf(SyntaxNode), | ||
340 | After(SyntaxElement), | ||
341 | } | ||
342 | |||
343 | #[derive(Default)] | ||
344 | pub struct SyntaxRewriter<'a> { | ||
345 | //FIXME: add debug_assertions that all elements are in fact from the same file. | ||
346 | replacements: FxHashMap<SyntaxElement, Replacement>, | ||
347 | insertions: IndexMap<InsertPos, Vec<SyntaxElement>>, | ||
348 | _pd: std::marker::PhantomData<&'a ()>, | ||
349 | } | ||
350 | |||
351 | impl fmt::Debug for SyntaxRewriter<'_> { | ||
352 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
353 | f.debug_struct("SyntaxRewriter") | ||
354 | .field("replacements", &self.replacements) | ||
355 | .field("insertions", &self.insertions) | ||
356 | .finish() | ||
357 | } | ||
358 | } | ||
359 | |||
360 | impl SyntaxRewriter<'_> { | ||
361 | pub fn delete<T: Clone + Into<SyntaxElement>>(&mut self, what: &T) { | ||
362 | let what = what.clone().into(); | ||
363 | let replacement = Replacement::Delete; | ||
364 | self.replacements.insert(what, replacement); | ||
365 | } | ||
366 | pub fn insert_before<T: Clone + Into<SyntaxElement>, U: Clone + Into<SyntaxElement>>( | ||
367 | &mut self, | ||
368 | before: &T, | ||
369 | what: &U, | ||
370 | ) { | ||
371 | let before = before.clone().into(); | ||
372 | let pos = match before.prev_sibling_or_token() { | ||
373 | Some(sibling) => InsertPos::After(sibling), | ||
374 | None => match before.parent() { | ||
375 | Some(parent) => InsertPos::FirstChildOf(parent), | ||
376 | None => return, | ||
377 | }, | ||
378 | }; | ||
379 | self.insertions.entry(pos).or_insert_with(Vec::new).push(what.clone().into()); | ||
380 | } | ||
381 | pub fn insert_after<T: Clone + Into<SyntaxElement>, U: Clone + Into<SyntaxElement>>( | ||
382 | &mut self, | ||
383 | after: &T, | ||
384 | what: &U, | ||
385 | ) { | ||
386 | self.insertions | ||
387 | .entry(InsertPos::After(after.clone().into())) | ||
388 | .or_insert_with(Vec::new) | ||
389 | .push(what.clone().into()); | ||
390 | } | ||
391 | pub fn insert_as_first_child<T: Clone + Into<SyntaxNode>, U: Clone + Into<SyntaxElement>>( | ||
392 | &mut self, | ||
393 | parent: &T, | ||
394 | what: &U, | ||
395 | ) { | ||
396 | self.insertions | ||
397 | .entry(InsertPos::FirstChildOf(parent.clone().into())) | ||
398 | .or_insert_with(Vec::new) | ||
399 | .push(what.clone().into()); | ||
400 | } | ||
401 | pub fn insert_many_before< | ||
402 | T: Clone + Into<SyntaxElement>, | ||
403 | U: IntoIterator<Item = SyntaxElement>, | ||
404 | >( | ||
405 | &mut self, | ||
406 | before: &T, | ||
407 | what: U, | ||
408 | ) { | ||
409 | let before = before.clone().into(); | ||
410 | let pos = match before.prev_sibling_or_token() { | ||
411 | Some(sibling) => InsertPos::After(sibling), | ||
412 | None => match before.parent() { | ||
413 | Some(parent) => InsertPos::FirstChildOf(parent), | ||
414 | None => return, | ||
415 | }, | ||
416 | }; | ||
417 | self.insertions.entry(pos).or_insert_with(Vec::new).extend(what); | ||
418 | } | ||
419 | pub fn insert_many_after< | ||
420 | T: Clone + Into<SyntaxElement>, | ||
421 | U: IntoIterator<Item = SyntaxElement>, | ||
422 | >( | ||
423 | &mut self, | ||
424 | after: &T, | ||
425 | what: U, | ||
426 | ) { | ||
427 | self.insertions | ||
428 | .entry(InsertPos::After(after.clone().into())) | ||
429 | .or_insert_with(Vec::new) | ||
430 | .extend(what); | ||
431 | } | ||
432 | pub fn insert_many_as_first_children< | ||
433 | T: Clone + Into<SyntaxNode>, | ||
434 | U: IntoIterator<Item = SyntaxElement>, | ||
435 | >( | ||
436 | &mut self, | ||
437 | parent: &T, | ||
438 | what: U, | ||
439 | ) { | ||
440 | self.insertions | ||
441 | .entry(InsertPos::FirstChildOf(parent.clone().into())) | ||
442 | .or_insert_with(Vec::new) | ||
443 | .extend(what) | ||
444 | } | ||
445 | pub fn replace<T: Clone + Into<SyntaxElement>>(&mut self, what: &T, with: &T) { | ||
446 | let what = what.clone().into(); | ||
447 | let replacement = Replacement::Single(with.clone().into()); | ||
448 | self.replacements.insert(what, replacement); | ||
449 | } | ||
450 | pub fn replace_with_many<T: Clone + Into<SyntaxElement>>( | ||
451 | &mut self, | ||
452 | what: &T, | ||
453 | with: Vec<SyntaxElement>, | ||
454 | ) { | ||
455 | let what = what.clone().into(); | ||
456 | let replacement = Replacement::Many(with); | ||
457 | self.replacements.insert(what, replacement); | ||
458 | } | ||
459 | pub fn replace_ast<T: AstNode>(&mut self, what: &T, with: &T) { | ||
460 | self.replace(what.syntax(), with.syntax()) | ||
461 | } | ||
462 | |||
463 | pub fn rewrite(&self, node: &SyntaxNode) -> SyntaxNode { | ||
464 | let _p = profile::span("rewrite"); | ||
465 | |||
466 | if self.replacements.is_empty() && self.insertions.is_empty() { | ||
467 | return node.clone(); | ||
468 | } | ||
469 | let green = self.rewrite_children(node); | ||
470 | with_green(node, green) | ||
471 | } | ||
472 | |||
473 | pub fn rewrite_ast<N: AstNode>(self, node: &N) -> N { | ||
474 | N::cast(self.rewrite(node.syntax())).unwrap() | ||
475 | } | ||
476 | |||
477 | /// Returns a node that encompasses all replacements to be done by this rewriter. | ||
478 | /// | ||
479 | /// Passing the returned node to `rewrite` will apply all replacements queued up in `self`. | ||
480 | /// | ||
481 | /// Returns `None` when there are no replacements. | ||
482 | pub fn rewrite_root(&self) -> Option<SyntaxNode> { | ||
483 | let _p = profile::span("rewrite_root"); | ||
484 | fn element_to_node_or_parent(element: &SyntaxElement) -> Option<SyntaxNode> { | ||
485 | match element { | ||
486 | SyntaxElement::Node(it) => Some(it.clone()), | ||
487 | SyntaxElement::Token(it) => it.parent(), | ||
488 | } | ||
489 | } | ||
490 | |||
491 | self.replacements | ||
492 | .keys() | ||
493 | .filter_map(element_to_node_or_parent) | ||
494 | .chain(self.insertions.keys().filter_map(|pos| match pos { | ||
495 | InsertPos::FirstChildOf(it) => Some(it.clone()), | ||
496 | InsertPos::After(it) => element_to_node_or_parent(it), | ||
497 | })) | ||
498 | // If we only have one replacement/insertion, we must return its parent node, since `rewrite` does | ||
499 | // not replace the node passed to it. | ||
500 | .map(|it| it.parent().unwrap_or(it)) | ||
501 | .fold1(|a, b| least_common_ancestor(&a, &b).unwrap()) | ||
502 | } | ||
503 | |||
504 | fn replacement(&self, element: &SyntaxElement) -> Option<Replacement> { | ||
505 | self.replacements.get(element).cloned() | ||
506 | } | ||
507 | |||
508 | fn insertions(&self, pos: &InsertPos) -> Option<impl Iterator<Item = SyntaxElement> + '_> { | ||
509 | self.insertions.get(pos).map(|insertions| insertions.iter().cloned()) | ||
510 | } | ||
511 | |||
512 | fn rewrite_children(&self, node: &SyntaxNode) -> rowan::GreenNode { | ||
513 | let _p = profile::span("rewrite_children"); | ||
514 | |||
515 | // FIXME: this could be made much faster. | ||
516 | let mut new_children = Vec::new(); | ||
517 | if let Some(elements) = self.insertions(&InsertPos::FirstChildOf(node.clone())) { | ||
518 | new_children.extend(elements.map(element_to_green)); | ||
519 | } | ||
520 | for child in node.children_with_tokens() { | ||
521 | self.rewrite_self(&mut new_children, &child); | ||
522 | } | ||
523 | |||
524 | rowan::GreenNode::new(rowan::SyntaxKind(node.kind() as u16), new_children) | ||
525 | } | ||
526 | |||
527 | fn rewrite_self( | ||
528 | &self, | ||
529 | acc: &mut Vec<NodeOrToken<rowan::GreenNode, rowan::GreenToken>>, | ||
530 | element: &SyntaxElement, | ||
531 | ) { | ||
532 | let _p = profile::span("rewrite_self"); | ||
533 | |||
534 | if let Some(replacement) = self.replacement(&element) { | ||
535 | match replacement { | ||
536 | Replacement::Single(element) => acc.push(element_to_green(element)), | ||
537 | Replacement::Many(replacements) => { | ||
538 | acc.extend(replacements.into_iter().map(element_to_green)) | ||
539 | } | ||
540 | Replacement::Delete => (), | ||
541 | }; | ||
542 | } else { | ||
543 | match element { | ||
544 | NodeOrToken::Token(it) => acc.push(NodeOrToken::Token(it.green().to_owned())), | ||
545 | NodeOrToken::Node(it) => { | ||
546 | acc.push(NodeOrToken::Node(self.rewrite_children(it))); | ||
547 | } | ||
548 | } | ||
549 | } | ||
550 | if let Some(elements) = self.insertions(&InsertPos::After(element.clone())) { | ||
551 | acc.extend(elements.map(element_to_green)); | ||
552 | } | ||
553 | } | ||
554 | } | ||
555 | |||
556 | fn element_to_green(element: SyntaxElement) -> NodeOrToken<rowan::GreenNode, rowan::GreenToken> { | ||
557 | match element { | ||
558 | NodeOrToken::Node(it) => NodeOrToken::Node(it.green().into_owned()), | ||
559 | NodeOrToken::Token(it) => NodeOrToken::Token(it.green().to_owned()), | ||
560 | } | ||
561 | } | ||
562 | |||
563 | impl ops::AddAssign for SyntaxRewriter<'_> { | ||
564 | fn add_assign(&mut self, rhs: SyntaxRewriter) { | ||
565 | self.replacements.extend(rhs.replacements); | ||
566 | for (pos, insertions) in rhs.insertions.into_iter() { | ||
567 | match self.insertions.entry(pos) { | ||
568 | indexmap::map::Entry::Occupied(mut occupied) => { | ||
569 | occupied.get_mut().extend(insertions) | ||
570 | } | ||
571 | indexmap::map::Entry::Vacant(vacant) => drop(vacant.insert(insertions)), | ||
572 | } | ||
573 | } | ||
574 | } | ||
575 | } | ||
576 | |||
577 | #[derive(Clone, Debug)] | ||
578 | enum Replacement { | ||
579 | Delete, | ||
580 | Single(SyntaxElement), | ||
581 | Many(Vec<SyntaxElement>), | ||
582 | } | ||
583 | |||
584 | fn with_children( | 333 | fn with_children( |
585 | parent: &SyntaxNode, | 334 | parent: &SyntaxNode, |
586 | new_children: Vec<NodeOrToken<rowan::GreenNode, rowan::GreenToken>>, | 335 | new_children: Vec<NodeOrToken<rowan::GreenNode, rowan::GreenToken>>, |
diff --git a/crates/syntax/src/ast.rs b/crates/syntax/src/ast.rs index 7f472d4db..a8071b51d 100644 --- a/crates/syntax/src/ast.rs +++ b/crates/syntax/src/ast.rs | |||
@@ -47,6 +47,12 @@ pub trait AstNode { | |||
47 | { | 47 | { |
48 | Self::cast(self.syntax().clone_for_update()).unwrap() | 48 | Self::cast(self.syntax().clone_for_update()).unwrap() |
49 | } | 49 | } |
50 | fn clone_subtree(&self) -> Self | ||
51 | where | ||
52 | Self: Sized, | ||
53 | { | ||
54 | Self::cast(self.syntax().clone_subtree()).unwrap() | ||
55 | } | ||
50 | } | 56 | } |
51 | 57 | ||
52 | /// Like `AstNode`, but wraps tokens rather than interior nodes. | 58 | /// Like `AstNode`, but wraps tokens rather than interior nodes. |
diff --git a/crates/syntax/src/ast/edit.rs b/crates/syntax/src/ast/edit.rs index 8c60927e4..61952377f 100644 --- a/crates/syntax/src/ast/edit.rs +++ b/crates/syntax/src/ast/edit.rs | |||
@@ -2,20 +2,16 @@ | |||
2 | //! immutable, all function here return a fresh copy of the tree, instead of | 2 | //! immutable, all function here return a fresh copy of the tree, instead of |
3 | //! doing an in-place modification. | 3 | //! doing an in-place modification. |
4 | use std::{ | 4 | use std::{ |
5 | array, fmt, iter, | 5 | fmt, iter, |
6 | ops::{self, RangeInclusive}, | 6 | ops::{self, RangeInclusive}, |
7 | }; | 7 | }; |
8 | 8 | ||
9 | use arrayvec::ArrayVec; | 9 | use arrayvec::ArrayVec; |
10 | 10 | ||
11 | use crate::{ | 11 | use crate::{ |
12 | algo::{self, SyntaxRewriter}, | 12 | algo, |
13 | ast::{ | 13 | ast::{self, make, AstNode}, |
14 | self, | 14 | ted, AstToken, InsertPosition, NodeOrToken, SyntaxElement, SyntaxKind, |
15 | make::{self, tokens}, | ||
16 | AstNode, GenericParamsOwner, NameOwner, TypeBoundsOwner, | ||
17 | }, | ||
18 | AstToken, Direction, InsertPosition, SmolStr, SyntaxElement, SyntaxKind, | ||
19 | SyntaxKind::{ATTR, COMMENT, WHITESPACE}, | 15 | SyntaxKind::{ATTR, COMMENT, WHITESPACE}, |
20 | SyntaxNode, SyntaxToken, T, | 16 | SyntaxNode, SyntaxToken, T, |
21 | }; | 17 | }; |
@@ -29,250 +25,6 @@ impl ast::BinExpr { | |||
29 | } | 25 | } |
30 | } | 26 | } |
31 | 27 | ||
32 | impl ast::Fn { | ||
33 | #[must_use] | ||
34 | pub fn with_body(&self, body: ast::BlockExpr) -> ast::Fn { | ||
35 | let mut to_insert: ArrayVec<SyntaxElement, 2> = ArrayVec::new(); | ||
36 | let old_body_or_semi: SyntaxElement = if let Some(old_body) = self.body() { | ||
37 | old_body.syntax().clone().into() | ||
38 | } else if let Some(semi) = self.semicolon_token() { | ||
39 | to_insert.push(make::tokens::single_space().into()); | ||
40 | semi.into() | ||
41 | } else { | ||
42 | to_insert.push(make::tokens::single_space().into()); | ||
43 | to_insert.push(body.syntax().clone().into()); | ||
44 | return self.insert_children(InsertPosition::Last, to_insert); | ||
45 | }; | ||
46 | to_insert.push(body.syntax().clone().into()); | ||
47 | self.replace_children(single_node(old_body_or_semi), to_insert) | ||
48 | } | ||
49 | |||
50 | #[must_use] | ||
51 | pub fn with_generic_param_list(&self, generic_args: ast::GenericParamList) -> ast::Fn { | ||
52 | if let Some(old) = self.generic_param_list() { | ||
53 | return self.replace_descendant(old, generic_args); | ||
54 | } | ||
55 | |||
56 | let anchor = self.name().expect("The function must have a name").syntax().clone(); | ||
57 | |||
58 | let to_insert = [generic_args.syntax().clone().into()]; | ||
59 | self.insert_children(InsertPosition::After(anchor.into()), array::IntoIter::new(to_insert)) | ||
60 | } | ||
61 | } | ||
62 | |||
63 | fn make_multiline<N>(node: N) -> N | ||
64 | where | ||
65 | N: AstNode + Clone, | ||
66 | { | ||
67 | let l_curly = match node.syntax().children_with_tokens().find(|it| it.kind() == T!['{']) { | ||
68 | Some(it) => it, | ||
69 | None => return node, | ||
70 | }; | ||
71 | let sibling = match l_curly.next_sibling_or_token() { | ||
72 | Some(it) => it, | ||
73 | None => return node, | ||
74 | }; | ||
75 | let existing_ws = match sibling.as_token() { | ||
76 | None => None, | ||
77 | Some(tok) if tok.kind() != WHITESPACE => None, | ||
78 | Some(ws) => { | ||
79 | if ws.text().contains('\n') { | ||
80 | return node; | ||
81 | } | ||
82 | Some(ws.clone()) | ||
83 | } | ||
84 | }; | ||
85 | |||
86 | let indent = leading_indent(node.syntax()).unwrap_or_default(); | ||
87 | let ws = tokens::WsBuilder::new(&format!("\n{}", indent)); | ||
88 | let to_insert = iter::once(ws.ws().into()); | ||
89 | match existing_ws { | ||
90 | None => node.insert_children(InsertPosition::After(l_curly), to_insert), | ||
91 | Some(ws) => node.replace_children(single_node(ws), to_insert), | ||
92 | } | ||
93 | } | ||
94 | |||
95 | impl ast::Impl { | ||
96 | #[must_use] | ||
97 | pub fn with_assoc_item_list(&self, items: ast::AssocItemList) -> ast::Impl { | ||
98 | let mut to_insert: ArrayVec<SyntaxElement, 2> = ArrayVec::new(); | ||
99 | if let Some(old_items) = self.assoc_item_list() { | ||
100 | let to_replace: SyntaxElement = old_items.syntax().clone().into(); | ||
101 | to_insert.push(items.syntax().clone().into()); | ||
102 | self.replace_children(single_node(to_replace), to_insert) | ||
103 | } else { | ||
104 | to_insert.push(make::tokens::single_space().into()); | ||
105 | to_insert.push(items.syntax().clone().into()); | ||
106 | self.insert_children(InsertPosition::Last, to_insert) | ||
107 | } | ||
108 | } | ||
109 | } | ||
110 | |||
111 | impl ast::AssocItemList { | ||
112 | #[must_use] | ||
113 | pub fn append_items( | ||
114 | &self, | ||
115 | items: impl IntoIterator<Item = ast::AssocItem>, | ||
116 | ) -> ast::AssocItemList { | ||
117 | let mut res = self.clone(); | ||
118 | if !self.syntax().text().contains_char('\n') { | ||
119 | res = make_multiline(res); | ||
120 | } | ||
121 | items.into_iter().for_each(|it| res = res.append_item(it)); | ||
122 | res.fixup_trailing_whitespace().unwrap_or(res) | ||
123 | } | ||
124 | |||
125 | #[must_use] | ||
126 | pub fn append_item(&self, item: ast::AssocItem) -> ast::AssocItemList { | ||
127 | let (indent, position, whitespace) = match self.assoc_items().last() { | ||
128 | Some(it) => ( | ||
129 | leading_indent(it.syntax()).unwrap_or_default().to_string(), | ||
130 | InsertPosition::After(it.syntax().clone().into()), | ||
131 | "\n\n", | ||
132 | ), | ||
133 | None => match self.l_curly_token() { | ||
134 | Some(it) => ( | ||
135 | " ".to_string() + &leading_indent(self.syntax()).unwrap_or_default(), | ||
136 | InsertPosition::After(it.into()), | ||
137 | "\n", | ||
138 | ), | ||
139 | None => return self.clone(), | ||
140 | }, | ||
141 | }; | ||
142 | let ws = tokens::WsBuilder::new(&format!("{}{}", whitespace, indent)); | ||
143 | let to_insert: ArrayVec<SyntaxElement, 2> = | ||
144 | [ws.ws().into(), item.syntax().clone().into()].into(); | ||
145 | self.insert_children(position, to_insert) | ||
146 | } | ||
147 | |||
148 | /// Remove extra whitespace between last item and closing curly brace. | ||
149 | fn fixup_trailing_whitespace(&self) -> Option<ast::AssocItemList> { | ||
150 | let first_token_after_items = | ||
151 | self.assoc_items().last()?.syntax().next_sibling_or_token()?; | ||
152 | let last_token_before_curly = self.r_curly_token()?.prev_sibling_or_token()?; | ||
153 | if last_token_before_curly != first_token_after_items { | ||
154 | // there is something more between last item and | ||
155 | // right curly than just whitespace - bail out | ||
156 | return None; | ||
157 | } | ||
158 | let whitespace = | ||
159 | last_token_before_curly.clone().into_token().and_then(ast::Whitespace::cast)?; | ||
160 | let text = whitespace.syntax().text(); | ||
161 | let newline = text.rfind('\n')?; | ||
162 | let keep = tokens::WsBuilder::new(&text[newline..]); | ||
163 | Some(self.replace_children( | ||
164 | first_token_after_items..=last_token_before_curly, | ||
165 | std::iter::once(keep.ws().into()), | ||
166 | )) | ||
167 | } | ||
168 | } | ||
169 | |||
170 | impl ast::RecordExprFieldList { | ||
171 | #[must_use] | ||
172 | pub fn append_field(&self, field: &ast::RecordExprField) -> ast::RecordExprFieldList { | ||
173 | self.insert_field(InsertPosition::Last, field) | ||
174 | } | ||
175 | |||
176 | #[must_use] | ||
177 | pub fn insert_field( | ||
178 | &self, | ||
179 | position: InsertPosition<&'_ ast::RecordExprField>, | ||
180 | field: &ast::RecordExprField, | ||
181 | ) -> ast::RecordExprFieldList { | ||
182 | let is_multiline = self.syntax().text().contains_char('\n'); | ||
183 | let ws; | ||
184 | let space = if is_multiline { | ||
185 | ws = tokens::WsBuilder::new(&format!( | ||
186 | "\n{} ", | ||
187 | leading_indent(self.syntax()).unwrap_or_default() | ||
188 | )); | ||
189 | ws.ws() | ||
190 | } else { | ||
191 | tokens::single_space() | ||
192 | }; | ||
193 | |||
194 | let mut to_insert: ArrayVec<SyntaxElement, 4> = ArrayVec::new(); | ||
195 | to_insert.push(space.into()); | ||
196 | to_insert.push(field.syntax().clone().into()); | ||
197 | to_insert.push(make::token(T![,]).into()); | ||
198 | |||
199 | macro_rules! after_l_curly { | ||
200 | () => {{ | ||
201 | let anchor = match self.l_curly_token() { | ||
202 | Some(it) => it.into(), | ||
203 | None => return self.clone(), | ||
204 | }; | ||
205 | InsertPosition::After(anchor) | ||
206 | }}; | ||
207 | } | ||
208 | |||
209 | macro_rules! after_field { | ||
210 | ($anchor:expr) => { | ||
211 | if let Some(comma) = $anchor | ||
212 | .syntax() | ||
213 | .siblings_with_tokens(Direction::Next) | ||
214 | .find(|it| it.kind() == T![,]) | ||
215 | { | ||
216 | InsertPosition::After(comma) | ||
217 | } else { | ||
218 | to_insert.insert(0, make::token(T![,]).into()); | ||
219 | InsertPosition::After($anchor.syntax().clone().into()) | ||
220 | } | ||
221 | }; | ||
222 | } | ||
223 | |||
224 | let position = match position { | ||
225 | InsertPosition::First => after_l_curly!(), | ||
226 | InsertPosition::Last => { | ||
227 | if !is_multiline { | ||
228 | // don't insert comma before curly | ||
229 | to_insert.pop(); | ||
230 | } | ||
231 | match self.fields().last() { | ||
232 | Some(it) => after_field!(it), | ||
233 | None => after_l_curly!(), | ||
234 | } | ||
235 | } | ||
236 | InsertPosition::Before(anchor) => { | ||
237 | InsertPosition::Before(anchor.syntax().clone().into()) | ||
238 | } | ||
239 | InsertPosition::After(anchor) => after_field!(anchor), | ||
240 | }; | ||
241 | |||
242 | self.insert_children(position, to_insert) | ||
243 | } | ||
244 | } | ||
245 | |||
246 | impl ast::TypeAlias { | ||
247 | #[must_use] | ||
248 | pub fn remove_bounds(&self) -> ast::TypeAlias { | ||
249 | let colon = match self.colon_token() { | ||
250 | Some(it) => it, | ||
251 | None => return self.clone(), | ||
252 | }; | ||
253 | let end = match self.type_bound_list() { | ||
254 | Some(it) => it.syntax().clone().into(), | ||
255 | None => colon.clone().into(), | ||
256 | }; | ||
257 | self.replace_children(colon.into()..=end, iter::empty()) | ||
258 | } | ||
259 | } | ||
260 | |||
261 | impl ast::TypeParam { | ||
262 | #[must_use] | ||
263 | pub fn remove_bounds(&self) -> ast::TypeParam { | ||
264 | let colon = match self.colon_token() { | ||
265 | Some(it) => it, | ||
266 | None => return self.clone(), | ||
267 | }; | ||
268 | let end = match self.type_bound_list() { | ||
269 | Some(it) => it.syntax().clone().into(), | ||
270 | None => colon.clone().into(), | ||
271 | }; | ||
272 | self.replace_children(colon.into()..=end, iter::empty()) | ||
273 | } | ||
274 | } | ||
275 | |||
276 | impl ast::Path { | 28 | impl ast::Path { |
277 | #[must_use] | 29 | #[must_use] |
278 | pub fn with_segment(&self, segment: ast::PathSegment) -> ast::Path { | 30 | pub fn with_segment(&self, segment: ast::PathSegment) -> ast::Path { |
@@ -313,33 +65,7 @@ impl ast::PathSegment { | |||
313 | } | 65 | } |
314 | } | 66 | } |
315 | 67 | ||
316 | impl ast::Use { | ||
317 | #[must_use] | ||
318 | pub fn with_use_tree(&self, use_tree: ast::UseTree) -> ast::Use { | ||
319 | if let Some(old) = self.use_tree() { | ||
320 | return self.replace_descendant(old, use_tree); | ||
321 | } | ||
322 | self.clone() | ||
323 | } | ||
324 | } | ||
325 | |||
326 | impl ast::UseTree { | 68 | impl ast::UseTree { |
327 | #[must_use] | ||
328 | pub fn with_path(&self, path: ast::Path) -> ast::UseTree { | ||
329 | if let Some(old) = self.path() { | ||
330 | return self.replace_descendant(old, path); | ||
331 | } | ||
332 | self.clone() | ||
333 | } | ||
334 | |||
335 | #[must_use] | ||
336 | pub fn with_use_tree_list(&self, use_tree_list: ast::UseTreeList) -> ast::UseTree { | ||
337 | if let Some(old) = self.use_tree_list() { | ||
338 | return self.replace_descendant(old, use_tree_list); | ||
339 | } | ||
340 | self.clone() | ||
341 | } | ||
342 | |||
343 | /// Splits off the given prefix, making it the path component of the use tree, appending the rest of the path to all UseTreeList items. | 69 | /// Splits off the given prefix, making it the path component of the use tree, appending the rest of the path to all UseTreeList items. |
344 | #[must_use] | 70 | #[must_use] |
345 | pub fn split_prefix(&self, prefix: &ast::Path) -> ast::UseTree { | 71 | pub fn split_prefix(&self, prefix: &ast::Path) -> ast::UseTree { |
@@ -376,134 +102,6 @@ impl ast::UseTree { | |||
376 | } | 102 | } |
377 | } | 103 | } |
378 | 104 | ||
379 | impl ast::MatchArmList { | ||
380 | #[must_use] | ||
381 | pub fn append_arms(&self, items: impl IntoIterator<Item = ast::MatchArm>) -> ast::MatchArmList { | ||
382 | let mut res = self.clone(); | ||
383 | res = res.strip_if_only_whitespace(); | ||
384 | if !res.syntax().text().contains_char('\n') { | ||
385 | res = make_multiline(res); | ||
386 | } | ||
387 | items.into_iter().for_each(|it| res = res.append_arm(it)); | ||
388 | res | ||
389 | } | ||
390 | |||
391 | fn strip_if_only_whitespace(&self) -> ast::MatchArmList { | ||
392 | let mut iter = self.syntax().children_with_tokens().skip_while(|it| it.kind() != T!['{']); | ||
393 | iter.next(); // Eat the curly | ||
394 | let mut inner = iter.take_while(|it| it.kind() != T!['}']); | ||
395 | if !inner.clone().all(|it| it.kind() == WHITESPACE) { | ||
396 | return self.clone(); | ||
397 | } | ||
398 | let start = match inner.next() { | ||
399 | Some(s) => s, | ||
400 | None => return self.clone(), | ||
401 | }; | ||
402 | let end = match inner.last() { | ||
403 | Some(s) => s, | ||
404 | None => start.clone(), | ||
405 | }; | ||
406 | self.replace_children(start..=end, &mut iter::empty()) | ||
407 | } | ||
408 | |||
409 | #[must_use] | ||
410 | pub fn remove_placeholder(&self) -> ast::MatchArmList { | ||
411 | let placeholder = | ||
412 | self.arms().find(|arm| matches!(arm.pat(), Some(ast::Pat::WildcardPat(_)))); | ||
413 | if let Some(placeholder) = placeholder { | ||
414 | self.remove_arm(&placeholder) | ||
415 | } else { | ||
416 | self.clone() | ||
417 | } | ||
418 | } | ||
419 | |||
420 | #[must_use] | ||
421 | fn remove_arm(&self, arm: &ast::MatchArm) -> ast::MatchArmList { | ||
422 | let start = arm.syntax().clone(); | ||
423 | let end = if let Some(comma) = start | ||
424 | .siblings_with_tokens(Direction::Next) | ||
425 | .skip(1) | ||
426 | .find(|it| !it.kind().is_trivia()) | ||
427 | .filter(|it| it.kind() == T![,]) | ||
428 | { | ||
429 | comma | ||
430 | } else { | ||
431 | start.clone().into() | ||
432 | }; | ||
433 | self.replace_children(start.into()..=end, None) | ||
434 | } | ||
435 | |||
436 | #[must_use] | ||
437 | pub fn append_arm(&self, item: ast::MatchArm) -> ast::MatchArmList { | ||
438 | let r_curly = match self.syntax().children_with_tokens().find(|it| it.kind() == T!['}']) { | ||
439 | Some(t) => t, | ||
440 | None => return self.clone(), | ||
441 | }; | ||
442 | let position = InsertPosition::Before(r_curly); | ||
443 | let arm_ws = tokens::WsBuilder::new(" "); | ||
444 | let match_indent = &leading_indent(self.syntax()).unwrap_or_default(); | ||
445 | let match_ws = tokens::WsBuilder::new(&format!("\n{}", match_indent)); | ||
446 | let to_insert: ArrayVec<SyntaxElement, 3> = | ||
447 | [arm_ws.ws().into(), item.syntax().clone().into(), match_ws.ws().into()].into(); | ||
448 | self.insert_children(position, to_insert) | ||
449 | } | ||
450 | } | ||
451 | |||
452 | impl ast::GenericParamList { | ||
453 | #[must_use] | ||
454 | pub fn append_params( | ||
455 | &self, | ||
456 | params: impl IntoIterator<Item = ast::GenericParam>, | ||
457 | ) -> ast::GenericParamList { | ||
458 | let mut res = self.clone(); | ||
459 | params.into_iter().for_each(|it| res = res.append_param(it)); | ||
460 | res | ||
461 | } | ||
462 | |||
463 | #[must_use] | ||
464 | pub fn append_param(&self, item: ast::GenericParam) -> ast::GenericParamList { | ||
465 | let space = tokens::single_space(); | ||
466 | |||
467 | let mut to_insert: ArrayVec<SyntaxElement, 4> = ArrayVec::new(); | ||
468 | if self.generic_params().next().is_some() { | ||
469 | to_insert.push(space.into()); | ||
470 | } | ||
471 | to_insert.push(item.syntax().clone().into()); | ||
472 | |||
473 | macro_rules! after_l_angle { | ||
474 | () => {{ | ||
475 | let anchor = match self.l_angle_token() { | ||
476 | Some(it) => it.into(), | ||
477 | None => return self.clone(), | ||
478 | }; | ||
479 | InsertPosition::After(anchor) | ||
480 | }}; | ||
481 | } | ||
482 | |||
483 | macro_rules! after_field { | ||
484 | ($anchor:expr) => { | ||
485 | if let Some(comma) = $anchor | ||
486 | .syntax() | ||
487 | .siblings_with_tokens(Direction::Next) | ||
488 | .find(|it| it.kind() == T![,]) | ||
489 | { | ||
490 | InsertPosition::After(comma) | ||
491 | } else { | ||
492 | to_insert.insert(0, make::token(T![,]).into()); | ||
493 | InsertPosition::After($anchor.syntax().clone().into()) | ||
494 | } | ||
495 | }; | ||
496 | } | ||
497 | |||
498 | let position = match self.generic_params().last() { | ||
499 | Some(it) => after_field!(it), | ||
500 | None => after_l_angle!(), | ||
501 | }; | ||
502 | |||
503 | self.insert_children(position, to_insert) | ||
504 | } | ||
505 | } | ||
506 | |||
507 | #[must_use] | 105 | #[must_use] |
508 | pub fn remove_attrs_and_docs<N: ast::AttrsOwner>(node: &N) -> N { | 106 | pub fn remove_attrs_and_docs<N: ast::AttrsOwner>(node: &N) -> N { |
509 | N::cast(remove_attrs_and_docs_inner(node.syntax().clone())).unwrap() | 107 | N::cast(remove_attrs_and_docs_inner(node.syntax().clone())).unwrap() |
@@ -554,6 +152,12 @@ impl ops::Add<u8> for IndentLevel { | |||
554 | } | 152 | } |
555 | 153 | ||
556 | impl IndentLevel { | 154 | impl IndentLevel { |
155 | pub fn single() -> IndentLevel { | ||
156 | IndentLevel(0) | ||
157 | } | ||
158 | pub fn is_zero(&self) -> bool { | ||
159 | self.0 == 0 | ||
160 | } | ||
557 | pub fn from_element(element: &SyntaxElement) -> IndentLevel { | 161 | pub fn from_element(element: &SyntaxElement) -> IndentLevel { |
558 | match element { | 162 | match element { |
559 | rowan::NodeOrToken::Node(it) => IndentLevel::from_node(it), | 163 | rowan::NodeOrToken::Node(it) => IndentLevel::from_node(it), |
@@ -588,54 +192,40 @@ impl IndentLevel { | |||
588 | /// ``` | 192 | /// ``` |
589 | /// if you indent the block, the `{` token would stay put. | 193 | /// if you indent the block, the `{` token would stay put. |
590 | fn increase_indent(self, node: SyntaxNode) -> SyntaxNode { | 194 | fn increase_indent(self, node: SyntaxNode) -> SyntaxNode { |
591 | let mut rewriter = SyntaxRewriter::default(); | 195 | let res = node.clone_subtree().clone_for_update(); |
592 | node.descendants_with_tokens() | 196 | let tokens = res.preorder_with_tokens().filter_map(|event| match event { |
593 | .filter_map(|el| el.into_token()) | 197 | rowan::WalkEvent::Leave(NodeOrToken::Token(it)) => Some(it), |
594 | .filter_map(ast::Whitespace::cast) | 198 | _ => None, |
595 | .filter(|ws| { | 199 | }); |
596 | let text = ws.syntax().text(); | 200 | for token in tokens { |
597 | text.contains('\n') | 201 | if let Some(ws) = ast::Whitespace::cast(token) { |
598 | }) | 202 | if ws.text().contains('\n') { |
599 | .for_each(|ws| { | 203 | let new_ws = make::tokens::whitespace(&format!("{}{}", ws.syntax(), self)); |
600 | let new_ws = make::tokens::whitespace(&format!("{}{}", ws.syntax(), self,)); | 204 | ted::replace(ws.syntax(), &new_ws) |
601 | rewriter.replace(ws.syntax(), &new_ws) | 205 | } |
602 | }); | 206 | } |
603 | rewriter.rewrite(&node) | 207 | } |
208 | res.clone_subtree() | ||
604 | } | 209 | } |
605 | 210 | ||
606 | fn decrease_indent(self, node: SyntaxNode) -> SyntaxNode { | 211 | fn decrease_indent(self, node: SyntaxNode) -> SyntaxNode { |
607 | let mut rewriter = SyntaxRewriter::default(); | 212 | let res = node.clone_subtree().clone_for_update(); |
608 | node.descendants_with_tokens() | 213 | let tokens = res.preorder_with_tokens().filter_map(|event| match event { |
609 | .filter_map(|el| el.into_token()) | 214 | rowan::WalkEvent::Leave(NodeOrToken::Token(it)) => Some(it), |
610 | .filter_map(ast::Whitespace::cast) | 215 | _ => None, |
611 | .filter(|ws| { | 216 | }); |
612 | let text = ws.syntax().text(); | 217 | for token in tokens { |
613 | text.contains('\n') | 218 | if let Some(ws) = ast::Whitespace::cast(token) { |
614 | }) | 219 | if ws.text().contains('\n') { |
615 | .for_each(|ws| { | 220 | let new_ws = make::tokens::whitespace( |
616 | let new_ws = make::tokens::whitespace( | 221 | &ws.syntax().text().replace(&format!("\n{}", self), "\n"), |
617 | &ws.syntax().text().replace(&format!("\n{}", self), "\n"), | 222 | ); |
618 | ); | 223 | ted::replace(ws.syntax(), &new_ws) |
619 | rewriter.replace(ws.syntax(), &new_ws) | 224 | } |
620 | }); | ||
621 | rewriter.rewrite(&node) | ||
622 | } | ||
623 | } | ||
624 | |||
625 | // FIXME: replace usages with IndentLevel above | ||
626 | fn leading_indent(node: &SyntaxNode) -> Option<SmolStr> { | ||
627 | for token in prev_tokens(node.first_token()?) { | ||
628 | if let Some(ws) = ast::Whitespace::cast(token.clone()) { | ||
629 | let ws_text = ws.text(); | ||
630 | if let Some(pos) = ws_text.rfind('\n') { | ||
631 | return Some(ws_text[pos + 1..].into()); | ||
632 | } | 225 | } |
633 | } | 226 | } |
634 | if token.text().contains('\n') { | 227 | res.clone_subtree() |
635 | break; | ||
636 | } | ||
637 | } | 228 | } |
638 | None | ||
639 | } | 229 | } |
640 | 230 | ||
641 | fn prev_tokens(token: SyntaxToken) -> impl Iterator<Item = SyntaxToken> { | 231 | fn prev_tokens(token: SyntaxToken) -> impl Iterator<Item = SyntaxToken> { |
@@ -662,23 +252,6 @@ pub trait AstNodeEdit: AstNode + Clone + Sized { | |||
662 | let new_syntax = algo::replace_children(self.syntax(), to_replace, to_insert); | 252 | let new_syntax = algo::replace_children(self.syntax(), to_replace, to_insert); |
663 | Self::cast(new_syntax).unwrap() | 253 | Self::cast(new_syntax).unwrap() |
664 | } | 254 | } |
665 | |||
666 | #[must_use] | ||
667 | fn replace_descendant<D: AstNode>(&self, old: D, new: D) -> Self { | ||
668 | self.replace_descendants(iter::once((old, new))) | ||
669 | } | ||
670 | |||
671 | #[must_use] | ||
672 | fn replace_descendants<D: AstNode>( | ||
673 | &self, | ||
674 | replacement_map: impl IntoIterator<Item = (D, D)>, | ||
675 | ) -> Self { | ||
676 | let mut rewriter = SyntaxRewriter::default(); | ||
677 | for (from, to) in replacement_map { | ||
678 | rewriter.replace(from.syntax(), to.syntax()) | ||
679 | } | ||
680 | rewriter.rewrite_ast(self) | ||
681 | } | ||
682 | fn indent_level(&self) -> IndentLevel { | 255 | fn indent_level(&self) -> IndentLevel { |
683 | IndentLevel::from_node(self.syntax()) | 256 | IndentLevel::from_node(self.syntax()) |
684 | } | 257 | } |
diff --git a/crates/syntax/src/ast/edit_in_place.rs b/crates/syntax/src/ast/edit_in_place.rs index 04f97f368..2676ed8c9 100644 --- a/crates/syntax/src/ast/edit_in_place.rs +++ b/crates/syntax/src/ast/edit_in_place.rs | |||
@@ -2,13 +2,18 @@ | |||
2 | 2 | ||
3 | use std::iter::empty; | 3 | use std::iter::empty; |
4 | 4 | ||
5 | use parser::T; | 5 | use parser::{SyntaxKind, T}; |
6 | use rowan::SyntaxElement; | ||
6 | 7 | ||
7 | use crate::{ | 8 | use crate::{ |
8 | algo::neighbor, | 9 | algo::neighbor, |
9 | ast::{self, edit::AstNodeEdit, make, GenericParamsOwner, WhereClause}, | 10 | ast::{ |
11 | self, | ||
12 | edit::{AstNodeEdit, IndentLevel}, | ||
13 | make, GenericParamsOwner, | ||
14 | }, | ||
10 | ted::{self, Position}, | 15 | ted::{self, Position}, |
11 | AstNode, AstToken, Direction, | 16 | AstNode, AstToken, Direction, SyntaxNode, |
12 | }; | 17 | }; |
13 | 18 | ||
14 | use super::NameOwner; | 19 | use super::NameOwner; |
@@ -37,7 +42,7 @@ impl GenericParamsOwnerEdit for ast::Fn { | |||
37 | } | 42 | } |
38 | } | 43 | } |
39 | 44 | ||
40 | fn get_or_create_where_clause(&self) -> WhereClause { | 45 | fn get_or_create_where_clause(&self) -> ast::WhereClause { |
41 | if self.where_clause().is_none() { | 46 | if self.where_clause().is_none() { |
42 | let position = if let Some(ty) = self.ret_type() { | 47 | let position = if let Some(ty) = self.ret_type() { |
43 | Position::after(ty.syntax()) | 48 | Position::after(ty.syntax()) |
@@ -67,7 +72,7 @@ impl GenericParamsOwnerEdit for ast::Impl { | |||
67 | } | 72 | } |
68 | } | 73 | } |
69 | 74 | ||
70 | fn get_or_create_where_clause(&self) -> WhereClause { | 75 | fn get_or_create_where_clause(&self) -> ast::WhereClause { |
71 | if self.where_clause().is_none() { | 76 | if self.where_clause().is_none() { |
72 | let position = if let Some(items) = self.assoc_item_list() { | 77 | let position = if let Some(items) = self.assoc_item_list() { |
73 | Position::before(items.syntax()) | 78 | Position::before(items.syntax()) |
@@ -97,7 +102,7 @@ impl GenericParamsOwnerEdit for ast::Trait { | |||
97 | } | 102 | } |
98 | } | 103 | } |
99 | 104 | ||
100 | fn get_or_create_where_clause(&self) -> WhereClause { | 105 | fn get_or_create_where_clause(&self) -> ast::WhereClause { |
101 | if self.where_clause().is_none() { | 106 | if self.where_clause().is_none() { |
102 | let position = if let Some(items) = self.assoc_item_list() { | 107 | let position = if let Some(items) = self.assoc_item_list() { |
103 | Position::before(items.syntax()) | 108 | Position::before(items.syntax()) |
@@ -127,7 +132,7 @@ impl GenericParamsOwnerEdit for ast::Struct { | |||
127 | } | 132 | } |
128 | } | 133 | } |
129 | 134 | ||
130 | fn get_or_create_where_clause(&self) -> WhereClause { | 135 | fn get_or_create_where_clause(&self) -> ast::WhereClause { |
131 | if self.where_clause().is_none() { | 136 | if self.where_clause().is_none() { |
132 | let tfl = self.field_list().and_then(|fl| match fl { | 137 | let tfl = self.field_list().and_then(|fl| match fl { |
133 | ast::FieldList::RecordFieldList(_) => None, | 138 | ast::FieldList::RecordFieldList(_) => None, |
@@ -165,7 +170,7 @@ impl GenericParamsOwnerEdit for ast::Enum { | |||
165 | } | 170 | } |
166 | } | 171 | } |
167 | 172 | ||
168 | fn get_or_create_where_clause(&self) -> WhereClause { | 173 | fn get_or_create_where_clause(&self) -> ast::WhereClause { |
169 | if self.where_clause().is_none() { | 174 | if self.where_clause().is_none() { |
170 | let position = if let Some(gpl) = self.generic_param_list() { | 175 | let position = if let Some(gpl) = self.generic_param_list() { |
171 | Position::after(gpl.syntax()) | 176 | Position::after(gpl.syntax()) |
@@ -195,18 +200,13 @@ impl ast::GenericParamList { | |||
195 | pub fn add_generic_param(&self, generic_param: ast::GenericParam) { | 200 | pub fn add_generic_param(&self, generic_param: ast::GenericParam) { |
196 | match self.generic_params().last() { | 201 | match self.generic_params().last() { |
197 | Some(last_param) => { | 202 | Some(last_param) => { |
198 | let mut elems = Vec::new(); | 203 | let position = Position::after(last_param.syntax()); |
199 | if !last_param | 204 | let elements = vec![ |
200 | .syntax() | 205 | make::token(T![,]).into(), |
201 | .siblings_with_tokens(Direction::Next) | 206 | make::tokens::single_space().into(), |
202 | .any(|it| it.kind() == T![,]) | 207 | generic_param.syntax().clone().into(), |
203 | { | 208 | ]; |
204 | elems.push(make::token(T![,]).into()); | 209 | ted::insert_all(position, elements); |
205 | elems.push(make::tokens::single_space().into()); | ||
206 | }; | ||
207 | elems.push(generic_param.syntax().clone().into()); | ||
208 | let after_last_param = Position::after(last_param.syntax()); | ||
209 | ted::insert_all(after_last_param, elems); | ||
210 | } | 210 | } |
211 | None => { | 211 | None => { |
212 | let after_l_angle = Position::after(self.l_angle_token().unwrap()); | 212 | let after_l_angle = Position::after(self.l_angle_token().unwrap()); |
@@ -277,6 +277,167 @@ impl ast::Use { | |||
277 | } | 277 | } |
278 | } | 278 | } |
279 | 279 | ||
280 | impl ast::Impl { | ||
281 | pub fn get_or_create_assoc_item_list(&self) -> ast::AssocItemList { | ||
282 | if self.assoc_item_list().is_none() { | ||
283 | let assoc_item_list = make::assoc_item_list().clone_for_update(); | ||
284 | ted::append_child(self.syntax(), assoc_item_list.syntax()); | ||
285 | } | ||
286 | self.assoc_item_list().unwrap() | ||
287 | } | ||
288 | } | ||
289 | |||
290 | impl ast::AssocItemList { | ||
291 | pub fn add_item(&self, item: ast::AssocItem) { | ||
292 | let (indent, position, whitespace) = match self.assoc_items().last() { | ||
293 | Some(last_item) => ( | ||
294 | IndentLevel::from_node(last_item.syntax()), | ||
295 | Position::after(last_item.syntax()), | ||
296 | "\n\n", | ||
297 | ), | ||
298 | None => match self.l_curly_token() { | ||
299 | Some(l_curly) => { | ||
300 | normalize_ws_between_braces(self.syntax()); | ||
301 | (IndentLevel::from_token(&l_curly) + 1, Position::after(&l_curly), "\n") | ||
302 | } | ||
303 | None => (IndentLevel::single(), Position::last_child_of(self.syntax()), "\n"), | ||
304 | }, | ||
305 | }; | ||
306 | let elements: Vec<SyntaxElement<_>> = vec![ | ||
307 | make::tokens::whitespace(&format!("{}{}", whitespace, indent)).into(), | ||
308 | item.syntax().clone().into(), | ||
309 | ]; | ||
310 | ted::insert_all(position, elements); | ||
311 | } | ||
312 | } | ||
313 | |||
314 | impl ast::Fn { | ||
315 | pub fn get_or_create_body(&self) -> ast::BlockExpr { | ||
316 | if self.body().is_none() { | ||
317 | let body = make::ext::empty_block_expr().clone_for_update(); | ||
318 | match self.semicolon_token() { | ||
319 | Some(semi) => { | ||
320 | ted::replace(semi, body.syntax()); | ||
321 | ted::insert(Position::before(body.syntax), make::tokens::single_space()); | ||
322 | } | ||
323 | None => ted::append_child(self.syntax(), body.syntax()), | ||
324 | } | ||
325 | } | ||
326 | self.body().unwrap() | ||
327 | } | ||
328 | } | ||
329 | |||
330 | impl ast::MatchArm { | ||
331 | pub fn remove(&self) { | ||
332 | if let Some(sibling) = self.syntax().prev_sibling_or_token() { | ||
333 | if sibling.kind() == SyntaxKind::WHITESPACE { | ||
334 | ted::remove(sibling); | ||
335 | } | ||
336 | } | ||
337 | if let Some(sibling) = self.syntax().next_sibling_or_token() { | ||
338 | if sibling.kind() == T![,] { | ||
339 | ted::remove(sibling); | ||
340 | } | ||
341 | } | ||
342 | ted::remove(self.syntax()); | ||
343 | } | ||
344 | } | ||
345 | |||
346 | impl ast::MatchArmList { | ||
347 | pub fn add_arm(&self, arm: ast::MatchArm) { | ||
348 | normalize_ws_between_braces(self.syntax()); | ||
349 | let position = match self.arms().last() { | ||
350 | Some(last_arm) => { | ||
351 | let curly = last_arm | ||
352 | .syntax() | ||
353 | .siblings_with_tokens(Direction::Next) | ||
354 | .find(|it| it.kind() == T![,]); | ||
355 | Position::after(curly.unwrap_or_else(|| last_arm.syntax().clone().into())) | ||
356 | } | ||
357 | None => match self.l_curly_token() { | ||
358 | Some(it) => Position::after(it), | ||
359 | None => Position::last_child_of(self.syntax()), | ||
360 | }, | ||
361 | }; | ||
362 | let indent = IndentLevel::from_node(self.syntax()) + 1; | ||
363 | let elements = vec![ | ||
364 | make::tokens::whitespace(&format!("\n{}", indent)).into(), | ||
365 | arm.syntax().clone().into(), | ||
366 | ]; | ||
367 | ted::insert_all(position, elements); | ||
368 | } | ||
369 | } | ||
370 | |||
371 | impl ast::RecordExprFieldList { | ||
372 | pub fn add_field(&self, field: ast::RecordExprField) { | ||
373 | let is_multiline = self.syntax().text().contains_char('\n'); | ||
374 | let whitespace = if is_multiline { | ||
375 | let indent = IndentLevel::from_node(self.syntax()) + 1; | ||
376 | make::tokens::whitespace(&format!("\n{}", indent)) | ||
377 | } else { | ||
378 | make::tokens::single_space() | ||
379 | }; | ||
380 | |||
381 | if is_multiline { | ||
382 | normalize_ws_between_braces(self.syntax()); | ||
383 | } | ||
384 | |||
385 | let position = match self.fields().last() { | ||
386 | Some(last_field) => { | ||
387 | let comma = match last_field | ||
388 | .syntax() | ||
389 | .siblings_with_tokens(Direction::Next) | ||
390 | .filter_map(|it| it.into_token()) | ||
391 | .find(|it| it.kind() == T![,]) | ||
392 | { | ||
393 | Some(it) => it, | ||
394 | None => { | ||
395 | let comma = ast::make::token(T![,]); | ||
396 | ted::insert(Position::after(last_field.syntax()), &comma); | ||
397 | comma | ||
398 | } | ||
399 | }; | ||
400 | Position::after(comma) | ||
401 | } | ||
402 | None => match self.l_curly_token() { | ||
403 | Some(it) => Position::after(it), | ||
404 | None => Position::last_child_of(self.syntax()), | ||
405 | }, | ||
406 | }; | ||
407 | |||
408 | ted::insert_all(position, vec![whitespace.into(), field.syntax().clone().into()]); | ||
409 | if is_multiline { | ||
410 | ted::insert(Position::after(field.syntax()), ast::make::token(T![,])); | ||
411 | } | ||
412 | } | ||
413 | } | ||
414 | |||
415 | fn normalize_ws_between_braces(node: &SyntaxNode) -> Option<()> { | ||
416 | let l = node | ||
417 | .children_with_tokens() | ||
418 | .filter_map(|it| it.into_token()) | ||
419 | .find(|it| it.kind() == T!['{'])?; | ||
420 | let r = node | ||
421 | .children_with_tokens() | ||
422 | .filter_map(|it| it.into_token()) | ||
423 | .find(|it| it.kind() == T!['}'])?; | ||
424 | |||
425 | let indent = IndentLevel::from_node(node); | ||
426 | |||
427 | match l.next_sibling_or_token() { | ||
428 | Some(ws) if ws.kind() == SyntaxKind::WHITESPACE => { | ||
429 | if ws.next_sibling_or_token()?.into_token()? == r { | ||
430 | ted::replace(ws, make::tokens::whitespace(&format!("\n{}", indent))); | ||
431 | } | ||
432 | } | ||
433 | Some(ws) if ws.kind() == T!['}'] => { | ||
434 | ted::insert(Position::after(l), make::tokens::whitespace(&format!("\n{}", indent))); | ||
435 | } | ||
436 | _ => (), | ||
437 | } | ||
438 | Some(()) | ||
439 | } | ||
440 | |||
280 | #[cfg(test)] | 441 | #[cfg(test)] |
281 | mod tests { | 442 | mod tests { |
282 | use std::fmt; | 443 | use std::fmt; |
diff --git a/crates/syntax/src/ast/make.rs b/crates/syntax/src/ast/make.rs index f8b508a90..d13926ded 100644 --- a/crates/syntax/src/ast/make.rs +++ b/crates/syntax/src/ast/make.rs | |||
@@ -3,25 +3,55 @@ | |||
3 | //! | 3 | //! |
4 | //! Note that all functions here intended to be stupid constructors, which just | 4 | //! Note that all functions here intended to be stupid constructors, which just |
5 | //! assemble a finish node from immediate children. If you want to do something | 5 | //! assemble a finish node from immediate children. If you want to do something |
6 | //! smarter than that, it probably doesn't belong in this module. | 6 | //! smarter than that, it belongs to the `ext` submodule. |
7 | //! | 7 | //! |
8 | //! Keep in mind that `from_text` functions should be kept private. The public | 8 | //! Keep in mind that `from_text` functions should be kept private. The public |
9 | //! API should require to assemble every node piecewise. The trick of | 9 | //! API should require to assemble every node piecewise. The trick of |
10 | //! `parse(format!())` we use internally is an implementation detail -- long | 10 | //! `parse(format!())` we use internally is an implementation detail -- long |
11 | //! term, it will be replaced with direct tree manipulation. | 11 | //! term, it will be replaced with direct tree manipulation. |
12 | use itertools::Itertools; | 12 | use itertools::Itertools; |
13 | use stdx::format_to; | 13 | use stdx::{format_to, never}; |
14 | 14 | ||
15 | use crate::{ast, AstNode, SourceFile, SyntaxKind, SyntaxNode, SyntaxToken}; | 15 | use crate::{ast, AstNode, SourceFile, SyntaxKind, SyntaxToken}; |
16 | |||
17 | /// While the parent module defines basic atomic "constructors", the `ext` | ||
18 | /// module defines shortcuts for common things. | ||
19 | /// | ||
20 | /// It's named `ext` rather than `shortcuts` just to keep it short. | ||
21 | pub mod ext { | ||
22 | use super::*; | ||
23 | |||
24 | pub fn ident_path(ident: &str) -> ast::Path { | ||
25 | path_unqualified(path_segment(name_ref(ident))) | ||
26 | } | ||
27 | |||
28 | pub fn expr_unreachable() -> ast::Expr { | ||
29 | expr_from_text("unreachable!()") | ||
30 | } | ||
31 | pub fn expr_todo() -> ast::Expr { | ||
32 | expr_from_text("todo!()") | ||
33 | } | ||
34 | pub fn empty_block_expr() -> ast::BlockExpr { | ||
35 | block_expr(None, None) | ||
36 | } | ||
37 | |||
38 | pub fn ty_bool() -> ast::Type { | ||
39 | ty_path(ident_path("bool")) | ||
40 | } | ||
41 | pub fn ty_option(t: ast::Type) -> ast::Type { | ||
42 | ty_from_text(&format!("Option<{}>", t)) | ||
43 | } | ||
44 | pub fn ty_result(t: ast::Type, e: ast::Type) -> ast::Type { | ||
45 | ty_from_text(&format!("Result<{}, {}>", t, e)) | ||
46 | } | ||
47 | } | ||
16 | 48 | ||
17 | pub fn name(text: &str) -> ast::Name { | 49 | pub fn name(text: &str) -> ast::Name { |
18 | ast_from_text(&format!("mod {}{};", raw_ident_esc(text), text)) | 50 | ast_from_text(&format!("mod {}{};", raw_ident_esc(text), text)) |
19 | } | 51 | } |
20 | |||
21 | pub fn name_ref(text: &str) -> ast::NameRef { | 52 | pub fn name_ref(text: &str) -> ast::NameRef { |
22 | ast_from_text(&format!("fn f() {{ {}{}; }}", raw_ident_esc(text), text)) | 53 | ast_from_text(&format!("fn f() {{ {}{}; }}", raw_ident_esc(text), text)) |
23 | } | 54 | } |
24 | |||
25 | fn raw_ident_esc(ident: &str) -> &'static str { | 55 | fn raw_ident_esc(ident: &str) -> &'static str { |
26 | let is_keyword = parser::SyntaxKind::from_keyword(ident).is_some(); | 56 | let is_keyword = parser::SyntaxKind::from_keyword(ident).is_some(); |
27 | if is_keyword && !matches!(ident, "self" | "crate" | "super" | "Self") { | 57 | if is_keyword && !matches!(ident, "self" | "crate" | "super" | "Self") { |
@@ -31,13 +61,23 @@ fn raw_ident_esc(ident: &str) -> &'static str { | |||
31 | } | 61 | } |
32 | } | 62 | } |
33 | 63 | ||
64 | pub fn lifetime(text: &str) -> ast::Lifetime { | ||
65 | let mut text = text; | ||
66 | let tmp; | ||
67 | if never!(!text.starts_with('\'')) { | ||
68 | tmp = format!("'{}", text); | ||
69 | text = &tmp; | ||
70 | } | ||
71 | ast_from_text(&format!("fn f<{}>() {{ }}", text)) | ||
72 | } | ||
73 | |||
34 | // FIXME: replace stringly-typed constructor with a family of typed ctors, a-la | 74 | // FIXME: replace stringly-typed constructor with a family of typed ctors, a-la |
35 | // `expr_xxx`. | 75 | // `expr_xxx`. |
36 | pub fn ty(text: &str) -> ast::Type { | 76 | pub fn ty(text: &str) -> ast::Type { |
37 | ast_from_text(&format!("fn f() -> {} {{}}", text)) | 77 | ty_from_text(text) |
38 | } | 78 | } |
39 | pub fn ty_unit() -> ast::Type { | 79 | pub fn ty_unit() -> ast::Type { |
40 | ty("()") | 80 | ty_from_text("()") |
41 | } | 81 | } |
42 | pub fn ty_tuple(types: impl IntoIterator<Item = ast::Type>) -> ast::Type { | 82 | pub fn ty_tuple(types: impl IntoIterator<Item = ast::Type>) -> ast::Type { |
43 | let mut count: usize = 0; | 83 | let mut count: usize = 0; |
@@ -46,19 +86,20 @@ pub fn ty_tuple(types: impl IntoIterator<Item = ast::Type>) -> ast::Type { | |||
46 | contents.push(','); | 86 | contents.push(','); |
47 | } | 87 | } |
48 | 88 | ||
49 | ty(&format!("({})", contents)) | 89 | ty_from_text(&format!("({})", contents)) |
50 | } | ||
51 | // FIXME: handle path to type | ||
52 | pub fn ty_generic(name: ast::NameRef, types: impl IntoIterator<Item = ast::Type>) -> ast::Type { | ||
53 | let contents = types.into_iter().join(", "); | ||
54 | ty(&format!("{}<{}>", name, contents)) | ||
55 | } | 90 | } |
56 | pub fn ty_ref(target: ast::Type, exclusive: bool) -> ast::Type { | 91 | pub fn ty_ref(target: ast::Type, exclusive: bool) -> ast::Type { |
57 | ty(&if exclusive { format!("&mut {}", target) } else { format!("&{}", target) }) | 92 | ty_from_text(&if exclusive { format!("&mut {}", target) } else { format!("&{}", target) }) |
93 | } | ||
94 | pub fn ty_path(path: ast::Path) -> ast::Type { | ||
95 | ty_from_text(&path.to_string()) | ||
96 | } | ||
97 | fn ty_from_text(text: &str) -> ast::Type { | ||
98 | ast_from_text(&format!("type _T = {};", text)) | ||
58 | } | 99 | } |
59 | 100 | ||
60 | pub fn assoc_item_list() -> ast::AssocItemList { | 101 | pub fn assoc_item_list() -> ast::AssocItemList { |
61 | ast_from_text("impl C for D {};") | 102 | ast_from_text("impl C for D {}") |
62 | } | 103 | } |
63 | 104 | ||
64 | pub fn impl_trait(trait_: ast::Path, ty: ast::Path) -> ast::Impl { | 105 | pub fn impl_trait(trait_: ast::Path, ty: ast::Path) -> ast::Impl { |
@@ -88,7 +129,7 @@ pub fn path_unqualified(segment: ast::PathSegment) -> ast::Path { | |||
88 | pub fn path_qualified(qual: ast::Path, segment: ast::PathSegment) -> ast::Path { | 129 | pub fn path_qualified(qual: ast::Path, segment: ast::PathSegment) -> ast::Path { |
89 | ast_from_text(&format!("{}::{}", qual, segment)) | 130 | ast_from_text(&format!("{}::{}", qual, segment)) |
90 | } | 131 | } |
91 | 132 | // FIXME: path concatenation operation doesn't make sense as AST op. | |
92 | pub fn path_concat(first: ast::Path, second: ast::Path) -> ast::Path { | 133 | pub fn path_concat(first: ast::Path, second: ast::Path) -> ast::Path { |
93 | ast_from_text(&format!("{}::{}", first, second)) | 134 | ast_from_text(&format!("{}::{}", first, second)) |
94 | } | 135 | } |
@@ -104,15 +145,14 @@ pub fn path_from_segments( | |||
104 | format!("use {};", segments) | 145 | format!("use {};", segments) |
105 | }) | 146 | }) |
106 | } | 147 | } |
107 | 148 | // FIXME: should not be pub | |
108 | pub fn path_from_text(text: &str) -> ast::Path { | 149 | pub fn path_from_text(text: &str) -> ast::Path { |
109 | ast_from_text(&format!("fn main() {{ let test = {}; }}", text)) | 150 | ast_from_text(&format!("fn main() {{ let test = {}; }}", text)) |
110 | } | 151 | } |
111 | 152 | ||
112 | pub fn glob_use_tree() -> ast::UseTree { | 153 | pub fn use_tree_glob() -> ast::UseTree { |
113 | ast_from_text("use *;") | 154 | ast_from_text("use *;") |
114 | } | 155 | } |
115 | |||
116 | pub fn use_tree( | 156 | pub fn use_tree( |
117 | path: ast::Path, | 157 | path: ast::Path, |
118 | use_tree_list: Option<ast::UseTreeList>, | 158 | use_tree_list: Option<ast::UseTreeList>, |
@@ -207,15 +247,6 @@ pub fn expr_literal(text: &str) -> ast::Literal { | |||
207 | pub fn expr_empty_block() -> ast::Expr { | 247 | pub fn expr_empty_block() -> ast::Expr { |
208 | expr_from_text("{}") | 248 | expr_from_text("{}") |
209 | } | 249 | } |
210 | pub fn expr_unimplemented() -> ast::Expr { | ||
211 | expr_from_text("unimplemented!()") | ||
212 | } | ||
213 | pub fn expr_unreachable() -> ast::Expr { | ||
214 | expr_from_text("unreachable!()") | ||
215 | } | ||
216 | pub fn expr_todo() -> ast::Expr { | ||
217 | expr_from_text("todo!()") | ||
218 | } | ||
219 | pub fn expr_path(path: ast::Path) -> ast::Expr { | 250 | pub fn expr_path(path: ast::Path) -> ast::Expr { |
220 | expr_from_text(&path.to_string()) | 251 | expr_from_text(&path.to_string()) |
221 | } | 252 | } |
@@ -275,6 +306,9 @@ pub fn expr_tuple(elements: impl IntoIterator<Item = ast::Expr>) -> ast::Expr { | |||
275 | let expr = elements.into_iter().format(", "); | 306 | let expr = elements.into_iter().format(", "); |
276 | expr_from_text(&format!("({})", expr)) | 307 | expr_from_text(&format!("({})", expr)) |
277 | } | 308 | } |
309 | pub fn expr_assignment(lhs: ast::Expr, rhs: ast::Expr) -> ast::Expr { | ||
310 | expr_from_text(&format!("{} = {}", lhs, rhs)) | ||
311 | } | ||
278 | fn expr_from_text(text: &str) -> ast::Expr { | 312 | fn expr_from_text(text: &str) -> ast::Expr { |
279 | ast_from_text(&format!("const C: () = {};", text)) | 313 | ast_from_text(&format!("const C: () = {};", text)) |
280 | } | 314 | } |
@@ -441,17 +475,6 @@ pub fn expr_stmt(expr: ast::Expr) -> ast::ExprStmt { | |||
441 | ast_from_text(&format!("fn f() {{ {}{} (); }}", expr, semi)) | 475 | ast_from_text(&format!("fn f() {{ {}{} (); }}", expr, semi)) |
442 | } | 476 | } |
443 | 477 | ||
444 | pub fn token(kind: SyntaxKind) -> SyntaxToken { | ||
445 | tokens::SOURCE_FILE | ||
446 | .tree() | ||
447 | .syntax() | ||
448 | .clone_for_update() | ||
449 | .descendants_with_tokens() | ||
450 | .filter_map(|it| it.into_token()) | ||
451 | .find(|it| it.kind() == kind) | ||
452 | .unwrap_or_else(|| panic!("unhandled token: {:?}", kind)) | ||
453 | } | ||
454 | |||
455 | pub fn param(pat: ast::Pat, ty: ast::Type) -> ast::Param { | 478 | pub fn param(pat: ast::Pat, ty: ast::Type) -> ast::Param { |
456 | ast_from_text(&format!("fn f({}: {}) {{ }}", pat, ty)) | 479 | ast_from_text(&format!("fn f({}: {}) {{ }}", pat, ty)) |
457 | } | 480 | } |
@@ -473,7 +496,7 @@ pub fn param_list( | |||
473 | ast_from_text(&list) | 496 | ast_from_text(&list) |
474 | } | 497 | } |
475 | 498 | ||
476 | pub fn generic_param(name: String, ty: Option<ast::TypeBoundList>) -> ast::GenericParam { | 499 | pub fn type_param(name: ast::Name, ty: Option<ast::TypeBoundList>) -> ast::TypeParam { |
477 | let bound = match ty { | 500 | let bound = match ty { |
478 | Some(it) => format!(": {}", it), | 501 | Some(it) => format!(": {}", it), |
479 | None => String::new(), | 502 | None => String::new(), |
@@ -481,6 +504,10 @@ pub fn generic_param(name: String, ty: Option<ast::TypeBoundList>) -> ast::Gener | |||
481 | ast_from_text(&format!("fn f<{}{}>() {{ }}", name, bound)) | 504 | ast_from_text(&format!("fn f<{}{}>() {{ }}", name, bound)) |
482 | } | 505 | } |
483 | 506 | ||
507 | pub fn lifetime_param(lifetime: ast::Lifetime) -> ast::LifetimeParam { | ||
508 | ast_from_text(&format!("fn f<{}>() {{ }}", lifetime)) | ||
509 | } | ||
510 | |||
484 | pub fn generic_param_list( | 511 | pub fn generic_param_list( |
485 | pats: impl IntoIterator<Item = ast::GenericParam>, | 512 | pats: impl IntoIterator<Item = ast::GenericParam>, |
486 | ) -> ast::GenericParamList { | 513 | ) -> ast::GenericParamList { |
@@ -574,15 +601,20 @@ fn ast_from_text<N: AstNode>(text: &str) -> N { | |||
574 | panic!("Failed to make ast node `{}` from text {}", std::any::type_name::<N>(), text) | 601 | panic!("Failed to make ast node `{}` from text {}", std::any::type_name::<N>(), text) |
575 | } | 602 | } |
576 | }; | 603 | }; |
577 | let node = node.syntax().clone(); | 604 | let node = node.clone_subtree(); |
578 | let node = unroot(node); | ||
579 | let node = N::cast(node).unwrap(); | ||
580 | assert_eq!(node.syntax().text_range().start(), 0.into()); | 605 | assert_eq!(node.syntax().text_range().start(), 0.into()); |
581 | node | 606 | node |
582 | } | 607 | } |
583 | 608 | ||
584 | fn unroot(n: SyntaxNode) -> SyntaxNode { | 609 | pub fn token(kind: SyntaxKind) -> SyntaxToken { |
585 | SyntaxNode::new_root(n.green().into()) | 610 | tokens::SOURCE_FILE |
611 | .tree() | ||
612 | .syntax() | ||
613 | .clone_for_update() | ||
614 | .descendants_with_tokens() | ||
615 | .filter_map(|it| it.into_token()) | ||
616 | .find(|it| it.kind() == kind) | ||
617 | .unwrap_or_else(|| panic!("unhandled token: {:?}", kind)) | ||
586 | } | 618 | } |
587 | 619 | ||
588 | pub mod tokens { | 620 | pub mod tokens { |
diff --git a/crates/syntax/src/ast/token_ext.rs b/crates/syntax/src/ast/token_ext.rs index 29d25a58a..4b1e1ccee 100644 --- a/crates/syntax/src/ast/token_ext.rs +++ b/crates/syntax/src/ast/token_ext.rs | |||
@@ -143,6 +143,30 @@ impl QuoteOffsets { | |||
143 | } | 143 | } |
144 | } | 144 | } |
145 | 145 | ||
146 | pub trait IsString: AstToken { | ||
147 | fn quote_offsets(&self) -> Option<QuoteOffsets> { | ||
148 | let text = self.text(); | ||
149 | let offsets = QuoteOffsets::new(text)?; | ||
150 | let o = self.syntax().text_range().start(); | ||
151 | let offsets = QuoteOffsets { | ||
152 | quotes: (offsets.quotes.0 + o, offsets.quotes.1 + o), | ||
153 | contents: offsets.contents + o, | ||
154 | }; | ||
155 | Some(offsets) | ||
156 | } | ||
157 | fn text_range_between_quotes(&self) -> Option<TextRange> { | ||
158 | self.quote_offsets().map(|it| it.contents) | ||
159 | } | ||
160 | fn open_quote_text_range(&self) -> Option<TextRange> { | ||
161 | self.quote_offsets().map(|it| it.quotes.0) | ||
162 | } | ||
163 | fn close_quote_text_range(&self) -> Option<TextRange> { | ||
164 | self.quote_offsets().map(|it| it.quotes.1) | ||
165 | } | ||
166 | } | ||
167 | |||
168 | impl IsString for ast::String {} | ||
169 | |||
146 | impl ast::String { | 170 | impl ast::String { |
147 | pub fn is_raw(&self) -> bool { | 171 | pub fn is_raw(&self) -> bool { |
148 | self.text().starts_with('r') | 172 | self.text().starts_with('r') |
@@ -187,32 +211,49 @@ impl ast::String { | |||
187 | (false, false) => Some(Cow::Owned(buf)), | 211 | (false, false) => Some(Cow::Owned(buf)), |
188 | } | 212 | } |
189 | } | 213 | } |
190 | |||
191 | pub fn quote_offsets(&self) -> Option<QuoteOffsets> { | ||
192 | let text = self.text(); | ||
193 | let offsets = QuoteOffsets::new(text)?; | ||
194 | let o = self.syntax().text_range().start(); | ||
195 | let offsets = QuoteOffsets { | ||
196 | quotes: (offsets.quotes.0 + o, offsets.quotes.1 + o), | ||
197 | contents: offsets.contents + o, | ||
198 | }; | ||
199 | Some(offsets) | ||
200 | } | ||
201 | pub fn text_range_between_quotes(&self) -> Option<TextRange> { | ||
202 | self.quote_offsets().map(|it| it.contents) | ||
203 | } | ||
204 | pub fn open_quote_text_range(&self) -> Option<TextRange> { | ||
205 | self.quote_offsets().map(|it| it.quotes.0) | ||
206 | } | ||
207 | pub fn close_quote_text_range(&self) -> Option<TextRange> { | ||
208 | self.quote_offsets().map(|it| it.quotes.1) | ||
209 | } | ||
210 | } | 214 | } |
211 | 215 | ||
216 | impl IsString for ast::ByteString {} | ||
217 | |||
212 | impl ast::ByteString { | 218 | impl ast::ByteString { |
213 | pub fn is_raw(&self) -> bool { | 219 | pub fn is_raw(&self) -> bool { |
214 | self.text().starts_with("br") | 220 | self.text().starts_with("br") |
215 | } | 221 | } |
222 | |||
223 | pub fn value(&self) -> Option<Cow<'_, [u8]>> { | ||
224 | if self.is_raw() { | ||
225 | let text = self.text(); | ||
226 | let text = | ||
227 | &text[self.text_range_between_quotes()? - self.syntax().text_range().start()]; | ||
228 | return Some(Cow::Borrowed(text.as_bytes())); | ||
229 | } | ||
230 | |||
231 | let text = self.text(); | ||
232 | let text = &text[self.text_range_between_quotes()? - self.syntax().text_range().start()]; | ||
233 | |||
234 | let mut buf: Vec<u8> = Vec::new(); | ||
235 | let mut text_iter = text.chars(); | ||
236 | let mut has_error = false; | ||
237 | unescape_literal(text, Mode::ByteStr, &mut |char_range, unescaped_char| match ( | ||
238 | unescaped_char, | ||
239 | buf.capacity() == 0, | ||
240 | ) { | ||
241 | (Ok(c), false) => buf.push(c as u8), | ||
242 | (Ok(c), true) if char_range.len() == 1 && Some(c) == text_iter.next() => (), | ||
243 | (Ok(c), true) => { | ||
244 | buf.reserve_exact(text.len()); | ||
245 | buf.extend_from_slice(&text[..char_range.start].as_bytes()); | ||
246 | buf.push(c as u8); | ||
247 | } | ||
248 | (Err(_), _) => has_error = true, | ||
249 | }); | ||
250 | |||
251 | match (has_error, buf.capacity() == 0) { | ||
252 | (true, _) => None, | ||
253 | (false, true) => Some(Cow::Borrowed(text.as_bytes())), | ||
254 | (false, false) => Some(Cow::Owned(buf)), | ||
255 | } | ||
256 | } | ||
216 | } | 257 | } |
217 | 258 | ||
218 | #[derive(Debug)] | 259 | #[derive(Debug)] |
diff --git a/crates/syntax/src/parsing.rs b/crates/syntax/src/parsing.rs index 333bde54a..431ed0699 100644 --- a/crates/syntax/src/parsing.rs +++ b/crates/syntax/src/parsing.rs | |||
@@ -6,14 +6,13 @@ mod text_token_source; | |||
6 | mod text_tree_sink; | 6 | mod text_tree_sink; |
7 | mod reparsing; | 7 | mod reparsing; |
8 | 8 | ||
9 | use crate::{syntax_node::GreenNode, AstNode, SyntaxError, SyntaxNode}; | 9 | use parser::SyntaxKind; |
10 | use text_token_source::TextTokenSource; | 10 | use text_token_source::TextTokenSource; |
11 | use text_tree_sink::TextTreeSink; | 11 | use text_tree_sink::TextTreeSink; |
12 | 12 | ||
13 | pub(crate) use lexer::*; | 13 | use crate::{syntax_node::GreenNode, AstNode, SyntaxError, SyntaxNode}; |
14 | 14 | ||
15 | pub(crate) use self::reparsing::incremental_reparse; | 15 | pub(crate) use crate::parsing::{lexer::*, reparsing::incremental_reparse}; |
16 | use parser::SyntaxKind; | ||
17 | 16 | ||
18 | pub(crate) fn parse_text(text: &str) -> (GreenNode, Vec<SyntaxError>) { | 17 | pub(crate) fn parse_text(text: &str) -> (GreenNode, Vec<SyntaxError>) { |
19 | let (tokens, lexer_errors) = tokenize(&text); | 18 | let (tokens, lexer_errors) = tokenize(&text); |
diff --git a/crates/syntax/src/parsing/text_tree_sink.rs b/crates/syntax/src/parsing/text_tree_sink.rs index 1934204ea..d63ec080b 100644 --- a/crates/syntax/src/parsing/text_tree_sink.rs +++ b/crates/syntax/src/parsing/text_tree_sink.rs | |||
@@ -147,8 +147,8 @@ fn n_attached_trivias<'a>( | |||
147 | trivias: impl Iterator<Item = (SyntaxKind, &'a str)>, | 147 | trivias: impl Iterator<Item = (SyntaxKind, &'a str)>, |
148 | ) -> usize { | 148 | ) -> usize { |
149 | match kind { | 149 | match kind { |
150 | MACRO_CALL | MACRO_RULES | MACRO_DEF | CONST | TYPE_ALIAS | STRUCT | UNION | ENUM | 150 | CONST | ENUM | FN | IMPL | MACRO_CALL | MACRO_DEF | MACRO_RULES | MODULE | RECORD_FIELD |
151 | | VARIANT | FN | TRAIT | MODULE | RECORD_FIELD | STATIC | USE => { | 151 | | STATIC | STRUCT | TRAIT | TUPLE_FIELD | TYPE_ALIAS | UNION | USE | VARIANT => { |
152 | let mut res = 0; | 152 | let mut res = 0; |
153 | let mut trivias = trivias.enumerate().peekable(); | 153 | let mut trivias = trivias.enumerate().peekable(); |
154 | 154 | ||
diff --git a/crates/syntax/src/ted.rs b/crates/syntax/src/ted.rs index 91a06101f..a50c0dbca 100644 --- a/crates/syntax/src/ted.rs +++ b/crates/syntax/src/ted.rs | |||
@@ -125,8 +125,11 @@ pub fn remove_all_iter(range: impl IntoIterator<Item = SyntaxElement>) { | |||
125 | } | 125 | } |
126 | 126 | ||
127 | pub fn replace(old: impl Element, new: impl Element) { | 127 | pub fn replace(old: impl Element, new: impl Element) { |
128 | replace_with_many(old, vec![new.syntax_element()]) | ||
129 | } | ||
130 | pub fn replace_with_many(old: impl Element, new: Vec<SyntaxElement>) { | ||
128 | let old = old.syntax_element(); | 131 | let old = old.syntax_element(); |
129 | replace_all(old.clone()..=old, vec![new.syntax_element()]) | 132 | replace_all(old.clone()..=old, new) |
130 | } | 133 | } |
131 | pub fn replace_all(range: RangeInclusive<SyntaxElement>, new: Vec<SyntaxElement>) { | 134 | pub fn replace_all(range: RangeInclusive<SyntaxElement>, new: Vec<SyntaxElement>) { |
132 | let start = range.start().index(); | 135 | let start = range.start().index(); |
diff --git a/crates/syntax/test_data/parser/ok/0045_block_attrs.rast b/crates/syntax/test_data/parser/ok/0045_block_attrs.rast index 50ab52d32..5e50b4e0b 100644 --- a/crates/syntax/test_data/parser/ok/0045_block_attrs.rast +++ b/crates/syntax/test_data/parser/ok/0045_block_attrs.rast | |||
@@ -127,9 +127,9 @@ [email protected] | |||
127 | [email protected] "\n" | 127 | [email protected] "\n" |
128 | [email protected] "}" | 128 | [email protected] "}" |
129 | [email protected] "\n\n" | 129 | [email protected] "\n\n" |
130 | COMMENT@541..601 "// https://github.com ..." | 130 | IMPL@541..763 |
131 | WHITESPACE@601..602 "\n" | 131 | COMMENT@541..601 "// https://github.com ..." |
132 | IMPL@602..763 | 132 | WHITESPACE@601..602 "\n" |
133 | [email protected] "impl" | 133 | [email protected] "impl" |
134 | [email protected] " " | 134 | [email protected] " " |
135 | [email protected] | 135 | [email protected] |